├── .github ├── CODEOWNERS └── workflows │ ├── archive.yml │ ├── ghpages.yml │ ├── publish.yml │ ├── setup.yml │ └── update.yml ├── .gitignore ├── .lint.py ├── .note.xml ├── .travis.yml ├── CONTRIBUTING.md ├── LICENSE.md ├── Makefile ├── README.md ├── artifacts ├── README.txt └── [ECH] HRR Design Team.pdf ├── draft-ietf-tls-esni.md └── id └── draft-rescorla-tls-esni.md /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Automatically generated CODEOWNERS 2 | # Regenerate with `make update-codeowners` 3 | draft-ietf-tls-esni.md caw@heapingbits.net 4 | -------------------------------------------------------------------------------- /.github/workflows/archive.yml: -------------------------------------------------------------------------------- 1 | name: "Archive Issues and Pull Requests" 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0 * * 0,2,4' 6 | repository_dispatch: 7 | types: [archive] 8 | workflow_dispatch: 9 | 10 | jobs: 11 | build: 12 | name: "Archive Issues and Pull Requests" 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: "Checkout" 16 | uses: actions/checkout@v2 17 | 18 | - name: "Update Archive" 19 | uses: martinthomson/i-d-template@v1 20 | with: 21 | make: archive 22 | token: ${{ github.token }} 23 | 24 | - name: "Update GitHub Pages" 25 | uses: martinthomson/i-d-template@v1 26 | with: 27 | make: gh-archive 28 | token: ${{ github.token }} 29 | 30 | - name: "Save Archive" 31 | uses: actions/upload-artifact@v4 32 | with: 33 | path: archive.json 34 | -------------------------------------------------------------------------------- /.github/workflows/ghpages.yml: -------------------------------------------------------------------------------- 1 | name: "Update Editor's Copy" 2 | 3 | on: 4 | push: 5 | paths-ignore: 6 | - README.md 7 | - CONTRIBUTING.md 8 | - LICENSE.md 9 | - .gitignore 10 | pull_request: 11 | paths-ignore: 12 | - README.md 13 | - CONTRIBUTING.md 14 | - LICENSE.md 15 | - .gitignore 16 | 17 | jobs: 18 | build: 19 | name: "Update Editor's Copy" 20 | runs-on: ubuntu-latest 21 | steps: 22 | - name: "Checkout" 23 | uses: actions/checkout@v2 24 | 25 | - name: "Cache Setup" 26 | id: cache-setup 27 | run: | 28 | mkdir -p "$HOME"/.cache/xml2rfc 29 | echo "::set-output name=path::$HOME/.cache/xml2rfc" 30 | date -u "+::set-output name=date::%FT%T" 31 | 32 | - name: "Cache References" 33 | uses: actions/cache@v4 34 | with: 35 | path: | 36 | ${{ steps.cache-setup.outputs.path }} 37 | .targets.mk 38 | key: refcache-${{ steps.cache-setup.outputs.date }} 39 | restore-keys: | 40 | refcache-${{ steps.cache-setup.outputs.date }} 41 | refcache- 42 | 43 | - name: "Build Drafts" 44 | uses: martinthomson/i-d-template@v1 45 | with: 46 | token: ${{ github.token }} 47 | 48 | - name: "Update GitHub Pages" 49 | uses: martinthomson/i-d-template@v1 50 | if: ${{ github.event_name == 'push' }} 51 | with: 52 | make: gh-pages 53 | token: ${{ github.token }} 54 | 55 | - name: "Archive Built Drafts" 56 | uses: actions/upload-artifact@v4 57 | with: 58 | path: | 59 | draft-*.html 60 | draft-*.txt 61 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: "Publish New Draft Version" 2 | 3 | on: 4 | push: 5 | tags: 6 | - "draft-*" 7 | 8 | jobs: 9 | build: 10 | name: "Publish New Draft Version" 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: "Checkout" 14 | uses: actions/checkout@v2 15 | 16 | # See https://github.com/actions/checkout/issues/290 17 | - name: "Get Tag Annotations" 18 | run: git fetch -f origin ${{ github.ref }}:${{ github.ref }} 19 | 20 | - name: "Cache Setup" 21 | id: cache-setup 22 | run: | 23 | mkdir -p "$HOME"/.cache/xml2rfc 24 | echo "::set-output name=path::$HOME/.cache/xml2rfc" 25 | date -u "+::set-output name=date::%FT%T" 26 | 27 | - name: "Cache References" 28 | uses: actions/cache@v4 29 | with: 30 | path: | 31 | ${{ steps.cache-setup.outputs.path }} 32 | .targets.mk 33 | key: refcache-${{ steps.date.outputs.date }} 34 | restore-keys: | 35 | refcache-${{ steps.date.outputs.date }} 36 | refcache- 37 | 38 | - name: "Build Drafts" 39 | uses: martinthomson/i-d-template@v1 40 | with: 41 | token: ${{ github.token }} 42 | 43 | - name: "Upload to Datatracker" 44 | uses: martinthomson/i-d-template@v1 45 | with: 46 | make: upload 47 | 48 | - name: "Archive Submitted Drafts" 49 | uses: actions/upload-artifact@v4 50 | with: 51 | path: "draft-*-[0-9][0-9].xml" 52 | -------------------------------------------------------------------------------- /.github/workflows/setup.yml: -------------------------------------------------------------------------------- 1 | name: "Perform Initial Repository Setup" 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | 7 | jobs: 8 | setup: 9 | name: "Setup Repository" 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: "Checkout" 13 | uses: actions/checkout@v2 14 | 15 | - name: "Check For Initial Commit" 16 | id: first 17 | run: | 18 | if [ -n "$(git show --max-parents=0 -q --format='format:%h' HEAD)" ]; then 19 | echo "Skipping build for the first commit" 20 | echo "::set-output name=first::true" 21 | fi 22 | 23 | - name: "Git Config" 24 | if: ${{ steps.first.outputs.first != 'true' }} 25 | run: | 26 | git config user.email "idbot@example.com" 27 | git config user.name "I-D Bot" 28 | 29 | - name: "Update Draft Name" 30 | if: ${{ steps.first.outputs.first != 'true' }} 31 | run: | 32 | if [ -z "$(ls draft-* rfc* 2>/dev/null | grep -v draft-todo-yourname-protocol.md)" ]; then 33 | echo 34 | echo "=============================================================" 35 | echo 36 | echo "Please rename the draft file." 37 | echo "The file can be renamed at:" 38 | echo 39 | echo " https://github.com/${{github.repository}}/edit/main/draft-todo-yourname-protocol.md" 40 | echo 41 | echo "Change the name of the file and its title." 42 | echo "Commit the changes to the 'main' branch." 43 | echo 44 | echo "=============================================================" 45 | echo 46 | exit 1 47 | fi 48 | for i in draft-*; do 49 | if [ "$(head -1 "$i")" = "---" ]; then 50 | sed -i -e '2,/^---/{/^###/,/^###/d 51 | s|^docname: .*|docname: '"${i%.md}-latest"'| 52 | s|^ name: Your Name Here| name: "'"$(git show -q --format='format:%aN' @)"'"| 53 | s|^ email: your\.email@example\.com| email: "'"$(git show -q --format='format:%aE' @)"'"| 54 | }' "$i" 55 | fi 56 | sed -i -e "s/draft-todo-yourname-protocol-latest/${i%.md}-latest/g" "$i" 57 | git add "$i" 58 | done 59 | if [ -n "$(git status --porcelain draft-*)" ]; then 60 | git commit -m "Update draft labels" draft-* 61 | fi 62 | 63 | - name: "Cleanup" 64 | if: ${{ steps.first.outputs.first != 'true' }} 65 | run: | 66 | git rm -rf .github/workflows/setup.yml README.md 67 | git commit -m "Remove setup files" 68 | 69 | - name: "Clone the i-d-template Repo" 70 | if: ${{ steps.first.outputs.first != 'true' }} 71 | run: | 72 | git clone --depth=1 https://github.com/martinthomson/i-d-template lib 73 | 74 | - name: "Run i-d-template Setup" 75 | if: ${{ steps.first.outputs.first != 'true' }} 76 | uses: martinthomson/i-d-template@v1 77 | with: 78 | make: setup 79 | 80 | - name: "Update Venue Information" 81 | if: ${{ steps.first.outputs.first != 'true' }} 82 | uses: martinthomson/i-d-template@v1 83 | with: 84 | make: update-venue 85 | 86 | - name: "Update GitHub Pages" 87 | if: ${{ steps.first.outputs.first != 'true' }} 88 | uses: martinthomson/i-d-template@v1 89 | with: 90 | make: gh-pages 91 | 92 | - name: "Push Changes" 93 | if: ${{ steps.first.outputs.first != 'true' }} 94 | run: | 95 | git push 96 | -------------------------------------------------------------------------------- /.github/workflows/update.yml: -------------------------------------------------------------------------------- 1 | name: "Update Generated Files" 2 | # This rule is not run automatically. 3 | # It can be run manually to update all of the files that are part 4 | # of the template, specifically: 5 | # - README.md 6 | # - CONTRIBUTING.md 7 | # - .note.xml 8 | # - .github/CODEOWNERS 9 | # - Makefile 10 | # 11 | # 12 | # This might be useful if you have: 13 | # - added, removed, or renamed drafts (including after adoption) 14 | # - added, removed, or changed draft editors 15 | # - changed the title of drafts 16 | # 17 | # Note that this removes any customizations you have made to 18 | # the affected files. 19 | on: workflow_dispatch 20 | 21 | jobs: 22 | build: 23 | name: "Update Files" 24 | runs-on: ubuntu-latest 25 | steps: 26 | - name: "Checkout" 27 | uses: actions/checkout@v2 28 | 29 | - name: "Update Generated Files" 30 | uses: martinthomson/i-d-template@v1 31 | with: 32 | make: update-files 33 | token: ${{ github.token }} 34 | 35 | - name: "Push Update" 36 | run: git push 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | *.pdf 3 | *.redxml 4 | *.swp 5 | *.txt 6 | *.upload 7 | *~ 8 | .refcache 9 | .tags 10 | .targets.mk 11 | /*-[0-9][0-9].xml 12 | issues.json 13 | pulls.json 14 | report.xml 15 | venv/ 16 | lib 17 | draft-ietf-tls-esni.xml 18 | -------------------------------------------------------------------------------- /.lint.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys 4 | import argparse 5 | import re 6 | 7 | parser = argparse.ArgumentParser(description="Lint markdown drafts.") 8 | parser.add_argument("files", metavar="file", nargs="+", help="Files to lint") 9 | parser.add_argument("-l", dest="maxLineLength", default=80) 10 | parser.add_argument("-f", dest="maxFigureLineLength", default=65) 11 | 12 | args = parser.parse_args() 13 | 14 | foundError = False 15 | 16 | for inputfile in args.files: 17 | insideFigure = False 18 | beforeAbstract = True 19 | 20 | with open(inputfile, mode="rt", newline=None, encoding="utf-8") as draft: 21 | linenumber = 0 22 | lines = draft.readlines() 23 | 24 | abstract = re.compile("^--- abstract") 25 | table = re.compile("^\s*(?:\||{:)") 26 | figure = re.compile("^[~`]{3,}") 27 | 28 | for line in lines: 29 | line = line.rstrip("\r\n") 30 | linenumber += 1 31 | 32 | def err(msg): 33 | global foundError 34 | foundError = True 35 | sys.stderr.write("{0}:{1}: {2}\n".format(inputfile, linenumber, msg)) 36 | sys.stderr.write("{0}\n".format(line)) 37 | 38 | if line.find("\t") >= 0: 39 | err("Line contains HTAB") 40 | 41 | # Skip everything before abstract 42 | if beforeAbstract: 43 | matchObj = abstract.match(line) 44 | if matchObj: 45 | beforeAbstract = False 46 | continue 47 | 48 | # Skip tables 49 | matchObj = table.match(line) 50 | if matchObj: 51 | continue 52 | 53 | # Toggle figure state 54 | matchObj = figure.match(line) 55 | if matchObj: 56 | insideFigure = not insideFigure 57 | continue 58 | 59 | # Check length 60 | length = len(line) 61 | limit = ( 62 | int(args.maxFigureLineLength) 63 | if insideFigure 64 | else int(args.maxLineLength) 65 | ) 66 | if length > limit: 67 | err("Line is {0} characters; limit is {1}".format(length, limit)) 68 | 69 | sys.exit(1 if foundError else 0) -------------------------------------------------------------------------------- /.note.xml: -------------------------------------------------------------------------------- 1 | 2 | Source for this draft and an issue tracker can be found at 3 | https://github.com/tlswg/draft-ietf-tls-esni. 4 | 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | sudo: false 3 | dist: xenial 4 | 5 | addons: 6 | apt: 7 | packages: 8 | - python-pip 9 | - xsltproc 10 | 11 | env: 12 | global: 13 | - GOPATH="${TRAVIS_BUILD_DIR}/.go_workspace" 14 | - mmark_src=github.com/miekg/mmark/mmark 15 | - mmark=./mmark 16 | 17 | install: 18 | - pip install xml2rfc 19 | - if head -1 -q *.md | grep '^\-\-\-' >/dev/null 2>&1; then gem install --no-doc kramdown-rfc2629; fi 20 | - if head -1 -q *.md | grep '^%%%' >/dev/null 2>&1; then go get "$mmark_src" && go build "$mmark_src"; fi 21 | 22 | script: 23 | - make 24 | - make issues || make issues DISABLE_ISSUE_FETCH=true && make gh-issues 25 | - make gh-pages 26 | 27 | deploy: 28 | provider: script 29 | script: make upload 30 | skip_cleanup: true 31 | on: 32 | tags: true 33 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | This repository relates to activities in the Internet Engineering Task Force 4 | ([IETF](https://www.ietf.org/)). All material in this repository is considered 5 | Contributions to the IETF Standards Process, as defined in the intellectual 6 | property policies of IETF currently designated as 7 | [BCP 78](https://www.rfc-editor.org/info/bcp78), 8 | [BCP 79](https://www.rfc-editor.org/info/bcp79) and the 9 | [IETF Trust Legal Provisions (TLP) Relating to IETF Documents](http://trustee.ietf.org/trust-legal-provisions.html). 10 | 11 | Any edit, commit, pull request, issue, comment or other change made to this 12 | repository constitutes Contributions to the IETF Standards Process 13 | (https://www.ietf.org/). 14 | 15 | You agree to comply with all applicable IETF policies and procedures, including, 16 | BCP 78, 79, the TLP, and the TLP rules regarding code components (e.g. being 17 | subject to a Simplified BSD License) in Contributions. 18 | 19 | 20 | ## Other Resources 21 | 22 | Discussion of this work occurs on the 23 | [tls working group mailing list](https://mailarchive.ietf.org/arch/browse/tls/) 24 | ([subscribe](https://www.ietf.org/mailman/listinfo/tls)). In addition to 25 | contributions in GitHub, you are encouraged to participate in discussions there. 26 | 27 | **Note**: Some working groups adopt a policy whereby substantive discussion of 28 | technical issues needs to occur on the mailing list. 29 | 30 | You might also like to familiarize yourself with other 31 | [working group documents](https://datatracker.ietf.org/wg/tls/documents/). 32 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # License 2 | 3 | See the 4 | [guidelines for contributions](https://github.com/tlswg/draft-ietf-tls-esni/blob/master/CONTRIBUTING.md). 5 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | LIBDIR := lib 2 | include $(LIBDIR)/main.mk 3 | 4 | $(LIBDIR)/main.mk: 5 | ifneq (,$(shell grep "path *= *$(LIBDIR)" .gitmodules 2>/dev/null)) 6 | git submodule sync 7 | git submodule update $(CLONE_ARGS) --init 8 | else 9 | git clone -q --depth 10 $(CLONE_ARGS) \ 10 | -b main https://github.com/martinthomson/i-d-template $(LIBDIR) 11 | endif 12 | 13 | latest:: lint 14 | .PHONY: lint 15 | 16 | PYTHON := $(shell which python3) 17 | ifeq ($(PYTHON),) 18 | PYTHON := $(shell which python) 19 | endif 20 | 21 | ifneq ($(PYTHON),) 22 | lint:: 23 | @$(PYTHON) ./.lint.py $(addsuffix .md,$(drafts)) 24 | endif 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TLS Encrypted Client Hello 2 | 3 | This is the working area for the IETF [TLS Working Group](https://datatracker.ietf.org/wg/tls/documents/) Internet-Draft, "TLS Encrypted Client Hello". 4 | 5 | * [Editor's Copy](https://tlswg.github.io/draft-ietf-tls-esni/#go.draft-ietf-tls-esni.html) 6 | * [Working Group Draft](https://tools.ietf.org/html/draft-ietf-tls-esni) 7 | * [Compare Editor's Copy to Working Group Draft](https://tlswg.github.io/draft-ietf-tls-esni/#go.draft-ietf-tls-esni.diff) 8 | 9 | ## Building the Draft 10 | 11 | Formatted text and HTML versions of the draft can be built using `make`. 12 | 13 | ```sh 14 | $ make 15 | ``` 16 | 17 | This requires that you have the necessary software installed. See 18 | [the instructions](https://github.com/martinthomson/i-d-template/blob/master/doc/SETUP.md). 19 | 20 | 21 | ## Contributing 22 | 23 | See the 24 | [guidelines for contributions](https://github.com/tlswg/draft-ietf-tls-esni/blob/master/CONTRIBUTING.md). 25 | -------------------------------------------------------------------------------- /artifacts/README.txt: -------------------------------------------------------------------------------- 1 | This directory contains artifacts related to the development of ECH. 2 | -------------------------------------------------------------------------------- /artifacts/[ECH] HRR Design Team.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tlswg/draft-ietf-tls-esni/b5e5b4e097b6aba3ca16250596dc6b3e27e3e960/artifacts/[ECH] HRR Design Team.pdf -------------------------------------------------------------------------------- /draft-ietf-tls-esni.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: TLS Encrypted Client Hello 3 | abbrev: TLS Encrypted Client Hello 4 | docname: draft-ietf-tls-esni-latest 5 | category: std 6 | 7 | ipr: trust200902 8 | submissiontype: IETF 9 | area: SEC 10 | workgroup: tls 11 | keyword: Internet-Draft 12 | 13 | stand_alone: yes 14 | pi: [toc, sortrefs, symrefs] 15 | 16 | author: 17 | - 18 | ins: E. Rescorla 19 | name: Eric Rescorla 20 | organization: Independent 21 | email: ekr@rtfm.com 22 | 23 | - 24 | ins: K. Oku 25 | name: Kazuho Oku 26 | organization: Fastly 27 | email: kazuhooku@gmail.com 28 | 29 | - 30 | ins: N. Sullivan 31 | name: Nick Sullivan 32 | organization: Cryptography Consulting LLC 33 | email: nicholas.sullivan+ietf@gmail.com 34 | 35 | - 36 | ins: C. A. Wood 37 | name: Christopher A. Wood 38 | organization: Cloudflare 39 | email: caw@heapingbits.net 40 | 41 | 42 | normative: 43 | RFC2119: 44 | RFC7918: 45 | 46 | informative: 47 | WHATWG-IPV4: 48 | title: "URL Living Standard - IPv4 Parser" 49 | target: https://url.spec.whatwg.org/#concept-ipv4-parser 50 | date: May 2021 51 | ECH-Analysis: 52 | title: "A Symbolic Analysis of Privacy for TLS 1.3 with Encrypted Client Hello" 53 | target: https://www.cs.ox.ac.uk/people/vincent.cheval/publis/BCW-ccs22.pdf 54 | date: November 2022 55 | authors: 56 | - 57 | ins: K. Bhargavan 58 | org: Inria 59 | - 60 | ins: V. Cheval 61 | org: Inria 62 | - 63 | ins: C. Wood 64 | org: Cloudflare 65 | 66 | 67 | --- abstract 68 | 69 | This document describes a mechanism in Transport Layer Security (TLS) for 70 | encrypting a ClientHello message under a server public key. 71 | 72 | --- middle 73 | 74 | # Introduction {#intro} 75 | 76 | Although TLS 1.3 {{!RFC8446}} encrypts most of the handshake, including the 77 | server certificate, there are several ways in which an on-path attacker can 78 | learn private information about the connection. The plaintext Server Name 79 | Indication (SNI) extension in ClientHello messages, which leaks the target 80 | domain for a given connection, is perhaps the most sensitive information 81 | left unencrypted in TLS 1.3. 82 | 83 | This document specifies a new TLS extension, called Encrypted Client Hello 84 | (ECH), that allows clients to encrypt their ClientHello to the TLS server. 85 | This protects the SNI and other potentially sensitive fields, such as the 86 | Application Layer Protocol Negotiation (ALPN) 87 | list {{?RFC7301}}. Co-located servers with consistent externally visible TLS 88 | configurations and behavior, including supported versions and cipher suites and 89 | how they respond to incoming client connections, form an anonymity set. (Note 90 | that implementation-specific choices, such as extension ordering within TLS 91 | messages or division of data into record-layer boundaries, can result in 92 | different externally visible behavior, even for servers with consistent TLS 93 | configurations.) Usage of this mechanism reveals that a client is connecting 94 | to a particular service provider, but does not reveal which server from the 95 | anonymity set terminates the connection. Deployment implications of this 96 | feature are discussed in {{deployment}}. 97 | 98 | ECH is not in itself sufficient to protect the identity of the server. 99 | The target domain may also be visible through other channels, such as 100 | plaintext client DNS queries or visible server IP addresses. However, 101 | encrypted DNS mechanisms such as 102 | DNS over HTTPS {{?RFC8484}}, DNS over TLS/DTLS {{?RFC7858}} {{?RFC8094}}, and 103 | DNS over QUIC {{?RFC9250}} 104 | provide mechanisms for clients to conceal 105 | DNS lookups from network inspection, and many TLS servers host multiple domains 106 | on the same IP address. Private origins may also be deployed behind a common 107 | provider, such as a reverse proxy. In such environments, the SNI remains the 108 | primary explicit signal available to observers to determine the 109 | server's identity. 110 | 111 | ECH is supported in TLS 1.3 {{!RFC8446}}, DTLS 1.3 {{!RFC9147}}, and 112 | newer versions of the TLS and DTLS protocols. 113 | 114 | # Conventions and Definitions 115 | 116 | The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", 117 | "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this 118 | document are to be interpreted as described in BCP 14 {{RFC2119}} {{!RFC8174}} 119 | when, and only when, they appear in all capitals, as shown here. All TLS 120 | notation comes from {{RFC8446, Section 3}}. 121 | 122 | # Overview 123 | 124 | This protocol is designed to operate in one of two topologies illustrated below, 125 | which we call "Shared Mode" and "Split Mode". These modes are described in the 126 | following section. 127 | 128 | ## Topologies 129 | 130 | ~~~~ 131 | +---------------------+ 132 | | | 133 | | 2001:DB8::1111 | 134 | | | 135 | Client <-----> | private.example.org | 136 | | | 137 | | public.example.com | 138 | | | 139 | +---------------------+ 140 | Server 141 | (Client-Facing and Backend Combined) 142 | ~~~~ 143 | {: #shared-mode title="Shared Mode Topology"} 144 | 145 | In Shared Mode, the provider is the origin server for all the domains whose DNS 146 | records point to it. In this mode, the TLS connection is terminated by the 147 | provider. 148 | 149 | ~~~~ 150 | +--------------------+ +---------------------+ 151 | | | | | 152 | | 2001:DB8::1111 | | 2001:DB8::EEEE | 153 | Client <----------------------------->| | 154 | | public.example.com | | private.example.org | 155 | | | | | 156 | +--------------------+ +---------------------+ 157 | Client-Facing Server Backend Server 158 | ~~~~ 159 | {: #split-mode title="Split Mode Topology"} 160 | 161 | In Split Mode, the provider is not the origin server for private domains. 162 | Rather, the DNS records for private domains point to the provider, and the 163 | provider's server relays the connection back to the origin server, who 164 | terminates the TLS connection with the client. Importantly, the service provider 165 | does not have access to the plaintext of the connection beyond the unencrypted 166 | portions of the handshake. 167 | 168 | In the remainder of this document, we will refer to the ECH-service provider as 169 | the "client-facing server" and to the TLS terminator as the "backend server". 170 | These are the same entity in Shared Mode, but in Split Mode, the client-facing 171 | and backend servers are physically separated. 172 | 173 | See {{security-considerations}} for more discussion about the ECH threat model 174 | and how it relates to the client, client-facing server, and backend server. 175 | 176 | ## Encrypted ClientHello (ECH) 177 | 178 | A client-facing server enables ECH by publishing an ECH configuration, which 179 | is an encryption public key and associated metadata. Domains which wish to 180 | use ECH must publish this configuration, using the key associated 181 | with the client-facing server. This document 182 | defines the ECH configuration's format, but delegates DNS publication details 183 | to {{!RFC9460}}. See 184 | {{!ECH-IN-DNS=I-D.ietf-tls-svcb-ech}} for specifics about how ECH configurations 185 | are advertised in SVCB and HTTPS records. Other delivery mechanisms are 186 | also possible. For example, the client may have the ECH configuration 187 | preconfigured. 188 | 189 | When a client wants to establish a TLS session with some backend server, it 190 | constructs a private ClientHello, referred to as the ClientHelloInner. 191 | The client then constructs a public ClientHello, referred to as the 192 | ClientHelloOuter. The ClientHelloOuter contains innocuous values for 193 | sensitive extensions and an "encrypted_client_hello" extension 194 | ({{encrypted-client-hello}}), which carries the encrypted ClientHelloInner. 195 | Finally, the client sends ClientHelloOuter to the server. 196 | 197 | The server takes one of the following actions: 198 | 199 | 1. If it does not support ECH or cannot decrypt the extension, it completes 200 | the handshake with ClientHelloOuter. This is referred to as rejecting ECH. 201 | 1. If it successfully decrypts the extension, it forwards the ClientHelloInner 202 | to the backend server, which completes the handshake. This is referred to 203 | as accepting ECH. 204 | 205 | Upon receiving the server's response, the client determines whether or not ECH 206 | was accepted ({{determining-ech-acceptance}}) and proceeds with the handshake 207 | accordingly. When ECH is rejected, the resulting connection is not usable by 208 | the client for application data. Instead, ECH rejection allows the client to 209 | retry with up-to-date configuration ({{rejected-ech}}). 210 | 211 | The primary goal of ECH is to ensure that connections to servers in the same 212 | anonymity set are indistinguishable from one another. Moreover, it should 213 | achieve this goal without affecting any existing security properties of TLS 1.3. 214 | See {{goals}} for more details about the ECH security and privacy goals. 215 | 216 | # Encrypted ClientHello Configuration {#ech-configuration} 217 | 218 | ECH uses HPKE for public key encryption {{!HPKE=RFC9180}}. 219 | The ECH configuration is defined by the following `ECHConfig` structure. 220 | 221 | ~~~~ 222 | opaque HpkePublicKey<1..2^16-1>; 223 | uint16 HpkeKemId; // Defined in RFC9180 224 | uint16 HpkeKdfId; // Defined in RFC9180 225 | uint16 HpkeAeadId; // Defined in RFC9180 226 | uint16 ECHConfigExtensionType; // Defined in Section 11.3 227 | 228 | struct { 229 | HpkeKdfId kdf_id; 230 | HpkeAeadId aead_id; 231 | } HpkeSymmetricCipherSuite; 232 | 233 | struct { 234 | uint8 config_id; 235 | HpkeKemId kem_id; 236 | HpkePublicKey public_key; 237 | HpkeSymmetricCipherSuite cipher_suites<4..2^16-4>; 238 | } HpkeKeyConfig; 239 | 240 | struct { 241 | ECHConfigExtensionType type; 242 | opaque data<0..2^16-1>; 243 | } ECHConfigExtension; 244 | 245 | struct { 246 | HpkeKeyConfig key_config; 247 | uint8 maximum_name_length; 248 | opaque public_name<1..255>; 249 | ECHConfigExtension extensions<0..2^16-1>; 250 | } ECHConfigContents; 251 | 252 | struct { 253 | uint16 version; 254 | uint16 length; 255 | select (ECHConfig.version) { 256 | case 0xfe0d: ECHConfigContents contents; 257 | } 258 | } ECHConfig; 259 | ~~~~ 260 | 261 | The structure contains the following fields: 262 | 263 | version 264 | : The version of ECH for which this configuration is used. The version 265 | is the same as the code point for the 266 | "encrypted_client_hello" extension. Clients MUST ignore any `ECHConfig` 267 | structure with a version they do not support. 268 | 269 | length 270 | : The length, in bytes, of the next field. This length field allows 271 | implementations to skip over the elements in such a list where they cannot 272 | parse the specific version of ECHConfig. 273 | 274 | contents 275 | : An opaque byte string whose contents depend on the version. For this 276 | specification, the contents are an `ECHConfigContents` structure. 277 | 278 | The `ECHConfigContents` structure contains the following fields: 279 | 280 | key_config 281 | : A `HpkeKeyConfig` structure carrying the configuration information 282 | associated with the HPKE public key (an "ECH key"). Note that this 283 | structure contains the `config_id` field, which applies to the entire 284 | ECHConfigContents. 285 | 286 | maximum_name_length 287 | : The longest name of a backend server, if known. If not known, this value can 288 | be set to zero. It is used to compute padding ({{padding}}) and does not 289 | constrain server name lengths. Names may exceed this length if, e.g., 290 | the server uses wildcard names or added new names to the anonymity set. 291 | 292 | public_name 293 | : The DNS name of the client-facing server, i.e., the entity trusted 294 | to update the ECH configuration. This is used to correct misconfigured clients, 295 | as described in {{rejected-ech}}. 296 | 297 | : See {{auth-public-name}} for how the client interprets and validates the 298 | public_name. 299 | 300 | extensions 301 | : A list of ECHConfigExtension values that the client must take into 302 | consideration when generating a ClientHello message. Each ECHConfigExtension 303 | has a 2-octet type and opaque data value, where the data value is encoded 304 | with a 2-octet integer representing the length of the data, in network byte 305 | order. ECHConfigExtension values are described below ({{config-extensions}}). 306 | 307 | The `HpkeKeyConfig` structure contains the following fields: 308 | 309 | config_id 310 | : A one-byte identifier for the given HPKE key configuration. This is used by 311 | clients to indicate the key used for ClientHello encryption. {{config-ids}} 312 | describes how client-facing servers allocate this value. 313 | 314 | kem_id 315 | : The HPKE Key Encapsulation Mechanism (KEM) identifier corresponding 316 | to `public_key`. Clients MUST ignore any `ECHConfig` structure with a 317 | key using a KEM they do not support. 318 | 319 | public_key 320 | : The HPKE public key used by the client to encrypt ClientHelloInner. 321 | 322 | cipher_suites 323 | : The list of HPKE KDF and AEAD identifier pairs clients can use for encrypting 324 | ClientHelloInner. See {{real-ech}} for how clients choose from this list. 325 | 326 | The client-facing server advertises a sequence of ECH configurations to clients, 327 | serialized as follows. 328 | 329 | ~~~~ 330 | ECHConfig ECHConfigList<4..2^16-1>; 331 | ~~~~ 332 | 333 | The `ECHConfigList` structure contains one or more `ECHConfig` structures in 334 | decreasing order of preference. This allows a server to support multiple 335 | versions of ECH and multiple sets of ECH parameters. 336 | 337 | ## Configuration Identifiers {#config-ids} 338 | 339 | A client-facing server has a set of known ECHConfig values, with corresponding 340 | private keys. This set SHOULD contain the currently published values, as well as 341 | previous values that may still be in use, since clients may cache DNS records 342 | up to a TTL or longer. 343 | 344 | {{client-facing-server}} describes a trial decryption process for decrypting the 345 | ClientHello. This can impact performance when the client-facing server maintains 346 | many known ECHConfig values. To avoid this, the client-facing server SHOULD 347 | allocate distinct `config_id` values for each ECHConfig in its known set. The 348 | RECOMMENDED strategy is via rejection sampling, i.e., to randomly select 349 | `config_id` repeatedly until it does not match any known ECHConfig. 350 | 351 | It is not necessary for `config_id` values across different client-facing 352 | servers to be distinct. A backend server may be hosted behind two different 353 | client-facing servers with colliding `config_id` values without any performance 354 | impact. Values may also be reused if the previous ECHConfig is no longer in the 355 | known set. 356 | 357 | ## Configuration Extensions {#config-extensions} 358 | 359 | ECH configuration extensions are used to provide room for additional 360 | functionality as needed. The format is as defined in 361 | {{ech-configuration}} and mirrors {{Section 4.2 of RFC8446}}. However, 362 | ECH configuration extension types are maintained by IANA as described 363 | in {{config-extensions-iana}}. ECH configuration extensions follow 364 | the same interpretation rules as TLS extensions: extensions MAY appear 365 | in any order, but there MUST NOT be more than one extension of the 366 | same type in the extensions block. Unlike TLS extensions, an extension 367 | can be tagged as mandatory by using an extension type codepoint with 368 | the high order bit set to 1. 369 | 370 | Clients MUST parse the extension list and check for unsupported mandatory 371 | extensions. If an unsupported mandatory extension is present, clients MUST 372 | ignore the `ECHConfig`. 373 | 374 | Any future information or hints that influence ClientHelloOuter SHOULD be 375 | specified as ECHConfig extensions. This is primarily because the outer 376 | ClientHello exists only in support of ECH. Namely, it is both an envelope for 377 | the encrypted inner ClientHello and enabler for authenticated key mismatch 378 | signals (see {{server-behavior}}). In contrast, the inner ClientHello is the 379 | true ClientHello used upon ECH negotiation. 380 | 381 | 382 | # The "encrypted_client_hello" Extension {#encrypted-client-hello} 383 | 384 | To offer ECH, the client sends an "encrypted_client_hello" extension in the 385 | ClientHelloOuter. When it does, it MUST also send the extension in 386 | ClientHelloInner. 387 | 388 | ~~~ 389 | enum { 390 | encrypted_client_hello(0xfe0d), (65535) 391 | } ExtensionType; 392 | ~~~ 393 | 394 | The payload of the extension has the following structure: 395 | 396 | ~~~~ 397 | enum { outer(0), inner(1) } ECHClientHelloType; 398 | 399 | struct { 400 | ECHClientHelloType type; 401 | select (ECHClientHello.type) { 402 | case outer: 403 | HpkeSymmetricCipherSuite cipher_suite; 404 | uint8 config_id; 405 | opaque enc<0..2^16-1>; 406 | opaque payload<1..2^16-1>; 407 | case inner: 408 | Empty; 409 | }; 410 | } ECHClientHello; 411 | ~~~~ 412 | 413 | The outer extension uses the `outer` variant and the inner extension uses the 414 | `inner` variant. The inner extension has an empty payload, which is included 415 | because TLS servers are not allowed to provide extensions in ServerHello 416 | which were not included in ClientHello. The outer extension has the following 417 | fields: 418 | 419 | config_id 420 | : The ECHConfigContents.key_config.config_id for the chosen ECHConfig. 421 | 422 | cipher_suite 423 | : The cipher suite used to encrypt ClientHelloInner. This MUST match a value 424 | provided in the corresponding `ECHConfigContents.cipher_suites` list. 425 | 426 | enc 427 | : The HPKE encapsulated key, used by servers to decrypt the corresponding 428 | `payload` field. This field is empty in a ClientHelloOuter sent in response to 429 | HelloRetryRequest. 430 | 431 | payload 432 | : The serialized and encrypted EncodedClientHelloInner structure, encrypted 433 | using HPKE as described in {{real-ech}}. 434 | 435 | When a client offers the `outer` version of an "encrypted_client_hello" 436 | extension, the server MAY include an "encrypted_client_hello" extension in its 437 | EncryptedExtensions message, as described in {{client-facing-server}}, with the 438 | following payload: 439 | 440 | ~~~ 441 | struct { 442 | ECHConfigList retry_configs; 443 | } ECHEncryptedExtensions; 444 | ~~~ 445 | 446 | The response is valid only when the server used the ClientHelloOuter. If the 447 | server sent this extension in response to the `inner` variant, then the client 448 | MUST abort with an "unsupported_extension" alert. 449 | 450 | retry_configs 451 | : An ECHConfigList structure containing one or more ECHConfig structures, in 452 | decreasing order of preference, to be used by the client as described in 453 | {{rejected-ech}}. These are known as the server's "retry configurations". 454 | 455 | Finally, when the client offers the "encrypted_client_hello", if the payload is 456 | the `inner` variant and the server responds with HelloRetryRequest, it MUST 457 | include an "encrypted_client_hello" extension with the following payload: 458 | 459 | ~~~ 460 | struct { 461 | opaque confirmation[8]; 462 | } ECHHelloRetryRequest; 463 | ~~~ 464 | 465 | The value of ECHHelloRetryRequest.confirmation is set to 466 | `hrr_accept_confirmation` as described in {{backend-server-hrr}}. 467 | 468 | This document also defines the "ech_required" alert, which the client MUST send 469 | when it offered an "encrypted_client_hello" extension that was not accepted by 470 | the server. (See {{alerts}}.) 471 | 472 | ## Encoding the ClientHelloInner {#encoding-inner} 473 | 474 | Before encrypting, the client pads and optionally compresses ClientHelloInner 475 | into a EncodedClientHelloInner structure, defined below: 476 | 477 | ~~~ 478 | struct { 479 | ClientHello client_hello; 480 | uint8 zeros[length_of_padding]; 481 | } EncodedClientHelloInner; 482 | ~~~ 483 | 484 | The `client_hello` field is computed by first making a copy of ClientHelloInner 485 | and setting the `legacy_session_id` field to the empty string. In TLS, this 486 | field uses the ClientHello structure defined in {{Section 4.1.2 of RFC8446}}. 487 | In DTLS, it uses the ClientHello structured defined in 488 | {{Section 5.3 of RFC9147}}. This does not include Handshake structure's 489 | four-byte header in TLS, nor twelve-byte header in DTLS. The `zeros` field MUST 490 | be all zeroes of length `length_of_padding` (see {{padding}}). 491 | 492 | Repeating large extensions, such as "key_share" with post-quantum algorithms, 493 | between ClientHelloInner and ClientHelloOuter can lead to excessive size. To 494 | reduce the size impact, the client MAY substitute extensions which it knows 495 | will be duplicated in ClientHelloOuter. It does so by removing and replacing 496 | extensions from EncodedClientHelloInner with a single "ech_outer_extensions" 497 | extension, defined as follows: 498 | 499 | ~~~ 500 | enum { 501 | ech_outer_extensions(0xfd00), (65535) 502 | } ExtensionType; 503 | 504 | ExtensionType OuterExtensions<2..254>; 505 | ~~~ 506 | 507 | OuterExtensions contains the removed ExtensionType values. Each value references 508 | the matching extension in ClientHelloOuter. The values MUST be ordered 509 | contiguously in ClientHelloInner, and the "ech_outer_extensions" extension MUST 510 | be inserted in the corresponding position in EncodedClientHelloInner. 511 | Additionally, the extensions MUST appear in ClientHelloOuter in the same 512 | relative order. However, there is no requirement that they be contiguous. For 513 | example, OuterExtensions may contain extensions A, B, C, while ClientHelloOuter 514 | contains extensions A, D, B, C, E, F. 515 | 516 | The "ech_outer_extensions" extension can only be included in 517 | EncodedClientHelloInner, and MUST NOT appear in either ClientHelloOuter or 518 | ClientHelloInner. 519 | 520 | Finally, the client pads the message by setting the `zeros` field to a byte 521 | string whose contents are all zeros and whose length is the amount of padding 522 | to add. {{padding}} describes a recommended padding scheme. 523 | 524 | The client-facing server computes ClientHelloInner by reversing this process. 525 | First it parses EncodedClientHelloInner, interpreting all bytes after 526 | `client_hello` as padding. If any padding byte is non-zero, the server MUST 527 | abort the connection with an "illegal_parameter" alert. 528 | 529 | Next it makes a copy of the `client_hello` field and copies the 530 | `legacy_session_id` field from ClientHelloOuter. It then looks for an 531 | "ech_outer_extensions" extension. If found, it replaces the extension with the 532 | corresponding sequence of extensions in the ClientHelloOuter. The server MUST 533 | abort the connection with an "illegal_parameter" alert if any of the following 534 | are true: 535 | 536 | * Any referenced extension is missing in ClientHelloOuter. 537 | 538 | * Any extension is referenced in OuterExtensions more than once. 539 | 540 | * "encrypted_client_hello" is referenced in OuterExtensions. 541 | 542 | * The extensions in ClientHelloOuter corresponding to those in OuterExtensions 543 | do not occur in the same order. 544 | 545 | These requirements prevent an attacker from performing a packet amplification 546 | attack, by crafting a ClientHelloOuter which decompresses to a much larger 547 | ClientHelloInner. This is discussed further in {{decompression-amp}}. 548 | 549 | Implementations SHOULD construct the ClientHelloInner in linear 550 | time. Quadratic time implementations (such as may happen via naive 551 | copying) create a denial of service risk. 552 | {{linear-outer-extensions}} describes a linear-time procedure that may be used 553 | for this purpose. 554 | 555 | ## Authenticating the ClientHelloOuter {#authenticating-outer} 556 | 557 | To prevent a network attacker from modifying the `ClientHelloOuter` 558 | while keeping the same encrypted `ClientHelloInner` 559 | (see {{flow-clienthello-malleability}}), ECH authenticates ClientHelloOuter 560 | by passing ClientHelloOuterAAD as the associated data for HPKE sealing 561 | and opening operations. The ClientHelloOuterAAD is a serialized 562 | ClientHello structure, defined in {{Section 4.1.2 of RFC8446}} for TLS and 563 | {{Section 5.3 of RFC9147}} for DTLS, which matches the ClientHelloOuter except 564 | that the `payload` field of the "encrypted_client_hello" is replaced with a byte 565 | string of the same length but whose contents are zeros. This value does not 566 | include Handshake structure's four-byte header in TLS nor twelve-byte header in 567 | DTLS. 568 | 569 | # Client Behavior 570 | 571 | Clients that implement the ECH extension behave in one of two ways: either they 572 | offer a real ECH extension, as described in {{real-ech}}; or they send a 573 | Generate Random Extensions And Sustain Extensibility (GREASE) {{?RFC8701}} 574 | ECH extension, as described in {{grease-ech}}. Clients of the latter type do not 575 | negotiate ECH. Instead, they generate a dummy ECH extension that is ignored by 576 | the server. (See {{dont-stick-out}} for an explanation.) The client offers ECH 577 | if it is in possession of a compatible ECH configuration and sends GREASE ECH 578 | (see {{grease-ech}}) otherwise. 579 | 580 | ## Offering ECH {#real-ech} 581 | 582 | To offer ECH, the client first chooses a suitable ECHConfig from the server's 583 | ECHConfigList. To determine if a given `ECHConfig` is suitable, it checks that 584 | it supports the KEM algorithm identified by `ECHConfig.contents.kem_id`, at 585 | least one KDF/AEAD algorithm identified by `ECHConfig.contents.cipher_suites`, 586 | and the version of ECH indicated by `ECHConfig.contents.version`. Once a 587 | suitable configuration is found, the client selects the cipher suite it will 588 | use for encryption. It MUST NOT choose a cipher suite or version not advertised 589 | by the configuration. If no compatible configuration is found, then the client 590 | SHOULD proceed as described in {{grease-ech}}. 591 | 592 | Next, the client constructs the ClientHelloInner message just as it does a 593 | standard ClientHello, with the exception of the following rules: 594 | 595 | 1. It MUST NOT offer to negotiate TLS 1.2 or below. This is necessary to ensure 596 | the backend server does not negotiate a TLS version that is incompatible with 597 | ECH. 598 | 1. It MUST NOT offer to resume any session for TLS 1.2 and below. 599 | 1. If it intends to compress any extensions (see {{encoding-inner}}), it MUST 600 | order those extensions consecutively. 601 | 1. It MUST include the "encrypted_client_hello" extension of type `inner` as 602 | described in {{encrypted-client-hello}}. (This requirement is not applicable 603 | when the "encrypted_client_hello" extension is generated as described in 604 | {{grease-ech}}.) 605 | 606 | The client then constructs EncodedClientHelloInner as described in 607 | {{encoding-inner}}. It also computes an HPKE encryption context and `enc` value 608 | as: 609 | 610 | ~~~ 611 | pkR = DeserializePublicKey(ECHConfig.contents.public_key) 612 | enc, context = SetupBaseS(pkR, 613 | "tls ech" || 0x00 || ECHConfig) 614 | ~~~ 615 | 616 | Next, it constructs a partial ClientHelloOuterAAD as it does a standard 617 | ClientHello, with the exception of the following rules: 618 | 619 | 1. It MUST offer to negotiate TLS 1.3 or above. 620 | 1. If it compressed any extensions in EncodedClientHelloInner, it MUST copy the 621 | corresponding extensions from ClientHelloInner. The copied extensions 622 | additionally MUST be in the same relative order as in ClientHelloInner. 623 | 1. It MUST copy the legacy\_session\_id field from ClientHelloInner. This 624 | allows the server to echo the correct session ID for TLS 1.3's compatibility 625 | mode (see {{Appendix D.4 of RFC8446}}) when ECH is negotiated. Note that 626 | compatibility mode is not used in DTLS 1.3, but following this rule will 627 | produce the correct results for both TLS 1.3 and DTLS 1.3. 628 | 1. It MAY copy any other field from the ClientHelloInner except 629 | ClientHelloInner.random. Instead, It MUST generate a fresh 630 | ClientHelloOuter.random using a secure random number generator. (See 631 | {{flow-client-reaction}}.) 632 | 1. It SHOULD place the value of `ECHConfig.contents.public_name` in the 633 | "server_name" extension. Clients that do not follow this step, or place a 634 | different value in the "server_name" extension, risk breaking the retry 635 | mechanism described in {{rejected-ech}} or failing to interoperate with 636 | servers that require this step to be done; see {{client-facing-server}}. 637 | 1. When the client offers the "pre_shared_key" extension in ClientHelloInner, it 638 | SHOULD also include a GREASE "pre_shared_key" extension in ClientHelloOuter, 639 | generated in the manner described in {{grease-psk}}. The client MUST NOT use 640 | this extension to advertise a PSK to the client-facing server. (See 641 | {{flow-clienthello-malleability}}.) When the client includes a GREASE 642 | "pre_shared_key" extension, it MUST also copy the "psk_key_exchange_modes" 643 | from the ClientHelloInner into the ClientHelloOuter. 644 | 1. When the client offers the "early_data" extension in ClientHelloInner, it 645 | MUST also include the "early_data" extension in ClientHelloOuter. This 646 | allows servers that reject ECH and use ClientHelloOuter to safely ignore any 647 | early data sent by the client per {{RFC8446, Section 4.2.10}}. 648 | 649 | The client might duplicate non-sensitive extensions in both messages. However, 650 | implementations need to take care to ensure that sensitive extensions are not 651 | offered in the ClientHelloOuter. See {{outer-clienthello}} for additional 652 | guidance. 653 | 654 | Finally, the client encrypts the EncodedClientHelloInner with the above values, 655 | as described in {{encrypting-clienthello}}, to construct a ClientHelloOuter. It 656 | sends this to the server, and processes the response as described in 657 | {{determining-ech-acceptance}}. 658 | 659 | ### Encrypting the ClientHello {#encrypting-clienthello} 660 | 661 | Given an EncodedClientHelloInner, an HPKE encryption context and `enc` value, 662 | and a partial ClientHelloOuterAAD, the client constructs a ClientHelloOuter as 663 | follows. 664 | 665 | First, the client determines the length L of encrypting EncodedClientHelloInner 666 | with the selected HPKE AEAD. This is typically the sum of the plaintext length 667 | and the AEAD tag length. The client then completes the ClientHelloOuterAAD with 668 | an "encrypted_client_hello" extension. This extension value contains the outer 669 | variant of ECHClientHello with the following fields: 670 | 671 | - `config_id`, the identifier corresponding to the chosen ECHConfig structure; 672 | - `cipher_suite`, the client's chosen cipher suite; 673 | - `enc`, as given above; and 674 | - `payload`, a placeholder byte string containing L zeros. 675 | 676 | If configuration identifiers (see {{ignored-configs}}) are to be 677 | ignored, `config_id` SHOULD be set to a randomly generated byte in the 678 | first ClientHelloOuter and, in the event of a HelloRetryRequest (HRR), 679 | MUST be left unchanged for the second ClientHelloOuter. 680 | 681 | The client serializes this structure to construct the ClientHelloOuterAAD. 682 | It then computes the final payload as: 683 | 684 | ~~~ 685 | final_payload = context.Seal(ClientHelloOuterAAD, 686 | EncodedClientHelloInner) 687 | ~~~ 688 | 689 | Including `ClientHelloOuterAAD` as the HPKE AAD binds the `ClientHelloOuter` 690 | to the `ClientHelloInner`, thus preventing attackers from modifying 691 | `ClientHelloOuter` while keeping the same `ClientHelloInner`, as described in 692 | {{flow-clienthello-malleability}}. 693 | 694 | Finally, the client replaces `payload` with `final_payload` to obtain 695 | ClientHelloOuter. The two values have the same length, so it is not necessary 696 | to recompute length prefixes in the serialized structure. 697 | 698 | Note this construction requires the "encrypted_client_hello" be computed after 699 | all other extensions. This is possible because the ClientHelloOuter's 700 | "pre_shared_key" extension is either omitted, or uses a random binder 701 | ({{grease-psk}}). 702 | 703 | ### GREASE PSK {#grease-psk} 704 | 705 | When offering ECH, the client is not permitted to advertise PSK identities in 706 | the ClientHelloOuter. However, the client can send a "pre_shared_key" extension 707 | in the ClientHelloInner. In this case, when resuming a session with the client, 708 | the backend server sends a "pre_shared_key" extension in its ServerHello. This 709 | would appear to a network observer as if the server were sending this 710 | extension without solicitation, which would violate the extension rules 711 | described in {{RFC8446}}. When offering a PSK in ClientHelloInner, 712 | clients SHOULD send a GREASE "pre_shared_key" extension in the 713 | ClientHelloOuter to make it appear to the network as if the extension were 714 | negotiated properly. 715 | 716 | The client generates the extension payload by constructing an `OfferedPsks` 717 | structure (see {{RFC8446, Section 4.2.11}}) as follows. For each PSK identity 718 | advertised in the ClientHelloInner, the client generates a random PSK identity 719 | with the same length. It also generates a random, 32-bit, unsigned integer to 720 | use as the `obfuscated_ticket_age`. Likewise, for each inner PSK binder, the 721 | client generates a random string of the same length. 722 | 723 | Per the rules of {{real-ech}}, the server is not permitted to resume a 724 | connection in the outer handshake. If ECH is rejected and the client-facing 725 | server replies with a "pre_shared_key" extension in its ServerHello, then the 726 | client MUST abort the handshake with an "illegal_parameter" alert. 727 | 728 | ### Recommended Padding Scheme {#padding} 729 | 730 | If the ClientHelloInner is encrypted without padding, then the length of 731 | the `ClientHelloOuter.payload` can leak information about `ClientHelloInner`. 732 | In order to prevent this the `EncodedClientHelloInner` structure 733 | has a padding field. This section describes a deterministic mechanism for 734 | computing the required amount of padding based on the following 735 | observation: individual extensions can reveal sensitive information through 736 | their length. Thus, each extension in the inner ClientHello may require 737 | different amounts of padding. This padding may be fully determined by the 738 | client's configuration or may require server input. 739 | 740 | By way of example, clients typically support a small number of application 741 | profiles. For instance, a browser might support HTTP with ALPN values 742 | ["http/1.1", "h2"] and WebRTC media with ALPNs ["webrtc", "c-webrtc"]. Clients 743 | SHOULD pad this extension by rounding up to the total size of the longest ALPN 744 | extension across all application profiles. The target padding length of most 745 | ClientHello extensions can be computed in this way. 746 | 747 | In contrast, clients do not know the longest SNI value in the client-facing 748 | server's anonymity set without server input. Clients SHOULD use the ECHConfig's 749 | `maximum_name_length` field as follows, where L is the `maximum_name_length` 750 | value. 751 | 752 | 1. If the ClientHelloInner contained a "server_name" extension with a name of 753 | length D, add max(0, L - D) bytes of padding. 754 | 2. If the ClientHelloInner did not contain a "server_name" extension (e.g., if 755 | the client is connecting to an IP address), add L + 9 bytes of padding. This 756 | is the length of a "server_name" extension with an L-byte name. 757 | 758 | Finally, the client SHOULD pad the entire message as follows: 759 | 760 | 1. Let L be the length of the EncodedClientHelloInner with all the padding 761 | computed so far. 762 | 2. Let N = 31 - ((L - 1) % 32) and add N bytes of padding. 763 | 764 | This rounds the length of EncodedClientHelloInner up to a multiple of 32 bytes, 765 | reducing the set of possible lengths across all clients. 766 | 767 | In addition to padding ClientHelloInner, clients and servers will also need to 768 | pad all other handshake messages that have sensitive-length fields. For example, 769 | if a client proposes ALPN values in ClientHelloInner, the server-selected value 770 | will be returned in an EncryptedExtension, so that handshake message also needs 771 | to be padded using TLS record layer padding. 772 | 773 | ### Determining ECH Acceptance {#determining-ech-acceptance} 774 | 775 | As described in {{server-behavior}}, the server may either accept ECH and use 776 | ClientHelloInner or reject it and use ClientHelloOuter. This is determined by 777 | the server's initial message. 778 | 779 | If the message does not negotiate TLS 1.3 or higher, the server has rejected 780 | ECH. Otherwise, it is either a ServerHello or HelloRetryRequest. 781 | 782 | If the message is a ServerHello, the client computes `accept_confirmation` as 783 | described in {{backend-server}}. If this value matches the last 8 bytes of 784 | `ServerHello.random`, the server has accepted ECH. Otherwise, it has rejected 785 | ECH. 786 | 787 | If the message is a HelloRetryRequest, the client checks for the 788 | "encrypted_client_hello" extension. If none is found, the server has rejected 789 | ECH. Otherwise, if it has a length other than 8, the client aborts the handshake 790 | with a "decode_error" alert. Otherwise, the client computes 791 | `hrr_accept_confirmation` as described in {{backend-server-hrr}}. If this value 792 | matches the extension payload, the server has accepted ECH. Otherwise, it has 793 | rejected ECH. 794 | 795 | If the server accepts ECH, the client handshakes with ClientHelloInner as 796 | described in {{accepted-ech}}. Otherwise, the client handshakes with 797 | ClientHelloOuter as described in {{rejected-ech}}. 798 | 799 | ### Handshaking with ClientHelloInner {#accepted-ech} 800 | 801 | If the server accepts ECH, the client proceeds with the connection as in 802 | {{RFC8446}}, with the following modifications: 803 | 804 | The client behaves as if it had sent ClientHelloInner as the ClientHello. That 805 | is, it evaluates the handshake using the ClientHelloInner's preferences, and, 806 | when computing the transcript hash ({{Section 4.4.1 of RFC8446}}), it uses 807 | ClientHelloInner as the first ClientHello. 808 | 809 | If the server responds with a HelloRetryRequest, the client computes the updated 810 | ClientHello message as follows: 811 | 812 | 1. It computes a second ClientHelloInner based on the first ClientHelloInner, as 813 | in {{Section 4.1.4 of RFC8446}}. The ClientHelloInner's 814 | "encrypted_client_hello" extension is left unmodified. 815 | 816 | 1. It constructs EncodedClientHelloInner as described in {{encoding-inner}}. 817 | 818 | 1. It constructs a second partial ClientHelloOuterAAD message. This message MUST 819 | be syntactically valid. The extensions MAY be copied from the original 820 | ClientHelloOuter unmodified, or omitted. If not sensitive, the client MAY 821 | copy updated extensions from the second ClientHelloInner for compression. 822 | 823 | 1. It encrypts EncodedClientHelloInner as described in 824 | {{encrypting-clienthello}}, using the second partial ClientHelloOuterAAD, to 825 | obtain a second ClientHelloOuter. It reuses the original HPKE encryption 826 | context computed in {{real-ech}} and uses the empty string for `enc`. 827 | 828 | The HPKE context maintains a sequence number, so this operation internally 829 | uses a fresh nonce for each AEAD operation. Reusing the HPKE context avoids 830 | an attack described in {{flow-hrr-hijack}}. 831 | 832 | 833 | The client then sends the second ClientHelloOuter to the server. However, as 834 | above, it uses the second ClientHelloInner for preferences, and both the 835 | ClientHelloInner messages for the transcript hash. Additionally, it checks the 836 | resulting ServerHello for ECH acceptance as in {{determining-ech-acceptance}}. 837 | If the ServerHello does not also indicate ECH acceptance, the client MUST 838 | terminate the connection with an "illegal_parameter" alert. 839 | 840 | ### Handshaking with ClientHelloOuter {#rejected-ech} 841 | 842 | If the server rejects ECH, the client proceeds with the handshake, 843 | authenticating for ECHConfig.contents.public_name as described in 844 | {{auth-public-name}}. If authentication or the handshake fails, the client MUST 845 | return a failure to the calling application. It MUST NOT use the retry 846 | configurations. It MUST NOT treat this as a secure signal to 847 | disable ECH. 848 | 849 | If the server supplied an "encrypted_client_hello" extension in its 850 | EncryptedExtensions message, the client MUST check that it is syntactically 851 | valid and the client MUST abort the connection with a "decode_error" alert 852 | otherwise. If an earlier TLS version was negotiated, the client MUST NOT enable 853 | the False Start optimization {{RFC7918}} for this handshake. If both 854 | authentication and the handshake complete successfully, the client MUST perform 855 | the processing described below then abort the connection with an "ech_required" 856 | alert before sending any application data to the server. 857 | 858 | If the server provided "retry_configs" and if at least one of the 859 | values contains a version supported by the client, the client can 860 | regard the ECH configuration as securely replaced by the server. It 861 | SHOULD retry the handshake with a new transport connection, using the 862 | retry configurations supplied by the server. 863 | 864 | Clients can implement a new transport connection in a way that best 865 | suits their deployment. For example, clients can reuse the same server 866 | IP address when establishing the new transport connection or they can 867 | choose to use a different IP address if provided with options from 868 | DNS. ECH does not mandate any specific implementation choices when 869 | establishing this new connection. 870 | 871 | The retry configurations are meant to be used for retried connections. Further 872 | use of retry configurations could yield a tracking vector. In settings where 873 | the client will otherwise already let the server track the client, e.g., 874 | because the client will send cookies to the server in parallel connections, 875 | using the retry configurations for these parallel connections does not 876 | introduce a new tracking vector. 877 | 878 | If none of the values provided in "retry_configs" contains a supported 879 | version, the server did not supply an "encrypted_client_hello" 880 | extension in its EncryptedExtensions message, or an earlier TLS 881 | version was negotiated, the client can regard ECH as securely disabled 882 | by the server, and it SHOULD retry the handshake with a new transport 883 | connection and ECH disabled. 884 | 885 | Clients SHOULD NOT accept "retry_config" in response to a connection 886 | initiated in response to a "retry_config". Sending a "retry_config" 887 | in this situation is a signal that the server is misconfigured, e.g., 888 | the server might have multiple inconsistent configurations so that the 889 | client reached a node with configuration A in the first connection and 890 | a node with configuration B in the second. Note that this guidance 891 | does not apply to the cases in the previous paragraph where the server 892 | has securely disabled ECH. 893 | 894 | If a client does not retry, it MUST report an error to the calling 895 | application. 896 | 897 | ### Authenticating for the Public Name {#auth-public-name} 898 | 899 | When the server rejects ECH, it continues with the handshake using the plaintext 900 | "server_name" extension instead (see {{server-behavior}}). Clients that offer 901 | ECH then authenticate the connection with the public name, as follows: 902 | 903 | - The client MUST verify that the certificate is valid for 904 | ECHConfig.contents.public_name. If invalid, it MUST abort the connection with 905 | the appropriate alert. 906 | 907 | - If the server requests a client certificate, the client MUST respond with an 908 | empty Certificate message, denoting no client certificate. 909 | 910 | In verifying the client-facing server certificate, the client MUST 911 | interpret the public name as a DNS-based reference identity 912 | {{!RFC6125}}. Clients that incorporate DNS names and IP addresses into 913 | the same syntax (e.g. {{Section 7.4 of ?RFC3986}} and {{WHATWG-IPV4}}) 914 | MUST reject names that would be interpreted as IPv4 addresses. 915 | Clients that enforce this by checking ECHConfig.contents.public_name 916 | do not need to repeat the check when processing ECH rejection. 917 | 918 | Note that authenticating a connection for the public name does not authenticate 919 | it for the origin. The TLS implementation MUST NOT report such connections as 920 | successful to the application. It additionally MUST ignore all session tickets 921 | and session IDs presented by the server. These connections are only used to 922 | trigger retries, as described in {{rejected-ech}}. This may be implemented, for 923 | instance, by reporting a failed connection with a dedicated error code. 924 | 925 | Prior to attempting a connection, a client SHOULD validate the `ECHConfig`. 926 | Clients SHOULD ignore any 927 | `ECHConfig` structure with a public_name that is not a valid host name in 928 | preferred name syntax (see {{Section 2 of ?DNS-TERMS=RFC9499}}). That is, to be 929 | valid, the public_name needs to be a dot-separated sequence of LDH labels, as 930 | defined in {{Section 2.3.1 of !RFC5890}}, where: 931 | 932 | * the sequence does not begin or end with an ASCII dot, and 933 | * all labels are at most 63 octets. 934 | 935 | Clients additionally SHOULD ignore the structure if the final LDH 936 | label either consists of all ASCII digits (i.e. '0' through '9') or is 937 | "0x" or "0X" followed by some, possibly empty, sequence of ASCII 938 | hexadecimal digits (i.e. '0' through '9', 'a' through 'f', and 'A' 939 | through 'F'). This avoids public_name values that may be interpreted 940 | as IPv4 literals. 941 | 942 | ### Impact of Retry on Future Connections 943 | 944 | Clients MAY use information learned from a rejected ECH for future 945 | connections to avoid repeatedly connecting to the same server and 946 | being forced to retry. However, they MUST handle ECH rejection for 947 | those connections as if it were a fresh connection, rather than 948 | enforcing the single retry limit from {{rejected-ech}}. The reason 949 | for this requirement is that if the server sends a "retry_config" 950 | and then immediately rejects the resulting connection, it is 951 | most likely misconfigured. However, if the server sends a "retry_config" 952 | and then the client tries to use that to connect some time 953 | later, it is possible that the server has changed 954 | its configuration again and is now trying to recover. 955 | 956 | Any persisted information MUST be associated with the ECHConfig source 957 | used to bootstrap the connection, such as a DNS SVCB ServiceMode record 958 | {{ECH-IN-DNS}}. Clients MUST limit any sharing of persisted ECH-related 959 | state to connections that use the same ECHConfig source. Otherwise, it 960 | might become possible for the client to have the wrong public name for 961 | the server, making recovery impossible. 962 | 963 | ECHConfigs learned from ECH rejection can be used as a tracking 964 | vector. Clients SHOULD impose the same lifetime and scope restrictions 965 | that they apply to other server-based 966 | tracking vectors such as PSKs. 967 | 968 | In general, the safest way for clients to minimize ECH retries is to 969 | comply with any freshness rules (e.g., DNS TTLs) imposed by the ECH 970 | configuration. 971 | 972 | 973 | ## GREASE ECH {#grease-ech} 974 | 975 | The GREASE ECH mechanism allows a connection between an ECH-capable client 976 | and a non-ECH server to appear to use ECH, thus reducing the extent to 977 | which ECH connections stick out (see {{dont-stick-out}}). 978 | 979 | ### Client Greasing 980 | 981 | If the client attempts to connect to a server and does not have an ECHConfig 982 | structure available for the server, it SHOULD send a GREASE {{?RFC8701}} 983 | "encrypted_client_hello" extension in the first ClientHello as follows: 984 | 985 | - Set the `config_id` field to a random byte. 986 | 987 | - Set the `cipher_suite` field to a supported HpkeSymmetricCipherSuite. The 988 | selection SHOULD vary to exercise all supported configurations, but MAY be 989 | held constant for successive connections to the same server in the same 990 | session. 991 | 992 | - Set the `enc` field to a randomly-generated valid encapsulated public key 993 | output by the HPKE KEM. 994 | 995 | - Set the `payload` field to a randomly-generated string of L+C bytes, where C 996 | is the ciphertext expansion of the selected AEAD scheme and L is the size of 997 | the EncodedClientHelloInner the client would compute when offering ECH, padded 998 | according to {{padding}}. 999 | 1000 | If sending a second ClientHello in response to a HelloRetryRequest, the 1001 | client copies the entire "encrypted_client_hello" extension from the first 1002 | ClientHello. The identical value will reveal to an observer that the value of 1003 | "encrypted_client_hello" was fake, but this only occurs if there is a 1004 | HelloRetryRequest. 1005 | 1006 | If the server sends an "encrypted_client_hello" extension in either 1007 | HelloRetryRequest or EncryptedExtensions, the client MUST check the extension 1008 | syntactically and abort the connection with a "decode_error" alert if it is 1009 | invalid. It otherwise ignores the extension. It MUST NOT save the 1010 | "retry_configs" value in EncryptedExtensions. 1011 | 1012 | Offering a GREASE extension is not considered offering an encrypted ClientHello 1013 | for purposes of requirements in {{real-ech}}. In particular, the client 1014 | MAY offer to resume sessions established without ECH. 1015 | 1016 | ### Server Greasing 1017 | 1018 | {{config-extensions-iana}} describes a set of Reserved extensions 1019 | which will never be registered. These can be used by servers to 1020 | "grease" the contents of the ECH configuration, as inspired by 1021 | {{?RFC8701}}. This helps ensure clients process ECH extensions 1022 | correctly. When constructing ECH configurations, servers SHOULD 1023 | randomly select from reserved values with the high-order bit 1024 | clear. Correctly-implemented client will ignore those extensions. 1025 | 1026 | The reserved values with the high-order bit set are mandatory, as 1027 | defined in {{config-extensions}}. Servers SHOULD randomly select from 1028 | these values and include them in extraneous ECH configurations. 1029 | Correctly-implemented clients will ignore these configurations because 1030 | they do not recognize the mandatory extension. Servers SHOULD ensure 1031 | that any client using these configurations encounters a warning or error 1032 | message. This can be accomplished in several ways, including: 1033 | 1034 | * By giving the extraneous configurations distinctive config IDs or 1035 | public names, and rejecting the TLS connection or inserting an 1036 | application-level warning message when these are observed. 1037 | 1038 | * By giving the extraneous configurations an invalid public 1039 | key and a public name not associated with the server, so that 1040 | the initial ClientHelloOuter will not be decryptable and 1041 | the server cannot perform the recovery flow described 1042 | in {{rejected-ech}}. 1043 | 1044 | # Server Behavior {#server-behavior} 1045 | 1046 | As described in {{topologies}}, servers can play two roles, either as 1047 | the client-facing server or as the back-end server. 1048 | Depending on the server role, the `ECHClientHello` will be different: 1049 | 1050 | * A client-facing server expects a `ECHClientHello.type` of `outer`, and 1051 | proceeds as described in {{client-facing-server}} to extract a 1052 | ClientHelloInner, if available. 1053 | 1054 | * A backend server expects a `ECHClientHello.type` of `inner`, and 1055 | proceeds as described in {{backend-server}}. 1056 | 1057 | In split mode, a client-facing server which receives a `ClientHello` 1058 | with `ECHClientHello.type` of `inner` MUST abort with an 1059 | "illegal_parameter" alert. Similarly, in split mode, a backend server 1060 | which receives a `ClientHello` with `ECHClientHello.type` of `outer` 1061 | MUST abort with an "illegal_parameter" alert. 1062 | 1063 | In shared mode, a server plays both roles, first decrypting the 1064 | `ClientHelloOuter` and then using the contents of the 1065 | `ClientHelloInner`. A shared mode server which receives a 1066 | `ClientHello` with `ECHClientHello.type` of `inner` MUST abort with an 1067 | "illegal_parameter" alert, because such a `ClientHello` should never 1068 | be received directly from the network. 1069 | 1070 | If `ECHClientHello.type` is not a valid `ECHClientHelloType`, then 1071 | the server MUST abort with an "illegal_parameter" alert. 1072 | 1073 | If the "encrypted_client_hello" is not present, then the server completes the 1074 | handshake normally, as described in {{RFC8446}}. 1075 | 1076 | 1077 | ## Client-Facing Server {#client-facing-server} 1078 | 1079 | Upon receiving an "encrypted_client_hello" extension in an initial 1080 | ClientHello, the client-facing server determines if it will accept ECH, prior 1081 | to negotiating any other TLS parameters. Note that successfully decrypting the 1082 | extension will result in a new ClientHello to process, so even the client's TLS 1083 | version preferences may have changed. 1084 | 1085 | First, the server collects a set of candidate ECHConfig values. This list is 1086 | determined by one of the two following methods: 1087 | 1088 | 1. Compare ECHClientHello.config_id against identifiers of each known ECHConfig 1089 | and select the ones that match, if any, as candidates. 1090 | 2. Collect all known ECHConfig values as candidates, with trial decryption 1091 | below determining the final selection. 1092 | 1093 | Some uses of ECH, such as local discovery mode, may randomize the 1094 | ECHClientHello.config_id since it can be used as a tracking vector. In such 1095 | cases, the second method SHOULD be used for matching the ECHClientHello to a 1096 | known ECHConfig. See {{ignored-configs}}. Unless specified by the application 1097 | profile or otherwise externally configured, implementations MUST use the first 1098 | method. 1099 | 1100 | The server then iterates over the candidate ECHConfig values, attempting to 1101 | decrypt the "encrypted_client_hello" extension as follows. 1102 | 1103 | The server verifies that the ECHConfig supports the cipher suite indicated by 1104 | the ECHClientHello.cipher_suite and that the version of ECH indicated by the 1105 | client matches the ECHConfig.version. If not, the server continues to the next 1106 | candidate ECHConfig. 1107 | 1108 | Next, the server decrypts ECHClientHello.payload, using the private key skR 1109 | corresponding to ECHConfig, as follows: 1110 | 1111 | ~~~ 1112 | context = SetupBaseR(ECHClientHello.enc, skR, 1113 | "tls ech" || 0x00 || ECHConfig) 1114 | EncodedClientHelloInner = context.Open(ClientHelloOuterAAD, 1115 | ECHClientHello.payload) 1116 | ~~~ 1117 | 1118 | ClientHelloOuterAAD is computed from ClientHelloOuter as described in 1119 | {{authenticating-outer}}. The `info` parameter to SetupBaseR is the 1120 | concatenation "tls ech", a zero byte, and the serialized ECHConfig. If 1121 | decryption fails, the server continues to the next candidate ECHConfig. 1122 | Otherwise, the server reconstructs ClientHelloInner from 1123 | EncodedClientHelloInner, as described in {{encoding-inner}}. It then stops 1124 | iterating over the candidate ECHConfig values. 1125 | 1126 | Once the server has chosen the correct ECHConfig, it MAY verify that the value 1127 | in the ClientHelloOuter "server_name" extension matches the value of 1128 | ECHConfig.contents.public_name, and abort with an "illegal_parameter" alert if 1129 | these do not match. This optional check allows the server to limit ECH 1130 | connections to only use the public SNI values advertised in its ECHConfigs. 1131 | The server MUST be careful not to unnecessarily reject connections if the same 1132 | ECHConfig id or keypair is used in multiple ECHConfigs with distinct public 1133 | names. 1134 | 1135 | Upon determining the ClientHelloInner, the client-facing server checks that the 1136 | message includes a well-formed "encrypted_client_hello" extension of type 1137 | `inner` and that it does not offer TLS 1.2 or below. If either of these checks 1138 | fails, the client-facing server MUST abort with an "illegal_parameter" alert. 1139 | 1140 | If these checks succeed, the client-facing server then forwards the 1141 | ClientHelloInner to the appropriate backend server, which proceeds as in 1142 | {{backend-server}}. If the backend server responds with a HelloRetryRequest, the 1143 | client-facing server forwards it, decrypts the client's second ClientHelloOuter 1144 | using the procedure in {{client-facing-server-hrr}}, and forwards the resulting 1145 | second ClientHelloInner. The client-facing server forwards all other TLS 1146 | messages between the client and backend server unmodified. 1147 | 1148 | Otherwise, if all candidate ECHConfig values fail to decrypt the extension, the 1149 | client-facing server MUST ignore the extension and proceed with the connection 1150 | using ClientHelloOuter, with the following modifications: 1151 | 1152 | * If sending a HelloRetryRequest, the server MAY include an 1153 | "encrypted_client_hello" extension with a payload of 8 random bytes; see 1154 | {{dont-stick-out}} for details. 1155 | 1156 | * If the server is configured with any ECHConfigs, it MUST include the 1157 | "encrypted_client_hello" extension in its EncryptedExtensions with the 1158 | "retry_configs" field set to one or more ECHConfig structures with up-to-date 1159 | keys. Servers MAY supply multiple ECHConfig values of different versions. 1160 | This allows a server to support multiple versions at once. 1161 | 1162 | Note that decryption failure could indicate a GREASE ECH extension (see 1163 | {{grease-ech}}), so it is necessary for servers to proceed with the connection 1164 | and rely on the client to abort if ECH was required. In particular, the 1165 | unrecognized value alone does not indicate a misconfigured ECH advertisement 1166 | ({{misconfiguration}}). Instead, servers can measure occurrences of the 1167 | "ech_required" alert to detect this case. 1168 | 1169 | ### Sending HelloRetryRequest {#client-facing-server-hrr} 1170 | 1171 | After sending or forwarding a HelloRetryRequest, the client-facing server does 1172 | not repeat the steps in {{client-facing-server}} with the second 1173 | ClientHelloOuter. Instead, it continues with the ECHConfig selection from the 1174 | first ClientHelloOuter as follows: 1175 | 1176 | If the client-facing server accepted ECH, it checks the second ClientHelloOuter 1177 | also contains the "encrypted_client_hello" extension. If not, it MUST abort the 1178 | handshake with a "missing_extension" alert. Otherwise, it checks that 1179 | ECHClientHello.cipher_suite and ECHClientHello.config_id are unchanged, and that 1180 | ECHClientHello.enc is empty. If not, it MUST abort the handshake with an 1181 | "illegal_parameter" alert. 1182 | 1183 | Finally, it decrypts the new ECHClientHello.payload as a second message with the 1184 | previous HPKE context: 1185 | 1186 | ~~~ 1187 | EncodedClientHelloInner = context.Open(ClientHelloOuterAAD, 1188 | ECHClientHello.payload) 1189 | ~~~ 1190 | 1191 | ClientHelloOuterAAD is computed as described in {{authenticating-outer}}, but 1192 | using the second ClientHelloOuter. If decryption fails, the client-facing 1193 | server MUST abort the handshake with a "decrypt_error" alert. Otherwise, it 1194 | reconstructs the second ClientHelloInner from the new EncodedClientHelloInner 1195 | as described in {{encoding-inner}}, using the second ClientHelloOuter for 1196 | any referenced extensions. 1197 | 1198 | The client-facing server then forwards the resulting ClientHelloInner to the 1199 | backend server. It forwards all subsequent TLS messages between the client and 1200 | backend server unmodified. 1201 | 1202 | If the client-facing server rejected ECH, or if the first ClientHello did not 1203 | include an "encrypted_client_hello" extension, the client-facing server 1204 | proceeds with the connection as usual. The server does not decrypt the 1205 | second ClientHello's ECHClientHello.payload value, if there is one. 1206 | Moreover, if the server is configured with any ECHConfigs, it MUST include the 1207 | "encrypted_client_hello" extension in its EncryptedExtensions with the 1208 | "retry_configs" field set to one or more ECHConfig structures with up-to-date 1209 | keys, as described in {{client-facing-server}}. 1210 | 1211 | Note that a client-facing server that forwards the first ClientHello cannot 1212 | include its own "cookie" extension if the backend server sends a 1213 | HelloRetryRequest. This means that the client-facing server either needs to 1214 | maintain state for such a connection or it needs to coordinate with the backend 1215 | server to include any information it requires to process the second ClientHello. 1216 | 1217 | ## Backend Server {#backend-server} 1218 | 1219 | Upon receipt of an "encrypted_client_hello" extension of type `inner` in a 1220 | ClientHello, if the backend server negotiates TLS 1.3 or higher, then it MUST 1221 | confirm ECH acceptance to the client by computing its ServerHello as described 1222 | here. 1223 | 1224 | The backend server embeds in ServerHello.random a string derived from the inner 1225 | handshake. It begins by computing its ServerHello as usual, except the last 8 1226 | bytes of ServerHello.random are set to zero. It then computes the transcript 1227 | hash for ClientHelloInner up to and including the modified ServerHello, as 1228 | described in {{RFC8446, Section 4.4.1}}. Let transcript_ech_conf denote the 1229 | output. Finally, the backend server overwrites the last 8 bytes of the 1230 | ServerHello.random with the following string: 1231 | 1232 | ~~~ 1233 | accept_confirmation = HKDF-Expand-Label( 1234 | HKDF-Extract(0, ClientHelloInner.random), 1235 | "ech accept confirmation", 1236 | transcript_ech_conf, 1237 | 8) 1238 | ~~~ 1239 | 1240 | where HKDF-Expand-Label is defined in {{RFC8446, Section 7.1}}, "0" indicates a 1241 | string of Hash.length bytes set to zero, and Hash is the hash function used to 1242 | compute the transcript hash. In DTLS, the modified version of HKDF-Expand-Label 1243 | defined in {{RFC9147, Section 5.9}} is used instead. 1244 | 1245 | The backend server MUST NOT perform this operation if it negotiated TLS 1.2 or 1246 | below. Note that doing so would overwrite the downgrade signal for TLS 1.3 (see 1247 | {{RFC8446, Section 4.1.3}}). 1248 | 1249 | ### Sending HelloRetryRequest {#backend-server-hrr} 1250 | 1251 | When the backend server sends HelloRetryRequest in response to the ClientHello, 1252 | it similarly confirms ECH acceptance by adding a confirmation signal to its 1253 | HelloRetryRequest. But instead of embedding the signal in the 1254 | HelloRetryRequest.random (the value of which is specified by {{RFC8446}}), it 1255 | sends the signal in an extension. 1256 | 1257 | The backend server begins by computing HelloRetryRequest as usual, except that 1258 | it also contains an "encrypted_client_hello" extension with a payload of 8 zero 1259 | bytes. It then computes the transcript hash for the first ClientHelloInner, 1260 | denoted ClientHelloInner1, up to and including the modified HelloRetryRequest. 1261 | Let transcript_hrr_ech_conf denote the output. Finally, the backend server 1262 | overwrites the payload of the "encrypted_client_hello" extension with the 1263 | following string: 1264 | 1265 | ~~~ 1266 | hrr_accept_confirmation = HKDF-Expand-Label( 1267 | HKDF-Extract(0, ClientHelloInner1.random), 1268 | "hrr ech accept confirmation", 1269 | transcript_hrr_ech_conf, 1270 | 8) 1271 | ~~~ 1272 | 1273 | In the subsequent ServerHello message, the backend server sends the 1274 | accept_confirmation value as described in {{backend-server}}. 1275 | 1276 | # Deployment Considerations {#deployment} 1277 | 1278 | The design of ECH as specified in this document necessarily requires changes 1279 | to client, client-facing server, and backend server. Coordination between 1280 | client-facing and backend server requires care, as deployment mistakes 1281 | can lead to compatibility issues. These are discussed in {{compat-issues}}. 1282 | 1283 | Beyond coordination difficulties, ECH deployments may also induce challenges 1284 | for use cases of information that ECH protects. In particular, 1285 | use cases which depend on this unencrypted information may no longer work 1286 | as desired. This is elaborated upon in {{no-sni}}. 1287 | 1288 | ## Compatibility Issues {#compat-issues} 1289 | 1290 | Unlike most TLS extensions, placing the SNI value in an ECH extension is not 1291 | interoperable with existing servers, which expect the value in the existing 1292 | plaintext extension. Thus server operators SHOULD ensure servers understand a 1293 | given set of ECH keys before advertising them. Additionally, servers SHOULD 1294 | retain support for any previously-advertised keys for the duration of their 1295 | validity. 1296 | 1297 | However, in more complex deployment scenarios, this may be difficult to fully 1298 | guarantee. Thus this protocol was designed to be robust in case of 1299 | inconsistencies between systems that advertise ECH keys and servers, at the cost 1300 | of extra round-trips due to a retry. Two specific scenarios are detailed below. 1301 | 1302 | ### Misconfiguration and Deployment Concerns {#misconfiguration} 1303 | 1304 | It is possible for ECH advertisements and servers to become inconsistent. This 1305 | may occur, for instance, from DNS misconfiguration, caching issues, or an 1306 | incomplete rollout in a multi-server deployment. This may also occur if a server 1307 | loses its ECH keys, or if a deployment of ECH must be rolled back on the server. 1308 | 1309 | The retry mechanism repairs inconsistencies, provided the TLS server 1310 | has a certificate for the public name. If server and advertised keys 1311 | mismatch, the server will reject ECH and respond with 1312 | "retry_configs". If the server does 1313 | not understand 1314 | the "encrypted_client_hello" extension at all, it will ignore it as required by 1315 | {{Section 4.1.2 of RFC8446}}. Provided the server can present a certificate 1316 | valid for the public name, the client can safely retry with updated settings, 1317 | as described in {{rejected-ech}}. 1318 | 1319 | Unless ECH is disabled as a result of successfully establishing a connection to 1320 | the public name, the client MUST NOT fall back to using unencrypted 1321 | ClientHellos, as this allows a network attacker to disclose the contents of this 1322 | ClientHello, including the SNI. It MAY attempt to use another server from the 1323 | DNS results, if one is provided. 1324 | 1325 | In order to ensure that the retry mechanism works successfully servers 1326 | SHOULD ensure that every endpoint which might receive a TLS connection 1327 | is provisioned with an appropriate certificate for the public name. 1328 | This is especially important during periods of server reconfiguration 1329 | when different endpoints might have different configurations. 1330 | 1331 | 1332 | ### Middleboxes 1333 | 1334 | The requirements in {{RFC8446, Section 9.3}} which require proxies to 1335 | act as conforming TLS client and server provide interoperability 1336 | with TLS-terminating proxies even in cases where the server supports 1337 | ECH but the proxy does not, as detailed below. 1338 | 1339 | The proxy must ignore unknown parameters, and 1340 | generate its own ClientHello containing only parameters it understands. Thus, 1341 | when presenting a certificate to the client or sending a ClientHello to the 1342 | server, the proxy will act as if connecting to the ClientHelloOuter 1343 | server_name, which SHOULD match the public name (see {{real-ech}}), without 1344 | echoing the "encrypted_client_hello" extension. 1345 | 1346 | Depending on whether the client is configured to accept the proxy's certificate 1347 | as authoritative for the public name, this may trigger the retry logic described 1348 | in {{rejected-ech}} or result in a connection failure. A proxy which is not 1349 | authoritative for the public name cannot forge a signal to disable ECH. 1350 | 1351 | ## Deployment Impact {#no-sni} 1352 | 1353 | Some use cases which depend on information ECH encrypts may break with the 1354 | deployment of ECH. The extent of breakage depends on a number of external 1355 | factors, including, for example, whether ECH can be disabled, whether or not 1356 | the party disabling ECH is trusted to do so, and whether or not client 1357 | implementations will fall back to TLS without ECH in the event of disablement. 1358 | 1359 | Depending on implementation details and deployment settings, use cases 1360 | which depend on plaintext TLS information may require fundamentally different 1361 | approaches to continue working. For example, in managed enterprise settings, 1362 | one approach may be to disable ECH entirely via group policy and for 1363 | client implementations to honor this action. Server deployments which 1364 | depend on SNI -- e.g., for load balancing -- may no longer function properly 1365 | without updates; the nature of those updates is out of scope of this 1366 | specification. 1367 | 1368 | In the context of {{rejected-ech}}, another approach may be to 1369 | intercept and decrypt client TLS connections. The feasibility of alternative 1370 | solutions is specific to individual deployments. 1371 | 1372 | # Compliance Requirements {#compliance} 1373 | 1374 | In the absence of an application profile standard specifying otherwise, 1375 | a compliant ECH application MUST implement the following HPKE cipher suite: 1376 | 1377 | - KEM: DHKEM(X25519, HKDF-SHA256) (see {{Section 7.1 of HPKE}}) 1378 | - KDF: HKDF-SHA256 (see {{Section 7.2 of HPKE}}) 1379 | - AEAD: AES-128-GCM (see {{Section 7.3 of HPKE}}) 1380 | 1381 | # Security Considerations 1382 | 1383 | This section contains security considerations for ECH. 1384 | 1385 | ## Security and Privacy Goals {#goals} 1386 | 1387 | ECH considers two types of attackers: passive and active. Passive attackers can 1388 | read packets from the network, but they cannot perform any sort of active 1389 | behavior such as probing servers or querying DNS. A middlebox that filters based 1390 | on plaintext packet contents is one example of a passive attacker. In contrast, 1391 | active attackers can also write packets into the network for malicious purposes, 1392 | such as interfering with existing connections, probing servers, and querying 1393 | DNS. In short, an active attacker corresponds to the conventional threat model 1394 | {{?RFC3552}} for TLS 1.3 {{RFC8446}}. 1395 | 1396 | Passive and active attackers can exist anywhere in the network, including 1397 | between the client and client-facing server, as well as between the 1398 | client-facing and backend servers when running ECH in Split Mode. However, 1399 | for Split Mode in particular, ECH makes two additional assumptions: 1400 | 1401 | 1. The channel between each client-facing and each backend server is 1402 | authenticated such that the backend server only accepts messages from trusted 1403 | client-facing servers. The exact mechanism for establishing this authenticated 1404 | channel is out of scope for this document. 1405 | 1. The attacker cannot correlate messages between client and client-facing 1406 | server with messages between client-facing and backend server. Such correlation 1407 | could allow an attacker to link information unique to a backend server, such as 1408 | their server name or IP address, with a client's encrypted ClientHelloInner. 1409 | Correlation could occur through timing analysis of messages across the 1410 | client-facing server, or via examining the contents of messages sent between 1411 | client-facing and backend servers. The exact mechanism for preventing this sort 1412 | of correlation is out of scope for this document. 1413 | 1414 | Given this threat model, the primary goals of ECH are as follows. 1415 | 1416 | 1. Security preservation. Use of ECH does not weaken the security properties of 1417 | TLS without ECH. 1418 | 1. Handshake privacy. TLS connection establishment to a server name 1419 | within an anonymity set is indistinguishable from a connection to 1420 | any other server name within the anonymity set. (The anonymity set 1421 | is defined in {{intro}}.) 1422 | 1. Downgrade resistance. An attacker cannot downgrade a connection that 1423 | attempts to use ECH to one that does not use ECH. 1424 | 1425 | These properties were formally proven in {{ECH-Analysis}}. 1426 | 1427 | With regards to handshake privacy, client-facing server configuration 1428 | determines the size of the anonymity set. For example, if a 1429 | client-facing server uses distinct ECHConfig values for each server 1430 | name, then each anonymity set has size k = 1. Client-facing servers 1431 | SHOULD deploy ECH in such a way so as to maximize the size of the 1432 | anonymity set where possible. This means client-facing servers should 1433 | use the same ECHConfig for as many server names as possible. An 1434 | attacker can distinguish two server names that have different 1435 | ECHConfig values based on the ECHClientHello.config_id value. 1436 | 1437 | This also means public information in a TLS handshake should be 1438 | consistent across server names. For example, if a client-facing server 1439 | services many backend origin server names, only one of which supports some 1440 | cipher suite, it may be possible to identify that server name based on the 1441 | contents of unencrypted handshake message. Similarly, if a backend 1442 | origin reuses KeyShare values, then that provides a unique identifier 1443 | for that server. 1444 | 1445 | Beyond these primary security and privacy goals, ECH also aims to hide, to some 1446 | extent, the fact that it is being used at all. Specifically, the GREASE ECH 1447 | extension described in {{grease-ech}} does not change the security properties of 1448 | the TLS handshake at all. Its goal is to provide "cover" for the real ECH 1449 | protocol ({{real-ech}}), as a means of addressing the "do not stick out" 1450 | requirements of {{?RFC8744}}. See {{dont-stick-out}} for details. 1451 | 1452 | 1453 | ## Unauthenticated and Plaintext DNS {#plaintext-dns} 1454 | 1455 | ECH supports delivery of configurations through the DNS using SVCB or HTTPS 1456 | records, without requiring any verifiable authenticity or provenance 1457 | information {{ECH-IN-DNS}}. This means that any attacker which can inject 1458 | DNS responses or poison DNS caches, which is a common scenario in 1459 | client access networks, can supply clients with fake ECH configurations (so 1460 | that the client encrypts data to them) or strip the ECH configurations from 1461 | the response. However, in the face of an attacker that controls DNS, 1462 | no encryption scheme can work because the attacker can replace the IP 1463 | address, thus blocking client connections, or substitute a unique IP 1464 | address for each DNS name that was looked up. Thus, using DNS records 1465 | without additional authentication does not make the situation significantly 1466 | worse. 1467 | 1468 | Clearly, DNSSEC (if the client validates and hard fails) is a defense 1469 | against this form of attack, but encrypted DNS transport is also a 1470 | defense against DNS attacks by attackers on the local network, which 1471 | is a common case where ClientHello and SNI encryption are 1472 | desired. Moreover, as noted in the introduction, SNI encryption is 1473 | less useful without encryption of DNS queries in transit. 1474 | 1475 | ## Client Tracking 1476 | 1477 | A malicious client-facing server could distribute unique, per-client ECHConfig 1478 | structures as a way of tracking clients across subsequent connections. On-path 1479 | adversaries which know about these unique keys could also track clients in this 1480 | way by observing TLS connection attempts. 1481 | 1482 | The cost of this type of attack scales linearly with the desired number of 1483 | target clients. Moreover, DNS caching behavior makes targeting individual users 1484 | for extended periods of time, e.g., using per-client ECHConfig structures 1485 | delivered via HTTPS RRs with high TTLs, challenging. Clients can help mitigate 1486 | this problem by flushing any DNS or ECHConfig state upon changing networks 1487 | (this may not be possible if clients use the operating system resolver 1488 | rather than doing their own resolution). 1489 | 1490 | ECHConfig rotation rate is also an issue for non-malicious servers, 1491 | which may want to rotate keys frequently to limit exposure if the key 1492 | is compromised. Rotating too frequently limits the client anonymity 1493 | set. In practice, servers which service many server names and thus 1494 | have high loads are the best candidates to be client-facing servers 1495 | and so anonymity sets will typically involve many connections even 1496 | with fairly fast rotation intervals. 1497 | 1498 | ## Ignored Configuration Identifiers and Trial Decryption {#ignored-configs} 1499 | 1500 | Ignoring configuration identifiers may be useful in scenarios where clients and 1501 | client-facing servers do not want to reveal information about the client-facing 1502 | server in the "encrypted_client_hello" extension. In such settings, clients send 1503 | a randomly generated config_id in the ECHClientHello. Servers in these settings 1504 | must perform trial decryption since they cannot identify the client's chosen ECH 1505 | key using the config_id value. As a result, ignoring configuration 1506 | identifiers may exacerbate DoS attacks. Specifically, an adversary may send 1507 | malicious ClientHello messages, i.e., those which will not decrypt with any 1508 | known ECH key, in order to force wasteful decryption. Servers that support this 1509 | feature should, for example, implement some form of rate limiting mechanism to 1510 | limit the potential damage caused by such attacks. 1511 | 1512 | Unless specified by the application using (D)TLS or externally configured, 1513 | implementations MUST NOT use this mode. 1514 | 1515 | ## Outer ClientHello {#outer-clienthello} 1516 | 1517 | Any information that the client includes in the ClientHelloOuter is visible to 1518 | passive observers. The client SHOULD NOT send values in the ClientHelloOuter 1519 | which would reveal a sensitive ClientHelloInner property, such as the true 1520 | server name. It MAY send values associated with the public name in the 1521 | ClientHelloOuter. 1522 | 1523 | In particular, some extensions require the client send a server-name-specific 1524 | value in the ClientHello. These values may reveal information about the 1525 | true server name. For example, the "cached_info" ClientHello extension 1526 | {{?RFC7924}} can contain the hash of a previously observed server certificate. 1527 | The client SHOULD NOT send values associated with the true server name in the 1528 | ClientHelloOuter. It MAY send such values in the ClientHelloInner. 1529 | 1530 | A client may also use different preferences in different contexts. For example, 1531 | it may send different ALPN lists to different servers or in different 1532 | application contexts. A client that treats this context as sensitive SHOULD NOT 1533 | send context-specific values in ClientHelloOuter. 1534 | 1535 | Values which are independent of the true server name, or other information the 1536 | client wishes to protect, MAY be included in ClientHelloOuter. If they match 1537 | the corresponding ClientHelloInner, they MAY be compressed as described in 1538 | {{encoding-inner}}. However, note that the payload length reveals information 1539 | about which extensions are compressed, so inner extensions which only sometimes 1540 | match the corresponding outer extension SHOULD NOT be compressed. 1541 | 1542 | Clients MAY include additional extensions in ClientHelloOuter to avoid 1543 | signaling unusual behavior to passive observers, provided the choice of value 1544 | and value itself are not sensitive. See {{dont-stick-out}}. 1545 | 1546 | ## Inner ClientHello {#inner-clienthello} 1547 | 1548 | Values which depend on the contents of ClientHelloInner, such as the 1549 | true server name, can influence how client-facing servers process this message. 1550 | In particular, timing side channels can reveal information about the contents 1551 | of ClientHelloInner. Implementations should take such side channels into 1552 | consideration when reasoning about the privacy properties that ECH provides. 1553 | 1554 | ## Related Privacy Leaks 1555 | 1556 | ECH requires encrypted DNS to be an effective privacy protection mechanism. 1557 | However, verifying the server's identity from the Certificate message, 1558 | particularly when using the X509 CertificateType, may result in additional 1559 | network traffic that may reveal the server identity. Examples of this traffic 1560 | may include requests for revocation information, such as OCSP or CRL traffic, or 1561 | requests for repository information, such as authorityInformationAccess. It may 1562 | also include implementation-specific traffic for additional information sources 1563 | as part of verification. 1564 | 1565 | Implementations SHOULD avoid leaking information that may identify the server. 1566 | Even when sent over an encrypted transport, such requests may result in indirect 1567 | exposure of the server's identity, such as indicating a specific CA or service 1568 | being used. To mitigate this risk, servers SHOULD deliver such information 1569 | in-band when possible, such as through the use of OCSP stapling, and clients 1570 | SHOULD take steps to minimize or protect such requests during certificate 1571 | validation. 1572 | 1573 | Attacks that rely on non-ECH traffic to infer server identity in an ECH 1574 | connection are out of scope for this document. For example, a client that 1575 | connects to a particular host prior to ECH deployment may later resume a 1576 | connection to that same host after ECH deployment. An adversary that observes 1577 | this can deduce that the ECH-enabled connection was made to a host that the 1578 | client previously connected to and which is within the same anonymity set. 1579 | 1580 | ## Cookies 1581 | 1582 | {{Section 4.2.2 of RFC8446}} defines a cookie value that servers may send in 1583 | HelloRetryRequest for clients to echo in the second ClientHello. While ECH 1584 | encrypts the cookie in the second ClientHelloInner, the backend server's 1585 | HelloRetryRequest is unencrypted.This means differences in cookies between 1586 | backend servers, such as lengths or cleartext components, may leak information 1587 | about the server identity. 1588 | 1589 | Backend servers in an anonymity set SHOULD NOT reveal information in the cookie 1590 | which identifies the server. This may be done by handling HelloRetryRequest 1591 | statefully, thus not sending cookies, or by using the same cookie construction 1592 | for all backend servers. 1593 | 1594 | Note that, if the cookie includes a key name, analogous to {{Section 4 of 1595 | ?RFC5077}}, this may leak information if different backend servers issue 1596 | cookies with different key names at the time of the connection. In particular, 1597 | if the deployment operates in Split Mode, the backend servers may not share 1598 | cookie encryption keys. Backend servers may mitigate this by either handling 1599 | key rotation with trial decryption, or coordinating to match key names. 1600 | 1601 | ## Attacks Exploiting Acceptance Confirmation 1602 | 1603 | To signal acceptance, the backend server overwrites 8 bytes of its 1604 | ServerHello.random with a value derived from the ClientHelloInner.random. (See 1605 | {{backend-server}} for details.) This behavior increases the likelihood of the 1606 | ServerHello.random colliding with the ServerHello.random of a previous session, 1607 | potentially reducing the overall security of the protocol. However, the 1608 | remaining 24 bytes provide enough entropy to ensure this is not a practical 1609 | avenue of attack. 1610 | 1611 | On the other hand, the probability that two 8-byte strings are the same is 1612 | non-negligible. This poses a modest operational risk. Suppose the client-facing 1613 | server terminates the connection (i.e., ECH is rejected or bypassed): if the 1614 | last 8 bytes of its ServerHello.random coincide with the confirmation signal, 1615 | then the client will incorrectly presume acceptance and proceed as if the 1616 | backend server terminated the connection. However, the probability of a false 1617 | positive occurring for a given connection is only 1 in 2^64. This value is 1618 | smaller than the probability of network connection failures in practice. 1619 | 1620 | Note that the same bytes of the ServerHello.random are used to implement 1621 | downgrade protection for TLS 1.3 (see {{RFC8446, Section 4.1.3}}). These 1622 | mechanisms do not interfere because the backend server only signals ECH 1623 | acceptance in TLS 1.3 or higher. 1624 | 1625 | ## Comparison Against Criteria 1626 | 1627 | {{?RFC8744}} lists several requirements for SNI encryption. 1628 | In this section, we re-iterate these requirements and assess the ECH design 1629 | against them. 1630 | 1631 | ### Mitigate Cut-and-Paste Attacks 1632 | 1633 | Since servers process either ClientHelloInner or ClientHelloOuter, and because 1634 | ClientHelloInner.random is encrypted, it is not possible for an attacker to "cut 1635 | and paste" the ECH value in a different Client Hello and learn information from 1636 | ClientHelloInner. 1637 | 1638 | ### Avoid Widely Shared Secrets 1639 | 1640 | This design depends upon DNS as a vehicle for semi-static public key 1641 | distribution. Server operators may partition their private keys 1642 | however they see fit provided each server behind an IP address has the 1643 | corresponding private key to decrypt a key. Thus, when one ECH key is 1644 | provided, sharing is optimally bound by the number of hosts that share 1645 | an IP address. Server operators may further limit sharing of private 1646 | keys by publishing different DNS records containing ECHConfig values 1647 | with different public keys using a short TTL. 1648 | 1649 | ### SNI-Based Denial-of-Service Attacks 1650 | 1651 | This design requires servers to decrypt ClientHello messages with ECHClientHello 1652 | extensions carrying valid digests. Thus, it is possible for an attacker to force 1653 | decryption operations on the server. This attack is bound by the number of valid 1654 | transport connections an attacker can open. 1655 | 1656 | ### Do Not Stick Out {#dont-stick-out} 1657 | 1658 | As a means of reducing the impact of network ossification, {{?RFC8744}} 1659 | recommends SNI-protection mechanisms be designed in such a way that network 1660 | operators do not differentiate connections using the mechanism from connections 1661 | not using the mechanism. To that end, ECH is designed to resemble a standard 1662 | TLS handshake as much as possible. The most obvious difference is the extension 1663 | itself: as long as middleboxes ignore it, as required by {{!RFC8446}}, the rest 1664 | of the handshake is designed to look very much as usual. 1665 | 1666 | The GREASE ECH protocol described in {{grease-ech}} provides a low-risk way to 1667 | evaluate the deployability of ECH. It is designed to mimic the real ECH protocol 1668 | ({{real-ech}}) without changing the security properties of the handshake. The 1669 | underlying theory is that if GREASE ECH is deployable without triggering 1670 | middlebox misbehavior, and real ECH looks enough like GREASE ECH, then ECH 1671 | should be deployable as well. Thus, the strategy for mitigating network 1672 | ossification is to deploy GREASE ECH widely enough to disincentivize 1673 | differential treatment of the real ECH protocol by the network. 1674 | 1675 | Ensuring that networks do not differentiate between real ECH and GREASE ECH may 1676 | not be feasible for all implementations. While most middleboxes will not treat 1677 | them differently, some operators may wish to block real ECH usage but allow 1678 | GREASE ECH. This specification aims to provide a baseline security level that 1679 | most deployments can achieve easily, while providing implementations enough 1680 | flexibility to achieve stronger security where possible. Minimally, real ECH is 1681 | designed to be indifferentiable from GREASE ECH for passive adversaries with 1682 | following capabilities: 1683 | 1684 | 1. The attacker does not know the ECHConfigList used by the server. 1685 | 1. The attacker keeps per-connection state only. In particular, it does not 1686 | track endpoints across connections. 1687 | 1688 | Moreover, real ECH and GREASE ECH are designed so that the following features 1689 | do not noticeably vary to the attacker, i.e., they are not distinguishers: 1690 | 1691 | 1. the code points of extensions negotiated in the clear, and their order; 1692 | 1. the length of messages; and 1693 | 1. the values of plaintext alert messages. 1694 | 1695 | This leaves a variety of practical differentiators out-of-scope. including, 1696 | though not limited to, the following: 1697 | 1698 | 1. the value of the configuration identifier; 1699 | 1. the value of the outer SNI; 1700 | 1. the TLS version negotiated, which may depend on ECH acceptance; 1701 | 1. client authentication, which may depend on ECH acceptance; and 1702 | 1. HRR issuance, which may depend on ECH acceptance. 1703 | 1704 | These can be addressed with more sophisticated implementations, but some 1705 | mitigations require coordination between the client and server, and even 1706 | across different client and server implementations. These mitigations are 1707 | out-of-scope for this specification. 1708 | 1709 | ### Maintain Forward Secrecy 1710 | 1711 | This design does not provide forward secrecy for the inner ClientHello 1712 | because the server's ECH key is static. However, the window of 1713 | exposure is bound by the key lifetime. It is RECOMMENDED that servers 1714 | rotate keys regularly. 1715 | 1716 | ### Enable Multi-party Security Contexts 1717 | 1718 | This design permits servers operating in Split Mode to forward connections 1719 | directly to backend origin servers. The client authenticates the identity of 1720 | the backend origin server, thereby allowing the backend origin server 1721 | to hide behind the client-facing server without the client-facing 1722 | server decrypting and reencrypting the connection. 1723 | 1724 | Conversely, if the DNS records used for configuration are 1725 | authenticated, e.g., via DNSSEC, 1726 | spoofing a client-facing server operating in Split Mode is not 1727 | possible. See {{plaintext-dns}} for more details regarding plaintext 1728 | DNS. 1729 | 1730 | Authenticating the ECHConfig structure naturally authenticates the included 1731 | public name. This also authenticates any retry signals from the client-facing 1732 | server because the client validates the server certificate against the public 1733 | name before retrying. 1734 | 1735 | ### Support Multiple Protocols 1736 | 1737 | This design has no impact on application layer protocol negotiation. It may 1738 | affect connection routing, server certificate selection, and client certificate 1739 | verification. Thus, it is compatible with multiple application and transport 1740 | protocols. By encrypting the entire ClientHello, this design additionally 1741 | supports encrypting the ALPN extension. 1742 | 1743 | ## Padding Policy 1744 | 1745 | Variations in the length of the ClientHelloInner ciphertext could leak 1746 | information about the corresponding plaintext. {{padding}} describes a 1747 | RECOMMENDED padding mechanism for clients aimed at reducing potential 1748 | information leakage. 1749 | 1750 | ## Active Attack Mitigations 1751 | 1752 | This section describes the rationale for ECH properties and mechanics as 1753 | defenses against active attacks. In all the attacks below, the attacker is 1754 | on-path between the target client and server. The goal of the attacker is to 1755 | learn private information about the inner ClientHello, such as the true SNI 1756 | value. 1757 | 1758 | ### Client Reaction Attack Mitigation {#flow-client-reaction} 1759 | 1760 | This attack uses the client's reaction to an incorrect certificate as an oracle. 1761 | The attacker intercepts a legitimate ClientHello and replies with a ServerHello, 1762 | Certificate, CertificateVerify, and Finished messages, wherein the Certificate 1763 | message contains a "test" certificate for the domain name it wishes to query. If 1764 | the client decrypted the Certificate and failed verification (or leaked 1765 | information about its verification process by a timing side channel), the 1766 | attacker learns that its test certificate name was incorrect. As an example, 1767 | suppose the client's SNI value in its inner ClientHello is "example.com," and 1768 | the attacker replied with a Certificate for "test.com". If the client produces a 1769 | verification failure alert because of the mismatch faster than it would due to 1770 | the Certificate signature validation, information about the name leaks. Note 1771 | that the attacker can also withhold the CertificateVerify message. In that 1772 | scenario, a client which first verifies the Certificate would then respond 1773 | similarly and leak the same information. 1774 | 1775 | ~~~ 1776 | Client Attacker Server 1777 | ClientHello 1778 | + key_share 1779 | + ech ------> (intercept) -----> X (drop) 1780 | 1781 | ServerHello 1782 | + key_share 1783 | {EncryptedExtensions} 1784 | {CertificateRequest*} 1785 | {Certificate*} 1786 | {CertificateVerify*} 1787 | <------ 1788 | Alert 1789 | ------> 1790 | ~~~ 1791 | {: #flow-diagram-client-reaction title="Client reaction attack"} 1792 | 1793 | ClientHelloInner.random prevents this attack. In particular, since the attacker 1794 | does not have access to this value, it cannot produce the right transcript and 1795 | handshake keys needed for encrypting the Certificate message. Thus, the client 1796 | will fail to decrypt the Certificate and abort the connection. 1797 | 1798 | ### HelloRetryRequest Hijack Mitigation {#flow-hrr-hijack} 1799 | 1800 | This attack aims to exploit server HRR state management to recover information 1801 | about a legitimate ClientHello using its own attacker-controlled ClientHello. 1802 | To begin, the attacker intercepts and forwards a legitimate ClientHello with an 1803 | "encrypted_client_hello" (ech) extension to the server, which triggers a 1804 | legitimate HelloRetryRequest in return. Rather than forward the retry to the 1805 | client, the attacker attempts to generate its own ClientHello in response based 1806 | on the contents of the first ClientHello and HelloRetryRequest exchange with the 1807 | result that the server encrypts the Certificate to the attacker. If the server 1808 | used the SNI from the first ClientHello and the key share from the second 1809 | (attacker-controlled) ClientHello, the Certificate produced would leak the 1810 | client's chosen SNI to the attacker. 1811 | 1812 | ~~~ 1813 | Client Attacker Server 1814 | ClientHello 1815 | + key_share 1816 | + ech ------> (forward) -------> 1817 | HelloRetryRequest 1818 | + key_share 1819 | (intercept) <------- 1820 | 1821 | ClientHello 1822 | + key_share' 1823 | + ech' -------> 1824 | ServerHello 1825 | + key_share 1826 | {EncryptedExtensions} 1827 | {CertificateRequest*} 1828 | {Certificate*} 1829 | {CertificateVerify*} 1830 | {Finished} 1831 | <------- 1832 | (process server flight) 1833 | ~~~ 1834 | {: #flow-diagram-hrr-hijack title="HelloRetryRequest hijack attack"} 1835 | 1836 | This attack is mitigated by using the same HPKE context for both ClientHello 1837 | messages. The attacker does not possess the context's keys, so it cannot 1838 | generate a valid encryption of the second inner ClientHello. 1839 | 1840 | If the attacker could manipulate the second ClientHello, it might be possible 1841 | for the server to act as an oracle if it required parameters from the first 1842 | ClientHello to match that of the second ClientHello. For example, imagine the 1843 | client's original SNI value in the inner ClientHello is "example.com", and the 1844 | attacker's hijacked SNI value in its inner ClientHello is "test.com". A server 1845 | which checks these for equality and changes behavior based on the result can be 1846 | used as an oracle to learn the client's SNI. 1847 | 1848 | ### ClientHello Malleability Mitigation {#flow-clienthello-malleability} 1849 | 1850 | This attack aims to leak information about secret parts of the encrypted 1851 | ClientHello by adding attacker-controlled parameters and observing the server's 1852 | response. In particular, the compression mechanism described in 1853 | {{encoding-inner}} references parts of a potentially attacker-controlled 1854 | ClientHelloOuter to construct ClientHelloInner, or a buggy server may 1855 | incorrectly apply parameters from ClientHelloOuter to the handshake. 1856 | 1857 | To begin, the attacker first interacts with a server to obtain a resumption 1858 | ticket for a given test domain, such as "example.com". Later, upon receipt of a 1859 | ClientHelloOuter, it modifies it such that the server will process the 1860 | resumption ticket with ClientHelloInner. If the server only accepts resumption 1861 | PSKs that match the server name, it will fail the PSK binder check with an 1862 | alert when ClientHelloInner is for "example.com" but silently ignore the PSK 1863 | and continue when ClientHelloInner is for any other name. This introduces an 1864 | oracle for testing encrypted SNI values. 1865 | 1866 | ~~~ 1867 | Client Attacker Server 1868 | 1869 | handshake and ticket 1870 | for "example.com" 1871 | <--------> 1872 | 1873 | ClientHello 1874 | + key_share 1875 | + ech 1876 | + ech_outer_extensions(pre_shared_key) 1877 | + pre_shared_key 1878 | --------> 1879 | (intercept) 1880 | ClientHello 1881 | + key_share 1882 | + ech 1883 | + ech_outer_extensions(pre_shared_key) 1884 | + pre_shared_key' 1885 | --------> 1886 | Alert 1887 | -or- 1888 | ServerHello 1889 | ... 1890 | Finished 1891 | <-------- 1892 | ~~~ 1893 | {: #tls-clienthello-malleability title="Message flow for malleable ClientHello"} 1894 | 1895 | This attack may be generalized to any parameter which the server varies by 1896 | server name, such as ALPN preferences. 1897 | 1898 | ECH mitigates this attack by only negotiating TLS parameters from 1899 | ClientHelloInner and authenticating all inputs to the ClientHelloInner 1900 | (EncodedClientHelloInner and ClientHelloOuter) with the HPKE AEAD. See 1901 | {{authenticating-outer}}. The decompression process in {{encoding-inner}} 1902 | forbids "encrypted_client_hello" in OuterExtensions. This ensures the 1903 | unauthenticated portion of ClientHelloOuter is not incorporated into 1904 | ClientHelloInner. 1905 | An earlier iteration of this specification only 1906 | encrypted and authenticated the "server_name" extension, which left the overall 1907 | ClientHello vulnerable to an analogue of this attack. 1908 | 1909 | ### ClientHelloInner Packet Amplification Mitigation {#decompression-amp} 1910 | 1911 | Client-facing servers must decompress EncodedClientHelloInners. A malicious 1912 | attacker may craft a packet which takes excessive resources to decompress 1913 | or may be much larger than the incoming packet: 1914 | 1915 | * If looking up a ClientHelloOuter extension takes time linear in the number of 1916 | extensions, the overall decoding process would take O(M\*N) time, where 1917 | M is the number of extensions in ClientHelloOuter and N is the 1918 | size of OuterExtensions. 1919 | 1920 | * If the same ClientHelloOuter extension can be copied multiple times, 1921 | an attacker could cause the client-facing server to construct a large 1922 | ClientHelloInner by including a large extension in ClientHelloOuter, 1923 | of length L, and an OuterExtensions list referencing N copies of that 1924 | extension. The client-facing server would then use O(N\*L) memory in 1925 | response to O(N+L) bandwidth from the client. In split-mode, an 1926 | O(N\*L) sized packet would then be transmitted to the 1927 | backend server. 1928 | 1929 | ECH mitigates this attack by requiring that OuterExtensions be referenced in 1930 | order, that duplicate references be rejected, and by recommending that 1931 | client-facing servers use a linear scan to perform decompression. These 1932 | requirements are detailed in {{encoding-inner}}. 1933 | 1934 | # IANA Considerations 1935 | 1936 | ## Update of the TLS ExtensionType Registry 1937 | 1938 | IANA is requested to create the following entries in the existing registry for 1939 | ExtensionType (defined in {{!RFC8446}}): 1940 | 1941 | 1. encrypted_client_hello(0xfe0d), with "TLS 1.3" column values set to 1942 | "CH, HRR, EE", "DTLS-Only" column set to "N", and "Recommended" column set 1943 | to "Yes". 1944 | 1. ech_outer_extensions(0xfd00), with the "TLS 1.3" column values set to "CH", 1945 | "DTLS-Only" column set to "N", "Recommended" column set to "Yes", and the 1946 | "Comment" column set to "Only appears in inner CH." 1947 | 1948 | ## Update of the TLS Alert Registry {#alerts} 1949 | 1950 | IANA is requested to create an entry, ech_required(121) in the existing registry 1951 | for Alerts (defined in {{!RFC8446}}), with the "DTLS-OK" column set to 1952 | "Y". 1953 | 1954 | ## ECH Configuration Extension Registry {#config-extensions-iana} 1955 | 1956 | IANA is requested to create a new "ECHConfig Extension" registry in a new 1957 | "TLS Encrypted Client Hello (ECH) Configuration Extensions" page. New 1958 | registrations need to list the following attributes: 1959 | 1960 | Value: 1961 | : The two-byte identifier for the ECHConfigExtension, i.e., the 1962 | ECHConfigExtensionType 1963 | 1964 | Extension Name: 1965 | : Name of the ECHConfigExtension 1966 | 1967 | Recommended: 1968 | : A "Y" or "N" value indicating if the extension is TLS WG recommends that the 1969 | extension be supported. This column is assigned a value of "N" unless 1970 | explicitly requested. Adding a value with a value of "Y" requires Standards 1971 | Action {{RFC8126}}. 1972 | 1973 | Reference: 1974 | : The specification where the ECHConfigExtension is defined 1975 | 1976 | Notes: 1977 | : Any notes associated with the entry 1978 | {: spacing="compact"} 1979 | 1980 | New entries in the "ECHConfig Extension" registry are subject to the 1981 | Specification Required registration policy ({{!RFC8126, Section 1982 | 4.6}}), with the policies described in {{!RFC8447, Section 17}}. IANA 1983 | [shall add/has added] the following note to the TLS ECHConfig Extension 1984 | registry: 1985 | 1986 | Note: The role of the designated expert is described in RFC 8447. 1987 | The designated expert [RFC8126] ensures that the specification is 1988 | publicly available. It is sufficient to have an Internet-Draft 1989 | (that is posted and never published as an RFC) or a document from 1990 | another standards body, industry consortium, university site, etc. 1991 | The expert may provide more in depth reviews, but their approval 1992 | should not be taken as an endorsement of the extension. 1993 | 1994 | This document defines several Reserved values for ECH configuration extensions 1995 | to be used for "greasing" as described in {{server-greasing}}. 1996 | 1997 | The initial contents for this registry consists of multiple reserved values, 1998 | with the following attributes, which are repeated for each registration: 1999 | 2000 | Value: 2001 | : 0x0000, 0x1A1A, 0x2A2A, 0x3A3A, 0x4A4A, 0x5A5A, 0x6A6A, 0x7A7A, 0x8A8A, 2002 | 0x9A9A, 0xAAAA, 0xBABA, 0xCACA, 0xDADA, 0xEAEA, 0xFAFA 2003 | 2004 | Extension Name: 2005 | : RESERVED 2006 | 2007 | Recommended: 2008 | : Y 2009 | 2010 | Reference: 2011 | : This document 2012 | 2013 | Notes: 2014 | : Grease entries. 2015 | {: spacing="compact"} 2016 | 2017 | --- back 2018 | 2019 | # Linear-time Outer Extension Processing {#linear-outer-extensions} 2020 | 2021 | The following procedure processes the "ech_outer_extensions" extension (see 2022 | {{encoding-inner}}) in linear time, ensuring that each referenced extension 2023 | in the ClientHelloOuter is included at most once: 2024 | 2025 | 1. Let I be initialized to zero and N be set to the number of extensions 2026 | in ClientHelloOuter. 2027 | 2028 | 1. For each extension type, E, in OuterExtensions: 2029 | 2030 | * If E is "encrypted_client_hello", abort the connection with an 2031 | "illegal_parameter" alert and terminate this procedure. 2032 | 2033 | * While I is less than N and the I-th extension of 2034 | ClientHelloOuter does not have type E, increment I. 2035 | 2036 | * If I is equal to N, abort the connection with an "illegal_parameter" 2037 | alert and terminate this procedure. 2038 | 2039 | * Otherwise, the I-th extension of ClientHelloOuter has type E. Copy 2040 | it to the EncodedClientHelloInner and increment I. 2041 | 2042 | # Acknowledgements 2043 | 2044 | This document draws extensively from ideas in {{?I-D.kazuho-protected-sni}}, but 2045 | is a much more limited mechanism because it depends on the DNS for the 2046 | protection of the ECH key. Richard Barnes, Christian Huitema, Patrick McManus, 2047 | Matthew Prince, Nick Sullivan, Martin Thomson, and David Benjamin also provided 2048 | important ideas and contributions. 2049 | 2050 | # Change Log 2051 | 2052 | > **RFC Editor's Note:** Please remove this section prior to publication of a 2053 | > final version of this document. 2054 | 2055 | Issue and pull request numbers are listed with a leading octothorp. 2056 | 2057 | ## Since draft-ietf-tls-esni-16 2058 | 2059 | - Keep-alive 2060 | 2061 | ## Since draft-ietf-tls-esni-15 2062 | 2063 | - Add CCS2022 reference and summary (#539) 2064 | 2065 | ## Since draft-ietf-tls-esni-14 2066 | 2067 | - Keep-alive 2068 | 2069 | ## Since draft-ietf-tls-esni-13 2070 | 2071 | - Editorial improvements 2072 | 2073 | ## Since draft-ietf-tls-esni-12 2074 | 2075 | - Abort on duplicate OuterExtensions (#514) 2076 | 2077 | - Improve EncodedClientHelloInner definition (#503) 2078 | 2079 | - Clarify retry configuration usage (#498) 2080 | 2081 | - Expand on config_id generation implications (#491) 2082 | 2083 | - Server-side acceptance signal extension GREASE (#481) 2084 | 2085 | - Refactor overview, client implementation, and middlebox 2086 | sections (#480, #478, #475, #508) 2087 | 2088 | - Editorial iprovements (#485, #488, #490, #495, #496, #499, #500, 2089 | #501, #504, #505, #507, #510, #511) 2090 | 2091 | ## Since draft-ietf-tls-esni-11 2092 | 2093 | - Move ClientHello padding to the encoding (#443) 2094 | 2095 | - Align codepoints (#464) 2096 | 2097 | - Relax OuterExtensions checks for alignment with RFC8446 (#467) 2098 | 2099 | - Clarify HRR acceptance and rejection logic (#470) 2100 | 2101 | - Editorial improvements (#468, #465, #462, #461) 2102 | 2103 | ## Since draft-ietf-tls-esni-10 2104 | 2105 | - Make HRR confirmation and ECH acceptance explicit (#422, #423) 2106 | 2107 | - Relax computation of the acceptance signal (#420, #449) 2108 | 2109 | - Simplify ClientHelloOuterAAD generation (#438, #442) 2110 | 2111 | - Allow empty enc in ECHClientHello (#444) 2112 | 2113 | - Authenticate ECHClientHello extensions position in ClientHelloOuterAAD (#410) 2114 | 2115 | - Allow clients to send a dummy PSK and early_data in ClientHelloOuter when 2116 | applicable (#414, #415) 2117 | 2118 | - Compress ECHConfigContents (#409) 2119 | 2120 | - Validate ECHConfig.contents.public_name (#413, #456) 2121 | 2122 | - Validate ClientHelloInner contents (#411) 2123 | 2124 | - Note split-mode challenges for HRR (#418) 2125 | 2126 | - Editorial improvements (#428, #432, #439, #445, #458, #455) 2127 | 2128 | ## Since draft-ietf-tls-esni-09 2129 | 2130 | - Finalize HPKE dependency (#390) 2131 | 2132 | - Move from client-computed to server-chosen, one-byte config 2133 | identifier (#376, #381) 2134 | 2135 | - Rename ECHConfigs to ECHConfigList (#391) 2136 | 2137 | - Clarify some security and privacy properties (#385, #383) 2138 | -------------------------------------------------------------------------------- /id/draft-rescorla-tls-esni.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Encrypted Server Name Indication for TLS 1.3 3 | abbrev: TLS 1.3 SNI Encryption 4 | docname: draft-rescorla-tls-esni-latest 5 | category: exp 6 | 7 | ipr: trust200902 8 | area: General 9 | workgroup: tls 10 | keyword: Internet-Draft 11 | 12 | stand_alone: yes 13 | pi: [toc, sortrefs, symrefs] 14 | 15 | author: 16 | - 17 | ins: E. Rescorla 18 | name: Eric Rescorla 19 | organization: RTFM, Inc. 20 | email: ekr@rtfm.com 21 | 22 | - 23 | ins: K. Oku 24 | name: Kazuho Oku 25 | organization: Fastly 26 | email: kazuhooku@gmail.com 27 | 28 | - 29 | ins: N. Sullivan 30 | name: Nick Sullivan 31 | organization: Cloudflare 32 | email: nick@cloudflare.com 33 | 34 | - 35 | ins: C. A. Wood 36 | name: Christopher A. Wood 37 | organization: Apple, Inc. 38 | email: cawood@apple.com 39 | 40 | 41 | normative: 42 | RFC1035: 43 | RFC2119: 44 | RFC6234: 45 | 46 | informative: 47 | 48 | 49 | 50 | --- abstract 51 | 52 | This document defines a simple mechanism for encrypting the 53 | Server Name Indication for TLS 1.3. 54 | 55 | --- middle 56 | 57 | # Introduction 58 | 59 | DISCLAIMER: This is very early a work-in-progress design and has not 60 | yet seen significant (or really any) security analysis. It should not 61 | be used as a basis for building production systems. 62 | 63 | Although TLS 1.3 {{!RFC8446}} encrypts most of the 64 | handshake, including the server certificate, there are several other 65 | channels that allow an on-path attacker to determine the domain name the 66 | client is trying to connect to, including: 67 | 68 | * Cleartext client DNS queries. 69 | * Visible server IP addresses, assuming the the server is not doing 70 | domain-based virtual hosting. 71 | * Cleartext Server Name Indication (SNI) {{!RFC6066}} in ClientHello messages. 72 | 73 | DoH {{?I-D.ietf-doh-dns-over-https}} and DPRIVE {{?RFC7858}} {{?RFC8094}} 74 | provide mechanisms for clients to conceal DNS lookups from network inspection, 75 | and many TLS servers host multiple domains on the same IP address. 76 | In such environments, SNI is an explicit signal used to determine the server's 77 | identity. Indirect mechanisms such as traffic analysis also exist. 78 | 79 | The TLS WG has extensively studied the problem of protecting SNI, but 80 | has been unable to develop a completely generic 81 | solution. {{?I-D.ietf-tls-sni-encryption}} provides a description 82 | of the problem space and some of the proposed techniques. One of the 83 | more difficult problems is "Do not stick out" 84 | ({{?I-D.ietf-tls-sni-encryption}}; Section 3.4): if only sensitive/private 85 | services use SNI encryption, then SNI encryption is a signal that 86 | a client is going to such a service. For this reason, 87 | much recent work has focused on 88 | concealing the fact that SNI is being protected. Unfortunately, 89 | the result often has undesirable performance consequences, incomplete 90 | coverage, or both. 91 | 92 | The design in this document takes a different approach: it assumes 93 | that private origins will co-locate with or hide behind a provider (CDN, app server, 94 | etc.) which is able to activate encrypted SNI (ESNI) for all of the domains 95 | it hosts. Thus, the use of encrypted SNI does not indicate that the 96 | client is attempting to reach a private origin, but only that it is 97 | going to a particular service provider, which the observer could 98 | already tell from the IP address. 99 | 100 | 101 | # Conventions and Definitions 102 | 103 | The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", 104 | "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this 105 | document are to be interpreted as described in BCP 14 {{RFC2119}} {{!RFC8174}} 106 | when, and only when, they appear in all capitals, as shown here. 107 | 108 | 109 | # Overview 110 | 111 | This document is designed to operate in one of two primary topologies 112 | shown below, which we call "Shared Mode" and "Split Mode" 113 | 114 | ## Topologies 115 | 116 | ~~~~ 117 | +---------------------+ 118 | | | 119 | | 2001:DB8::1111 | 120 | | | 121 | Client <-----> | private.example.org | 122 | | | 123 | | public.example.com | 124 | | | 125 | +---------------------+ 126 | Server 127 | ~~~~ 128 | {: #shared-mode title="Shared Mode Topology"} 129 | 130 | In Shared Mode, the provider is the origin server for all the domains 131 | whose DNS records point to it and clients form a TLS connection directly 132 | to that provider, which has access to the plaintext of the connection. 133 | 134 | ~~~~ 135 | +--------------------+ +---------------------+ 136 | | | | | 137 | | 2001:DB8::1111 | | 2001:DB8::EEEE | 138 | Client <------------------------------------>| | 139 | | public.example.com | | private.example.com | 140 | | | | | 141 | +--------------------+ +---------------------+ 142 | Client-Facing Server Backend Server 143 | ~~~~ 144 | {: #split-mode title="Split Mode Topology"} 145 | 146 | In Split Mode, the provider is *not* the origin server for private 147 | domains. Rather the DNS records for private domains point to the provider, 148 | but the provider's server just relays the connection back to the 149 | backend server, which is the true origin server. The provider does 150 | not have access to the plaintext of the connection. In principle, 151 | the provider might not be the origin for any domains, but as 152 | a practical matter, it is probably the origin for a large set of 153 | innocuous domains, but is also providing protection for some private 154 | domains. Note that the backend server can be an unmodified TLS 1.3 155 | server. 156 | 157 | 158 | ## SNI Encryption 159 | 160 | The protocol designed in this document is quite straightforward. 161 | 162 | First, the provider publishes a public key which is used for SNI encryption 163 | for all the domains for which it serves directly or indirectly (via Split mode). 164 | This document defines a publication mechanism using DNS, but other mechanisms 165 | are also possible. In particular, if some of the clients of 166 | a private server are applications rather than Web browsers, those 167 | applications might have the public key preconfigured. 168 | 169 | When a client wants to form a TLS connection to any of the domains 170 | served by an ESNI-supporting provider, it replaces the 171 | "server_name" extension in the ClientHello with an "encrypted_server_name" 172 | extension, which contains the true extension encrypted under the 173 | provider's public key. The provider can then decrypt the extension 174 | and either terminate the connection (in Shared Mode) or forward 175 | it to the backend server (in Split Mode). 176 | 177 | # Publishing the SNI Encryption Key {#publishing-key} 178 | 179 | SNI Encryption keys can be published in the DNS using the ESNIKeys 180 | structure, defined below. 181 | 182 | ~~~~ 183 | // Copied from TLS 1.3 184 | struct { 185 | NamedGroup group; 186 | opaque key_exchange<1..2^16-1>; 187 | } KeyShareEntry; 188 | 189 | struct { 190 | uint8 checksum[4]; 191 | KeyShareEntry keys<4..2^16-1>; 192 | CipherSuite cipher_suites<2..2^16-2>; 193 | uint16 padded_length; 194 | uint64 not_before; 195 | uint64 not_after; 196 | Extension extensions<0..2^16-1>; 197 | } ESNIKeys; 198 | ~~~~ 199 | 200 | checksum 201 | : The first four (4) octets of the SHA-256 message digest {{RFC6234}} 202 | of the ESNIKeys structure starting from the first octet of "keys" to 203 | the end of the structure. 204 | 205 | keys 206 | : The list of keys which can be used by the client to encrypt the SNI. 207 | Every key being listed MUST belong to a different group. 208 | 209 | padded_length 210 | : 211 | The length to pad the ServerNameList value to prior to encryption. 212 | This value SHOULD be set to the largest ServerNameList the server 213 | expects to support rounded up the nearest multiple of 16. If the 214 | server supports wildcard names, it SHOULD set this value to 260. 215 | 216 | not_before 217 | : The moment when the keys become valid for use. The value is represented 218 | as seconds from 00:00:00 UTC on Jan 1 1970, not including leap seconds. 219 | 220 | not_after 221 | : The moment when the keys become invalid. Uses the same unit as 222 | not_before. 223 | 224 | extensions 225 | : A list of extensions that the client can take into consideration when 226 | generating a Client Hello message. The format is defined in 227 | {{RFC8446}}; Section 4.2. The purpose of the field is to 228 | provide room for additional features in the future; this document does 229 | not define any extension. 230 | 231 | The semantics of this structure are simple: any of the listed keys may 232 | be used to encrypt the SNI for the associated domain name. 233 | The cipher suite list is orthogonal to the 234 | list of keys, so each key may be used with any cipher suite. 235 | 236 | This structure is placed in the RRData section of a TXT record 237 | as a base64-encoded string. If this encoding exceeds the 255 octet 238 | limit of TXT strings, it must be split across multiple concatenated 239 | strings as per Section 3.1.3 of {{!RFC4408}}. 240 | 241 | The name of each TXT record MUST match the name composed 242 | of \_esni and the query domain name. That is, if a client queries 243 | example.com, the ESNI TXT Resource Record might be: 244 | 245 | ~~~ 246 | _esni.example.com. 60S IN TXT "..." "..." 247 | ~~~ 248 | 249 | Servers MUST ensure that if multiple A or AAAA records are returned for a 250 | domain with ESNI support, all the servers pointed to by those records are 251 | able to handle the keys returned as part of a ESNI TXT record for that domain. 252 | 253 | Clients obtain these records by querying DNS for ESNI-enabled server domains. 254 | Clients may initiate these queries in parallel alongside normal A or AAAA queries, 255 | and SHOULD block TLS handshakes until they complete, perhaps by timing out. 256 | 257 | Servers operating in Split Mode SHOULD have DNS configured to return 258 | the same A (or AAAA) record for all ESNI-enabled servers they service. This yields 259 | an anonymity set of cardinality equal to the number of ESNI-enabled server domains 260 | supported by a given client-facing server. Thus, even with SNI encryption, 261 | an attacker which can enumerate the set of ESNI-enabled domains supported 262 | by a client-facing server can guess the correct SNI with probability at least 263 | 1/K, where K is the size of this ESNI-enabled server anonymity set. This probability 264 | may be increased via traffic analysis or other mechanisms. 265 | 266 | The "checksum" field provides protection against transmission errors, 267 | including those caused by intermediaries such as a DNS proxy running on a 268 | home router. 269 | 270 | "not_before" and "not_after" fields represent the validity period of the 271 | published ESNI keys. Clients MUST NOT use ESNI keys that was covered by an 272 | invalid checksum or beyond the published 273 | period. Servers SHOULD set the Resource Record TTL small enough so that the 274 | record gets discarded by the cache before the ESNI keys reach the end of 275 | their validity period. Note that servers MAY need to retain the decryption key 276 | for some time after "not_after", and will need to consider clock skew, internal 277 | caches and the like, when selecting the "not_before" and "not_after" values. 278 | 279 | Client MAY cache the ESNIKeys for a particular domain based on the TTL of the 280 | Resource Record, but SHOULD NOT cache it based on the not_after value, to allow 281 | servers to rotate the keys often and improve forward secrecy. 282 | 283 | Note that the length of this structure MUST NOT exceed 2^16 - 1, as the 284 | RDLENGTH is only 16 bits {{RFC1035}}. 285 | 286 | # The "encrypted_server_name" extension {#esni-extension} 287 | 288 | The encrypted SNI is carried in an "encrypted_server_name" 289 | extension, defined as follows: 290 | 291 | ~~~ 292 | enum { 293 | encrypted_server_name(0xffce), (65535) 294 | } ExtensionType; 295 | ~~~ 296 | 297 | For clients (in ClientHello), this extension contains the following 298 | EncryptedSNI structure: 299 | 300 | ~~~~ 301 | struct { 302 | CipherSuite suite; 303 | KeyShareEntry key_share; 304 | opaque record_digest<0..2^16-1>; 305 | opaque encrypted_sni<0..2^16-1>; 306 | } EncryptedSNI; 307 | ~~~~ 308 | 309 | suite 310 | : The cipher suite used to encrypt the SNI. 311 | 312 | key_share 313 | : The KeyShareEntry carrying the client's public ephemeral key shared 314 | used to derive the ESNI key. 315 | 316 | record_digest 317 | : A cryptographic hash of the ESNIKeys structure from which the ESNI 318 | key was obtained, i.e., from the first byte of "checksum" to the end 319 | of the structure. This hash is computed using the hash function 320 | associated with `suite`. 321 | 322 | encrypted_sni 323 | : The original ServerNameList from the "server_name" extension, 324 | padded and AEAD-encrypted using cipher suite "suite" and with the key 325 | generated as described below. 326 | {:br} 327 | 328 | For servers (in EncryptedExtensions), this extension contains the following 329 | structure: 330 | 331 | ~~~ 332 | struct { 333 | uint8 nonce[16]; 334 | } EncryptedSNI; 335 | ~~~ 336 | 337 | nonce 338 | : The contents of PaddedServerNameList.nonce. (See {{client-behavior}}.) 339 | 340 | ## Client Behavior {#client-behavior} 341 | 342 | In order to send an encrypted SNI, the client MUST first select one of 343 | the server ESNIKeyShareEntry values and generate an (EC)DHE share in the 344 | matching group. This share will then be sent to the server in the EncryptedSNI 345 | extension and used to derive the SNI encryption key. It does not affect the 346 | (EC)DHE shared secret used in the TLS key schedule. 347 | 348 | Let Z be the DH shared secret derived from a key share in ESNIKeys and the 349 | corresponding client share in EncryptedSNI.key_share. The SNI encryption key is 350 | computed from Z as follows: 351 | 352 | ~~~~ 353 | Zx = HKDF-Extract(0, Z) 354 | key = HKDF-Expand-Label(Zx, "esni key", Hash(ESNIContents), key_length) 355 | iv = HKDF-Expand-Label(Zx, "esni iv", Hash(ESNIContents), iv_length) 356 | ~~~~ 357 | 358 | where ESNIContents is as specified below and Hash is the hash function 359 | associated with the HKDF instantiation. 360 | 361 | ~~~ 362 | struct { 363 | opaque record_digest<0..2^16-1>; 364 | KeyShareEntry esni_key_share; 365 | Random client_hello_random; 366 | } ESNIContents; 367 | ~~~ 368 | 369 | The client then creates a PaddedServerNameList: 370 | 371 | ~~~~ 372 | struct { 373 | ServerNameList sni; 374 | uint8 nonce[16]; 375 | opaque zeros[ESNIKeys.padded_length - length(sni)]; 376 | } PaddedServerNameList; 377 | ~~~~ 378 | 379 | sni 380 | : The true SNI. 381 | 382 | nonce 383 | : A random 16-octet value to be echoed by the server in the 384 | "encrypted_server_name" extension. 385 | 386 | zeros 387 | : Zero padding whose length makes the serialized struct length 388 | match ESNIKeys.padded_length. 389 | 390 | This value consists of the serialized ServerNameList from the "server_name" extension, 391 | padded with enough zeroes to make the total structure ESNIKeys.padded_length 392 | bytes long. The purpose of the padding is to prevent attackers 393 | from using the length of the "encrypted_server_name" extension 394 | to determine the true SNI. If the serialized ServerNameList is 395 | longer than ESNIKeys.padded_length, the client MUST NOT use 396 | the "encrypted_server_name" extension. 397 | 398 | The EncryptedSNI.encrypted_sni value is then computed using the usual 399 | TLS 1.3 AEAD: 400 | 401 | ~~~~ 402 | encrypted_sni = AEAD-Encrypt(key, iv, ClientHello.KeyShareClientHello, PaddedServerNameList) 403 | ~~~~ 404 | 405 | Including ClientHello.KeyShareClientHello in the AAD of AEAD-Encrypt 406 | binds the EncryptedSNI value to the ClientHello and prevents cut-and-paste 407 | attacks. 408 | 409 | Note: future extensions may end up reusing the server's ESNIKeyShareEntry 410 | for other purposes within the same message (e.g., encrypting other 411 | values). Those usages MUST have their own HKDF labels to avoid 412 | reuse. 413 | 414 | [[OPEN ISSUE: If in the future you were to reuse these keys for 415 | 0-RTT priming, then you would have to worry about potentially 416 | expanding twice of Z_extracted. We should think about how 417 | to harmonize these to make sure that we maintain key separation.]] 418 | 419 | This value is placed in an "encrypted_server_name" extension. 420 | 421 | The client MAY either omit the "server_name" extension or provide 422 | an innocuous dummy one (this is required for technical conformance 423 | with {{!RFC7540}}; Section 9.2.) 424 | 425 | If the server does not provide an "encrypted_server_name" extension 426 | in EncryptedExtensions, the client MUST abort the connection with 427 | an "illegal_parameter" alert. Moreover, it MUST check that 428 | PaddedServerNameList.nonce matches the value of the 429 | "encrypted_server_name" extension provided by the server, 430 | and otherwise abort the connection with an "illegal_parameter" 431 | alert. 432 | 433 | ## Client-Facing Server Behavior 434 | 435 | Upon receiving an "encrypted_server_name" extension, the client-facing 436 | server MUST first perform the following checks: 437 | 438 | - If it is unable to negotiate TLS 1.3 or greater, it MUST 439 | abort the connection with a "handshake_failure" alert. 440 | 441 | - If the EncryptedSNI.record_digest value does not match the cryptographic 442 | hash of any known ENSIKeys structure, it MUST abort the connection with 443 | an "illegal_parameter" alert. This is necessary to prevent downgrade attacks. 444 | [[OPEN ISSUE: We looked at ignoring the extension but concluded 445 | this was better.]] 446 | 447 | - If the EncryptedSNI.key_share group does not match one in the ESNIKeys.keys, 448 | it MUST abort the connection with an "illegal_parameter" alert. 449 | 450 | - If the length of the "encrypted_server_name" extension is 451 | inconsistent with the advertised padding length (plus AEAD 452 | expansion) the server MAY abort the connection with an 453 | "illegal_parameter" alert without attempting to decrypt. 454 | 455 | Assuming these checks succeed, the server then computes K_sni 456 | and decrypts the ServerName value. If decryption fails, the server 457 | MUST abort the connection with a "decrypt_error" alert. 458 | 459 | If the decrypted value's length is different from 460 | the advertised ESNIKeys.padded_length or the padding consists of 461 | any value other than 0, then the server MUST abort the 462 | connection with an illegal_parameter alert. Otherwise, the 463 | server uses the PaddedServerNameList.sni value as if it were 464 | the "server_name" extension. Any actual "server_name" extension is 465 | ignored. 466 | 467 | Upon determining the true SNI, the client-facing server then either 468 | serves the connection directly (if in Shared Mode), in which case 469 | it executes the steps in the following section, or forwards 470 | the TLS connection to the backend server (if in Split Mode). In 471 | the latter case, it does not make any changes to the TLS 472 | messages, but just blindly forwards them. 473 | 474 | ## Shared Mode Server Behavior 475 | 476 | A server operating in Shared Mode uses PaddedServerNameList.sni as 477 | if it were the "server_name" extension to finish the handshake. It 478 | SHOULD pad the Certificate message, via padding at the record layer, 479 | such that its length equals the size of the largest possible Certificate 480 | (message) covered by the same ESNI key. Moreover, the server MUST 481 | include the "encrypted_server_name" extension in EncryptedExtensions, 482 | and the value of this extension MUST match PaddedServerNameList.nonce. 483 | 484 | ## Split Mode Server Behavior {#backend-server-behavior} 485 | 486 | In Split Mode, the backend server must know PaddedServerNameList.nonce 487 | to echo it back in EncryptedExtensions and complete the handshake. 488 | {{communicating-sni}} describes one mechanism for sending both 489 | PaddedServerNameList.sni and PaddedServerNameList.nonce to the backend 490 | server. Thus, backend servers function the same as servers operating 491 | in Shared mode. 492 | 493 | # Compatibility Issues 494 | 495 | In general, this mechanism is designed only to be used with 496 | servers which have opted in, thus minimizing compatibility 497 | issues. However, there are two scenarios where that does not 498 | apply, as detailed below. 499 | 500 | ## Misconfiguration 501 | 502 | If DNS is misconfigured so that a client receives ESNI keys for a 503 | server which is not prepared to receive ESNI, then the server will 504 | ignore the "encrypted_server_name" extension, as required by 505 | {{RFC8446}}; Section 4.1.2. If the servers does not 506 | require SNI, it will complete the handshake with its default 507 | certificate. Most likely, this will cause a certificate name 508 | mismatch and thus handshake failure. Clients SHOULD NOT fall 509 | back to cleartext SNI, because that allows a network attacker 510 | to disclose the SNI. They MAY attempt to use another server 511 | from the DNS results, if one is provided. 512 | 513 | 514 | ## Middleboxes 515 | 516 | A more serious problem is MITM proxies which do not support this 517 | extension. {{RFC8446}}; Section 9.3 requires that 518 | such proxies remove any extensions they do not understand. 519 | This will have one of two results when connecting to the client-facing 520 | server: 521 | 522 | 1. The handshake will fail if the client-facing server requires SNI. 523 | 2. The handshake will succeed with the client-facing server's default 524 | certificate. 525 | 526 | A Web client client can securely detect case (2) because it will result 527 | in a connection which has an invalid identity (most likely) 528 | but which is signed by a certificate which does not chain 529 | to a publicly known trust anchor. The client can detect this 530 | case and disable ESNI while in that network configuration. 531 | 532 | In order to enable this mechanism, client-facing servers SHOULD NOT 533 | require SNI, but rather respond with some default certificate. 534 | 535 | A non-conformant MITM proxy will forward the ESNI extension, 536 | substituting its own KeyShare value, with the result that 537 | the client-facing server will not be able to decrypt the SNI. 538 | This causes a hard failure. Detecting this case is difficult, 539 | but clients might opt to attempt captive portal detection 540 | to see if they are in the presence of a MITM proxy, and if 541 | so disable ESNI. Hopefully, the TLS 1.3 deployment experience 542 | has cleaned out most such proxies. 543 | 544 | 545 | # Security Considerations 546 | 547 | ## Why is cleartext DNS OK? {#cleartext-dns} 548 | 549 | In comparison to {{?I-D.kazuho-protected-sni}}, wherein DNS Resource 550 | Records are signed via a server private key, ESNIKeys have no 551 | authenticity or provenance information. This means that any attacker 552 | which can inject DNS responses or poison DNS caches, which is a common 553 | scenario in client access networks, can supply clients with fake 554 | ESNIKeys (so that the client encrypts SNI to them) or strip the 555 | ESNIKeys from the response. However, in the face of an attacker that 556 | controls DNS, no SNI encryption scheme can work because the attacker 557 | can replace the IP address, thus blocking client connections, or 558 | substituting a unique IP address which is 1:1 with the DNS name that 559 | was looked up (modulo DNS wildcards). Thus, allowing the ESNIKeys in 560 | the clear does not make the situation significantly worse. 561 | 562 | Clearly, DNSSEC (if the client validates and hard fails) is a defense against 563 | this form of attack, but DoH/DPRIVE are also defenses against DNS attacks 564 | by attackers on the local network, which is a common case where SNI. 565 | Moreover, as noted in the introduction, SNI encryption is less useful 566 | without encryption of DNS queries in transit via DoH or DPRIVE mechanisms. 567 | 568 | ## Comparison Against Criteria 569 | 570 | {{?I-D.ietf-tls-sni-encryption}} lists several requirements for SNI 571 | encryption. In this section, we re-iterate these requirements and assess 572 | the ESNI design against them. 573 | 574 | ### Mitigate against replay attacks 575 | 576 | Since the SNI encryption key is derived from a (EC)DH operation 577 | between the client's ephemeral and server's semi-static ESNI key, the ESNI 578 | encryption is bound to the Client Hello. It is not possible for 579 | an attacker to "cut and paste" the ESNI value in a different Client 580 | Hello, with a different ephemeral key share, as the terminating server 581 | will fail to decrypt and verify the ESNI value. 582 | 583 | ### Avoid widely-deployed shared secrets 584 | 585 | This design depends upon DNS as a vehicle for semi-static public key distribution. 586 | Server operators may partition their private keys however they see fit provided 587 | each server behind an IP address has the corresponding private key to decrypt 588 | a key. Thus, when one ESNI key is provided, sharing is optimally bound by the number 589 | of hosts that share an IP address. Server operators may further limit sharing 590 | by sending different Resource Records containing ESNIKeys with different keys 591 | using a short TTL. 592 | 593 | ### Prevent SNI-based DoS attacks 594 | 595 | This design requires servers to decrypt ClientHello messages with EncryptedSNI 596 | extensions carrying valid digests. Thus, it is possible for an attacker to force 597 | decryption operations on the server. This attack is bound by the number of 598 | valid TCP connections an attacker can open. 599 | 600 | ### Do not stick out 601 | 602 | As more clients enable ESNI support, e.g., as normal part of Web browser 603 | functionality, with keys supplied by shared hosting providers, the presence 604 | of ESNI extensions becomes less suspicious and part of common or predictable 605 | client behavior. In other words, if all Web browsers start using ESNI, 606 | the presence of this value does not signal suspicious behavior to passive 607 | eavesdroppers. 608 | 609 | ### Forward secrecy 610 | 611 | This design is not forward secret because the server's ESNI key is static. 612 | However, the window of exposure is bound by the key lifetime. It is 613 | RECOMMEMDED that servers rotate keys frequently. 614 | 615 | ### Proper security context 616 | 617 | This design permits servers operating in Split Mode to forward connections 618 | directly to backend origin servers, thereby avoiding unnecessary MiTM attacks. 619 | 620 | ### Split server spoofing 621 | 622 | Assuming ESNIKeys retrieved from DNS are validated, e.g., via DNSSEC or fetched 623 | from a trusted Recursive Resolver, spoofing a server operating in Split Mode 624 | is not possible. See {{cleartext-dns}} for more details regarding cleartext 625 | DNS. 626 | 627 | ### Supporting multiple protocols 628 | 629 | This design has no impact on application layer protocol negotiation. It may affect 630 | connection routing, server certificate selection, and client certificate verification. 631 | Thus, it is compatible with multiple protocols. 632 | 633 | ## Misrouting 634 | 635 | Note that the backend server has no way of knowing what the SNI was, 636 | but that does not lead to additional privacy exposure because the 637 | backend server also only has one identity. This does, however, change 638 | the situation slightly in that the backend server might previously have 639 | checked SNI and now cannot (and an attacker can route a connection 640 | with an encrypted SNI to any backend server and the TLS connection will 641 | still complete). However, the client is still responsible for 642 | verifying the server's identity in its certificate. 643 | 644 | [[TODO: Some more analysis needed in this case, as it is a little 645 | odd, and probably some precise rules about handling ESNI and no 646 | SNI uniformly?]] 647 | 648 | # IANA Considerations 649 | 650 | ## Update of the TLS ExtensionType Registry 651 | 652 | IANA is requested to Create an entry, encrypted_server_name(0xffce), 653 | in the existing registry for ExtensionType (defined in 654 | {{!RFC8446}}), with "TLS 1.3" column values being set to 655 | "CH, EE", and "Recommended" column being set to "Yes". 656 | 657 | ## Update of the DNS Underscore Global Scoped Entry Registry 658 | 659 | IANA is requested to create an entry in the DNS Underscore Global 660 | Scoped Entry Registry (defined in {{!I-D.ietf-dnsop-attrleaf}}) with the 661 | "RR Type" column value being set to "TXT", the "_NODE NAME" column 662 | value being set to "_esni", and the "Reference" column value being set 663 | to this document. 664 | 665 | --- back 666 | 667 | 668 | # Communicating SNI and Nonce to Backend Server {#communicating-sni} 669 | 670 | When operating in Split mode, backend servers will not have access 671 | to PaddedServerNameList.sni or PaddedServerNameList.nonce without 672 | access to the ESNI keys or a way to decrypt EncryptedSNI.encrypted_sni. 673 | 674 | One way to address this for a single connection, at the cost of having 675 | communication not be unmodified TLS 1.3, is as follows. 676 | Assume there is a shared (symmetric) key between the 677 | client-facing server and the backend server and use it to AEAD-encrypt Z 678 | and send the encrypted blob at the beginning of the connection before 679 | the ClientHello. The backend server can then decrypt ESNI to recover 680 | the true SNI and nonce. 681 | 682 | Another way for backend servers to access the true SNI and nonce is by the 683 | client-facing server sharing the ESNI keys. 684 | 685 | # Alternative SNI Protection Designs 686 | 687 | Alternative approaches to encrypted SNI may be implemented at the TLS or 688 | application layer. In this section we describe several alternatives and discuss 689 | drawbacks in comparison to the design in this document. 690 | 691 | ## TLS-layer 692 | 693 | ### TLS in Early Data 694 | 695 | In this variant, TLS Client Hellos are tunneled within early data payloads 696 | belonging to outer TLS connections established with the client-facing server. This 697 | requires clients to have established a previous session -— and obtained PSKs —- with 698 | the server. The client-facing server decrypts early data payloads to uncover Client Hellos 699 | destined for the backend server, and forwards them onwards as necessary. Afterwards, all 700 | records to and from backend servers are forwarded by the client-facing server -- unmodified. 701 | This avoids double encryption of TLS records. 702 | 703 | Problems with this approach are: (1) servers may not always be able to 704 | distinguish inner Client Hellos from legitimate application data, (2) nested 0-RTT 705 | data may not function correctly, (3) 0-RTT data may not be supported -- 706 | especially under DoS -- leading to availability concerns, and (4) clients must bootstrap 707 | tunnels (sessions), costing an additional round trip and potentially revealing the SNI 708 | during the initial connection. In contrast, encrypted SNI protects the SNI in a distinct 709 | Client Hello extension and neither abuses early data nor requires a bootstrapping connection. 710 | 711 | ### Combined Tickets 712 | 713 | In this variant, client-facing and backend servers coordinate to produce "combined tickets" 714 | that are consumable by both. Clients offer combined tickets to client-facing servers. 715 | The latter parse them to determine the correct backend server to which the Client Hello 716 | should be forwarded. This approach is problematic due to non-trivial coordination between 717 | client-facing and backend servers for ticket construction and consumption. Moreover, 718 | it requires a bootstrapping step similar to that of the previous variant. In contrast, 719 | encrypted SNI requires no such coordination. 720 | 721 | ## Application-layer 722 | 723 | ### HTTP/2 CERTIFICATE Frames 724 | 725 | In this variant, clients request secondary certificates with CERTIFICATE_REQUEST HTTP/2 726 | frames after TLS connection completion. In response, servers supply certificates via TLS 727 | exported authenticators {{!I-D.ietf-tls-exported-authenticator}} in CERTIFICATE frames. 728 | Clients use a generic SNI for the underlying client-facing server TLS connection. 729 | Problems with this approach include: (1) one additional round trip before peer 730 | authentication, (2) non-trivial application-layer dependencies and interaction, 731 | and (3) obtaining the generic SNI to bootstrap the connection. In contrast, encrypted 732 | SNI induces no additional round trip and operates below the application layer. 733 | 734 | 735 | # Total Client Hello Encryption 736 | 737 | The design described here only provides encryption for the SNI, but 738 | not for other extensions, such as ALPN. Another potential design 739 | would be to encrypt all of the extensions using the same basic 740 | structure as we use here for ESNI. That design has the following 741 | advantages: 742 | 743 | - It protects all the extensions from ordinary eavesdroppers 744 | - If the encrypted block has its own KeyShare, it does not 745 | necessarily require the client to use a single KeyShare, 746 | because the client's share is bound to the SNI by the 747 | AEAD (analysis needed). 748 | 749 | It also has the following disadvantages: 750 | 751 | - The client-facing server can still see the other extensions. By 752 | contrast we could introduce another EncryptedExtensions 753 | block that was encrypted to the backend server and not 754 | the client-facing server. 755 | - It requires a mechanism for the client-facing server to provide the 756 | extension-encryption key to the backend server (as in {{communicating-sni}} 757 | and thus cannot be used with an unmodified backend server. 758 | - A conformant middlebox will strip every extension, which might 759 | result in a ClientHello which is just unacceptable to the server 760 | (more analysis needed). 761 | 762 | # Acknowledgments 763 | 764 | This document draws extensively from ideas in {{?I-D.kazuho-protected-sni}}, but 765 | is a much more limited mechanism because it depends on the DNS for the 766 | protection of the ESNI key. Richard Barnes, Christian Huitema, Patrick McManus, 767 | Matthew Prince, Nick Sullivan, Martin Thomson, and Chris Wood also provided 768 | important ideas. 769 | 770 | 771 | --------------------------------------------------------------------------------