[
  {
    "path": ".github/workflows/gh-pages.yml",
    "content": "name: deploy gh-pages\n\non:\n  pull_request:\n    types: [closed]\n    branches: [master]\n\n  push:\n    branches: [master]\n\njobs:\n  build-and-deploy:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout 🛎️\n        uses: actions/checkout@v2\n\n      - name: Environment Setup 🔧\n        uses: actions/setup-node@v4\n        with:\n          node-version: \"18.x\"\n\n      - name: Cache dependencies 🍪\n        uses: actions/cache@v4\n        id: cache\n        with:\n          path: |\n            **/.npm\n            **/node_modules/\n          key: ${{ runner.os }}-node-${{ hashFiles('**/package.json') }}\n          restore-keys: |\n            ${{ runner.os }}-node-\n      - name: Install dependencies without Proxy 📁\n        # only install when cache miss or package.json changed\n        if: steps.cache.outputs.cache-hit != 'true'\n        run: npm --proxy=null --https_proxy=null --strict-ssl=true install\n\n      - name: Cache buildings 🏗\n        uses: actions/cache@v4\n        id: building\n        with:\n          path: |\n            **/.parcel-cache/\n            **/dist/\n          key: ${{ runner.os }}-build\n\n      - name: Build 🚧\n        run: npm run build\n\n      - name: Deploy 🚀\n        uses: peaceiris/actions-gh-pages@v3\n        with:\n          github_token: ${{ secrets.GITHUB_TOKEN }}\n          publish_branch: gh-pages\n          publish_dir: ./dist"
  },
  {
    "path": ".gitignore",
    "content": "build\ndist\nnode_modules\n\n.parcel-cache\n\n*.local\n\nyarn.lock\npackage-lock.json\n"
  },
  {
    "path": ".npmrc",
    "content": "registry = https://registry.npmmirror.com/\n"
  },
  {
    "path": ".parcelrc",
    "content": "{\n  \"extends\": [\n    \"@parcel/config-default\"\n  ],\n  \"reporters\": [\n    \"...\",\n    \"parcel-reporter-static-files-copy\"\n  ]\n}\n"
  },
  {
    "path": ".prettierrc",
    "content": "{\n  \"trailingComma\": \"es5\",\n  \"singleQuote\": false,\n  \"tabWidth\": 2,\n  \"semi\": true,\n  \"endOfLine\": \"lf\"\n}\n"
  },
  {
    "path": ".vscode/settings.json",
    "content": "{\n  \"cSpell.words\": [\"middleboxes\", \"middlebox\", \"DTLS\", \"QUIC\"]\n}\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2023 Allen Lee\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE."
  },
  {
    "path": "README.md",
    "content": "# illustrate QUIC, TLS 1.2, TLS 1.3, DTLS 中文翻译\n\n图解 QUIC, TLS 1.2, TLS 1.3, DTLS 协议的连接及会话过程\n\n翻译自：\n\n- https://quic.xargs.org/\n- https://dtls.xargs.org/\n- https://tls12.xargs.org/\n- https://tls13.xargs.org/\n- https://x25519.xargs.org/\n\n原作者 [syncsynchalt](https://github.com/syncsynchalt), 译者 [AllenLee](https://github.com/cangSDARM)\n\n## 进度\n\n- [x] QUIC\n- [x] DTLS\n- [x] TLS 1.2\n- [x] TLS 1.3\n- [x] x25519\n"
  },
  {
    "path": "frombootstrap.css",
    "content": "/* everything we wanted from bootstrap but nothing more */\n\n*,\n*::before,\n*::after {\n  box-sizing: border-box;\n}\n\nhtml {\n  line-height: 1.15;\n  -webkit-text-size-adjust: 100%;\n  -ms-text-size-adjust: 100%;\n  -ms-overflow-style: scrollbar;\n  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n}\n\nbody {\n  margin: 0;\n  font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto,\n    \"Helvetica Neue\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\",\n    \"Segoe UI Symbol\", \"Noto Color Emoji\";\n  font-size: 1rem;\n  font-weight: 400;\n  line-height: 1.5;\n  color: #212529;\n  text-align: left;\n  background-color: #fff;\n}\n\np {\n  margin-top: 0;\n  margin-bottom: 1rem;\n}\n\na {\n  color: #007bff;\n  text-decoration: none;\n  background-color: transparent;\n  -webkit-text-decoration-skip: objects;\n}\na:hover {\n  color: #0056b3;\n  text-decoration: underline;\n}\n\npre,\ncode,\nkbd,\nsamp {\n  font-family: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\",\n    \"Courier New\", monospace;\n  font-size: 1em;\n}\n\nh1,\nh2,\nh3,\nh4,\nh5,\nh6,\n.h1,\n.h2,\n.h3,\n.h4,\n.h5,\n.h6 {\n  margin-top: 0;\n  margin-bottom: 0.5rem;\n  font-weight: 500;\n  line-height: 1.2;\n}\nh1,\n.h1 {\n  font-size: 2.5rem;\n}\nh2,\n.h2 {\n  font-size: 2rem;\n}\nh3,\n.h3 {\n  font-size: 1.75rem;\n}\nh4,\n.h4 {\n  font-size: 1.5rem;\n}\nh5,\n.h5 {\n  font-size: 1.25rem;\n}\nh6,\n.h6 {\n  font-size: 1rem;\n}\n\npre {\n  display: block;\n  font-size: 87.5%;\n  color: #212529;\n}\n\ntable {\n  background-color: transparent;\n  border-spacing: 0;\n  border-collapse: collapse;\n}\ntd, th {\n  padding: 0;\n}\nth {\n  text-align: left;\n}\n.table {\n  width: 100%;\n  max-width: 100%;\n  margin-bottom: 20px;\n}\n.table > thead > tr > th,\n.table > tbody > tr > th,\n.table > tfoot > tr > th,\n.table > thead > tr > td,\n.table > tbody > tr > td,\n.table > tfoot > tr > td {\n  padding: 8px;\n  line-height: 1.42857143;\n  vertical-align: top;\n  border-top: 1px solid #ddd;\n}\n.table > thead > tr > th {\n  vertical-align: bottom;\n  border-bottom: 2px solid #ddd;\n}\n.table > caption + thead > tr:first-child > th,\n.table > colgroup + thead > tr:first-child > th,\n.table > thead:first-child > tr:first-child > th,\n.table > caption + thead > tr:first-child > td,\n.table > colgroup + thead > tr:first-child > td,\n.table > thead:first-child > tr:first-child > td {\n  border-top: 0;\n}\n.table > tbody + tbody {\n  border-top: 2px solid #ddd;\n}\n.table .table {\n  background-color: #fff;\n}\n.table-condensed > thead > tr > th,\n.table-condensed > tbody > tr > th,\n.table-condensed > tfoot > tr > th,\n.table-condensed > thead > tr > td,\n.table-condensed > tbody > tr > td,\n.table-condensed > tfoot > tr > td {\n  padding: 5px;\n}\ntable col[class*=\"col-\"] {\n  position: static;\n  display: table-column;\n  float: none;\n}\ntable td[class*=\"col-\"],\ntable th[class*=\"col-\"] {\n  position: static;\n  display: table-cell;\n  float: none;\n}\n\n.container {\n  width: 100%;\n  padding-right: 15px;\n  padding-left: 15px;\n  margin-right: auto;\n  margin-left: auto;\n}\n\n@media (min-width: 400px) {\n  .container {\n    padding-left: 5px;\n    padding-right: 5px;\n  }\n}\n\n@media (min-width: 768px) {\n  .container {\n    max-width: 720px;\n  }\n}\n\n@media (min-width: 992px) {\n  .container {\n    max-width: 960px;\n  }\n}\n\n@media (min-width: 1200px) {\n  .container {\n    max-width: 1140px;\n  }\n}\n"
  },
  {
    "path": "index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"cn\" dir=\"ltr\">\n\n<head>\n  <script type=\"text/javascript\">\n    // Single Page Apps for GitHub Pages\n    // MIT License\n    // https://github.com/rafgraph/spa-github-pages\n    // This script checks to see if a redirect is present in the query string,\n    // converts it back into the correct url and adds it to the\n    // browser's history using window.history.replaceState(...),\n    // which won't cause the browser to attempt to load the new url.\n    // When the single page app is loaded further down in this file,\n    // the correct url will be waiting in the browser's history for\n    // the single page app to route accordingly.\n    (function (l) {\n      if (l.search[1] === '/') {\n        var decoded = l.search.slice(1).split('&').map(function (s) {\n          return s.replace(/~and~/g, '&')\n        }).join('?');\n        var path = l.pathname.slice(0, -1) + decoded + l.hash;\n        window.history.replaceState({ path }, null,\n          path,\n        );\n      }\n    }(window.location));\n  </script>\n\n  <meta charset=\"utf-8\" />\n  <title>图解</title>\n  <meta name=\"description\" content=\"图解 QUIC, TLS 1.2, TLS 1.3, DTLS 协议的连接及会话过程\" />\n  <link rel='canonical' href='https://cangsdarm.github.io/illustrate/' />\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\" />\n  <link rel=\"stylesheet\" href=\"./frombootstrap.css?bust\" />\n\n  <meta name=\"msapplication-TileColor\" content=\"#da532c\" />\n  <meta name=\"theme-color\" content=\"#ffffff\" />\n  \n  <meta content=\"图解,QUIC,TLS 1.2,TLS 1.3,DTLS,协议连接,会话过程\" name=\"Keywords\" />\n  <meta name=\"google-site-verification\" content=\"Qa9IAETG_rUMzy5ZXHjOxjAK7awElBSO6p_XVLGzuZk\" />\n\n</head>\n\n<body>\n  <div id=\"app\"></div>\n  <script type=\"module\" src=\"src/index.js\"></script>\n  <script type=\"module\" async src=\"./print.js\"></script>\n  <a class=\"print-mode\" href=\"#print\" onclick=\"globalThis.illustrate.printMode()\">\n    [print, TODO]\n  </a>\n</body>\n\n</html>\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"illustrate\",\n  \"version\": \"1.0.0\",\n  \"description\": \"\",\n  \"scripts\": {\n    \"start\": \"parcel index.html\",\n    \"format\": \"prettier --write \\\"./src/**/*.{js,jsx,css,scss}\\\"\",\n    \"rm:cache\": \"rimraf .parcel-cache && rimraf ./dist\",\n    \"build\": \"npm run rm:cache && parcel build index.html --public-url ./\"\n  },\n  \"author\": \"AllenLee<648384410li@gmail.com>\",\n  \"license\": \"MIT\",\n  \"staticFiles\": {\n    \"staticPath\": \"public\"\n  },\n  \"devDependencies\": {\n    \"parcel\": \"^2.8.3\",\n    \"parcel-reporter-static-files-copy\": \"^1.5.0\",\n    \"prettier\": \"^2.8.7\",\n    \"process\": \"^0.11.10\",\n    \"rimraf\": \"^5.0.1\"\n  },\n  \"dependencies\": {\n    \"clsx\": \"^1.2.1\",\n    \"github-slugger\": \"^2.0.0\",\n    \"react\": \"^18.2.0\",\n    \"react-dom\": \"^18.2.0\",\n    \"react-wrap-balancer\": \"^0.4.0\"\n  }\n}\n"
  },
  {
    "path": "print.js",
    "content": "globalThis.illustrate = {\n  printMode: () => {\n    // add printmode css\n    let inject = document.createElement(\"link\");\n    inject.setAttribute(\"rel\", \"stylesheet\");\n    inject.setAttribute(\"href\", \"printmode.css\");\n    document.head.appendChild(inject);\n    // open everything up\n    // TODO:\n    // [].forEach.call(document.querySelectorAll(\".record, .calculation\"), (el) => {\n    //   el.classList.add(\"selected\");\n    //   el.classList.add(\"annotate\");\n    // });\n    // [].forEach.call(document.querySelectorAll(\"codesample\"), (el) => {\n    //   el.classList.add(\"show\");\n    // });\n    [].forEach.call(document.querySelectorAll(\"*\"), (el) => {\n      el.onclick = null;\n    });\n  }\n};\n"
  },
  {
    "path": "printmode.css",
    "content": "/* print mode */\n\n@media (min-width: 0) {\n  .container {\n    max-width: none !important;\n    margin: 5px 0 !important;\n  }\n}\n\n*,\n*:hover {\n  color: #000 !important;\n  background-color: #fff !important;\n  text-shadow: none !important;\n}\n\n.illustration {\n  display: none !important;\n}\n\nbutton {\n  display: none !important;\n}\n\n.client,\n.server {\n  background-color: #fff !important;\n  border: 1px solid black !important;\n  box-shadow: none !important;\n  max-width: none !important;\n  margin: 1em 0;\n}\n\n.rec-label:after {\n  content: \"\" !important;\n}\n\n.string > .explanation,\n.decryption > .explanation {\n  background-color: #fff !important;\n  box-shadow: none !important;\n  border: 2px solid black !important;\n}\n\n.record.annotate .string > .explanation:before {\n  display: none;\n}\n\npre code {\n  border-radius: 0 !important;\n  border: 2px solid black !important;\n  white-space: pre-wrap !important;\n}\n\na:after {\n  content: \" [link]\";\n}\n\na.no-show:after {\n  content: \"\";\n}\n\n.print-mode {\n  display: none;\n}\n\n.header {\n  display: none;\n}\n"
  },
  {
    "path": "public/.nojekyll",
    "content": ""
  },
  {
    "path": "public/404.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<head>\n    <meta charset=\"utf-8\">\n    <title>Single Page Apps for GitHub Pages</title>\n    <script type=\"text/javascript\">\n        // Single Page Apps for GitHub Pages\n        // MIT License\n        // https://github.com/rafgraph/spa-github-pages\n        // This script takes the current url and converts the path and query\n        // string into just a query string, and then redirects the browser\n        // to the new url with only a query string and hash fragment,\n        // e.g. https://www.foo.tld/one/two?a=b&c=d#qwe, becomes\n        // https://www.foo.tld/?/one/two&a=b~and~c=d#qwe\n        // Note: this 404.html file must be at least 512 bytes for it to work\n        // with Internet Explorer (it is currently > 512 bytes)\n\n        // If you're creating a Project Pages site and NOT using a custom domain,\n        // then set pathSegmentsToKeep to 1 (enterprise users may need to set it to > 1).\n        // This way the code will only replace the route part of the path, and not\n        // the real directory in which the app resides, for example:\n        // https://username.github.io/repo-name/one/two?a=b&c=d#qwe becomes\n        // https://username.github.io/repo-name/?/one/two&a=b~and~c=d#qwe\n        // Otherwise, leave pathSegmentsToKeep as 0.\n        var pathSegmentsToKeep = 1;\n\n        var l = window.location;\n        l.replace(\n            l.protocol + '//' + l.hostname + (l.port ? ':' + l.port : '') +\n            l.pathname.split('/').slice(0, 1 + pathSegmentsToKeep).join('/') + '/?/' +\n            l.pathname.slice(1).split('/').slice(pathSegmentsToKeep).join('/').replace(/&/g, '~and~') +\n            (l.search ? '&' + l.search.slice(1).replace(/&/g, '~and~') : '') +\n            l.hash\n        );\n    </script>\n</head>\n\n<body>\n</body>\n\n</html>"
  },
  {
    "path": "public/sitemap.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\"\n    xmlns:news=\"http://www.google.com/schemas/sitemap-news/0.9\"\n    xmlns:xhtml=\"http://www.w3.org/1999/xhtml\"\n    xmlns:image=\"http://www.google.com/schemas/sitemap-image/1.1\"\n    xmlns:video=\"http://www.google.com/schemas/sitemap-video/1.1\">\n    <url>\n        <loc>https://cangsdarm.github.io/illustrate/</loc>\n        <changefreq>weekly</changefreq>\n        <priority>0.5</priority>\n    </url>\n    <url>\n        <loc>https://cangsdarm.github.io/illustrate/quic</loc>\n        <changefreq>weekly</changefreq>\n        <priority>0.5</priority>\n    </url>\n    <url>\n        <loc>https://cangsdarm.github.io/illustrate/dtls</loc>\n        <changefreq>weekly</changefreq>\n        <priority>0.5</priority>\n    </url>\n    <url>\n        <loc>https://cangsdarm.github.io/illustrate/tls13</loc>\n        <changefreq>weekly</changefreq>\n        <priority>0.5</priority>\n    </url>\n    <url>\n        <loc>https://cangsdarm.github.io/illustrate/tls12</loc>\n        <changefreq>weekly</changefreq>\n        <priority>0.5</priority>\n    </url>\n</urlset>"
  },
  {
    "path": "src/App.jsx",
    "content": "import React from \"react\";\n\nimport \"./common.css\";\nimport \"./hard-encoded.css\";\nimport \"./illustrated.css\";\n\nimport Header from \"./Header\";\nimport Intro from \"./Intro\";\nimport { routers, base } from \"./router\";\nimport RecOuter from \"./RecOuter\";\nimport { SluggerContextProvider } from \"./context/slugger\";\nimport Datagram from \"./Datagram\";\nimport Footer from \"./Footer\";\n\nconst App = () => {\n  const [curPage, setCurPage] = React.useState();\n  const [loading, setLoading] = React.useState(false);\n  const [JSON, setJSON] = React.useState({});\n\n  React.useEffect(() => {\n    curPage?.json().then((json) => {\n      setJSON(json?.default || json);\n      setLoading(false);\n    });\n  }, [curPage]);\n\n  return (\n    <>\n      <Header\n        routers={routers}\n        base={base}\n        onRouterChange={(rt) => {\n          window.document.title = rt.title;\n          setLoading(true);\n          setCurPage(rt);\n        }}\n      />\n      {loading ? (\n        <div>loading...</div>\n      ) : (\n        <SluggerContextProvider>\n          <Intro {...JSON?.intro} />\n          {(function () {\n            let datagramMeta = { name: \"\", children: [], length: 0 };\n\n            return JSON?.sections?.map((sec) => {\n              const { type, tags, datagram, ...restSec } = sec;\n              const key = sec.id || sec.label;\n\n              if (type === \"Datagram\") {\n                datagramMeta.name = sec.label;\n                datagramMeta.length = datagram;\n                datagramMeta.children = [];\n\n                return undefined;\n              }\n\n              if (datagramMeta.name) {\n                let newLen = datagramMeta.children.length;\n                if (newLen < datagramMeta.length) {\n                  newLen = datagramMeta.children.push(\n                    <RecOuter key={sec.label} types={tags} {...restSec} />\n                  );\n                }\n\n                if (newLen < datagramMeta.length) {\n                  return undefined;\n                } else {\n                  const { name, children } = datagramMeta;\n                  datagramMeta = {};\n                  return (\n                    <Datagram key={key} label={name} children={children} />\n                  );\n                }\n              }\n\n              return <RecOuter key={key} types={tags} {...restSec} />;\n            });\n          })()}\n          <Footer {...JSON.ending} />\n        </SluggerContextProvider>\n      )}\n    </>\n  );\n};\n\nexport default App;\n"
  },
  {
    "path": "src/DTLS/clientApplicationDataDatagram.json",
    "content": "[\n  \"客户端发送数据：字符串\\\"ping\\\"\",\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"头部信息字节\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"2f\"\n          },\n          [\n            \"加密的 DTLS 数据包都以一个 \\\"统一的头部(unified header)\\\"开始。头部的第一个字节给出了头部和数据包的结构信息，以及解密时需要的信息。\",\n            \"值 0x2f 具有以下含义：\",\n            {\n              \"Tag\": \"Table\",\n              \"props\": {\n                \"headers\": [\"\", \"值\", \"含义\"],\n                \"data\": [\n                  [\"高位\", \"001\", \"固定位\"],\n                  [\"\", \"0\", \"头部中不存在连接 ID 字段(1则存在)\"],\n                  [\"\", \"1\", \"序列号在头部中占 2 字节长\"],\n                  [\"\", \"1\", \"头部中存在\\\"记录长度\\\"字段(0则不存在)\"],\n                  [\n                    \"低位\",\n                    \"11\",\n                    \"加密序列指示(Encryption epoch 3)，现在密钥是会话时密钥\"\n                  ]\n                ]\n              }\n            }\n          ]\n        ],\n        [\n          \"记录序号\",\n          {\n            \"props\": {\n              \"className\": \"bytes protected\",\n              \"title\": \"被加密\"\n            },\n            \"content\": \"68 3f\"\n          },\n          {\n            \"props\": {\n              \"className\": \"bytes unprotected\"\n            },\n            \"content\": \"00 00\"\n          },\n          [\n            \"记录序号是被加密了的，用以防止中间件误解(interpreting)或干扰数据包的排序。\",\n            \"加密是通过用 \\\"客户端序号保护密钥\\\" 对每个数据包的有效载荷样本进行加密，然后将每个数据包中的某些比特和字节与所得数据进行 XOR 得到。\",\n            \"如果说的不够详细，这里有一个如何加密的例子：\",\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### \\\"client record number key\\\" from application keys calc step above\\n$ key=5cb5bd8bac29777c650c0dde22d16d47\\n### sample is taken from 16 bytes of payload starting 5 bytes into the record\\n$ sample=7d72278b6c649f1e7b56b3cad411faf7\\n$ echo $sample | xxd -r -p | openssl aes-128-ecb -K $key | head -c 2 | xxd -p\\n\\n683f\\n\\n### the above bytes are xor'd one-for-one into the bytes of the record number\"\n              }\n            }\n          ]\n        ],\n        [\n          \"记录长度\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 15\"\n          },\n          [\n            \"每个记录除非给出这个长度字段，否则对等端将认为数据报剩余的所有字节都是同一个记录的真实载荷。有了这一字段，则在一个数据报中可以发送好几个 DTLS 记录(尽管例子中的连接没有利用这个优势)。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 15 - 表示 DTLS 记录长度为 0x15(21) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"加密的数据载荷\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\",\n              \"title\": \"被\\\"会话密钥\\\"加密\"\n            },\n            \"content\": \"7d 72 27 8b 6c\"\n          },\n          [\"这些数据使用客户端的\\\"会话密钥\\\"进行加密。\"]\n        ],\n        [\n          \"AEAD 鉴别标签\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"64 9f 1e 7b 56 b3 ca d4 11 fa f7 bd 51 8b fb 15\"\n          },\n          [\n            {\n              \"children\": [\n                \"这是 \",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://zhuanlan.zhihu.com/p/28566058\"\n                  },\n                  \"content\": \"AEAD 算法\"\n                },\n                \"的鉴别标签，确认加密数据和记录头的完整性。它由加密算法产生，并由解密算法消耗。\"\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"data\": [\n        [\n          \"\",\n          {\n            \"props\": {\n              \"className\": \"decryption-header\"\n            },\n            \"content\": \"解密后的数据载荷\"\n          },\n          [\n            { \"Tag\": \"h4\", \"content\": \"解密\" },\n            \"数据被 \\\"客户端会话密钥计算\\\" 步骤中产生的初始密钥和初始向量(IVs)加密。IVs 通过密钥和已经用密钥加密的记录长度进行异或操作生成。在例子中 IV 为 0。\",\n            \"数据包开头的 5 字节(记录头)还会作为解密过程解密成功时必须满足的认证条件。\",\n            {\n              \"children\": [\n                \"openssl 命令行工具还不支持 AEAD 算法加解密(AEAD ciphers)，你可以使用作者的命令行工具来\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://dtls.xargs.org/files/aes_128_gcm_decrypt.c\"\n                  },\n                  \"content\": \"解密\"\n                },\n                \"和\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://dtls.xargs.org/files/aes_128_gcm_encrypt.c\"\n                  },\n                  \"content\": \"加密\"\n                },\n                \"这些数据。\"\n              ]\n            },\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### from the \\\"Client Application Keys Calc\\\" step\\n$ key=9ba90dbce8857bc1fcb81d41a0465cfe\\n$ iv=682219974631fa0656ee4eff\\n### from this record\\n$ recdata=2f00000015\\n$ authtag=649f1e7b56b3cad411faf7bd518bfb15\\n$ recordnum=0\\n### may need to add -I and -L flags for include and lib dirs\\n$ cc -o aes_128_gcm_decrypt aes_128_gcm_decrypt.c -lssl -lcrypto\\n$ cat /tmp/msg1   | ./aes_128_gcm_decrypt $iv $recordnum $key $recdata $authtag   | hexdump -C\\n\\n00000000  70 69 6e 67 17                                    |ping.|\"\n              }\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"数据\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"70 69 6e 67\"\n          },\n          [\"字符串\\\"ping\\\"\"]\n        ],\n        [\n          \"记录类型\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"17\"\n          },\n          [\n            \"每一个加密的 DTLS 1.3 记录的最后一个字节都需要表明其真正的记录类型\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"17 - 类型为 0x17(23), 会话数据\"\n                }\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/DTLS/clientApplicationKeysCalc.json",
    "content": "[\n  \"客户端现在也可以计算应用会话时的密钥了。如果计算正确，结果应和服务器端的一致：\",\n  {\n    \"Tag\": \"ul\",\n    \"children\": [\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"客户端会话密钥: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"9ba90dbce8857bc1fcb81d41a0465cfe\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"客户端会话向量: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"682219974631fa0656ee4eff\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"客户端序号保护密钥: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"5cb5bd8bac29777c650c0dde22d16d47\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"服务器端会话密钥: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"2b65fffbbc8189474aa2003c43c32d4d\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"服务器端会话向量: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"582f5a11bdaf973fe3ffeb4e\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"服务器端序号保护密钥: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"57ba02596c6a1352d7fe8416c7e17d5a\"\n          }\n        ]\n      }\n    ]\n  }\n]\n"
  },
  {
    "path": "src/DTLS/clientHandshakeFinishedDatagram.json",
    "content": "[\n  \"为了验证握手成功且没有被篡改过，客户端和服务器端一样，需要创建一些验证数据给服务器端确认。验证数据是基于所有握手信息的哈希值计算得到。\",\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"头部信息字节\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"2e\"\n          },\n          [\n            \"加密的 DTLS 数据包都以一个 \\\"统一的头部(unified header)\\\"开始。头部的第一个字节给出了头部和数据包的结构信息，以及解密时需要的信息。\",\n            \"值 0x2e 具有以下含义：\",\n            {\n              \"Tag\": \"Table\",\n              \"props\": {\n                \"headers\": [\"\", \"值\", \"含义\"],\n                \"data\": [\n                  [\"高位\", \"001\", \"固定位\"],\n                  [\"\", \"0\", \"头部中不存在连接 ID 字段(1则存在)\"],\n                  [\"\", \"1\", \"序列号在头部中占 2 字节长\"],\n                  [\"\", \"1\", \"头部中存在\\\"记录长度\\\"字段(0则不存在)\"],\n                  [\n                    \"低位\",\n                    \"10\",\n                    \"加密序列指示(Encryption epoch 2)，现在密钥是握手时密钥\"\n                  ]\n                ]\n              }\n            }\n          ]\n        ],\n        [\n          \"记录序号\",\n          {\n            \"props\": {\n              \"className\": \"bytes protected\",\n              \"title\": \"被加密\"\n            },\n            \"content\": \"c2 48\"\n          },\n          {\n            \"props\": {\n              \"className\": \"bytes unprotected\"\n            },\n            \"content\": \"00 00\"\n          },\n          [\n            \"记录序号是被加密了的，用以防止中间件误解(interpreting)或干扰数据包的排序。\",\n            \"加密是通过用 \\\"客户端序号保护密钥\\\" 对每个数据包的有效载荷样本进行加密，然后将每个数据包中的某些比特和字节与所得数据进行 XOR 得到。\",\n            \"如果说的不够详细，这里有一个如何加密的例子：\",\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### \\\"client record number key\\\" from handshake keys calc step above\\n$ key=beed6218676635c2cb46a45694144fec\\n### sample is taken from 16 bytes of payload starting 5 bytes into the record\\n$ sample=8a2cd52d5000f8786afb47cdf0b8f2b8\\n$ echo $sample | xxd -r -p | openssl aes-128-ecb -K $key | head -c 2 | xxd -p\\n\\nc248\\n\\n### the above bytes are xor'd one-for-one into the bytes of the record number\"\n              }\n            }\n          ]\n        ],\n        [\n          \"记录长度\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 3d\"\n          },\n          [\n            \"每个记录除非给出这个长度字段，否则对等端将认为数据报剩余的所有字节都是同一个记录的真实载荷。有了这一字段，则在一个数据报中可以发送好几个 TLS 记录(尽管例子中的连接没有利用这个优势)。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 3d - 表示 TLS 记录长度为 0x3d(61) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"加密的数据载荷\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\",\n              \"title\": \"被\\\"握手密钥\\\"加密\"\n            },\n            \"content\": \"8a 2c d5 2d 50 00 f8 78 6a fb 47 cd f0 b8 f2 b8 13 42 b0 0c 43 dc e6 4b 1d 01 94 d2 e2 01 f6 81 75 09 78 52 8b be 26 af 79 61 24 01 c0\"\n          },\n          [\"这些数据使用服务器端的\\\"握手密钥\\\"进行加密。\"]\n        ],\n        [\n          \"AEAD 鉴别标签\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"07 a2 c5 f7 5f 7c ff b7 46 5b c0 1d 23 d8 51 1f\"\n          },\n          [\n            {\n              \"children\": [\n                \"这是 \",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://zhuanlan.zhihu.com/p/28566058\"\n                  },\n                  \"content\": \"AEAD 算法\"\n                },\n                \"的鉴别标签，确认加密数据和记录头的完整性。它由加密算法产生，并由解密算法消耗。\"\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"data\": [\n        [\n          \"\",\n          {\n            \"props\": {\n              \"className\": \"decryption-header\"\n            },\n            \"content\": \"解密后的数据载荷\"\n          },\n          [\n            { \"Tag\": \"h4\", \"content\": \"解密\" },\n            \"数据被 \\\"客户端握手密钥计算\\\" 步骤中产生的初始密钥和初始向量(IVs)加密。IVs 通过密钥和已经用密钥加密的记录长度进行异或操作生成。在例子中 IV 为 0。\",\n            \"数据包开头的 5 字节(记录头)还会作为解密过程解密成功时必须满足的认证条件。\",\n            {\n              \"children\": [\n                \"openssl 命令行工具还不支持 AEAD 算法加解密(AEAD ciphers)，你可以使用作者的命令行工具来\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://dtls.xargs.org/files/aes_128_gcm_decrypt.c\"\n                  },\n                  \"content\": \"解密\"\n                },\n                \"和\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://dtls.xargs.org/files/aes_128_gcm_encrypt.c\"\n                  },\n                  \"content\": \"加密\"\n                },\n                \"这些数据。\"\n              ]\n            },\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### from the \\\"Client Handshake Keys Calc\\\" step\\n$ key=6caa2633d5e48f10051e69dc45549c97\\n$ iv=106dc6e393b7a9ea8ef29dd7\\n### from this record\\n$ recdata=2e0000003d\\n$ authtag=07a2c5f75f7cffb7465bc01d23d8511f\\n$ recordnum=0\\n### may need to add -I and -L flags for include and lib dirs\\n$ cc -o aes_128_gcm_decrypt aes_128_gcm_decrypt.c -lssl -lcrypto\\n$ cat /tmp/msg1   | ./aes_128_gcm_decrypt $iv $recordnum $key $recdata $authtag   | hexdump -C\\n\\n00000000  14 00 00 20 00 01 00 00  00 00 00 20 6f 28 01 39\\n00000010  6b 0e 90 eb b4 a3 ba 38  4a bc fc 6b 24 20 1a bd\\n00000020  81 b3 16 b2 39 1d a3 78  37 7f ac f5 16\"\n              }\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"握手消息头\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"14 00 00 20\"\n          },\n          [\n            \"每个握手消息都以一个 type 和一个 len 开始。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"14 - 握手消息类型 0x14 (finished)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 20 - 紧接着的握手消息数据的长度 0x20 (32) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"用于重建握手顺序的信息(Handshake Reconstruction Data)\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"00 01 00 00 00 00 00 20\"\n          },\n          [\n            \"因为 UDP (或其他数据报协议)不保证交付或排序，而且数据报的长度可能比需要发送的握手记录长度要小。因此 DTLS 必须提供一定的信息，以支持在数据丢失、包重排序或有记录碎片的情况下，使得对等端(peer)能够重新构建一条正确的 DTLS 记录。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 01 - DTLS 序列号 0x1(1)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 00 - 表明记录碎片的偏移量为 0x00(0) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 20 - 表明之后的记录碎片的长度为 0x20(32) 字节\"\n                }\n              ]\n            },\n            \"在本文例子中，整个握手记录的长度要短于一个 UDP 数据报的可承载长度，因此偏移量为零，且长度为整个握手记录长。\"\n          ]\n        ],\n        [\n          \"验证数据\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"6f 28 01 39 6b 0e 90 eb b4 a3 ba 38 4a bc fc 6b 24 20 1a bd 81 b3 16 b2 39 1d a3 78 37 7f ac f5\"\n          },\n          [\n            \"使用 \\\"客户端生成握手密钥\\\" 步骤中的客户端密文和在这之前的每个握手记录(ClientHello 到 服务器端握手结束)的 SHA256 哈希值生成。\",\n            {\n              \"Tag\": \"pre\",\n              \"children\": [\n                {\n                  \"Tag\": \"code\",\n                  \"props\": { \"className\": \"longboi\" },\n                  \"content\": \"finished_key = HKDF-Expand-Label(key: client_secret, label: \\\"finished\\\", ctx: \\\"\\\", len: 32)\\nfinished_hash = SHA256(Client Hello ... Server Handshake Finished)\\nverify_data = HMAC-SHA256(key: finished_key, msg: finished_hash)\"\n                }\n              ]\n            },\n            {\n              \"children\": [\n                \"在命令行中使用\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://dtls.xargs.org/files/hkdf-dtls.sh\"\n                  },\n                  \"content\": \"原作者制作的 HKDF 命令行脚本\"\n                },\n                \"，你也可以自己试试：\"\n              ]\n            },\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### find the hash of the conversation to this point, excluding\\n### cleartext record headers, DTLS-only record headers,\\n### or 1-byte decrypted record trailers\\n$ fin_hash=$((\\n    cat record-chello  | perl -0777 -pe 's/.{13}(.{4}).{8}/$1/s';\\n    cat record-shello  | perl -0777 -pe 's/.{13}(.{4}).{8}/$1/s';\\n    cat record-encext  | perl -0777 -pe 's/(.{4}).{8}(.*).$/$1$2/s';\\n    cat record-cert    | perl -0777 -pe 's/(.{4}).{8}(.*).$/$1$2/s';\\n    cat record-cverify | perl -0777 -pe 's/(.{4}).{8}(.*).$/$1$2/s';\\n    cat record-sfin    | perl -0777 -pe 's/(.{4}).{8}(.*).$/$1$2/s';\\n  ) | openssl sha256)\\n$ cht_secret=33e472fb8d821b0193314626bebee307ccbd1aeb3d3a17ba468888ffc5246da1\\n$ fin_key=$(./hkdf-dtls expandlabel $cht_secret \\\"finished\\\" \\\"\\\" 32)\\n$ echo $fin_hash | xxd -r -p     | openssl dgst -sha256 -mac HMAC -macopt hexkey:$fin_key\\n\\n6f2801396b0e90ebb4a3ba384abcfc6b24201abd81b316b2391da378377facf5\"\n              }\n            }\n          ]\n        ],\n        [\n          \"记录类型\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"16\"\n          },\n          [\n            \"每一个加密的 DTLS 1.3 记录的最后一个字节都需要表明其真正的记录类型\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"16 - 类型为 0x16(22), 握手记录\"\n                }\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/DTLS/clientHandshakeKeysCalc.json",
    "content": "[\n  \"客户端现在也拥有了用于计算握手时的加密密钥的所有信息。在这个计算中，客户端使用了以下信息：\",\n  {\n    \"Tag\": \"ul\",\n    \"children\": [\n      {\n        \"Tag\": \"li\",\n        \"content\": \"服务器端公钥(提取自 ServerHello 数据报)\"\n      },\n      {\n        \"Tag\": \"li\",\n        \"content\": \"客户端私钥(之前准备密钥交换时生成)\"\n      },\n      {\n        \"Tag\": \"li\",\n        \"content\": \"ClientHello 和 ServerHello 的 SHA256 哈希值\"\n      }\n    ]\n  },\n  \"首先，客户端需要找到共享的密文(shared secret)，即密钥交换步骤的最终值。客户端通过使用 curve25519 算法将服务器端的公钥乘以客户端的私钥(椭圆曲线乘法的特性将使得计算结果与服务器在其计算中得到的结果一致)，即可发现 32 字节的最终值是：\",\n  {\n    \"Tag\": \"pre\",\n    \"props\": {\n      \"className\": \"ind2\"\n    },\n    \"children\": [\n      {\n        \"Tag\": \"code\",\n        \"props\": { \"className\": \"longboi\" },\n        \"content\": \"df4a291baa1eb7cfa6934b29b474baad2697e29f1f920dcc77c8a0a088447624\"\n      }\n    ]\n  },\n  {\n    \"children\": [\n      \"你可以使用\",\n      {\n        \"Tag\": \"a\",\n        \"props\": { \"href\": \"https://quic.xargs.org/files/curve25519-mult.c\" },\n        \"content\": \"原作者的脚本\"\n      },\n      \"快速验证结果：\"\n    ]\n  },\n  {\n    \"Tag\": \"CodeSample\",\n    \"props\": {\n      \"code\": \"$ cc -o curve25519-mult curve25519-mult.c\\n$ ./curve25519-mult client-ephemeral-private.key                     server-ephemeral-public.key | hexdump\\n\\n0000000 df 4a 29 1b aa 1e b7 cf a6 93 4b 29 b4 74 ba ad\\n0000010 26 97 e2 9f 1f 92 0d cc 77 c8 a0 a0 88 44 76 24\"\n    }\n  },\n  \"由于上面的计算结果与服务器端计算结果相同，且剩下的计算过程也相同，因此算出来的数值也应该是相同的：\",\n  {\n    \"Tag\": \"ul\",\n    \"children\": [\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"客户端握手时密钥: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"6caa2633d5e48f10051e69dc45549c97\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"客户端握手时向量: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"106dc6e393b7a9ea8ef29dd7\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"客户端序号保护密钥(record number key): \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"beed6218676635c2cb46a45694144fec\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"服务器端握手时密钥: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"004e03e64ab6cba6b542775ec230e20a\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"服务器端握手时向量: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"6d9924be044ee97c624913f2\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"服务器端序号保护密钥: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"7173fac51194e775001d625ef69d7c9f\"\n          }\n        ]\n      }\n    ]\n  }\n]\n"
  },
  {
    "path": "src/DTLS/clientHelloDatagram.json",
    "content": "[\n  \"DTLS 加密会话以 \\\"ClientHello\\\" 开始。客户端提供的信息包括以下内容：\",\n  {\n    \"Tag\": \"ul\",\n    \"children\": [\n      {\n        \"Tag\": \"li\",\n        \"content\": \"客户端随机数(用于之后的握手过程)\"\n      },\n      {\n        \"Tag\": \"li\",\n        \"content\": \"一个客户端支持的有序的加解密算法数组\"\n      },\n      {\n        \"Tag\": \"li\",\n        \"content\": \"用于密钥交换的公钥\"\n      },\n      {\n        \"Tag\": \"li\",\n        \"content\": \"客户端支持的协议版本列表\"\n      }\n    ]\n  },\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"DTLS 数据头\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"16 fe fd 00 00 00 00 00 00 00 00 00 9d\"\n          },\n          [\n            \"每个 DTLS 记录都以一个 type、一些序列信息(seq info)和一个 len 开始。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"16 - 表示 DTLS 记录类型 0x16(22, Handshake)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"fe fd - 协议版本 (DTLS 1.2, 细节见下文)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 - 密钥序列指示(key epoch，每次密钥更新时都会递增)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 00 00 00 00 - DTLS 序列号 0x0(0)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 9d - 紧接着的数组载荷长度 0x9D(157) 字节\"\n                }\n              ]\n            },\n            \"DTLS 版本的编码方式是将协议版本分成几个部分，然后取每个部分的补码。(因此 \\\"1.3\\\" 变成 {1, 3}，变成字节 0xFE 0xFC)。这种补码技术使 DTLS 版本与 TLS 版本有所差别。\",\n            \"由于已经创建和部署的网络中间件(middleboxes)不允许它们所不承认的协议版本通过，因此所有 DTLS 1.3 会话在未加密的记录中都会显示为 DTLS 1.2(0xFE 0xFD)。\"\n          ]\n        ],\n        [\n          \"TLS 握手记录头\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"01 00 00 91\"\n          },\n          [\n            \"每个 TLS 握手消息都以一个 type 和一个 len 开始。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"01 - 握手消息类型 0x01 (ClientHello)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 91 - 紧接着的握手消息数据的长度 0x91 (145) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"用于重建握手顺序的信息(Handshake Reconstruction Data)\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 00 00 00 00 00 00 91\"\n          },\n          [\n            \"因为 UDP (或其他数据报协议)不保证交付或排序，而且数据报的长度可能比需要发送的握手记录长度要小。因此 DTLS 必须提供一定的信息，以支持在数据丢失、包重排序或有记录碎片的情况下，使得对等端(peer)能够重新构建一条正确的 DTLS 记录。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 - DTLS 序列号 0x0(0)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 00 - 表明记录碎片的偏移量为 0x00(0) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 91 - 表明之后的记录碎片的长度为 0x91(145) 字节\"\n                }\n              ]\n            },\n            \"在本文例子中，整个握手记录的长度要短于一个 UDP 数据报的可承载长度，因此偏移量为零，且长度为整个握手记录长。\"\n          ]\n        ],\n        [\n          \"客户端版本(废弃)\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"fe fd\"\n          },\n          [\n            \"DTLS 版本的编码方式是将协议版本分成几个部分，然后取每个部分的补码。(因此 \\\"1.3\\\" 变成 {1, 3}，变成字节 0xFE 0xFC)。这种补码技术使 DTLS 版本与 TLS 版本有所差别。\",\n            \"由于已经创建和部署的网络中间件(middleboxes)不允许它们所不承认的协议版本通过，因此所有 DTLS 1.3 会话在未加密的记录中都会显示为 DTLS 1.2(0xFE 0xFD)。所有的 DTLS 1.3 及以上版本的会话需要通过后面提到的\\\"支持的版本\\\"拓展协商真实版本号。\"\n          ]\n        ],\n        [\n          \"客户端随机数\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff\"\n          },\n          [\n            \"客户端提供的 32 字节的随机数。这个数将在之后的会话中使用。在本文的例子中，我们暂时将一个方便记忆的字符串当作随机数。\"\n          ]\n        ],\n        [\n          \"会话 ID (废弃)\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00\"\n          },\n          [\n            \"这是一个废弃(legacy)字段，不在 DTLS 1.3 中使用。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 - 0 字节的会话 ID\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"Cookie (废弃)\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00\"\n          },\n          [\n            \"这是一个废弃(legacy)字段，不在 DTLS 1.3 中使用。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 - 0 字节的 Cookie\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"加解密算法列表\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 06 13 01 13 02 13 03\"\n          },\n          [\n            \"客户端提供一个有序的列表，以说明它支持哪些加解密算法进行加密。该列表是按照客户端的偏好顺序排列的，以最高偏好为先。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 06 - 6 字节的加解密算法列表数据\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"13 01 - 代表 TLS_AES_128_GCM_SHA256\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"13 02 - 代表 TLS_AES_256_GCM_SHA384\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"13 03 - 代表 TLS_CHACHA20_POLY1305_SHA256\"\n                }\n              ]\n            },\n            {\n              \"children\": [\n                \"完整加解密算法列表请查看 \",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml\"\n                  },\n                  \"content\": \"IANA tls-parameters\"\n                },\n                \" 的 TLS Cipher Suites 部分。\"\n              ]\n            }\n          ]\n        ],\n        [\n          \"压缩算法\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"01 00\"\n          },\n          [\n            {\n              \"children\": [\n                \"以前的 TLS 版本支持压缩(以及对应延伸出的 DTLS 版本)，这被发现会泄露加密数据的信息(见 \",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://zhuanlan.zhihu.com/p/333314023\"\n                  },\n                  \"content\": \"CRIME/BREACH 攻击\"\n                },\n                \")。\"\n              ]\n            },\n            \"TLS 1.3 (DTLS 1.3)不再允许压缩。所以这个字段不会变化：采用 \\\"null\\\" 压缩算法，对数据不做任何改变。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"01 - 1 字节的压缩算法长度\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 - 代表 \\\"没有\\\"(null) 压缩算法\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"扩展的长度\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 61\"\n          },\n          [\n            \"客户端提供了一个可选的扩展列表，服务器可以根据它来采取某些行动或启用某些特性。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 61 - 扩展列表的长度为 0x61(97) 字节\"\n                }\n              ]\n            },\n            \"每个扩展将以两个字节开始，表明它是哪个扩展。然后是两个字节的内容长度字段，最后是扩展的具体内容。\"\n          ]\n        ],\n        [\n          \"扩展 - 算法公钥列表(Key Share)\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 33 00 26 00 24 00 1d 00 20 35 80 72 d6 36 58 80 d1 ae ea 32 9a df 91 21 38 38 51 ed 21 a2 8e 3b 75 e9 65 d0 d2 cd 16 62 54\"\n          },\n          [\n            \"客户端会给某些它认为服务器也会支持的算法发送对应的短暂的公钥。这允许在 ClientHello 和 ServerHello 消息之后的其余握手记录被加密。而不用像以前的协议版本，以透明的方式发送握手记录。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 33 - 表示这是 \\\"算法公钥列表\\\" 扩展\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 26 - 扩展信息长度为 0x26(38) 字节 \"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 24 - 算法公钥列表长度为 0x24(36) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 1d - 代表 x25519 算法(例子中为通过 curve25519 算法进行密钥交换)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 20 - 公钥长度为 0x20(32) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"35 80 ... 62 54 - \\\"客户端准备密钥交换\\\" 步骤中生成的公钥\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"扩展 - 支持的版本\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 2b 00 03 02 fe fc\"\n          },\n          [\n            \"客户端表明其支持 DTLS 1.3。由于兼容性的原因，这被放在一个扩展中，而不是上面的客户端版本字段。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 2b - 表示这是 \\\"支持的版本\\\" 扩展\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 03 - 扩展信息长度为 0x03(3) 字节 \"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"02 - DTLS 版本长度为 0x02(2) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"fe fc - 代表 DTLS 1.3\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"扩展 - 签名算法列表\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 0d 00 20 00 1e 06 03 05 03 04 03 02 03 08 06 08 0b 08 05 08 0a 08 04 08 09 06 01 05 01 04 01 03 01 02 01\"\n          },\n          [\n            \"这个扩展表示客户端支持哪些签名算法。这可能会影响到服务器提交给客户端的证书，以及服务器在 CertificateVerify 记录中发送的签名。\",\n            \"这个列表同样是按照客户端的偏好降序排列的。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 0d - 表示这是 \\\"签名算法列表\\\" 扩展\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 20 - 扩展信息长度为 0x20(32) 字节 \"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 1e - 算法列表长度为 0x1e(30) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"06 03 - 代表 ECDSA-SECP512r1-SHA512 算法\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"05 03 - 代表 ECDSA-SECP384r1-SHA384 算法\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"04 03 - 代表 ECDSA-SECP256r1-SHA256 算法\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"02 03 - 代表 ECDSA-SHA1 算法\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"08 06 - 代表 RSA-PSS-RSAE-SHA512 算法\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"08 0b - 代表 RSA-PSS-PSS-SHA512 算法\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"08 05 - 代表 RSA-PSS-RSAE-SHA384 算法\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"08 0a - 代表 RSA-PSS-PSS-SHA384 算法\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"08 04 - 代表 RSA-PSS-RSAE-SHA256 算法\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"08 09 - 代表 RSA-PSS-PSS-SHA256 算法\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"06 01 - 代表 RSA-PKCS1-SHA512 算法\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"05 01 - 代表 RSA-PKCS1-SHA384 算法\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"04 01 - 代表 RSA-PKCS1-SHA256 算法\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"03 01 - 代表 SHA224-RSA 算法\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"02 01 - 代表 RSA-PKCS1-SHA1 算法\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"扩展 - Encrypt-then-MAC\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 16 00 00\"\n          },\n          [\n            {\n              \"children\": [\n                \"客户端表示其支持 EtM。EtM 可以防止早期版本的 TLS 和 DTLS 的\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://iacr.org/archive/crypto2001/21390309.pdf\"\n                  },\n                  \"content\": \"某些漏洞\"\n                },\n                \"。在 DTLS 1.3 中，这个机制始终被开启，因此这个扩展在会话中没有影响。\"\n              ]\n            },\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 16 - 表示这是 \\\"EtM\\\" 扩展\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 - 扩展信息长度为 0x00(0) 字节 \"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"扩展 - 支持的组\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 0a 00 04 00 02 00 1d\"\n          },\n          [\n            \"(例子中的)客户端表示它支持一种类型的椭圆曲线算法(ECC)。为了使这个扩展更加通用，未来可以支持其他的密码学类型，因此称这些为 \\\"支持的组\\\" 而不是 \\\"支持的曲线\\\"。\",\n            \"这个列表同样是按照客户端的偏好降序排列的。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 0a - 表示这是 \\\"支持的组\\\" 扩展\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 04 - 扩展信息长度为 0x04(4) 字节 \"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 02 - 曲线列表条目长度为 0x02(2) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 1d - 代表 x25519 曲线\"\n                }\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/DTLS/clientKeyExchangeGeneration.json",
    "content": "[\n  \"连接开始时，客户端生成一个用于密钥交换的“私钥/公钥”对。密钥交换(Key exchange)是一种技术，双方可以在同一数字上达成一致，而窃听者却无法知道这个数字是什么。\",\n  {\n    \"Tag\": \"p\",\n    \"children\": [\n      \"学习 DTLS 并不需要深入了解，但你可以从\",\n      {\n        \"Tag\": \"a\",\n        \"props\": { \"href\": \"https://cangsdarm.github.io/illustrate/x25519\" },\n        \"content\": \"X25519 密钥交换算法\"\n      },\n      \"获取涉及到的密钥交换算法的具体解释。\"\n    ]\n  },\n  [\n    \"**私钥**是 0 到 \",\n    {\n      \"Tag\": \"Math\",\n      \"content\": \"2^256-1\"\n    },\n    \" 之间的一个随机整数(32bytes, 256bits)\",\n    \"。为方便后续解释，假设我们生成的私钥是：\"\n  ],\n  {\n    \"Tag\": \"pre\",\n    \"props\": {\n      \"className\": \"ind2\"\n    },\n    \"children\": [\n      {\n        \"Tag\": \"code\",\n        \"props\": { \"className\": \"longboi\" },\n        \"content\": \"202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f\"\n      }\n    ]\n  },\n  \"**公钥**由上面提到的 X25519 密钥交换算法生成。例子中计算出的公钥应如下所示：\",\n  {\n    \"Tag\": \"pre\",\n    \"props\": {\n      \"className\": \"ind2\"\n    },\n    \"children\": [\n      {\n        \"Tag\": \"code\",\n        \"props\": { \"className\": \"longboi\" },\n        \"content\": \"358072d6365880d1aeea329adf9121383851ed21a28e3b75e965d0d2cd166254\"\n      }\n    ]\n  },\n  \"公钥的计算也可以在命令行中运行以下命令得到：\",\n  {\n    \"Tag\": \"CodeSample\",\n    \"props\": {\n      \"code\": \"The public key calculation can be confirmed at the command line:\\n### requires openssl 1.1.0 or higher\\n$ openssl pkey -noout -text < client-ephemeral-private.key\\n\\nX25519 Private-Key:\\npriv:\\n    20:21:22:23:24:25:26:27:28:29:2a:2b:2c:2d:2e:\\n    2f:30:31:32:33:34:35:36:37:38:39:3a:3b:3c:3d:\\n    3e:3f\\npub:\\n    35:80:72:d6:36:58:80:d1:ae:ea:32:9a:df:91:21:\\n    38:38:51:ed:21:a2:8e:3b:75:e9:65:d0:d2:cd:16:\\n    62:54\"\n    }\n  },\n  \"此时，网络连接仍在准备中，没有任何数据被传输。\"\n]\n"
  },
  {
    "path": "src/DTLS/index.js",
    "content": "const data = {\n  intro: {\n    title: \"图解 DTLS 连接\",\n    subtitle: \"对每一个字节的解释和再现\",\n    desc: 'DTLS 应被称为 \"通过数据报传输的TLS\"；到目前为止，有五个 DTLS-over-XYZ 的 RFC，涵盖了 UDP、DCCP、CAPWAP、SCTP 和 SRTP',\n    intro:\n      '在这个演示中，客户端通过 DTLS 1.3 加密协商连接服务器。客户端发送\"ping\"、接收\"pong\"后终止连接。点击下面开始探索。',\n  },\n  sections: [\n    {\n      type: \"RecOuter\",\n      tags: [\"calculation\", \"client\"],\n      label: \"客户端准备密钥交换\",\n      illustration: {\n        src: \"https://quic.xargs.org/images/key1.png\",\n        width: \"135\",\n        height: \"250\",\n      },\n      json: () => import(\"./clientKeyExchangeGeneration.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"client\"],\n      label: \"ClientHello 数据报\",\n      json: () => import(\"./clientHelloDatagram.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"calculation\", \"server\"],\n      label: \"服务器端准备密钥交换\",\n      illustration: {\n        src: \"https://quic.xargs.org/images/key3.png\",\n        width: \"130\",\n        height: \"250\",\n      },\n      json: () => import(\"./serverKeyExchangeGeneration.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"server\"],\n      label: \"ServerHello 数据报\",\n      json: () => import(\"./serverHelloDatagram.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"calculation\", \"server\"],\n      label: \"服务器端生成握手密钥\",\n      illustration: {\n        src: \"https://quic.xargs.org/images/key5.png\",\n        width: \"124\",\n        height: \"250\",\n      },\n      json: () => import(\"./serverHandshakeKeysCalc.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"calculation\", \"client\"],\n      label: \"客户端生成握手密钥\",\n      illustration: {\n        src: \"https://quic.xargs.org/images/key6.png\",\n        width: \"105\",\n        height: \"250\",\n      },\n      json: () => import(\"./clientHandshakeKeysCalc.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"server\"],\n      label: \"服务器端加密后的额外扩展数据报\",\n      illustration: {\n        src: \"https://quic.xargs.org/images/key5.png\",\n        width: \"124\",\n        height: \"250\",\n      },\n      json: () => import(\"./serverEncryptedExtensionsDatagram.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"server\"],\n      label: \"服务器端证书数据报\",\n      illustration: {\n        src: \"https://quic.xargs.org/images/key3.png\",\n        width: \"130\",\n        height: \"250\",\n      },\n      json: () => import(\"./serverCertificateDatagram.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"server\"],\n      label: \"服务器端证书验证数据报\",\n      illustration: {\n        src: \"https://quic.xargs.org/images/key5.png\",\n        width: \"124\",\n        height: \"250\",\n      },\n      json: () => import(\"./serverCertVerifyDatagram.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"server\"],\n      label: \"服务器端握手完成数据报\",\n      json: () => import(\"./serverHandshakeFinishedDatagram.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"client\"],\n      label: \"客户端握手完成数据报\",\n      json: () => import(\"./clientHandshakeFinishedDatagram.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"calculation\", \"server\"],\n      label: \"服务器端生成会话密钥\",\n      illustration: {\n        src: \"https://quic.xargs.org/images/key9.png\",\n        width: \"97\",\n        height: \"250\",\n      },\n      json: () => import(\"./serverApplicationKeysCalc.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"calculation\", \"client\"],\n      label: \"客户端生成会话密钥\",\n      illustration: {\n        src: \"https://quic.xargs.org/images/key8.png\",\n        width: \"97\",\n        height: \"250\",\n      },\n      json: () => import(\"./clientApplicationKeysCalc.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"server\"],\n      label: \"服务器端握手 ACK 数据报\",\n      id: \"serverHandshake3\",\n      json: () => import(\"./serverACKDatagram.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"client\"],\n      label: \"客户端会话数据报\",\n      json: () => import(\"./clientApplicationDataDatagram.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"server\"],\n      label: \"服务器端会话数据报\",\n      json: () => import(\"./serverApplicationDataDatagram.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"server\"],\n      label: \"服务器端警告数据报(alert datagram)\",\n      json: () => import(\"./serverAlertDatagram.json\"),\n    },\n  ],\n  ending: {\n    mother: \"https://dtls.xargs.org/\",\n    desc: \"你可能也对 <a href='/illustrate/tls13' target='_blank'>TLS 1.3</a> 的内容感兴趣。\",\n  },\n};\n\nexport default data;\n"
  },
  {
    "path": "src/DTLS/serverACKDatagram.json",
    "content": "[\n  \"每个对等端(peer)必须响应或确认从其他对等端收到的数据，否则对应对等端将假定数据已丢失并会再次发送。\",\n  \"因此，在这个记录中，服务器端需要确认收到了客户端握手完成的记录。\",\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"头部信息字节\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"2f\"\n          },\n          [\n            \"加密的 DTLS 数据包都以一个 \\\"统一的头部(unified header)\\\"开始。头部的第一个字节给出了头部和数据包的结构信息，以及解密时需要的信息。\",\n            \"值 0x2f 具有以下含义：\",\n            {\n              \"Tag\": \"Table\",\n              \"props\": {\n                \"headers\": [\"\", \"值\", \"含义\"],\n                \"data\": [\n                  [\"高位\", \"001\", \"固定位\"],\n                  [\"\", \"0\", \"头部中不存在连接 ID 字段(1则存在)\"],\n                  [\"\", \"1\", \"序列号在头部中占 2 字节长\"],\n                  [\"\", \"1\", \"头部中存在\\\"记录长度\\\"字段(0则不存在)\"],\n                  [\n                    \"低位\",\n                    \"11\",\n                    \"加密序列指示(Encryption epoch 3)，现在密钥是会话时密钥\"\n                  ]\n                ]\n              }\n            }\n          ]\n        ],\n        [\n          \"记录序号\",\n          {\n            \"props\": {\n              \"className\": \"bytes protected\",\n              \"title\": \"被加密\"\n            },\n            \"content\": \"31 50\"\n          },\n          {\n            \"props\": {\n              \"className\": \"bytes unprotected\"\n            },\n            \"content\": \"00 00\"\n          },\n          [\n            \"记录序号是被加密了的，用以防止中间件误解(interpreting)或干扰数据包的排序。\",\n            \"加密是通过用 \\\"服务器端序号保护密钥\\\" 对每个数据包的有效载荷样本进行加密，然后将每个数据包中的某些比特和字节与所得数据进行 XOR 得到。\",\n            \"如果说的不够详细，这里有一个如何加密的例子：\",\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### \\\"server record number key\\\" from application keys calc step above\\n$ key=57ba02596c6a1352d7fe8416c7e17d5a\\n### sample is taken from 16 bytes of payload starting 5 bytes into the record\\n$ sample=ea80ab8e08c93895418d243571ea6de7\\n$ echo $sample | xxd -r -p | openssl aes-128-ecb -K $key | head -c 2 | xxd -p\\n\\n3150\\n\\n### the above bytes are xor'd one-for-one into the bytes of the record number\"\n              }\n            }\n          ]\n        ],\n        [\n          \"记录长度\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 23\"\n          },\n          [\n            \"每个记录除非给出这个长度字段，否则对等端将认为数据报剩余的所有字节都是同一个记录的真实载荷。有了这一字段，则在一个数据报中可以发送好几个 DTLS 记录(尽管例子中的连接没有利用这个优势)。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 23 - 表示 DTLS 记录长度为 0x23(35) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"加密的数据载荷\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\",\n              \"title\": \"被\\\"会话密钥\\\"加密\"\n            },\n            \"content\": \"ea 80 ab 8e 08 c9 38 95 41 8d 24 35 71 ea 6d e7 d8 63 ee\"\n          },\n          [\"这些数据使用服务器端的\\\"会话密钥\\\"进行加密。\"]\n        ],\n        [\n          \"AEAD 鉴别标签\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"84 23 0b b6 04 3c b3 84 df 94 b6 da 28 5a 3b c4\"\n          },\n          [\n            {\n              \"children\": [\n                \"这是 \",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://zhuanlan.zhihu.com/p/28566058\"\n                  },\n                  \"content\": \"AEAD 算法\"\n                },\n                \"的鉴别标签，确认加密数据和记录头的完整性。它由加密算法产生，并由解密算法消耗。\"\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"data\": [\n        [\n          \"\",\n          {\n            \"props\": {\n              \"className\": \"decryption-header\"\n            },\n            \"content\": \"解密后的数据载荷\"\n          },\n          [\n            { \"Tag\": \"h4\", \"content\": \"解密\" },\n            \"数据被 \\\"服务器端会话密钥计算\\\" 步骤中产生的初始密钥和初始向量(IVs)加密。IVs 通过密钥和已经用密钥加密的记录长度进行异或操作生成。在例子中 IV 为 0。\",\n            \"数据包开头的 5 字节(记录头)还会作为解密过程解密成功时必须满足的认证条件。\",\n            {\n              \"children\": [\n                \"openssl 命令行工具还不支持 AEAD 算法加解密(AEAD ciphers)，你可以使用作者的命令行工具来\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://dtls.xargs.org/files/aes_128_gcm_decrypt.c\"\n                  },\n                  \"content\": \"解密\"\n                },\n                \"和\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://dtls.xargs.org/files/aes_128_gcm_encrypt.c\"\n                  },\n                  \"content\": \"加密\"\n                },\n                \"这些数据。\"\n              ]\n            },\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### from the \\\"Server Application Keys Calc\\\" step\\n$ key=2b65fffbbc8189474aa2003c43c32d4d\\n$ iv=582f5a11bdaf973fe3ffeb4e\\n### from this record\\n$ recdata=2f00000023\\n$ authtag=84230bb6043cb384df94b6da285a3bc4\\n$ recordnum=0\\n### may need to add -I and -L flags for include and lib dirs\\n$ cc -o aes_128_gcm_decrypt aes_128_gcm_decrypt.c -lssl -lcrypto\\n$ cat /tmp/msg1   | ./aes_128_gcm_decrypt $iv $recordnum $key $recdata $authtag   | hexdump -C\\n\\n00000000  00 10 00 00 00 00 00 00  00 02 00 00 00 00 00 00  |................|\\n00000010  00 00 1a                                          |...|\"\n              }\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"ACK 长度\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"00 10\"\n          },\n          [\n            \"每个 ACK 消息都以一个 len 开始。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 10 - 紧接着的 ACK 消息数据的长度 0x10 (16) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"已收到记录信息(Record Acknowledgement)\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"00 00 00 00 00 00 00 02 00 00 00 00 00 00 00 00\"\n          },\n          [\n            \"服务器确认其收到的记录。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 00 00 00 00 02 - 记录的序列指示号(record epoch)，握手密钥\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 00 00 00 00 00 00 - 对应记录序号\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"记录类型\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"1a\"\n          },\n          [\n            \"每一个加密的 DTLS 1.3 记录的最后一个字节都需要表明其真正的记录类型\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"1a - 类型为 0x1a(26), ACK 记录\"\n                }\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/DTLS/serverAlertDatagram.json",
    "content": "[\n  \"服务器端发送警告信号，表明连接即将终止(此时是有序终止即正常终止连接)\",\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"头部信息字节\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"2f\"\n          },\n          [\n            \"加密的 DTLS 数据包都以一个 \\\"统一的头部(unified header)\\\"开始。头部的第一个字节给出了头部和数据包的结构信息，以及解密时需要的信息。\",\n            \"值 0x2f 具有以下含义：\",\n            {\n              \"Tag\": \"Table\",\n              \"props\": {\n                \"headers\": [\"\", \"值\", \"含义\"],\n                \"data\": [\n                  [\"高位\", \"001\", \"固定位\"],\n                  [\"\", \"0\", \"头部中不存在连接 ID 字段(1则存在)\"],\n                  [\"\", \"1\", \"序列号在头部中占 2 字节长\"],\n                  [\"\", \"1\", \"头部中存在\\\"记录长度\\\"字段(0则不存在)\"],\n                  [\n                    \"低位\",\n                    \"11\",\n                    \"加密序列指示(Encryption epoch 3)，现在密钥是会话时密钥\"\n                  ]\n                ]\n              }\n            }\n          ]\n        ],\n        [\n          \"记录序号\",\n          {\n            \"props\": {\n              \"className\": \"bytes protected\",\n              \"title\": \"被加密\"\n            },\n            \"content\": \"69 0c\"\n          },\n          {\n            \"props\": {\n              \"className\": \"bytes unprotected\"\n            },\n            \"content\": \"00 02\"\n          },\n          [\n            \"记录序号是被加密了的，用以防止中间件误解(interpreting)或干扰数据包的排序。\",\n            \"加密是通过用 \\\"服务器端序号保护密钥\\\" 对每个数据包的有效载荷样本进行加密，然后将每个数据包中的某些比特和字节与所得数据进行 XOR 得到。\",\n            \"如果说的不够详细，这里有一个如何加密的例子：\",\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### \\\"server record number key\\\" from application keys calc step above\\n$ key=57ba02596c6a1352d7fe8416c7e17d5a\\n### sample is taken from 16 bytes of payload starting 5 bytes into the record\\n$ sample=dd8cd07daa964fd1ab508825378fc96f\\n$ echo $sample | xxd -r -p | openssl aes-128-ecb -K $key | head -c 2 | xxd -p\\n\\n690e\\n\\n### the above bytes are xor'd one-for-one into the bytes of the record number\"\n              }\n            }\n          ]\n        ],\n        [\n          \"记录长度\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 13\"\n          },\n          [\n            \"每个记录除非给出这个长度字段，否则对等端将认为数据报剩余的所有字节都是同一个记录的真实载荷。有了这一字段，则在一个数据报中可以发送好几个 DTLS 记录(尽管例子中的连接没有利用这个优势)。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 13 - 表示 DTLS 记录长度为 0x13(19) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"加密的数据载荷\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\",\n              \"title\": \"被\\\"会话密钥\\\"加密\"\n            },\n            \"content\": \"dd 8c d0\"\n          },\n          [\"这些数据使用服务器端的\\\"会话密钥\\\"进行加密。\"]\n        ],\n        [\n          \"AEAD 鉴别标签\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"7d aa 96 4f d1 ab 50 88 25 37 8f c9 6f a8 b1 e8\"\n          },\n          [\n            {\n              \"children\": [\n                \"这是 \",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://zhuanlan.zhihu.com/p/28566058\"\n                  },\n                  \"content\": \"AEAD 算法\"\n                },\n                \"的鉴别标签，确认加密数据和记录头的完整性。它由加密算法产生，并由解密算法消耗。\"\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"data\": [\n        [\n          \"\",\n          {\n            \"props\": {\n              \"className\": \"decryption-header\"\n            },\n            \"content\": \"解密后的数据载荷\"\n          },\n          [\n            { \"Tag\": \"h4\", \"content\": \"解密\" },\n            \"数据被 \\\"服务器端会话密钥计算\\\" 步骤中产生的初始密钥和初始向量(IVs)加密。IVs 通过密钥和已经用密钥加密的记录长度进行异或操作生成。在例子中 IV 为 1。\",\n            \"数据包开头的 5 字节(记录头)还会作为解密过程解密成功时必须满足的认证条件。\",\n            {\n              \"children\": [\n                \"openssl 命令行工具还不支持 AEAD 算法加解密(AEAD ciphers)，你可以使用作者的命令行工具来\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://dtls.xargs.org/files/aes_128_gcm_decrypt.c\"\n                  },\n                  \"content\": \"解密\"\n                },\n                \"和\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://dtls.xargs.org/files/aes_128_gcm_encrypt.c\"\n                  },\n                  \"content\": \"加密\"\n                },\n                \"这些数据。\"\n              ]\n            },\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### from the \\\"Server Application Keys Calc\\\" step\\n$ key=2b65fffbbc8189474aa2003c43c32d4d\\n$ iv=582f5a11bdaf973fe3ffeb4e\\n### from this record\\n$ recdata=2f00020013\\n$ authtag=7daa964fd1ab508825378fc96fa8b1e8\\n$ recordnum=2\\n### may need to add -I and -L flags for include and lib dirs\\n$ cc -o aes_128_gcm_decrypt aes_128_gcm_decrypt.c -lssl -lcrypto\\n$ cat /tmp/msg1   | ./aes_128_gcm_decrypt $iv $recordnum $key $recdata $authtag   | hexdump -C\\n\\n00000000  01 00 15\"\n              }\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"警告信息\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"01 00\"\n          },\n          [\n            \"服务器发送一个 \\\"连接关闭通知\\\"(close notify) 的警告，表示连接正有序地终止(正常终止)。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"01 - 警告级别为 0x01(warning), 未使用\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 - 表明这是 close notify 警告\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"记录类型\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"15\"\n          },\n          [\n            \"每一个加密的 DTLS 1.3 记录的最后一个字节都需要表明其真正的记录类型\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"15 - 类型为 0x15(21), 警告\"\n                }\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/DTLS/serverApplicationDataDatagram.json",
    "content": "[\n  \"服务器端响应数据：字符串\\\"pong\\\"\",\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"头部信息字节\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"2f\"\n          },\n          [\n            \"加密的 DTLS 数据包都以一个 \\\"统一的头部(unified header)\\\"开始。头部的第一个字节给出了头部和数据包的结构信息，以及解密时需要的信息。\",\n            \"值 0x2f 具有以下含义：\",\n            {\n              \"Tag\": \"Table\",\n              \"props\": {\n                \"headers\": [\"\", \"值\", \"含义\"],\n                \"data\": [\n                  [\"高位\", \"001\", \"固定位\"],\n                  [\"\", \"0\", \"头部中不存在连接 ID 字段(1则存在)\"],\n                  [\"\", \"1\", \"序列号在头部中占 2 字节长\"],\n                  [\"\", \"1\", \"头部中存在\\\"记录长度\\\"字段(0则不存在)\"],\n                  [\n                    \"低位\",\n                    \"11\",\n                    \"加密序列指示(Encryption epoch 3)，现在密钥是会话时密钥\"\n                  ]\n                ]\n              }\n            }\n          ]\n        ],\n        [\n          \"记录序号\",\n          {\n            \"props\": {\n              \"className\": \"bytes protected\",\n              \"title\": \"被加密\"\n            },\n            \"content\": \"a2 58\"\n          },\n          {\n            \"props\": {\n              \"className\": \"bytes unprotected\"\n            },\n            \"content\": \"00 01\"\n          },\n          [\n            \"记录序号是被加密了的，用以防止中间件误解(interpreting)或干扰数据包的排序。\",\n            \"加密是通过用 \\\"服务器端序号保护密钥\\\" 对每个数据包的有效载荷样本进行加密，然后将每个数据包中的某些比特和字节与所得数据进行 XOR 得到。\",\n            \"如果说的不够详细，这里有一个如何加密的例子：\",\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### \\\"server record number key\\\" from application keys calc step above\\n$ key=57ba02596c6a1352d7fe8416c7e17d5a\\n### sample is taken from 16 bytes of payload starting 5 bytes into the record\\n$ sample=f5bd33f27b72780e351fa00703fb9f65\\n$ echo $sample | xxd -r -p | openssl aes-128-ecb -K $key | head -c 2 | xxd -p\\n\\na259\\n\\n### the above bytes are xor'd one-for-one into the bytes of the record number\"\n              }\n            }\n          ]\n        ],\n        [\n          \"记录长度\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 15\"\n          },\n          [\n            \"每个记录除非给出这个长度字段，否则对等端将认为数据报剩余的所有字节都是同一个记录的真实载荷。有了这一字段，则在一个数据报中可以发送好几个 DTLS 记录(尽管例子中的连接没有利用这个优势)。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 15 - 表示 DTLS 记录长度为 0x15(21) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"加密的数据载荷\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\",\n              \"title\": \"被\\\"会话密钥\\\"加密\"\n            },\n            \"content\": \"f5 bd 33 f2 7b\"\n          },\n          [\"这些数据使用服务器端的\\\"会话密钥\\\"进行加密。\"]\n        ],\n        [\n          \"AEAD 鉴别标签\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"72 78 0e 35 1f a0 07 03 fb 9f 65 8c 68 9f 95 ae\"\n          },\n          [\n            {\n              \"children\": [\n                \"这是 \",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://zhuanlan.zhihu.com/p/28566058\"\n                  },\n                  \"content\": \"AEAD 算法\"\n                },\n                \"的鉴别标签，确认加密数据和记录头的完整性。它由加密算法产生，并由解密算法消耗。\"\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"data\": [\n        [\n          \"\",\n          {\n            \"props\": {\n              \"className\": \"decryption-header\"\n            },\n            \"content\": \"解密后的数据载荷\"\n          },\n          [\n            { \"Tag\": \"h4\", \"content\": \"解密\" },\n            \"数据被 \\\"服务器端会话密钥计算\\\" 步骤中产生的初始密钥和初始向量(IVs)加密。IVs 通过密钥和已经用密钥加密的记录长度进行异或操作生成。在例子中 IV 为 1。\",\n            \"数据包开头的 5 字节(记录头)还会作为解密过程解密成功时必须满足的认证条件。\",\n            {\n              \"children\": [\n                \"openssl 命令行工具还不支持 AEAD 算法加解密(AEAD ciphers)，你可以使用作者的命令行工具来\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://dtls.xargs.org/files/aes_128_gcm_decrypt.c\"\n                  },\n                  \"content\": \"解密\"\n                },\n                \"和\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://dtls.xargs.org/files/aes_128_gcm_encrypt.c\"\n                  },\n                  \"content\": \"加密\"\n                },\n                \"这些数据。\"\n              ]\n            },\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### from the \\\"Server Application Keys Calc\\\" step\\n$ key=2b65fffbbc8189474aa2003c43c32d4d\\n$ iv=582f5a11bdaf973fe3ffeb4e\\n### from this record\\n$ recdata=2f00010015\\n$ authtag=72780e351fa00703fb9f658c689f95ae\\n$ recordnum=1\\n### may need to add -I and -L flags for include and lib dirs\\n$ cc -o aes_128_gcm_decrypt aes_128_gcm_decrypt.c -lssl -lcrypto\\n$ cat /tmp/msg1   | ./aes_128_gcm_decrypt $iv $recordnum $key $recdata $authtag   | hexdump -C\\n\\n00000000  70 6f 6e 67 17                                    |pong.|\"\n              }\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"数据\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"70 6f 6e 67\"\n          },\n          [\"字符串\\\"pong\\\"\"]\n        ],\n        [\n          \"记录类型\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"17\"\n          },\n          [\n            \"每一个加密的 DTLS 1.3 记录的最后一个字节都需要表明其真正的记录类型\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"17 - 类型为 0x17(23), 会话数据\"\n                }\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/DTLS/serverApplicationKeysCalc.json",
    "content": "[\n  \"服务器端现在可以计算应用会话时的密钥了。在这个计算中，服务器使用了以下信息：\",\n  {\n    \"Tag\": \"ul\",\n    \"children\": [\n      {\n        \"Tag\": \"li\",\n        \"content\": \"服务器端密文(之前准备握手密钥时生成)\"\n      },\n      {\n        \"Tag\": \"li\",\n        \"content\": \"ClientHello 到 服务器握手结束 所有握手时包的 SHA256 哈希值(哈希值的输入不包括明文的记录头部、DTLS 特有的记录头以及最后 1 字节的记录类型)\"\n      }\n    ]\n  },\n  \"32 字节的 \\\"handshake_hash\\\" 最终值是：\",\n  {\n    \"Tag\": \"pre\",\n    \"props\": {\n      \"className\": \"ind2\"\n    },\n    \"children\": [\n      {\n        \"Tag\": \"code\",\n        \"props\": { \"className\": \"longboi\" },\n        \"content\": \"77ff5eee528abc269960b0ea316eb8578dc8325d86ec1336ffe4b2941e26d82b\"\n      }\n    ]\n  },\n  {\n    \"Tag\": \"CodeSample\",\n    \"props\": {\n      \"code\": \"$ (\\n  cat record-chello  | perl -0777 -pe 's/.{13}(.{4}).{8}/$1/';\\n  cat record-shello  | perl -0777 -pe 's/.{13}(.{4}).{8}/$1/';\\n  cat record-encext  | perl -0777 -pe 's/(.{4}).{8}(.*).$/$1$2/s';\\n  cat record-cert    | perl -0777 -pe 's/(.{4}).{8}(.*).$/$1$2/s';\\n  cat record-cverify | perl -0777 -pe 's/(.{4}).{8}(.*).$/$1$2/s';\\n  cat record-sfin    | perl -0777 -pe 's/(.{4}).{8}(.*).$/$1$2/s';\\n  )| openssl sha256\\n\\n77ff5eee528abc269960b0ea316eb8578dc8325d86ec1336ffe4b2941e26d82b\"\n    }\n  },\n  \"然后，我们将哈希值和共享的签名信息进行一些密钥派生操作(key derivation operations)，以防止可能的攻击：\",\n  {\n    \"Tag\": \"pre\",\n    \"children\": [\n      {\n        \"Tag\": \"code\",\n        \"props\": { \"className\": \"longboi\" },\n        \"content\": \"empty_hash = SHA256(\\\"\\\")\\nderived_secret = HKDF-Expand-Label(key: handshake_secret, label: \\\"derived\\\", ctx: empty_hash, len: 32)\\nmaster_secret = HKDF-Extract(salt: derived_secret, key: 00...)\\nclient_secret = HKDF-Expand-Label(key: master_secret, label: \\\"c ap traffic\\\", ctx: handshake_hash, len: 32)\\nserver_secret = HKDF-Expand-Label(key: master_secret, label: \\\"s ap traffic\\\", ctx: handshake_hash, len: 32)\\nclient_application_key = HKDF-Expand-Label(key: client_secret, label: \\\"key\\\", ctx: \\\"\\\", len: 16)\\nserver_application_key = HKDF-Expand-Label(key: server_secret, label: \\\"key\\\", ctx: \\\"\\\", len: 16)\\nclient_application_iv = HKDF-Expand-Label(key: client_secret, label: \\\"iv\\\", ctx: \\\"\\\", len: 12)\\nserver_application_iv = HKDF-Expand-Label(key: server_secret, label: \\\"iv\\\", ctx: \\\"\\\", len: 12)\"\n      }\n    ]\n  },\n  {\n    \"children\": [\n      \"在命令行中使用\",\n      {\n        \"Tag\": \"a\",\n        \"props\": { \"href\": \"https://dtls.xargs.org/files/hkdf-dtls.sh\" },\n        \"content\": \"原作者制作的 HKDF 命令行脚本\"\n      },\n      \"，你也可以自己试试：\"\n    ]\n  },\n  {\n    \"Tag\": \"CodeSample\",\n    \"props\": {\n      \"code\": \"$ handshake_hash=77ff5eee528abc269960b0ea316eb8578dc8325d86ec1336ffe4b2941e26d82b\\n$ handshake_secret=d0d1397bb3c445d37f26f7ed00c83b73d2f67540de3761465ffe524f8f944e12\\n$ zero_key=0000000000000000000000000000000000000000000000000000000000000000\\n$ empty_hash=$(openssl sha256 < /dev/null | sed -e 's/.* //')\\n$ derived_secret=$(./hkdf-dtls expandlabel $handshake_secret \\\"derived\\\" $empty_hash 32)\\n$ master_secret=$(./hkdf-dtls extract $derived_secret $zero_key)\\n$ csecret=$(./hkdf-dtls expandlabel $master_secret \\\"c ap traffic\\\" $handshake_hash 32)\\n$ ssecret=$(./hkdf-dtls expandlabel $master_secret \\\"s ap traffic\\\" $handshake_hash 32)\\n$ client_application_key=$(./hkdf-dtls expandlabel $csecret \\\"key\\\" \\\"\\\" 16)\\n$ server_application_key=$(./hkdf-dtls expandlabel $ssecret \\\"key\\\" \\\"\\\" 16)\\n$ client_application_iv=$(./hkdf-dtls expandlabel $csecret \\\"iv\\\" \\\"\\\" 12)\\n$ server_application_iv=$(./hkdf-dtls expandlabel $ssecret \\\"iv\\\" \\\"\\\" 12)\\n$ client_sn_key=$(./hkdf-dtls expandlabel $csecret \\\"sn\\\" \\\"\\\" 16)\\n$ server_sn_key=$(./hkdf-dtls expandlabel $ssecret \\\"sn\\\" \\\"\\\" 16)\\n$ echo client_key: $client_application_key\\n$ echo client_iv: $client_application_iv\\n$ echo server_key: $server_application_key\\n$ echo server_iv: $server_application_iv\\n$ echo client_sn_key: $client_sn_key\\n$ echo server_sn_key: $server_sn_key\\n\\nclient_key: 9ba90dbce8857bc1fcb81d41a0465cfe\\nclient_iv: 682219974631fa0656ee4eff\\nserver_key: 2b65fffbbc8189474aa2003c43c32d4d\\nserver_iv: 582f5a11bdaf973fe3ffeb4e\\nclient_sn_key: 5cb5bd8bac29777c650c0dde22d16d47\\nserver_sn_key: 57ba02596c6a1352d7fe8416c7e17d5a\"\n    }\n  },\n  \"由此我们可以得到以下密钥以及向量：\",\n  {\n    \"Tag\": \"ul\",\n    \"children\": [\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"客户端会话密钥: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"9ba90dbce8857bc1fcb81d41a0465cfe\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"客户端会话向量: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"682219974631fa0656ee4eff\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"客户端序号保护密钥: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"5cb5bd8bac29777c650c0dde22d16d47\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"服务器端会话密钥: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"2b65fffbbc8189474aa2003c43c32d4d\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"服务器端会话向量: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"582f5a11bdaf973fe3ffeb4e\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"服务器端序号保护密钥: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"57ba02596c6a1352d7fe8416c7e17d5a\"\n          }\n        ]\n      }\n    ]\n  }\n]\n"
  },
  {
    "path": "src/DTLS/serverCertVerifyDatagram.json",
    "content": "[\n  \"服务器提供证书验证相关的数据，用以验证服务器密钥交换生成过程中产生的短暂公钥与证书私钥的所有权是否一致。\",\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"头部信息字节\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"2e\"\n          },\n          [\n            \"加密的 DTLS 数据包都以一个 \\\"统一的头部(unified header)\\\"开始。头部的第一个字节给出了头部和数据包的结构信息，以及解密时需要的信息。\",\n            \"值 0x2e 具有以下含义：\",\n            {\n              \"Tag\": \"Table\",\n              \"props\": {\n                \"headers\": [\"\", \"值\", \"含义\"],\n                \"data\": [\n                  [\"高位\", \"001\", \"固定位\"],\n                  [\"\", \"0\", \"头部中不存在连接 ID 字段(1则存在)\"],\n                  [\"\", \"1\", \"序列号在头部中占 2 字节长\"],\n                  [\"\", \"1\", \"头部中存在\\\"记录长度\\\"字段(0则不存在)\"],\n                  [\n                    \"低位\",\n                    \"10\",\n                    \"加密序列指示(Encryption epoch 2)，现在密钥是握手时密钥\"\n                  ]\n                ]\n              }\n            }\n          ]\n        ],\n        [\n          \"记录序号\",\n          {\n            \"props\": {\n              \"className\": \"bytes protected\",\n              \"title\": \"被加密\"\n            },\n            \"content\": \"a4 3e\"\n          },\n          {\n            \"props\": {\n              \"className\": \"bytes unprotected\"\n            },\n            \"content\": \"00 02\"\n          },\n          [\n            \"记录序号是被加密了的，用以防止中间件误解(interpreting)或干扰数据包的排序。\",\n            \"加密是通过用 \\\"服务器端序号保护密钥\\\" 对每个数据包的有效载荷样本进行加密，然后将每个数据包中的某些比特和字节与所得数据进行 XOR 得到。\",\n            \"如果说的不够详细，这里有一个如何加密的例子：\",\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### \\\"server record number key\\\" from handshake keys calc step above\\n$ key=7173fac51194e775001d625ef69d7c9f\\n### sample is taken from 16 bytes of payload starting 5 bytes into the record\\n$ sample=83bedfea0f4aa578453af4f4a4be4106\\n$ echo $sample | xxd -r -p | openssl aes-128-ecb -K $key | head -c 2 | xxd -p\\n\\na43c\\n\\n### the above bytes are xor'd one-for-one into the bytes of the record number\"\n              }\n            }\n          ]\n        ],\n        [\n          \"记录长度\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"01 24\"\n          },\n          [\n            \"每个记录除非给出这个长度字段，否则对等端将认为数据报剩余的所有字节都是同一个记录的真实载荷。有了这一字段，则在一个数据报中可以发送好几个 TLS 记录(尽管例子中的连接没有利用这个优势)。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"01 21 - 表示 TLS 记录长度为 0x121(289) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"加密的数据载荷\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\",\n              \"title\": \"被\\\"握手密钥\\\"加密\"\n            },\n            \"content\": \"83 be df ea 0f 4a a5 78 45 3a f4 f4 a4 be 41 06 9b eb e5 9c e4 93 3b f2 f2 ff 35 36 f0 e6 11 45 9f 7a fc 07 14 1e 4a 80 e4 b1 10 f2 c5 48 24 4e 83 42 cd 13 46 26 f0 d6 bc 12 2c 6e e3 cc 81 64 e3 e1 1f b8 bc 7b 58 ff 8d ef af 99 c9 26 81 f7 42 64 cc 29 5d f2 69 b4 63 af e5 78 53 ba 86 04 bd 8e ef 74 91 a0 fc 5a 5d df c2 2b 87 f7 cc 55 94 fd 2b 13 69 68 ab 07 ce 1d 84 33 07 df 9f 41 37 27 11 0f e0 5a c6 df 33 7c 44 4c 9a 2d 8b 28 30 b3 50 48 13 72 dd a1 4b e3 04 63 cb 94 16 f8 15 b7 29 b8 20 be b9 1e df 34 f8 b2 29 fa 71 4d fa 58 68 61 c5 25 15 aa d2 8e 98 52 90 d2 a7 e1 97 df 5a 4f 73 20 4d 95 2c a3 e2 34 af 34 fa e6 5a 3a 34 c1 33 8b 52 dd b7 8e 87 a9 14 95 21 2c 8e da ed 59 6e 0b 4b ad 18 65 66 8d 5a 33 9f d7 61 31 43 bc b8 5d 96 10 41 22 f6 17 e5 39 3b 4c ba 44 d0 86 e5 32 c7 39 e8 15 ea dc 2a 84 07 c4 72\"\n          },\n          [\"这些数据使用服务器端的\\\"握手密钥\\\"进行加密。\"]\n        ],\n        [\n          \"AEAD 鉴别标签\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"bd f0 f6 f0 06 0d b4 71 19 71 38 7c 21 89 39 4f\"\n          },\n          [\n            {\n              \"children\": [\n                \"这是 \",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://zhuanlan.zhihu.com/p/28566058\"\n                  },\n                  \"content\": \"AEAD 算法\"\n                },\n                \"的鉴别标签，确认加密数据和记录头的完整性。它由加密算法产生，并由解密算法消耗。\"\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"data\": [\n        [\n          \"\",\n          {\n            \"props\": {\n              \"className\": \"decryption-header\"\n            },\n            \"content\": \"解密后的数据载荷\"\n          },\n          [\n            { \"Tag\": \"h4\", \"content\": \"解密\" },\n            \"数据被 \\\"服务器端握手密钥计算\\\" 步骤中产生的初始密钥和初始向量(IVs)加密。IVs 通过密钥和已经用密钥加密的记录长度进行异或操作生成。在例子中 IV 为 2。\",\n            \"数据包开头的 5 字节(记录头)还会作为解密过程解密成功时必须满足的认证条件。\",\n            {\n              \"children\": [\n                \"openssl 命令行工具还不支持 AEAD 算法加解密(AEAD ciphers)，你可以使用作者的命令行工具来\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://dtls.xargs.org/files/aes_128_gcm_decrypt.c\"\n                  },\n                  \"content\": \"解密\"\n                },\n                \"和\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://dtls.xargs.org/files/aes_128_gcm_encrypt.c\"\n                  },\n                  \"content\": \"加密\"\n                },\n                \"这些数据。\"\n              ]\n            },\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### from the \\\"Server Handshake Keys Calc\\\" step\\n$ key=004e03e64ab6cba6b542775ec230e20a\\n$ iv=6d9924be044ee97c624913f2\\n### from this record\\n$ recdata=2e00020121\\n$ authtag=bdf0f6f0060db4711971387c2189394f\\n$ recordnum=2\\n### may need to add -I and -L flags for include and lib dirs\\n$ cc -o aes_128_gcm_decrypt aes_128_gcm_decrypt.c -lssl -lcrypto\\n$ cat /tmp/msg1   | ./aes_128_gcm_decrypt $iv $recordnum $key $recdata $authtag   | hexdump -C\\n\\n00000000  0f 00 01 04 00 03 00 00  00 00 01 04 08 04 01 00  |................|\\n00000010  2c 76 3d 6a d3 d8 af 7f  a3 7d a6 d8 d9 0e 73 7c  |,v=j?د.?}???.s||\\n00000020  ea 53 ee 7a ff a5 61 48  74 cc 68 48 9c 73 a2 f3  |?S?z??aHt?hH.s??|\\n00000030  a0 43 cb ba e6 c2 7a 41  91 0e de 9a df c7 22 23  |?C˺??zA..?.??\\\"#|\\n00000040  58 26 12 ec 96 79 fe 1f  9f a5 f4 a4 b6 12 f8 6f  |X&.?.y?..????.?o|\\n... snip ...\"\n              }\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"握手消息头\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"0f 00 01 04\"\n          },\n          [\n            \"每个握手消息都以一个 type 和一个 len 开始。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"0f - 握手消息类型 0x0f (certificate verify)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 01 04 - 紧接着的握手消息数据的长度 0x104 (260) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"用于重建握手顺序的信息(Handshake Reconstruction Data)\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"00 03 00 00 00 00 01 04\"\n          },\n          [\n            \"因为 UDP (或其他数据报协议)不保证交付或排序，而且数据报的长度可能比需要发送的握手记录长度要小。因此 DTLS 必须提供一定的信息，以支持在数据丢失、包重排序或有记录碎片的情况下，使得对等端(peer)能够重新构建一条正确的 DTLS 记录。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 03 - DTLS 序列号 0x3(3)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 00 - 表明记录碎片的偏移量为 0x00(0) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 01 04 - 表明之后的记录碎片的长度为 0x104(260) 字节\"\n                }\n              ]\n            },\n            \"在本文例子中，整个握手记录的长度要短于一个 UDP 数据报的可承载长度，因此偏移量为零，且长度为整个握手记录长。\"\n          ]\n        ],\n        [\n          \"签名\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"08 04 01 00 2c 76 3d 6a d3 d8 af 7f a3 7d a6 d8 d9 0e 73 7c ea 53 ee 7a ff a5 61 48 74 cc 68 48 9c 73 a2 f3 a0 43 cb ba e6 c2 7a 41 91 0e de 9a df c7 22 23 58 26 12 ec 96 79 fe 1f 9f a5 f4 a4 b6 12 f8 6f 40 88 49 a3 29 f7 63 e0 4f be 95 9a 91 e8 d1 8d 4a ba 79 29 57 6f a0 24 ec b2 37 d6 33 78 e9 8e e5 9d c9 59 49 b2 63 b3 06 53 0a 2e 6f b9 b2 2f a2 3c 64 32 33 43 03 89 33 01 fd 60 e2 05 82 6e b9 ec 41 4f ec 5f 9a 0d 6f 8f 3d 89 a0 9f 14 8e 0f 05 03 49 bc 1e 17 97 d9 28 1e ed f6 e7 66 9c e2 56 ae 79 d4 ee 8c 96 56 0d cf 07 6c 2a 45 a4 ee e8 d2 79 71 0f 0c e7 03 4a 3f 5c aa 94 41 4e ae df 61 08 48 66 e4 9e 81 88 3e e2 1a 12 59 3c cb 96 dd 11 76 9e 34 0f 1e 6c c2 14 b0 57 95 e5 4a fc 94 79 84 5e 4d f2 bf 96 9f bb 21 8c b9 c4 b8 34 a8 51 be 34 75 a1 45 2f 4b 33 55 4f 9d 65\"\n          },\n          [\n            \"由于服务器会为每个会话都生成短暂的密钥，所以和 TLS 之前的版本不同，密钥与证书不会有内在的联系。\",\n            \"为了证明服务器拥有服务器证书(在这个 TLS 会话中的有效性)，它需要使用证书的私钥对握手信息的哈希进行签名。而客户端可以通过使用证书的公钥来证明该签名的有效性。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"08 04 - 表明签名算法为 RSA-PSS-RSAE-SHA256\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"01 00 - 表明签名算法长度为 0x100(256) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"2c 76 3d ... 4f 9d 65 - 签名\"\n                }\n              ]\n            },\n            \"签名过程不能在命令行中逐字逐句地复制，因为签名工具在签名中引入了随机或变化的数据。\",\n            {\n              \"children\": [\n                \"相反，我们可以像客户端那样，在命令行使用\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://dtls.xargs.org/files/server.crt\"\n                  },\n                  \"content\": \"服务器的证书\"\n                },\n                \"提供的公钥来验证签名：\"\n              ]\n            },\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### find the hash of the conversation to this point, excluding\\n### cleartext record headers, DTLS-only record headers,\\n### or 1-byte decrypted record trailers\\n$ handshake_hash=$((\\n   cat record-chello | perl -0777 -pe 's/.{13}(.{4}).{8}/$1/s';\\n   cat record-shello | perl -0777 -pe 's/.{13}(.{4}).{8}/$1/s';\\n   cat record-encext | perl -0777 -pe 's/(.{4}).{8}(.*).$/$1$2/s';\\n   cat record-cert   | perl -0777 -pe 's/(.{4}).{8}(.*).$/$1$2/s';\\n   )| openssl sha256)\\n\\n### build the data that was signed:\\n### 1. add 64 space characters\\n$ echo -n '                                ' > /tmp/tosign\\n$ echo -n '                                ' >> /tmp/tosign\\n### 2. add this fixed string\\n$ echo -n 'TLS 1.3, server CertificateVerify' >> /tmp/tosign\\n### 3. add a single null character\\n$ echo -en '\\\\u0000' >> /tmp/tosign\\n### 4. add hash of handshake to this point\\n$ echo $handshake_hash | xxd -r -p >> /tmp/tosign\\n\\n### copy the signature that we want to verify\\n$ echo \\\"2c 76 3d 6a d3 d8 af 7f a3 7d a6 d8 d9 0e 73 7c ea 53 ee 7a\\n  ff a5 61 48 74 cc 68 48 9c 73 a2 f3 a0 43 cb ba e6 c2 7a 41 91 0e\\n  de 9a df c7 22 23 58 26 12 ec 96 79 fe 1f 9f a5 f4 a4 b6 12 f8 6f\\n  40 88 49 a3 29 f7 63 e0 4f be 95 9a 91 e8 d1 8d 4a ba 79 29 57 6f\\n  a0 24 ec b2 37 d6 33 78 e9 8e e5 9d c9 59 49 b2 63 b3 06 53 0a 2e\\n  6f b9 b2 2f a2 3c 64 32 33 43 03 89 33 01 fd 60 e2 05 82 6e b9 ec\\n  41 4f ec 5f 9a 0d 6f 8f 3d 89 a0 9f 14 8e 0f 05 03 49 bc 1e 17 97\\n  d9 28 1e ed f6 e7 66 9c e2 56 ae 79 d4 ee 8c 96 56 0d cf 07 6c 2a\\n  45 a4 ee e8 d2 79 71 0f 0c e7 03 4a 3f 5c aa 94 41 4e ae df 61 08\\n  48 66 e4 9e 81 88 3e e2 1a 12 59 3c cb 96 dd 11 76 9e 34 0f 1e 6c\\n  c2 14 b0 57 95 e5 4a fc 94 79 84 5e 4d f2 bf 96 9f bb 21 8c b9 c4\\n  b8 34 a8 51 be 34 75 a1 45 2f 4b 33 55 4f 9d 65\\\" | xxd -r -p > /tmp/sig\\n\\n### extract the public key from the certificate\\n$ openssl x509 -pubkey -noout -in server.crt > server.pub\\n\\n### verify the signature\\n$ cat /tmp/tosign | openssl dgst -verify server.pub -sha256     -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:-1 -signature /tmp/sig\\n\\nVerified OK\"\n              }\n            }\n          ]\n        ],\n        [\n          \"记录类型\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"16\"\n          },\n          [\n            \"每一个加密的 DTLS 1.3 记录的最后一个字节都需要表明其真正的记录类型\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"16 - 类型为 0x16(22), 握手记录\"\n                }\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/DTLS/serverCertificateDatagram.json",
    "content": "[\n  \"服务器会发送一个或多个证书：\",\n  {\n    \"Tag\": \"ul\",\n    \"children\": [\n      {\n        \"Tag\": \"li\",\n        \"content\": \"该主机的证书，包含主机名、公钥和第三方的签名(签名证明该证书主机名的所有者持有该证书的私钥)。\"\n      },\n      {\n        \"Tag\": \"li\",\n        \"content\": \"其他证书的可选列表，从主机证书一直到预先安装在客户端上的可信证书。其中每一个都对前一个证书进行签名，形成一个信任链。\"\n      }\n    ]\n  },\n  {\n    \"children\": [\n      \"为了让这个例子不至于太过于庞大，我们只发送一个主机证书。证书以一种叫做 \",\n      {\n        \"Tag\": \"a\",\n        \"props\": {\n          \"href\": \"https://www.cnblogs.com/flydean/p/16541577.html\"\n        },\n        \"content\": \"DER\"\n      },\n      \" 的二进制格式书写。\"\n    ]\n  },\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"头部信息字节\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"2e\"\n          },\n          [\n            \"加密的 DTLS 数据包都以一个 \\\"统一的头部(unified header)\\\"开始。头部的第一个字节给出了头部和数据包的结构信息，以及解密时需要的信息。\",\n            \"值 0x2e 具有以下含义：\",\n            {\n              \"Tag\": \"Table\",\n              \"props\": {\n                \"headers\": [\"\", \"值\", \"含义\"],\n                \"data\": [\n                  [\"高位\", \"001\", \"固定位\"],\n                  [\"\", \"0\", \"头部中不存在连接 ID 字段(1则存在)\"],\n                  [\"\", \"1\", \"序列号在头部中占 2 字节长\"],\n                  [\"\", \"1\", \"头部中存在\\\"记录长度\\\"字段(0则不存在)\"],\n                  [\n                    \"低位\",\n                    \"10\",\n                    \"加密序列指示(Encryption epoch 2)，现在密钥是握手时密钥\"\n                  ]\n                ]\n              }\n            }\n          ]\n        ],\n        [\n          \"记录序号\",\n          {\n            \"props\": {\n              \"className\": \"bytes protected\",\n              \"title\": \"被加密\"\n            },\n            \"content\": \"ed 2b\"\n          },\n          {\n            \"props\": {\n              \"className\": \"bytes unprotected\"\n            },\n            \"content\": \"00 01\"\n          },\n          [\n            \"记录序号是被加密了的，用以防止中间件误解(interpreting)或干扰数据包的排序。\",\n            \"加密是通过用 \\\"服务器端序号保护密钥\\\" 对每个数据包的有效载荷样本进行加密，然后将每个数据包中的某些比特和字节与所得数据进行 XOR 得到。\",\n            \"如果说的不够详细，这里有一个如何加密的例子：\",\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### \\\"server record number key\\\" from handshake keys calc step above\\n$ key=7173fac51194e775001d625ef69d7c9f\\n### sample is taken from 16 bytes of payload starting 5 bytes into the record\\n$ sample=d3777e1adf9e98c8c4ffa072c2c3b6bb\\n$ echo $sample | xxd -r -p | openssl aes-128-ecb -K $key | head -c 2 | xxd -p\\n\\ned2a\\n\\n### the above bytes are xor'd one-for-one into the bytes of the record number\"\n              }\n            }\n          ]\n        ],\n        [\n          \"记录长度\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"03 4b\"\n          },\n          [\n            \"每个记录除非给出这个长度字段，否则对等端将认为数据报剩余的所有字节都是同一个记录的真实载荷。有了这一字段，则在一个数据报中可以发送好几个 TLS 记录(尽管例子中的连接没有利用这个优势)。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"03 4b - 表示 TLS 记录长度为 0x34B(843) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"加密的数据载荷\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\",\n              \"title\": \"被\\\"握手密钥\\\"加密\"\n            },\n            \"content\": \"d3 77 7e 1a df 9e 98 c8 c4 ff a0 72 c2 c3 b6 bb cd 9f bd 2d 1f 34 3c 5d f9 54 d9 97 a2 cd 1d 33 42 a3 3d 3f 6a 85 e1 21 42 3c e0 02 ea 33 5e 37 7e 7a 21 5b 8a 9e cc 5e 26 7c 60 a2 bc 79 4e d1 d8 1f 39 8b ce df a3 68 fb db 7c a3 67 a0 46 65 5d 61 e4 86 67 62 fa ac fd a4 9d 0f 3a 39 71 86 d8 32 e4 81 87 d0 76 ea 8d e5 32 12 87 be 9b fd a2 15 19 da 58 e0 c4 80 56 99 7e 49 2e df e4 76 6b 2c d5 1e a1 2b c2 f6 d5 50 5b 80 e5 1a 64 5d a9 b0 7f bf 7a 01 b8 4d 5b a7 22 b2 e1 7d d9 52 8c 28 63 cd 63 a7 35 b5 4c d8 23 95 87 84 1a 59 2f be 57 5b 2d e1 8a 6c 99 f7 82 a9 56 e2 8c e7 69 67 42 67 3d 7e e7 37 f4 6e 9c ba a2 89 2d 97 21 ef cc c9 1f 16 72 26 a5 be 4c 9c d8 6b 97 fe f3 32 3f d1 92 f4 60 e8 ef 8b 91 3b bf 9f 97 05 63 85 d4 c3 ec 2b 2b dc 2e c4 8a 66 8c f6 f1 0d b3 fe 00 91 97 fa b9 8d 7c 2a 88 15 ac 5a 4e d3 aa 08 94 b9 f9 f9 95 12 43 0d f2 1f 13 4c 49 34 40 73 f9 af 32 8e 35 c2 e1 6b 91 3f 4e 61 33 21 e4 a7 9b d2 d3 38 47 32 1e 61 5d 58 94 09 b1 65 f9 c2 b0 18 80 4f 3c 33 40 e4 0a d5 f5 9a 26 46 0a 12 0f 2d 55 fc 8b ca 47 22 74 fd b9 06 09 a2 18 70 e1 cc 41 aa d0 24 fa 48 a8 6f 07 8f 90 8b c6 26 18 c4 c3 2f 0c fc fb b5 95 a7 d2 93 f4 ba ab 93 ff 35 f0 de 10 71 17 1e 4c 51 0d 75 dd 29 f5 0d 3d e8 1c ae 9e 1c 56 ed 60 9c 1b c7 27 5e ac 1d 69 33 df 08 93 dd 0e 3c 5c 7f d3 65 14 26 b3 e4 c3 ca 6d 46 1d 82 0a df ff 75 fb 7b 15 8b e9 89 30 89 da c9 30 a0 15 f8 9c b4 ef 22 7a b9 e4 3d f0 14 7a 25 07 59 e3 e0 1b 5d b7 48 0c 52 7a 1d 4b 8a 09 c4 ac 05 fc c6 d6 40 15 d6 af 2c 3e 52 15 03 a8 2f b9 02 5c 61 98 18 ca 31 fb 24 03 63 0a c0 6a b7 11 90 53 a7 02 86 24 0b 3f 8e 43 96 61 ad 95 48 7a a5 72 d7 08 60 8d d0 d4 fe 27 bb cf 1e df 50 3a 54 05 46 0b 9e 10 f6 93 4a 41 a8 cf b7 0b 60 90 6f 7e 66 d6 53 15 61 ef 08 ad e3 de 45 77 a7 77 6b f6 56 bb 48 5c ee 28 2c 83 7a a8 bc e0 6a e6 06 a1 71 d7 54 96 36 fe d8 3e 24 bf 9f 10 5b 7d 1d 02 da 30 86 ce 24 49 af a2 d0 ec 26 18 5d 0c 1f 05 2f 88 cd 9d 55 eb 12 4b da e3 66 7f 59 79 97 95 f9 27 50 b9 ca 70 55 66 86 6a 99 24 a2 46 a4 71 90 4b 2d 69 dc 17 cb fe 50 a5 62 ff 26 ff 9e 40 4d 7b 2a 11 67 0c 27 56 3f 3e 37 99 3c c6 e6 73 43 6d c3 a8 51 21 4d 6d 27 86 2b 64 5d cb 0b f4 d4 c7 44 0f 6a d4 83 ef 9d 58 fa b4 7d 24 4b d6 cf a6 8f 12 e9 aa ae cd 2d 52 8e 85 66 f9 7f 50 56 cf 8e fc 7d 1e 55 fb ee 1b e8 7f 7f 89 73 7c 8a fa 20 e4 96 37 0d 25 f7 52 99 e5 91 8c b9 4b a5 b5 ef db 84 7d 9c a5 44 a5 38 65 a3 6d 69 1e be 8b e8 e2 da 08 c1 7b e9 02 38 0d b9 a3 d7 04 91 b8 98 f8 c5 88 e7 44 64 8e b9 37 70 53 0c 83 ce cf\"\n          },\n          [\"这些数据使用服务器端的\\\"握手密钥\\\"进行加密。\"]\n        ],\n        [\n          \"AEAD 鉴别标签\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"a4 30 70 21 45 22 93 8c 0e 66 82 9e f1 33 34 9b\"\n          },\n          [\n            {\n              \"children\": [\n                \"这是 \",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://zhuanlan.zhihu.com/p/28566058\"\n                  },\n                  \"content\": \"AEAD 算法\"\n                },\n                \"的鉴别标签，确认加密数据和记录头的完整性。它由加密算法产生，并由解密算法消耗。\"\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"data\": [\n        [\n          \"\",\n          {\n            \"props\": {\n              \"className\": \"decryption-header\"\n            },\n            \"content\": \"解密后的数据载荷\"\n          },\n          [\n            { \"Tag\": \"h4\", \"content\": \"解密\" },\n            \"数据被 \\\"服务器端握手密钥计算\\\" 步骤中产生的初始密钥和初始向量(IVs)加密。IVs 通过密钥和已经用密钥加密的记录长度进行异或操作生成。在例子中 IV 为 1。\",\n            \"数据包开头的 5 字节(记录头)还会作为解密过程解密成功时必须满足的认证条件。\",\n            {\n              \"children\": [\n                \"openssl 命令行工具还不支持 AEAD 算法加解密(AEAD ciphers)，你可以使用作者的命令行工具来\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://dtls.xargs.org/files/aes_128_gcm_decrypt.c\"\n                  },\n                  \"content\": \"解密\"\n                },\n                \"和\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://dtls.xargs.org/files/aes_128_gcm_encrypt.c\"\n                  },\n                  \"content\": \"加密\"\n                },\n                \"这些数据。\"\n              ]\n            },\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### from the \\\"Server Handshake Keys Calc\\\" step\\n$ key=004e03e64ab6cba6b542775ec230e20a\\n$ iv=6d9924be044ee97c624913f2\\n### from this record\\n$ recdata=2e0001034b\\n$ authtag=a43070214522938c0e66829ef133349b\\n$ recordnum=1\\n### may need to add -I and -L flags for include and lib dirs\\n$ cc -o aes_128_gcm_decrypt aes_128_gcm_decrypt.c -lssl -lcrypto\\n$ cat /tmp/msg1   | ./aes_128_gcm_decrypt $iv $recordnum $key $recdata $authtag   | hexdump -C\\n\\n00000000  0b 00 03 2e 00 02 00 00  00 00 03 2e 00 00 03 2a  |...............*|\\n00000010  00 03 25 30 82 03 21 30  82 02 09 a0 03 02 01 02  |..a4..!0...?....|\\n00000020  02 08 15 5a 92 ad c2 04  8f 90 30 0d 06 09 2a 86  |...Z.??...0...*.|\\n00000030  48 86 f7 0d 01 01 0b 05  00 30 22 31 0b 30 09 06  |H.?......0\\\"1.0..|\\n00000040  03 55 04 06 13 02 55 53  31 13 30 11 06 03 55 04  |.U....US1.0...U.|\\n00000050  0a 13 0a 45 78 61 6d 70  6c 65 20 43 41 30 1e 17  |...Example CA0..|\\n00000060  0d 31 38 31 30 30 35 30  31 33 38 31 37 5a 17 0d  |.181005013817Z..|\\n00000070  31 39 31 30 30 35 30 31  33 38 31 37 5a 30 2b 31  |191005013817Z0+1|\\n00000080  0b 30 09 06 03 55 04 06  13 02 55 53 31 1c 30 1a  |.0...U....US1.0.|\\n00000090  06 03 55 04 03 13 13 65  78 61 6d 70 6c 65 2e 75  |..U....example.u|\\n... snip ...\\n\"\n              }\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"握手消息头\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"0b 00 03 2e\"\n          },\n          [\n            \"每个握手消息都以一个 type 和一个 len 开始。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"0b - 握手消息类型 0x0b (certificate)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 03 2e - 紧接着的握手消息数据的长度 0x32E (814) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"用于重建握手顺序的信息(Handshake Reconstruction Data)\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"00 02 00 00 00 00 03 2e\"\n          },\n          [\n            \"因为 UDP (或其他数据报协议)不保证交付或排序，而且数据报的长度可能比需要发送的握手记录长度要小。因此 DTLS 必须提供一定的信息，以支持在数据丢失、包重排序或有记录碎片的情况下，使得对等端(peer)能够重新构建一条正确的 DTLS 记录。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 02 - DTLS 序列号 0x2(2)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 00 - 表明记录碎片的偏移量为 0x00(0) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 03 2e - 表明之后的记录碎片的长度为 0x32E(814) 字节\"\n                }\n              ]\n            },\n            \"在本文例子中，整个握手记录的长度要短于一个 UDP 数据报的可承载长度，因此偏移量为零，且长度为整个握手记录长。\"\n          ]\n        ],\n        [\n          \"请求上下文\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"00\"\n          },\n          [\n            \"数据为空，因为该证书不是响应证书请求而发送。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 - 没有数据\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"所有证书长度\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 03 2a\"\n          },\n          [\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 03 2a - 紧接着的证书的长度 0x32A (810) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"第一个证书长度\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 03 25\"\n          },\n          [\n            \"此时同时也是唯一一个\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 03 25 - 紧接着的证书的长度 0x325 (805) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"证书\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"30 82 03 21 30 82 02 09 a0 03 02 01 02 02 08 15 5a 92 ad c2 04 8f 90 30 0d 06 09 2a 86 48 86 f7 0d 01 01 0b 05 00 30 22 31 0b 30 09 06 03 55 04 06 13 02 55 53 31 13 30 11 06 03 55 04 0a 13 0a 45 78 61 6d 70 6c 65 20 43 41 30 1e 17 0d 31 38 31 30 30 35 30 31 33 38 31 37 5a 17 0d 31 39 31 30 30 35 30 31 33 38 31 37 5a 30 2b 31 0b 30 09 06 03 55 04 06 13 02 55 53 31 1c 30 1a 06 03 55 04 03 13 13 65 78 61 6d 70 6c 65 2e 75 6c 66 68 65 69 6d 2e 6e 65 74 30 82 01 22 30 0d 06 09 2a 86 48 86 f7 0d 01 01 01 05 00 03 82 01 0f 00 30 82 01 0a 02 82 01 01 00 c4 80 36 06 ba e7 47 6b 08 94 04 ec a7 b6 91 04 3f f7 92 bc 19 ee fb 7d 74 d7 a8 0d 00 1e 7b 4b 3a 4a e6 0f e8 c0 71 fc 73 e7 02 4c 0d bc f4 bd d1 1d 39 6b ba 70 46 4a 13 e9 4a f8 3d f3 e1 09 59 54 7b c9 55 fb 41 2d a3 76 52 11 e1 f3 dc 77 6c aa 53 37 6e ca 3a ec be c3 aa b7 3b 31 d5 6c b6 52 9c 80 98 bc c9 e0 28 18 e2 0b f7 f8 a0 3a fd 17 04 50 9e ce 79 bd 9f 39 f1 ea 69 ec 47 97 2e 83 0f b5 ca 95 de 95 a1 e6 04 22 d5 ee be 52 79 54 a1 e7 bf 8a 86 f6 46 6d 0d 9f 16 95 1a 4c f7 a0 46 92 59 5c 13 52 f2 54 9e 5a fb 4e bf d7 7a 37 95 01 44 e4 c0 26 87 4c 65 3e 40 7d 7d 23 07 44 01 f4 84 ff d0 8f 7a 1f a0 52 10 d1 f4 f0 d5 ce 79 70 29 32 e2 ca be 70 1f df ad 6b 4b b7 11 01 f4 4b ad 66 6a 11 13 0f e2 ee 82 9e 4d 02 9d c9 1c dd 67 16 db b9 06 18 86 ed c1 ba 94 21 02 03 01 00 01 a3 52 30 50 30 0e 06 03 55 1d 0f 01 01 ff 04 04 03 02 05 a0 30 1d 06 03 55 1d 25 04 16 30 14 06 08 2b 06 01 05 05 07 03 02 06 08 2b 06 01 05 05 07 03 01 30 1f 06 03 55 1d 23 04 18 30 16 80 14 89 4f de 5b cc 69 e2 52 cf 3e a3 00 df b1 97 b8 1d e1 c1 46 30 0d 06 09 2a 86 48 86 f7 0d 01 01 0b 05 00 03 82 01 01 00 59 16 45 a6 9a 2e 37 79 e4 f6 dd 27 1a ba 1c 0b fd 6c d7 55 99 b5 e7 c3 6e 53 3e ff 36 59 08 43 24 c9 e7 a5 04 07 9d 39 e0 d4 29 87 ff e3 eb dd 09 c1 cf 1d 91 44 55 87 0b 57 1d d1 9b df 1d 24 f8 bb 9a 11 fe 80 fd 59 2b a0 39 8c de 11 e2 65 1e 61 8c e5 98 fa 96 e5 37 2e ef 3d 24 8a fd e1 74 63 eb bf ab b8 e4 d1 ab 50 2a 54 ec 00 64 e9 2f 78 19 66 0d 3f 27 cf 20 9e 66 7f ce 5a e2 e4 ac 99 c7 c9 38 18 f8 b2 51 07 22 df ed 97 f3 2e 3e 93 49 d4 c6 6c 9e a6 39 6d 74 44 62 a0 6b 42 c6 d5 ba 68 8e ac 3a 01 7b dd fc 8e 2c fc ad 27 cb 69 d3 cc dc a2 80 41 44 65 d3 ae 34 8c e0 f3 4a b2 fb 9c 61 83 71 31 2b 19 10 41 64 1c 23 7f 11 a5 d6 5c 84 4f 04 04 84 99 38 71 2b 95 9e d6 85 bc 5c 5d d6 45 ed 19 90 94 73 40 29 26 dc b4 0e 34 69 a1 59 41 e8 e2 cc a8 4b b6 08 46 36 a0\"\n          },\n          [\n            {\n              \"children\": [\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://dtls.xargs.org/files/server.crt\"\n                  },\n                  \"content\": \"该证书\"\n                },\n                \"采用 ASN.1 DER 编码。可以在命令行中使用以下命令转换为二进制数据：\"\n              ]\n            },\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"$ openssl x509 -outform der < server.crt | hexdump\\n\\n0000000 30 82 03 21 30 82 02 09 a0 03 02 01 02 02 08 15\\n0000010 5a 92 ad c2 04 8f 90 30 0d 06 09 2a 86 48 86 f7\\n... snip ...\"\n              }\n            }\n          ]\n        ],\n        [\n          \"证书的扩展\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"00 00\"\n          },\n          [\n            \"服务器可以提供证书需要的扩展信息。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 - 没有扩展信息\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"记录类型\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"16\"\n          },\n          [\n            \"每一个加密的 DTLS 1.3 记录的最后一个字节都需要表明其真正的记录类型\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"16 - 类型为 0x16(22), 握手记录\"\n                }\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/DTLS/serverEncryptedExtensionsDatagram.json",
    "content": "[\n  \"连接(包括握手)的数据从这时候起就能够被加密了。加密握手数据是 DTLS 1.3 的新特性。\",\n  \"任何不需要协商其他加密密钥的扩展都会列在这里。加密以隐藏它们不被窃听者和中间件(middleboxes)发现。\",\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"头部信息字节\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"2e\"\n          },\n          [\n            \"加密的 DTLS 数据包都以一个 \\\"统一的头部(unified header)\\\"开始。头部的第一个字节给出了头部和数据包的结构信息，以及解密时需要的信息。\",\n            \"值 0x2e 具有以下含义：\",\n            {\n              \"Tag\": \"Table\",\n              \"props\": {\n                \"headers\": [\"\", \"值\", \"含义\"],\n                \"data\": [\n                  [\"高位\", \"001\", \"固定位\"],\n                  [\"\", \"0\", \"头部中不存在连接 ID 字段(1则存在)\"],\n                  [\"\", \"1\", \"序列号在头部中占 2 字节长\"],\n                  [\"\", \"1\", \"头部中存在\\\"记录长度\\\"字段(0则不存在)\"],\n                  [\n                    \"低位\",\n                    \"10\",\n                    \"加密序列指示(Encryption epoch 2)，现在密钥是握手时密钥\"\n                  ]\n                ]\n              }\n            }\n          ]\n        ],\n        [\n          \"记录序号\",\n          {\n            \"props\": {\n              \"className\": \"bytes protected\",\n              \"title\": \"被加密\"\n            },\n            \"content\": \"79 fa\"\n          },\n          {\n            \"props\": {\n              \"className\": \"bytes unprotected\"\n            },\n            \"content\": \"00 00\"\n          },\n          [\n            \"记录序号是被加密了的，用以防止中间件误解(interpreting)或干扰数据包的排序。\",\n            \"加密是通过用 \\\"服务器端序号保护密钥\\\" 对每个数据包的有效载荷样本进行加密，然后将每个数据包中的某些比特和字节与所得数据进行 XOR 得到。\",\n            \"如果说的不够详细，这里有一个如何加密的例子：\",\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### \\\"server record number key\\\" from handshake keys calc step above\\n$ key=7173fac51194e775001d625ef69d7c9f\\n### sample is taken from 16 bytes of payload starting 5 bytes into the record\\n$ sample=ee9dcff3f8679a4859fe68377fb34ada\\n$ echo $sample | xxd -r -p | openssl aes-128-ecb -K $key | head -c 2 | xxd -p\\n\\n79fa\\n\\n### the above bytes are xor'd one-for-one into the bytes of the record number\"\n              }\n            }\n          ]\n        ],\n        [\n          \"记录长度\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 2f\"\n          },\n          [\n            \"每个记录除非给出这个长度字段，否则对等端将认为数据报剩余的所有字节都是同一个记录的真实载荷。有了这一字段，则在一个数据报中可以发送好几个 TLS 记录(尽管例子中的连接没有利用这个优势)。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 2f - 表示 TLS 记录长度为 0x2F(47) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"加密的数据载荷\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\",\n              \"title\": \"被\\\"握手密钥\\\"加密\"\n            },\n            \"content\": \"ee 9d cf f3 f8 67 9a 48 59 fe 68 37 7f b3 4a da 85 df 87 9c 67 3e 50 1d 7a 4e 8f 19 50 e0 fc\"\n          },\n          [\"这些数据使用服务器端的\\\"握手密钥\\\"进行加密。\"]\n        ],\n        [\n          \"AEAD 鉴别标签\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"f6 7f e4 42 e7 d7 d2 b8 a3 d5 fa 59 57 4f fd 00\"\n          },\n          [\n            {\n              \"children\": [\n                \"这是 \",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://zhuanlan.zhihu.com/p/28566058\"\n                  },\n                  \"content\": \"AEAD 算法\"\n                },\n                \"的鉴别标签，确认加密数据和记录头的完整性。它由加密算法产生，并由解密算法消耗。\"\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"data\": [\n        [\n          \"\",\n          {\n            \"props\": {\n              \"className\": \"decryption-header\"\n            },\n            \"content\": \"解密后的数据载荷\"\n          },\n          [\n            { \"Tag\": \"h4\", \"content\": \"解密\" },\n            \"数据被 \\\"服务器端握手密钥计算\\\" 步骤中产生的初始密钥和初始向量(IVs)加密。IVs 通过密钥和已经用密钥加密的记录长度进行异或操作生成。在例子中 IV 为 0。\",\n            \"数据包开头的 5 字节(记录头)还会作为解密过程解密成功时必须满足的认证条件。\",\n            {\n              \"children\": [\n                \"openssl 命令行工具还不支持 AEAD 算法加解密(AEAD ciphers)，你可以使用作者的命令行工具来\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://dtls.xargs.org/files/aes_128_gcm_decrypt.c\"\n                  },\n                  \"content\": \"解密\"\n                },\n                \"和\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://dtls.xargs.org/files/aes_128_gcm_encrypt.c\"\n                  },\n                  \"content\": \"加密\"\n                },\n                \"这些数据。\"\n              ]\n            },\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### from the \\\"Server Handshake Keys Calc\\\" step\\n$ key=004e03e64ab6cba6b542775ec230e20a\\n$ iv=6d9924be044ee97c624913f2\\n### from this record\\n$ recdata=2e0000002f\\n$ authtag=f67fe442e7d7d2b8a3d5fa59574ffd00\\n$ recordnum=0\\n### may need to add -I and -L flags for include and lib dirs\\n$ cc -o aes_128_gcm_decrypt aes_128_gcm_decrypt.c -lssl -lcrypto\\n$ cat /tmp/msg1   | ./aes_128_gcm_decrypt $iv $recordnum $key $recdata $authtag   | hexdump -C\\n\\n00000000  08 00 00 12 00 01 00 00  00 00 00 12 00 10 00 0a  |................|\\n00000010  00 0c 00 0a 00 17 00 1d  00 18 00 19 01 00 16     |...............|\"\n              }\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"握手消息头\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"08 00 00 12\"\n          },\n          [\n            \"每个握手消息都以一个 type 和一个 len 开始。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"08 - 握手消息类型 0x08 (encrypted extensions)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 12 - 紧接着的握手消息数据的长度 0x12 (18) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"用于重建握手顺序的信息(Handshake Reconstruction Data)\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"00 01 00 00 00 00 00 12\"\n          },\n          [\n            \"因为 UDP (或其他数据报协议)不保证交付或排序，而且数据报的长度可能比需要发送的握手记录长度要小。因此 DTLS 必须提供一定的信息，以支持在数据丢失、包重排序或有记录碎片的情况下，使得对等端(peer)能够重新构建一条正确的 DTLS 记录。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 01 - DTLS 序列号 0x1(1)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 00 - 表明记录碎片的偏移量为 0x00(0) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 12 - 表明之后的记录碎片的长度为 0x12(18) 字节\"\n                }\n              ]\n            },\n            \"在本文例子中，整个握手记录的长度要短于一个 UDP 数据报的可承载长度，因此偏移量为零，且长度为整个握手记录长。\"\n          ]\n        ],\n        [\n          \"扩展的长度\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"00 10\"\n          },\n          [\n            \"服务器向客户端返回的扩展有序列表的长度。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 10 - 扩展列表的长度为 0x10(16) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"扩展 - 支持的组\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"00 0a 00 0c 00 0a 00 17 00 1d 00 18 00 19 01 00\"\n          },\n          [\n            \"服务器返回它所支持的椭圆曲线加解密算法列表。为了使这个扩展更加通用，未来可以支持其他的密码学类型，因此称这些为 \\\"支持的组\\\" 而不是 \\\"支持的曲线\\\"。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 0a - 表示这是 \\\"支持的组\\\" 扩展\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 0c - 扩展信息长度为 0x0c(12) 字节 \"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 0a - 曲线列表条目长度为 0x0a(10) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 17 - 代表 secp256r1 曲线\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 1d - 代表 x25519 曲线\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 18 - 代表 secp384r1 曲线\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 19 - 代表 secp521r1 曲线\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"10 00 - 代表 ffdhe2048 曲线\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"记录类型\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"16\"\n          },\n          [\n            \"每一个加密的 DTLS 1.3 记录的最后一个字节都需要表明其真正的记录类型\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"16 - 类型为 0x16(22), 握手记录\"\n                }\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/DTLS/serverHandshakeFinishedDatagram.json",
    "content": "[\n  \"为了验证握手成功且没有被篡改过，服务器会创建一些验证数据给客户端确认。验证数据是基于所有握手信息的哈希值计算得到。\",\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"头部信息字节\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"2e\"\n          },\n          [\n            \"加密的 DTLS 数据包都以一个 \\\"统一的头部(unified header)\\\"开始。头部的第一个字节给出了头部和数据包的结构信息，以及解密时需要的信息。\",\n            \"值 0x2e 具有以下含义：\",\n            {\n              \"Tag\": \"Table\",\n              \"props\": {\n                \"headers\": [\"\", \"值\", \"含义\"],\n                \"data\": [\n                  [\"高位\", \"001\", \"固定位\"],\n                  [\"\", \"0\", \"头部中不存在连接 ID 字段(1则存在)\"],\n                  [\"\", \"1\", \"序列号在头部中占 2 字节长\"],\n                  [\"\", \"1\", \"头部中存在\\\"记录长度\\\"字段(0则不存在)\"],\n                  [\n                    \"低位\",\n                    \"10\",\n                    \"加密序列指示(Encryption epoch 2)，现在密钥是握手时密钥\"\n                  ]\n                ]\n              }\n            }\n          ]\n        ],\n        [\n          \"记录序号\",\n          {\n            \"props\": {\n              \"className\": \"bytes protected\",\n              \"title\": \"被加密\"\n            },\n            \"content\": \"0b b8\"\n          },\n          {\n            \"props\": {\n              \"className\": \"bytes unprotected\"\n            },\n            \"content\": \"00 03\"\n          },\n          [\n            \"记录序号是被加密了的，用以防止中间件误解(interpreting)或干扰数据包的排序。\",\n            \"加密是通过用 \\\"服务器端序号保护密钥\\\" 对每个数据包的有效载荷样本进行加密，然后将每个数据包中的某些比特和字节与所得数据进行 XOR 得到。\",\n            \"如果说的不够详细，这里有一个如何加密的例子：\",\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### \\\"server record number key\\\" from handshake keys calc step above\\n$ key=7173fac51194e775001d625ef69d7c9f\\n### sample is taken from 16 bytes of payload starting 5 bytes into the record\\n$ sample=a44135732a099823b8a5f61a2b35ce92\\n$ echo $sample | xxd -r -p | openssl aes-128-ecb -K $key | head -c 2 | xxd -p\\n\\n0bbb\\n\\n### the above bytes are xor'd one-for-one into the bytes of the record number\"\n              }\n            }\n          ]\n        ],\n        [\n          \"记录长度\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 3d\"\n          },\n          [\n            \"每个记录除非给出这个长度字段，否则对等端将认为数据报剩余的所有字节都是同一个记录的真实载荷。有了这一字段，则在一个数据报中可以发送好几个 DTLS 记录(尽管例子中的连接没有利用这个优势)。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 3d - 表示 DTLS 记录长度为 0x3d(61) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"加密的数据载荷\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\",\n              \"title\": \"被\\\"握手密钥\\\"加密\"\n            },\n            \"content\": \"a4 41 35 73 2a 09 98 23 b8 a5 f6 1a 2b 35 ce 92 1a 89 ab b1 52 f8 76 cd 26 79 7d c3 ed 73 d9 17 b2 99 c1 69 28 b9 cf 9e 58 d1 cd 58 68\"\n          },\n          [\"这些数据使用服务器端的\\\"握手密钥\\\"进行加密。\"]\n        ],\n        [\n          \"AEAD 鉴别标签\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"6b 8b 90 ce 9f e6 45 4e 0c ef 9e fc 40 f2 39 7a\"\n          },\n          [\n            {\n              \"children\": [\n                \"这是 \",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://zhuanlan.zhihu.com/p/28566058\"\n                  },\n                  \"content\": \"AEAD 算法\"\n                },\n                \"的鉴别标签，确认加密数据和记录头的完整性。它由加密算法产生，并由解密算法消耗。\"\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"data\": [\n        [\n          \"\",\n          {\n            \"props\": {\n              \"className\": \"decryption-header\"\n            },\n            \"content\": \"解密后的数据载荷\"\n          },\n          [\n            { \"Tag\": \"h4\", \"content\": \"解密\" },\n            \"数据被 \\\"服务器端握手密钥计算\\\" 步骤中产生的初始密钥和初始向量(IVs)加密。IVs 通过密钥和已经用密钥加密的记录长度进行异或操作生成。在例子中 IV 为 3。\",\n            \"数据包开头的 5 字节(记录头)还会作为解密过程解密成功时必须满足的认证条件。\",\n            {\n              \"children\": [\n                \"openssl 命令行工具还不支持 AEAD 算法加解密(AEAD ciphers)，你可以使用作者的命令行工具来\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://dtls.xargs.org/files/aes_128_gcm_decrypt.c\"\n                  },\n                  \"content\": \"解密\"\n                },\n                \"和\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://dtls.xargs.org/files/aes_128_gcm_encrypt.c\"\n                  },\n                  \"content\": \"加密\"\n                },\n                \"这些数据。\"\n              ]\n            },\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### from the \\\"Server Handshake Keys Calc\\\" step\\n$ key=004e03e64ab6cba6b542775ec230e20a\\n$ iv=6d9924be044ee97c624913f2\\n### from this record\\n$ recdata=2e0003003d\\n$ authtag=6b8b90ce9fe6454e0cef9efc40f2397a\\n$ recordnum=3\\n### may need to add -I and -L flags for include and lib dirs\\n$ cc -o aes_128_gcm_decrypt aes_128_gcm_decrypt.c -lssl -lcrypto\\n$ cat /tmp/msg1   | ./aes_128_gcm_decrypt $iv $recordnum $key $recdata $authtag   | hexdump -C\\n\\n00000000  14 00 00 20 00 04 00 00  00 00 00 20 1d 89 aa 62\\n00000010  e5 f8 8a 0f c9 52 88 47  15 d8 ac b3 79 86 59 af\\n00000020  b9 e7 78 9a 8d b2 b3 81  6b a4 52 46 16\\n... snip ...\"\n              }\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"握手消息头\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"14 00 00 20\"\n          },\n          [\n            \"每个握手消息都以一个 type 和一个 len 开始。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"14 - 握手消息类型 0x14 (finished)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 20 - 紧接着的握手消息数据的长度 0x20 (32) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"用于重建握手顺序的信息(Handshake Reconstruction Data)\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"00 04 00 00 00 00 00 20\"\n          },\n          [\n            \"因为 UDP (或其他数据报协议)不保证交付或排序，而且数据报的长度可能比需要发送的握手记录长度要小。因此 DTLS 必须提供一定的信息，以支持在数据丢失、包重排序或有记录碎片的情况下，使得对等端(peer)能够重新构建一条正确的 DTLS 记录。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 04 - DTLS 序列号 0x4(4)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 00 - 表明记录碎片的偏移量为 0x00(0) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 20 - 表明之后的记录碎片的长度为 0x20(32) 字节\"\n                }\n              ]\n            },\n            \"在本文例子中，整个握手记录的长度要短于一个 UDP 数据报的可承载长度，因此偏移量为零，且长度为整个握手记录长。\"\n          ]\n        ],\n        [\n          \"验证数据\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"1d 89 aa 62 e5 f8 8a 0f c9 52 88 47 15 d8 ac b3 79 86 59 af b9 e7 78 9a 8d b2 b3 81 6b a4 52 46\"\n          },\n          [\n            \"使用 \\\"服务器端生成握手密钥\\\" 步骤中的服务器握手时密钥和在这之前的每个握手记录(ClientHello 到 证书验证数据)的 SHA256 哈希值生成。\",\n            {\n              \"Tag\": \"pre\",\n              \"children\": [\n                {\n                  \"Tag\": \"code\",\n                  \"props\": { \"className\": \"longboi\" },\n                  \"content\": \"finished_key = HKDF-Expand-Label(key: server_secret, label: \\\"finished\\\", ctx: \\\"\\\", len: 32)\\nfinished_hash = SHA256(Client Hello ... Server Cert Verify)\\nverify_data = HMAC-SHA256(key: finished_key, msg: finished_hash)\"\n                }\n              ]\n            },\n            {\n              \"children\": [\n                \"在命令行中使用\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://dtls.xargs.org/files/hkdf-dtls.sh\"\n                  },\n                  \"content\": \"原作者制作的 HKDF 命令行脚本\"\n                },\n                \"，你也可以自己试试：\"\n              ]\n            },\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### find the hash of the conversation to this point, excluding\\n### cleartext record headers, DTLS-only record headers,\\n### or 1-byte decrypted record trailers\\n$ fin_hash=$((\\n    cat record-chello  | perl -0777 -pe 's/.{13}(.{4}).{8}/$1/s';\\n    cat record-shello  | perl -0777 -pe 's/.{13}(.{4}).{8}/$1/s';\\n    cat record-encext  | perl -0777 -pe 's/(.{4}).{8}(.*).$/$1$2/s';\\n    cat record-cert    | perl -0777 -pe 's/(.{4}).{8}(.*).$/$1$2/s';\\n    cat record-cverify | perl -0777 -pe 's/(.{4}).{8}(.*).$/$1$2/s';\\n  ) | openssl sha256)\\n$ sht_secret=8ad7990b9d249bcbaa0805d8d3f3ad2259e75f3a42c5d84db3ea3c6ee57b3d38\\n$ fin_key=$(./hkdf-dtls expandlabel $sht_secret \\\"finished\\\" \\\"\\\" 32)\\n$ echo $fin_hash | xxd -r -p     | openssl dgst -sha256 -mac HMAC -macopt hexkey:$fin_key\\n\\n1d89aa62e5f88a0fc952884715d8acb3798659afb9e7789a8db2b3816ba45246\"\n              }\n            }\n          ]\n        ],\n        [\n          \"记录类型\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"16\"\n          },\n          [\n            \"每一个加密的 DTLS 1.3 记录的最后一个字节都需要表明其真正的记录类型\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"16 - 类型为 0x16(22), 握手记录\"\n                }\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/DTLS/serverHandshakeKeysCalc.json",
    "content": "[\n  \"服务器现在拥有了用于计算握手时的加密密钥的所有信息。在这个计算中，服务器使用了以下信息：\",\n  {\n    \"Tag\": \"ul\",\n    \"children\": [\n      {\n        \"Tag\": \"li\",\n        \"content\": \"客户端公钥(提取自 ClientHello 数据报)\"\n      },\n      {\n        \"Tag\": \"li\",\n        \"content\": \"服务器端私钥(之前准备密钥交换时生成)\"\n      },\n      {\n        \"Tag\": \"li\",\n        \"content\": \"ClientHello 和 ServerHello 的 SHA256 哈希值\"\n      }\n    ]\n  },\n  \"首先，服务器需要找到共享的密文(shared secret)，即密钥交换步骤的最终值。服务器通过使用 curve25519 算法将客户端的公钥乘以服务器的私钥，即可发现 32 字节的最终值是：\",\n  {\n    \"Tag\": \"pre\",\n    \"props\": {\n      \"className\": \"ind2\"\n    },\n    \"children\": [\n      {\n        \"Tag\": \"code\",\n        \"props\": { \"className\": \"longboi\" },\n        \"content\": \"df4a291baa1eb7cfa6934b29b474baad2697e29f1f920dcc77c8a0a088447624\"\n      }\n    ]\n  },\n  {\n    \"children\": [\n      \"你可以使用\",\n      {\n        \"Tag\": \"a\",\n        \"props\": { \"href\": \"https://quic.xargs.org/files/curve25519-mult.c\" },\n        \"content\": \"原作者的脚本\"\n      },\n      \"快速验证结果：\"\n    ]\n  },\n  {\n    \"Tag\": \"CodeSample\",\n    \"props\": {\n      \"code\": \"$ cc -o curve25519-mult curve25519-mult.c\\n$ ./curve25519-mult server-ephemeral-private.key                     client-ephemeral-public.key | hexdump\\n\\n0000000 df 4a 29 1b aa 1e b7 cf a6 93 4b 29 b4 74 ba ad\\n0000010 26 97 e2 9f 1f 92 0d cc 77 c8 a0 a0 88 44 76 24\"\n    }\n  },\n  \"然后，服务器计算到此为止的所有握手信息(ClientHello 和 ServerHello)的 SHA256 哈希值(哈希值不包括记录中的 DTLS 专用字节，即 0-12 和 17-24 的字节。忽略这些字节允许实现者在 TLS 和 DTLS 的实现之间共享代码)。这个 \\\"hello_hash\\\" 是\",\n  {\n    \"Tag\": \"pre\",\n    \"props\": {\n      \"className\": \"ind2\"\n    },\n    \"children\": [\n      {\n        \"Tag\": \"code\",\n        \"props\": { \"className\": \"longboi\" },\n        \"content\": \"aee8eba0d2ee87052fbbc6864c1514c5a927d6f0ffb4f7954c7f379d95f1b1d7\"\n      }\n    ]\n  },\n  {\n    \"Tag\": \"CodeSample\",\n    \"props\": {\n      \"code\": \"$ (cat captures/caps/record-chello | perl -0777 -pe 's/.{13}(.{4}).{8}/$1/';\\n   cat captures/caps/record-shello | perl -0777 -pe 's/.{13}(.{4}).{8}/$1/')    | openssl sha256\\n\\naee8eba0d2ee87052fbbc6864c1514c5a927d6f0ffb4f7954c7f379d95f1b1d7\"\n    }\n  },\n  \"然后，我们将哈希值和共享的签名信息进行一些密钥派生操作(key derivation operations)，以防止可能的攻击：\",\n  {\n    \"Tag\": \"pre\",\n    \"children\": [\n      {\n        \"Tag\": \"code\",\n        \"props\": { \"className\": \"longboi\" },\n        \"content\": \"early_secret = HKDF-Extract(salt=00, key=00...)\\nempty_hash = SHA256(\\\"\\\")\\nderived_secret = HKDF-Expand-Label(key: early_secret, label: \\\"derived\\\", ctx: empty_hash, len: 32)\\nhandshake_secret = HKDF-Extract(salt: derived_secret, key: shared_secret)\\nclient_secret = HKDF-Expand-Label(key: handshake_secret, label: \\\"c hs traffic\\\", ctx: hello_hash, len: 32)\\nserver_secret = HKDF-Expand-Label(key: handshake_secret, label: \\\"s hs traffic\\\", ctx: hello_hash, len: 32)\\nclient_key = HKDF-Expand-Label(key: client_secret, label: \\\"key\\\", ctx: \\\"\\\", len: 16)\\nserver_key = HKDF-Expand-Label(key: server_secret, label: \\\"key\\\", ctx: \\\"\\\", len: 16)\\nclient_iv = HKDF-Expand-Label(key: client_secret, label: \\\"iv\\\", ctx: \\\"\\\", len: 12)\\nserver_iv = HKDF-Expand-Label(key: server_secret, label: \\\"iv\\\", ctx: \\\"\\\", len: 12)\\nclient_sn_key = HKDF-Expand-Label(key: client_secret, label: \\\"sn\\\", ctx: \\\"\\\", len: 16)\\nserver_sn_key = HKDF-Expand-Label(key: server_secret, label: \\\"sn\\\", ctx: \\\"\\\", len: 16)\"\n      }\n    ]\n  },\n  {\n    \"children\": [\n      \"在命令行中使用\",\n      {\n        \"Tag\": \"a\",\n        \"props\": { \"href\": \"https://dtls.xargs.org/files/hkdf-dtls.sh\" },\n        \"content\": \"原作者制作的 HKDF 命令行脚本\"\n      },\n      \"，你也可以自己试试：\"\n    ]\n  },\n  {\n    \"Tag\": \"CodeSample\",\n    \"props\": {\n      \"code\": \"$ hello_hash=aee8eba0d2ee87052fbbc6864c1514c5a927d6f0ffb4f7954c7f379d95f1b1d7\\n$ shared_secret=df4a291baa1eb7cfa6934b29b474baad2697e29f1f920dcc77c8a0a088447624\\n$ zero_key=0000000000000000000000000000000000000000000000000000000000000000\\n$ early_secret=$(./hkdf-dtls extract 00 $zero_key)\\n$ empty_hash=$(openssl sha256 < /dev/null | sed -e 's/.* //')\\n$ derived_secret=$(./hkdf-dtls expandlabel $early_secret \\\"derived\\\" $empty_hash 32)\\n$ handshake_secret=$(./hkdf-dtls extract $derived_secret $shared_secret)\\n$ csecret=$(./hkdf-dtls expandlabel $handshake_secret \\\"c hs traffic\\\" $hello_hash 32)\\n$ ssecret=$(./hkdf-dtls expandlabel $handshake_secret \\\"s hs traffic\\\" $hello_hash 32)\\n$ client_handshake_key=$(./hkdf-dtls expandlabel $csecret \\\"key\\\" \\\"\\\" 16)\\n$ server_handshake_key=$(./hkdf-dtls expandlabel $ssecret \\\"key\\\" \\\"\\\" 16)\\n$ client_handshake_iv=$(./hkdf-dtls expandlabel $csecret \\\"iv\\\" \\\"\\\" 12)\\n$ server_handshake_iv=$(./hkdf-dtls expandlabel $ssecret \\\"iv\\\" \\\"\\\" 12)\\n$ client_sn_key=$(./hkdf-dtls expandlabel $csecret \\\"sn\\\" \\\"\\\" 16)\\n$ server_sn_key=$(./hkdf-dtls expandlabel $ssecret \\\"sn\\\" \\\"\\\" 16)\\n$ echo client_key: $client_handshake_key\\n$ echo client_iv: $client_handshake_iv\\n$ echo server_key: $server_handshake_key\\n$ echo server_iv: $server_handshake_iv\\n$ echo client_sn_key: $client_sn_key\\n$ echo server_sn_key: $server_sn_key\\n\\nclient_key: 6caa2633d5e48f10051e69dc45549c97\\nclient_iv: 106dc6e393b7a9ea8ef29dd7\\nserver_key: 004e03e64ab6cba6b542775ec230e20a\\nserver_iv: 6d9924be044ee97c624913f2\\nclient_sn_key: beed6218676635c2cb46a45694144fec\\nserver_sn_key: 7173fac51194e775001d625ef69d7c9f\"\n    }\n  },\n  \"由此我们可以得到以下密钥以及向量：\",\n  {\n    \"Tag\": \"ul\",\n    \"children\": [\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"客户端握手时密钥: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"6caa2633d5e48f10051e69dc45549c97\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"客户端握手时向量: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"106dc6e393b7a9ea8ef29dd7\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"客户端序号保护密钥(record number key): \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"beed6218676635c2cb46a45694144fec\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"服务器端握手时密钥: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"004e03e64ab6cba6b542775ec230e20a\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"服务器端握手时向量: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"6d9924be044ee97c624913f2\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"服务器端序号保护密钥: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"7173fac51194e775001d625ef69d7c9f\"\n          }\n        ]\n      }\n    ]\n  }\n]\n"
  },
  {
    "path": "src/DTLS/serverHelloDatagram.json",
    "content": "[\n  \"服务器回复 \\\"ServerHello\\\"。服务器提供的信息包括以下内容：\",\n  {\n    \"Tag\": \"ul\",\n    \"children\": [\n      {\n        \"Tag\": \"li\",\n        \"content\": \"服务器端随机数(在随后的握手时使用)\"\n      },\n      {\n        \"Tag\": \"li\",\n        \"content\": \"服务器端选择的加解密算法\"\n      },\n      {\n        \"Tag\": \"li\",\n        \"content\": \"用于密钥交换的公钥\"\n      },\n      {\n        \"Tag\": \"li\",\n        \"content\": \"服务器协商的具体协议的版本\"\n      }\n    ]\n  },\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"DTLS 记录头\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"16 fe fd 00 00 00 00 00 00 00 00 00 62\"\n          },\n          [\n            \"每个 DTLS 记录都以一个 type、一些序列信息(seq info)和一个 len 开始。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"16 - 表示 TLS 记录类型 0x16(22, Handshake)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"fe fd - 协议版本 (DTLS 1.2, 细节见下文)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 - 密钥序列指示(key epoch，每次密钥更新时都会递增)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 00 00 00 00 - DTLS 序列号 0x0(0)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 62 - 紧接着的数组载荷长度 0x62(98) 字节\"\n                }\n              ]\n            },\n            \"DTLS 版本的编码方式是将协议版本分成几个部分，然后取每个部分的补码。(因此 \\\"1.3\\\" 变成 {1, 3}，变成字节 0xFE 0xFC)。这种补码技术使 DTLS 版本与 TLS 版本有所差别。\",\n            \"由于已经创建和部署的网络中间件(middleboxes)不允许它们所不承认的协议版本通过，因此所有 DTLS 1.3 会话在未加密的记录中都会显示为 DTLS 1.2(0xFE 0xFD)。\"\n          ]\n        ],\n        [\n          \"TLS 握手记录头\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"02 00 00 56\"\n          },\n          [\n            \"每个 TLS 握手消息都以一个 type 和一个 len 开始。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"02 - 握手消息类型 0x02 (ServerHello)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 56 - 紧接着的握手消息数据的长度 0x56 (86) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"用于重建握手顺序的信息(Handshake Reconstruction Data)\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 00 00 00 00 00 00 56\"\n          },\n          [\n            \"因为 UDP (或其他数据报协议)不保证交付或排序，而且数据报的长度可能比需要发送的握手记录长度要小。因此 DTLS 必须提供一定的信息，以支持在数据丢失、包重排序或有记录碎片的情况下，使得对等端(peer)能够重新构建一条正确的数据记录。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 - DTLS 序列号 0x0(0)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 00 - 表明记录碎片的偏移量为 0x00(0) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 56 - 表明之后的记录碎片的长度为 0x56(86) 字节\"\n                }\n              ]\n            },\n            \"在本文例子中，整个握手记录记录的长度要短于一个 UDP 数据报的可承载长度，因此偏移量为零，且长度为整个握手记录长。\"\n          ]\n        ],\n        [\n          \"服务器端 DTLS 版本号(废弃)\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"fe fd\"\n          },\n          [\n            \"DTLS 版本的编码方式是将协议版本分成几个部分，然后取每个部分的补码。(因此 \\\"1.3\\\" 变成 {1, 3}，变成字节 0xFE 0xFC)。这种补码技术使 DTLS 版本与 TLS 版本有所差别。\",\n            \"由于已经创建和部署的网络中间件(middleboxes)不允许它们所不承认的协议版本通过，因此所有 DTLS 1.3 会话在未加密的记录中都会显示为 DTLS 1.2(0xFE 0xFD)。所有的 DTLS 1.3 及以上版本的会话需要通过后面提到的\\\"支持的版本\\\"拓展协商真实版本号。\"\n          ]\n        ],\n        [\n          \"服务器端随机数\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f\"\n          },\n          [\n            \"服务器端提供的 32 字节的随机数。这个数将在之后的会话中使用。在本文的例子中，我们暂时将一个方便记忆的字符串当作随机数。\"\n          ]\n        ],\n        [\n          \"会话 ID (废弃)\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00\"\n          },\n          [\n            \"这是一个废弃(legacy)字段，不在 DTLS 1.3 中使用。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 - 0 字节的会话 ID\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"所选择的加解密算法\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"13 01\"\n          },\n          [\n            \"服务器从客户端给出的选项列表中选择的加解密算法 0x1301(TLS_AES_128_GCM_SHA256)。\"\n          ]\n        ],\n        [\n          \"压缩算法\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00\"\n          },\n          [\"服务器从客户端给出的选项列表中选择的压缩算法 0x00(null)。\"]\n        ],\n        [\n          \"扩展的长度\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 2e\"\n          },\n          [\n            \"服务器向客户端返回的扩展有序列表的长度。因为服务器被禁止回复 ClientHello 消息中不存在的扩展，因此服务器知道客户端将理解并支持列出的所有扩展。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 2e - 扩展列表的长度为 0x2E(46) 字节\"\n                }\n              ]\n            },\n            \"每个扩展将以两个字节开始，表明它是哪个扩展。然后是两个字节的内容长度字段，最后是扩展的具体内容。\"\n          ]\n        ],\n        [\n          \"扩展 - 算法公钥列表\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 33 00 24 00 1d 00 20 9f d7 ad 6d cf f4 29 8d d3 f9 6d 5b 1b 2a f9 10 a0 53 5b 14 88 d7 f8 fa bb 34 9a 98 28 80 b6 15\"\n          },\n          [\n            \"服务器使用和客户端发送公钥时相同的算法发送一个自己的公钥。一旦这个被发送，加密密钥就可以被计算出来。其余的握手则将被加密。而不用像以前的协议版本，以透明的方式发送握手记录。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 33 - 表示这是 \\\"算法公钥列表\\\" 扩展\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 24 - 算法公钥列表长度为 0x24(36) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 1d - 代表 x25519 算法(例子中为通过 curve25519 算法进行密钥交换)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 20 - 公钥长度为 0x20(32) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"9f d7 ... b6 15 - \\\"服务器端准备密钥交换\\\" 步骤中生成的公钥\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"扩展 - 支持的版本\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 2b 00 02 fe fc\"\n          },\n          [\n            \"客户端表明其支持 DTLS 1.3。由于兼容性的原因，这被放在一个扩展中，而不是上面的客户端版本字段。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 2b - 表示这是 \\\"支持的版本\\\" 扩展\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 02 - 扩展信息长度为 0x02(2) 字节 \"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"fe fc - 代表 DTLS 1.3\"\n                }\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/DTLS/serverKeyExchangeGeneration.json",
    "content": "[\n  \"服务器端也需要生成一个用于密钥交换的自己的“私钥/公钥”对。密钥交换(Key exchange)是一种技术，双方可以在同一数字上达成一致，而窃听者却无法知道这个数字是什么。\",\n  {\n    \"Tag\": \"p\",\n    \"children\": [\n      \"学习 DTLS 并不需要深入了解，但你可以从\",\n      {\n        \"Tag\": \"a\",\n        \"props\": { \"href\": \"https://cangsdarm.github.io/illustrate/x25519\" },\n        \"content\": \"X25519 密钥交换算法\"\n      },\n      \"获取涉及到的密钥交换算法的具体解释。\"\n    ]\n  },\n  [\n    \"**私钥**是 0 到 \",\n    {\n      \"Tag\": \"Math\",\n      \"content\": \"2^256-1\"\n    },\n    \" 之间的一个随机整数(32bytes, 256bits)\",\n    \"。为方便后续解释，假设我们生成的私钥是：\"\n  ],\n  {\n    \"Tag\": \"pre\",\n    \"props\": {\n      \"className\": \"ind2\"\n    },\n    \"children\": [\n      {\n        \"Tag\": \"code\",\n        \"props\": { \"className\": \"longboi\" },\n        \"content\": \"909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeaf\"\n      }\n    ]\n  },\n  \"**公钥**由上面提到的 X25519 密钥交换算法生成。例子中计算出的公钥应如下所示：\",\n  {\n    \"Tag\": \"pre\",\n    \"props\": {\n      \"className\": \"ind2\"\n    },\n    \"children\": [\n      {\n        \"Tag\": \"code\",\n        \"props\": { \"className\": \"longboi\" },\n        \"content\": \"9fd7ad6dcff4298dd3f96d5b1b2af910a0535b1488d7f8fabb349a982880b615\"\n      }\n    ]\n  },\n  \"公钥的计算也可以在命令行中运行以下命令得到：\",\n  {\n    \"Tag\": \"CodeSample\",\n    \"props\": {\n      \"code\": \"### requires openssl 1.1.0 or higher\\n$ openssl pkey -noout -text < server-ephemeral-private.key\\n\\nX25519 Private-Key:\\npriv:\\n    90:91:92:93:94:95:96:97:98:99:9a:9b:9c:9d:9e:\\n    9f:a0:a1:a2:a3:a4:a5:a6:a7:a8:a9:aa:ab:ac:ad:\\n    ae:af\\npub:\\n    9f:d7:ad:6d:cf:f4:29:8d:d3:f9:6d:5b:1b:2a:f9:\\n    10:a0:53:5b:14:88:d7:f8:fa:bb:34:9a:98:28:80:\\n    b6:15\"\n    }\n  }\n]\n"
  },
  {
    "path": "src/Datagram/index.jsx",
    "content": "import React from \"react\";\nimport classes from \"./style.module.css\";\n\nconst Datagram = ({ label = \"\", children }) => {\n  return (\n    <div className={classes[\"datagram\"]}>\n      <span className={classes[\"label\"]}>{label}</span>\n      {children}\n    </div>\n  );\n};\n\nexport default Datagram;\n"
  },
  {
    "path": "src/Datagram/style.module.css",
    "content": "/***** datagram borders *****/\n.datagram {\n  max-width: 800px;\n  border-radius: 1.2em;\n  border: 1px solid black;\n  padding: 5px 15px;\n  margin: 0.8em auto;\n}\n\n.datagram > .label {\n  font-size: 0.8em;\n  font-weight: bold;\n}\n"
  },
  {
    "path": "src/Footer/index.jsx",
    "content": "import React from \"react\";\n\nconst Footer = ({ desc, mother, references }) => {\n  return (\n    <>\n      <div>\n        <p\n          style={{ textAlign: \"center\" }}\n          dangerouslySetInnerHTML={{ __html: desc }}\n        />\n      </div>\n      <div>\n        <p style={{ textAlign: \"center\" }}>\n          原作者{\" \"}\n          <a href=\"https://twitter.com/xargsnotbombs\" target=\"_blank\">\n            (Twitter)@XargsNotBombs\n          </a>\n          ,{\" \"}\n          <a href={mother} target=\"_blank\">\n            {mother}\n          </a>\n        </p>\n      </div>\n      <div>\n        <p style={{ textAlign: \"center\" }}>\n          译者{\" \"}\n          <a href=\"https://github.com/cangSDARM\" target=\"_blank\">\n            (Github)@AllenLee\n          </a>\n          , 源代码托管在{\" \"}\n          <a href=\"https://github.com/cangSDARM/illustrate/\" target=\"_blank\">\n            https://github.com/cangSDARM/illustrate/\n          </a>\n        </p>\n      </div>\n      {references && (\n        <div>\n          <dl\n            style={{\n              display: \"flex\",\n              flexDirection: \"column\",\n              maxWidth: \"80vw\",\n              margin: \"0 auto\",\n            }}\n          >\n            <span style={{ alignSelf: \"start\" }}>参考：</span>\n            {references.map((ref) => (\n              <li key={ref.title}>\n                {ref.title}：<a href={ref.href}>{ref.href}</a>\n                {ref.quote}\n              </li>\n            ))}\n          </dl>\n        </div>\n      )}\n    </>\n  );\n};\n\nexport default Footer;\n"
  },
  {
    "path": "src/Header/index.jsx",
    "content": "import React from \"react\";\nimport clsx from \"clsx\";\nimport classes from \"./style.module.css\";\nimport { jump } from \"../utils\";\n\nconst getRouterUsingPath = (routers = [], path = \"\") =>\n  routers.find((rt) => rt.href.startsWith(path)) || routers[0];\n\nconst Header = ({ routers = [], onRouterChange, base = \"\" }) => {\n  const [pathName, setPathName] = React.useState(globalThis.location.pathname);\n\n  const handleRouterChange = React.useCallback((rt) => {\n    onRouterChange?.(rt);\n    setPathName(rt.href);\n  }, []);\n\n  React.useEffect(() => {\n    let curPath = globalThis.location.pathname;\n\n    if (routers.findIndex((rt) => curPath.startsWith(rt.href)) < 0) {\n      // try state for github-pages\n      curPath = globalThis.history.state?.path || \"\";\n    }\n\n    if (routers.findIndex((rt) => curPath.startsWith(rt.href)) < 0) {\n      curPath = routers[0];\n      jump(curPath.href);\n    }\n\n    handleRouterChange(getRouterUsingPath(routers, curPath));\n  }, []);\n\n  React.useEffect(() => {\n    const listener = (e) => {\n      const curPath = globalThis.document.location.pathname;\n      setPathName(curPath);\n      handleRouterChange(getRouterUsingPath(routers, curPath));\n    };\n\n    window?.addEventListener(\"popstate\", listener);\n\n    return () => window?.removeEventListener(\"popstate\", listener);\n  }, []);\n\n  return (\n    <div className={classes.header}>\n      {routers.map((rt) => (\n        <a\n          key={rt.href}\n          href={rt.href}\n          className={clsx(rt.href === pathName && classes[\"this-page\"])}\n          onClick={(e) => {\n            if (rt.href === pathName) return;\n\n            e.stopPropagation();\n            e.preventDefault();\n            jump(rt.href);\n            handleRouterChange(rt);\n          }}\n        >\n          {rt.label}\n        </a>\n      ))}\n    </div>\n  );\n};\n\nexport default Header;\n"
  },
  {
    "path": "src/Header/style.module.css",
    "content": ".header {\n  width: 100%;\n  margin: 0;\n  padding: 2px;\n  border-bottom: 1px solid grey;\n  background-color: bisque;\n  color: #444;\n  line-height: 20px;\n  font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n  font-size: 14px;\n  text-align: right;\n}\n.header a,\n.header a:visited,\n.header a:hover {\n  text-decoration: none;\n  color: #444;\n}\n.header a.this-page:before {\n  content: \"❧\\00a0\";\n}\n.header a,\n.header span {\n  margin-right: 0.5em;\n}\n.header a.this-page {\n  font-weight: bold;\n}\n"
  },
  {
    "path": "src/Intro/index.jsx",
    "content": "import classes from \"./style.module.css\";\nimport Balancer from \"react-wrap-balancer\";\n\nconst Intro = ({ title = \"\", subtitle = \"\", desc = \"\", intro = \"\" }) => {\n  return (\n    <section>\n      <h1>\n        <Balancer>{title}</Balancer>\n      </h1>\n      {subtitle && (\n        <h3>\n          <Balancer>{subtitle}</Balancer>\n        </h3>\n      )}\n      {desc && (\n        <h5>\n          <Balancer>{desc}</Balancer>\n        </h5>\n      )}\n      {intro && (\n        <div className={classes[\"intro-block\"]}>\n          <p>\n            <Balancer>{intro}</Balancer>\n          </p>\n        </div>\n      )}\n    </section>\n  );\n};\n\nexport default Intro;\n"
  },
  {
    "path": "src/Intro/style.module.css",
    "content": "h1:after {\n  content: \"\\00a0❧\";\n}\nh1:before {\n  content: \"❧\\00a0\";\n}\nh1,\nh3,\nh5 {\n  text-align: center;\n  padding: 10px;\n  max-width: 800px;\n  margin: 0 auto;\n}\nh5 {\n  font-style: italic;\n  font-family: serif;\n}\n\n.intro-block {\n  max-width: 600px;\n  margin: 0.5em auto 1em;\n}\n.intro-block p {\n  text-align: center;\n}\n"
  },
  {
    "path": "src/QUIC/clientAck.json",
    "content": "[\n  \"客户端再发送一个 \\\"初始\\\" 的数据包。其中包含服务器的最后一个 \\\"初始\\\" 数据包的 ACK。\",\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"数据包头字节\",\n          {\n            \"props\": {\n              \"className\": \"bytes protected\",\n              \"title\": \"被数据包头保护密钥加密\"\n            },\n            \"content\": \"cf\"\n          },\n          {\n            \"props\": {\n              \"className\": \"bytes unprotected\"\n            },\n            \"content\": \"c0\"\n          },\n          [\n            \"数据包以一个头字节开始，该字节应用了头保护。头部保护用于隐藏数据包序号和其他信息，使其不被外界观察到。\",\n            \"包头保护是通过用\\\"数据包头保护密钥\\\"对每个数据包的有效载荷的样本进行加密，然后将每个数据包中的某些比特和字节与所得数据进行异或(XOR)操作得到的。对于像这样的\\\"长\\\"格式数据包，受保护的部分是这个字节的低 4 位，以及数据包序号的字节（见下文）。\",\n            \"这里有一个关于如何计算出加密头字节的例子：\",\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### \\\"client header protection key\\\" from handshake keys calc step above\\n$ key=6df4e9d737cdf714711d7c617ee82981\\n### sample is taken from 16 bytes of payload starting\\n### 4 bytes past the first byte of the packet number\\n$ sample=ed1f7b0555cdb783fbdf5b52724b7d29\\n$ echo $sample | xxd -r -p | openssl aes-128-ecb -K $key | head -c 5 | xxd -p\\n\\n8f57c29e79\\n\\n### first byte of result is xor'd into lower 4 bits of this byte,\\n### remaining bytes are xor'd one-for-one into the bytes of\\n### the packet number (which in this packet is only one byte)\"\n              }\n            },\n            \"解密出的字节 0xC0 中的位有以下含义：\",\n            {\n              \"Tag\": \"Table\",\n              \"props\": {\n                \"headers\": [\"\", \"值\", \"含义\"],\n                \"data\": [\n                  [\"高位\", \"1\", \"Long Header 格式\"],\n                  [\"\", \"1\", \"固定位(总是被置1)\"],\n                  [\"\", \"00\", \"数据包类型：初始化\"],\n                  [\"\", \"00\", \"保留(总是被置0)\"],\n                  [\n                    \"低位\",\n                    \"00\",\n                    \"数据包序号长度(表示下面的 \\\"数据包序号\\\" 将有一个字节的长度，默认值)\"\n                  ]\n                ]\n              }\n            }\n          ]\n        ],\n        [\n          \"QUIC 版本号\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 00 00 01\"\n          },\n          [\"QUIC的版本是：0x1\"]\n        ],\n        [\n          \"目的地连接标识 ID\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"05 73 5f 63 69 64\"\n          },\n          [\n            \"服务器的标识 ID (服务器端的源连接标识 ID)\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"05 - 紧跟着的目的地连接标识 ID 的长度\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"73 5f 63 69 64 - 实际的目的地连接标识 ID(\\\"s_cid\\\")\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"源连接标识 ID\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"05 63 5f 63 69 64\"\n          },\n          [\n            \"客户端使用这个字段来表明它选择的传输给服务器的源连接标识 ID。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"05 - 紧跟着的源连接标识 ID 的长度(5bytes)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"63 5f 63 69 64 - 实际的源连接标识 ID (\\\"c_cid\\\")\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"令牌\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00\"\n          },\n          [\n            \"客户端在某些情况下可以使用这个字段来提供服务器所要求的令牌，例如证明其连接尝试不是欺骗。此时，没有令牌需要提供，该字段为空。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 - 紧跟着的令牌长度(0bytes)\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"数据包长度\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"40 17\"\n          },\n          [\n            \"客户端指明后续数据包数据的长度。这个字段是一个长度可变的整数——第一个字节的前两位表示该整数中总共有多少个字节。\",\n            \"此时，第一个字节以 \\\"0 1\\\"(0x4)这两个位开始，表示该整数共两个字节。其余的位给出数字 0x17，表示 23 个字节的有效载荷。\"\n          ]\n        ],\n        [\n          \"数据包序号\",\n          {\n            \"props\": {\n              \"className\": \"bytes protected\",\n              \"title\": \"被数据包头保护密钥加密\"\n            },\n            \"content\": \"56\"\n          },\n          {\n            \"props\": {\n              \"className\": \"bytes unprotected\"\n            },\n            \"content\": \"01\"\n          },\n          [\n            \"该字节应用了头保护。详见 \\\"数据包头字节\\\"。\",\n            \"这个字节的未受保护的值为 0x01，表明它是第 1 号包，或者说是客户端发送的第二个\\\"初始\\\"包。\",\n            \"这个数据也有可能被截断。发送端点通过几个步骤截断：①计算已发送的最高的序号和未确认的最低的序号之间的差值；②出于安全考虑将差值加倍并四舍五入；③计算它在明确表示两端之间的序号的前提下可以从序号的高位删除的字节数；④截断编码后的数据包序号直至长度满足该字节数。而接收端点根据会它最近看到的数据包号码填入完整的序号。\",\n            {\n              \"children\": [\n                \"由于我们的例子对话发送的数据包很小(少于 64 个字节)，所以这种截断不会在本文中出现。详情见 \",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://www.rfc-editor.org/rfc/rfc9000.html#section-17.1\"\n                  },\n                  \"content\": \"RFC 9000\"\n                },\n                \"。\"\n              ]\n            }\n          ]\n        ],\n        [\n          \"加密的数据载荷\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\",\n              \"title\": \"被\\\"初始密钥\\\"加密\"\n            },\n            \"content\": \"6e 1f 98 ed 1f 7b\"\n          },\n          [\"这些数据使用客户端的\\\"初始密钥\\\"进行加密。\"]\n        ],\n        [\n          \"AEAD 鉴别标签\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"05 55 cd b7 83 fb df 5b 52 72 4b 7d 29 f0 af e3\"\n          },\n          [\n            {\n              \"children\": [\n                \"这是 \",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://zhuanlan.zhihu.com/p/28566058\"\n                  },\n                  \"content\": \"AEAD 算法\"\n                },\n                \"的鉴别标签，用以确认加密数据和数据包头的完整性。它由加密算法产生，并由解密算法消耗。\"\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"data\": [\n        [\n          \"\",\n          {\n            \"props\": {\n              \"className\": \"decryption-header\"\n            },\n            \"content\": \"解密后的数据载荷\"\n          },\n          [\n            { \"Tag\": \"h4\", \"content\": \"解密\" },\n            \"数据被 \\\"客户端初始密钥计算\\\" 步骤中产生的初始密钥和初始向量(IVs)加密。IVs 通过对密钥和数据包序号进行异或操作生成。在例子中 IV 为 1。\",\n            \"数据包开头的 21 字节(数据包头)还会作为解密过程解密成功时必须满足的认证条件。\",\n            {\n              \"children\": [\n                \"openssl 命令行工具还不支持 AEAD 算法加解密(AEAD ciphers)，你可以使用作者的命令行工具来\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://quic.xargs.org/files/aes_128_gcm_decrypt.c\"\n                  },\n                  \"content\": \"解密\"\n                },\n                \"和\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://quic.xargs.org/files/aes_128_gcm_encrypt.c\"\n                  },\n                  \"content\": \"加密\"\n                },\n                \"这些数据。\"\n              ]\n            },\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### from the \\\"Initial Keys Calc\\\" step\\n$ key=b14b918124fda5c8d79847602fa3520b\\n$ iv=ddbc15dea80925a55686a7df\\n### from this record\\n$ recdata=c00000000105735f63696405635f63696400401701\\n$ authtag=0555cdb783fbdf5b52724b7d29f0afe3\\n$ recordnum=1\\n### may need to add -I and -L flags for include and lib dirs\\n$ cc -o aes_128_gcm_decrypt aes_128_gcm_decrypt.c -lssl -lcrypto\\n$ cat /tmp/msg1   | ./aes_128_gcm_decrypt $iv $recordnum $key $recdata $authtag   | hexdump -C\\n\\n00000000  02 00 40 81 00 00                                 |..@...|\"\n              }\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"ACK 帧\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"02 00 40 81 00 00\"\n          },\n          [\n            \"服务器确认收到客户端的初始数据包0。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"02 - 表明帧类型 ACK\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 - largest_acknowledged: 被确认的最大数据包\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"children\": [\n                    \"40 81 - ack_delay: 变长的整数。给出这个 ack 被延迟发送的时间，单位是微秒。\",\n                    \"通过计算式子：2\",\n                    {\n                      \"Tag\": \"sup\",\n                      \"content\": \"ack_delay_exponent\"\n                    },\n                    \" 得到。其中 ack_delay_exponent = 129 * 8 = 1,032 (µseconds)。\"\n                  ]\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 - ack_range_count: 额外的 ACK 帧数据长度 0x(0)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 - first_ack_range: 可变长度的整数。给出在 largest_acknowledged 之前被确认过的数据包数量。\"\n                }\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/QUIC/clientApplicationKeysCalc.json",
    "content": "[\n  \"客户端现在也可以计算应用会话时的密钥了。如果计算正确，结果应和服务器端的一致：\",\n  {\n    \"Tag\": \"ul\",\n    \"children\": [\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"客户端会话密钥: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"e010a295f0c2864f186b2a7e8fdc9ed7\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"客户端会话向量: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"eb3fbc384a3199dcf6b4c808\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"客户端会话数据包头保护密钥: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"8a6a38bc5cc40cb482a254dac68c9d2f\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"服务器端会话密钥: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"fd8c7da9de1b2da4d2ef9fd5188922d0\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"服务器端会话向量: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"02f6180e4f4aa456d7e8a602\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"服务器端会话数据包头保护密钥: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"b7f6f021453e52b58940e4bba72a35d4\"\n          }\n        ]\n      }\n    ]\n  }\n]\n"
  },
  {
    "path": "src/QUIC/clientApplicationPacket1.json",
    "content": "[\n  \"客户端发送其第一个握手后的数据包，即第一个实际包含应用内容的会话过程数据包。其中包含内容为 \\\"ping\\\" 的流数据。\",\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"数据包头字节\",\n          {\n            \"props\": {\n              \"className\": \"bytes protected\",\n              \"title\": \"被数据包头保护密钥加密\"\n            },\n            \"content\": \"4e\"\n          },\n          {\n            \"props\": {\n              \"className\": \"bytes unprotected\"\n            },\n            \"content\": \"40\"\n          },\n          [\n            \"数据包以一个头字节开始，该字节应用了头保护。头部保护用于隐藏数据包序号和其他信息，使其不被外界观察到。\",\n            \"包头保护是通过用\\\"数据包头保护密钥\\\"对每个数据包的有效载荷的样本进行加密，然后将每个数据包中的某些比特和字节与所得数据进行异或(XOR)操作得到的。对于像这样的\\\"长\\\"格式数据包，受保护的部分是这个字节的低 4 位，以及数据包序号的字节（见下文）。\",\n            \"这里有一个关于如何计算出加密头字节的例子：\",\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### \\\"client header protection key\\\" from application keys calc step above\\n$ key=8a6a38bc5cc40cb482a254dac68c9d2f\\n### sample is taken from 16 bytes of payload starting\\n### 4 bytes past the first byte of the packet number\\n$ sample=e66e8ee950ba8b8ed10cba39a06ab7b0\\n$ echo $sample | xxd -r -p | openssl aes-128-ecb -K $key | head -c 5 | xxd -p\\n\\n4e1e62a65d\\n\\n### first byte of result is xor'd into lower 5 bits of this byte,\\n### remaining bytes are xor'd one-for-one into the bytes of\\n### the packet number (which in this packet is only one byte)\"\n              }\n            },\n            \"解密出的字节 0x40 中的位有以下含义：\",\n            {\n              \"Tag\": \"Table\",\n              \"props\": {\n                \"headers\": [\"\", \"值\", \"含义\"],\n                \"data\": [\n                  [\"高位\", \"0\", \"Short Header 格式\"],\n                  [\"\", \"1\", \"固定位(总是被置1)\"],\n                  [\n                    \"\",\n                    \"0\",\n                    \"可选的 \\\"Spin\\\" 位。用于允许观察者测量 RTT，但 QUIC 未使用。\"\n                  ],\n                  [\"\", \"00\", \"保留(总是被置0)\"],\n                  [\"\", \"0\", \"密钥相位位(key phase bit), 密钥发生轮替时置1\"],\n                  [\n                    \"低位\",\n                    \"00\",\n                    \"数据包序号长度(表示下面的 \\\"数据包序号\\\" 将有一个字节的长度，默认值)\"\n                  ]\n                ]\n              }\n            }\n          ]\n        ],\n        [\n          \"目的地连接标识 ID\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"73 5f 63 69 64\"\n          },\n          [\n            \"服务器端的标识 ID (服务器端的源连接标识 ID)\",\n            \"注意此时标识 ID 的长度(以及本应在后面的源连接标识 ID)已经省略。对等端(peer)在这之前应该且必须知道标识 ID 长度，该长度在会话过程中要么一直被省略，要么在标识 ID 中编码长度。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"73 5f 63 69 64 - 实际的目的地连接标识 ID(\\\"s_cid\\\")\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"数据包序号\",\n          {\n            \"props\": {\n              \"className\": \"bytes protected\",\n              \"title\": \"被数据包头保护密钥加密\"\n            },\n            \"content\": \"1e\"\n          },\n          {\n            \"props\": {\n              \"className\": \"bytes unprotected\"\n            },\n            \"content\": \"00\"\n          },\n          [\n            \"该字节应用了头保护。详见 \\\"数据包头字节\\\"。\",\n            \"这个字节的未受保护的值为 0x00，表明它是第 0 号包，或者说是客户端发送的第一个\\\"会话\\\"包。\",\n            \"这个数据也有可能被截断。发送端点通过几个步骤截断：①计算已发送的最高的序号和未确认的最低的序号之间的差值；②出于安全考虑将差值加倍并四舍五入；③计算它在明确表示两端之间的序号的前提下可以从序号的高位删除的字节数；④截断编码后的数据包序号直至长度满足该字节数。而接收端点根据会它最近看到的数据包号码填入完整的序号。\",\n            {\n              \"children\": [\n                \"由于我们的例子对话发送的数据包很小(少于 64 个字节)，所以这种截断不会在本文中出现。详情见 \",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://www.rfc-editor.org/rfc/rfc9000.html#section-17.1\"\n                  },\n                  \"content\": \"RFC 9000\"\n                },\n                \"。\"\n              ]\n            }\n          ]\n        ],\n        [\n          \"加密的数据载荷\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\",\n              \"title\": \"被\\\"会话密钥\\\"加密\"\n            },\n            \"content\": \"cc 91 70 e6 6e 8e e9 50 ba\"\n          },\n          [\"这些数据使用客户端的\\\"会话密钥\\\"进行加密。\"]\n        ],\n        [\n          \"AEAD 鉴别标签\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"8b 8e d1 0c ba 39 a0 6a b7 b0 67 0a 50 ef 68 e6\"\n          },\n          [\n            {\n              \"children\": [\n                \"这是 \",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://zhuanlan.zhihu.com/p/28566058\"\n                  },\n                  \"content\": \"AEAD 算法\"\n                },\n                \"的鉴别标签，用以确认加密数据和数据包头的完整性。它由加密算法产生，并由解密算法消耗。\"\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"data\": [\n        [\n          \"\",\n          {\n            \"props\": {\n              \"className\": \"decryption-header\"\n            },\n            \"content\": \"解密后的数据载荷\"\n          },\n          [\n            { \"Tag\": \"h4\", \"content\": \"解密\" },\n            \"数据被 \\\"客户端生成会话密钥\\\" 步骤中产生的会话密钥和会话向量(IVs)加密。IVs 通过对密钥和数据包序号进行异或操作生成。在例子中 IV 为 0。\",\n            \"数据包开头的 16 字节(数据包头)还会作为解密过程解密成功时必须满足的认证条件。\",\n            {\n              \"children\": [\n                \"openssl 命令行工具还不支持 AEAD 算法加解密(AEAD ciphers)，你可以使用作者的命令行工具来\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://quic.xargs.org/files/aes_128_gcm_decrypt.c\"\n                  },\n                  \"content\": \"解密\"\n                },\n                \"和\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://quic.xargs.org/files/aes_128_gcm_encrypt.c\"\n                  },\n                  \"content\": \"加密\"\n                },\n                \"这些数据。\"\n              ]\n            },\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### from the \\\"Application Keys Calc\\\" step\\n$ key=e010a295f0c2864f186b2a7e8fdc9ed7\\n$ iv=eb3fbc384a3199dcf6b4c808\\n### from this record\\n$ recdata=40735f63696400\\n$ authtag=8b8ed10cba39a06ab7b0670a50ef68e6\\n$ recordnum=0\\n### may need to add -I and -L flags for include and lib dirs\\n$ cc -o aes_128_gcm_decrypt aes_128_gcm_decrypt.c -lssl -lcrypto\\n$ cat /tmp/msg1   | ./aes_128_gcm_decrypt $iv $recordnum $key $recdata $authtag   | hexdump -C\\n\\n00000000  0f 00 00 40 04 70 69 6e  67                       |...@.ping|\"\n              }\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"流数据帧标识\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"0f\"\n          },\n          [\n            \"客户端用此表明它发送的是数据流。流是 QUIC 连接中发送所有应用数据的机制，类似于单个 TCP 连接。\",\n            \"数据流的帧类型标识是一个范围在 0x8 到 0xf 的数字(二进制表示应该是：0b00001xxx)，外加额外的可变位提供关于流的额外信息：\",\n            {\n              \"Tag\": \"Table\",\n              \"props\": {\n                \"headers\": [\"位掩码\", \"含义\"],\n                \"data\": [\n                  [\"0x4\", \"OFF: 该帧中存在一个\\\"偏移\\\"字段(否则偏移量为0)\"],\n                  [\n                    \"0x2\",\n                    \"LEN：该帧中存在一个\\\"长度\\\"字段(否则对等端应消耗帧中的所有数据)。\"\n                  ],\n                  [\n                    \"0x1\",\n                    \"FIN：该帧包含该数据流的最终数据，发送方已经完成了对它的写入。\"\n                  ]\n                ]\n              }\n            },\n            \"在本文例子中，发送方表示所有三个含义：将有一个偏移字段、一个长度字段，以及此帧包含该条数据流的最终数据。\"\n          ]\n        ],\n        [\n          \"流的 ID 序号\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"00\"\n          },\n          [\n            \"客户端给出流的 ID 序号。ID 序号按顺序增加，且最后两个比特表示流的类型和方向：\",\n            {\n              \"Tag\": \"Table\",\n              \"props\": {\n                \"headers\": [\"位掩码\", \"含义\"],\n                \"data\": [\n                  [\"0x2\", \"表示流是双向的(0)还是单向的(1)。\"],\n                  [\n                    \"0x1\",\n                    \"表示流是由客户端(0)还是服务器端(1)打开的。\"\n                  ]\n                ]\n              }\n            },\n            \"在本文例子中，表示流的 ID 序号为 0，且是客户端打开的双向数据流。\"\n          ]\n        ],\n        [\n          \"流的偏移\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"00\"\n          },\n          [\n            \"一个可变长度的整数，表示流数据的偏移量。\",\n            \"在本文例子中，一个单字节的整数显示偏移量为 0。\"\n          ]\n        ],\n        [\n          \"流的长度\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"40 04\"\n          },\n          [\n            \"一个可变长度的整数，表示流数据的长度。\",\n            \"在本文例子中，前两个比特(0 1)表示一个两字节的整数，其余比特表明流的长度为 4 字节。\"\n          ]\n        ],\n        [\n          \"数据\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"70 69 6e 67\"\n          },\n          [\n            \"客户端发送的数据，字符串\\\"ping\\\"\"\n          ]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/QUIC/clientApplicationPacket2.json",
    "content": "[\n  \"客户端接收后，需要发送一个用于告知服务器已收到数据的 \\\"ACK\\\" 数据包。\",\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"数据包头字节\",\n          {\n            \"props\": {\n              \"className\": \"bytes protected\",\n              \"title\": \"被数据包头保护密钥加密\"\n            },\n            \"content\": \"5a\"\n          },\n          {\n            \"props\": {\n              \"className\": \"bytes unprotected\"\n            },\n            \"content\": \"40\"\n          },\n          [\n            \"数据包以一个头字节开始，该字节应用了头保护。头部保护用于隐藏数据包序号和其他信息，使其不被外界观察到。\",\n            \"包头保护是通过用\\\"数据包头保护密钥\\\"对每个数据包的有效载荷的样本进行加密，然后将每个数据包中的某些比特和字节与所得数据进行异或(XOR)操作得到的。对于像这样的\\\"长\\\"格式数据包，受保护的部分是这个字节的低 4 位，以及数据包序号的字节（见下文）。\",\n            \"这里有一个关于如何计算出加密头字节的例子：\",\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### \\\"client header protection key\\\" from application keys calc step above\\n$ key=8a6a38bc5cc40cb482a254dac68c9d2f\\n### sample is taken from 16 bytes of payload starting\\n### 4 bytes past the first byte of the packet number\\n$ sample=90588b44b10d7cd32b03e34502802f25\\n$ echo $sample | xxd -r -p | openssl aes-128-ecb -K $key | head -c 5 | xxd -p\\n\\n1ac9ce3a7a0\\n\\n### first byte of result is xor'd into lower 5 bits of this byte,\\n### remaining bytes are xor'd one-for-one into the bytes of\\n### the packet number (which in this packet is only one byte)\"\n              }\n            },\n            \"解密出的字节 0x40 中的位有以下含义：\",\n            {\n              \"Tag\": \"Table\",\n              \"props\": {\n                \"headers\": [\"\", \"值\", \"含义\"],\n                \"data\": [\n                  [\"高位\", \"0\", \"Short Header 格式\"],\n                  [\"\", \"1\", \"固定位(总是被置1)\"],\n                  [\n                    \"\",\n                    \"0\",\n                    \"可选的 \\\"Spin\\\" 位。用于允许观察者测量 RTT，但 QUIC 未使用。\"\n                  ],\n                  [\"\", \"00\", \"保留(总是被置0)\"],\n                  [\"\", \"0\", \"密钥相位位(key phase bit), 密钥发生轮替时置1\"],\n                  [\n                    \"低位\",\n                    \"00\",\n                    \"数据包序号长度(表示下面的 \\\"数据包序号\\\" 将有一个字节的长度，默认值)\"\n                  ]\n                ]\n              }\n            }\n          ]\n        ],\n        [\n          \"目的地连接标识 ID\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"73 5f 63 69 64\"\n          },\n          [\n            \"服务器端的标识 ID (服务器端的源连接标识 ID)\",\n            \"注意此时标识 ID 的长度(以及本应在后面的源连接标识 ID)已经省略。对等端(peer)在这之前应该且必须知道标识 ID 长度，该长度在会话过程中要么一直被省略，要么在标识 ID 中编码长度。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"73 5f 63 69 64 - 实际的目的地连接标识 ID(\\\"s_cid\\\")\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"数据包序号\",\n          {\n            \"props\": {\n              \"className\": \"bytes protected\",\n              \"title\": \"被数据包头保护密钥加密\"\n            },\n            \"content\": \"c8\"\n          },\n          {\n            \"props\": {\n              \"className\": \"bytes unprotected\"\n            },\n            \"content\": \"01\"\n          },\n          [\n            \"该字节应用了头保护。详见 \\\"数据包头字节\\\"。\",\n            \"这个字节的未受保护的值为 0x01，表明它是第 1 号包，或者说是客户端发送的第二个\\\"会话\\\"包。\",\n            \"这个数据也有可能被截断。发送端点通过几个步骤截断：①计算已发送的最高的序号和未确认的最低的序号之间的差值；②出于安全考虑将差值加倍并四舍五入；③计算它在明确表示两端之间的序号的前提下可以从序号的高位删除的字节数；④截断编码后的数据包序号直至长度满足该字节数。而接收端点根据会它最近看到的数据包号码填入完整的序号。\",\n            {\n              \"children\": [\n                \"由于我们的例子对话发送的数据包很小(少于 64 个字节)，所以这种截断不会在本文中出现。详情见 \",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://www.rfc-editor.org/rfc/rfc9000.html#section-17.1\"\n                  },\n                  \"content\": \"RFC 9000\"\n                },\n                \"。\"\n              ]\n            }\n          ]\n        ],\n        [\n          \"加密的数据载荷\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\",\n              \"title\": \"被\\\"会话密钥\\\"加密\"\n            },\n            \"content\": \"67 e0 b4 90 58\"\n          },\n          [\"这些数据使用客户端的\\\"会话密钥\\\"进行加密。\"]\n        ],\n        [\n          \"AEAD 鉴别标签\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"8b 44 b1 0d 7c d3 2b 03 e3 45 02 80 2f 25 a1 93\"\n          },\n          [\n            {\n              \"children\": [\n                \"这是 \",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://zhuanlan.zhihu.com/p/28566058\"\n                  },\n                  \"content\": \"AEAD 算法\"\n                },\n                \"的鉴别标签，用以确认加密数据和数据包头的完整性。它由加密算法产生，并由解密算法消耗。\"\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"data\": [\n        [\n          \"\",\n          {\n            \"props\": {\n              \"className\": \"decryption-header\"\n            },\n            \"content\": \"解密后的数据载荷\"\n          },\n          [\n            { \"Tag\": \"h4\", \"content\": \"解密\" },\n            \"数据被 \\\"客户端生成会话密钥\\\" 步骤中产生的会话密钥和会话向量(IVs)加密。IVs 通过对密钥和数据包序号进行异或操作生成。在例子中 IV 为 0。\",\n            \"数据包开头的 16 字节(数据包头)还会作为解密过程解密成功时必须满足的认证条件。\",\n            {\n              \"children\": [\n                \"openssl 命令行工具还不支持 AEAD 算法加解密(AEAD ciphers)，你可以使用作者的命令行工具来\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://quic.xargs.org/files/aes_128_gcm_decrypt.c\"\n                  },\n                  \"content\": \"解密\"\n                },\n                \"和\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://quic.xargs.org/files/aes_128_gcm_encrypt.c\"\n                  },\n                  \"content\": \"加密\"\n                },\n                \"这些数据。\"\n              ]\n            },\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### from the \\\"Application Keys Calc\\\" step\\n$ key=e010a295f0c2864f186b2a7e8fdc9ed7\\n$ iv=eb3fbc384a3199dcf6b4c808\\n### from this record\\n$ recdata=40735f63696401\\n$ authtag=8b44b10d7cd32b03e34502802f25a193\\n$ recordnum=1\\n### may need to add -I and -L flags for include and lib dirs\\n$ cc -o aes_128_gcm_decrypt aes_128_gcm_decrypt.c -lssl -lcrypto\\n$ cat /tmp/msg1   | ./aes_128_gcm_decrypt $iv $recordnum $key $recdata $authtag   | hexdump -C\\n\\n00000000  02 00 0b 00 00                                    |.....|\"\n              }\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"ACK 帧\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"02 00 0b 00 00\"\n          },\n          [\n            \"客户端确认收到服务器端的会话数据包0。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"02 - 表明帧类型 ACK\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 - largest_acknowledged: 被确认的最大数据包\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"children\": [\n                    \"0b - ack_delay: 变长的整数。给出这个 ack 被延迟发送的时间，单位是微秒。\",\n                    \"通过计算式子：2\",\n                    {\n                      \"Tag\": \"sup\",\n                      \"content\": \"ack_delay_exponent\"\n                    },\n                    \" 得到。其中 ack_delay_exponent = 11 * 8 = 88 (µseconds)。\"\n                  ]\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 - ack_range_count: 额外的 ACK 帧数据长度 0x(0)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 - first_ack_range: 可变长度的整数。给出在 largest_acknowledged 之前被确认过的数据包数量。\"\n                }\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/QUIC/clientHandshake1.json",
    "content": "[\n  \"紧跟着，客户端需要发送一个用于 \\\"握手\\\" 的数据包。这个数据包包含对服务器端 \\\"握手\\\" 数据包的 ACK。\",\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"数据包头字节\",\n          {\n            \"props\": {\n              \"className\": \"bytes protected\",\n              \"title\": \"被数据包头保护密钥加密\"\n            },\n            \"content\": \"ee\"\n          },\n          {\n            \"props\": {\n              \"className\": \"bytes unprotected\"\n            },\n            \"content\": \"e0\"\n          },\n          [\n            \"数据包以一个头字节开始，该字节应用了头保护。头部保护用于隐藏数据包序号和其他信息，使其不被外界观察到。\",\n            \"包头保护是通过用\\\"数据包头保护密钥\\\"对每个数据包的有效载荷的样本进行加密，然后将每个数据包中的某些比特和字节与所得数据进行异或(XOR)操作得到的。对于像这样的\\\"长\\\"格式数据包，受保护的部分是这个字节的低 4 位，以及数据包序号的字节（见下文）。\",\n            \"这里有一个关于如何计算出加密头字节的例子：\",\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### \\\"client header protection key\\\" from handshake keys calc step above\\n$ key=84b3c21cacaf9f54c885e9a506459079\\n### sample is taken from 16 bytes of payload starting\\n### 4 bytes past the first byte of the packet number\\n$ sample=c6cc12512d7eda141ec057b804d30feb\\n$ echo $sample | xxd -r -p | openssl aes-128-ecb -K $key | head -c 5 | xxd -p\\n\\n5e8c3ee850\\n\\n### first byte of result is xor'd into lower 4 bits of this byte,\\n### remaining bytes are xor'd one-for-one into the bytes of\\n### the packet number (which in this packet is only one byte)\"\n              }\n            },\n            \"解密出的字节 0xE0 中的位有以下含义：\",\n            {\n              \"Tag\": \"Table\",\n              \"props\": {\n                \"headers\": [\"\", \"值\", \"含义\"],\n                \"data\": [\n                  [\"高位\", \"1\", \"Long Header 格式\"],\n                  [\"\", \"1\", \"固定位(总是被置1)\"],\n                  [\"\", \"10\", \"数据包类型：握手\"],\n                  [\"\", \"00\", \"保留(总是被置0)\"],\n                  [\n                    \"低位\",\n                    \"00\",\n                    \"数据包序号长度(表示下面的 \\\"数据包序号\\\" 将有一个字节的长度，默认值)\"\n                  ]\n                ]\n              }\n            }\n          ]\n        ],\n        [\n          \"QUIC 版本号\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 00 00 01\"\n          },\n          [\"QUIC的版本是：0x1\"]\n        ],\n        [\n          \"目的地连接标识 ID\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"05 73 5f 63 69 64\"\n          },\n          [\n            \"服务器的标识 ID (服务器端的源连接标识 ID)\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"05 - 紧跟着的目的地连接标识 ID 的长度\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"73 5f 63 69 64 - 实际的目的地连接标识 ID(\\\"s_cid\\\")\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"源连接标识 ID\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"05 63 5f 63 69 64\"\n          },\n          [\n            \"客户端使用这个字段来表明它选择的传输给服务器的源连接标识 ID。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"05 - 紧跟着的源连接标识 ID 的长度(5bytes)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"63 5f 63 69 64 - 实际的源连接标识 ID (\\\"c_cid\\\")\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"数据包长度\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"40 16\"\n          },\n          [\n            \"客户端指明后续数据包数据的长度。这个字段是一个长度可变的整数——第一个字节的前两位表示该整数中总共有多少个字节。\",\n            \"此时，第一个字节以 \\\"0 1\\\"(0x4)这两个位开始，表示该整数共两个字节。其余的位给出数字 0x16，表示 22 个字节的有效载荷。\"\n          ]\n        ],\n        [\n          \"数据包序号\",\n          {\n            \"props\": {\n              \"className\": \"bytes protected\",\n              \"title\": \"被数据包头保护密钥加密\"\n            },\n            \"content\": \"8c\"\n          },\n          {\n            \"props\": {\n              \"className\": \"bytes unprotected\"\n            },\n            \"content\": \"00\"\n          },\n          [\n            \"该字节应用了头保护。详见 \\\"数据包头字节\\\"。\",\n            \"这个字节的未受保护的值为 0x00，表明它是第 0 号包，或者说是客户端发送的第一个\\\"握手\\\"包。\",\n            \"这个数据也有可能被截断。发送端点通过几个步骤截断：①计算已发送的最高的序号和未确认的最低的序号之间的差值；②出于安全考虑将差值加倍并四舍五入；③计算它在明确表示两端之间的序号的前提下可以从序号的高位删除的字节数；④截断编码后的数据包序号直至长度满足该字节数。而接收端点根据会它最近看到的数据包号码填入完整的序号。\",\n            {\n              \"children\": [\n                \"由于我们的例子对话发送的数据包很小(少于 64 个字节)，所以这种截断不会在本文中出现。详情见 \",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://www.rfc-editor.org/rfc/rfc9000.html#section-17.1\"\n                  },\n                  \"content\": \"RFC 9000\"\n                },\n                \"。\"\n              ]\n            }\n          ]\n        ],\n        [\n          \"加密的数据载荷\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\",\n              \"title\": \"被\\\"握手时密钥\\\"加密\"\n            },\n            \"content\": \"b1 95 1f c6 cc\"\n          },\n          [\"这些数据使用客户端的\\\"握手时密钥\\\"进行加密。\"]\n        ],\n        [\n          \"AEAD 鉴别标签\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"12 51 2d 7e da 14 1e c0 57 b8 04 d3 0f eb 51 5b\"\n          },\n          [\n            {\n              \"children\": [\n                \"这是 \",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://zhuanlan.zhihu.com/p/28566058\"\n                  },\n                  \"content\": \"AEAD 算法\"\n                },\n                \"的鉴别标签，用以确认加密数据和数据包头的完整性。它由加密算法产生，并由解密算法消耗。\"\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"data\": [\n        [\n          \"\",\n          {\n            \"props\": {\n              \"className\": \"decryption-header\"\n            },\n            \"content\": \"解密后的数据载荷\"\n          },\n          [\n            { \"Tag\": \"h4\", \"content\": \"解密\" },\n            \"数据被 \\\"客户端握手时密钥计算\\\" 步骤中产生的初始密钥和初始向量(IVs)加密。IVs 通过对密钥和数据包序号进行异或操作生成。在例子中 IV 为 0。\",\n            \"数据包开头的 20 字节(数据包头)还会作为解密过程解密成功时必须满足的认证条件。\",\n            {\n              \"children\": [\n                \"openssl 命令行工具还不支持 AEAD 算法加解密(AEAD ciphers)，你可以使用作者的命令行工具来\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://quic.xargs.org/files/aes_128_gcm_decrypt.c\"\n                  },\n                  \"content\": \"解密\"\n                },\n                \"和\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://quic.xargs.org/files/aes_128_gcm_encrypt.c\"\n                  },\n                  \"content\": \"加密\"\n                },\n                \"这些数据。\"\n              ]\n            },\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### from the \\\"Handshake Keys Calc\\\" step\\n$ key=30a7e816f6a1e1b3434cf39cf4b415e7\\n$ iv=11e70a5d1361795d2bb04465\\n### from this record\\n$ recdata=e00000000105735f63696405635f636964401600\\n$ authtag=12512d7eda141ec057b804d30feb515b\\n$ recordnum=0\\n### may need to add -I and -L flags for include and lib dirs\\n$ cc -o aes_128_gcm_decrypt aes_128_gcm_decrypt.c -lssl -lcrypto\\n$ cat /tmp/msg1   | ./aes_128_gcm_decrypt $iv $recordnum $key $recdata $authtag   | hexdump -C\\n\\n00000000  02 00 20 00 00                                    |.. ..|\"\n              }\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"ACK 帧\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"02 00 20 00 00\"\n          },\n          [\n            \"客户端确认收到服务器端的握手数据包0。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"02 - 表明帧类型 ACK\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 - largest_acknowledged: 被确认的最大数据包\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"children\": [\n                    \"20 - ack_delay: 变长的整数。给出这个 ack 被延迟发送的时间，单位是微秒。\",\n                    \"通过计算式子：2\",\n                    {\n                      \"Tag\": \"sup\",\n                      \"content\": \"ack_delay_exponent\"\n                    },\n                    \" 得到。其中 ack_delay_exponent = 32 * 8 = 256 (µseconds)。\"\n                  ]\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 - ack_range_count: 额外的 ACK 帧数据长度 0x(0)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 - first_ack_range: 可变长度的整数。给出在 largest_acknowledged 之前被确认过的数据包数量。\"\n                }\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/QUIC/clientHandshake2.json",
    "content": "[\n  \"紧跟着，客户端需要再发送一个用于 \\\"握手\\\" 的数据包，包含了 \\\"握手完成\\\"的 TLS 记录，用于完成 TLS 1.3 加密会话的握手过程。\",\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"数据包头字节\",\n          {\n            \"props\": {\n              \"className\": \"bytes protected\",\n              \"title\": \"被数据包头保护密钥加密\",\n              \"key\": \"pk\"\n            },\n            \"content\": \"e0\"\n          },\n          {\n            \"props\": {\n              \"className\": \"bytes unprotected\"\n            },\n            \"content\": \"e0\"\n          },\n          [\n            \"数据包以一个头字节开始，该字节应用了头保护。头部保护用于隐藏数据包序号和其他信息，使其不被外界观察到。\",\n            \"包头保护是通过用\\\"数据包头保护密钥\\\"对每个数据包的有效载荷的样本进行加密，然后将每个数据包中的某些比特和字节与所得数据进行异或(XOR)操作得到的。对于像这样的\\\"长\\\"格式数据包，受保护的部分是这个字节的低 4 位，以及数据包序号的字节（见下文）。\",\n            \"这里有一个关于如何计算出加密头字节的例子：\",\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### \\\"client header protection key\\\" from handshake keys calc step above\\n$ key=84b3c21cacaf9f54c885e9a506459079\\n### sample is taken from 16 bytes of payload starting\\n### 4 bytes past the first byte of the packet number\\n$ sample=9da7e61daa07732aa10b5fbd11a00a62\\n$ echo $sample | xxd -r -p | openssl aes-128-ecb -K $key | head -c 5 | xxd -p\\n\\nb0b3b06690\\n\\n### first byte of result is xor'd into lower 4 bits of this byte,\\n### remaining bytes are xor'd one-for-one into the bytes of\\n### the packet number (which in this packet is only one byte)\"\n              }\n            },\n            \"解密出的字节 0xE0 中的位有以下含义：\",\n            {\n              \"Tag\": \"Table\",\n              \"props\": {\n                \"headers\": [\"\", \"值\", \"含义\"],\n                \"data\": [\n                  [\"高位\", \"1\", \"Long Header 格式\"],\n                  [\"\", \"1\", \"固定位(总是被置1)\"],\n                  [\"\", \"10\", \"数据包类型：握手\"],\n                  [\"\", \"00\", \"保留(总是被置0)\"],\n                  [\n                    \"低位\",\n                    \"00\",\n                    \"数据包序号长度(表示下面的 \\\"数据包序号\\\" 将有一个字节的长度，默认值)\"\n                  ]\n                ]\n              }\n            }\n          ]\n        ],\n        [\n          \"QUIC 版本号\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 00 00 01\"\n          },\n          [\"QUIC的版本是：0x1\"]\n        ],\n        [\n          \"目的地连接标识 ID\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"05 73 5f 63 69 64\"\n          },\n          [\n            \"服务器的标识 ID (服务器端的源连接标识 ID)\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"05 - 紧跟着的目的地连接标识 ID 的长度\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"73 5f 63 69 64 - 实际的目的地连接标识 ID(\\\"s_cid\\\")\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"源连接标识 ID\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"05 63 5f 63 69 64\"\n          },\n          [\n            \"客户端使用这个字段来表明它选择的传输给服务器的源连接标识 ID。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"05 - 紧跟着的源连接标识 ID 的长度(5bytes)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"63 5f 63 69 64 - 实际的源连接标识 ID (\\\"c_cid\\\")\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"数据包长度\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"40 3f\"\n          },\n          [\n            \"客户端指明后续数据包数据的长度。这个字段是一个长度可变的整数——第一个字节的前两位表示该整数中总共有多少个字节。\",\n            \"此时，第一个字节以 \\\"0 1\\\"(0x4)这两个位开始，表示该整数共两个字节。其余的位给出数字 0x3f，表示 63 个字节的有效载荷。\"\n          ]\n        ],\n        [\n          \"数据包序号\",\n          {\n            \"props\": {\n              \"className\": \"bytes protected\",\n              \"title\": \"被数据包头保护密钥加密\"\n            },\n            \"content\": \"b2\"\n          },\n          {\n            \"props\": {\n              \"className\": \"bytes unprotected\"\n            },\n            \"content\": \"01\"\n          },\n          [\n            \"该字节应用了头保护。详见 \\\"数据包头字节\\\"。\",\n            \"这个字节的未受保护的值为 0x01，表明它是第 1 号包，或者说是客户端发送的第二个\\\"握手\\\"包。\",\n            \"这个数据也有可能被截断。发送端点通过几个步骤截断：①计算已发送的最高的序号和未确认的最低的序号之间的差值；②出于安全考虑将差值加倍并四舍五入；③计算它在明确表示两端之间的序号的前提下可以从序号的高位删除的字节数；④截断编码后的数据包序号直至长度满足该字节数。而接收端点根据会它最近看到的数据包号码填入完整的序号。\",\n            {\n              \"children\": [\n                \"由于我们的例子对话发送的数据包很小(少于 64 个字节)，所以这种截断不会在本文中出现。详情见 \",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://www.rfc-editor.org/rfc/rfc9000.html#section-17.1\"\n                  },\n                  \"content\": \"RFC 9000\"\n                },\n                \"。\"\n              ]\n            }\n          ]\n        ],\n        [\n          \"加密的数据载荷\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\",\n              \"title\": \"被\\\"握手时密钥\\\"加密\"\n            },\n            \"content\": \"5e 1e 45 9d a7 e6 1d aa 07 73 2a a1 0b 5f bd 11 a0 0a 62 0b f5 e1 27 e3 7b 81 bb 10 f1 1c 31 2e 7f 9c 04 a4 3c d5 30 f3 d9 81 d5 02 3a bd\"\n          },\n          [\"这些数据使用客户端的\\\"握手时密钥\\\"进行加密。\"]\n        ],\n        [\n          \"AEAD 鉴别标签\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"5e 98 f2 2d c6 f2 59 79 91 9b ad 30 2f 44 8c 0a\"\n          },\n          [\n            {\n              \"children\": [\n                \"这是 \",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://zhuanlan.zhihu.com/p/28566058\"\n                  },\n                  \"content\": \"AEAD 算法\"\n                },\n                \"的鉴别标签，用以确认加密数据和数据包头的完整性。它由加密算法产生，并由解密算法消耗。\"\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"data\": [\n        [\n          \"\",\n          {\n            \"props\": {\n              \"className\": \"decryption-header\"\n            },\n            \"content\": \"解密后的数据载荷\"\n          },\n          [\n            { \"Tag\": \"h4\", \"content\": \"解密\" },\n            \"数据被 \\\"客户端握手时密钥计算\\\" 步骤中产生的初始密钥和初始向量(IVs)加密。IVs 通过对密钥和数据包序号进行异或操作生成。在例子中 IV 为 0。\",\n            \"数据包开头的 20 字节(数据包头)还会作为解密过程解密成功时必须满足的认证条件。\",\n            {\n              \"children\": [\n                \"openssl 命令行工具还不支持 AEAD 算法加解密(AEAD ciphers)，你可以使用作者的命令行工具来\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://quic.xargs.org/files/aes_128_gcm_decrypt.c\"\n                  },\n                  \"content\": \"解密\"\n                },\n                \"和\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://quic.xargs.org/files/aes_128_gcm_encrypt.c\"\n                  },\n                  \"content\": \"加密\"\n                },\n                \"这些数据。\"\n              ]\n            },\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### from the \\\"Handshake Keys Calc\\\" step\\n$ key=30a7e816f6a1e1b3434cf39cf4b415e7\\n$ iv=11e70a5d1361795d2bb04465\\n### from this record\\n$ recdata=e00000000105735f63696405635f636964403f01\\n$ authtag=5e98f22dc6f25979919bad302f448c0a\\n$ recordnum=1\\n### may need to add -I and -L flags for include and lib dirs\\n$ cc -o aes_128_gcm_decrypt aes_128_gcm_decrypt.c -lssl -lcrypto\\n$ cat /tmp/msg1   | ./aes_128_gcm_decrypt $iv $recordnum $key $recdata $authtag   | hexdump -C\\n\\n00000000  02 01 40 46 00 01 06 00  40 24 14 00 00 20 50 ff  |..@F....@$... P.|\\n00000010  b0 c1 a4 25 c6 41 89 1c  98 3d 12 67 26 02 6d 3d  |...%.A...=.g&.m=|\\n00000020  b2 8e a3 51 0b dc 20 54  fc d6 37 ed ca cc        |...Q.. T..7...|\"\n              }\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"ACK 帧\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"02 01 40 46 00 01\"\n          },\n          [\n            \"客户端确认收到服务器端的握手数据包1。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"02 - 表明帧类型 ACK\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"01 - largest_acknowledged: 被确认的最大数据包\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"children\": [\n                    \"40 46 - ack_delay: 变长的整数。给出这个 ack 被延迟发送的时间，单位是微秒。\",\n                    \"通过计算式子：2\",\n                    {\n                      \"Tag\": \"sup\",\n                      \"content\": \"ack_delay_exponent\"\n                    },\n                    \" 得到。其中 ack_delay_exponent = 70 * 8 = 560 (µseconds)。\"\n                  ]\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 - ack_range_count: 额外的 ACK 帧数据长度 0x(0)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"01 - first_ack_range: 可变长度的整数。给出在 largest_acknowledged 之前被确认过的数据包数量。\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"加密帧帧头\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"06 00 40 24\"\n          },\n          [\n            {\n              \"children\": [\n                \"每个 QUIC 数据包在其有效载荷中包含\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://zhuanlan.zhihu.com/p/438579683\"\n                  },\n                  \"content\": \"一个或多个帧的列表\"\n                },\n                \"。例子中的数据包只包含一个帧，即 加密帧。\"\n              ]\n            },\n            \"加密帧帧头的目的是加密字节流，用于建立安全的 TLS 连接(TLS 1.3 握手)。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"06 - 表明这是 加密帧\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 - 可变长度的整数(前两位表示2字节的整数)。表示加密帧帧内字节的偏移量为 0x00(0) 字节。\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"40 24 - 长度可变的整数(前两位表示2字节的整数)，显示加密帧帧内字节长度为 0x24(36) 字节。\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"TLS 握手完成\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"14 00 00 20 50 ff b0 c1 a4 25 c6 41 89 1c 98 3d 12 67 26 02 6d 3d b2 8e a3 51 0b dc 20 54 fc d6 37 ed ca cc\"\n          },\n          [\"这一记录将在后面详细说明。\"]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/QUIC/clientHandshakeKeysCalc.json",
    "content": "[\n  \"客户端现在拥有了用于计算剩余握手步骤的加密密钥的所有信息。在这个计算中，客户端使用了以下信息：\",\n  {\n    \"Tag\": \"ul\",\n    \"children\": [\n      {\n        \"Tag\": \"li\",\n        \"content\": \"服务器端公钥(提取自 ServerHello 数据包)\"\n      },\n      {\n        \"Tag\": \"li\",\n        \"content\": \"客户端私钥(之前准备密钥交换时生成)\"\n      },\n      {\n        \"Tag\": \"li\",\n        \"content\": \"ClientHello 和 ServerHello 的 SHA256 哈希值\"\n      }\n    ]\n  },\n  \"首先，客户端需要找到共享的密文(shared secret)，即密钥交换步骤的最终值。客户端通过使用 curve25519 算法将服务器端的公钥乘以客户端的私钥(椭圆曲线乘法的特性将使得计算结果与服务器在其计算中得到的结果一致)，即可发现 32 字节的最终值是：\",\n  {\n    \"Tag\": \"pre\",\n    \"props\": {\n      \"className\": \"ind2\"\n    },\n    \"children\": [\n      {\n        \"Tag\": \"code\",\n        \"props\": { \"className\": \"longboi\" },\n        \"content\": \"df4a291baa1eb7cfa6934b29b474baad2697e29f1f920dcc77c8a0a088447624\"\n      }\n    ]\n  },\n  {\n    \"children\": [\n      \"你可以使用\",\n      {\n        \"Tag\": \"a\",\n        \"props\": { \"href\": \"https://quic.xargs.org/files/curve25519-mult.c\" },\n        \"content\": \"原作者的脚本\"\n      },\n      \"快速验证结果：\"\n    ]\n  },\n  {\n    \"Tag\": \"CodeSample\",\n    \"props\": {\n      \"code\": \"$ cc -o curve25519-mult curve25519-mult.c\\n$ ./curve25519-mult client-ephemeral-private.key                     server-ephemeral-public.key | hexdump\\n\\n0000000 df 4a 29 1b aa 1e b7 cf a6 93 4b 29 b4 74 ba ad\\n0000010 26 97 e2 9f 1f 92 0d cc 77 c8 a0 a0 88 44 76 24\"\n    }\n  },\n  \"由于上面的计算结果与服务器端计算结果相同，且剩下的计算过程也相同，因此算出来的数值也应该是相同的：\",\n  {\n    \"Tag\": \"ul\",\n    \"children\": [\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"客户端握手时密钥: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"30a7e816f6a1e1b3434cf39cf4b415e7\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"客户端握手时向量: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"11e70a5d1361795d2bb04465\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"客户端握手时数据包头保护密钥: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"84b3c21cacaf9f54c885e9a506459079\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"服务器端握手时密钥: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"17abbf0a788f96c6986964660414e7ec\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"服务器端握手时向量: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"09597a2ea3b04c00487e71f3\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"服务器端握手时数据包头保护密钥: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"2a18061c396c2828582b41b0910ed536\"\n          }\n        ]\n      }\n    ]\n  }\n]\n"
  },
  {
    "path": "src/QUIC/clientInitialKeysCalc.json",
    "content": "[\n  \"接下来，客户端继续为连接做准备：生成初始数据包(packet)的加密密钥。由于客户端和服务器之间还没有进行任何数据传输，因此这些密钥的安全性有限——任何观察者都可以得出密钥，并像服务器一样读取流量。加密初始数据包可以防止某些类型的攻击，如请求伪造攻击。\",\n  \"客户端生成 8bytes 的随机数据，在本文中，假设是如下内容：\",\n  {\n    \"Tag\": \"pre\",\n    \"props\": {\n      \"className\": \"ind2\"\n    },\n    \"children\": [\n      {\n        \"Tag\": \"code\",\n        \"props\": { \"className\": \"longboi\" },\n        \"content\": \"0001020304050607\"\n      }\n    ]\n  },\n  \"然后，客户端使用以下过程得出加密密钥：\",\n  {\n    \"Tag\": \"CodeSample\",\n    \"props\": {\n      \"code\": \"initial_salt = 38762cf7f55934b34d179ae6a4c80cadccbb7f0a\\ninitial_random = (random bytes from client given above)\\ninitial_secret = HKDF-Extract(salt: initial_salt, key: initial_random)\\nclient_secret = HKDF-Expand-Label(key: initial_secret, label: \\\"client in\\\", ctx: \\\"\\\", len: 32)\\nserver_secret = HKDF-Expand-Label(key: initial_secret, label: \\\"server in\\\", ctx: \\\"\\\", len: 32)\\nclient_key = HKDF-Expand-Label(key: client_secret, label: \\\"quic key\\\", ctx: \\\"\\\", len: 16)\\nserver_key = HKDF-Expand-Label(key: server_secret, label: \\\"quic key\\\", ctx: \\\"\\\", len: 16)\\nclient_iv = HKDF-Expand-Label(key: client_secret, label: \\\"quic iv\\\", ctx: \\\"\\\", len: 12)\\nserver_iv = HKDF-Expand-Label(key: server_secret, label: \\\"quic iv\\\", ctx: \\\"\\\", len: 12)\\nclient_hp_key = HKDF-Expand-Label(key: client_secret, label: \\\"quic hp\\\", ctx: \\\"\\\", len: 16)\\nserver_hp_key = HKDF-Expand-Label(key: server_secret, label: \\\"quic hp\\\", ctx: \\\"\\\", len: 16)\"\n    }\n  },\n  {\n    \"children\": [\n      \"一个有趣的点，选择常数\",\n      {\n        \"Tag\": \"code\",\n        \"content\": \"38762cf7f55934b34d179ae6a4c80cadccbb7f0a\"\n      },\n      \"作为盐，并不是它可以从数学或密码学原理推导得出，而是因为它是由谷歌研究人员发现的第一个 SHA-1 碰撞的哈希值(QUIC 本身最初是由谷歌创建、赞助和部署的)。\"\n    ]\n  },\n  \"QUIC 还从 TLS 1.3 中引入了两个新的加密概念：\",\n  {\n    \"Tag\": \"ul\",\n    \"children\": [\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          {\n            \"Tag\": \"a\",\n            \"props\": {\n              \"href\": \"https://hxndg.github.io/2020/08/27/HKDF-Extract%E4%B8%8EHKDF-Expand/\"\n            },\n            \"content\": \"HKDF-Extract\"\n          },\n          \" - 给定一个盐值和一些字节的密钥材料，派生出一个 32bytes(256bits) 固定长度的新密钥材料，输入的密钥材料的熵均匀地分布在输出中。\"\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          {\n            \"Tag\": \"a\",\n            \"props\": {\n              \"href\": \"https://hxndg.github.io/2020/08/27/HKDF-Extract%E4%B8%8EHKDF-Expand/\"\n            },\n            \"content\": \"HKDF-Expand-Label\"\n          },\n          \" - 给出伪随机密钥、标签和上下文数据的输入，拓展为一个要求长度的密钥。\"\n        ]\n      }\n    ]\n  },\n  {\n    \"children\": [\n      \"在命令行中使用\",\n      {\n        \"Tag\": \"a\",\n        \"props\": { \"href\": \"https://quic.xargs.org/files/hkdf.sh\" },\n        \"content\": \"原作者制作的 HKDF 命令行脚本\"\n      },\n      \"，你也可以自己试试：\"\n    ]\n  },\n  {\n    \"Tag\": \"CodeSample\",\n    \"props\": {\n      \"code\": \"$ init_salt=38762cf7f55934b34d179ae6a4c80cadccbb7f0a\\n$ init_dcid=0001020304050607\\n$ init_secret=$(./hkdf extract $init_salt $init_dcid)\\n$ csecret=$(./hkdf expandlabel $init_secret \\\"client in\\\" \\\"\\\" 32)\\n$ ssecret=$(./hkdf expandlabel $init_secret \\\"server in\\\" \\\"\\\" 32)\\n$ client_init_key=$(./hkdf expandlabel $csecret \\\"quic key\\\" \\\"\\\" 16)\\n$ server_init_key=$(./hkdf expandlabel $ssecret \\\"quic key\\\" \\\"\\\" 16)\\n$ client_init_iv=$(./hkdf expandlabel $csecret \\\"quic iv\\\" \\\"\\\" 12)\\n$ server_init_iv=$(./hkdf expandlabel $ssecret \\\"quic iv\\\" \\\"\\\" 12)\\n$ client_init_hp=$(./hkdf expandlabel $csecret \\\"quic hp\\\" \\\"\\\" 16)\\n$ server_init_hp=$(./hkdf expandlabel $ssecret \\\"quic hp\\\" \\\"\\\" 16)\\n$ echo ckey: $client_init_key\\n$ echo civ: $client_init_iv\\n$ echo chp: $client_init_hp\\n$ echo skey: $server_init_key\\n$ echo siv: $server_init_iv\\n$ echo shp: $server_init_hp\\n\\nckey: b14b918124fda5c8d79847602fa3520b\\nciv: ddbc15dea80925a55686a7df\\nchp: 6df4e9d737cdf714711d7c617ee82981\\nskey: d77fc4056fcfa32bd1302469ee6ebf90\\nsiv: fcb748e37ff79860faa07477\\nshp: 440b2725e91dc79b370711ef792faa3d\"\n    }\n  },\n  \"由此我们可以得到以下密钥以及初始向量：\",\n  {\n    \"Tag\": \"ul\",\n    \"children\": [\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"客户端初始密钥: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"b14b918124fda5c8d79847602fa3520b\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"客户端初始向量: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"ddbc15dea80925a55686a7df\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"服务器端初始密钥: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"d77fc4056fcfa32bd1302469ee6ebf90\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"服务器端初始向量: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"fcb748e37ff79860faa07477\"\n          }\n        ]\n      }\n    ]\n  },\n  \"我们还得到了以下的 \\\"数据包头保护密钥\\\"(header protection keys)，后面将解释其作用：\",\n  {\n    \"Tag\": \"ul\",\n    \"children\": [\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"客户端初始数据包头保护密钥: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"6df4e9d737cdf714711d7c617ee82981\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"服务器端初始数据包头保护密钥: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"440b2725e91dc79b370711ef792faa3d\"\n          }\n        ]\n      }\n    ]\n  },\n  \"此时，网络连接仍在准备中，没有任何数据被传输。\"\n]\n"
  },
  {
    "path": "src/QUIC/clientInitialPacket.json",
    "content": "[\n  \"会话开始时，客户端发送一个初始数据包。这个数据包包含 \\\"ClientHello\\\" TLS 记录，用于开始 TLS 1.3 加密会话。\",\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"数据包头字节\",\n          {\n            \"props\": {\n              \"className\": \"bytes protected\",\n              \"title\": \"被数据包头保护密钥加密\"\n            },\n            \"content\": \"cd\"\n          },\n          {\n            \"props\": {\n              \"className\": \"bytes unprotected\"\n            },\n            \"content\": \"c0\"\n          },\n          [\n            \"数据包以一个头字节开始，该字节应用了头保护。头部保护用于隐藏数据包序号和其他信息，使其不被外界观察到。\",\n            \"包头保护是通过用\\\"数据包头保护密钥\\\"对每个数据包的有效载荷的样本进行加密，然后将每个数据包中的某些比特和字节与所得数据进行异或(XOR)操作得到的。对于像这样的\\\"长\\\"格式数据包，受保护的部分是这个字节的低 4 位，以及数据包序号的字节（见下文）。\",\n            \"这里有一个关于如何计算出加密头字节的例子：\",\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### \\\"client header protection key\\\" from calc step above\\n$ key=6df4e9d737cdf714711d7c617ee82981\\n### sample is taken from 16 bytes of payload starting\\n### 4 bytes past the first byte of the packet number\\n$ sample=ed78716be9711ba498b7ed868443bb2e\\n$ echo $sample | xxd -r -p | openssl aes-128-ecb -K $key | head -c 5 | xxd -p\\n\\ned9895bb15\\n\\n### first byte of result is xor'd into lower 4 bits of this byte,\\n### remaining bytes are xor'd one-for-one into the bytes of\\n### the packet number (which in this packet is only one byte)\"\n              }\n            },\n            \"解密出的字节 0xC0 中的位有以下含义：\",\n            {\n              \"Tag\": \"Table\",\n              \"props\": {\n                \"headers\": [\"\", \"值\", \"含义\"],\n                \"data\": [\n                  [\"高位\", \"1\", \"Long Header 格式\"],\n                  [\"\", \"1\", \"固定位(总是被置1)\"],\n                  [\"\", \"00\", \"数据包类型：初始化\"],\n                  [\"\", \"00\", \"保留(总是被置0)\"],\n                  [\n                    \"低位\",\n                    \"00\",\n                    \"数据包序号长度(表示下面的 \\\"数据包序号\\\" 将有一个字节的长度，默认值)\"\n                  ]\n                ]\n              }\n            }\n          ]\n        ],\n        [\n          \"QUIC 版本号\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 00 00 01\"\n          },\n          [\"QUIC的版本是：0x1\"]\n        ],\n        [\n          \"目的地连接标识 ID\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"08 00 01 02 03 04 05 06 07\"\n          },\n          [\n            \"客户端还没有收到服务器选择的连接 ID。因此这个字段仅来提供用于推导初始加密密钥的 8 个字节的随机数据，如上文 \\\"客户端生成初始密钥\\\" 所解释。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"08 - 紧跟着的目的地连接标识 ID 的长度(8bytes)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 01 ... 06 07 - 随机数据\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"源连接标识 ID\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"05 63 5f 63 69 64\"\n          },\n          [\n            \"客户端使用这个字段来表明它选择的传输给服务器的源连接标识 ID。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"05 - 紧跟着的源连接标识 ID 的长度(5bytes)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"63 5f 63 69 64 - 实际的源连接标识 ID (\\\"c_cid\\\")\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"令牌\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00\"\n          },\n          [\n            \"客户端在某些情况下可以使用这个字段来提供服务器所要求的令牌，例如证明其连接尝试不是欺骗。此时，没有令牌需要提供，该字段为空。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 - 紧跟着的令牌长度(0bytes)\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"数据包长度\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"41 03\"\n          },\n          [\n            \"客户端指明后续数据包数据的长度。这个字段是一个长度可变的整数——第一个字节的前两位表示该整数中总共有多少个字节。\",\n            \"此时，第一个字节以 \\\"0 1\\\"(0x4)这两个位开始，表示该整数共两个字节。其余的位给出数字 0x103，表示 259 个字节的有效载荷。\"\n          ]\n        ],\n        [\n          \"数据包序号\",\n          {\n            \"props\": {\n              \"className\": \"bytes protected\",\n              \"title\": \"被数据包头保护密钥加密\"\n            },\n            \"content\": \"98\"\n          },\n          {\n            \"props\": {\n              \"className\": \"bytes unprotected\"\n            },\n            \"content\": \"00\"\n          },\n          [\n            \"该字节应用了头保护。详见 \\\"数据包头字节\\\"。\",\n            \"这个字节的未受保护的值为 0x00，表明它是第 0 号包，或者说是客户端发送的第一个\\\"初始\\\"包。\",\n            \"这个数据也有可能被截断。发送端点通过几个步骤截断：①计算已发送的最高的序号和未确认的最低的序号之间的差值；②出于安全考虑将差值加倍并四舍五入；③计算它在明确表示两端之间的序号的前提下可以从序号的高位删除的字节数；④截断编码后的数据包序号直至长度满足该字节数。而接收端点根据会它最近看到的数据包号码填入完整的序号。\",\n            {\n              \"children\": [\n                \"由于我们的例子对话发送的数据包很小(少于 64 个字节)，所以这种截断不会在本文中出现。详情见 \",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://www.rfc-editor.org/rfc/rfc9000.html#section-17.1\"\n                  },\n                  \"content\": \"RFC 9000\"\n                },\n                \"。\"\n              ]\n            }\n          ]\n        ],\n        [\n          \"加密的数据载荷\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\",\n              \"title\": \"被\\\"初始密钥\\\"加密\"\n            },\n            \"content\": \"1c 36 a7 ed 78 71 6b e9 71 1b a4 98 b7 ed 86 84 43 bb 2e 0c 51 4d 4d 84 8e ad cc 7a 00 d2 5c e9 f9 af a4 83 97 80 88 de 83 6b e6 8c 0b 32 a2 45 95 d7 81 3e a5 41 4a 91 99 32 9a 6d 9f 7f 76 0d d8 bb 24 9b f3 f5 3d 9a 77 fb b7 b3 95 b8 d6 6d 78 79 a5 1f e5 9e f9 60 1f 79 99 8e b3 56 8e 1f dc 78 9f 64 0a ca b3 85 8a 82 ef 29 30 fa 5c e1 4b 5b 9e a0 bd b2 9f 45 72 da 85 aa 3d ef 39 b7 ef af ff a0 74 b9 26 70 70 d5 0b 5d 07 84 2e 49 bb a3 bc 78 7f f2 95 d6 ae 3b 51 43 05 f1 02 af e5 a0 47 b3 fb 4c 99 eb 92 a2 74 d2 44 d6 04 92 c0 e2 e6 e2 12 ce f0 f9 e3 f6 2e fd 09 55 e7 1c 76 8a a6 bb 3c d8 0b bb 37 55 c8 b7 eb ee 32 71 2f 40 f2 24 51 19 48 70 21 b4 b8 4e 15 65 e3 ca 31 96 7a c8 60 4d 40 32 17 0d ec 28 0a ee fa 09 5d 08\"\n          },\n          [\"这些数据使用客户端的\\\"初始密钥\\\"进行加密。\"]\n        ],\n        [\n          \"AEAD 鉴别标签\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"b3 b7 24 1e f6 64 6a 6c 86 e5 c6 2c e0 8b e0 99\"\n          },\n          [\n            {\n              \"children\": [\n                \"这是 \",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://zhuanlan.zhihu.com/p/28566058\"\n                  },\n                  \"content\": \"AEAD 算法\"\n                },\n                \"的鉴别标签，用以确认加密数据和数据包头的完整性。它由加密算法产生，并由解密算法消耗。\"\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"data\": [\n        [\n          \"\",\n          {\n            \"props\": {\n              \"className\": \"decryption-header\"\n            },\n            \"content\": \"解密后的数据载荷\"\n          },\n          [\n            { \"Tag\": \"h4\", \"content\": \"解密\" },\n            \"数据被 \\\"客户端初始密钥计算\\\" 步骤中产生的初始密钥和初始向量(IVs)加密。IVs 通过对密钥和数据包序号进行异或操作生成。在例子中 IV 为 0。\",\n            \"数据包开头的 24 字节(数据包头)还会作为解密过程解密成功时必须满足的认证条件。\",\n            {\n              \"children\": [\n                \"openssl 命令行工具还不支持 AEAD 算法加解密(AEAD ciphers)，你可以使用作者的命令行工具来\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://quic.xargs.org/files/aes_128_gcm_decrypt.c\"\n                  },\n                  \"content\": \"解密\"\n                },\n                \"和\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://quic.xargs.org/files/aes_128_gcm_encrypt.c\"\n                  },\n                  \"content\": \"加密\"\n                },\n                \"这些数据。\"\n              ]\n            },\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### from the \\\"Initial Keys Calc\\\" step\\n$ key=b14b918124fda5c8d79847602fa3520b\\n$ iv=ddbc15dea80925a55686a7df\\n### from this record\\n$ recdata=c00000000108000102030405060705635f63696400410300\\n$ authtag=b3b7241ef6646a6c86e5c62ce08be099\\n$ recordnum=0\\n### may need to add -I and -L flags for include and lib dirs\\n$ cc -o aes_128_gcm_decrypt aes_128_gcm_decrypt.c -lssl -lcrypto\\n$ cat /tmp/msg1   | ./aes_128_gcm_decrypt $iv $recordnum $key $recdata $authtag   | hexdump -C\\n\\n00000000  06 00 40 ee 01 00 00 ea  03 03 00 01 02 03 04 05  |..@.............|\\n00000010  06 07 08 09 0a 0b 0c 0d  0e 0f 10 11 12 13 14 15  |................|\\n... snip ...\"\n              }\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"加密帧帧头\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"06 00 40 ee\"\n          },\n          [\n            {\n              \"children\": [\n                \"每个 QUIC 数据包在其有效载荷中包含\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://zhuanlan.zhihu.com/p/438579683\"\n                  },\n                  \"content\": \"一个或多个帧的列表\"\n                },\n                \"。例子中的数据包只包含一个帧，即 加密帧。\"\n              ]\n            },\n            \"加密帧帧头的目的是加密字节流，用于建立安全的 TLS 连接(TLS 1.3 握手)。\"\n          ]\n        ],\n        [\n          \"ClientHello TLS 记录\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"01 00 00 ea 03 03 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 00 00 06 13 01 13 02 13 03 01 00 00 bb 00 00 00 18 00 16 00 00 13 65 78 61 6d 70 6c 65 2e 75 6c 66 68 65 69 6d 2e 6e 65 74 00 0a 00 08 00 06 00 1d 00 17 00 18 00 10 00 0b 00 09 08 70 69 6e 67 2f 31 2e 30 00 0d 00 14 00 12 04 03 08 04 04 01 05 03 08 05 05 01 08 06 06 01 02 01 00 33 00 26 00 24 00 1d 00 20 35 80 72 d6 36 58 80 d1 ae ea 32 9a df 91 21 38 38 51 ed 21 a2 8e 3b 75 e9 65 d0 d2 cd 16 62 54 00 2d 00 02 01 01 00 2b 00 03 02 03 04 00 39 00 31 03 04 80 00 ff f7 04 04 80 a0 00 00 05 04 80 10 00 00 06 04 80 10 00 00 07 04 80 10 00 00 08 01 0a 09 01 0a 0a 01 03 0b 01 19 0f 05 63 5f 63 69 64\"\n          },\n          [\"这一记录将在后面详细说明。\"]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/QUIC/clientKeyExchangeGeneration.json",
    "content": "[\n  \"连接开始时，客户端生成一个用于密钥交换的“私钥/公钥”对。密钥交换(Key exchange)是一种技术，双方可以在同一数字上达成一致，而窃听者却无法知道这个数字是什么。\",\n  {\n    \"Tag\": \"p\",\n    \"children\": [\n      \"学习 QUIC 并不需要深入了解，但你可以从\",\n      {\n        \"Tag\": \"a\",\n        \"props\": { \"href\": \"https://cangsdarm.github.io/illustrate/x25519\" },\n        \"content\": \"X25519 密钥交换算法\"\n      },\n      \"获取涉及到的密钥交换算法的具体解释。\"\n    ]\n  },\n  [\n    \"**私钥**是 0 到 \",\n    {\n      \"Tag\": \"Math\",\n      \"content\": \"2^256-1\"\n    },\n    \" 之间的一个随机整数(32bytes, 256bits)\",\n    \"。为方便后续解释，假设我们生成的私钥是：\"\n  ],\n  {\n    \"Tag\": \"pre\",\n    \"props\": {\n      \"className\": \"ind2\"\n    },\n    \"children\": [\n      {\n        \"Tag\": \"code\",\n        \"props\": { \"className\": \"longboi\" },\n        \"content\": \"202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f\"\n      }\n    ]\n  },\n  \"**公钥**由上面提到的 X25519 密钥交换算法生成。例子中计算出的公钥应如下所示：\",\n  {\n    \"Tag\": \"pre\",\n    \"props\": {\n      \"className\": \"ind2\"\n    },\n    \"children\": [\n      {\n        \"Tag\": \"code\",\n        \"props\": { \"className\": \"longboi\" },\n        \"content\": \"358072d6365880d1aeea329adf9121383851ed21a28e3b75e965d0d2cd166254\"\n      }\n    ]\n  },\n  \"公钥的计算也可以在命令行中运行以下命令得到：\",\n  {\n    \"Tag\": \"CodeSample\",\n    \"props\": {\n      \"code\": \"The public key calculation can be confirmed at the command line:\\n### requires openssl 1.1.0 or higher\\n$ openssl pkey -noout -text < client-ephemeral-private.key\\n\\nX25519 Private-Key:\\npriv:\\n    20:21:22:23:24:25:26:27:28:29:2a:2b:2c:2d:2e:\\n    2f:30:31:32:33:34:35:36:37:38:39:3a:3b:3c:3d:\\n    3e:3f\\npub:\\n    35:80:72:d6:36:58:80:d1:ae:ea:32:9a:df:91:21:\\n    38:38:51:ed:21:a2:8e:3b:75:e9:65:d0:d2:cd:16:\\n    62:54\"\n    }\n  },\n  \"此时，网络连接仍在准备中，没有任何数据被传输。\"\n]\n"
  },
  {
    "path": "src/QUIC/clientTLSHandshakeFinished.json",
    "content": "[\n  \"为了验证握手成功且没有被篡改过，客户端和服务器端一样，需要创建一些验证数据给服务器端确认。验证数据是基于所有握手信息的哈希值计算得到。\",\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"TLS 握手消息头\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"14 00 00 20\"\n          },\n          [\n            \"每个 TLS 握手消息都以一个 type 和一个 len 开始。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"14 - 握手消息类型 0x14 (finished)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 20 - 紧接着的握手消息数据的长度 0x20 (32) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"验证数据\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"50 ff b0 c1 a4 25 c6 41 89 1c 98 3d 12 67 26 02 6d 3d b2 8e a3 51 0b dc 20 54 fc d6 37 ed ca cc\"\n          },\n          [\n            \"使用 \\\"客户端生成握手密钥\\\" 步骤中的客户端密文和在这之前的每个握手记录(ClientHello 到 服务器握手完成)的 SHA256 哈希值生成。\",\n            {\n              \"Tag\": \"pre\",\n              \"children\": [\n                {\n                  \"Tag\": \"code\",\n                  \"props\": { \"className\": \"longboi\" },\n                  \"content\": \"finished_key = HKDF-Expand-Label(key: client_secret, label: \\\"finished\\\", ctx: \\\"\\\", len: 32)\\nfinished_hash = SHA256(ClientHello ... ServerFinished)\\nverify_data = HMAC-SHA256(key: finished_key, msg: finished_hash)\"\n                }\n              ]\n            },\n            {\n              \"children\": [\n                \"在命令行中使用\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": { \"href\": \"https://quic.xargs.org/files/hkdf.sh\" },\n                  \"content\": \"原作者制作的 HKDF 命令行脚本\"\n                },\n                \"，你也可以自己试试：\"\n              ]\n            },\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### find the hash of the conversation to this point, excluding\\n### QUIC frame headers\\n$ fin_hash=$(cat crypto_clienthello crypto_serverhello crypto_extensions   crypto_cert crypto_certverify crypto_s_finished | openssl sha256)\\n$ cht_secret=b8902ab5f9fe52fdec3aea54e9293e4b8eabf955fcd88536bf44b8b584f14982\\n$ fin_key=$(./hkdf expandlabel $cht_secret \\\"finished\\\" \\\"\\\" 32)\\n$ echo $fin_hash | xxd -r -p     | openssl dgst -sha256 -mac HMAC -macopt hexkey:$fin_key\\n\\n50ffb0c1a425c641891c983d126726026d3db28ea3510bdc2054fcd637edcacc\"\n              }\n            }\n          ]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/QUIC/index.js",
    "content": "const data = {\n  intro: {\n    title: \"图解 QUIC 连接\",\n    subtitle: \"对每一个字节的解释和再现\",\n    desc: \"QUIC 是一个基于 UDP 的安全流协议，构成了 HTTP/3 的基础。\",\n    intro:\n      '在这个演示中，客户端通过 TLS 加密协商 QUIC 协议连接服务器。客户端发送\"ping\"、接收\"pong\"后终止连接。点击下面开始探索。',\n  },\n  sections: [\n    {\n      type: \"RecOuter\",\n      tags: [\"calculation\", \"client\"],\n      label: \"客户端准备密钥交换\",\n      illustration: {\n        src: \"https://quic.xargs.org/images/key1.png\",\n        width: \"135\",\n        height: \"250\",\n      },\n      json: () => import(\"./clientKeyExchangeGeneration.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"calculation\", \"client\"],\n      label: \"客户端生成初始密钥\",\n      illustration: {\n        src: \"https://quic.xargs.org/images/key2.png\",\n        width: \"135\",\n        height: \"250\",\n      },\n      json: () => import(\"./clientInitialKeysCalc.json\"),\n    },\n    {\n      type: \"Datagram\",\n      datagram: 3,\n      label: \"UDP 数据报 01 - ClientHello\",\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"client\"],\n      label: \"客户端初始数据包\",\n      json: () => import(\"./clientInitialPacket.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"client\", \"embedded\"],\n      label: \"TLS: ClientHello\",\n      json: () => import(\"./tls-clientHello.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"client\"],\n      label: \"长度填充/对齐\",\n      json: () => import(\"./padding.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"calculation\", \"server\"],\n      label: \"服务器端准备密钥交换\",\n      illustration: {\n        src: \"https://quic.xargs.org/images/key3.png\",\n        width: \"135\",\n        height: \"250\",\n      },\n      json: () => import(\"./serverKeyExchangeGeneration.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"calculation\", \"server\"],\n      label: \"服务器端生成初始密钥\",\n      illustration: {\n        src: \"https://quic.xargs.org/images/key4.png\",\n        width: \"135\",\n        height: \"250\",\n      },\n      json: () => import(\"./serverInitialKeysCalc.json\"),\n    },\n    {\n      type: \"Datagram\",\n      datagram: 6,\n      label: \"UDP 数据报 02 - ServerHello + 服务器发起握手\",\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"server\"],\n      label: \"服务器端初始数据包\",\n      json: () => import(\"./serverInitialPacket.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"server\", \"embedded\"],\n      label: \"TLS: ServerHello\",\n      json: () => import(\"./tls-serverHello.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"calculation\", \"server\"],\n      label: \"服务器端生成握手密钥\",\n      illustration: {\n        src: \"https://quic.xargs.org/images/key5.png\",\n        width: \"135\",\n        height: \"250\",\n      },\n      json: () => import(\"./serverHandshakeKeysCalc.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"server\"],\n      label: \"服务器端握手数据包\",\n      json: () => import(\"./serverHandshakePacket1.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"server\", \"embedded\"],\n      label: \"TLS: TLS 记录的加密的扩展\",\n      json: () => import(\"./tls-encryptedExtensions.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"server\", \"embedded\"],\n      label: \"TLS: TLS 记录证书\",\n      json: () => import(\"./tls-certificate.json\"),\n    },\n    {\n      type: \"Datagram\",\n      datagram: 3,\n      label: \"UDP 数据报 03 - 服务器第二个握手包(服务器握手过程完成)\",\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"server\"],\n      label: \"服务器端握手数据包\",\n      id: \"serverHandshake2\",\n      json: () => import(\"./serverHandshakePacket2.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"server\", \"embedded\"],\n      label: \"TLS: 证书验证数据\",\n      json: () => import(\"./tls-certificateVerify.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"server\", \"embedded\"],\n      label: \"TLS: 握手完成\",\n      json: () => import(\"./serverTLSHandshakeFinished.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"calculation\", \"client\"],\n      label: \"客户端生成握手密钥\",\n      illustration: {\n        src: \"https://quic.xargs.org/images/key6.png\",\n        width: \"135\",\n        height: \"250\",\n      },\n      json: () => import(\"./clientHandshakeKeysCalc.json\"),\n    },\n    {\n      type: \"Datagram\",\n      datagram: 3,\n      label: \"UDP 数据报 04 - Ack\",\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"client\"],\n      label: \"客户端 Ack\",\n      json: () => import(\"./clientAck.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"client\"],\n      label: \"客户端握手数据包\",\n      json: () => import(\"./clientHandshake1.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"client\"],\n      label: \"长度填充/对齐\",\n      id: \"padding2\",\n      json: () => import(\"./padding.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"calculation\", \"server\"],\n      label: \"服务器端生成会话密钥(application keys)\",\n      illustration: {\n        src: \"https://quic.xargs.org/images/key7.png\",\n        width: \"116\",\n        height: \"250\",\n      },\n      json: () => import(\"./serverApplicationKeysCalc.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"calculation\", \"client\"],\n      label: \"客户端生成会话密钥(application keys)\",\n      illustration: {\n        src: \"https://quic.xargs.org/images/key8.png\",\n        width: \"97\",\n        height: \"250\",\n      },\n      json: () => import(\"./clientApplicationKeysCalc.json\"),\n    },\n    {\n      type: \"Datagram\",\n      datagram: 3,\n      label: 'UDP 数据报 05 - 客户端握手完成，客户端开始 \"ping\"',\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"client\"],\n      label: \"客户端握手数据包\",\n      id: \"clientHandshake2\",\n      json: () => import(\"./clientHandshake2.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"client\", \"embedded\"],\n      label: \"TLS: 握手完成\",\n      id: \"tls-handshakeFinishedClient\",\n      json: () => import(\"./clientTLSHandshakeFinished.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"client\"],\n      label: \"客户端会话包\",\n      json: () => import(\"./clientApplicationPacket1.json\"),\n    },\n    {\n      type: \"Datagram\",\n      datagram: 2,\n      label: 'UDP 数据报 06 - 服务器端握手完成，服务器端发送 \"pong\" 响应',\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"server\"],\n      label: \"服务器端握手 ACK 数据包\",\n      id: \"serverHandshake3\",\n      json: () => import(\"./serverHandshakePacket3.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"server\"],\n      label: \"服务器端会话包\",\n      json: () => import(\"./serverApplicationPacket1.json\"),\n    },\n    {\n      type: \"Datagram\",\n      datagram: 1,\n      label: 'UDP 数据报 07 - 客户端收到 \"pong\"(ACK)，并停止响应',\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"client\"],\n      label: \"客户端会话包\",\n      id: \"clientApplicationPacket2\",\n      json: () => import(\"./clientApplicationPacket2.json\"),\n    },\n    {\n      type: \"Datagram\",\n      datagram: 1,\n      label: \"UDP 数据报 08 - 连接关闭\",\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"server\"],\n      label: \"服务器端会话包\",\n      id: \"serverApplicationPacket2\",\n      json: () => import(\"./serverApplicationPacket2.json\"),\n    },\n  ],\n  ending: {\n    mother: \"https://quic.xargs.org/\",\n    desc: \"你可能也对 <a href='/illustrate/tls13' target='_blank'>TLS 1.3</a> 的内容感兴趣。\",\n  },\n};\n\nexport default data;\n"
  },
  {
    "path": "src/QUIC/padding.json",
    "content": "[\n  \"客户端发送的任何包含初始数据包的数据报必须将包填充/对齐到 1200 字节的长度。在数据报上添加 nul 字节(填0)即可。\",\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"填充的字节\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 00 00 00 00 00 00 00 ... 省略 ... 00 00 00 00 00 00 00 00\"\n          },\n          [\n            \"将这个数据包填充到 1200 字节的大小有两个目的：\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"children\": [\n                    {\n                      \"Tag\": \"b\",\n                      \"content\": \"网络 MTU 验证\"\n                    },\n                    \" - 任何 IPv4 主机或路由器都被允许放弃超过其 MTU 限制的数据包，最小为 576 字节。绝大多数互联网的 MTU 要高得多(通常是1500字节)。较高的数据包大小将增加吞吐量和性能。鉴于这些现实情况，QUIC 选择了 1200 字节的最小尺寸限制。这样它应该能穿越绝大多数实际网络(包括隧道网络)，而不会因为尺寸而被丢弃。\",\n                    {\n                      \"Tag\": \"br\"\n                    },\n                    \"为了防止出现用较小的数据包成功建立连接，但一旦发送较大的数据包就开始超时的情况，初始数据包被填充到 1200 字节的长度，以证明端到端路径允许该尺寸的数据包。\"\n                  ]\n                },\n                {\n                    \"Tag\": \"li\",\n                    \"children\": [\n                      {\n                        \"Tag\": \"b\",\n                        \"content\": \"缓解放大攻击\"\n                      },\n                      \" - 此类攻击是指：攻击者向一个无辜的第三方发送少量的流量，该第三方则向需要攻击的目标发送大量流量作为回应。在 QUIC 的情况中，通过 IP 地址欺骗即可造成此类攻击，从而导致 QUIC 服务器以大得多的握手响应来回复很小的初始数据报。\",\n                      {\n                        \"Tag\": \"br\"\n                      },\n                      \"为了帮助缓解这种情况，QUIC 服务器被禁止以超过 3 倍于发送给它的流量来回复，除非服务器从客户端那里收到了一些明确它在给定地址的证据(例如最初来自服务器的往返数据)。在这个初始数据报中添加填充物，给了服务器一个 \\\"较为宽裕的字节预算区间\\\"，使得服务器可以在不超过这个 3 倍的限制前提下执行握手响应。\"\n                    ]\n                  }\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/QUIC/serverApplicationKeysCalc.json",
    "content": "[\n  \"服务器端现在可以计算应用会话时的密钥了。在这个计算中，服务器使用了以下信息：\",\n  {\n    \"Tag\": \"ul\",\n    \"children\": [\n      {\n        \"Tag\": \"li\",\n        \"content\": \"服务器端密文(之前准备握手密钥时生成)\"\n      },\n      {\n        \"Tag\": \"li\",\n        \"content\": \"ClientHello 到 服务器握手结束 所有握手时包的 SHA256 哈希值(散列输入不包括QUIC帧头)\"\n      }\n    ]\n  },\n  \"32 字节的 \\\"handshake_hash\\\" 最终值是：\",\n  {\n    \"Tag\": \"pre\",\n    \"props\": {\n      \"className\": \"ind2\"\n    },\n    \"children\": [\n      {\n        \"Tag\": \"code\",\n        \"props\": { \"className\": \"longboi\" },\n        \"content\": \"b965185af5034eda0ea13ab424dde193afcb42451823a96921ae9d2dad9594ef\"\n      }\n    ]\n  },\n  {\n    \"Tag\": \"CodeSample\",\n    \"props\": {\n      \"code\": \"$ cat crypto_clienthello crypto_serverhello crypto_extensions   crypto_cert crypto_certverify crypto_s_finished  | openssl sha256\\n\\nb965185af5034eda0ea13ab424dde193afcb42451823a96921ae9d2dad9594ef\"\n    }\n  },\n  \"然后，我们将哈希值和共享的签名信息进行一些密钥派生操作(key derivation operations)，以防止可能的攻击：\",\n  {\n    \"Tag\": \"pre\",\n    \"children\": [\n      {\n        \"Tag\": \"code\",\n        \"props\": { \"className\": \"longboi\" },\n        \"content\": \"empty_hash = SHA256(\\\"\\\")\\nderived_secret = HKDF-Expand-Label(key: handshake_secret, label: \\\"derived\\\", ctx: empty_hash, len: 32)\\nmaster_secret = HKDF-Extract(salt: derived_secret, key: 00...)\\nclient_secret = HKDF-Expand-Label(key: master_secret, label: \\\"c ap traffic\\\", ctx: handshake_hash, len: 32)\\nserver_secret = HKDF-Expand-Label(key: master_secret, label: \\\"s ap traffic\\\", ctx: handshake_hash, len: 32)\\nclient_key = HKDF-Expand-Label(key: client_secret, label: \\\"quic key\\\", ctx: \\\"\\\", len: 16)\\nserver_key = HKDF-Expand-Label(key: server_secret, label: \\\"quic key\\\", ctx: \\\"\\\", len: 16)\\nclient_iv = HKDF-Expand-Label(key: client_secret, label: \\\"quic iv\\\", ctx: \\\"\\\", len: 12)\\nserver_iv = HKDF-Expand-Label(key: server_secret, label: \\\"quic iv\\\", ctx: \\\"\\\", len: 12)\\nclient_hp_key = HKDF-Expand-Label(key: client_secret, label: \\\"quic hp\\\", ctx: \\\"\\\", len: 16)\\nserver_hp_key = HKDF-Expand-Label(key: server_secret, label: \\\"quic hp\\\", ctx: \\\"\\\", len: 16)\"\n      }\n    ]\n  },\n  {\n    \"children\": [\n      \"在命令行中使用\",\n      {\n        \"Tag\": \"a\",\n        \"props\": { \"href\": \"https://quic.xargs.org/files/hkdf.sh\" },\n        \"content\": \"原作者制作的 HKDF 命令行脚本\"\n      },\n      \"，你也可以自己试试：\"\n    ]\n  },\n  {\n    \"Tag\": \"CodeSample\",\n    \"props\": {\n      \"code\": \"$ handshake_hash=b965185af5034eda0ea13ab424dde193afcb42451823a96921ae9d2dad9594ef\\n$ handshake_secret=fb9fc80689b3a5d02c33243bf69a1b1b20705588a794304a6e7120155edf149a\\n$ zero_key=0000000000000000000000000000000000000000000000000000000000000000\\n$ empty_hash=$(openssl sha256 < /dev/null | sed -e 's/.* //')\\n$ derived_secret=$(./hkdf expandlabel $handshake_secret \\\"derived\\\" $empty_hash 32)\\n$ master_secret=$(./hkdf extract $derived_secret $zero_key)\\n$ csecret=$(./hkdf expandlabel $master_secret \\\"c ap traffic\\\" $handshake_hash 32)\\n$ ssecret=$(./hkdf expandlabel $master_secret \\\"s ap traffic\\\" $handshake_hash 32)\\n$ client_data_key=$(./hkdf expandlabel $csecret \\\"quic key\\\" \\\"\\\" 16)\\n$ server_data_key=$(./hkdf expandlabel $ssecret \\\"quic key\\\" \\\"\\\" 16)\\n$ client_data_iv=$(./hkdf expandlabel $csecret \\\"quic iv\\\" \\\"\\\" 12)\\n$ server_data_iv=$(./hkdf expandlabel $ssecret \\\"quic iv\\\" \\\"\\\" 12)\\n$ client_data_hp=$(./hkdf expandlabel $csecret \\\"quic hp\\\" \\\"\\\" 16)\\n$ server_data_hp=$(./hkdf expandlabel $ssecret \\\"quic hp\\\" \\\"\\\" 16)\\n$ echo skey: $server_data_key\\n$ echo siv: $server_data_iv\\n$ echo shp: $server_data_hp\\n$ echo ckey: $client_data_key\\n$ echo civ: $client_data_iv\\n$ echo chp: $client_data_hp\\n\\nskey: fd8c7da9de1b2da4d2ef9fd5188922d0\\nsiv: 02f6180e4f4aa456d7e8a602\\nshp: b7f6f021453e52b58940e4bba72a35d4\\nckey: e010a295f0c2864f186b2a7e8fdc9ed7\\nciv: eb3fbc384a3199dcf6b4c808\\nchp: 8a6a38bc5cc40cb482a254dac68c9d2f\"\n    }\n  },\n  \"由此我们可以得到以下密钥以及向量：\",\n  {\n    \"Tag\": \"ul\",\n    \"children\": [\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"客户端会话密钥: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"e010a295f0c2864f186b2a7e8fdc9ed7\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"客户端会话向量: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"eb3fbc384a3199dcf6b4c808\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"客户端会话数据包头保护密钥: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"8a6a38bc5cc40cb482a254dac68c9d2f\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"服务器端会话密钥: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"fd8c7da9de1b2da4d2ef9fd5188922d0\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"服务器端会话向量: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"02f6180e4f4aa456d7e8a602\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"服务器端会话数据包头保护密钥: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"b7f6f021453e52b58940e4bba72a35d4\"\n          }\n        ]\n      }\n    ]\n  }\n]\n"
  },
  {
    "path": "src/QUIC/serverApplicationPacket1.json",
    "content": "[\n  \"服务器端发送其第一个握手后的数据包，即第一个实际包含应用内容的会话过程数据包。其中包含内容为 \\\"pong\\\" 的流数据响应。\",\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"数据包头字节\",\n          {\n            \"props\": {\n              \"className\": \"bytes protected\",\n              \"title\": \"被数据包头保护密钥加密\"\n            },\n            \"content\": \"49\"\n          },\n          {\n            \"props\": {\n              \"className\": \"bytes unprotected\"\n            },\n            \"content\": \"40\"\n          },\n          [\n            \"数据包以一个头字节开始，该字节应用了头保护。头部保护用于隐藏数据包序号和其他信息，使其不被外界观察到。\",\n            \"包头保护是通过用\\\"数据包头保护密钥\\\"对每个数据包的有效载荷的样本进行加密，然后将每个数据包中的某些比特和字节与所得数据进行异或(XOR)操作得到的。对于像这样的\\\"长\\\"格式数据包，受保护的部分是这个字节的低 4 位，以及数据包序号的字节（见下文）。\",\n            \"这里有一个关于如何计算出加密头字节的例子：\",\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### \\\"server header protection key\\\" from application keys calc step above\\n$ key=b7f6f021453e52b58940e4bba72a35d4\\n### sample is taken from 16 bytes of payload starting\\n### 4 bytes past the first byte of the packet number\\n$ sample=4057c883e94d9c296baa8ca0ea6e3a21\\n$ echo $sample | xxd -r -p | openssl aes-128-ecb -K $key | head -c 5 | xxd -p\\n\\n09cd79a059\\n\\n### first byte of result is xor'd into lower 5 bits of this byte,\\n### remaining bytes are xor'd one-for-one into the bytes of\\n### the packet number (which in this packet is only one byte)\"\n              }\n            },\n            \"解密出的字节 0x40 中的位有以下含义：\",\n            {\n              \"Tag\": \"Table\",\n              \"props\": {\n                \"headers\": [\"\", \"值\", \"含义\"],\n                \"data\": [\n                  [\"高位\", \"0\", \"Short Header 格式\"],\n                  [\"\", \"1\", \"固定位(总是被置1)\"],\n                  [\n                    \"\",\n                    \"0\",\n                    \"可选的 \\\"Spin\\\" 位。用于允许观察者测量 RTT，但 QUIC 未使用。\"\n                  ],\n                  [\"\", \"00\", \"保留(总是被置0)\"],\n                  [\"\", \"0\", \"密钥相位位(key phase bit), 密钥发生轮替时置1\"],\n                  [\n                    \"低位\",\n                    \"00\",\n                    \"数据包序号长度(表示下面的 \\\"数据包序号\\\" 将有一个字节的长度，默认值)\"\n                  ]\n                ]\n              }\n            }\n          ]\n        ],\n        [\n          \"目的地连接标识 ID\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"63 5f 63 69 64\"\n          },\n          [\n            \"客户端的标识 ID (客户端的源连接标识 ID)\",\n            \"注意此时标识 ID 的长度(以及本应在后面的源连接标识 ID)已经省略。对等端(peer)在这之前应该且必须知道标识 ID 长度，该长度在会话过程中要么一直被省略，要么在标识 ID 中编码长度。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"63 5f 63 69 64 - 实际的目的地连接标识 ID(\\\"c_cid\\\")\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"数据包序号\",\n          {\n            \"props\": {\n              \"className\": \"bytes protected\",\n              \"title\": \"被数据包头保护密钥加密\"\n            },\n            \"content\": \"cd\"\n          },\n          {\n            \"props\": {\n              \"className\": \"bytes unprotected\"\n            },\n            \"content\": \"00\"\n          },\n          [\n            \"该字节应用了头保护。详见 \\\"数据包头字节\\\"。\",\n            \"这个字节的未受保护的值为 0x00，表明它是第 0 号包，或者说是客户端发送的第一个\\\"会话\\\"包。\",\n            \"这个数据也有可能被截断。发送端点通过几个步骤截断：①计算已发送的最高的序号和未确认的最低的序号之间的差值；②出于安全考虑将差值加倍并四舍五入；③计算它在明确表示两端之间的序号的前提下可以从序号的高位删除的字节数；④截断编码后的数据包序号直至长度满足该字节数。而接收端点根据会它最近看到的数据包号码填入完整的序号。\",\n            {\n              \"children\": [\n                \"由于我们的例子对话发送的数据包很小(少于 64 个字节)，所以这种截断不会在本文中出现。详情见 \",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://www.rfc-editor.org/rfc/rfc9000.html#section-17.1\"\n                  },\n                  \"content\": \"RFC 9000\"\n                },\n                \"。\"\n              ]\n            }\n          ]\n        ],\n        [\n          \"加密的数据载荷\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\",\n              \"title\": \"被\\\"会话\\\"加密\"\n            },\n            \"content\": \"9a 64 12 40 57 c8 83 e9 4d 9c 29 6b aa 8c a0\"\n          },\n          [\"这些数据使用服务器端的\\\"会话\\\"进行加密。\"]\n        ],\n        [\n          \"AEAD 鉴别标签\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"ea 6e 3a 21 fa af 99 af 2f e1 03 21 69 20 57 d2\"\n          },\n          [\n            {\n              \"children\": [\n                \"这是 \",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://zhuanlan.zhihu.com/p/28566058\"\n                  },\n                  \"content\": \"AEAD 算法\"\n                },\n                \"的鉴别标签，用以确认加密数据和数据包头的完整性。它由加密算法产生，并由解密算法消耗。\"\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"data\": [\n        [\n          \"\",\n          {\n            \"props\": {\n              \"className\": \"decryption-header\"\n            },\n            \"content\": \"解密后的数据载荷\"\n          },\n          [\n            { \"Tag\": \"h4\", \"content\": \"解密\" },\n            \"数据被 \\\"服务器端生成会话密钥\\\" 步骤中产生的会话密钥和会话向量(IVs)加密。IVs 通过对密钥和数据包序号进行异或操作生成。在例子中 IV 为 0。\",\n            \"数据包开头的 16 字节(数据包头)还会作为解密过程解密成功时必须满足的认证条件。\",\n            {\n              \"children\": [\n                \"openssl 命令行工具还不支持 AEAD 算法加解密(AEAD ciphers)，你可以使用作者的命令行工具来\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://quic.xargs.org/files/aes_128_gcm_decrypt.c\"\n                  },\n                  \"content\": \"解密\"\n                },\n                \"和\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://quic.xargs.org/files/aes_128_gcm_encrypt.c\"\n                  },\n                  \"content\": \"加密\"\n                },\n                \"这些数据。\"\n              ]\n            },\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### from the \\\"Application Keys Calc\\\" step\\n$ key=fd8c7da9de1b2da4d2ef9fd5188922d0\\n$ iv=02f6180e4f4aa456d7e8a602\\n### from this record\\n$ recdata=40635f63696400\\n$ authtag=ea6e3a21faaf99af2fe10321692057d2\\n$ recordnum=0\\n### may need to add -I and -L flags for include and lib dirs\\n$ cc -o aes_128_gcm_decrypt aes_128_gcm_decrypt.c -lssl -lcrypto\\n$ cat /tmp/msg1   | ./aes_128_gcm_decrypt $iv $recordnum $key $recdata $authtag   | hexdump -C\\n\\n00000000  02 00 12 00 00 1e 0f 00  00 40 04 70 6f 6e 67     |.........@.pong|\"\n              }\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"ACK 帧\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"02 00 12 00 00\"\n          },\n          [\n            \"服务器端确认收到客户端的会话数据包0。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"02 - 表明帧类型 ACK\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 - largest_acknowledged: 被确认的最大数据包\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"children\": [\n                    \"12 - ack_delay: 变长的整数。给出这个 ack 被延迟发送的时间，单位是微秒。\",\n                    \"通过计算式子：2\",\n                    {\n                      \"Tag\": \"sup\",\n                      \"content\": \"ack_delay_exponent\"\n                    },\n                    \" 得到。其中 ack_delay_exponent = 18 * 8 = 144 (µseconds)。\"\n                  ]\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 - ack_range_count: 额外的 ACK 帧数据长度 0x(0)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 - first_ack_range: 可变长度的整数。给出在 largest_acknowledged 之前被确认过的数据包数量。\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"HANDSHAKE_DONE 帧\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"1e\"\n          },\n          [\n            \"服务器向客户确认，握手已经完成。该帧除了其类型外没有其他内容。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"1e - 表明帧类型 HANDSHAKE_DONE\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"流数据帧标识\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"0f\"\n          },\n          [\n            \"服务器端用此表明它发送的是数据流。流是 QUIC 连接中发送所有应用数据的机制，类似于单个 TCP 连接。\",\n            \"数据流的帧类型标识是一个范围在 0x8 到 0xf 的数字(二进制表示应该是：0b00001xxx)，外加额外的可变位提供关于流的额外信息：\",\n            {\n              \"Tag\": \"Table\",\n              \"props\": {\n                \"headers\": [\"位掩码\", \"含义\"],\n                \"data\": [\n                  [\"0x4\", \"OFF: 该帧中存在一个\\\"偏移\\\"字段(否则偏移量为0)\"],\n                  [\n                    \"0x2\",\n                    \"LEN：该帧中存在一个\\\"长度\\\"字段(否则对等端应消耗帧中的所有数据)。\"\n                  ],\n                  [\n                    \"0x1\",\n                    \"FIN：该帧包含该数据流的最终数据，发送方已经完成了对它的写入。\"\n                  ]\n                ]\n              }\n            },\n            \"在本文例子中，发送方表示所有三个含义：将有一个偏移字段、一个长度字段，以及此帧包含该条数据流的最终数据。\"\n          ]\n        ],\n        [\n          \"流的 ID 序号\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"00\"\n          },\n          [\n            \"客户端给出流的 ID 序号。ID 序号按顺序增加，且最后两个比特表示流的类型和方向：\",\n            {\n              \"Tag\": \"Table\",\n              \"props\": {\n                \"headers\": [\"位掩码\", \"含义\"],\n                \"data\": [\n                  [\"0x2\", \"表示流是双向的(0)还是单向的(1)。\"],\n                  [\n                    \"0x1\",\n                    \"表示流是由客户端(0)还是服务器端(1)打开的。\"\n                  ]\n                ]\n              }\n            },\n            \"在本文例子中，表示流的 ID 序号为 0，且是客户端打开的双向数据流。\"\n          ]\n        ],\n        [\n          \"流的偏移\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"00\"\n          },\n          [\n            \"一个可变长度的整数，表示流数据的偏移量。\",\n            \"在本文例子中，一个单字节的整数显示偏移量为 0。\"\n          ]\n        ],\n        [\n          \"流的长度\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"40 04\"\n          },\n          [\n            \"一个可变长度的整数，表示流数据的长度。\",\n            \"在本文例子中，前两个比特(0 1)表示一个两字节的整数，其余比特表明流的长度为 4 字节。\"\n          ]\n        ],\n        [\n          \"数据\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"70 69 6e 67\"\n          },\n          [\n            \"服务器对客户端的响应数据，字符串\\\"pong\\\"\"\n          ]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/QUIC/serverApplicationPacket2.json",
    "content": "[\n  \"服务器在确认客户端已收到所有待处理数据后，关闭连接。\",\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"数据包头字节\",\n          {\n            \"props\": {\n              \"className\": \"bytes protected\",\n              \"title\": \"被数据包头保护密钥加密\"\n            },\n            \"content\": \"54\"\n          },\n          {\n            \"props\": {\n              \"className\": \"bytes unprotected\"\n            },\n            \"content\": \"40\"\n          },\n          [\n            \"数据包以一个头字节开始，该字节应用了头保护。头部保护用于隐藏数据包序号和其他信息，使其不被外界观察到。\",\n            \"包头保护是通过用\\\"数据包头保护密钥\\\"对每个数据包的有效载荷的样本进行加密，然后将每个数据包中的某些比特和字节与所得数据进行异或(XOR)操作得到的。对于像这样的\\\"长\\\"格式数据包，受保护的部分是这个字节的低 4 位，以及数据包序号的字节（见下文）。\",\n            \"这里有一个关于如何计算出加密头字节的例子：\",\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### \\\"server header protection key\\\" from application keys calc step above\\n$ key=b7f6f021453e52b58940e4bba72a35d4\\n### sample is taken from 16 bytes of payload starting\\n### 4 bytes past the first byte of the packet number\\n$ sample=ffeb17b67ec27f97e50d271dc702d92c\\n$ echo $sample | xxd -r -p | openssl aes-128-ecb -K $key | head -c 5 | xxd -p\\n\\nf494fdfbb6\\n\\n### first byte of result is xor'd into lower 5 bits of this byte,\\n### remaining bytes are xor'd one-for-one into the bytes of\\n### the packet number (which in this packet is only one byte)\"\n              }\n            },\n            \"解密出的字节 0x40 中的位有以下含义：\",\n            {\n              \"Tag\": \"Table\",\n              \"props\": {\n                \"headers\": [\"\", \"值\", \"含义\"],\n                \"data\": [\n                  [\"高位\", \"0\", \"Short Header 格式\"],\n                  [\"\", \"1\", \"固定位(总是被置1)\"],\n                  [\n                    \"\",\n                    \"0\",\n                    \"可选的 \\\"Spin\\\" 位。用于允许观察者测量 RTT，但 QUIC 未使用。\"\n                  ],\n                  [\"\", \"00\", \"保留(总是被置0)\"],\n                  [\"\", \"0\", \"密钥相位位(key phase bit), 密钥发生轮替时置1\"],\n                  [\n                    \"低位\",\n                    \"00\",\n                    \"数据包序号长度(表示下面的 \\\"数据包序号\\\" 将有一个字节的长度，默认值)\"\n                  ]\n                ]\n              }\n            }\n          ]\n        ],\n        [\n          \"目的地连接标识 ID\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"63 5f 63 69 64\"\n          },\n          [\n            \"客户端的标识 ID (客户端的源连接标识 ID)\",\n            \"注意此时标识 ID 的长度(以及本应在后面的源连接标识 ID)已经省略。对等端(peer)在这之前应该且必须知道标识 ID 长度，该长度在会话过程中要么一直被省略，要么在标识 ID 中编码长度。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"63 5f 63 69 64 - 实际的目的地连接标识 ID(\\\"s_cid\\\")\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"数据包序号\",\n          {\n            \"props\": {\n              \"className\": \"bytes protected\",\n              \"title\": \"被数据包头保护密钥加密\"\n            },\n            \"content\": \"95\"\n          },\n          {\n            \"props\": {\n              \"className\": \"bytes unprotected\"\n            },\n            \"content\": \"01\"\n          },\n          [\n            \"该字节应用了头保护。详见 \\\"数据包头字节\\\"。\",\n            \"这个字节的未受保护的值为 0x01，表明它是第 1 号包，或者说是服务器端发送的第二个\\\"会话\\\"包。\",\n            \"这个数据也有可能被截断。发送端点通过几个步骤截断：①计算已发送的最高的序号和未确认的最低的序号之间的差值；②出于安全考虑将差值加倍并四舍五入；③计算它在明确表示两端之间的序号的前提下可以从序号的高位删除的字节数；④截断编码后的数据包序号直至长度满足该字节数。而接收端点根据会它最近看到的数据包号码填入完整的序号。\",\n            {\n              \"children\": [\n                \"由于我们的例子对话发送的数据包很小(少于 64 个字节)，所以这种截断不会在本文中出现。详情见 \",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://www.rfc-editor.org/rfc/rfc9000.html#section-17.1\"\n                  },\n                  \"content\": \"RFC 9000\"\n                },\n                \"。\"\n              ]\n            }\n          ]\n        ],\n        [\n          \"加密的数据载荷\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\",\n              \"title\": \"被\\\"会话密钥\\\"加密\"\n            },\n            \"content\": \"18 c4 a5 ff eb 17 b6 7e c2 7f 97 e5 0d 27 1d c7 02 d9 2c ef b0\"\n          },\n          [\"这些数据使用服务器端的\\\"会话密钥\\\"进行加密。\"]\n        ],\n        [\n          \"AEAD 鉴别标签\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"68 8b e9 fd 7b 30 2d 9e b4 7c df 1f c4 cd 9a ac\"\n          },\n          [\n            {\n              \"children\": [\n                \"这是 \",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://zhuanlan.zhihu.com/p/28566058\"\n                  },\n                  \"content\": \"AEAD 算法\"\n                },\n                \"的鉴别标签，用以确认加密数据和数据包头的完整性。它由加密算法产生，并由解密算法消耗。\"\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"data\": [\n        [\n          \"\",\n          {\n            \"props\": {\n              \"className\": \"decryption-header\"\n            },\n            \"content\": \"解密后的数据载荷\"\n          },\n          [\n            { \"Tag\": \"h4\", \"content\": \"解密\" },\n            \"数据被 \\\"客户端生成会话密钥\\\" 步骤中产生的会话密钥和会话向量(IVs)加密。IVs 通过对密钥和数据包序号进行异或操作生成。在例子中 IV 为 0。\",\n            \"数据包开头的 16 字节(数据包头)还会作为解密过程解密成功时必须满足的认证条件。\",\n            {\n              \"children\": [\n                \"openssl 命令行工具还不支持 AEAD 算法加解密(AEAD ciphers)，你可以使用作者的命令行工具来\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://quic.xargs.org/files/aes_128_gcm_decrypt.c\"\n                  },\n                  \"content\": \"解密\"\n                },\n                \"和\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://quic.xargs.org/files/aes_128_gcm_encrypt.c\"\n                  },\n                  \"content\": \"加密\"\n                },\n                \"这些数据。\"\n              ]\n            },\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### from the \\\"Application Keys Calc\\\" step\\n$ key=fd8c7da9de1b2da4d2ef9fd5188922d0\\n$ iv=02f6180e4f4aa456d7e8a602\\n### from this record\\n$ recdata=40635f63696401\\n$ authtag=688be9fd7b302d9eb47cdf1fc4cd9aac\\n$ recordnum=1\\n### may need to add -I and -L flags for include and lib dirs\\n$ cc -o aes_128_gcm_decrypt aes_128_gcm_decrypt.c -lssl -lcrypto\\n$ cat /tmp/msg1   | ./aes_128_gcm_decrypt $iv $recordnum $key $recdata $authtag   | hexdump -C\\n\\n00000000  1c 00 00 11 67 72 61 63  65 66 75 6c 20 73 68 75  |....graceful shu|\\n00000010  74 64 6f 77 6e                                    |tdown|\"\n              }\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"CONNECTION_CLOSE 帧\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"1c 00 00 11 67 72 61 63 65 66 75 6c 20 73 68 75 74 64 6f 77 6e\"\n          },\n          [\n            \"服务器发送指示，关闭连接。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"1c - 表明帧类型 CONNECTION_CLOSE\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 - error_code: 没有错误\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 - 触发错误的帧类型 (0 = unknown)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"11 - 可变长度的整数。表明关闭连接的原因的长度 0x11 (17) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"67 72 .. 77 6e - 关闭连接的原因: 字符串\\\"graceful shutdown\\\"\"\n                }\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/QUIC/serverHandshakeKeysCalc.json",
    "content": "[\n  \"服务器现在拥有了用于计算握手时的加密密钥的所有信息。在这个计算中，服务器使用了以下信息：\",\n  {\n    \"Tag\": \"ul\",\n    \"children\": [\n      {\n        \"Tag\": \"li\",\n        \"content\": \"客户端公钥(提取自 ClientHello 数据包)\"\n      },\n      {\n        \"Tag\": \"li\",\n        \"content\": \"服务器端私钥(之前准备密钥交换时生成)\"\n      },\n      {\n        \"Tag\": \"li\",\n        \"content\": \"ClientHello 和 ServerHello 的 SHA256 哈希值\"\n      }\n    ]\n  },\n  \"首先，服务器需要找到共享的密文(shared secret)，即密钥交换步骤的最终值。服务器通过使用 curve25519 算法将客户端的公钥乘以服务器的私钥，即可发现 32 字节的最终值是：\",\n  {\n    \"Tag\": \"pre\",\n    \"props\": {\n      \"className\": \"ind2\"\n    },\n    \"children\": [\n      {\n        \"Tag\": \"code\",\n        \"props\": { \"className\": \"longboi\" },\n        \"content\": \"df4a291baa1eb7cfa6934b29b474baad2697e29f1f920dcc77c8a0a088447624\"\n      }\n    ]\n  },\n  {\n    \"children\": [\n      \"你可以使用\",\n      {\n        \"Tag\": \"a\",\n        \"props\": { \"href\": \"https://quic.xargs.org/files/curve25519-mult.c\" },\n        \"content\": \"原作者的脚本\"\n      },\n      \"快速验证结果：\"\n    ]\n  },\n  {\n    \"Tag\": \"CodeSample\",\n    \"props\": {\n      \"code\": \"$ cc -o curve25519-mult curve25519-mult.c\\n$ ./curve25519-mult server-ephemeral-private.key                     client-ephemeral-public.key | hexdump\\n\\n0000000 df 4a 29 1b aa 1e b7 cf a6 93 4b 29 b4 74 ba ad\\n0000010 26 97 e2 9f 1f 92 0d cc 77 c8 a0 a0 88 44 76 24\"\n    }\n  },\n  \"然后，服务器计算到此为止的所有握手信息(ClientHello 和 ServerHello)的 SHA256 哈希值(不包括 6 字节的 加密帧)。这个 \\\"hello_hash\\\" 是\",\n  {\n    \"Tag\": \"pre\",\n    \"props\": {\n      \"className\": \"ind2\"\n    },\n    \"children\": [\n      {\n        \"Tag\": \"code\",\n        \"props\": { \"className\": \"longboi\" },\n        \"content\": \"ff788f9ed09e60d8142ac10a8931cdb6a3726278d3acdba54d9d9ffc7326611b\"\n      }\n    ]\n  },\n  {\n    \"Tag\": \"CodeSample\",\n    \"props\": {\n      \"code\": \"$ cat crypto_clienthello crypto_serverhello | openssl sha256\\nff788f9ed09e60d8142ac10a8931cdb6a3726278d3acdba54d9d9ffc7326611b\"\n    }\n  },\n  \"然后，我们将哈希值和共享的签名信息进行一些密钥派生操作(key derivation operations)，以防止可能的攻击：\",\n  {\n    \"Tag\": \"pre\",\n    \"children\": [\n      {\n        \"Tag\": \"code\",\n        \"props\": { \"className\": \"longboi\" },\n        \"content\": \"early_secret = HKDF-Extract(salt=00, key=00...)\\nempty_hash = SHA256(\\\"\\\")\\nderived_secret = HKDF-Expand-Label(key: early_secret, label: \\\"derived\\\", ctx: empty_hash, len: 32)\\nhandshake_secret = HKDF-Extract(salt: derived_secret, key: shared_secret)\\nclient_secret = HKDF-Expand-Label(key: handshake_secret, label: \\\"c hs traffic\\\", ctx: hello_hash, len: 32)\\nserver_secret = HKDF-Expand-Label(key: handshake_secret, label: \\\"s hs traffic\\\", ctx: hello_hash, len: 32)\\nclient_key = HKDF-Expand-Label(key: client_secret, label: \\\"quic key\\\", ctx: \\\"\\\", len: 16)\\nserver_key = HKDF-Expand-Label(key: server_secret, label: \\\"quic key\\\", ctx: \\\"\\\", len: 16)\\nclient_iv = HKDF-Expand-Label(key: client_secret, label: \\\"quic iv\\\", ctx: \\\"\\\", len: 12)\\nserver_iv = HKDF-Expand-Label(key: server_secret, label: \\\"quic iv\\\", ctx: \\\"\\\", len: 12)\"\n      }\n    ]\n  },\n  {\n    \"children\": [\n      \"在命令行中使用\",\n      {\n        \"Tag\": \"a\",\n        \"props\": { \"href\": \"https://quic.xargs.org/files/hkdf.sh\" },\n        \"content\": \"原作者制作的 HKDF 命令行脚本\"\n      },\n      \"，你也可以自己试试：\"\n    ]\n  },\n  {\n    \"Tag\": \"CodeSample\",\n    \"props\": {\n      \"code\": \"$ hello_hash=ff788f9ed09e60d8142ac10a8931cdb6a3726278d3acdba54d9d9ffc7326611b\\n$ shared_secret=df4a291baa1eb7cfa6934b29b474baad2697e29f1f920dcc77c8a0a088447624\\n$ zero_key=0000000000000000000000000000000000000000000000000000000000000000\\n$ early_secret=$(./hkdf extract 00 $zero_key)\\n$ empty_hash=$(openssl sha256 < /dev/null | sed -e 's/.* //')\\n$ derived_secret=$(./hkdf expandlabel $early_secret \\\"derived\\\" $empty_hash 32)\\n$ handshake_secret=$(./hkdf extract $derived_secret $shared_secret)\\n$ csecret=$(./hkdf expandlabel $handshake_secret \\\"c hs traffic\\\" $hello_hash 32)\\n$ ssecret=$(./hkdf expandlabel $handshake_secret \\\"s hs traffic\\\" $hello_hash 32)\\n$ client_handshake_key=$(./hkdf expandlabel $csecret \\\"quic key\\\" \\\"\\\" 16)\\n$ server_handshake_key=$(./hkdf expandlabel $ssecret \\\"quic key\\\" \\\"\\\" 16)\\n$ client_handshake_iv=$(./hkdf expandlabel $csecret \\\"quic iv\\\" \\\"\\\" 12)\\n$ server_handshake_iv=$(./hkdf expandlabel $ssecret \\\"quic iv\\\" \\\"\\\" 12)\\n$ client_handshake_hp=$(./hkdf expandlabel $csecret \\\"quic hp\\\" \\\"\\\" 16)\\n$ server_handshake_hp=$(./hkdf expandlabel $ssecret \\\"quic hp\\\" \\\"\\\" 16)\\n$ echo ckey: $client_handshake_key\\n$ echo civ: $client_handshake_iv\\n$ echo chp: $client_handshake_hp\\n$ echo skey: $server_handshake_key\\n$ echo siv: $server_handshake_iv\\n$ echo shp: $server_handshake_hp\\n\\nckey: 30a7e816f6a1e1b3434cf39cf4b415e7\\nciv: 11e70a5d1361795d2bb04465\\nchp: 84b3c21cacaf9f54c885e9a506459079\\nskey: 17abbf0a788f96c6986964660414e7ec\\nsiv: 09597a2ea3b04c00487e71f3\\nshp: 2a18061c396c2828582b41b0910ed536\"\n    }\n  },\n  \"由此我们可以得到以下密钥以及向量：\",\n  {\n    \"Tag\": \"ul\",\n    \"children\": [\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"客户端握手时密钥: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"30a7e816f6a1e1b3434cf39cf4b415e7\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"客户端握手时向量: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"11e70a5d1361795d2bb04465\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"客户端握手时数据包头保护密钥: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"84b3c21cacaf9f54c885e9a506459079\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"服务器端握手时密钥: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"17abbf0a788f96c6986964660414e7ec\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"服务器端握手时向量: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"09597a2ea3b04c00487e71f3\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"服务器端握手时数据包头保护密钥: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"2a18061c396c2828582b41b0910ed536\"\n          }\n        ]\n      }\n    ]\n  }\n]\n"
  },
  {
    "path": "src/QUIC/serverHandshakePacket1.json",
    "content": "[\n  \"紧跟着，服务器需要发送一个用于 \\\"握手\\\" 的数据包。这个数据包包含 TLS 需求的握手记录，用于继续 TLS 1.3 加密会话的协商。\",\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"数据包头字节\",\n          {\n            \"props\": {\n              \"className\": \"bytes protected\",\n              \"title\": \"被数据包头保护密钥加密\"\n            },\n            \"content\": \"ed\"\n          },\n          {\n            \"props\": {\n              \"className\": \"bytes unprotected\"\n            },\n            \"content\": \"e0\"\n          },\n          [\n            \"数据包以一个头字节开始，该字节应用了头保护。头部保护用于隐藏数据包序号和其他信息，使其不被外界观察到。\",\n            \"包头保护是通过用\\\"数据包头保护密钥\\\"对每个数据包的有效载荷的样本进行加密，然后将每个数据包中的某些比特和字节与所得数据进行异或(XOR)操作得到的。对于像这样的\\\"长\\\"格式数据包，受保护的部分是这个字节的低 4 位，以及数据包序号的字节（见下文）。\",\n            \"这里有一个关于如何计算出加密头字节的例子：\",\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### \\\"server header protection key\\\" from calc step above\\n$ key=2a18061c396c2828582b41b0910ed536\\n### sample is taken from 16 bytes of payload starting\\n### 4 bytes past the first byte of the packet number\\n$ sample=296209dff2d02d3d50af692176dd4d50\\n$ echo $sample | xxd -r -p | openssl aes-128-ecb -K $key | head -c 5 | xxd -p\\n\\nddb7ce7613\\n\\n### first byte of result is xor'd into lower 4 bits of this byte,\\n### remaining bytes are xor'd one-for-one into the bytes of\\n### the packet number (which in this packet is only one byte)\"\n              }\n            },\n            \"解密出的字节 0xE0 中的位有以下含义：\",\n            {\n              \"Tag\": \"Table\",\n              \"props\": {\n                \"headers\": [\"\", \"值\", \"含义\"],\n                \"data\": [\n                  [\"高位\", \"1\", \"Long Header 格式\"],\n                  [\"\", \"1\", \"固定位(总是被置1)\"],\n                  [\"\", \"10\", \"数据包类型：握手\"],\n                  [\"\", \"00\", \"保留(总是被置0)\"],\n                  [\n                    \"低位\",\n                    \"00\",\n                    \"数据包序号长度(表示下面的 \\\"数据包序号\\\" 将有一个字节的长度，默认值)\"\n                  ]\n                ]\n              }\n            }\n          ]\n        ],\n        [\n          \"QUIC 版本号\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 00 00 01\"\n          },\n          [\"QUIC的版本是：0x1\"]\n        ],\n        [\n          \"目的地连接标识 ID\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"05 63 5f 63 69 64\"\n          },\n          [\n            \"服务器回传客户端的标识 ID (客户端的源连接标识 ID)。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"05 - 紧跟着的目的地连接标识 ID 的长度(5bytes)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"63 5f 63 69 64 - 实际的目的地连接标识 (\\\"c_cid\\\")\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"源连接标识 ID\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"05 73 5f 63 69 64\"\n          },\n          [\n            \"服务器使用这个字段来向客户端表示它所选择的连接 ID。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"05 - 紧跟着的源连接标识 ID 的长度(5bytes)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"73 5f 63 69 64 - 实际的源连接标识 ID (\\\"s_cid\\\")\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"数据包长度\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"44 14\"\n          },\n          [\n            \"服务器端表示数据包中有多少字节的加密有效载荷。这个字段是一个长度可变的整数——第一个字节的前两位表示该整数中总共有多少个字节。\",\n            \"此时，第一个字节以 \\\"0 1\\\"(0x4)这两个位开始，表示该整数共两个字节。其余的位给出数字 0x414，表示 1044 个字节的有效载荷。\"\n          ]\n        ],\n        [\n          \"数据包序号\",\n          {\n            \"props\": {\n              \"className\": \"bytes protected\",\n              \"title\": \"被数据包头保护密钥加密\"\n            },\n            \"content\": \"b7\"\n          },\n          {\n            \"props\": {\n              \"className\": \"bytes unprotected\"\n            },\n            \"content\": \"00\"\n          },\n          [\n            \"该字节应用了头保护。详见 \\\"数据包头字节\\\"。\",\n            \"这个字节的未受保护的值为 0x00，表明它是第 0 号包，或者说是服务器端发送的第一个\\\"握手\\\"包。\",\n            \"这个数据也有可能被截断。发送端点通过几个步骤截断：①计算已发送的最高的序号和未确认的最低的序号之间的差值；②出于安全考虑将差值加倍并四舍五入；③计算它在明确表示两端之间的序号的前提下可以从序号的高位删除的字节数；④截断编码后的数据包序号直至长度满足该字节数。而接收端点根据会它最近看到的数据包号码填入完整的序号。\",\n            {\n              \"children\": [\n                \"由于我们的例子对话发送的数据包很小(少于 64 个字节)，所以这种截断不会在本文中出现。详情见 \",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://www.rfc-editor.org/rfc/rfc9000.html#section-17.1\"\n                  },\n                  \"content\": \"RFC 9000\"\n                },\n                \"。\"\n              ]\n            }\n          ]\n        ],\n        [\n          \"加密的数据载荷\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\",\n              \"title\": \"被\\\"握手时密钥\\\"加密\"\n            },\n            \"content\": \"dd 73 ae 29 62 09 df f2 d0 2d 3d 50 af 69 21 76 dd 4d 50 9f e8 cb 1b 46 e4 5b 09 36 4d 81 5f a7 a5 74 8e 21 80 da d2 b7 b6 68 ca b8 6f bd c2 98 8c 45 cb b8 51 dd cf 16 01 b7 80 d7 48 b9 ee 64 1e bc be 20 12 6e 32 26 7e 66 4d 2f 37 cf 53 b7 53 d1 24 71 7c 2e 13 c4 8a 09 e3 42 8b 11 dc 73 ba eb d4 98 e8 ca f5 be ce fe a7 60 d0 e7 a5 cd b7 6b 52 bc b1 92 29 97 3e 5d 09 aa 05 5e 9c 97 18 dc 58 14 54 77 5c 58 ec dd 5e e7 e7 72 78 f5 60 10 70 40 41 62 a7 9e e8 c5 96 45 d6 ca 24 a2 00 18 6a e9 9c e4 7e ac e1 cf c9 52 7b 24 ae 8b c6 cc db ac b7 9b 81 c9 1a 26 95 47 07 ba 35 cb a0 ca e9 af f4 18 c6 e0 8d a6 50 61 63 a3 9f 19 b6 76 a6 6a c1 74 e3 29 5f 1a b9 ea 73 83 a9 c2 85 d7 3e 95 75 8d c9 bd 8d a9 07 34 a9 fe df d7 e1 f7 4d 2b 69 c7 0b f7 39 a4 8c 5a 5d 0a fa 0b fa 16 03 47 1b 0c 61 a9 ca de 12 0b 39 86 a6 ce 02 95 be 82 28 c6 92 70 13 b0 6d a5 8d 31 99 62 31 b9 e3 15 0b b5 82 70 96 0e 61 cb c6 69 8a 2f 13 79 a2 25 84 65 da 73 25 b3 49 c6 cd 55 d1 05 fd 54 85 fd 0a c7 9a 1d f1 db ba 7f 85 b4 9b 72 36 5b fa b9 d5 78 e0 1d cb ff 85 15 a6 32 fd 70 01 38 2e d9 0f 6c dc b1 7d b9 9a 33 fa 11 81 f6 f6 1a 89 e7 83 cf b0 42 fc 0f 2f 67 cd b6 0e 89 f2 63 88 56 81 ae 64 5a 1c 7a b1 59 0e b2 f8 46 9f 46 0f 04 e0 9f ea 2a 3a 41 1b 49 86 63 01 0b 3c 38 2a 3f 25 83 7c 2c 70 86 af 5a 9a d2 90 cf 3c cf 1a c6 eb 0f 44 55 35 e8 b0 0a 55 7c 87 a5 3d 93 07 14 62 a0 bc 22 61 4e 5c 3a e0 84 17 b7 20 a7 36 c1 ad 48 ea 37 75 cd 0f 00 9f 0c 57 50 0e 0b b2 e7 e9 c5 3f 83 69 9a 47 e5 f1 3b b2 07 72 ab 23 50 64 24 b7 6f 6e f9 6a 61 c9 17 22 6e 6e 04 8d e6 f8 24 26 ca 63 ea bf 3b 59 43 af 0b 5f 0d 12 3d 9a f0 45 bb 35 7c ad bd 10 92 ad 0a 1d 75 51 16 2a 3b 4b 48 6c 27 1e 00 24 4b 23 d8 ad ec 81 c9 2e 31 23 9c 75 af 41 cb 07 98 08 57 1b 48 ac b5 07 33 3f fb f1 a4 86 d8 05 3e dc c8 62 b6 a9 bf d3 6a 09 cd db a3 29 1b 9b 8b a1 58 49 34 59 80 5c e2 41 da f5 c1 30 85 99 fc 0e 6e 6e a7 10 30 33 b2 94 cc 7a 5f db 2d 46 54 f1 d4 40 78 25 eb c3 75 ab df b2 cc a1 ab f5 a2 41 34 3d ec 3b 16 5d 32 0a f8 4b c1 fa 21 11 2e fd b9 d4 5c 6c fc 7b 8a 64 42 ff 59 3d 09 21 93 36 fa 07 56 d9 e4 5b ab 4f a6 33 94 a2 a8 80 3d f4 67 8e 79 21 6f df 13 1f 55 82 2f 9e ad 69 4a b7 5e e2 54 96 e6 b7 8c 3b 09 04 66 58 e2 c4 27 dd c4 53 8a f8 de 2a cb 81 39 8b 74 82 83 37 f2 69 cb 03 1d 99 7a 5c f6 3e 11 ab 05 0a a8 ae e1 f0 79 62 dd d7 51 5a b6 0e 19 2e 40 3c 30 03 11 e9 e4 b9 b7 0f 16 15 02 9d 07 fe 1c 23 19 39 02 71 49 f4 fd 29 72 02 3a 55 de 29 35 65 05 fb e7 49 90 8c 62 aa 33 eb 25 9a 39 9b f7 11 b9 2b 61 6c b7 48 de 73 c8 bf ad d5 d4 3e 2d ae 91 6a 7b a0 db 61 df cd 6f af 95 76 08 26 2b 68 34 e3 31 85 b8 d5 59 8f 87 e6 99 2a ac f5 76 96 ad d5 55 8a 7d 96 94 38 1f 5d 7d 65 9d a2 de 95 1b 60 74 78 f6 1d a2 08 a2 4a 07 ba 8d a0 02 58 fa 7f 2f e1 0d ef 61 83 26 7f 5d 38 e0 4c 94 23 00 b9 c8 74 e8 98 3c 1b e1 4e 16 08 ff dc a6 7d 7e 45 13 cc 0c b9 ca b8 1d 63 19 dd 10 74 b2 17 e5 19 54 65 13 1e 06 dd 0b af ab a8 4e b5 2c 22 a4 a8 c6 12 a4 05 fe 6c 87 42 32 e4 a9 34 61 1b c7 3c 56 fe 70 b2 cb 7a 59 6c 1f 53 c7 29 b6 64 3c bd 70 d5 30 fe 31 96 06 9f c0 07 8e 89 fb b7 0d c1 b3 8a b4 e1 77 0c\"\n          },\n          [\"这些数据使用服务器端的\\\"握手时密钥\\\"进行加密。\"]\n        ],\n        [\n          \"AEAD 鉴别标签\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"8f fb 53 31 6d 67 3a 32 b8 92 59 b5 d3 3e 94 ad\"\n          },\n          [\n            {\n              \"children\": [\n                \"这是 \",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://zhuanlan.zhihu.com/p/28566058\"\n                  },\n                  \"content\": \"AEAD 算法\"\n                },\n                \"的鉴别标签，用以确认加密数据和数据包头的完整性。它由加密算法产生，并由解密算法消耗。\"\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"data\": [\n        [\n          \"\",\n          {\n            \"props\": {\n              \"className\": \"decryption-header\"\n            },\n            \"content\": \"解密后的数据载荷\"\n          },\n          [\n            { \"Tag\": \"h4\", \"content\": \"解密\" },\n            \"数据被 \\\"服务器端生成握手密钥\\\" 步骤中产生的握手时密钥和向量(IVs)加密。IVs 通过对密钥和数据包序号进行异或操作生成。在例子中 IV 为 0。\",\n            \"数据包开头的 20 字节(数据包头)还会作为解密过程解密成功时必须满足的认证条件。\",\n            {\n              \"children\": [\n                \"openssl 命令行工具还不支持 AEAD 算法加解密(AEAD ciphers)，你可以使用作者的命令行工具来\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://quic.xargs.org/files/aes_128_gcm_decrypt.c\"\n                  },\n                  \"content\": \"解密\"\n                },\n                \"和\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://quic.xargs.org/files/aes_128_gcm_encrypt.c\"\n                  },\n                  \"content\": \"加密\"\n                },\n                \"这些数据。\"\n              ]\n            },\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### From the \\\"Handshake Keys Calc\\\" step\\n$ key=17abbf0a788f96c6986964660414e7ec\\n$ iv=09597a2ea3b04c00487e71f3\\n### from this record\\n$ recdata=e00000000105635f63696405735f636964441400\\n$ authtag=8ffb53316d673a32b89259b5d33e94ad\\n$ recordnum=0\\n### may need to add -I and -L flags for include and lib dirs\\n$ cc -o aes_128_gcm_decrypt aes_128_gcm_decrypt.c -lssl -lcrypto\\n$ cat /tmp/msg1   | ./aes_128_gcm_decrypt $iv $recordnum $key $recdata $authtag   | hexdump -C\\n\\n00000000  06 00 43 ff 08 00 00 56  00 54 00 10 00 0b 00 09  |..C....V.T......|\\n00000010  08 70 69 6e 67 2f 31 2e  30 00 39 00 41 00 08 00  |.ping/1.0.9.A...|\\n... snip ...\"\n              }\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"加密帧帧头\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"06 00 43 ff\"\n          },\n          [\n            {\n              \"children\": [\n                \"每个 QUIC 数据包在其有效载荷中包含\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://zhuanlan.zhihu.com/p/438579683\"\n                  },\n                  \"content\": \"一个或多个帧的列表\"\n                },\n                \"。例子中的数据包只包含一个帧，即 加密帧。\"\n              ]\n            },\n            \"加密帧帧头的目的是加密字节流，用于建立安全的 TLS 连接(TLS 1.3 握手)。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"06 - 表明这是 加密帧\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 - 可变长度的整数，提供加密帧帧内字节的偏移量(0)。\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"43 ff - 长度可变的整数(前两位表示2字节的整数)，显示加密帧帧内字节长度为 0x3FF(1023) 字节。\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"TLS 记录的加密的扩展\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"08 00 00 56 00 54 00 10 00 0b 00 09 08 70 69 6e 67 2f 31 2e 30 00 39 00 41 00 08 00 01 02 03 04 05 06 07 01 04 80 01 d4 c0 03 04 80 00 ff f7 04 04 80 50 00 00 05 04 80 08 00 00 06 04 80 08 00 00 07 04 80 08 00 00 08 01 02 09 01 02 0a 01 03 0b 01 19 0f 05 73 5f 63 69 64\"\n          },\n          [\"这一记录将在后面详细说明。\"]\n        ],\n        [\n          \"TLS 记录证书\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"0b 00 03 2e 00 00 03 2a 00 03 25 30 82 03 21 30 82 02 09 a0 03 02 01 02 02 08 15 5a 92 ad c2 04 8f 90 30 0d 06 09 2a 86 48 86 f7 0d 01 01 0b 05 00 30 22 31 0b 30 09 06 03 55 04 06 13 02 55 53 31 13 30 11 06 03 55 04 0a 13 0a 45 78 61 6d 70 6c 65 20 43 41 30 1e 17 0d 31 38 31 30 30 35 30 31 33 38 31 37 5a 17 0d 31 39 31 30 30 35 30 31 33 38 31 37 5a 30 2b 31 0b 30 09 06 03 55 04 06 13 02 55 53 31 1c 30 1a 06 03 55 04 03 13 13 65 78 61 6d 70 6c 65 2e 75 6c 66 68 65 69 6d 2e 6e 65 74 30 82 01 22 30 0d 06 09 2a 86 48 86 f7 0d 01 01 01 05 00 03 82 01 0f 00 30 82 01 0a 02 82 01 01 00 c4 80 36 06 ba e7 47 6b 08 94 04 ec a7 b6 91 04 3f f7 92 bc 19 ee fb 7d 74 d7 a8 0d 00 1e 7b 4b 3a 4a e6 0f e8 c0 71 fc 73 e7 02 4c 0d bc f4 bd d1 1d 39 6b ba 70 46 4a 13 e9 4a f8 3d f3 e1 09 59 54 7b c9 55 fb 41 2d a3 76 52 11 e1 f3 dc 77 6c aa 53 37 6e ca 3a ec be c3 aa b7 3b 31 d5 6c b6 52 9c 80 98 bc c9 e0 28 18 e2 0b f7 f8 a0 3a fd 17 04 50 9e ce 79 bd 9f 39 f1 ea 69 ec 47 97 2e 83 0f b5 ca 95 de 95 a1 e6 04 22 d5 ee be 52 79 54 a1 e7 bf 8a 86 f6 46 6d 0d 9f 16 95 1a 4c f7 a0 46 92 59 5c 13 52 f2 54 9e 5a fb 4e bf d7 7a 37 95 01 44 e4 c0 26 87 4c 65 3e 40 7d 7d 23 07 44 01 f4 84 ff d0 8f 7a 1f a0 52 10 d1 f4 f0 d5 ce 79 70 29 32 e2 ca be 70 1f df ad 6b 4b b7 11 01 f4 4b ad 66 6a 11 13 0f e2 ee 82 9e 4d 02 9d c9 1c dd 67 16 db b9 06 18 86 ed c1 ba 94 21 02 03 01 00 01 a3 52 30 50 30 0e 06 03 55 1d 0f 01 01 ff 04 04 03 02 05 a0 30 1d 06 03 55 1d 25 04 16 30 14 06 08 2b 06 01 05 05 07 03 02 06 08 2b 06 01 05 05 07 03 01 30 1f 06 03 55 1d 23 04 18 30 16 80 14 89 4f de 5b cc 69 e2 52 cf 3e a3 00 df b1 97 b8 1d e1 c1 46 30 0d 06 09 2a 86 48 86 f7 0d 01 01 0b 05 00 03 82 01 01 00 59 16 45 a6 9a 2e 37 79 e4 f6 dd 27 1a ba 1c 0b fd 6c d7 55 99 b5 e7 c3 6e 53 3e ff 36 59 08 43 24 c9 e7 a5 04 07 9d 39 e0 d4 29 87 ff e3 eb dd 09 c1 cf 1d 91 44 55 87 0b 57 1d d1 9b df 1d 24 f8 bb 9a 11 fe 80 fd 59 2b a0 39 8c de 11 e2 65 1e 61 8c e5 98 fa 96 e5 37 2e ef 3d 24 8a fd e1 74 63 eb bf ab b8 e4 d1 ab 50 2a 54 ec 00 64 e9 2f 78 19 66 0d 3f 27 cf 20 9e 66 7f ce 5a e2 e4 ac 99 c7 c9 38 18 f8 b2 51 07 22 df ed 97 f3 2e 3e 93 49 d4 c6 6c 9e a6 39 6d 74 44 62 a0 6b 42 c6 d5 ba 68 8e ac 3a 01 7b dd fc 8e 2c fc ad 27 cb 69 d3 cc dc a2 80 41 44 65 d3 ae 34 8c e0 f3 4a b2 fb 9c 61 83 71 31 2b 19 10 41 64 1c 23 7f 11 a5 d6 5c 84 4f 04 04 84 99 38 71 2b 95 9e d6 85 bc 5c 5d d6 45 ed 19 90 94 73 40 29 26 dc b4 0e 34 69 a1 59 41 e8 e2 cc a8 4b b6 08 46 36 a0 00 00\"\n          },\n          [\"这一记录将在后面详细说明。\"]\n        ],\n        [\n          \"TLS 证书验证数据(前一部分)\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"0f 00 01 04 08 04 01 00 0a 99 af 32 a9 e4 06 d7 25 f9 a9 39 6d e5 af 37 56 b7 a8 f6 e4 da d5 85 ab c3 f8 7c 6d 1f c1 5f 5f 00 ab a8 dc a9 d0 5c db 51 d3 c9 35 43 36 56 d8 8b 74 32 00 5e e7 e0 48 03 b2 47 57 44 d7 55 5c f3 de 48 9c c2 16 a4 85 a7 28 b2 18 90 e8 7a a9 41 5d 19 e6 3a 6a 77 9b 9c db b1 28 a8 04 c4 28 b8 27 fa 65 df cd 95 2a ce 54\"\n          },\n          [\"这一记录将在后面详细说明。\"]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/QUIC/serverHandshakePacket2.json",
    "content": "[\n  \"服务器需要再发送另一个 \\\"握手\\\" 数据包，用于继续 TLS 1.3 加密会话的握手过程。\",\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"数据包头字节\",\n          {\n            \"props\": {\n              \"className\": \"bytes protected\",\n              \"title\": \"被数据包头保护密钥加密\"\n            },\n            \"content\": \"e5\"\n          },\n          {\n            \"props\": {\n              \"className\": \"bytes unprotected\"\n            },\n            \"content\": \"e0\"\n          },\n          [\n            \"数据包以一个头字节开始，该字节应用了头保护。头部保护用于隐藏数据包序号和其他信息，使其不被外界观察到。\",\n            \"包头保护是通过用\\\"数据包头保护密钥\\\"对每个数据包的有效载荷的样本进行加密，然后将每个数据包中的某些比特和字节与所得数据进行异或(XOR)操作得到的。对于像这样的\\\"长\\\"格式数据包，受保护的部分是这个字节的低 4 位，以及数据包序号的字节（见下文）。\",\n            \"这里有一个关于如何计算出加密头字节的例子：\",\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### \\\"server header protection key\\\" from calc step above\\n$ key=2a18061c396c2828582b41b0910ed536\\n### sample is taken from 16 bytes of payload starting\\n### 4 bytes past the first byte of the packet number\\n$ sample=19681c3f0f102a30f5e647a3399abf54\\n$ echo $sample | xxd -r -p | openssl aes-128-ecb -K $key | head -c 5 | xxd -p\\n\\ne54e8fcd38\\n\\n### first byte of result is xor'd into lower 4 bits of this byte,\\n### remaining bytes are xor'd one-for-one into the bytes of\\n### the packet number (which in this packet is only one byte)\"\n              }\n            },\n            \"解密出的字节 0xE0 中的位有以下含义：\",\n            {\n              \"Tag\": \"Table\",\n              \"props\": {\n                \"headers\": [\"\", \"值\", \"含义\"],\n                \"data\": [\n                  [\"高位\", \"1\", \"Long Header 格式\"],\n                  [\"\", \"1\", \"固定位(总是被置1)\"],\n                  [\"\", \"10\", \"数据包类型：握手\"],\n                  [\"\", \"00\", \"保留(总是被置0)\"],\n                  [\n                    \"低位\",\n                    \"00\",\n                    \"数据包序号长度(表示下面的 \\\"数据包序号\\\" 将有一个字节的长度，默认值)\"\n                  ]\n                ]\n              }\n            }\n          ]\n        ],\n        [\n          \"QUIC 版本号\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 00 00 01\"\n          },\n          [\"QUIC的版本是：0x1\"]\n        ],\n        [\n          \"目的地连接标识 ID\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"05 63 5f 63 69 64\"\n          },\n          [\n            \"服务器回传客户端的标识 ID (客户端的源连接标识 ID)。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"05 - 紧跟着的目的地连接标识 ID 的长度(5bytes)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"63 5f 63 69 64 - 实际的目的地连接标识 (\\\"c_cid\\\")\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"源连接标识 ID\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"05 73 5f 63 69 64\"\n          },\n          [\n            \"服务器使用这个字段来向客户端表示它所选择的连接 ID。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"05 - 紧跟着的源连接标识 ID 的长度(5bytes)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"73 5f 63 69 64 - 实际的源连接标识 ID (\\\"s_cid\\\")\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"数据包长度\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"40 cf\"\n          },\n          [\n            \"服务器端表示数据包中有多少字节的加密有效载荷。这个字段是一个长度可变的整数——第一个字节的前两位表示该整数中总共有多少个字节。\",\n            \"此时，第一个字节以 \\\"0 1\\\"(0x4)这两个位开始，表示该整数共两个字节。其余的位给出数字 0xcf，表示 207 个字节的有效载荷。\"\n          ]\n        ],\n        [\n          \"数据包序号\",\n          {\n            \"props\": {\n              \"className\": \"bytes protected\",\n              \"title\": \"被数据包头保护密钥加密\"\n            },\n            \"content\": \"4f\"\n          },\n          {\n            \"props\": {\n              \"className\": \"bytes unprotected\"\n            },\n            \"content\": \"01\"\n          },\n          [\n            \"该字节应用了头保护。详见 \\\"数据包头字节\\\"。\",\n            \"这个字节的未受保护的值为 0x01，表明它是第 1 号包，或者说是服务器端发送的第二个\\\"握手\\\"包。\",\n            \"这个数据也有可能被截断。发送端点通过几个步骤截断：①计算已发送的最高的序号和未确认的最低的序号之间的差值；②出于安全考虑将差值加倍并四舍五入；③计算它在明确表示两端之间的序号的前提下可以从序号的高位删除的字节数；④截断编码后的数据包序号直至长度满足该字节数。而接收端点根据会它最近看到的数据包号码填入完整的序号。\",\n            {\n              \"children\": [\n                \"由于我们的例子对话发送的数据包很小(少于 64 个字节)，所以这种截断不会在本文中出现。详情见 \",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://www.rfc-editor.org/rfc/rfc9000.html#section-17.1\"\n                  },\n                  \"content\": \"RFC 9000\"\n                },\n                \"。\"\n              ]\n            }\n          ]\n        ],\n        [\n          \"加密的数据载荷\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\",\n              \"title\": \"被\\\"握手时密钥\\\"加密\"\n            },\n            \"content\": \"44 20 f9 19 68 1c 3f 0f 10 2a 30 f5 e6 47 a3 39 9a bf 54 bc 8e 80 45 31 34 99 6b a3 30 99 05 62 42 f3 b8 e6 62 bb fc e4 2f 3e f2 b6 ba 87 15 91 47 48 9f 84 79 e8 49 28 4e 98 3f d9 05 32 0a 62 fc 7d 67 e9 58 77 97 09 6c a6 01 01 d0 b2 68 5d 87 47 81 11 78 13 3a d9 17 2b 7f f8 ea 83 fd 81 a8 14 ba e2 7b 95 3a 97 d5 7e bf f4 b4 71 0d ba 8d f8 2a 6b 49 d7 d7 fa 3d 81 79 cb db 86 83 d4 bf a8 32 64 54 01 e5 a5 6a 76 53 5f 71 c6 fb 3e 61 6c 24 1b b1 f4 3b c1 47 c2 96 f5 91 40 29 97 ed 49 aa 0c 55 e3 17 21 d0 3e 14 11 4a f2 dc 45 8a e0 39 44 de 51 26 fe 08 d6 6a 6e f3 ba\"\n          },\n          [\"这些数据使用服务器端的\\\"握手时密钥\\\"进行加密。\"]\n        ],\n        [\n          \"AEAD 鉴别标签\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"2e d1 02 5f 98 fe a6 d6 02 49 98 18 46 87 dc 06\"\n          },\n          [\n            {\n              \"children\": [\n                \"这是 \",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://zhuanlan.zhihu.com/p/28566058\"\n                  },\n                  \"content\": \"AEAD 算法\"\n                },\n                \"的鉴别标签，用以确认加密数据和数据包头的完整性。它由加密算法产生，并由解密算法消耗。\"\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"data\": [\n        [\n          \"\",\n          {\n            \"props\": {\n              \"className\": \"decryption-header\"\n            },\n            \"content\": \"解密后的数据载荷\"\n          },\n          [\n            { \"Tag\": \"h4\", \"content\": \"解密\" },\n            \"数据被 \\\"服务器端生成握手密钥\\\" 步骤中产生的握手时密钥和向量(IVs)加密。IVs 通过对密钥和数据包序号进行异或操作生成。在例子中 IV 为 0。\",\n            \"数据包开头的 20 字节(数据包头)还会作为解密过程解密成功时必须满足的认证条件。\",\n            {\n              \"children\": [\n                \"openssl 命令行工具还不支持 AEAD 算法加解密(AEAD ciphers)，你可以使用作者的命令行工具来\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://quic.xargs.org/files/aes_128_gcm_decrypt.c\"\n                  },\n                  \"content\": \"解密\"\n                },\n                \"和\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://quic.xargs.org/files/aes_128_gcm_encrypt.c\"\n                  },\n                  \"content\": \"加密\"\n                },\n                \"这些数据。\"\n              ]\n            },\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### From the \\\"Handshake Keys Calc\\\" step\\n$ key=17abbf0a788f96c6986964660414e7ec\\n$ iv=09597a2ea3b04c00487e71f3\\n### from this record\\n$ recdata=e00000000105635f63696405735f63696440cf01\\n$ authtag=2ed1025f98fea6d6024998184687dc06\\n$ recordnum=1\\n### may need to add -I and -L flags for include and lib dirs\\n$ cc -o aes_128_gcm_decrypt aes_128_gcm_decrypt.c -lssl -lcrypto\\n$ cat /tmp/msg1   | ./aes_128_gcm_decrypt $iv $recordnum $key $recdata $authtag   | hexdump -C\\n\\n00000000  06 43 ff 40 b9 46 1e 8a  23 40 58 98 8e 7f 26 4d  |.C.@.F..#@X...&M|\\n00000010  7a b6 a5 1a 21 c6 29 79  b7 a6 79 f4 a0 87 70 85  |z...!.)y..y...p.|\\n... snip ...\"\n              }\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"加密帧帧头\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"06 43 ff 40 b9\"\n          },\n          [\n            {\n              \"children\": [\n                \"每个 QUIC 数据包在其有效载荷中包含\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://zhuanlan.zhihu.com/p/438579683\"\n                  },\n                  \"content\": \"一个或多个帧的列表\"\n                },\n                \"。例子中的数据包只包含一个帧，即 加密帧。\"\n              ]\n            },\n            \"加密帧帧头的目的是加密字节流，用于建立安全的 TLS 连接(TLS 1.3 握手)。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"06 - 表明这是 加密帧\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"43 ff - 可变长度的整数(前两位表示2字节的整数)。表示加密帧帧内字节的偏移量为 0x3FF(1023) 字节。\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"40 b9 - 长度可变的整数(前两位表示2字节的整数)，显示加密帧帧内字节长度为 0xB9(185) 字节。\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"TLS 证书验证数据(后一部分)\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"46 1e 8a 23 40 58 98 8e 7f 26 4d 7a b6 a5 1a 21 c6 29 79 b7 a6 79 f4 a0 87 70 85 6e 92 6d 37 1b 2e 89 16 9a a1 90 b8 03 63 6b b1 0c 0f b9 05 98 3d 2b 50 0a ad 26 83 df be 15 6e cc f6 66 de 1a 5a d4 5d 77 38 d5 e7 8b d1 7b c3 e6 d2 5f 9a d4 af ba 8f 81 de 9f 4d 55 72 11 8e 08 55 1a 4b b9 4b 56 a9 70 e8 04 c6 82 67 45 4b 51 7f c8 38 6c 9b ae 3a 77 cc cb 7f 29 0f 6e 58 fb a1 26 f0 53 33 a1 1f 8a b0 89 2e 6e 7a 89 58 53 82 d3 6e ef 25 29 cf 5b 7b\"\n          },\n          [\"表示验证数据的剩余部分。这一记录将在后面详细说明。\"]\n        ],\n        [\n          \"TLS 记录握手完成\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"14 00 00 20 06 8f cb 60 6a a1 c8 aa 35 4d 7b 60 64 a3 32 8c f3 76 bc d9 f3 20 0e 68 ac e3 de 2e e9 fc ac cb\"\n          },\n          [\"这一记录将在后面详细说明。\"]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/QUIC/serverHandshakePacket3.json",
    "content": "[\n  \"服务器端接收后，需要发送一个用于 \\\"握手 ACK\\\" 的数据包，用于完成 TLS 1.3 加密会话的握手过程。\",\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"数据包头字节\",\n          {\n            \"props\": {\n              \"className\": \"bytes protected\",\n              \"title\": \"被数据包头保护密钥加密\"\n            },\n            \"content\": \"e5\"\n          },\n          {\n            \"props\": {\n              \"className\": \"bytes unprotected\"\n            },\n            \"content\": \"e0\"\n          },\n          [\n            \"数据包以一个头字节开始，该字节应用了头保护。头部保护用于隐藏数据包序号和其他信息，使其不被外界观察到。\",\n            \"包头保护是通过用\\\"数据包头保护密钥\\\"对每个数据包的有效载荷的样本进行加密，然后将每个数据包中的某些比特和字节与所得数据进行异或(XOR)操作得到的。对于像这样的\\\"长\\\"格式数据包，受保护的部分是这个字节的低 4 位，以及数据包序号的字节（见下文）。\",\n            \"这里有一个关于如何计算出加密头字节的例子：\",\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### \\\"server header protection key\\\" from handshake keys calc step above\\n$ key=2a18061c396c2828582b41b0910ed536\\n### sample is taken from 16 bytes of payload starting\\n### 4 bytes past the first byte of the packet number\\n$ sample=169e6f1b817e4623e1acbe1db3899b00\\n$ echo $sample | xxd -r -p | openssl aes-128-ecb -K $key | head -c 5 | xxd -p\\n\\na5a6f88ece\\n\\n### first byte of result is xor'd into lower 4 bits of this byte,\\n### remaining bytes are xor'd one-for-one into the bytes of\\n### the packet number (which in this packet is only one byte)\"\n              }\n            },\n            \"解密出的字节 0xE0 中的位有以下含义：\",\n            {\n              \"Tag\": \"Table\",\n              \"props\": {\n                \"headers\": [\"\", \"值\", \"含义\"],\n                \"data\": [\n                  [\"高位\", \"1\", \"Long Header 格式\"],\n                  [\"\", \"1\", \"固定位(总是被置1)\"],\n                  [\"\", \"10\", \"数据包类型：握手\"],\n                  [\"\", \"00\", \"保留(总是被置0)\"],\n                  [\n                    \"低位\",\n                    \"00\",\n                    \"数据包序号长度(表示下面的 \\\"数据包序号\\\" 将有一个字节的长度，默认值)\"\n                  ]\n                ]\n              }\n            }\n          ]\n        ],\n        [\n          \"QUIC 版本号\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 00 00 01\"\n          },\n          [\"QUIC的版本是：0x1\"]\n        ],\n        [\n          \"目的地连接标识 ID\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"05 63 5f 63 69 64\"\n          },\n          [\n            \"服务器回传客户端的标识 ID (客户端的源连接标识 ID)\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"05 - 紧跟着的目的地连接标识 ID 的长度\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"63 5f 63 69 64 - 实际的目的地连接标识 ID(\\\"c_cid\\\")\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"源连接标识 ID\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"05 73 5f 63 69 64\"\n          },\n          [\n            \"服务器使用这个字段来向客户端表示它所选择的连接 ID。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"05 - 紧跟着的源连接标识 ID 的长度(5bytes)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"73 5f 63 69 64 - 实际的源连接标识 ID (\\\"s_cid\\\")\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"数据包长度\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"40 16\"\n          },\n          [\n            \"服务器端表示数据包中有多少字节的加密有效载荷。这个字段是一个长度可变的整数——第一个字节的前两位表示该整数中总共有多少个字节。\",\n            \"此时，第一个字节以 \\\"0 1\\\"(0x4)这两个位开始，表示该整数共两个字节。其余的位给出数字 0x16，表示 22 个字节的有效载荷。\"\n          ]\n        ],\n        [\n          \"数据包序号\",\n          {\n            \"props\": {\n              \"className\": \"bytes protected\",\n              \"title\": \"被数据包头保护密钥加密\"\n            },\n            \"content\": \"a4\"\n          },\n          {\n            \"props\": {\n              \"className\": \"bytes unprotected\"\n            },\n            \"content\": \"02\"\n          },\n          [\n            \"该字节应用了头保护。详见 \\\"数据包头字节\\\"。\",\n            \"这个字节的未受保护的值为 0x02，表明它是第 2 号包，或者说是服务器端发送的第三个\\\"握手\\\"包。\",\n            \"这个数据也有可能被截断。发送端点通过几个步骤截断：①计算已发送的最高的序号和未确认的最低的序号之间的差值；②出于安全考虑将差值加倍并四舍五入；③计算它在明确表示两端之间的序号的前提下可以从序号的高位删除的字节数；④截断编码后的数据包序号直至长度满足该字节数。而接收端点根据会它最近看到的数据包号码填入完整的序号。\",\n            {\n              \"children\": [\n                \"由于我们的例子对话发送的数据包很小(少于 64 个字节)，所以这种截断不会在本文中出现。详情见 \",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://www.rfc-editor.org/rfc/rfc9000.html#section-17.1\"\n                  },\n                  \"content\": \"RFC 9000\"\n                },\n                \"。\"\n              ]\n            }\n          ]\n        ],\n        [\n          \"加密的数据载荷\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\",\n              \"title\": \"被\\\"握手时密钥\\\"加密\"\n            },\n            \"content\": \"87 5b 25 16 9e\"\n          },\n          [\"这些数据使用服务器端的\\\"握手时密钥\\\"进行加密。\"]\n        ],\n        [\n          \"AEAD 鉴别标签\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"6f 1b 81 7e 46 23 e1 ac be 1d b3 89 9b 00 ec fb\"\n          },\n          [\n            {\n              \"children\": [\n                \"这是 \",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://zhuanlan.zhihu.com/p/28566058\"\n                  },\n                  \"content\": \"AEAD 算法\"\n                },\n                \"的鉴别标签，用以确认加密数据和数据包头的完整性。它由加密算法产生，并由解密算法消耗。\"\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"data\": [\n        [\n          \"\",\n          {\n            \"props\": {\n              \"className\": \"decryption-header\"\n            },\n            \"content\": \"解密后的数据载荷\"\n          },\n          [\n            { \"Tag\": \"h4\", \"content\": \"解密\" },\n            \"数据被 \\\"服务器端端握手时密钥计算\\\" 步骤中产生的握手时密钥和握手时向量(IVs)加密。IVs 通过对密钥和数据包序号进行异或操作生成。在例子中 IV 为 0。\",\n            \"数据包开头的 20 字节(数据包头)还会作为解密过程解密成功时必须满足的认证条件。\",\n            {\n              \"children\": [\n                \"openssl 命令行工具还不支持 AEAD 算法加解密(AEAD ciphers)，你可以使用作者的命令行工具来\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://quic.xargs.org/files/aes_128_gcm_decrypt.c\"\n                  },\n                  \"content\": \"解密\"\n                },\n                \"和\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://quic.xargs.org/files/aes_128_gcm_encrypt.c\"\n                  },\n                  \"content\": \"加密\"\n                },\n                \"这些数据。\"\n              ]\n            },\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### from the \\\"Handshake Keys Calc\\\" step\\n$ key=17abbf0a788f96c6986964660414e7ec\\n$ iv=09597a2ea3b04c00487e71f3\\n### from this record\\n$ recdata=e00000000105635f63696405735f636964401602\\n$ authtag=6f1b817e4623e1acbe1db3899b00ecfb\\n$ recordnum=2\\n### may need to add -I and -L flags for include and lib dirs\\n$ cc -o aes_128_gcm_decrypt aes_128_gcm_decrypt.c -lssl -lcrypto\\n$ cat /tmp/msg1   | ./aes_128_gcm_decrypt $iv $recordnum $key $recdata $authtag   | hexdump -C\\n\\n00000000  02 01 1c 00 01                                    |.....|\"\n              }\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"ACK 帧\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"02 01 1c 00 01\"\n          },\n          [\n            \"客户端确认收到服务器端的握手数据包1。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"02 - 表明帧类型 ACK\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"01 - largest_acknowledged: 被确认的最大数据包\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"children\": [\n                    \"1c - ack_delay: 变长的整数。给出这个 ack 被延迟发送的时间，单位是微秒。\",\n                    \"通过计算式子：2\",\n                    {\n                      \"Tag\": \"sup\",\n                      \"content\": \"ack_delay_exponent\"\n                    },\n                    \" 得到。其中 ack_delay_exponent = 28 * 8 = 224 (µseconds)。\"\n                  ]\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 - ack_range_count: 额外的 ACK 帧数据长度 0x(0)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"01 - first_ack_range: 可变长度的整数。给出在 largest_acknowledged 之前被确认过的数据包数量。\"\n                }\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/QUIC/serverInitialKeysCalc.json",
    "content": "[\n  \"接下来，服务器需要对自己的初始密钥进行计算。它从客户端的第一个初始数据包的 \\\"目的地连接标识 ID\\\" 字段中获得8字节的随机数据：\",\n  {\n    \"Tag\": \"pre\",\n    \"props\": {\n      \"className\": \"ind2\"\n    },\n    \"children\": [\n      {\n        \"Tag\": \"code\",\n        \"props\": { \"className\": \"longboi\" },\n        \"content\": \"0001020304050607\"\n      }\n    ]\n  },\n  \"并使用 \\\"客户端生成初始密钥\\\" 中所示的方法计算出相同的密钥和初始向量：\",\n  {\n    \"Tag\": \"ul\",\n    \"children\": [\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"客户端初始密钥: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"b14b918124fda5c8d79847602fa3520b\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"客户端初始向量: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"ddbc15dea80925a55686a7df\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"客户端初始数据包头保护密钥: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"6df4e9d737cdf714711d7c617ee82981\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"服务器端初始密钥: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"d77fc4056fcfa32bd1302469ee6ebf90\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"服务器端初始向量: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"fcb748e37ff79860faa07477\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"服务器端初始数据包头保护密钥: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"440b2725e91dc79b370711ef792faa3d\"\n          }\n        ]\n      }\n    ]\n  }\n]\n"
  },
  {
    "path": "src/QUIC/serverInitialPacket.json",
    "content": "[\n  \"服务器以一个 \\\"初始\\\" 数据包作为回应。这个数据包包含 \\\"ServerHello\\\" 的 TLS 记录，用于继续 TLS 1.3 加密会话的协商。\",\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"数据包头字节\",\n          {\n            \"props\": {\n              \"className\": \"bytes protected\",\n              \"title\": \"被数据包头保护密钥加密\"\n            },\n            \"content\": \"cd\"\n          },\n          {\n            \"props\": {\n              \"className\": \"bytes unprotected\"\n            },\n            \"content\": \"c0\"\n          },\n          [\n            \"数据包以一个头字节开始，该字节应用了头保护。头部保护用于隐藏数据包序号和其他信息，使其不被外界观察到。\",\n            \"包头保护是通过用\\\"数据包头保护密钥\\\"对每个数据包的有效载荷的样本进行加密，然后将每个数据包中的某些比特和字节与所得数据进行异或(XOR)操作得到的。对于像这样的\\\"长\\\"格式数据包，受保护的部分是这个字节的低 4 位，以及数据包序号的字节（见下文）。\",\n            \"这里有一个关于如何计算出加密头字节的例子：\",\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### \\\"server header protection key\\\" from calc step above\\n$ key=440b2725e91dc79b370711ef792faa3d\\n### sample is taken from 16 bytes of payload starting\\n### 4 bytes past the first byte of the packet number\\n$ sample=d5d9c823d07c616882ca770279249864\\n$ echo $sample | xxd -r -p | openssl aes-128-ecb -K $key | head -c 5 | xxd -p\\n\\n4d3acc3988\\n\\n### first byte of result is xor'd into lower 4 bits of this byte,\\n### remaining bytes are xor'd one-for-one into the bytes of\\n### the packet number (which in this packet is only one byte)\"\n              }\n            },\n            \"解密出的字节 0xC0 中的位有以下含义：\",\n            {\n              \"Tag\": \"Table\",\n              \"props\": {\n                \"headers\": [\"\", \"值\", \"含义\"],\n                \"data\": [\n                  [\"高位\", \"1\", \"Long Header 格式\"],\n                  [\"\", \"1\", \"固定位(总是被置1)\"],\n                  [\"\", \"00\", \"数据包类型：初始化\"],\n                  [\"\", \"00\", \"保留(总是被置0)\"],\n                  [\n                    \"低位\",\n                    \"00\",\n                    \"数据包序号长度(表示下面的 \\\"数据包序号\\\" 将有一个字节的长度，默认值)\"\n                  ]\n                ]\n              }\n            }\n          ]\n        ],\n        [\n          \"QUIC 版本号\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 00 00 01\"\n          },\n          [\"QUIC的版本是：0x1\"]\n        ],\n        [\n          \"目的地连接标识 ID\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"05 63 5f 63 69 64\"\n          },\n          [\n            \"服务器回传客户端的标识 ID (客户端的源连接标识 ID)。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"05 - 紧跟着的目的地连接标识 ID 的长度(8bytes)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"63 5f 63 69 64 - 实际的目的地连接标识 (\\\"c_cid\\\")\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"源连接标识 ID\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"05 73 5f 63 69 64\"\n          },\n          [\n            \"服务器使用这个字段来向客户端表示它所选择的连接 ID。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"05 - 紧跟着的源连接标识 ID 的长度(5bytes)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"73 5f 63 69 64 - 实际的源连接标识 ID (\\\"s_cid\\\")\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"令牌\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00\"\n          },\n          [\n            \"该字段在服务器响应中未使用。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 - 紧跟着的令牌长度(0bytes)\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"数据包长度\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"40 75\"\n          },\n          [\n            \"服务器端表示数据包中有多少字节的加密有效载荷。这个字段是一个长度可变的整数——第一个字节的前两位表示该整数中总共有多少个字节。\",\n            \"此时，第一个字节以 \\\"0 1\\\"(0x4)这两个位开始，表示该整数共两个字节。其余的位给出数字 0x75，表示 117 个字节的有效载荷。\"\n          ]\n        ],\n        [\n          \"数据包序号\",\n          {\n            \"props\": {\n              \"className\": \"bytes protected\",\n              \"title\": \"被数据包头保护密钥加密\"\n            },\n            \"content\": \"3a\"\n          },\n          {\n            \"props\": {\n              \"className\": \"bytes unprotected\"\n            },\n            \"content\": \"00\"\n          },\n          [\n            \"该字节应用了头保护。详见 \\\"数据包头字节\\\"。\",\n            \"这个字节的未受保护的值为 0x00，表明它是第 0 号包，或者说是服务器端发送的第一个\\\"初始\\\"包。\",\n            \"这个数据也有可能被截断。发送端点通过几个步骤截断：①计算已发送的最高的序号和未确认的最低的序号之间的差值；②出于安全考虑将差值加倍并四舍五入；③计算它在明确表示两端之间的序号的前提下可以从序号的高位删除的字节数；④截断编码后的数据包序号直至长度满足该字节数。而接收端点根据会它最近看到的数据包号码填入完整的序号。\",\n            {\n              \"children\": [\n                \"由于我们的例子对话发送的数据包很小(少于 64 个字节)，所以这种截断不会在本文中出现。详情见 \",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://www.rfc-editor.org/rfc/rfc9000.html#section-17.1\"\n                  },\n                  \"content\": \"RFC 9000\"\n                },\n                \"。\"\n              ]\n            }\n          ]\n        ],\n        [\n          \"加密的数据载荷\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\",\n              \"title\": \"被\\\"初始密钥\\\"加密\"\n            },\n            \"content\": \"83 68 55 d5 d9 c8 23 d0 7c 61 68 82 ca 77 02 79 24 98 64 b5 56 e5 16 32 25 7e 2d 8a b1 fd 0d c0 4b 18 b9 20 3f b9 19 d8 ef 5a 33 f3 78 a6 27 db 67 4d 3c 7f ce 6c a5 bb 3e 8c f9 01 09 cb b9 55 66 5f c1 a4 b9 3d 05 f6 eb 83 25 2f 66 31 bc ad c7 40 2c 10 f6 5c 52 ed 15 b4 42 9c 9f 64 d8 4d 64 fa 40 6c\"\n          },\n          [\"这些数据使用服务器端的\\\"初始密钥\\\"进行加密。\"]\n        ],\n        [\n          \"AEAD 鉴别标签\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"f0 b5 17 a9 26 d6 2a 54 a9 29 41 36 b1 43 b0 33\"\n          },\n          [\n            {\n              \"children\": [\n                \"这是 \",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://zhuanlan.zhihu.com/p/28566058\"\n                  },\n                  \"content\": \"AEAD 算法\"\n                },\n                \"的鉴别标签，用以确认加密数据和数据包头的完整性。它由加密算法产生，并由解密算法消耗。\"\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"data\": [\n        [\n          \"\",\n          {\n            \"props\": {\n              \"className\": \"decryption-header\"\n            },\n            \"content\": \"解密后的数据载荷\"\n          },\n          [\n            { \"Tag\": \"h4\", \"content\": \"解密\" },\n            \"数据被 \\\"服务器端初始密钥计算\\\" 步骤中产生的初始密钥和初始向量(IVs)加密。IVs 通过对密钥和数据包序号进行异或操作生成。在例子中 IV 为 0。\",\n            \"数据包开头的 21 字节(数据包头)还会作为解密过程解密成功时必须满足的认证条件。\",\n            {\n              \"children\": [\n                \"openssl 命令行工具还不支持 AEAD 算法加解密(AEAD ciphers)，你可以使用作者的命令行工具来\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://quic.xargs.org/files/aes_128_gcm_decrypt.c\"\n                  },\n                  \"content\": \"解密\"\n                },\n                \"和\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://quic.xargs.org/files/aes_128_gcm_encrypt.c\"\n                  },\n                  \"content\": \"加密\"\n                },\n                \"这些数据。\"\n              ]\n            },\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### From the \\\"Initial Keys Calc\\\" step\\n$ key=d77fc4056fcfa32bd1302469ee6ebf90\\n$ iv=fcb748e37ff79860faa07477\\n### from this record\\n$ recdata=c00000000105635f63696405735f63696400407500\\n$ authtag=f0b517a926d62a54a9294136b143b033\\n$ recordnum=0\\n### may need to add -I and -L flags for include and lib dirs\\n$ cc -o aes_128_gcm_decrypt aes_128_gcm_decrypt.c -lssl -lcrypto\\n$ cat /tmp/msg1   | ./aes_128_gcm_decrypt $iv $recordnum $key $recdata $authtag   | hexdump -C\\n\\n00000000  02 00 42 40 00 00 06 00  40 5a 02 00 00 56 03 03  |..B@....@Z...V..|\\n00000010  70 71 72 73 74 75 76 77  78 79 7a 7b 7c 7d 7e 7f  |pqrstuvwxyz{|}~.|\\n... snip ...\"\n              }\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"ACK 帧\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"02 00 42 40 00 00\"\n          },\n          [\n            \"服务器确认收到客户端的初始数据包0。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"02 - 表明帧类型 ACK\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 - largest_acknowledged: 被确认的最大数据包\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"children\": [\n                    \"42 40 - ack_delay: 变长的整数。给出这个 ack 被延迟发送的时间，单位是微秒。\",\n                    \"通过计算式子：2\",\n                    {\n                      \"Tag\": \"sup\",\n                      \"content\": \"ack_delay_exponent\"\n                    },\n                    \" 得到。其中 ack_delay_exponent = 64 * 8 = 512 (µseconds)。\"\n                  ]\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 - ack_range_count: 额外的 ACK 帧数据长度 0x(0)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 - first_ack_range: 可变长度的整数。给出在 largest_acknowledged 之前被确认过的数据包数量。\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"加密帧帧头\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"06 00 40 5a\"\n          },\n          [\n            {\n              \"children\": [\n                \"每个 QUIC 数据包在其有效载荷中包含\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://zhuanlan.zhihu.com/p/438579683\"\n                  },\n                  \"content\": \"一个或多个帧的列表\"\n                },\n                \"。例子中的数据包只包含一个帧，即 加密帧。\"\n              ]\n            },\n            \"加密帧帧头的目的是加密字节流，用于建立安全的 TLS 连接(TLS 1.3 握手)。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"06 - 表明这是 加密帧\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 - 可变长度的整数，提供加密帧帧内字节的偏移量(0)。\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"40 5a - 长度可变的整数(前两位表示2字节的整数)，显示加密帧帧内字节长度为 0x5A(90) 字节。\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"ServerHello TLS 记录\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"02 00 00 56 03 03 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f 00 13 01 00 00 2e 00 33 00 24 00 1d 00 20 9f d7 ad 6d cf f4 29 8d d3 f9 6d 5b 1b 2a f9 10 a0 53 5b 14 88 d7 f8 fa bb 34 9a 98 28 80 b6 15 00 2b 00 02 03 04\"\n          },\n          [\"这一记录将在后面详细说明。\"]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/QUIC/serverKeyExchangeGeneration.json",
    "content": "[\n  \"服务器端也需要生成一个用于密钥交换的自己的“私钥/公钥”对。密钥交换(Key exchange)是一种技术，双方可以在同一数字上达成一致，而窃听者却无法知道这个数字是什么。\",\n  {\n    \"Tag\": \"p\",\n    \"children\": [\n      \"学习 QUIC 并不需要深入了解，但你可以从\",\n      {\n        \"Tag\": \"a\",\n        \"props\": { \"href\": \"https://cangsdarm.github.io/illustrate/x25519\" },\n        \"content\": \"X25519 密钥交换算法\"\n      },\n      \"获取涉及到的密钥交换算法的具体解释。\"\n    ]\n  },\n  [\n    \"**私钥**是 0 到 \",\n    {\n      \"Tag\": \"Math\",\n      \"content\": \"2^256-1\"\n    },\n    \" 之间的一个随机整数(32bytes, 256bits)\",\n    \"。为方便后续解释，假设我们生成的私钥是：\"\n  ],\n  {\n    \"Tag\": \"pre\",\n    \"props\": {\n      \"className\": \"ind2\"\n    },\n    \"children\": [\n      {\n        \"Tag\": \"code\",\n        \"props\": { \"className\": \"longboi\" },\n        \"content\": \"909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeaf\"\n      }\n    ]\n  },\n  \"**公钥**由上面提到的 X25519 密钥交换算法生成。例子中计算出的公钥应如下所示：\",\n  {\n    \"Tag\": \"pre\",\n    \"props\": {\n      \"className\": \"ind2\"\n    },\n    \"children\": [\n      {\n        \"Tag\": \"code\",\n        \"props\": { \"className\": \"longboi\" },\n        \"content\": \"9fd7ad6dcff4298dd3f96d5b1b2af910a0535b1488d7f8fabb349a982880b615\"\n      }\n    ]\n  },\n  \"公钥的计算也可以在命令行中运行以下命令得到：\",\n  {\n    \"Tag\": \"CodeSample\",\n    \"props\": {\n      \"code\": \"### requires openssl 1.1.0 or higher\\n$ openssl pkey -noout -text < server-ephemeral-private.key\\n\\nX25519 Private-Key:\\npriv:\\n    90:91:92:93:94:95:96:97:98:99:9a:9b:9c:9d:9e:\\n    9f:a0:a1:a2:a3:a4:a5:a6:a7:a8:a9:aa:ab:ac:ad:\\n    ae:af\\npub:\\n    9f:d7:ad:6d:cf:f4:29:8d:d3:f9:6d:5b:1b:2a:f9:\\n    10:a0:53:5b:14:88:d7:f8:fa:bb:34:9a:98:28:80:\\n    b6:15\"\n    }\n  }\n]\n"
  },
  {
    "path": "src/QUIC/serverTLSHandshakeFinished.json",
    "content": "[\n  \"为了验证握手成功且没有被篡改过，服务器会创建一些验证数据给客户端确认。验证数据是基于所有握手信息的哈希值计算得到。\",\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"TLS 握手消息头\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"14 00 00 20\"\n          },\n          [\n            \"每个 TLS 握手消息都以一个 type 和一个 len 开始。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"14 - 握手消息类型 0x14 (finished)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 20 - 紧接着的握手消息数据的长度 0x20 (32) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"验证数据\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"06 8f cb 60 6a a1 c8 aa 35 4d 7b 60 64 a3 32 8c f3 76 bc d9 f3 20 0e 68 ac e3 de 2e e9 fc ac cb\"\n          },\n          [\n            \"使用 \\\"服务器端生成握手密钥\\\" 步骤中的服务器握手时密钥和在这之前的每个握手记录(ClientHello 到 证书验证数据)的 SHA256 哈希值生成。\",\n            {\n              \"Tag\": \"pre\",\n              \"children\": [\n                {\n                  \"Tag\": \"code\",\n                  \"props\": { \"className\": \"longboi\" },\n                  \"content\": \"finished_key = HKDF-Expand-Label(key: server_secret, label: \\\"finished\\\", ctx: \\\"\\\", len: 32)\\nfinished_hash = SHA256(ClientHello ... CertificateVerify)\\nverify_data = HMAC-SHA256(key: finished_key, msg: finished_hash)\"\n                }\n              ]\n            },\n            {\n              \"children\": [\n                \"在命令行中使用\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": { \"href\": \"https://quic.xargs.org/files/hkdf.sh\" },\n                  \"content\": \"原作者制作的 HKDF 命令行脚本\"\n                },\n                \"，你也可以自己试试：\"\n              ]\n            },\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### find the hash of the conversation to this point, excluding\\n### QUIC frame headers\\n$ fin_hash=$(cat crypto_clienthello crypto_serverhello   crypto_extensions crypto_cert crypto_certverify | openssl sha256)\\n$ sht_secret=88ad8d3b0986a71965a28d108b0f40ffffe629284a6028c80ddc5dc083b3f5d1\\n$ fin_key=$(./hkdf expandlabel $sht_secret \\\"finished\\\" \\\"\\\" 32)\\n$ echo $fin_hash | xxd -r -p     | openssl dgst -sha256 -mac HMAC -macopt hexkey:$fin_key\\n\\n068fcb606aa1c8aa354d7b6064a3328cf376bcd9f3200e68ace3de2ee9fcaccb\"\n              }\n            }\n          ]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/QUIC/tls-certificate.json",
    "content": "[\n  \"服务器会发送一个或多个证书：\",\n  {\n    \"Tag\": \"ul\",\n    \"children\": [\n      {\n        \"Tag\": \"li\",\n        \"content\": \"该主机的证书，包含主机名、公钥和第三方的签名(签名证明该证书主机名的所有者持有该证书的私钥)。\"\n      },\n      {\n        \"Tag\": \"li\",\n        \"content\": \"其他证书的可选列表，从主机证书一直到预先安装在客户端上的可信证书。其中每一个都对前一个证书进行签名，形成一个信任链。\"\n      }\n    ]\n  },\n  {\n    \"children\": [\n      \"为了让这个例子不至于太过于庞大，我们只发送一个主机证书。证书以一种叫做 \",\n      {\n        \"Tag\": \"a\",\n        \"props\": {\n          \"href\": \"https://www.cnblogs.com/flydean/p/16541577.html\"\n        },\n        \"content\": \"DER\"\n      },\n      \" 的二进制格式书写。\"\n    ]\n  },\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"TLS 握手消息头\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"0b 00 03 2e\"\n          },\n          [\n            \"每个 TLS 握手消息都以一个 type 和一个 len 开始。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"0b - 握手消息类型 0x0b (certificate)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 03 2e - 紧接着的证书数据的长度 0x32E (814) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"请求上下文\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00\"\n          },\n          [\n            \"数据为空，因为该证书不是响应证书请求而发送。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 - 没有数据\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"所有证书长度\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 03 2a\"\n          },\n          [\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 03 2a - 紧接着的证书的长度 0x32A (810) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"第一个证书长度\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 03 25\"\n          },\n          [\n            \"此时同时也是唯一一个\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 03 25 - 紧接着的证书的长度 0x325 (805) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"证书\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"30 82 03 21 30 82 02 09 a0 03 02 01 02 02 08 15 5a 92 ad c2 04 8f 90 30 0d 06 09 2a 86 48 86 f7 0d 01 01 0b 05 00 30 22 31 0b 30 09 06 03 55 04 06 13 02 55 53 31 13 30 11 06 03 55 04 0a 13 0a 45 78 61 6d 70 6c 65 20 43 41 30 1e 17 0d 31 38 31 30 30 35 30 31 33 38 31 37 5a 17 0d 31 39 31 30 30 35 30 31 33 38 31 37 5a 30 2b 31 0b 30 09 06 03 55 04 06 13 02 55 53 31 1c 30 1a 06 03 55 04 03 13 13 65 78 61 6d 70 6c 65 2e 75 6c 66 68 65 69 6d 2e 6e 65 74 30 82 01 22 30 0d 06 09 2a 86 48 86 f7 0d 01 01 01 05 00 03 82 01 0f 00 30 82 01 0a 02 82 01 01 00 c4 80 36 06 ba e7 47 6b 08 94 04 ec a7 b6 91 04 3f f7 92 bc 19 ee fb 7d 74 d7 a8 0d 00 1e 7b 4b 3a 4a e6 0f e8 c0 71 fc 73 e7 02 4c 0d bc f4 bd d1 1d 39 6b ba 70 46 4a 13 e9 4a f8 3d f3 e1 09 59 54 7b c9 55 fb 41 2d a3 76 52 11 e1 f3 dc 77 6c aa 53 37 6e ca 3a ec be c3 aa b7 3b 31 d5 6c b6 52 9c 80 98 bc c9 e0 28 18 e2 0b f7 f8 a0 3a fd 17 04 50 9e ce 79 bd 9f 39 f1 ea 69 ec 47 97 2e 83 0f b5 ca 95 de 95 a1 e6 04 22 d5 ee be 52 79 54 a1 e7 bf 8a 86 f6 46 6d 0d 9f 16 95 1a 4c f7 a0 46 92 59 5c 13 52 f2 54 9e 5a fb 4e bf d7 7a 37 95 01 44 e4 c0 26 87 4c 65 3e 40 7d 7d 23 07 44 01 f4 84 ff d0 8f 7a 1f a0 52 10 d1 f4 f0 d5 ce 79 70 29 32 e2 ca be 70 1f df ad 6b 4b b7 11 01 f4 4b ad 66 6a 11 13 0f e2 ee 82 9e 4d 02 9d c9 1c dd 67 16 db b9 06 18 86 ed c1 ba 94 21 02 03 01 00 01 a3 52 30 50 30 0e 06 03 55 1d 0f 01 01 ff 04 04 03 02 05 a0 30 1d 06 03 55 1d 25 04 16 30 14 06 08 2b 06 01 05 05 07 03 02 06 08 2b 06 01 05 05 07 03 01 30 1f 06 03 55 1d 23 04 18 30 16 80 14 89 4f de 5b cc 69 e2 52 cf 3e a3 00 df b1 97 b8 1d e1 c1 46 30 0d 06 09 2a 86 48 86 f7 0d 01 01 0b 05 00 03 82 01 01 00 59 16 45 a6 9a 2e 37 79 e4 f6 dd 27 1a ba 1c 0b fd 6c d7 55 99 b5 e7 c3 6e 53 3e ff 36 59 08 43 24 c9 e7 a5 04 07 9d 39 e0 d4 29 87 ff e3 eb dd 09 c1 cf 1d 91 44 55 87 0b 57 1d d1 9b df 1d 24 f8 bb 9a 11 fe 80 fd 59 2b a0 39 8c de 11 e2 65 1e 61 8c e5 98 fa 96 e5 37 2e ef 3d 24 8a fd e1 74 63 eb bf ab b8 e4 d1 ab 50 2a 54 ec 00 64 e9 2f 78 19 66 0d 3f 27 cf 20 9e 66 7f ce 5a e2 e4 ac 99 c7 c9 38 18 f8 b2 51 07 22 df ed 97 f3 2e 3e 93 49 d4 c6 6c 9e a6 39 6d 74 44 62 a0 6b 42 c6 d5 ba 68 8e ac 3a 01 7b dd fc 8e 2c fc ad 27 cb 69 d3 cc dc a2 80 41 44 65 d3 ae 34 8c e0 f3 4a b2 fb 9c 61 83 71 31 2b 19 10 41 64 1c 23 7f 11 a5 d6 5c 84 4f 04 04 84 99 38 71 2b 95 9e d6 85 bc 5c 5d d6 45 ed 19 90 94 73 40 29 26 dc b4 0e 34 69 a1 59 41 e8 e2 cc a8 4b b6 08 46 36 a0\"\n          },\n          [\n            {\n              \"children\": [\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://quic.xargs.org/files/server.crt\"\n                  },\n                  \"content\": \"该证书\"\n                },\n                \"采用 ASN.1 DER 编码。可以在命令行中使用以下命令转换为二进制数据：\"\n              ]\n            },\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"$ openssl x509 -outform der < server.crt | hexdump\\n\\n0000000 30 82 03 21 30 82 02 09 a0 03 02 01 02 02 08 15\\n0000010 5a 92 ad c2 04 8f 90 30 0d 06 09 2a 86 48 86 f7\\n... snip ...\"\n              }\n            }\n          ]\n        ],\n        [\n          \"证书的扩展\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 00\"\n          },\n          [\n            \"服务器可以提供证书需要的扩展信息。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 - 没有扩展信息\"\n                }\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/QUIC/tls-certificateVerify.json",
    "content": "[\n  \"服务器提供证书验证相关的数据，用以验证服务器密钥交换生成过程中产生的短暂公钥与证书私钥的所有权是否一致。\",\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"TLS 握手消息头\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"0f 00 01 04\"\n          },\n          [\n            \"每个 TLS 握手消息都以一个 type 和一个 len 开始。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"0f - 握手消息类型 0x0f (certificate verify)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 01 04 - 紧接着的握手消息数据的长度 0x104 (260) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"签名算法\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"08 04\"\n          },\n          [\"08 04 - 代表 rsa_pss_rsae_sha256 算法\"]\n        ],\n        [\n          \"签名长度\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"01 00\"\n          },\n          [\"签名数据的长度为 0x100(256) 字节\"]\n        ],\n        [\n          \"签名\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"0a 99 af 32 a9 e4 06 d7 25 f9 a9 39 6d e5 af 37 56 b7 a8 f6 e4 da d5 85 ab c3 f8 7c 6d 1f c1 5f 5f 00 ab a8 dc a9 d0 5c db 51 d3 c9 35 43 36 56 d8 8b 74 32 00 5e e7 e0 48 03 b2 47 57 44 d7 55 5c f3 de 48 9c c2 16 a4 85 a7 28 b2 18 90 e8 7a a9 41 5d 19 e6 3a 6a 77 9b 9c db b1 28 a8 04 c4 28 b8 27 fa 65 df cd 95 2a ce 54 46 1e 8a 23 40 58 98 8e 7f 26 4d 7a b6 a5 1a 21 c6 29 79 b7 a6 79 f4 a0 87 70 85 6e 92 6d 37 1b 2e 89 16 9a a1 90 b8 03 63 6b b1 0c 0f b9 05 98 3d 2b 50 0a ad 26 83 df be 15 6e cc f6 66 de 1a 5a d4 5d 77 38 d5 e7 8b d1 7b c3 e6 d2 5f 9a d4 af ba 8f 81 de 9f 4d 55 72 11 8e 08 55 1a 4b b9 4b 56 a9 70 e8 04 c6 82 67 45 4b 51 7f c8 38 6c 9b ae 3a 77 cc cb 7f 29 0f 6e 58 fb a1 26 f0 53 33 a1 1f 8a b0 89 2e 6e 7a 89 58 53 82 d3 6e ef 25 29 cf 5b 7b\"\n          },\n          [\n            \"由于服务器会为每个会话都生成短暂的密钥，所以和 TLS 之前的版本不同，密钥与证书不会有内在的联系。\",\n            \"为了证明服务器拥有服务器证书(在这个 TLS 会话中的有效性)，它需要使用证书的私钥对握手信息的哈希进行签名。而客户端可以通过使用证书的公钥来证明该签名的有效性。\",\n            \"签名过程不能在命令行中逐字逐句地复制，因为签名工具在签名中引入了随机或变化的数据。\",\n            {\n              \"children\": [\n                \"相反，我们可以像客户端那样，在命令行使用\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://quic.xargs.org/files/server.crt\"\n                  },\n                  \"content\": \"服务器的证书\"\n                },\n                \"提供的公钥来验证签名：\"\n              ]\n            },\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### find the hash of the conversation to this point, excluding\\n### 5-byte record headers or 1-byte wrapped record trailers\\n$ handshake_hash=$(cat crypto_clienthello crypto_serverhello   crypto_extensions crypto_cert | openssl sha256)\\n\\n### build the data that was signed:\\n### 1. add 64 space characters\\n$ echo -n '                                ' > /tmp/tosign\\n$ echo -n '                                ' >> /tmp/tosign\\n### 2. add this fixed string\\n$ echo -n 'TLS 1.3, server CertificateVerify' >> /tmp/tosign\\n### 3. add a single null character\\n$ echo -en '\\\\u0000' >> /tmp/tosign\\n### 4. add hash of handshake to this point\\n$ echo $handshake_hash | xxd -r -p >> /tmp/tosign\\n\\n### copy the signature that we want to verify\\n$ echo \\\"0a 99 af 32 a9 e4 06 d7 25 f9 a9 39 6d e5 af 37 56 b7 a8 f6\\n  e4 da d5 85 ab c3 f8 7c 6d 1f c1 5f 5f 00 ab a8 dc a9 d0 5c db 51\\n  d3 c9 35 43 36 56 d8 8b 74 32 00 5e e7 e0 48 03 b2 47 57 44 d7 55\\n  5c f3 de 48 9c c2 16 a4 85 a7 28 b2 18 90 e8 7a a9 41 5d 19 e6 3a\\n  6a 77 9b 9c db b1 28 a8 04 c4 28 b8 27 fa 65 df cd 95 2a ce 54 46\\n  1e 8a 23 40 58 98 8e 7f 26 4d 7a b6 a5 1a 21 c6 29 79 b7 a6 79 f4\\n  a0 87 70 85 6e 92 6d 37 1b 2e 89 16 9a a1 90 b8 03 63 6b b1 0c 0f\\n  b9 05 98 3d 2b 50 0a ad 26 83 df be 15 6e cc f6 66 de 1a 5a d4 5d\\n  77 38 d5 e7 8b d1 7b c3 e6 d2 5f 9a d4 af ba 8f 81 de 9f 4d 55 72\\n  11 8e 08 55 1a 4b b9 4b 56 a9 70 e8 04 c6 82 67 45 4b 51 7f c8 38\\n  6c 9b ae 3a 77 cc cb 7f 29 0f 6e 58 fb a1 26 f0 53 33 a1 1f 8a b0\\n  89 2e 6e 7a 89 58 53 82 d3 6e ef 25 29 cf 5b 7b\\\" | xxd -r -p > /tmp/sig\\n### extract the public key from the certificate\\n$ openssl x509 -pubkey -noout -in server.crt > server.pub\\n\\n### verify the signature\\n$ cat /tmp/tosign | openssl dgst -verify server.pub -sha256     -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:-1 -signature /tmp/sig\\n\\nVerified OK\"\n              }\n            }\n          ]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/QUIC/tls-clientHello.json",
    "content": "[\n  \"加密会话以 TLS 的 \\\"ClientHello\\\" 开始。客户端提供的信息包括以下内容：\",\n  {\n    \"Tag\": \"ul\",\n    \"children\": [\n      {\n        \"Tag\": \"li\",\n        \"content\": \"客户端随机数(在随后的握手时使用)\"\n      },\n      {\n        \"Tag\": \"li\",\n        \"content\": \"客户端支持的加解密算法的列表\"\n      },\n      {\n        \"Tag\": \"li\",\n        \"content\": \"用于密钥交换的公钥\"\n      },\n      {\n        \"Tag\": \"li\",\n        \"content\": \"客户端支持的协议版本\"\n      }\n    ]\n  },\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"TLS 握手消息头\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"01 00 00 ea\"\n          },\n          [\n            \"每个 TLS 握手消息都以一个 type 和一个 len 开始。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"01 - 握手消息类型 0x01 (client hello)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 ea - 紧接着的 client hello 数据的长度 0xEA (234) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"客户端 TLS 协议版本号\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"03 03\"\n          },\n          [\n            \"协议版本为 \\\"3,3\\\"(指 TLS 1.2)。因为被广泛部署的中间件(middleboxes)并不允许他们不承认的协议版本，所以所有的 TLS 1.3 会话在这个字段中都以 TLS 1.2 版本表示。也因为如此，这个字段不再被使用，而是使用下面的 \\\"支持的版本\\\" 扩展来进行版本协商。\",\n            \"而不寻常的版本号(\\\"3,3\\\" 代表 TLS 1.2)是由于 TLS 1.0 是 SSL 3.0 协议的一个小修订。因此，TLS 1.0 用 \\\"3,1\\\" 表示，TLS 1.1 用 \\\"3,2\\\"，以此类推。\"\n          ]\n        ],\n        [\n          \"客户端随机数\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f\"\n          },\n          [\n            \"客户端提供的 32 字节的随机数。这个数将在之后的会话中使用。在本文的例子中，我们暂时将一个方便记忆的字符串当作随机数。\"\n          ]\n        ],\n        [\n          \"会话 ID (废弃)\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00\"\n          },\n          [\n            \"这是一个废弃(legacy)字段，不在 QUIC 中使用。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 - 0 字节的会话 ID\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"加解密算法列表\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 06 13 01 13 02 13 03\"\n          },\n          [\n            \"客户端提供一个有序的列表，以说明它支持哪些加解密算法进行加密。该列表是按照客户端的偏好顺序排列的，以最高偏好为先。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 06 - 6 字节的加解密算法列表数据\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"13 01 - 代表 TLS_AES_128_GCM_SHA256\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"13 02 - 代表 TLS_AES_256_GCM_SHA384\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"13 03 - 代表 TLS_CHACHA20_POLY1305_SHA256\"\n                }\n              ]\n            },\n            {\n              \"children\": [\n                \"完整加解密算法列表请查看 \",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml\"\n                  },\n                  \"content\": \"IANA tls-parameters\"\n                },\n                \" 的 TLS Cipher Suites 部分。\"\n              ]\n            }\n          ]\n        ],\n        [\n          \"压缩算法\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"01 00\"\n          },\n          [\n            {\n              \"children\": [\n                \"以前的 TLS 版本支持压缩，这被发现会泄露加密数据的信息(见 \",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://zhuanlan.zhihu.com/p/333314023\"\n                  },\n                  \"content\": \"CRIME/BREACH 攻击\"\n                },\n                \")。\"\n              ]\n            },\n            \"TLS 1.3 不再允许压缩。所以这个字段不会变化：采用 \\\"null\\\" 压缩算法，对数据不做任何改变。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"01 - 1 字节的压缩算法长度\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 - 代表 \\\"没有\\\"(null) 压缩算法\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"扩展的长度\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 bb\"\n          },\n          [\n            \"客户端提供了一个可选的扩展列表，服务器可以根据它来采取某些行动或启用某些特性。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 bb - 扩展列表的长度为 0xBB(187) 字节\"\n                }\n              ]\n            },\n            \"每个扩展将以两个字节开始，表明它是哪个扩展。然后是两个字节的内容长度字段，最后是扩展的具体内容。\"\n          ]\n        ],\n        [\n          \"扩展 - 服务器名称指示\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 00 00 18 00 16 00 00 13 65 78 61 6d 70 6c 65 2e 75 6c 66 68 65 69 6d 2e 6e 65 74\"\n          },\n          [\n            \"(例子中的)客户端提供了它想要联系的服务器的名称，也被称为 SNI(服务器名称指示)。\",\n            \"如果没有这个扩展，HTTPS 服务器将无法为一个 IP 地址上的多个主机名(虚拟主机)提供服务。因为它在 TLS 会话协商结束、HTTP 请求发出后才知道要发送哪个主机名的证书。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 - 表示这是 \\\"服务器名称指示\\\" 扩展\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 18 - 扩展信息长度为 0x18(24) 字节 \"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 16 - 第一个(也是唯一的)列表条目长度为 0x16(22) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 - 0x00 表示这是 \\\"主机名\\\" 列表\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 13 - 主机名列表的长度为 0x13(19) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"65 78 61 ... 6e 65 74 - 主机名：\\\"example.ulfheim.net\\\"\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"扩展 - 支持的组\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 0a 00 08 00 06 00 1d 00 17 00 18\"\n          },\n          [\n            \"(例子中的)客户端表示它支持三种类型的椭圆曲线算法(ECC)。为了使这个扩展更加通用，未来可以支持其他的密码学类型，因此称这些为 \\\"支持的组\\\" 而不是 \\\"支持的曲线\\\"。\",\n            \"这个列表同样是按照客户端的偏好降序排列的。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 0a - 表示这是 \\\"支持的组\\\" 扩展\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 08 - 扩展信息长度为 0x08(8) 字节 \"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 06 - 曲线列表条目长度为 0x06(6) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 1d - 代表 x25519 曲线\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 17 - 代表 secp256r1 曲线\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 18 - 代表 secp384r1 曲线\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"扩展 - ALPN 协议\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 10 00 0b 00 09 08 70 69 6e 67 2f 31 2e 30\"\n          },\n          [\n            \"应用层协议协商，即 ALPN，被 QUIC 用来在服务器和客户端之间协商支持的协议和版本。\",\n            \"对应的应用层协议可能是：\\\"http/1.1\\\"、\\\"h2\\\"(HTTP/2)或 \\\"h3\\\"(HTTP/3)。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 10 - 表示这是 \\\"ALPN\\\" 扩展\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 0b - 扩展信息长度为 0xb(11) 字节 \"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 09 - ALPN 条目长度为 0x09(9) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"08 - 协议名称长度为 0x08(8) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"70 69 ... 2e 30 - 字符串 \\\"ping/1.0\\\"\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"扩展 - 签名算法列表\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 0d 00 14 00 12 04 03 08 04 04 01 05 03 08 05 05 01 08 06 06 01 02 01\"\n          },\n          [\n            \"这个扩展表示客户端支持哪些签名算法。这可能会影响到服务器提交给客户端的证书，以及服务器在 CertificateVerify 记录中发送的签名。\",\n            \"这个列表同样是按照客户端的偏好降序排列的。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 0d - 表示这是 \\\"签名算法列表\\\" 扩展\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 14 - 扩展信息长度为 0x14(20) 字节 \"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 12 - 算法列表长度为 0x12(18) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"04 03 - 代表 ECDSA-SECP256r1-SHA256 算法\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"08 04 - 代表 RSA-PSS-RSAE-SHA256 算法\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"04 01 - 代表 RSA-PKCS1-SHA256 算法\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"05 03 - 代表 ECDSA-SECP384r1-SHA384 算法\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"08 05 - 代表 RSA-PSS-RSAE-SHA384 算法\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"05 01 - 代表 RSA-PKCS1-SHA384 算法\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"08 06 - 代表 RSA-PSS-RSAE-SHA512 算法\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"06 01 - 代表 RSA-PKCS1-SHA512 算法\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"02 01 - 代表 RSA-PKCS1-SHA1 算法\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"扩展 - 算法公钥列表(Key Share)\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 33 00 26 00 24 00 1d 00 20 35 80 72 d6 36 58 80 d1 ae ea 32 9a df 91 21 38 38 51 ed 21 a2 8e 3b 75 e9 65 d0 d2 cd 16 62 54\"\n          },\n          [\n            \"客户端会给某些它认为服务器也会支持的算法发送对应的短暂的公钥。这允许在 ClientHello 和 ServerHello 消息之后的其余握手动作消息被加密。而不用像以前的协议版本，以透明的方式发送握手消息。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 33 - 表示这是 \\\"算法公钥列表\\\" 扩展\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 26 - 扩展信息长度为 0x26(38) 字节 \"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 24 - 算法公钥列表长度为 0x24(36) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 1d - 代表 x25519 算法(例子中为通过 curve25519 算法进行密钥交换)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 20 - 公钥长度为 0x20(32) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"35 80 ... 62 54 - \\\"客户端准备密钥交换\\\" 步骤中生成的公钥\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"扩展 - PSK 密钥交换模型协商\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 2d 00 02 01 01\"\n          },\n          [\n            \"客户端指出从预共享密钥(PSKs)建立密钥的可用模式。由于例子中的会话中不使用 PSK，因此这个扩展没有影响。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 2d - 表示这是 \\\"PSK 密钥交换模型协商\\\" 扩展\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 02 - 扩展信息长度为 0x02(2) 字节 \"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"01 - RSK 模型长度为 0x01(1) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"01 - 代表 PSK with (EC)DHE key establishment\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"扩展 - 支持的版本\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 2b 00 03 02 03 04\"\n          },\n          [\n            \"客户端表明其支持 TLS 1.3。由于兼容性的原因，这被放在一个扩展中，而不是上面的客户端版本字段。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 2b - 表示这是 \\\"支持的版本\\\" 扩展\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 03 - 扩展信息长度为 0x03(3) 字节 \"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"02 - TLS 版本长度为 0x02(2) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"03 04 - 代表 TLS 1.3\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"扩展 - QUIC 协议参数\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 39 00 31 03 04 80 00 ff f7 04 04 80 a0 00 00 05 04 80 10 00 00 06 04 80 10 00 00 07 04 80 10 00 00 08 01 0a 09 01 0a 0a 01 03 0b 01 19 0f 05 63 5f 63 69 64\"\n          },\n          [\n            \"这里给出了客户端对 QUIC 连接的配置值。它们被放入该记录中而不是初始数据包的头部中，是因为 TLS 记录中的所有数据都会被加密保护，不会被篡改。\",\n            \"以下是例子中的 QUIC 的设置参数：\",\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"max_udp_payload_size: 65527\\ninitial_max_data: 10485760\\ninitial_max_stream_data_bidi_local: 1048576\\ninitial_max_stream_data_bidi_remote: 1048576\\ninitial_max_stream_data_uni: 1048576\\ninitial_max_streams_bidi: 10\\ninitial_max_streams_uni: 10\\nack_delay_exponent: 3\\ninitial_source_connection_id: \\\"c_cid\\\"\"\n              }\n            },\n            \"以下是相关字节的完整列表和解释：\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 39 - 表示这是 \\\"QUIC 协议参数\\\" 扩展\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 31 - 扩展信息长度为 0x31(49) 字节 \"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"03 - 代表 max_udp_payload_size 参数\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"04 - max_udp_payload_size 参数长度为 0x04(4) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"80 00 ff f7 - 参数值 0xfff7 (65527)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"04 - 代表 initial_max_data 参数\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"04 - initial_max_data 参数长度为 0x04(4) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"80 a0 00 00 - 参数值 0xa00000 (10485760)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"05 - 代表 initial_max_stream_data_bidi_local 参数\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"04 - initial_max_stream_data_bidi_local 参数长度为 0x04(4) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"80 10 00 00 - 参数值 0x100000 (1048576)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"06 - 代表 initial_max_stream_data_bidi_remote 参数\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"04 - initial_max_stream_data_bidi_remote 参数长度为 0x04(4) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"80 10 00 00 - 参数值 0x100000 (1048576)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"07 - 代表 initial_max_stream_data_uni 参数\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"04 - initial_max_stream_data_bidi_remote 参数长度为 0x04(4) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"80 10 00 00 - 参数值 0x100000 (1048576)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"08 - 代表 initial_max_streams_bidi 参数\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"01 - initial_max_streams_bidi 参数长度为 0x01(1) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"0a - 参数值 0xA (10)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"09 - 代表 initial_max_streams_uni 参数\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"01 - initial_max_streams_uni 参数长度为 0x01(1) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"0a - 参数值 0xA (10)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"0a - 代表 ack_delay_exponent 参数\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"01 - ack_delay_exponent 参数长度为 0x01(1) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"03 - 参数值 0x03 (3)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"children\": [\n                    \"0a - 代表 GREASE 参数。\",\n                    {\n                      \"Tag\": \"a\",\n                      \"props\": {\n                        \"href\": \"https://zhuanlan.zhihu.com/p/343562875\"\n                      },\n                      \"content\": \"GREASE\"\n                    },\n                    \" 是一种用于防止中间盒不允许新扩展的技术，通过预先保留扩展值并将其随机地注入连接中来解决。\"\n                  ]\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"01 - GREASE 参数长度为 0x01(1) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"19 - 参数值 0x19 (25)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"0f - 代表 initial_source_connection_id 参数\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"05 - initial_source_connection_id 参数长度为 0x05(5) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"63 5f 63 69 64 - 数据包头中源连接标识 ID 的副本(s_cid)\"\n                }\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/QUIC/tls-encryptedExtensions.json",
    "content": "[\n  \"任何不需要协商更多加密密钥的扩展都会列在这里。加密以隐藏它们不被窃听者和中间件(middleboxes)发现。\",\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"TLS 握手消息头\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"08 00 00 56\"\n          },\n          [\n            \"每个 TLS 握手消息都以一个 type 和一个 len 开始。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"08 - 握手消息类型 0x08 (encrypted extensions)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 56 - 紧接着的扩展数据的长度 0x56 (86) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"扩展的具体数据长度\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 54\"\n          },\n          [\"00 54 - 扩展的具体数据长度为 0x54(84) 字节\"]\n        ],\n        [\n          \"扩展 - ALPN 协议\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 10 00 0b 00 09 08 70 69 6e 67 2f 31 2e 30\"\n          },\n          [\n            \"应用层协议协商，即 ALPN，被 QUIC 用来在服务器和客户端之间协商支持的协议和版本。\",\n            \"对应的应用层协议可能是：\\\"http/1.1\\\"、\\\"h2\\\"(HTTP/2)或 \\\"h3\\\"(HTTP/3)。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 10 - 表示这是 \\\"ALPN\\\" 扩展\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 0b - 扩展信息长度为 0xb(11) 字节 \"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 09 - ALPN 条目长度为 0x09(9) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"08 - 协议名称长度为 0x08(8) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"70 69 ... 2e 30 - 字符串 \\\"ping/1.0\\\"\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"扩展 - QUIC 协议参数\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 39 00 41 00 08 00 01 02 03 04 05 06 07 01 04 80 01 d4 c0 03 04 80 00 ff f7 04 04 80 50 00 00 05 04 80 08 00 00 06 04 80 08 00 00 07 04 80 08 00 00 08 01 02 09 01 02 0a 01 03 0b 01 19 0f 05 73 5f 63 69 64\"\n          },\n          [\n            \"这里给出了服务器端对 QUIC 连接的配置值。\",\n            \"以下是例子中的 QUIC 的设置参数：\",\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"original_destination_connection_id: 0001020304050607\\nmax_idle_timeout: 120000ms (2 minutes)\\nmax_udp_payload_size: 65527\\ninitial_max_data: 5242880\\ninitial_max_stream_data_bidi_local: 524288\\ninitial_max_stream_data_bidi_remote: 524288\\ninitial_max_stream_data_uni: 524288\\ninitial_max_streams_bidi: 2\\ninitial_max_streams_uni: 2\\nack_delay_exponent: 3\"\n              }\n            },\n            \"以下是相关字节的完整列表和解释：\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 39 - 表示这是 \\\"QUIC 协议参数\\\" 扩展\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 41 - 扩展信息长度为 0x31(65) 字节 \"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 - 代表 original_destination_connection_id 参数\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"08 - original_destination_connection_id 参数长度为 0x08(8) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 01 ... 06 07 - 客户端给出的初始连接ID(用于初始密钥)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"01 - 代表 max_idle_timeout 参数\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"04 - max_idle_timeout 参数长度为 0x04(4) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"80 01 d4 c0 - 参数值 0x1d4c0 (120000)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"03 - 代表 max_udp_payload_size 参数\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"04 - max_udp_payload_size 参数长度为 0x04(4) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"80 00 ff f7 - 参数值 0xfff7 (65527)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"04 - 代表 initial_max_data 参数\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"04 - initial_max_data 参数长度为 0x04(4) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"80 50 00 00 - 参数值 0x500000 (5242880)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"05 - 代表 initial_max_stream_data_bidi_local 参数\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"04 - initial_max_stream_data_bidi_local 参数长度为 0x04(4) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"80 08 00 00 - 参数值 0x80000 (524288)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"06 - 代表 initial_max_stream_data_bidi_remote 参数\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"04 - initial_max_stream_data_bidi_remote 参数长度为 0x04(4) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"80 08 00 00 - 参数值 0x80000 (524288)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"07 - 代表 initial_max_stream_data_uni 参数\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"04 - initial_max_stream_data_uni 参数长度为 0x04(4) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"80 08 00 00 - 参数值 0x80000 (524288)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"08 - 代表 initial_max_streams_bidi 参数\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"01 - initial_max_streams_bidi 参数长度为 0x01(1) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"02 - 参数值 0x02 (2)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"09 - 代表 initial_max_streams_uni 参数\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"01 - initial_max_streams_uni 参数长度为 0x01(1) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"02 - 参数值 0x02 (2)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"0a - 代表 ack_delay_exponent 参数\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"01 - ack_delay_exponent 参数长度为 0x01(1) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"03 - 参数值 0x03 (3)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"children\": [\n                    \"0a - 代表 GREASE 参数。\",\n                    {\n                      \"Tag\": \"a\",\n                      \"props\": {\n                        \"href\": \"https://zhuanlan.zhihu.com/p/343562875\"\n                      },\n                      \"content\": \"GREASE\"\n                    },\n                    \" 是一种用于防止中间盒不允许新扩展的技术，通过预先保留扩展值并将其随机地注入连接中来解决。\"\n                  ]\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"01 - GREASE 参数长度为 0x01(1) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"19 - 参数值 0x19 (25)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"0f - 代表 initial_source_connection_id 参数\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"05 - initial_source_connection_id 参数长度为 0x05(5) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"73 5f 63 69 64 - 数据包头中源连接标识 ID 的副本(s_cid)\"\n                }\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/QUIC/tls-serverHello.json",
    "content": "[\n  \"服务器回复 \\\"ServerHello\\\"。服务器提供的信息包括以下内容：\",\n  {\n    \"Tag\": \"ul\",\n    \"children\": [\n      {\n        \"Tag\": \"li\",\n        \"content\": \"服务器端随机数(在随后的握手时使用)\"\n      },\n      {\n        \"Tag\": \"li\",\n        \"content\": \"服务器端选择的加解密算法\"\n      },\n      {\n        \"Tag\": \"li\",\n        \"content\": \"用于密钥交换的公钥\"\n      },\n      {\n        \"Tag\": \"li\",\n        \"content\": \"服务器协商的具体协议的版本\"\n      }\n    ]\n  },\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"TLS 握手消息头\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"02 00 00 56\"\n          },\n          [\n            \"每个 TLS 握手消息都以一个 type 和一个 len 开始。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"02 - 握手消息类型 0x02 (server hello)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 56 - 紧接着的 server hello 数据的长度 0x56 (86) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"服务器端 TLS 协议版本号\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"03 03\"\n          },\n          [\n            \"协议版本为 \\\"3,3\\\"(指 TLS 1.2)。因为被广泛部署的中间件(middleboxes)并不允许他们不承认的协议版本，所以所有的 TLS 1.3 会话在这个字段中都以 TLS 1.2 版本表示。也因为如此，这个字段不再被使用，而是使用下面的 \\\"支持的版本\\\" 扩展来进行版本协商。\",\n            \"而不寻常的版本号(\\\"3,3\\\" 代表 TLS 1.2)是由于 TLS 1.0 是 SSL 3.0 协议的一个小修订。因此，TLS 1.0 用 \\\"3,1\\\" 表示，TLS 1.1 用 \\\"3,2\\\"，以此类推。\"\n          ]\n        ],\n        [\n          \"服务器端随机数\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f\"\n          },\n          [\n            \"服务器端提供的 32 字节的随机数。这个数将在之后的会话中使用。在本文的例子中，我们暂时将一个方便记忆的字符串当作随机数。\"\n          ]\n        ],\n        [\n          \"会话 ID\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00\"\n          },\n          [\n            \"这是一个废弃(legacy)字段，不在 QUIC 中使用。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 - 0 字节的会话 ID\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"所选择的加解密算法\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"13 01\"\n          },\n          [\n            \"服务器从客户端给出的选项列表中选择的加解密算法 0x1301(TLS_AES_128_GCM_SHA256)。\"\n          ]\n        ],\n        [\n          \"压缩算法\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00\"\n          },\n          [\"服务器从客户端给出的选项列表中选择的压缩算法 0x00(null)。\"]\n        ],\n        [\n          \"扩展的长度\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 2e\"\n          },\n          [\n            \"服务器向客户端返回的扩展有序列表的长度。因为服务器被禁止回复客户端 hello 消息中不存在的扩展，因此服务器知道客户端将理解并支持列出的所有扩展。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 2e - 扩展列表的长度为 0x2E(46) 字节\"\n                }\n              ]\n            },\n            \"每个扩展将以两个字节开始，表明它是哪个扩展。然后是两个字节的内容长度字段，最后是扩展的具体内容。\"\n          ]\n        ],\n        [\n          \"扩展 - 算法公钥列表(Key Share)\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 33 00 24 00 1d 00 20 9f d7 ad 6d cf f4 29 8d d3 f9 6d 5b 1b 2a f9 10 a0 53 5b 14 88 d7 f8 fa bb 34 9a 98 28 80 b6 15\"\n          },\n          [\n            \"服务器使用和客户端发送公钥时相同的算法发送一个自己的公钥。一旦这个被发送，加密密钥就可以被计算出来。其余的握手则将被加密。而不用像以前的协议版本，以透明的方式发送握手消息。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 33 - 表示这是 \\\"算法公钥列表\\\" 扩展\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 24 - 算法公钥列表长度为 0x24(36) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 1d - 代表 x25519 算法(例子中为通过 curve25519 算法进行密钥交换)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 20 - 公钥长度为 0x20(32) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"9f d7 ... b6 15 - \\\"服务器端准备密钥交换\\\" 步骤中生成的公钥\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"扩展 - 支持的版本\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 2b 00 02 03 04\"\n          },\n          [\n            \"客户端表明其支持 TLS 1.3。由于兼容性的原因，这被放在一个扩展中，而不是上面的客户端版本字段。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 2b - 表示这是 \\\"支持的版本\\\" 扩展\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 03 - 扩展信息长度为 0x03(3) 字节 \"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"02 - TLS 版本长度为 0x02(2) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"03 04 - 代表 TLS 1.3\"\n                }\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/RecOuter/Annotations/context.js",
    "content": "import React from \"react\";\n\nconst AnnotationContext = React.createContext();\n\nexport const AnnotationContextProvider = ({\n  children,\n  defaultFolded = true,\n}) => {\n  const [folding, setFolding] = React.useState(defaultFolded);\n\n  return (\n    <AnnotationContext.Provider value={{ folding, setFolding }}>\n      {children}\n    </AnnotationContext.Provider>\n  );\n};\n\nexport const useAnnotationContext = () => {\n  return React.useContext(AnnotationContext);\n};\n"
  },
  {
    "path": "src/RecOuter/Annotations/index.jsx",
    "content": "import React from \"react\";\nimport clsx from \"clsx\";\nimport classes from \"./style.module.css\";\nimport { renderExplanations } from \"../utils\";\nimport { useAnnotationContext } from \"./context\";\n\nexport const AnnotationToggler = () => {\n  const { folding, setFolding } = useAnnotationContext();\n\n  return (\n    <button\n      className={classes[\"annotate-toggle\"]}\n      onClick={() => setFolding(!folding)}\n    >\n      {folding ? \"展开注释\" : \"折叠注释\"}\n    </button>\n  );\n};\n\nconst Annotations = ({ type, data }) => {\n  const { folding } = useAnnotationContext();\n\n  return (\n    <>\n      <span className={classes[type]}>\n        {data.map((item, idx) => {\n          // item is not modifiable\n          const [label, ...restItem] = item;\n          const explanations = restItem.pop();\n\n          return (\n            <span\n              className={clsx(\n                classes[\"string\"],\n                !folding && classes[\"annotate\"]\n              )}\n              key={idx}\n            >\n              <span key=\"label\">{label}</span>\n              {restItem.map((info, i) => (\n                <span key={info.content} {...(info.props || {})}>\n                  {info.content}\n                </span>\n              ))}\n              <span\n                className={clsx(\n                  classes[\"explanation\"],\n                  !folding && classes[\"explanation-expanded\"]\n                )}\n                key=\"last\"\n              >\n                {renderExplanations([\n                  { Tag: \"h4\", content: label },\n                  ...explanations,\n                ])}\n              </span>\n            </span>\n          );\n        })}\n      </span>\n    </>\n  );\n};\n\nexport default Annotations;\n"
  },
  {
    "path": "src/RecOuter/Annotations/style.module.css",
    "content": ".annotate-toggle {\n  margin-bottom: 1em;\n\n  display: inline-block;\n  outline: none;\n  cursor: pointer;\n  text-align: center;\n  text-decoration: none;\n  font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n  font-size: 1em;\n  font-weight: bold;\n  padding: 0.4em 1.7em 0.45em;\n  text-shadow: 0 1px 0 var(--act-btn-shadow);\n  border-radius: 0.5em;\n  box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.3);\n\n  color: #fff;\n  border: solid 1px var(--act-btn-border);\n  background: linear-gradient(180deg, var(--act-btn0) 1%, var(--act-btn9) 100%);\n}\n.annotate-toggle:hover {\n  text-decoration: none;\n  background: linear-gradient(\n    180deg,\n    var(--act-hov-btn0) 1%,\n    var(--act-hov-btn9) 100%\n  );\n}\n.annotate-toggle:active {\n  position: relative;\n  top: 1px;\n}\n\n.record-data {\n  display: block;\n  font-family: Menlo, Monaco, Consolas, \"Courier New\", monospace;\n  position: relative;\n}\n.annotate:hover > :first-child {\n  display: none !important;\n}\n.record-data .string {\n  position: relative;\n}\n.record-data .string > :first-child {\n  display: none;\n  position: absolute;\n  background-color: #faf7dc;\n  border-radius: 5px;\n  margin: 2px 0;\n  padding: 2px 7px;\n  line-height: 1.2;\n  white-space: nowrap;\n  -webkit-user-select: none;\n  user-select: none;\n  top: -28px;\n  box-shadow: rgba(0, 0, 0, 0.3) 2px 2px 2px;\n}\n.record-data .string > :first-child::after {\n  content: \"\";\n  position: absolute;\n  box-shadow: rgba(0, 0, 0, 0.3) 2px 2px 2px;\n  transform: rotate(45deg);\n  bottom: -3px;\n  left: 10px;\n  border-width: 3px;\n  border-style: solid;\n  border-color: transparent #faf7dc #faf7dc transparent;\n  z-index: 9;\n}\n.record-data .string:hover > :first-child {\n  display: inline;\n}\n\n.explanation {\n  display: none;\n}\n.explanation-expanded {\n  position: relative;\n  display: block;\n  font-size: 0.9em;\n  color: black;\n  margin: 1em 0;\n  padding: 1em;\n  background-color: #faf7dc;\n  border: 2px solid #e0d998;\n  border-radius: 1em;\n  font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n  box-shadow: rgba(0, 0, 0, 0.3) 2px 2px 2px;\n}\n.record-data .explanation p {\n  margin-bottom: 0.35rem;\n}\n.record-data .explanation-expanded:before {\n  content: \" \";\n  position: absolute;\n  padding: 5px;\n  transform: rotate(45deg);\n  top: -7px;\n  left: 20px;\n  border: 2px solid;\n  background-color: #faf7dc;\n  border-color: #e0d998 transparent transparent #e0d998;\n}\n\nul {\n  margin: 0.75rem 0;\n}\n"
  },
  {
    "path": "src/RecOuter/CodeSample/index.jsx",
    "content": "import clsx from \"clsx\";\nimport React from \"react\";\nimport classes from \"./style.module.css\";\n\nconst CodeSample = ({ code = \"\" }) => {\n  const [show, setShow] = React.useState(false);\n\n  return (\n    <span className={clsx(classes[\"sample\"], show && classes[\"show\"])}>\n      <button className={classes[\"show-code\"]} onClick={() => setShow(!show)}>\n        {show ? \"隐藏\" : \"展示\"}代码\n      </button>\n      <pre>\n        <code>{code}</code>\n      </pre>\n    </span>\n  );\n};\n\nexport default CodeSample;\n"
  },
  {
    "path": "src/RecOuter/CodeSample/style.module.css",
    "content": "/***** .sample *****/\n\n.sample {\n  display: block;\n  margin: 1em 0;\n  position: relative;\n}\n\n.sample pre {\n  margin: 0;\n  height: 0;\n  padding: 0;\n}\n\n.sample button.show-code {\n  display: block;\n  clear: both;\n  box-shadow: inset 0 0 5px 0 var(--sam-btn-inset);\n  background: linear-gradient(\n    to bottom,\n    var(--sam-btn0) 1%,\n    var(--sam-btn9) 100%\n  );\n  border-radius: 5px;\n  border: 1px solid var(--sam-btn-border);\n  cursor: pointer;\n  color: white;\n  font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n  font-size: 15px;\n  font-weight: bold;\n  padding: 11px 23px;\n  text-decoration: none;\n  text-shadow: 0 1px 0 var(--sam-btn-shadow);\n}\n\n.sample button.show-code:hover {\n  background: linear-gradient(\n    to bottom,\n    var(--sam-hov-btn0) 1%,\n    var(--sam-hov-btn9) 100%\n  );\n}\n.sample button.show-code:active {\n  position: relative;\n  top: 1px;\n}\n.sample.show button.show-code {\n  position: absolute;\n  right: 0;\n  top: 0;\n  display: none;\n}\n.sample.show:hover button.show-code {\n  display: block;\n}\n.sample.show pre {\n  height: auto;\n  background-color: black;\n}\n\n.sample pre code {\n  font-size: inherit;\n  word-break: normal;\n  display: block;\n  color: white;\n  padding: 0.5em;\n  border-radius: 5px;\n}\n/***** annotation toggle button *****/\n"
  },
  {
    "path": "src/RecOuter/Math/index.jsx",
    "content": "import React from \"react\";\nimport classes from \"./style.module.css\";\n\nconst Types = {\n  Normal: 0,\n  NumericalLeading: 1,\n  SingleAlphabetOrNumericalLeading: 3,\n};\n\nconst Tokens = {\n  sup: \"^\",\n  sub: \"_\",\n};\n\nconst TokensRMap = Object.keys(Tokens).reduce(\n  (acc, curK) => ({ ...acc, [Tokens[curK]]: curK }),\n  {}\n);\n\nconst shiftable = (str = \"\") => {\n  const totalLen = str.length;\n  let curIdx = 0;\n\n  return {\n    shift: (sfLen = 1) => {\n      if (sfLen <= 0) throw RangeError(\"out of shift range: \" + sfLen);\n\n      const ender = Math.min(curIdx + sfLen, totalLen);\n      if (curIdx < totalLen) {\n        const result = str.slice(curIdx, ender);\n        curIdx = ender;\n        return result;\n      }\n      return undefined;\n    },\n    rest: () => {\n      if (curIdx >= totalLen) return undefined;\n      return str.slice(curIdx);\n    },\n    shiftIndex: () => curIdx,\n  };\n};\n\nconst typeTracker = (initType = Types.Normal) => {\n  let types = [initType];\n\n  return {\n    nextType: (type) => types.push(type),\n    handledType: () => {\n      if (!types.pop()) {\n        types = [initType];\n      }\n    },\n    curType: () => types[Math.max(types.length - 1, 0)],\n    priorType: () =>\n      types.length - 2 > 0 ? types[types.length - 2] : undefined,\n  };\n};\n\nconst resolver = (equation) => {\n  if (typeof equation !== \"string\") return equation;\n\n  let result = [],\n    curChar = \"x\",\n    pendingContent = [],\n    Token = undefined;\n\n  const { shift, shiftIndex, rest } = shiftable(equation);\n  const { nextType, curType, handledType, priorType } = typeTracker(\n    Types.Normal\n  );\n\n  const getNumerical = (str = \"\") => {\n    const numerical = str.match(/^-?\\d+/i)?.[0];\n\n    return numerical;\n  };\n\n  const isAlphabet = (str = \"\") => {\n    const numerical = str.match(/^[a-zA-Z]+/i)?.[0];\n\n    return !!numerical;\n  };\n\n  const pendingContentToResult = (Token = \"span\") => {\n    const joint = pendingContent.join(\"\");\n    pendingContent = [];\n    if (joint) result.push(<Token key={shiftIndex()}>{joint}</Token>);\n  };\n\n  const NumericalLeadingHandler = () => {\n    const numerical = getNumerical(curChar + rest());\n    if (numerical) {\n      if (numerical.length > 1) shift(numerical.length - 1);\n      pendingContent.push(numerical);\n      pendingContentToResult(Token);\n    } else {\n      result.push(curChar);\n    }\n    handledType();\n  };\n\n  while (true) {\n    curChar = shift();\n\n    if (curChar === undefined) {\n      pendingContentToResult(Token);\n      break;\n    }\n\n    switch (curType()) {\n      case Types.NumericalLeading:\n        NumericalLeadingHandler();\n        break;\n      case Types.SingleAlphabetOrNumericalLeading: {\n        const alphabet = isAlphabet(curChar);\n        if (alphabet) {\n          pendingContent.push(curChar);\n          pendingContentToResult(Token);\n          handledType();\n        } else {\n          NumericalLeadingHandler();\n        }\n        break;\n      }\n      case Types.Normal:\n        switch (curChar) {\n          case Tokens.sup:\n          case Tokens.sub:\n            nextType(Types.SingleAlphabetOrNumericalLeading);\n            pendingContent = [];\n            Token = TokensRMap[curChar];\n            break;\n          default:\n            result.push(curChar);\n        }\n        break;\n    }\n  }\n\n  // combine strings\n  return result.reduce((acc, cur) => {\n    const previous = acc[acc.length - 1];\n    if (typeof previous === \"string\" && typeof cur === \"string\") {\n      return [...acc.slice(0, Math.max(acc.length - 1, 0)), previous + cur];\n    }\n    return [...acc, cur];\n  }, []);\n};\n\nconst MathBlock = ({ children = \"\", style }) => {\n  return (\n    <span className={classes[\"math\"]} style={style}>\n      {resolver(children)}\n    </span>\n  );\n};\n\nexport default MathBlock;\n"
  },
  {
    "path": "src/RecOuter/Math/style.module.css",
    "content": ".math {\n  font-family: \"Times New Roman\", serif;\n  font-style: oblique;\n}\n.math sup {\n  top: -0.5em;\n}\n.math sub {\n  bottom: -0.25em;\n}\n"
  },
  {
    "path": "src/RecOuter/Table/index.jsx",
    "content": "import React from \"react\";\nimport { renderExplanations } from \"../utils\";\n\nconst Table = ({ headers, data, dataProps = [], style }) => {\n  return (\n    <table style={style}>\n      {headers && (\n        <thead>\n          <tr>{renderExplanations(headers, 0, { defaultTag: \"th\" })}</tr>\n        </thead>\n      )}\n      <tbody>\n        {data.map((item, idx) => {\n          const content = renderExplanations(item, 0, {\n            defaultTag: \"td\",\n          });\n          return (\n            <tr key={idx}>\n              {React.Children.map(content, (child, idx) => {\n                const props = dataProps[idx] || {};\n                if (child?.type !== \"td\") {\n                  return (\n                    <td key={child.key} {...props}>\n                      {child}\n                    </td>\n                  );\n                }\n                return React.cloneElement(child, props);\n              })}\n            </tr>\n          );\n        })}\n      </tbody>\n    </table>\n  );\n};\n\nexport default Table;\n"
  },
  {
    "path": "src/RecOuter/X25519/Calculator/PublicKeyMultiplier.jsx",
    "content": "import React from \"react\";\nimport { useHexValue, calcMulti, getHexValue } from \"./utils\";\n\nconst PublicKeyMultiplier = () => {\n  const [displayPrivkey, privkey, setPrivkey] = useHexValue(\"\");\n  const [displayPubkey, pubkey, setPubkey] = useHexValue(\"\");\n  const [result, setResult] = React.useState(\"\");\n\n  return (\n    <table>\n      <tbody>\n        <tr>\n          <td>\n            <label htmlFor=\"mult-n\">secret key (n):</label>\n          </td>\n          <td>\n            <input\n              id=\"mult-n\"\n              type=\"text\"\n              data-deco=\"hex-ify\"\n              placeholder=\"multiplier\"\n              value={displayPrivkey}\n              onInput={(e) => {\n                const hex = getHexValue(e.target.value);\n                setPrivkey(hex || 0n);\n                setResult(calcMulti({ privkey: hex, pubkey }));\n              }}\n            />\n          </td>\n        </tr>\n        <tr>\n          <td>\n            <label htmlFor=\"mult-point\">public key (Q):</label>\n          </td>\n          <td>\n            <input\n              id=\"mult-point\"\n              type=\"text\"\n              data-deco=\"hex-ify\"\n              placeholder=\"point\"\n              value={displayPubkey}\n              onInput={(e) => {\n                const hex = getHexValue(e.target.value);\n                setPubkey(hex || 0n);\n                setResult(calcMulti({ privkey, pubkey: hex }));\n              }}\n            />\n          </td>\n        </tr>\n        <tr>\n          <td>\n            <label htmlFor=\"mult-result\">new point (nQ):</label>\n          </td>\n          <td>\n            <input\n              id=\"mult-result\"\n              type=\"text\"\n              placeholder=\"result\"\n              readOnly\n              value={result}\n            />\n          </td>\n        </tr>\n      </tbody>\n    </table>\n  );\n};\n\nexport default PublicKeyMultiplier;\n"
  },
  {
    "path": "src/RecOuter/X25519/Calculator/SecretKeyMultiplier.jsx",
    "content": "import React from \"react\";\nimport classes from \"./style.module.css\";\nimport { useHexValue, calcPubkey, getHexValue } from \"./utils\";\n\nconst plusOne = (privkey = 0n) => privkey + 1n;\nconst double = (privkey) => (privkey ? privkey * 2n : 1n);\nconst clamp = (privkey = 0n) => {\n  let n = privkey;\n  n %= 2n ** 255n;\n  n &= 2n ** 255n - 8n;\n  n |= 1n << 254n;\n\n  return n;\n};\n\nconst SecretKeyMultiplier = () => {\n  const [displayPrivkey, privkey, setPrivkey] = useHexValue(\"0x1\");\n  const [result, setResult] = React.useState(\"\");\n\n  React.useEffect(() => {\n    setResult(calcPubkey({ privkey, setPrivkey }));\n  }, []);\n\n  return (\n    <table>\n      <tbody>\n        <tr>\n          <td></td>\n          <td style={{ textAlign: \"right\" }}>\n            <button\n              type=\"button\"\n              id=\"pubkey-plus-one\"\n              onClick={() => {\n                const newPrivkey = plusOne(privkey);\n                setPrivkey(newPrivkey);\n                setResult(calcPubkey({ privkey: newPrivkey, setPrivkey }));\n              }}\n            >\n              +1\n            </button>\n            <button\n              type=\"button\"\n              id=\"pubkey-double\"\n              onClick={() => {\n                const newPrivkey = double(privkey);\n                setPrivkey(newPrivkey);\n                setResult(calcPubkey({ privkey: newPrivkey, setPrivkey }));\n              }}\n            >\n              x2\n            </button>\n            <button\n              type=\"button\"\n              id=\"pubkey-clamp\"\n              onClick={() => {\n                const newPrivkey = clamp(privkey);\n                setPrivkey(newPrivkey);\n                setResult(calcPubkey({ privkey: newPrivkey, setPrivkey }));\n              }}\n            >\n              Clamp\n            </button>\n            <span\n              className={classes[\"help-tip\"]}\n              data-tooltip-text=\"箝位(Clamp)：设置曲线安全所必需的 5 位，详细说明见下面的 Q&A 章节。\"\n            >\n              ?\n            </span>\n          </td>\n        </tr>\n        <tr>\n          <td>\n            <label htmlFor=\"pubkey-privkey\">secret key (n):</label>\n          </td>\n          <td>\n            <input\n              id=\"pubkey-privkey\"\n              type=\"text\"\n              data-deco=\"hex-ify\"\n              value={displayPrivkey}\n              onInput={(e) => {\n                const hex = getHexValue(e.target.value);\n                setPrivkey(hex || 0n);\n                setResult(\n                  calcPubkey({\n                    privkey: hex,\n                    setPrivkey,\n                  })\n                );\n              }}\n            />\n          </td>\n        </tr>\n        <tr>\n          <td>\n            <label htmlFor=\"pubkey-result\">public key (nP):</label>\n          </td>\n          <td>\n            <input id=\"pubkey-result\" type=\"text\" value={result} readOnly />\n          </td>\n        </tr>\n      </tbody>\n    </table>\n  );\n};\n\nexport default SecretKeyMultiplier;\n"
  },
  {
    "path": "src/RecOuter/X25519/Calculator/YCoordinate.jsx",
    "content": "import React, { useState } from \"react\";\nimport { useHexValue, calcY, getHexValue } from \"./utils\";\nimport MathBlock from \"../../Math\";\n\nconst YCoordinate = () => {\n  const [displayX, x, setX] = useHexValue(\"0x0\");\n  const [yResults, setYResults] = useState([\"\", \"\"]);\n\n  React.useEffect(() => {\n    setYResults(calcY({ x }));\n  }, []);\n\n  return (\n    <table>\n      <tbody>\n        <tr>\n          <td>\n            <label htmlFor=\"xy-x\">\n              <MathBlock>x</MathBlock>:\n            </label>\n          </td>\n          <td>\n            <input\n              id=\"xy-x\"\n              type=\"text\"\n              data-deco=\"hex-ify\"\n              value={displayX}\n              onInput={(e) => {\n                const hex = getHexValue(e.target.value);\n                setX(hex || 0n);\n                setYResults(calcY({ x: hex }));\n              }}\n            />\n          </td>\n        </tr>\n        <tr>\n          <td>\n            <label htmlFor=\"xy-y1\">\n              <MathBlock>y_1</MathBlock>:\n            </label>\n          </td>\n          <td>\n            <input id=\"xy-y1\" type=\"text\" value={yResults[0]} readOnly />\n          </td>\n        </tr>\n        <tr>\n          <td>\n            <label htmlFor=\"xy-y2\">\n              <MathBlock>y_2</MathBlock>:\n            </label>\n          </td>\n          <td>\n            <input id=\"xy-y2\" type=\"text\" value={yResults[1]} readOnly />\n          </td>\n        </tr>\n      </tbody>\n    </table>\n  );\n};\n\nexport default YCoordinate;\n"
  },
  {
    "path": "src/RecOuter/X25519/Calculator/index.jsx",
    "content": "import React from \"react\";\nimport classes from \"./style.module.css\";\nimport { startCase } from \"./utils\";\nimport SecretKeyMultiplier from \"./SecretKeyMultiplier\";\nimport PublicKeyMultiplier from \"./PublicKeyMultiplier\";\nimport YCoordinate from \"./YCoordinate\";\n\nconst Calculator = ({ type = \"\" }) => {\n  let calculator;\n\n  if (type === \"SecretKeyMultiplier\") calculator = <SecretKeyMultiplier />;\n  if (type === \"PublicKeyMultiplier\") calculator = <PublicKeyMultiplier />;\n  if (type === \"YCoordinate\") calculator = <YCoordinate />;\n\n  return (\n    <div className={classes[\"calculator\"]}>\n      <div style={{ fontWeight: \"bold\", paddingBottom: \"1em\" }}>\n        {startCase(type)}\n      </div>\n      {calculator}\n    </div>\n  );\n};\n\nexport default Calculator;\n"
  },
  {
    "path": "src/RecOuter/X25519/Calculator/style.module.css",
    "content": ".calculator {\n  max-width: 48em;\n  display: block;\n  font-size: 14px;\n  margin: 1em auto;\n  padding: 1em;\n  background-color: antiquewhite;\n  border: 2px solid #b6a083;\n  border-radius: 1em;\n  box-shadow: 1px 1px 3px rgba(0, 0, 0, 0.3);\n}\n\n.calculator table {\n  width: 100%;\n}\n\n.calculator table td {\n  padding: 2px;\n}\n\n.calculator table td:first-child {\n  text-align: right;\n  min-width: 8em;\n}\n\n.calculator button {\n  touch-action: manipulation;\n  user-select: none;\n  -webkit-user-select: none;\n  margin: 2px;\n  display: inline-block;\n  text-align: center;\n  text-decoration: none;\n  font: inherit;\n  font-family: Arial, Helvetica, sans-serif;\n  padding: 3px 11px 4px;\n  text-shadow: 1px 1px 1px rgba(255, 255, 255, 0.3);\n  border-radius: 7px;\n  box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.3);\n  color: #111;\n  border: solid 1px #c5a884;\n  background: bisque;\n  background: linear-gradient(180deg, #f8e8d5 1%, #f5ddc3 100%);\n}\n\n.calculator button:hover {\n  background: bisque;\n  background: linear-gradient(180deg, #f5ddc3 1%, #f8e8d5 100%);\n}\n.calculator button:active {\n  position: relative;\n  top: 1px;\n}\n\n.help-tip {\n  display: inline-block;\n  position: relative;\n  font-size: 75%;\n  top: -0.5em;\n  text-align: center;\n  min-width: 1.4em;\n  color: #444;\n  background-color: #f8e8d5;\n  border-radius: 10px;\n  border: #c5a884 1px solid;\n  user-select: none;\n  cursor: help;\n}\n\n.help-tip[data-tooltip-text]:hover::after {\n  text-align: left;\n  background-color: #000;\n  background-color: rgba(0, 0, 0, 0.7);\n  box-shadow: 0 0 3px 1px rgba(50, 50, 50, 0.4);\n  border-radius: 5px;\n  color: #ffffff;\n  font-size: 12px;\n  content: attr(data-tooltip-text);\n  top: 130%;\n  right: -1em;\n  padding: 7px 12px;\n  position: absolute;\n  width: auto;\n  min-width: 11em;\n  z-index: 9999;\n}\n\n.calculator label {\n  display: inline-block;\n  max-width: 100%;\n  margin-bottom: 5px;\n  font-weight: bold;\n}\n\n.calculator input {\n  width: 36em;\n  font-family: \"Courier\", monospace;\n  letter-spacing: -1px;\n  text-align: right;\n  margin: 0;\n  font: inherit;\n  color: inherit;\n  line-height: inherit;\n}\n\n.calculator input:read-only {\n  background-color: transparent;\n  border-color: transparent;\n}\n\n@media (max-width: 700px) {\n  .calculator input {\n    max-width: 25em;\n    text-overflow: ellipsis;\n  }\n}\n\n@media (max-width: 450px) {\n  .calculator input {\n    max-width: 15em;\n    text-overflow: ellipsis;\n  }\n}\n"
  },
  {
    "path": "src/RecOuter/X25519/Calculator/utils.js",
    "content": "import React from \"react\";\nimport field from \"../field\";\nimport curve from \"../curve\";\n\nexport function getHexValue(raw = \"\", def) {\n  if (!raw.startsWith(\"0x\") && raw.match(/^[0-9a-fA-F]+$/)) {\n    raw = \"0x\" + raw;\n  }\n  if (raw.match(/^0[^x]+$/)) {\n    raw = \"0x\" + raw.substring(1);\n  }\n  if (raw.match(/^0*$/)) {\n    return def;\n  }\n  try {\n    return BigInt(raw);\n  } catch {\n    return def;\n  }\n}\n\nfunction setHexValue(callback = () => {}) {\n  return (n = \"\") => {\n    callback(`0x${n.toString(16)}`);\n  };\n}\n\nexport const useHexValue = (init = \"0x1\") => {\n  const [raw, setRaw] = React.useState(init);\n\n  return [raw, getHexValue(raw), setHexValue(setRaw)];\n};\n\nexport const calcPubkey = (ctx) => {\n  let result = \"\";\n  try {\n    let n = ctx.privkey;\n    if (n) {\n      const nn = n % 2n ** 255n;\n      if (n !== nn) {\n        n = nn;\n        console.log(\"calc\", n);\n        ctx.setPrivkey(n);\n      }\n    }\n    if (!n) {\n      return \"N/A\";\n    }\n    let { x, z } = curve.pointMult(9n, n);\n    let pubkey = curve.X(x, z);\n    result = `0x${field.toHex(pubkey, 256)}`;\n  } catch (e) {\n    result = `Error: ${e}`;\n  }\n\n  return result;\n};\n\nexport const calcMulti = (ctx) => {\n  let result = \"N/A\";\n  try {\n    let n = ctx.privkey;\n    let point = ctx.pubkey;\n    if (!n || !point) {\n      return result;\n    }\n    n %= 2n ** 255n;\n    point %= field.p;\n    let { x, z } = curve.pointMult(point, n);\n    let X = curve.X(x, z);\n    result = `0x${field.toHex(X, 256)}`;\n  } catch (e) {\n    result = `Error: ${e}`;\n  }\n\n  return result;\n};\n\nexport const calcY = (ctx) => {\n  let y1result = \"\";\n  let y2result = \"\";\n  try {\n    y1result = \"N/A\";\n    y2result = \"N/A\";\n    let x = ctx.x;\n    x %= field.p;\n    let [y1, y2] = curve.Y(x);\n    y1result = `0x${field.toHex(y1)}`;\n    y2result = `0x${field.toHex(y2)}`;\n  } catch (e) {\n    if (e instanceof RangeError) {\n      y1result = y2result = `x=0x${field.toHex(ctx.x)} 的点不在曲线上`;\n    } else {\n      y1result = e.message;\n      y2result = e.message;\n    }\n  }\n\n  return [y1result, y2result];\n};\n\nexport const startCase = (str = \"\") => {\n  return str.replace(/(?:[A-Z]|\\s+[a-zA-Z])\\w+?/g, ($1) => \" \" + $1).trim();\n};\n"
  },
  {
    "path": "src/RecOuter/X25519/Flex/index.jsx",
    "content": "import React from \"react\";\nimport classes from \"./style.module.css\";\nimport { renderExplanations } from \"../../utils\";\n\nconst FlexContainer = ({ Right, RightProps = {}, children, ...props }) => {\n  return (\n    <div className={classes[\"container\"]} {...props}>\n      <div>{children}</div>\n      <div {...RightProps}>\n        {renderExplanations(Right, 0, { defaultTag: \"div\" })}\n      </div>\n    </div>\n  );\n};\n\nexport default FlexContainer;\n"
  },
  {
    "path": "src/RecOuter/X25519/Flex/style.module.css",
    "content": ".container {\n  display: flex;\n  width: 100%;\n  flex-direction: row;\n}\n"
  },
  {
    "path": "src/RecOuter/X25519/QA/index.jsx",
    "content": "import React from \"react\";\nimport classes from \"./style.module.css\";\nimport { renderExplanations } from \"../../utils\";\n\nconst QA = ({ content }) => {\n  return (\n    <>\n      {content.map((child, idx) => {\n        const mark = idx === 0 ? \"Q:\" : idx === 1 ? \"A:\" : undefined;\n        if (mark) {\n          return (\n            <div key={idx}>\n              <span className={classes[\"qa-mark\"]}>{mark}</span>\n              {...renderExplanations(\n                Array.isArray(child) ? child : [child],\n                1,\n                {\n                  defaultTag: React.Fragment,\n                }\n              )}\n            </div>\n          );\n        }\n      })}\n    </>\n  );\n};\n\nexport default QA;\n"
  },
  {
    "path": "src/RecOuter/X25519/QA/style.module.css",
    "content": ".qa-mark {\n  margin-top: 12px;\n  margin-right: 4px;\n  display: inline-block;\n  font-family: \"Baskerville\", sans-serif;\n  font-size: 3em;\n  line-height: 0.9em;\n  color: #666;\n}\n"
  },
  {
    "path": "src/RecOuter/X25519/curve.js",
    "content": "import field from './field';\n\nfunction curve(basePointX = 9n) {\n  const curveA = 486662n;\n\n  /**\n   * Given intermediate ratio x/z for a point, compute X=(x/z)\n   * @param {BigInt} x\n   * @param {BigInt} z\n   * @return {BigInt} X = x/z\n   */\n  function X(x, z) {\n    return field.reduce(x * field.inverseOf(z));\n  }\n\n  const doubleA24 = (curveA + 2n) / 4n;\n  /**\n   * Double the point P at X=x/z\n   *\n   *     X_{2n} = (X_n+Z_n)^2(X_n-Z_n)^2\n   *     Z_{2n} = (4X_nZ_n)((X_n-Z_n)^2+((A+2)/4)(4X_nZ_n))\n   *\n   * @param x {BigInt} from intermediate ratio form of X=x/z for point P\n   * @param z {BigInt} from intermediate ratio form of X=x/z for point P\n   * @return {{x: BigInt, z: BigInt}} x/z for point 2P\n   */\n  function pointDouble(x, z) {\n    let x2_1 = (x + z) * (x + z);\n    let x2_2 = (x - z) * (x - z);\n    let x2 = field.reduce(x2_1 * x2_2);\n    let z2_1 = field.reduce(4n * x * z);\n    let z2_2 = field.reduce(x - z) * field.reduce(x - z);\n    let z2_3 = doubleA24 * z2_1;\n    let z2_23 = z2_2 + z2_3;\n    let z2 = z2_1 * z2_23;\n    return { x: field.reduce(x2), z: field.reduce(z2) };\n  }\n\n  /**\n   * Given X coordinates for nP and (n-1)P, calculate for (n+1)P\n   *\n   *    X_{n+1} = Z_{n-1}((X_n-Z_n)(X_1+Z_1)+(X_n+Z_n)(X_1-Z_1))^2\n   *    Z_{n+1} = X_{n-1}((X_n-Z_n)(X_1+Z_1)-(X_n+Z_n)(X_1-Z_1))^2\n   *\n   * @param x {BigInt} X coordinate of current point n, in intermediate x/z form.\n   * @param z {BigInt} X coordinate of current point n, in intermediate x/z form.\n   * @param prevX {BigInt} X coordinate of point n-1, in intermediate x/z form.\n   * @param prevZ {BigInt} X coordinate of point n-1, in intermediate x/z form.\n   * @returns {{x: BigInt, z: BigInt}} the X coordinate of point n+1, in intermediate x/z form.\n   */\n  function pointAdd1(x, z, prevX, prevZ) {\n    let [baseX, baseZ] = [basePointX, 1n];\n    let xa = (x - z) * (baseX + baseZ);\n    let xb = (x + z) * (baseX - baseZ);\n    let xc = field.square(xa + xb);\n    let x_nplus1 = prevZ * xc;\n\n    let zc = field.square(xa - xb);\n    let z_nplus1 = prevX * zc;\n\n    return { x: field.reduce(x_nplus1), z: field.reduce(z_nplus1) };\n  }\n\n  /**\n   * Conditional swap of two values.\n   *\n   * Adapted from RFC7748, with constant-time magic removed since we're using BigInt anyway.\n   *\n   * @param swap {Boolean} whether to swap\n   * @param a {BigInt}\n   * @param b {BigInt}\n   * @returns {[BigInt, BigInt]} the values a and b, swapped if needed.\n   */\n  let cswap = (swap, a, b) => {\n    return swap ? [b, a] : [a, b];\n  };\n\n  const multA24 = (curveA - 2n) / 4n;\n  /**\n   * Scalar multiplication of a point\n   *\n   * Given an X-coordinate for point P, \"point add\" it to itself n times to yield x/z for nP.\n   * Adapted from RFC7748.\n   *\n   * @param X {BigInt} X-coordinate for point P\n   * @param n {BigInt} multiplicand\n   * @return {{x: BigInt, z: BigInt}} X-Coordinate for point nP in x/z intermediate form\n   */\n  function pointMult(X, n) {\n    let x_1 = X;\n    let x_2 = 1n;\n    let z_2 = 0n;\n    let x_3 = X;\n    let z_3 = 1n;\n    let swap = 0;\n\n    for (let t = 255n; t >= 0n; t--) {\n      let k_t = (n >> t) & 1n;\n      swap ^= k_t !== 0n ? 1 : 0;\n      [x_2, x_3] = cswap(!!swap, x_2, x_3);\n      [z_2, z_3] = cswap(!!swap, z_2, z_3);\n      swap = k_t !== 0n ? 1 : 0;\n\n      let A = x_2 + z_2;\n      let AA = field.square(A);\n      let B = x_2 - z_2;\n      let BB = field.square(B);\n      let E = AA - BB;\n      let C = x_3 + z_3;\n      let D = x_3 - z_3;\n      let DA = D * A;\n      let CB = C * B;\n      x_3 = field.reduce((DA + CB) * (DA + CB));\n      z_3 = field.reduce(x_1 * (DA - CB) * (DA - CB));\n      x_2 = field.reduce(AA * BB);\n      z_2 = field.reduce(E * (AA + multA24 * E));\n    }\n    let _rest;\n    [x_2, ..._rest] = cswap(!!swap, x_2, x_3);\n    [z_2, ..._rest] = cswap(!!swap, z_2, z_3);\n    return { x: x_2, z: z_2 };\n  }\n\n  /**\n   * For the given X coordinate, find Y on the curve.\n   * @param X {BigInt} X coordinate in range 0..2**255-19-1\n   * @return {BigInt[2]} the two Y coordinates for X\n   * @throw {RangeError} X is not a valid coordinate on the curve (true for half of all inputs)\n   */\n  function Y(X) {\n    let YY = field.pow(X, 3n) + curveA * field.pow(X, 2n) + X;\n    try {\n      return field.sqrt(YY % field.p);\n    } catch (e) {\n      if (e instanceof RangeError) {\n        throw RangeError(`x=0x${field.toHex(X)} does not have points on curve`);\n      }\n    }\n  }\n\n  return {\n    basePointX,\n    X,\n    Y,\n    pointDouble,\n    pointAdd1,\n    pointMult,\n  };\n}\n\nexport default curve();\n"
  },
  {
    "path": "src/RecOuter/X25519/field.js",
    "content": "// duplication from: https://github.com/syncsynchalt/illustrated-x25519/tree/main/js\nfunction field(p = 2n ** 255n - 19n) {\n  /**\n   * Returns a three-tuple (gcd, x, y) such that\n   * a * x + b * y == gcd, where gcd is the greatest\n   * common divisor of a and b.\n   *\n   * This function implements the extended Euclidean\n   * algorithm and runs in O(log b) in the worst case.\n   *\n   * @param {BigInt} a\n   * @param {BigInt} b\n   * @returns {{gcd: BigInt, x: BigInt, y: BigInt}} containing GCD and x,y such that a*x + b*y == gcd\n   */\n  function extended_euclidean_algorithm(a, b) {\n    let s = 0n,\n      old_s = 1n;\n    let t = 1n,\n      old_t = 0n;\n    let r = b,\n      old_r = a;\n    let tmp = undefined;\n\n    while (r !== 0n) {\n      let quotient = old_r / r;\n\n      tmp = old_r - quotient * r;\n      old_r = r;\n      r = tmp;\n      tmp = old_s - quotient * s;\n      old_s = s;\n      s = tmp;\n      tmp = old_t - quotient * t;\n      old_t = t;\n      t = tmp;\n    }\n    return {\n      gcd: old_r,\n      x: old_s,\n      y: old_t,\n    };\n  }\n\n  /**\n   * @param {BigInt} n\n   * @return {BigInt}\n   */\n  function square(n) {\n    return n * n;\n  }\n\n  /**\n   * Reduce a number to modulo p (into the positive range of this field).\n   * @param {BigInt} n\n   * @return {BigInt} result\n   */\n  function reduce(n) {\n    n %= p;\n    if (n < 0) {\n      n += p;\n    }\n    return n;\n  }\n\n  /**\n   * Returns the multiplicative inverse of n modulo p.\n   *\n   * @param {BigInt} n\n   * @returns {BigInt} m such that (n * m) % p == 1.\n   */\n  function inverseOf(n) {\n    if (n === 0n) {\n      throw Error(\"Illegal argument zero\");\n    }\n    // noinspection JSUnusedLocalSymbols\n    let { _gcd, x, _y } = extended_euclidean_algorithm(n, p);\n    return reduce(x);\n  }\n\n  /**\n   * \"By factoring out powers of 2, find Q and S such that p−1 = Q*2^S with Q odd\"\n   * @returns {BigInt[2]} array of [Q, S]\n   */\n  let shanksPartitions = (prime) => {\n    let Q = prime - 1n;\n    let S = 0n;\n\n    while (Q !== 0n && Q % 2n === 0n) {\n      Q >>= 1n;\n      S += 1n;\n    }\n    if (!Q) {\n      throw Error(\"Unexpected failure to factor out Shanks partitions\");\n    }\n    return [Q, S];\n  };\n\n  /**\n   * Modular exponentiation - find n^e mod p efficiently.\n   * @param n {BigInt} number\n   * @param e {BigInt} exponent\n   * @return {BigInt} n**e mod p\n   */\n  function pow(n, e) {\n    // result = x * y**e, keep this true while reducing y and e\n    let x = 1n;\n    let y = n;\n    for (;;) {\n      if (e === 1n) {\n        return (x * y) % p;\n      } else if (e % 2n === 1n) {\n        e -= 1n;\n        x = (x * y) % p;\n      } else {\n        e /= 2n;\n        y = y ** 2n % p;\n      }\n    }\n  }\n\n  /**\n   * Use Euler's Criterion to test whether n has valid roots in Fp.\n   * @param n {BigInt} number to be tested\n   * @returns {boolean} true if n is a square in Fp\n   */\n  let eulersCriterion = (n) => {\n    const pHalf = (p - 1n) / 2n;\n    return pow(n, pHalf) === 1n;\n  };\n\n  /**\n   * Given one root in Fp, derive and return the pair.\n   * @param r {BigInt} one root in Fp\n   * @returns {BigInt[2]} pair of roots [r,-r] mod p\n   */\n  let rootsFor = (r) => {\n    return [r, p - r];\n  };\n\n  /**\n   * Find the square roots of n in Fp, if any.\n   *\n   * @param n {BigInt}\n   * @throws {RangeError} if n has no roots in Fp\n   * @return {BigInt[2]} the two square roots of n\n   */\n  function sqrt(n) {\n    if (n === 0n) {\n      return [0n, 0n];\n    }\n    if (!eulersCriterion(n)) {\n      throw RangeError(`${n} has no roots in Fp`);\n    }\n\n    // Tonelli–Shanks algorithm\n    let [Q, S] = shanksPartitions(p);\n\n    // find a z which is not a square\n    let z;\n    for (z = 2n; z < p; z++) {\n      if (!eulersCriterion(z)) {\n        break;\n      }\n    }\n\n    let M = S;\n    let c = pow(z, Q);\n    let t = pow(n, Q);\n    let R = pow(n, (Q + 1n) / 2n);\n    for (;;) {\n      if (t === 0n) {\n        return rootsFor(0n);\n      }\n      if (t === 1n) {\n        return rootsFor(R);\n      }\n      // use repeated squaring to find the least i, 0 < i < M, such that t^{2^i} = 1 mod p\n      let i = 1n;\n      for (; i < M; i++) {\n        let chk = pow(t, 2n ** i);\n        if (chk === 1n) {\n          break;\n        }\n      }\n      let b = c ** (2n ** (M - i - 1n));\n      M = i;\n      let bb = b * b;\n      c = bb % p;\n      t = (t * bb) % p;\n      R = (R * b) % p;\n    }\n  }\n\n  /**\n   * Return the bignum as a hex string, padded with zeros.\n   * @param {BigInt} a\n   * @param {Number=} bits number of bits output to zero-pad to (rounded up to 8-boundary)\n   * @return {string}\n   */\n  function toHex(a, bits) {\n    bits = Number(bits || 0);\n    let nibbles = 2 * Math.floor((bits + 7) / 8);\n    let result = a.toString(16);\n    if (result.length < nibbles) {\n      result = \"0\".repeat(nibbles - result.length) + result;\n    }\n    return result;\n  }\n\n  /**\n   * Given a hex string reverse its endianness, e.g. \"abcdef\" => \"efcdab\"\n   * @param s {String} hex string\n   * @return {String} flipped result\n   */\n  function hexFlipEndian(s) {\n    return s.match(/../g).reverse().join(\"\");\n  }\n\n  return { square, reduce, inverseOf, pow, sqrt, toHex, hexFlipEndian, p };\n}\n\nexport default field();\n"
  },
  {
    "path": "src/RecOuter/X25519/index.js",
    "content": "export { default as Calculator } from \"./Calculator\";\nexport { default as FlexContainer } from \"./Flex\";\nexport { default as QA } from \"./QA\";\n"
  },
  {
    "path": "src/RecOuter/index.jsx",
    "content": "import clsx from \"clsx\";\nimport React from \"react\";\nimport classes from \"./style.module.css\";\nimport { AnnotationContextProvider } from \"./Annotations/context\";\nimport { useSluggerContext } from \"../context/slugger\";\nimport { renderExplanations } from \"./utils\";\n\nconst RecOuter = ({\n  types = [],\n  id = \"\",\n  label = \"\",\n  illustration,\n  eagerLoad = false,\n  json,\n}) => {\n  const { curSlug, changeCurSlug, getSlug } = useSluggerContext();\n  const slug = React.useMemo(() => getSlug(id || label), [id, label]);\n  const [labelOriginalSize, setLabelOriginalSize] = React.useState();\n  const labelRef = React.useRef();\n  const [lazyExplanation, setLazyExplanation] = React.useState();\n\n  React.useLayoutEffect(() => {\n    if (curSlug === slug || eagerLoad) {\n      if (!lazyExplanation && typeof json === \"function\") {\n        json().then(setLazyExplanation);\n      }\n    }\n  }, [curSlug, json, eagerLoad]);\n\n  React.useEffect(() => {\n    if (curSlug === slug) {\n      // we have some expanding action to do, so need to delay it\n      window.requestAnimationFrame(() => {\n        try {\n          document.querySelector(\"#\" + slug)?.scrollIntoView?.({\n            behavior: \"smooth\",\n            block: \"nearest\",\n            inline: \"nearest\",\n          });\n\n          // maybe its not a valid selector\n        } catch (e) {}\n      });\n    }\n  }, [curSlug, slug]);\n\n  return (\n    <div className={classes[\"rec-outer\"]}>\n      <div\n        id={slug}\n        className={clsx(\n          ...types.map((t) => classes[t]),\n          curSlug === slug && classes[\"selected\"]\n        )}\n        onClick={(e) => {\n          const client = { x: e.clientX, y: e.clientY };\n          const currentPos = labelRef.current?.getBoundingClientRect();\n          // TODO: we lost our label, maybe we should reload this component/page entirely\n          if (!currentPos || !labelOriginalSize) return;\n\n          const rect = {\n            width: labelOriginalSize.width,\n            height: labelOriginalSize.height,\n            x: currentPos.x,\n            y: currentPos.y,\n          };\n          if (client.x > rect.x && client.x - rect.x < rect.width) {\n            if (client.y > rect.y && client.y - rect.y < rect.height) {\n              const willSelect = currentPos.width >= rect.width;\n              changeCurSlug(willSelect ? slug : \"\");\n\n              // only stop the label's event\n              e.stopPropagation();\n              e.preventDefault();\n            }\n          }\n        }}\n      >\n        <div\n          className={classes[\"rec-label\"]}\n          ref={(ref) => {\n            if (ref) {\n              !labelOriginalSize &&\n                setLabelOriginalSize(ref.getBoundingClientRect());\n              labelRef.current = ref;\n            }\n          }}\n        >\n          {label}\n        </div>\n        {illustration && (\n          <img\n            className={classes[\"illustration\"]}\n            src={illustration.src}\n            width={illustration.width}\n            height={illustration.height}\n            alt={illustration.alt || \"key of \" + label}\n            loading=\"lazy\"\n          />\n        )}\n        <div className={classes[\"rec-explanation\"]}>\n          <AnnotationContextProvider>\n            {!lazyExplanation ? (\n              <div>Loading...</div>\n            ) : (\n              renderExplanations(lazyExplanation)\n            )}\n          </AnnotationContextProvider>\n        </div>\n      </div>\n    </div>\n  );\n};\n\nexport default RecOuter;\n"
  },
  {
    "path": "src/RecOuter/style.module.css",
    "content": ".rec-outer {\n  max-width: 800px;\n  margin: 0.8em auto;\n  position: relative;\n}\n\n.server {\n  background-color: var(--server-bg);\n}\n\n.client {\n  background-color: var(--client-bg);\n}\n\n.embedded {\n  margin-left: 2em;\n}\n.embedded::before {\n  content: \"➥\";\n  left: 0;\n  top: 0;\n  position: absolute;\n  display: inline-block;\n  font-size: 2em;\n  color: var(--chunky-text);\n}\n\n.record,\n.calculation,\n.stem {\n  max-width: 800px;\n  padding: 1em;\n  border-radius: 1em;\n  border: 2px solid transparent;\n}\n\n.record:hover,\n.calculation:hover {\n  border: 2px solid #666;\n}\n\n.client.record .rec-label:before {\n  content: \"❱ \";\n  color: var(--chunky-text);\n}\n\n.server.record .rec-label:before {\n  content: \"❰ \";\n  color: var(--chunky-text);\n}\n\n.calculation .rec-label:before {\n  content: \"± \";\n  font-weight: bold;\n  color: var(--chunky-text);\n}\n\n.stem .rec-label:before {\n  content: \"❡ \";\n  color: var(--chunky-text);\n}\n\n.rec-label {\n  cursor: pointer;\n  font-size: 1.3em;\n  user-select: none;\n  overflow: visible;\n  transform: translateX(0px);\n\n  white-space: nowrap;\n  text-align: center;\n  width: 100%;\n  transition: all 0.3s;\n}\n.rec-label a {\n  color: inherit;\n  text-decoration: none;\n}\n\n.record .illustration,\n.calculation .illustration {\n  margin: -40px 0 0 0;\n  display: none;\n  float: right;\n  user-select: none;\n}\n\n.rec-explanation {\n  display: none;\n  margin-top: 1em;\n  margin-bottom: 1em;\n}\n\n.rec-outer p:last-child {\n  margin-bottom: 0;\n}\n\n@media (max-width: 600px) {\n  .record .illustration,\n  .calculation .illustration {\n    /* hide keys when there is no room for them */\n    display: none !important;\n  }\n}\n\n/* ---------------------------------- selected */\n\n.record.selected,\n.calculation.selected,\n.stem {\n  cursor: inherit;\n  box-shadow: rgba(0, 0, 0, 0.3) 2px 2px 2px;\n}\n\n.record.selected:hover,\n.calculation.selected:hover {\n  border: 2px solid transparent;\n}\n\n.record.selected .rec-label,\n.calculation.selected .rec-label,\n.stem .rec-label {\n  padding: 0 0 10px 0;\n  width: 0;\n}\n\n.record.selected .rec-label:after,\n.calculation.selected .rec-label:after {\n  color: var(--chunky-text);\n  content: \" ×\";\n  font-weight: bold;\n  cursor: pointer;\n}\n\n.record.selected .illustration,\n.calculation.selected .illustration,\n.stem .illustration {\n  display: block;\n}\n\n.selected .rec-explanation,\n.stem .rec-explanation {\n  display: block;\n}\n\n.stem img[fit] {\n  display: block;\n  margin: 20px auto 10px;\n  max-width: 70%;\n  max-height: 70%;\n  border: 1px solid #666;\n  box-shadow: rgba(0, 0, 0, 0.3) 2px 2px 2px;\n  border-radius: 10px;\n}\n/* ---------------------------------- selected */\n"
  },
  {
    "path": "src/RecOuter/utils.jsx",
    "content": "import CodeSample from \"./CodeSample\";\nimport Annotations, { AnnotationToggler } from \"./Annotations\";\nimport Table from \"./Table\";\nimport MathBlock from \"./Math\";\nimport * as X25519Series from './X25519';\n\nconst mdText = (str = \"\") => {\n  const strong = /\\*\\*(.*?)\\*\\*/gi;\n  const result = [str];\n  let matched,\n    lastIndex = 0;\n\n  while ((matched = strong.exec(str)) !== null) {\n    result.pop();\n    result.push(str.slice(lastIndex, strong.lastIndex - matched[0].length));\n    result.push(<strong key={matched[1] + lastIndex}>{matched[1]}</strong>);\n    lastIndex = strong.lastIndex;\n    result.push(str.slice(lastIndex));\n  }\n\n  if (result.every((v) => typeof v === \"string\")) return result.join(\"\");\n  return result;\n};\n\nexport const renderExplanations = (explanations = [], depth = 0, config) => {\n  const DefaultTag = config?.defaultTag || \"p\";\n  const CommonProps = config?.commonProps || {};\n\n  return explanations.map((exp, idx) => {\n    if (typeof exp === \"string\") {\n      // p don't allowed in p\n      if (depth !== 0) return mdText(exp);\n      else return <DefaultTag key={idx}>{mdText(exp)}</DefaultTag>;\n    }\n\n    const {\n      Tag: expTag = DefaultTag,\n      content: expContent,\n      children: expChildren,\n      props: expProps = CommonProps,\n    } = exp;\n\n    let Tag = expTag || \"p\";\n\n    switch (Tag) {\n      case \"CodeSample\":\n        Tag = CodeSample;\n        break;\n      case \"Annotations\":\n        Tag = Annotations;\n        break;\n      case \"AnnotationToggler\":\n        Tag = AnnotationToggler;\n        break;\n      case \"Table\":\n        Tag = Table;\n        break;\n      case \"Math\":\n        Tag = MathBlock;\n        break;\n      case \"Flex\":\n        Tag = X25519Series.FlexContainer;\n        break;\n      case \"Calculator\":\n        Tag = X25519Series.Calculator;\n        break;\n      case \"QA\":\n        Tag = X25519Series.QA;\n        break;\n      case \"a\":\n        expProps.target ||= \"_blank\";\n        break;\n    }\n\n    let content = expContent;\n\n    // support naked string\n    if (Array.isArray(exp)) {\n      content = renderExplanations(exp, depth + 1, config);\n    }\n    // configured children\n    if (Array.isArray(expChildren)) {\n      content = renderExplanations(expChildren, depth + 1, config);\n    }\n\n    if (typeof content === \"string\") content = mdText(content);\n\n    return (\n      <Tag key={idx} {...expProps}>\n        {content}\n      </Tag>\n    );\n  });\n};\n"
  },
  {
    "path": "src/TLS12/clientApplicationData.json",
    "content": "[\n  \"此时客户端和服务器就可以开始正式的 TLS 1.2 会话了。\",\n  \"客户端首先发送它的数据，字符串\\\"ping\\\"。\",\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"记录头\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"17 03 03 00 30\"\n          },\n          [\n            \"TLS 会话被分解成 \\\"记录\\\"(record) 的形式发送和接收。记录是具有类型、协议版本和长度的数据块。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"17 - 表示 TLS 记录类型 0x17(23, application data)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"03 03 - 协议版本 (3.3, 即 TLS 1.2)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 30 - 紧接着的数组载荷长度 0x30(48) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"加密向量\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\"\n          },\n          [\n            \"客户端发送一个用于解密这个区块的初始化向量。因为我们的数据是可预测的(we have overridden the rand function)，因此它也是一个可预测的序列。\"\n          ]\n        ],\n        [\n          \"加密的数据载荷\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"6c 42 1c 71 c4 2b 18 3b fa 06 19 5d 13 3d 0a 09 d0 0f c7 cb 4e 0f 5d 1c da 59 d1 47 ec 79 0c 99\"\n          },\n          [\n            \"这个数据是用客户端的写时密钥加密的。因为它包含一个消息验证码(MAC)和填充字节，所以它比解密后的数据要大一截。\"\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"data\": [\n        [\n          \"\",\n          {\n            \"props\": {\n              \"className\": \"decryption-header\"\n            },\n            \"content\": \"解密后的数据载荷\"\n          },\n          [\n            { \"Tag\": \"h4\", \"content\": \"解密\" },\n            \"数据被 \\\"客户端生成会话密钥\\\" 步骤中产生的写时密钥和该记录开头所携带的初始向量(IVs)加密。\",\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### client key\\n$ hexkey=f656d037b173ef3e11169f27231a84b6\\n### IV for this record\\n$ hexiv=000102030405060708090a0b0c0d0e0f\\n### encrypted data\\n$ echo '6c 42 1c 71 c4 2b 18 3b fa 06 19 5d 13 3d 0a 09'  > /tmp/msg1\\n$ echo 'd0 0f c7 cb 4e 0f 5d 1c da 59 d1 47 ec 79 0c 99' >> /tmp/msg1\\n$ xxd -r -p /tmp/msg1 \\\\\\n  | openssl enc -d -nopad -aes-128-cbc -K $hexkey -iv $hexiv | hexdump\\n\\n0000000 70 69 6e 67 60 10 12 49 f7 4a 03 77 c9 ca cf 63\\n0000010 09 75 13 70 d8 0c fc aa 07 07 07 07 07 07 07 07\\n\\nThe last 28 bytes contain a 20-byte MAC and padding to bring the data to a\\nmultiple of 16 bytes.  The 20-byte MAC can be reproduced as follows:\\n\\n### from https://tools.ietf.org/html/rfc2246#section-6.2.3.1\\n$ sequence='0000000000000001'\\n$ rechdr='17 03 03'\\n$ datalen='00 04'\\n$ data='70 69 6e 67'\\n### from \\\"Encryption Keys Calculation\\\"\\n$ mackey=1b7d117c7d5f690bc263cae8ef60af0f1878acc2\\n$ echo $sequence $rechdr $datalen $data | xxd -r -p \\\\\\n  | openssl dgst -sha1 -mac HMAC -macopt hexkey:$mackey\\n\\n60101249f74a0377c9cacf6309751370d80cfcaa\"\n              }\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"数据载荷\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"70 69 6e 67\"\n          },\n          [\"字符串\\\"ping\\\"\"]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/TLS12/clientChangeCipherSpec.json",
    "content": "[\n  \"生成完主秘钥之后，客户端会发送一个密钥规格变更记录(Change Cipher Spec)，表示已经生成主秘钥，并且将模式切换到加密模式，告诉服务器端开始使用加密方式发送消息。密钥规格变更记录之前传输的 TLS 握手数据都是明文的，之后都是对称秘钥加密的秘文。\",\n  \"在 TLS 的下一个版本中，这一消息类型会被删除，因为它可以被服务器端推断出来。\",\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"记录头\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"14 03 03 00 01\"\n          },\n          [\n            \"TLS 会话被分解成 \\\"记录\\\"(record) 的形式发送和接收。记录是具有类型、协议版本和长度的数据块。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"14 - 表示 TLS 记录类型 0x14(20, ChangeCipherSpec)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"03 03 - 协议版本 (3.3, 即 TLS 1.2)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 01 - 紧接着的数组载荷长度 0x01(1) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"数据载荷\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"01\"\n          },\n          []\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/TLS12/clientCloseNotify.json",
    "content": "[\n  \"客户端发送警告信号，表明连接即将终止(此时是有序终止即正常终止连接)\",\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"记录头\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"15 03 03 00 30\"\n          },\n          [\n            \"TLS 会话被分解成 \\\"记录\\\"(record) 的形式发送和接收。记录是具有类型、协议版本和长度的数据块。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"15 - 表示 TLS 记录类型 0x15(21, alert)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"03 03 - 协议版本 (3.3, 即 TLS 1.2)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 30 - 紧接着的数组载荷长度 0x30(48) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"加密向量\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f\"\n          },\n          [\n            \"客户端发送一个用于解密这个区块的初始化向量。因为我们的数据是可预测的(we have overridden the rand function)，因此它也是一个可预测的序列。\"\n          ]\n        ],\n        [\n          \"加密的数据载荷\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"0d 83 f9 79 04 75 0d d8 fd 8a a1 30 21 86 32 63 4f d0 65 e4 62 83 79 b8 8b bf 9e fd 12 87 a6 2d\"\n          },\n          [\n            \"这个数据是用客户端的写时密钥加密的。因为它包含一个消息验证码(MAC)和填充字节，所以它比解密后的数据要大一截。\"\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"data\": [\n        [\n          \"\",\n          {\n            \"props\": {\n              \"className\": \"decryption-header\"\n            },\n            \"content\": \"解密后的数据载荷\"\n          },\n          [\n            { \"Tag\": \"h4\", \"content\": \"解密\" },\n            \"数据被 \\\"客户端生成会话密钥\\\" 步骤中产生的写时密钥和该记录开头所携带的初始向量(IVs)加密。\",\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### client key\\n$ hexkey=f656d037b173ef3e11169f27231a84b6\\n### IV for this record\\n$ hexiv=101112131415161718191a1b1c1d1e1f\\n### encrypted data\\n$ echo '0d 83 f9 79 04 75 0d d8 fd 8a a1 30 21 86 32 63'  > /tmp/msg1\\n$ echo '4f d0 65 e4 62 83 79 b8 8b bf 9e fd 12 87 a6 2d' >> /tmp/msg1\\n$ xxd -r -p /tmp/msg1 \\\\\\n  | openssl enc -d -nopad -aes-128-cbc -K $hexkey -iv $hexiv | hexdump\\n\\n0000000 01 00 92 79 9c ba 81 9f 31 07 44 c5 59 62 2b e4\\n0000010 2b ce 3d 6a 41 fb 09 09 09 09 09 09 09 09 09 09\\n\\nThe last 30 bytes contain a 20-byte MAC and padding to bring the data to a\\nmultiple of 16 bytes.  The 20-byte MAC can be reproduced as follows:\\n\\n### from https://tools.ietf.org/html/rfc2246#section-6.2.3.1\\n$ sequence='0000000000000002'\\n$ rechdr='15 03 03'\\n$ datalen='00 02'\\n$ data='01 00'\\n### from \\\"Encryption Keys Calculation\\\"\\n$ mackey=1b7d117c7d5f690bc263cae8ef60af0f1878acc2\\n$ echo $sequence $rechdr $datalen $data | xxd -r -p \\\\\\n  | openssl dgst -sha1 -mac HMAC -macopt hexkey:$mackey\\n\\n92799cba819f310744c559622be42bce3d6a41fb\"\n              }\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"警告信息\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"01 00\"\n          },\n          [\n            \"客户端发送一个 \\\"连接关闭通知\\\"(close notify) 的警告，表示连接正有序地终止(正常终止)。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"01 - 警告级别为 0x01(warning), 未使用\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 - 表明这是 close notify 警告\"\n                }\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/TLS12/clientEncryptionKeysGeneration.json",
    "content": "[\n  \"客户端现在可以计算应用会话时的密钥了。在这个计算中，客户端使用了以下信息：\",\n  {\n    \"Tag\": \"ul\",\n    \"children\": [\n      {\n        \"Tag\": \"li\",\n        \"content\": \"服务器端随机数(ServerHello)\"\n      },\n      {\n        \"Tag\": \"li\",\n        \"content\": \"客户端随机数(ClientHello)\"\n      },\n      {\n        \"Tag\": \"li\",\n        \"content\": \"服务器端公钥(服务器端密钥交换)\"\n      },\n      {\n        \"Tag\": \"li\",\n        \"content\": \"客户端私钥(客户端准备密钥交换)\"\n      }\n    ]\n  },\n  \"客户端使用 curve25519 将服务器端公钥乘以自己的私钥。该 32 字节的结果为(称为 PreMasterSecret)：\",\n  {\n    \"Tag\": \"pre\",\n    \"props\": {\n      \"className\": \"ind2\"\n    },\n    \"children\": [\n      {\n        \"Tag\": \"code\",\n        \"props\": { \"className\": \"longboi\" },\n        \"content\": \"df4a291baa1eb7cfa6934b29b474baad2697e29f1f920dcc77c8a0a088447624\"\n      }\n    ]\n  },\n  {\n    \"children\": [\n      \"也可以使用原作者提供的\",\n      {\n        \"Tag\": \"a\",\n        \"props\": {\n          \"href\": \"https://tls12.xargs.org/files/curve25519-mult.c\"\n        },\n        \"content\": \"工具\"\n      },\n      \"快速计算：\"\n    ]\n  },\n  {\n    \"Tag\": \"CodeSample\",\n    \"props\": {\n      \"code\": \"$ gcc -o curve25519-mult curve25519-mult.c\\n$ ./curve25519-mult client-ephemeral-private.key                     server-ephemeral-public.key | hexdump\\n\\n0000000 df 4a 29 1b aa 1e b7 cf a6 93 4b 29 b4 74 ba ad\\n0000010 26 97 e2 9f 1f 92 0d cc 77 c8 a0 a0 88 44 76 24\"\n    }\n  },\n  \"然后，客户端需要使用以下方法从 PreMasterSecret 中计算出 48 字节的主密钥(MasterSecret)：\",\n  {\n    \"Tag\": \"pre\",\n    \"children\": [\n      {\n        \"Tag\": \"code\",\n        \"props\": { \"className\": \"longboi\" },\n        \"content\": \"seed = \\\"master secret\\\" + client_random + server_random\\na0 = seed\\na1 = HMAC-SHA256(key=PreMasterSecret, data=a0)\\na2 = HMAC-SHA256(key=PreMasterSecret, data=a1)\\np1 = HMAC-SHA256(key=PreMasterSecret, data=a1 + seed)\\np2 = HMAC-SHA256(key=PreMasterSecret, data=a2 + seed)\\nMasterSecret = p1[all 32 bytes] + p2[first 16 bytes]\"\n      }\n    ]\n  },\n  {\n    \"Tag\": \"CodeSample\",\n    \"props\": {\n      \"code\": \"### set up our PreMasterSecret as a hex string\\n$ pmshex=df4a291baa1eb7cfa6934b29b474baad\\n$ pmshex=${pmshex}2697e29f1f920dcc77c8a0a088447624\\n### client random from Client Hello\\n$ echo -en '\\\\x00\\\\x01\\\\x02\\\\x03\\\\x04\\\\x05\\\\x06\\\\x07' >  /tmp/c_rand\\n$ echo -en '\\\\x08\\\\x09\\\\x0a\\\\x0b\\\\x0c\\\\x0d\\\\x0e\\\\x0f' >> /tmp/c_rand\\n$ echo -en '\\\\x10\\\\x11\\\\x12\\\\x13\\\\x14\\\\x15\\\\x16\\\\x17' >> /tmp/c_rand\\n$ echo -en '\\\\x18\\\\x19\\\\x1a\\\\x1b\\\\x1c\\\\x1d\\\\x1e\\\\x1f' >> /tmp/c_rand\\n### server random from Server Hello\\n$ echo -en '\\\\x70\\\\x71\\\\x72\\\\x73\\\\x74\\\\x75\\\\x76\\\\x77' >  /tmp/s_rand\\n$ echo -en '\\\\x78\\\\x79\\\\x7a\\\\x7b\\\\x7c\\\\x7d\\\\x7e\\\\x7f' >> /tmp/s_rand\\n$ echo -en '\\\\x80\\\\x81\\\\x82\\\\x83\\\\x84\\\\x85\\\\x86\\\\x87' >> /tmp/s_rand\\n$ echo -en '\\\\x88\\\\x89\\\\x8a\\\\x8b\\\\x8c\\\\x8d\\\\x8e\\\\x8f' >> /tmp/s_rand\\n### build the seed\\n$ echo -en 'master secret' > /tmp/seed\\n$ cat /tmp/c_rand /tmp/s_rand >> /tmp/seed\\n### a0 is the same as the seed\\n$ cat /tmp/seed > /tmp/a0\\n### a(n) is hmac-sha256(key=secret, data=a(n-1))\\n$ cat /tmp/a0 | openssl dgst -sha256 \\\\\\n   -mac HMAC -macopt hexkey:$pmshex -binary > /tmp/a1\\n$ cat /tmp/a1 | openssl dgst -sha256 \\\\\\n   -mac HMAC -macopt hexkey:$pmshex -binary > /tmp/a2\\n### p(n) is hmac-sha256(key=secret, data=a(n)+seed)\\n$ cat /tmp/a1 /tmp/seed | openssl dgst -sha256 \\\\\\n   -mac HMAC -macopt hexkey:$pmshex -binary > /tmp/p1\\n$ cat /tmp/a2 /tmp/seed | openssl dgst -sha256 \\\\\\n   -mac HMAC -macopt hexkey:$pmshex -binary > /tmp/p2\\n### first 48 bytes is MasterSecret\\n$ cat /tmp/p1 /tmp/p2 | head -c 48 > /tmp/mastersecret\\n$ hexdump /tmp/mastersecret\\n\\n0000000 91 6a bf 9d a5 59 73 e1 36 14 ae 0a 3f 5d 3f 37\\n0000010 b0 23 ba 12 9a ee 02 cc 91 34 33 81 27 cd 70 49\\n0000020 78 1c 8e 19 fc 1e b2 a7 38 7a c0 6a e2 37 34 4c\"\n    }\n  },\n  \"计算出的主密钥应是：\",\n  {\n    \"Tag\": \"pre\",\n    \"children\": [\n      {\n        \"Tag\": \"code\",\n        \"props\": { \"className\": \"longboi\" },\n        \"content\": \"916abf9da55973e13614ae0a3f5d3f37b023ba129aee02cc9134338127cd7049781c8e19fc1eb2a7387ac06ae237344c\"\n      }\n    ]\n  },\n  \"然后，我们使用密钥扩展算法(Key Expansion)生成最终的加密密钥：\",\n  {\n    \"Tag\": \"pre\",\n    \"children\": [\n      {\n        \"Tag\": \"code\",\n        \"props\": { \"className\": \"longboi\" },\n        \"content\": \"seed = \\\"key expansion\\\" + server_random + client_random\\na0 = seed\\na1 = HMAC-SHA256(key=MasterSecret, data=a0)\\na2 = HMAC-SHA256(key=MasterSecret, data=a1)\\na3 = HMAC-SHA256(key=MasterSecret, data=a2)\\na4 = ...\\np1 = HMAC-SHA256(key=MasterSecret, data=a1 + seed)\\np2 = HMAC-SHA256(key=MasterSecret, data=a2 + seed)\\np3 = HMAC-SHA256(key=MasterSecret, data=a3 + seed)\\np4 = ...\\np = p1 + p2 + p3 + p4 ...\\nclient write mac key = [first 20 bytes of p]\\nserver write mac key = [next 20 bytes of p]\\nclient write key = [next 16 bytes of p]\\nserver write key = [next 16 bytes of p]\\nclient write IV = [next 16 bytes of p]\\nserver write IV = [next 16 bytes of p]\"\n      }\n    ]\n  },\n  {\n    \"Tag\": \"CodeSample\",\n    \"props\": {\n      \"code\": \"### continued from above command line example\\n### set up our MasterSecret as a hex string\\n$ mshex=$(hexdump -ve '/1 \\\"%02x\\\"' /tmp/mastersecret)\\n### build the seed\\n$ echo -en 'key expansion' > /tmp/seed\\n$ cat /tmp/s_rand /tmp/c_rand >> /tmp/seed\\n### a0 is the same as the seed\\n$ cat /tmp/seed > /tmp/a0\\n### a(n) is hmac-sha256(key=secret, data=a(n-1))\\n$ cat /tmp/a0 | openssl dgst -sha256 \\\\\\n   -mac HMAC -macopt hexkey:$mshex -binary > /tmp/a1\\n$ cat /tmp/a1 | openssl dgst -sha256 \\\\\\n   -mac HMAC -macopt hexkey:$mshex -binary > /tmp/a2\\n$ cat /tmp/a2 | openssl dgst -sha256 \\\\\\n   -mac HMAC -macopt hexkey:$mshex -binary > /tmp/a3\\n$ cat /tmp/a3 | openssl dgst -sha256 \\\\\\n   -mac HMAC -macopt hexkey:$mshex -binary > /tmp/a4\\n### p(n) is hmac-sha256(key=secret, data=a(n)+seed)\\n$ cat /tmp/a1 /tmp/seed | openssl dgst -sha256 \\\\\\n   -mac HMAC -macopt hexkey:$mshex -binary > /tmp/p1\\n$ cat /tmp/a2 /tmp/seed | openssl dgst -sha256 \\\\\\n   -mac HMAC -macopt hexkey:$mshex -binary > /tmp/p2\\n$ cat /tmp/a3 /tmp/seed | openssl dgst -sha256 \\\\\\n   -mac HMAC -macopt hexkey:$mshex -binary > /tmp/p3\\n$ cat /tmp/a4 /tmp/seed | openssl dgst -sha256 \\\\\\n   -mac HMAC -macopt hexkey:$mshex -binary > /tmp/p4\\n### combine them into a single stream\\n$ cat /tmp/p1 /tmp/p2 /tmp/p3 /tmp/p4 > /tmp/p\\n$ dd if=/tmp/p of=/tmp/client_mac_key bs=1 skip=0  count=20\\n$ dd if=/tmp/p of=/tmp/server_mac_key bs=1 skip=20 count=20\\n$ dd if=/tmp/p of=/tmp/client_key     bs=1 skip=40 count=16\\n$ dd if=/tmp/p of=/tmp/server_key     bs=1 skip=56 count=16\\n$ dd if=/tmp/p of=/tmp/client_iv      bs=1 skip=72 count=16\\n$ dd if=/tmp/p of=/tmp/server_iv      bs=1 skip=88 count=16\\n$ hexdump /tmp/client_mac_key\\n0000000 1b 7d 11 7c 7d 5f 69 0b c2 63 ca e8 ef 60 af 0f\\n0000010 18 78 ac c2\\n\\n$ hexdump /tmp/server_mac_key\\n0000000 2a d8 bd d8 c6 01 a6 17 12 6f 63 54 0e b2 09 06\\n0000010 f7 81 fa d2\\n\\n$ hexdump /tmp/client_key\\n0000000 f6 56 d0 37 b1 73 ef 3e 11 16 9f 27 23 1a 84 b6\\n\\n$ hexdump /tmp/server_key\\n0000000 75 2a 18 e7 a9 fc b7 cb cd d8 f9 8d d8 f7 69 eb\\n\\n$ hexdump /tmp/client_iv\\n0000000 a0 d2 55 0c 92 38 ee bf ef 5c 32 25 1a bb 67 d6\\n\\n$ hexdump /tmp/server_iv\\n0000000 43 45 28 db 49 37 d5 40 d3 93 13 5e 06 a1 1b b8\"\n    }\n  },\n  \"由此我们可以得到以下密钥以及向量：\",\n  {\n    \"Tag\": \"ul\",\n    \"children\": [\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"客户端 MAC 密钥: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"1b7d117c7d5f690bc263cae8ef60af0f1878acc2\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"客户端写时密钥: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"f656d037b173ef3e11169f27231a84b6\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"客户端写时向量: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"a0d2550c9238eebfef5c32251abb67d6\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"服务器端 MAC 密钥: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"2ad8bdd8c601a617126f63540eb20906f781fad2\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"服务器端写时密钥: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"752a18e7a9fcb7cbcdd8f98dd8f769eb\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"服务器端写时向量: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"434528db4937d540d393135e06a11bb8\"\n          }\n        ]\n      }\n    ]\n  }\n]\n"
  },
  {
    "path": "src/TLS12/clientHandshakeFinished.json",
    "content": "[\n  \"为了验证握手成功且没有被篡改过，客户端需要创建一些验证数据给服务器端确认。验证数据是基于所有握手信息的哈希值计算得到。\",\n  \"此时开始传输的客户端数据都是加密的密文了。\",\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"记录头\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"16 03 03 00 40\"\n          },\n          [\n            \"TLS 会话被分解成 \\\"记录\\\"(record) 的形式发送和接收。记录是具有类型、协议版本和长度的数据块。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"14 - 表示 TLS 记录类型 0x16(22, handshake)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"03 03 - 协议版本 (3.3, 即 TLS 1.2)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 40 - 紧接着的数组载荷长度 0x40(64) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"加密向量\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f\"\n          },\n          [\n            \"客户端发送一个用于解密这个区块的初始化向量。因为我们的数据是可预测的(we have overridden the rand function)，因此它也是一个可预测的序列。\"\n          ]\n        ],\n        [\n          \"加密的数据载荷\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"22 7b c9 ba 81 ef 30 f2 a8 a7 8f f1 df 50 84 4d 58 04 b7 ee b2 e2 14 c3 2b 68 92 ac a3 db 7b 78 07 7f dd 90 06 7c 51 6b ac b3 ba 90 de df 72 0f\"\n          },\n          [\n            \"这个数据是用客户端的写时密钥加密的。因为它包含一个消息验证码(MAC)和填充字节，所以它比解密后的数据要大一截。\"\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"data\": [\n        [\n          \"\",\n          {\n            \"props\": {\n              \"className\": \"decryption-header\"\n            },\n            \"content\": \"解密后的数据载荷\"\n          },\n          [\n            { \"Tag\": \"h4\", \"content\": \"解密\" },\n            \"数据被 \\\"客户端生成会话密钥\\\" 步骤中产生的写时密钥和该记录开头所携带的初始向量(IVs)加密。\",\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### client key\\n$ hexkey=f656d037b173ef3e11169f27231a84b6\\n### IV for this record\\n$ hexiv=404142434445464748494a4b4c4d4e4f\\n### encrypted data\\n$ echo '22 7b c9 ba 81 ef 30 f2 a8 a7 8f f1 df 50 84 4d'  > /tmp/msg1\\n$ echo '58 04 b7 ee b2 e2 14 c3 2b 68 92 ac a3 db 7b 78' >> /tmp/msg1\\n$ echo '07 7f dd 90 06 7c 51 6b ac b3 ba 90 de df 72 0f' >> /tmp/msg1\\n$ xxd -r -p /tmp/msg1 \\\\\\n  | openssl enc -d -nopad -aes-128-cbc -K $hexkey -iv $hexiv | hexdump\\n\\n0000000 14 00 00 0c cf 91 96 26 f1 36 0c 53 6a aa d7 3a\\n0000010 a5 a0 3d 23 30 56 e4 ac 6e ba 7f d9 e5 31 7f ac\\n0000020 2d b5 b7 0e 0b 0b 0b 0b 0b 0b 0b 0b 0b 0b 0b 0b\\n\\nThe last 32 bytes contain a 20-byte MAC and padding to bring the data to a\\nmultiple of 16 bytes.  The 20-byte MAC can be reproduced as follows:\\n\\n### from https://tools.ietf.org/html/rfc2246#section-6.2.3.1\\n$ sequence='0000000000000000'\\n$ rechdr='16 03 03'\\n$ datalen='00 10'\\n$ data='14 00 00 0c cf 91 96 26 f1 36 0c 53 6a aa d7 3a'\\n### from \\\"Encryption Keys Calculation\\\"\\n$ mackey=1b7d117c7d5f690bc263cae8ef60af0f1878acc2\\n$ echo $sequence $rechdr $datalen $data | xxd -r -p \\\\\\n  | openssl dgst -sha1 -mac HMAC -macopt hexkey:$mackey\\n\\na5a03d233056e4ac6eba7fd9e5317fac2db5b70e\"\n              }\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"握手消息头\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"14 00 00 0c\"\n          },\n          [\n            \"每个握手消息都以一个 type 和一个 len 开始。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"14 - 握手消息类型 0x14 (finished)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 0c - 紧接着的握手消息数据的长度 0x0C (12) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"验证数据\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"cf 91 96 26 f1 36 0c 53 6a aa d7 3a\"\n          },\n          [\n            \"使用 \\\"客户端生成会话密钥\\\" 步骤中的主密钥和在这之前的每个握手记录(type=0x16)的 SHA256 哈希值生成。\",\n            \"这个哈希值应该是：\",\n            {\n              \"Tag\": \"pre\",\n              \"children\": [\n                {\n                  \"Tag\": \"code\",\n                  \"props\": { \"className\": \"longboi\" },\n                  \"content\": \"061dda04b3c2217ff73bd79b9cf88a2bb6ec505404aac8722db03ef417b54cb4\"\n                }\n              ]\n            },\n            \"计算过程及结果：\",\n            {\n              \"Tag\": \"pre\",\n              \"children\": [\n                {\n                  \"Tag\": \"code\",\n                  \"props\": { \"className\": \"longboi\" },\n                  \"content\": \"seed = \\\"client finished\\\" + SHA256(all handshake messages)\\na0 = seed\\na1 = HMAC-SHA256(key=MasterSecret, data=a0)\\np1 = HMAC-SHA256(key=MasterSecret, data=a1 + seed)\\nverify_data = p1[first 12 bytes]\"\n                }\n              ]\n            },\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### set up our MasterSecret as a hex string\\n$ mshex=$(hexdump -ve '/1 \\\"%02x\\\"' /tmp/mastersecret)\\n### build the seed\\n$ echo -en 'client finished' > /tmp/seed\\n### add SHA256(all_messages) to seed\\n$ echo -en '\\\\x06\\\\x1d\\\\xda\\\\x04\\\\xb3\\\\xc2\\\\x21\\\\x7f' >> /tmp/seed\\n$ echo -en '\\\\xf7\\\\x3b\\\\xd7\\\\x9b\\\\x9c\\\\xf8\\\\x8a\\\\x2b' >> /tmp/seed\\n$ echo -en '\\\\xb6\\\\xec\\\\x50\\\\x54\\\\x04\\\\xaa\\\\xc8\\\\x72' >> /tmp/seed\\n$ echo -en '\\\\x2d\\\\xb0\\\\x3e\\\\xf4\\\\x17\\\\xb5\\\\x4c\\\\xb4' >> /tmp/seed\\n### a0 is the same as the seed\\n$ cat /tmp/seed > /tmp/a0\\n### a(n) is hmac-sha256(key=secret, data=a(n-1))\\n$ cat /tmp/a0 | openssl dgst -sha256 \\\\\\n   -mac HMAC -macopt hexkey:$mshex -binary > /tmp/a1\\n### p(n) is hmac-sha256(key=secret, data=a(n)+seed)\\n$ cat /tmp/a1 /tmp/seed | openssl dgst -sha256 \\\\\\n   -mac HMAC -macopt hexkey:$mshex -binary > /tmp/p1\\n$ head -c 12 /tmp/p1 > /tmp/verify_data\\n$ hexdump /tmp/verify_data\\n\\n0000000 cf 91 96 26 f1 36 0c 53 6a aa d7 3a\"\n              }\n            }\n          ]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/TLS12/clientHello.json",
    "content": "[\n  \"例子中的会话以 \\\"ClientHello\\\" 开始。客户端提供的信息包括以下内容：\",\n  {\n    \"Tag\": \"ul\",\n    \"children\": [\n      {\n        \"Tag\": \"li\",\n        \"content\": \"协议版本号\"\n      },\n      {\n        \"Tag\": \"li\",\n        \"content\": \"客户端随机数(用于之后的握手过程)\"\n      },\n      {\n        \"Tag\": \"li\",\n        \"content\": \"一个用于快速回复(resume)的\\\"会话 ID\\\"\"\n      },\n      {\n        \"Tag\": \"li\",\n        \"content\": \"一个客户端支持的有序的加解密算法数组\"\n      },\n      {\n        \"Tag\": \"li\",\n        \"content\": \"一个客户端支持的有序的压缩算法数组\"\n      },\n      {\n        \"Tag\": \"li\",\n        \"content\": \"扩展列表\"\n      }\n    ]\n  },\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"记录头\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"16 03 01 00 a5\"\n          },\n          [\n            \"TLS 会话被分解成 \\\"记录\\\"(record) 的形式发送和接收。记录是具有类型、协议版本和长度的数据块。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"16 - 表示 TLS 记录类型 0x16(22, Handshake)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"03 01 - 协议版本 (3.1, 即 TLS 1.0)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 a5 - 紧接着的数组载荷长度 0xA5(165) 字节\"\n                }\n              ]\n            },\n            {\n              \"children\": [\n                \"有趣的是，这里的版本号并不是预期的 3.3(TLS 1.2)，而是 3.1(TLS 1.0)。在 golang 的\",\n                {\n                  \"Tag\": \"code\",\n                  \"content\": \"crypto/tls\"\n                },\n                \"中有以下注释可能会解释其原因：\"\n              ]\n            },\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"if vers == 0 {\\n    // Some TLS servers fail if the record version is\\n    // greater than TLS 1.0 for the initial ClientHello.\\n    vers = VersionTLS10\\n}\"\n              }\n            }\n          ]\n        ],\n        [\n          \"TLS 握手记录头\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"01 00 00 a1\"\n          },\n          [\n            \"每个 TLS 握手消息都以一个 type 和一个 len 开始。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"01 - 握手消息类型 0x01 (ClientHello)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 a1 - 紧接着的握手消息数据的长度 0xA1 (161) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"客户端 TLS 版本号\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"03 03\"\n          },\n          [\n            \"指出版本号为 3.3 (TLS 1.2)\",\n            \"不寻常的版本号(\\\"3,3\\\" 代表 TLS 1.2)是由于 TLS 1.0 是 SSL 3.0 协议的一个小修订。因此，TLS 1.0 用 \\\"3,1\\\" 表示，TLS 1.1用 \\\"3,2\\\"，以此类推。\"\n          ]\n        ],\n        [\n          \"客户端随机数\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f\"\n          },\n          [\n            \"客户端提供的 32 字节的随机数。这个数将在之后的会话中使用。在本文的例子中，我们暂时将一个方便记忆的字符串当作随机数。\",\n            {\n              \"children\": [\n                \"TLS 1.2 规范说，前 4 个字节应该是自 1970 年以来的秒数。但现在\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://tools.ietf.org/html/draft-mathewson-no-gmtunixtime-00\"\n                  },\n                  \"content\": \"已建议不要这样做\"\n                },\n                \"，因为这样可以对主机和服务器进行\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://nmap.org/nmap-fingerprinting-article-tw.html\"\n                  },\n                  \"content\": \"指纹识别\"\n                },\n                \"。\"\n              ]\n            }\n          ]\n        ],\n        [\n          \"会话 ID\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00\"\n          },\n          [\n            \"客户端可以提供旧连接的 TLS 会话 ID，它能够恢复之前的会话。要做到这一点，服务器端和客户端都需要存储之前连接的密钥信息。恢复连接可以节省大量的计算和网络往返时间，所以只要有可能就会执行。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 - 0 字节的会话 ID\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"加解密算法列表\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 20 cc a8 cc a9 c0 2f c0 30 c0 2b c0 2c c0 13 c0 09 c0 14 c0 0a 00 9c 00 9d 00 2f 00 35 c0 12 00 0a\"\n          },\n          [\n            \"客户端提供一个有序的列表，以说明它支持哪些加解密算法进行加密。该列表是按照客户端的偏好顺序排列的，以最高偏好为先。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 20 - 0x20(32) 字节的加解密算法列表数据\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"cc a8 ... 00 0a - 代表一连串的加解密算法\"\n                }\n              ]\n            },\n            {\n              \"children\": [\n                \"完整加解密算法列表请查看 \",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml\"\n                  },\n                  \"content\": \"IANA tls-parameters\"\n                },\n                \" 的 TLS Cipher Suites 部分。\"\n              ]\n            }\n          ]\n        ],\n        [\n          \"压缩算法\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"01 00\"\n          },\n          [\n            \"客户端提供一个有序的列表，说明它将支持哪些压缩方法。压缩将在加密步骤之前(因为加密的数据通常是不可压缩的)。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"01 - 1 字节的压缩算法长度\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 - 代表 \\\"没有\\\"(null) 压缩算法\"\n                }\n              ]\n            },\n            {\n              \"children\": [\n                \"由于最近压缩被发现会泄露加密数据的信息(见 \",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://zhuanlan.zhihu.com/p/333314023\"\n                  },\n                  \"content\": \"CRIME/BREACH 攻击\"\n                },\n                \")。\",\n                \"因此之后的 TLS 版本(如 TLS 1.3)将禁用压缩。出于安全考虑，TLS 1.2 的客户端也应禁用压缩。\"\n              ]\n            }\n          ]\n        ],\n        [\n          \"扩展的长度\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 58\"\n          },\n          [\n            \"客户端提供了一个可选的扩展列表，服务器可以根据它来采取某些行动或启用某些特性。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 58 - 扩展列表的长度为 0x58(88) 字节\"\n                }\n              ]\n            },\n            \"每个扩展将以两个字节开始，表明它是哪个扩展。然后是两个字节的内容长度字段，最后是扩展的具体内容。\"\n          ]\n        ],\n        [\n          \"扩展 - 服务器名称指示\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 00 00 18 00 16 00 00 13 65 78 61 6d 70 6c 65 2e 75 6c 66 68 65 69 6d 2e 6e 65 74\"\n          },\n          [\n            \"(例子中的)客户端提供了它想要联系的服务器的名称，也被称为 SNI(服务器名称指示)。\",\n            \"如果没有这个扩展，HTTPS 服务器将无法为一个 IP 地址上的多个主机名(虚拟主机)提供服务。因为它在 TLS 会话协商结束、HTTP 请求发出后才知道要发送哪个主机名的证书。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 - 表示这是 \\\"服务器名称指示\\\" 扩展\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 18 - 扩展信息长度为 0x18(24) 字节 \"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 16 - 第一个(也是唯一的)列表条目长度为 0x16(22) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 - 0x00 表示这是 \\\"主机名\\\" 列表\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 13 - 主机名列表的长度为 0x13(19) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"65 78 61 ... 6e 65 74 - 主机名：\\\"example.ulfheim.net\\\"\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"扩展 - 证书状态请求\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 05 00 05 01 00 00 00 00\"\n          },\n          [\n            \"客户端要求服务器在其响应中提供 OCSP(Offensive Security Certified Professional certification) 信息提供许可。OCSP 可用于检查证书是否已被撤销。\",\n            \"客户端此时会发送一个空内容的扩展。而这种形式是必要的，因为服务器不能使用客户没有提供的扩展来回复。因此，客户端需要先发送一个空的扩展内容，而服务器则会填充对应数据，并按对应扩展进行回复。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 05 - 表示这是 \\\"证书状态请求\\\" 扩展\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 05 - 扩展信息长度为 0x5(5) 字节 \"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"01 - 表示 \\\"证书状态类型\\\"：OSCP\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 - responderID (OSCP 的字段) 长度为 0 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 - 表示 OSCP 的内容长度为 0 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"扩展 - 支持的组\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 0a 00 0a 00 08 00 1d 00 17 00 18 00 19\"\n          },\n          [\n            \"(例子中的)客户端表示它支持四种类型的椭圆曲线算法(ECC)。为了使这个扩展更加通用，未来可以支持其他的密码学类型，因此称这些为 \\\"支持的组\\\" 而不是 \\\"支持的曲线\\\"。\",\n            \"这个列表同样是按照客户端的偏好降序排列的。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 0a - 表示这是 \\\"支持的组\\\" 扩展\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 0a - 扩展信息长度为 0x0a(10) 字节 \"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 08 - 曲线列表条目长度为 0x08(8) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 1d - 代表 x25519 曲线\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 17 - 代表 secp256r1 曲线\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 18 - 代表 secp384r1 曲线\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 19 - 代表 secp521r1 曲线\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"扩展 - 椭圆曲线参数指示(EC Point Formats)\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 0b 00 02 01 00\"\n          },\n          [\n            \"在椭圆曲线(EC)加密过程中，客户端和服务器通过这个扩展协商椭圆曲线相关的信息(如选择的起始点和是否压缩)。此时该扩展表示客户端只能从服务器解析未压缩的信息。\",\n            \"在后续的 TLS 版本中(如 TLS 1.3)，不存在协商点的能力(相反，每条曲线都有一个预选的点)，所以这个扩展并不起实质性作用。(更多内容请参考 TLS 1.3 的内容)\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 0b - 表示这是 \\\"椭圆曲线参数指示\\\" 扩展\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 02 - 扩展信息长度为 0x02(2) 字节 \"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"01 - 指示长度为 0x01(1) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 - 表示未压缩\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"扩展 - 签名算法列表\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 0d 00 12 00 10 04 01 04 03 05 01 05 03 06 01 06 03 02 01 02 03\"\n          },\n          [\n            \"随着 TLS 的发展，有必要支持更强大的签名算法，如 SHA-256；同时也必须向前兼容，以支持使用 MD5 和 SHA1 的实现。因此需要有一个扩展来表示客户端支持哪些签名算法。这可能会影响到服务器提交给客户端的证书，以及服务器在 CertificateVerify 记录中发送的签名。\",\n            \"这个列表同样是按照客户端的偏好降序排列的。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 0d - 表示这是 \\\"签名算法列表\\\" 扩展\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 12 - 扩展信息长度为 0x12(18) 字节 \"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 10 - 算法列表长度为 0x10(16) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"04 01 - 代表 RSA/PKCS1/SHA256 算法\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"04 03 - 代表 ECDSA/SECP256r1/SHA256 算法\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"05 01 - 代表 RSA/PKCS1/SHA384 算法\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"05 03 - 代表 ECDSA/SECP384r1/SHA384 算法\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"06 01 - 代表 RSA/PKCS1/SHA512 算法\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"06 03 - 代表 ECDSA/SECP521r1/SHA512 算法\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"02 01 - 代表 RSA/PKCS1/SHA1 算法\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"02 03 - 代表 ECDSA/SHA1 算法\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"扩展 - 重协商信息(Renegotiation Info)\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"ff 01 00 01 00\"\n          },\n          [\n            {\n              \"children\": [\n                \"这个扩展的存在防止了\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://blog.csdn.net/edmond999/article/details/53007991\"\n                  },\n                  \"content\": \"一种用 TLS 重协商进行的攻击(Renegotiating TLS)\"\n                },\n                \"。之后的 TLS 版本(如 TLS 1.3)由于完全移除了重协商的功能，因此这个扩展也不会出现。\"\n              ]\n            },\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"ff 01 - 表示这是 \\\"重协商信息\\\" 扩展\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 01 - 扩展信息长度为 0x01(1) 字节 \"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 - 重协商的长度为 0x0(0) 字节，因为这是一个全新连接\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"扩展 - SCT 编码\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 12 00 00\"\n          },\n          [\n            {\n              \"children\": [\n                \"客户端为服务器返回 SCT 信息(已签名的证书时间戳, signed certificate timestamp)提供许可。SCT 用于支持 \",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://imququ.com/post/certificate-transparency.html\"\n                  },\n                  \"content\": \"CT 技术(证书透明, Certificate Transparency)\"\n                },\n                \"。\"\n              ]\n            },\n            \"客户端此时会发送一个空内容的扩展。而这种形式是必要的，因为服务器不能使用客户没有提供的扩展来回复。因此，客户端需要先发送一个空的扩展内容，而服务器则会填充对应数据，并按对应扩展进行回复。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 12 - 表示这是 \\\"SCT 编码\\\" 扩展\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 - 扩展信息长度为 0x00(0) 字节 \"\n                }\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/TLS12/clientKeyExchange.json",
    "content": "[\n  \"客户端现在可以算出自己的密钥。交换后，双方都可以根据私钥和交换过来的公钥生成共享的加密密钥了。\",\n  \"现在双方已经同意使用 ECDHE 的密码加解密算法(ServiceHello 的 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA)。这意味着密钥对将基于选定的椭圆曲线(**E**lliptic **C**urve)，使用 **D**iffie-**H**ellman 编码，并且密钥对是基于该次连接(**E**phemeral)，而不是使用证书上的公/私钥。\",\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"记录头\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"16 03 03 00 25\"\n          },\n          [\n            \"TLS 会话被分解成 \\\"记录\\\"(record) 的形式发送和接收。记录是具有类型、协议版本和长度的数据块。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"16 - 表示 TLS 记录类型 0x16(22, Handshake)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"03 03 - 协议版本 (3.3, 即 TLS 1.2)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 25 - 紧接着的数组载荷长度 0x025(37) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"TLS 握手记录头\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"10 00 00 21\"\n          },\n          [\n            \"每个 TLS 握手消息都以一个 type 和一个 len 开始。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"10 - 握手消息类型 0x10 (client key exchange)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 21 - 紧接着的握手消息数据的长度 0x021 (33) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"客户端公钥\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"20 35 80 72 d6 36 58 80 d1 ae ea 32 9a df 91 21 38 38 51 ed 21 a2 8e 3b 75 e9 65 d0 d2 cd 16 62 54\"\n          },\n          [\n            \"\\\"客户端准备密钥交换\\\" 步骤中生成的公钥。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 20 - 公钥长度为 0x20(32) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"35 80 ... 62 54 - 公钥\"\n                }\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/TLS12/clientKeyExchangeGeneration.json",
    "content": "[\n  \"客户端也需要生成一个用于密钥交换的自己的“私钥/公钥”对。密钥交换(Key exchange)是一种技术，双方可以在同一数字上达成一致，而窃听者却无法知道这个数字是什么。\",\n  {\n    \"Tag\": \"p\",\n    \"children\": [\n      \"学习 TLS 并不需要深入了解，但你可以从\",\n      {\n        \"Tag\": \"a\",\n        \"props\": { \"href\": \"https://cangsdarm.github.io/illustrate/x25519\" },\n        \"content\": \"X25519 密钥交换算法\"\n      },\n      \"获取涉及到的密钥交换算法的具体解释。\"\n    ]\n  },\n  [\n    \"**私钥**是 0 到 \",\n    {\n      \"Tag\": \"Math\",\n      \"content\": \"2^256-1\"\n    },\n    \" 之间的一个随机整数(32bytes, 256bits)\",\n    \"。为方便后续解释，假设我们生成的私钥是：\"\n  ],\n  {\n    \"Tag\": \"pre\",\n    \"props\": {\n      \"className\": \"ind2\"\n    },\n    \"children\": [\n      {\n        \"Tag\": \"code\",\n        \"props\": { \"className\": \"longboi\" },\n        \"content\": \"202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f\"\n      }\n    ]\n  },\n  \"**公钥**由上面提到的 X25519 密钥交换算法生成。例子中计算出的公钥应如下所示：\",\n  {\n    \"Tag\": \"pre\",\n    \"props\": {\n      \"className\": \"ind2\"\n    },\n    \"children\": [\n      {\n        \"Tag\": \"code\",\n        \"props\": { \"className\": \"longboi\" },\n        \"content\": \"358072d6365880d1aeea329adf9121383851ed21a28e3b75e965d0d2cd166254\"\n      }\n    ]\n  },\n  \"公钥的计算也可以在命令行中运行以下命令得到：\",\n  {\n    \"Tag\": \"CodeSample\",\n    \"props\": {\n      \"code\": \"### requires openssl 1.1.0 or higher\\n$ openssl pkey -noout -text < client-ephemeral-private.key\\n\\nX25519 Private-Key:\\npriv:\\n    20:21:22:23:24:25:26:27:28:29:2a:2b:2c:2d:2e:\\n    2f:30:31:32:33:34:35:36:37:38:39:3a:3b:3c:3d:\\n    3e:3f\\npub:\\n    35:80:72:d6:36:58:80:d1:ae:ea:32:9a:df:91:21:\\n    38:38:51:ed:21:a2:8e:3b:75:e9:65:d0:d2:cd:16:\\n    62:54\"\n    }\n  }\n]\n"
  },
  {
    "path": "src/TLS12/index.js",
    "content": "const data = {\n  intro: {\n    title: \"图解 TLS 1.2 连接\",\n    subtitle: \"对每一个字节的解释和再现\",\n    desc: \"TLS 1.3 已于 2018/08 释出。\",\n    intro:\n      '在这个演示中，客户端通过 TLS 1.2 加密协商连接服务器。客户端发送\"ping\"、接收\"pong\"后终止连接。点击下面开始探索。',\n  },\n  sections: [\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"client\"],\n      label: \"ClientHello\",\n      illustration: {\n        src: \"https://quic.xargs.org/images/key1.png\",\n        width: \"135\",\n        height: \"250\",\n      },\n      json: () => import(\"./clientHello.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"server\"],\n      label: \"ServerHello\",\n      illustration: {\n        src: \"https://quic.xargs.org/images/key2.png\",\n        width: \"124\",\n        height: \"250\",\n      },\n      json: () => import(\"./serverHello.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"server\"],\n      label: \"服务器端证书\",\n      illustration: {\n        src: \"https://quic.xargs.org/images/key3.png\",\n        width: \"130\",\n        height: \"250\",\n      },\n      json: () => import(\"./serverCertificate.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"calculation\", \"server\"],\n      label: \"服务器端准备密钥交换\",\n      illustration: {\n        src: \"https://quic.xargs.org/images/key4.png\",\n        width: \"106\",\n        height: \"250\",\n      },\n      json: () => import(\"./serverKeyExchangeGeneration.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"server\"],\n      label: \"服务器端密钥交换\",\n      illustration: {\n        src: \"https://quic.xargs.org/images/key5.png\",\n        width: \"138\",\n        height: \"250\",\n      },\n      json: () => import(\"./serverKeyExchange.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"server\"],\n      label: \"ServerHello完成\",\n      json: () => import(\"./serverHelloDone.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"calculation\", \"client\"],\n      label: \"客户端准备密钥交换\",\n      illustration: {\n        src: \"https://quic.xargs.org/images/key6.png\",\n        width: \"105\",\n        height: \"250\",\n      },\n      json: () => import(\"./clientKeyExchangeGeneration.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"client\"],\n      label: \"客户端密钥交换\",\n      illustration: {\n        src: \"https://quic.xargs.org/images/key7.png\",\n        width: \"116\",\n        height: \"250\",\n      },\n      json: () => import(\"./clientKeyExchange.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"calculation\", \"client\"],\n      label: \"客户端生成会话密钥\",\n      illustration: {\n        src: \"https://quic.xargs.org/images/key8.png\",\n        width: \"97\",\n        height: \"250\",\n      },\n      json: () => import(\"./clientEncryptionKeysGeneration.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"client\"],\n      label: \"客户端秘钥规格变更\",\n      json: () => import(\"./clientChangeCipherSpec.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"client\"],\n      label: \"客户端握手完成\",\n      json: () => import(\"./clientHandshakeFinished.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"calculation\", \"server\"],\n      label: \"服务器端生成会话密钥\",\n      illustration: {\n        src: \"https://quic.xargs.org/images/key9.png\",\n        width: \"97\",\n        height: \"250\",\n      },\n      json: () => import(\"./serverEncryptionKeysGeneration.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"server\"],\n      label: \"服务器端秘钥规格变更\",\n      json: () => import(\"./serverChangeCipherSpec.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"server\"],\n      label: \"服务器端握手完成\",\n      json: () => import(\"./serverHandshakeFinished.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"client\"],\n      label: \"客户端发送会话数据\",\n      json: () => import(\"./clientApplicationData.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"server\"],\n      label: \"服务器端响应客户端的会话数据\",\n      json: () => import(\"./serverApplicationData.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"client\"],\n      label: \"客户端连接关闭通知(close notify)\",\n      json: () => import(\"./clientCloseNotify.json\"),\n    },\n  ],\n  ending: {\n    mother: \"https://tls12.xargs.org/\",\n    desc: \"你可能也对更新的 <a href='/illustrate/tls13' target='_blank'>TLS 1.3</a> 的内容感兴趣。\",\n    references: [\n      {\n        title: \"TLS Parameters\",\n        href: \"https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml\",\n      },\n    ],\n  },\n};\n\nexport default data;\n"
  },
  {
    "path": "src/TLS12/serverApplicationData.json",
    "content": "[\n  \"服务器端响应客户端的数据，返回字符串\\\"pong\\\"。\",\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"记录头\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"17 03 03 00 30\"\n          },\n          [\n            \"TLS 会话被分解成 \\\"记录\\\"(record) 的形式发送和接收。记录是具有类型、协议版本和长度的数据块。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"17 - 表示 TLS 记录类型 0x17(23, application data)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"03 03 - 协议版本 (3.3, 即 TLS 1.2)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 30 - 紧接着的数组载荷长度 0x30(48) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"加密向量\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70\"\n          },\n          [\n            \"服务器端发送一个用于解密这个区块的初始化向量。因为我们的数据是可预测的(we have overridden the rand function)，因此它也是一个可预测的序列。\"\n          ]\n        ],\n        [\n          \"加密的数据载荷\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"97 83 48 8a f5 fa 20 bf 7a 2e f6 9d eb b5 34 db 9f b0 7a 8c 27 21 de e5 40 9f 77 af 0c 3d de 56\"\n          },\n          [\n            \"这个数据是用服务器端的写时密钥加密的。因为它包含一个消息验证码(MAC)和填充字节，所以它比解密后的数据要大一截。\"\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"data\": [\n        [\n          \"\",\n          {\n            \"props\": {\n              \"className\": \"decryption-header\"\n            },\n            \"content\": \"解密后的数据载荷\"\n          },\n          [\n            { \"Tag\": \"h4\", \"content\": \"解密\" },\n            \"数据被 \\\"服务器端生成会话密钥\\\" 步骤中产生的写时密钥和该记录开头所携带的初始向量(IVs)加密。\",\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### server key\\n$ hexkey=752a18e7a9fcb7cbcdd8f98dd8f769eb\\n### IV for this record\\n$ hexiv=6162636465666768696a6b6c6d6e6f70\\n### encrypted data\\n$ echo '97 83 48 8a f5 fa 20 bf 7a 2e f6 9d eb b5 34 db'  > /tmp/msg1\\n$ echo '9f b0 7a 8c 27 21 de e5 40 9f 77 af 0c 3d de 56' >> /tmp/msg1\\n$ xxd -r -p /tmp/msg1 \\\\\\n  | openssl enc -d -nopad -aes-128-cbc -K $hexkey -iv $hexiv | hexdump\\n\\n0000000 70 6f 6e 67 5a c7 99 dc cf dc 0f af 95 2b dc 91\\n0000010 18 af 20 0e e3 1c 51 05 07 07 07 07 07 07 07 07\\n\\nThe last 28 bytes contain a 20-byte MAC and padding to bring the data to a\\nmultiple of 16 bytes.  The 20-byte MAC can be reproduced as follows:\\n\\n### from https://tools.ietf.org/html/rfc2246#section-6.2.3.1\\n$ sequence='0000000000000001'\\n$ rechdr='17 03 03'\\n$ datalen='00 04'\\n$ data='70 6f 6e 67'\\n### from \\\"Encryption Keys Calculation\\\"\\n$ mackey=2ad8bdd8c601a617126f63540eb20906f781fad2\\n$ echo $sequence $rechdr $datalen $data | xxd -r -p \\\\\\n  | openssl dgst -sha1 -mac HMAC -macopt hexkey:$mackey\\n\\n5ac799dccfdc0faf952bdc9118af200ee31c5105\"\n              }\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"数据载荷\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"70 6f 6e 67\"\n          },\n          [\"字符串\\\"pong\\\"\"]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/TLS12/serverCertificate.json",
    "content": "[\n  \"服务器端发送的证书包含以下信息：\",\n  {\n    \"Tag\": \"ul\",\n    \"children\": [\n      {\n        \"Tag\": \"li\",\n        \"content\": \"服务器端的主机名\"\n      },\n      {\n        \"Tag\": \"li\",\n        \"content\": \"服务器端的公钥\"\n      },\n      {\n        \"Tag\": \"li\",\n        \"content\": \"受信任的第三方的证明。保证该主机名的所有者持有该公钥的私钥。\"\n      }\n    ]\n  },\n  {\n    \"children\": [\n      {\n        \"Tag\": \"a\",\n        \"props\": {\n          \"href\": \"https://tls12.xargs.org/certificate.html\"\n        },\n        \"content\": \"点击查看证书细节 (暂未翻译)\"\n      }\n    ]\n  },\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"记录头\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"16 03 03 03 2f\"\n          },\n          [\n            \"TLS 会话被分解成 \\\"记录\\\"(record) 的形式发送和接收。记录是具有类型、协议版本和长度的数据块。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"16 - 表示 TLS 记录类型 0x16(22, Handshake)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"03 03 - 协议版本 (3.3, 即 TLS 1.2)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"03 2f - 紧接着的数组载荷长度 0x32F(815) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"握手消息头\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"0b 00 03 2b\"\n          },\n          [\n            \"每个握手消息都以一个 type 和一个 len 开始。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"0b - 握手消息类型 0x0b (certificate)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 03 2b - 紧接着的握手消息数据的长度 0x32B (811) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"所有证书长度\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 03 28\"\n          },\n          [\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 03 28 - 紧接着的证书的长度 0x328 (808) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"第一个证书长度\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 03 25\"\n          },\n          [\n            \"此时同时也是唯一一个\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 03 25 - 紧接着的证书的长度 0x325 (805) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"证书\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"30 82 03 21 30 82 02 09 a0 03 02 01 02 02 08 15 5a 92 ad c2 04 8f 90 30 0d 06 09 2a 86 48 86 f7 0d 01 01 0b 05 00 30 22 31 0b 30 09 06 03 55 04 06 13 02 55 53 31 13 30 11 06 03 55 04 0a 13 0a 45 78 61 6d 70 6c 65 20 43 41 30 1e 17 0d 31 38 31 30 30 35 30 31 33 38 31 37 5a 17 0d 31 39 31 30 30 35 30 31 33 38 31 37 5a 30 2b 31 0b 30 09 06 03 55 04 06 13 02 55 53 31 1c 30 1a 06 03 55 04 03 13 13 65 78 61 6d 70 6c 65 2e 75 6c 66 68 65 69 6d 2e 6e 65 74 30 82 01 22 30 0d 06 09 2a 86 48 86 f7 0d 01 01 01 05 00 03 82 01 0f 00 30 82 01 0a 02 82 01 01 00 c4 80 36 06 ba e7 47 6b 08 94 04 ec a7 b6 91 04 3f f7 92 bc 19 ee fb 7d 74 d7 a8 0d 00 1e 7b 4b 3a 4a e6 0f e8 c0 71 fc 73 e7 02 4c 0d bc f4 bd d1 1d 39 6b ba 70 46 4a 13 e9 4a f8 3d f3 e1 09 59 54 7b c9 55 fb 41 2d a3 76 52 11 e1 f3 dc 77 6c aa 53 37 6e ca 3a ec be c3 aa b7 3b 31 d5 6c b6 52 9c 80 98 bc c9 e0 28 18 e2 0b f7 f8 a0 3a fd 17 04 50 9e ce 79 bd 9f 39 f1 ea 69 ec 47 97 2e 83 0f b5 ca 95 de 95 a1 e6 04 22 d5 ee be 52 79 54 a1 e7 bf 8a 86 f6 46 6d 0d 9f 16 95 1a 4c f7 a0 46 92 59 5c 13 52 f2 54 9e 5a fb 4e bf d7 7a 37 95 01 44 e4 c0 26 87 4c 65 3e 40 7d 7d 23 07 44 01 f4 84 ff d0 8f 7a 1f a0 52 10 d1 f4 f0 d5 ce 79 70 29 32 e2 ca be 70 1f df ad 6b 4b b7 11 01 f4 4b ad 66 6a 11 13 0f e2 ee 82 9e 4d 02 9d c9 1c dd 67 16 db b9 06 18 86 ed c1 ba 94 21 02 03 01 00 01 a3 52 30 50 30 0e 06 03 55 1d 0f 01 01 ff 04 04 03 02 05 a0 30 1d 06 03 55 1d 25 04 16 30 14 06 08 2b 06 01 05 05 07 03 02 06 08 2b 06 01 05 05 07 03 01 30 1f 06 03 55 1d 23 04 18 30 16 80 14 89 4f de 5b cc 69 e2 52 cf 3e a3 00 df b1 97 b8 1d e1 c1 46 30 0d 06 09 2a 86 48 86 f7 0d 01 01 0b 05 00 03 82 01 01 00 59 16 45 a6 9a 2e 37 79 e4 f6 dd 27 1a ba 1c 0b fd 6c d7 55 99 b5 e7 c3 6e 53 3e ff 36 59 08 43 24 c9 e7 a5 04 07 9d 39 e0 d4 29 87 ff e3 eb dd 09 c1 cf 1d 91 44 55 87 0b 57 1d d1 9b df 1d 24 f8 bb 9a 11 fe 80 fd 59 2b a0 39 8c de 11 e2 65 1e 61 8c e5 98 fa 96 e5 37 2e ef 3d 24 8a fd e1 74 63 eb bf ab b8 e4 d1 ab 50 2a 54 ec 00 64 e9 2f 78 19 66 0d 3f 27 cf 20 9e 66 7f ce 5a e2 e4 ac 99 c7 c9 38 18 f8 b2 51 07 22 df ed 97 f3 2e 3e 93 49 d4 c6 6c 9e a6 39 6d 74 44 62 a0 6b 42 c6 d5 ba 68 8e ac 3a 01 7b dd fc 8e 2c fc ad 27 cb 69 d3 cc dc a2 80 41 44 65 d3 ae 34 8c e0 f3 4a b2 fb 9c 61 83 71 31 2b 19 10 41 64 1c 23 7f 11 a5 d6 5c 84 4f 04 04 84 99 38 71 2b 95 9e d6 85 bc 5c 5d d6 45 ed 19 90 94 73 40 29 26 dc b4 0e 34 69 a1 59 41 e8 e2 cc a8 4b b6 08 46 36 a0\"\n          },\n          [\n            {\n              \"children\": [\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://tls12.xargs.org/files/server.crt\"\n                  },\n                  \"content\": \"该证书\"\n                },\n                \"采用 ASN.1 DER 编码。可以在命令行中使用以下命令转换为二进制数据：\"\n              ]\n            },\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"$ openssl x509 -outform der < server.crt | hexdump\\n\\n0000000 30 82 03 21 30 82 02 09 a0 03 02 01 02 02 08 15\\n0000010 5a 92 ad c2 04 8f 90 30 0d 06 09 2a 86 48 86 f7\\n... snip ...\"\n              }\n            }\n          ]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/TLS12/serverChangeCipherSpec.json",
    "content": "[\n  \"生成完主秘钥之后，服务器端也需要发送一个密钥规格变更记录(Change Cipher Spec)，表示已经生成主秘钥，并且将模式切换到加密模式，告诉客户端开始使用加密方式发送消息。密钥规格变更记录之前传输的 TLS 握手数据都是明文的，之后都是对称秘钥加密的秘文。\",\n  \"在 TLS 的下一个版本中，这一消息类型会被删除，因为它可以被客户端推断出来。\",\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"记录头\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"14 03 03 00 01\"\n          },\n          [\n            \"TLS 会话被分解成 \\\"记录\\\"(record) 的形式发送和接收。记录是具有类型、协议版本和长度的数据块。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"14 - 表示 TLS 记录类型 0x14(20, ChangeCipherSpec)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"03 03 - 协议版本 (3.3, 即 TLS 1.2)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 01 - 紧接着的数组载荷长度 0x01(1) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"数据载荷\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"01\"\n          },\n          []\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/TLS12/serverEncryptionKeysGeneration.json",
    "content": "[\n  \"服务器端现在也可以计算应用会话时的密钥了。在这个计算中，服务器端使用了以下信息：\",\n  {\n    \"Tag\": \"ul\",\n    \"children\": [\n      {\n        \"Tag\": \"li\",\n        \"content\": \"服务器端随机数(ServerHello)\"\n      },\n      {\n        \"Tag\": \"li\",\n        \"content\": \"客户端随机数(ClientHello)\"\n      },\n      {\n        \"Tag\": \"li\",\n        \"content\": \"客户端公钥(客户端密钥交换)\"\n      },\n      {\n        \"Tag\": \"li\",\n        \"content\": \"服务器端私钥(服务器端准备密钥交换)\"\n      }\n    ]\n  },\n  \"服务器端使用 curve25519 将客户端公钥乘以自己的私钥。该 32 字节的结果为(称为 PreMasterSecret)：\",\n  {\n    \"Tag\": \"pre\",\n    \"props\": {\n      \"className\": \"ind2\"\n    },\n    \"children\": [\n      {\n        \"Tag\": \"code\",\n        \"props\": { \"className\": \"longboi\" },\n        \"content\": \"df4a291baa1eb7cfa6934b29b474baad2697e29f1f920dcc77c8a0a088447624\"\n      }\n    ]\n  },\n  {\n    \"children\": [\n      \"也可以使用原作者提供的\",\n      {\n        \"Tag\": \"a\",\n        \"props\": {\n          \"href\": \"https://tls12.xargs.org/files/curve25519-mult.c\"\n        },\n        \"content\": \"工具\"\n      },\n      \"快速计算：\"\n    ]\n  },\n  {\n    \"Tag\": \"CodeSample\",\n    \"props\": {\n      \"code\": \"$ gcc -o curve25519-mult curve25519-mult.c\\n$ ./curve25519-mult server-ephemeral-private.key \\\\\\n                    client-ephemeral-public.key | hexdump\\n\\n0000000 df 4a 29 1b aa 1e b7 cf a6 93 4b 29 b4 74 ba ad\\n0000010 26 97 e2 9f 1f 92 0d cc 77 c8 a0 a0 88 44 76 24\"\n    }\n  },\n  \"这与客户端计算得到的 PreMasterSecret 是一致的，因此下面的计算也一样。\",\n  \"然后，服务器端需要使用以下方法从 PreMasterSecret 中计算出 48 字节的主密钥(MasterSecret)：\",\n  {\n    \"Tag\": \"pre\",\n    \"children\": [\n      {\n        \"Tag\": \"code\",\n        \"props\": { \"className\": \"longboi\" },\n        \"content\": \"seed = \\\"master secret\\\" + client_random + server_random\\na0 = seed\\na1 = HMAC-SHA256(key=PreMasterSecret, data=a0)\\na2 = HMAC-SHA256(key=PreMasterSecret, data=a1)\\np1 = HMAC-SHA256(key=PreMasterSecret, data=a1 + seed)\\np2 = HMAC-SHA256(key=PreMasterSecret, data=a2 + seed)\\nMasterSecret = p1[all 32 bytes] + p2[first 16 bytes]\"\n      }\n    ]\n  },\n  {\n    \"Tag\": \"CodeSample\",\n    \"props\": {\n      \"code\": \"### set up our PreMasterSecret as a hex string\\n$ pmshex=df4a291baa1eb7cfa6934b29b474baad\\n$ pmshex=${pmshex}2697e29f1f920dcc77c8a0a088447624\\n### client random from Client Hello\\n$ echo -en '\\\\x00\\\\x01\\\\x02\\\\x03\\\\x04\\\\x05\\\\x06\\\\x07' >  /tmp/c_rand\\n$ echo -en '\\\\x08\\\\x09\\\\x0a\\\\x0b\\\\x0c\\\\x0d\\\\x0e\\\\x0f' >> /tmp/c_rand\\n$ echo -en '\\\\x10\\\\x11\\\\x12\\\\x13\\\\x14\\\\x15\\\\x16\\\\x17' >> /tmp/c_rand\\n$ echo -en '\\\\x18\\\\x19\\\\x1a\\\\x1b\\\\x1c\\\\x1d\\\\x1e\\\\x1f' >> /tmp/c_rand\\n### server random from Server Hello\\n$ echo -en '\\\\x70\\\\x71\\\\x72\\\\x73\\\\x74\\\\x75\\\\x76\\\\x77' >  /tmp/s_rand\\n$ echo -en '\\\\x78\\\\x79\\\\x7a\\\\x7b\\\\x7c\\\\x7d\\\\x7e\\\\x7f' >> /tmp/s_rand\\n$ echo -en '\\\\x80\\\\x81\\\\x82\\\\x83\\\\x84\\\\x85\\\\x86\\\\x87' >> /tmp/s_rand\\n$ echo -en '\\\\x88\\\\x89\\\\x8a\\\\x8b\\\\x8c\\\\x8d\\\\x8e\\\\x8f' >> /tmp/s_rand\\n### build the seed\\n$ echo -en 'master secret' > /tmp/seed\\n$ cat /tmp/c_rand /tmp/s_rand >> /tmp/seed\\n### a0 is the same as the seed\\n$ cat /tmp/seed > /tmp/a0\\n### a(n) is hmac-sha256(key=secret, data=a(n-1))\\n$ cat /tmp/a0 | openssl dgst -sha256 \\\\\\n   -mac HMAC -macopt hexkey:$pmshex -binary > /tmp/a1\\n$ cat /tmp/a1 | openssl dgst -sha256 \\\\\\n   -mac HMAC -macopt hexkey:$pmshex -binary > /tmp/a2\\n### p(n) is hmac-sha256(key=secret, data=a(n)+seed)\\n$ cat /tmp/a1 /tmp/seed | openssl dgst -sha256 \\\\\\n   -mac HMAC -macopt hexkey:$pmshex -binary > /tmp/p1\\n$ cat /tmp/a2 /tmp/seed | openssl dgst -sha256 \\\\\\n   -mac HMAC -macopt hexkey:$pmshex -binary > /tmp/p2\\n### first 48 bytes is MasterSecret\\n$ cat /tmp/p1 /tmp/p2 | head -c 48 > /tmp/mastersecret\\n$ hexdump /tmp/mastersecret\\n\\n0000000 91 6a bf 9d a5 59 73 e1 36 14 ae 0a 3f 5d 3f 37\\n0000010 b0 23 ba 12 9a ee 02 cc 91 34 33 81 27 cd 70 49\\n0000020 78 1c 8e 19 fc 1e b2 a7 38 7a c0 6a e2 37 34 4c\"\n    }\n  },\n  \"计算出的主密钥应是：\",\n  {\n    \"Tag\": \"pre\",\n    \"children\": [\n      {\n        \"Tag\": \"code\",\n        \"props\": { \"className\": \"longboi\" },\n        \"content\": \"916abf9da55973e13614ae0a3f5d3f37b023ba129aee02cc9134338127cd7049781c8e19fc1eb2a7387ac06ae237344c\"\n      }\n    ]\n  },\n  \"然后，我们使用密钥扩展算法(Key Expansion)生成最终的加密密钥：\",\n  {\n    \"Tag\": \"pre\",\n    \"children\": [\n      {\n        \"Tag\": \"code\",\n        \"props\": { \"className\": \"longboi\" },\n        \"content\": \"seed = \\\"key expansion\\\" + server_random + client_random\\na0 = seed\\na1 = HMAC-SHA256(key=MasterSecret, data=a0)\\na2 = HMAC-SHA256(key=MasterSecret, data=a1)\\na3 = HMAC-SHA256(key=MasterSecret, data=a2)\\na4 = ...\\np1 = HMAC-SHA256(key=MasterSecret, data=a1 + seed)\\np2 = HMAC-SHA256(key=MasterSecret, data=a2 + seed)\\np3 = HMAC-SHA256(key=MasterSecret, data=a3 + seed)\\np4 = ...\\np = p1 + p2 + p3 + p4 ...\\nclient write mac key = [first 20 bytes of p]\\nserver write mac key = [next 20 bytes of p]\\nclient write key = [next 16 bytes of p]\\nserver write key = [next 16 bytes of p]\\nclient write IV = [next 16 bytes of p]\\nserver write IV = [next 16 bytes of p]\"\n      }\n    ]\n  },\n  {\n    \"Tag\": \"CodeSample\",\n    \"props\": {\n      \"code\": \"### continued from above command line example\\n### set up our MasterSecret as a hex string\\n$ mshex=$(hexdump -ve '/1 \\\"%02x\\\"' /tmp/mastersecret)\\n### build the seed\\n$ echo -en 'key expansion' > /tmp/seed\\n$ cat /tmp/s_rand /tmp/c_rand >> /tmp/seed\\n### a0 is the same as the seed\\n$ cat /tmp/seed > /tmp/a0\\n### a(n) is hmac-sha256(key=secret, data=a(n-1))\\n$ cat /tmp/a0 | openssl dgst -sha256 \\\\\\n   -mac HMAC -macopt hexkey:$mshex -binary > /tmp/a1\\n$ cat /tmp/a1 | openssl dgst -sha256 \\\\\\n   -mac HMAC -macopt hexkey:$mshex -binary > /tmp/a2\\n$ cat /tmp/a2 | openssl dgst -sha256 \\\\\\n   -mac HMAC -macopt hexkey:$mshex -binary > /tmp/a3\\n$ cat /tmp/a3 | openssl dgst -sha256 \\\\\\n   -mac HMAC -macopt hexkey:$mshex -binary > /tmp/a4\\n### p(n) is hmac-sha256(key=secret, data=a(n)+seed)\\n$ cat /tmp/a1 /tmp/seed | openssl dgst -sha256 \\\\\\n   -mac HMAC -macopt hexkey:$mshex -binary > /tmp/p1\\n$ cat /tmp/a2 /tmp/seed | openssl dgst -sha256 \\\\\\n   -mac HMAC -macopt hexkey:$mshex -binary > /tmp/p2\\n$ cat /tmp/a3 /tmp/seed | openssl dgst -sha256 \\\\\\n   -mac HMAC -macopt hexkey:$mshex -binary > /tmp/p3\\n$ cat /tmp/a4 /tmp/seed | openssl dgst -sha256 \\\\\\n   -mac HMAC -macopt hexkey:$mshex -binary > /tmp/p4\\n$ cat /tmp/p1 /tmp/p2 /tmp/p3 /tmp/p4 > /tmp/p\\n$ dd if=/tmp/p of=/tmp/client_mac_key bs=1 skip=0  count=20\\n$ dd if=/tmp/p of=/tmp/server_mac_key bs=1 skip=20 count=20\\n$ dd if=/tmp/p of=/tmp/client_key     bs=1 skip=40 count=16\\n$ dd if=/tmp/p of=/tmp/server_key     bs=1 skip=56 count=16\\n$ dd if=/tmp/p of=/tmp/client_iv      bs=1 skip=72 count=16\\n$ dd if=/tmp/p of=/tmp/server_iv      bs=1 skip=88 count=16\\n$ hexdump /tmp/client_mac_key\\n0000000 1b 7d 11 7c 7d 5f 69 0b c2 63 ca e8 ef 60 af 0f\\n0000010 18 78 ac c2\\n\\n$ hexdump /tmp/server_mac_key\\n0000000 2a d8 bd d8 c6 01 a6 17 12 6f 63 54 0e b2 09 06\\n0000010 f7 81 fa d2\\n\\n$ hexdump /tmp/client_key\\n0000000 f6 56 d0 37 b1 73 ef 3e 11 16 9f 27 23 1a 84 b6\\n\\n$ hexdump /tmp/server_key\\n0000000 75 2a 18 e7 a9 fc b7 cb cd d8 f9 8d d8 f7 69 eb\\n\\n$ hexdump /tmp/client_iv\\n0000000 a0 d2 55 0c 92 38 ee bf ef 5c 32 25 1a bb 67 d6\\n\\n$ hexdump /tmp/server_iv\\n0000000 43 45 28 db 49 37 d5 40 d3 93 13 5e 06 a1 1b b8\"\n    }\n  },\n  \"由此我们可以得到以下密钥以及向量：\",\n  {\n    \"Tag\": \"ul\",\n    \"children\": [\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"客户端 MAC 密钥: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"1b7d117c7d5f690bc263cae8ef60af0f1878acc2\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"客户端写时密钥: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"f656d037b173ef3e11169f27231a84b6\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"客户端写时向量: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"a0d2550c9238eebfef5c32251abb67d6\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"服务器端 MAC 密钥: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"2ad8bdd8c601a617126f63540eb20906f781fad2\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"服务器端写时密钥: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"752a18e7a9fcb7cbcdd8f98dd8f769eb\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"服务器端写时向量: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"434528db4937d540d393135e06a11bb8\"\n          }\n        ]\n      }\n    ]\n  }\n]\n"
  },
  {
    "path": "src/TLS12/serverHandshakeFinished.json",
    "content": "[\n  \"为了验证握手成功且没有被篡改过，服务器端也需要创建一些验证数据给客户端确认。验证数据是基于所有握手信息的哈希值计算得到。\",\n  \"此时开始传输的服务器端数据都是加密的密文了。\",\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"记录头\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"16 03 03 00 40\"\n          },\n          [\n            \"TLS 会话被分解成 \\\"记录\\\"(record) 的形式发送和接收。记录是具有类型、协议版本和长度的数据块。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"14 - 表示 TLS 记录类型 0x16(22, handshake)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"03 03 - 协议版本 (3.3, 即 TLS 1.2)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 40 - 紧接着的数组载荷长度 0x40(64) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"加密向量\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f 60\"\n          },\n          [\n            \"服务器端发送一个用于解密这个区块的初始化向量。因为我们的数据是可预测的(we have overridden the rand function)，因此它也是一个可预测的序列。\"\n          ]\n        ],\n        [\n          \"加密的数据载荷\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"18 e0 75 31 7b 10 03 15 f6 08 1f cb f3 13 78 1a ac 73 ef e1 9f e2 5b a1 af 59 c2 0b e9 4f c0 1b da 2d 68 00 29 8b 73 a7 e8 49 d7 4b d4 94 cf 7d\"\n          },\n          [\n            \"这个数据是用服务器端的写时密钥加密的。因为它包含一个消息验证码(MAC)和填充字节，所以它比解密后的数据要大一截。\"\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"data\": [\n        [\n          \"\",\n          {\n            \"props\": {\n              \"className\": \"decryption-header\"\n            },\n            \"content\": \"解密后的数据载荷\"\n          },\n          [\n            { \"Tag\": \"h4\", \"content\": \"解密\" },\n            \"数据被 \\\"服务器端生成会话密钥\\\" 步骤中产生的写时密钥和该记录开头所携带的初始向量(IVs)加密。\",\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### server key\\n$ hexkey=752a18e7a9fcb7cbcdd8f98dd8f769eb\\n### IV for this record\\n$ hexiv=5152535455565758595a5b5c5d5e5f60\\n### encrypted data\\n$ echo '18 e0 75 31 7b 10 03 15 f6 08 1f cb f3 13 78 1a'  > /tmp/msg1\\n$ echo 'ac 73 ef e1 9f e2 5b a1 af 59 c2 0b e9 4f c0 1b' >> /tmp/msg1\\n$ echo 'da 2d 68 00 29 8b 73 a7 e8 49 d7 4b d4 94 cf 7d' >> /tmp/msg1\\n$ xxd -r -p /tmp/msg1 \\\\\\n  | openssl enc -d -nopad -aes-128-cbc -K $hexkey -iv $hexiv | hexdump\\n\\n0000000 14 00 00 0c 84 4d 3c 10 74 6d d7 22 f9 2f 0c 7e\\n0000010 20 c4 97 46 d2 a3 0f 23 57 39 90 58 07 53 52 43\\n0000020 af f2 bf e0 0b 0b 0b 0b 0b 0b 0b 0b 0b 0b 0b 0b\\n\\nThe last 32 bytes contain a 20-byte MAC and padding to bring the data to a\\nmultiple of 16 bytes.  The 20-byte MAC can be reproduced as follows:\\n\\n### from https://tools.ietf.org/html/rfc2246#section-6.2.3.1\\n$ sequence='0000000000000000'\\n$ rechdr='16 03 03'\\n$ datalen='00 10'\\n$ data='14 00 00 0c 84 4d 3c 10 74 6d d7 22 f9 2f 0c 7e'\\n### from \\\"Encryption Keys Calculation\\\"\\n$ mackey=2ad8bdd8c601a617126f63540eb20906f781fad2\\n$ echo $sequence $rechdr $datalen $data | xxd -r -p \\\\\\n  | openssl dgst -sha1 -mac HMAC -macopt hexkey:$mackey\\n\\n20c49746d2a30f235739905807535243aff2bfe0\"\n              }\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"握手消息头\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"14 00 00 0c\"\n          },\n          [\n            \"每个握手消息都以一个 type 和一个 len 开始。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"14 - 握手消息类型 0x14 (finished)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 0c - 紧接着的握手消息数据的长度 0x0C (12) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"验证数据\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"84 4d 3c 10 74 6d d7 22 f9 2f 0c 7e\"\n          },\n          [\n            \"使用 \\\"服务器端生成会话密钥\\\" 步骤中的主密钥和在这之前的每个握手记录(type=0x16)的 SHA256 哈希值生成。\",\n            \"这个哈希值应该是：\",\n            {\n              \"Tag\": \"pre\",\n              \"children\": [\n                {\n                  \"Tag\": \"code\",\n                  \"props\": { \"className\": \"longboi\" },\n                  \"content\": \"b2017ba28d0e27f03ae327456b6ff00b4d5bbf0ef7cda83ce1029b521c3e7c35\"\n                }\n              ]\n            },\n            \"计算过程及结果：\",\n            {\n              \"Tag\": \"pre\",\n              \"children\": [\n                {\n                  \"Tag\": \"code\",\n                  \"props\": { \"className\": \"longboi\" },\n                  \"content\": \"seed = \\\"server finished\\\" + SHA256(all handshake messages)\\na0 = seed\\na1 = HMAC-SHA256(key=MasterSecret, data=a0)\\np1 = HMAC-SHA256(key=MasterSecret, data=a1 + seed)\\nverify_data = p1[first 12 bytes]\"\n                }\n              ]\n            },\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### set up our MasterSecret as a hex string\\n$ mshex=$(hexdump -ve '/1 \\\"%02x\\\"' /tmp/mastersecret)\\n### build the seed\\n$ echo -en 'server finished' > /tmp/seed\\n### add SHA256(all_messages) to seed\\n$ echo -en '\\\\xb2\\\\x01\\\\x7b\\\\xa2\\\\x8d\\\\x0e\\\\x27\\\\xf0' >> /tmp/seed\\n$ echo -en '\\\\x3a\\\\xe3\\\\x27\\\\x45\\\\x6b\\\\x6f\\\\xf0\\\\x0b' >> /tmp/seed\\n$ echo -en '\\\\x4d\\\\x5b\\\\xbf\\\\x0e\\\\xf7\\\\xcd\\\\xa8\\\\x3c' >> /tmp/seed\\n$ echo -en '\\\\xe1\\\\x02\\\\x9b\\\\x52\\\\x1c\\\\x3e\\\\x7c\\\\x35' >> /tmp/seed\\n### a0 is the same as the seed\\n$ cat /tmp/seed > /tmp/a0\\n### a(n) is hmac-sha256(key=secret, data=a(n-1))\\n$ cat /tmp/a0 | openssl dgst -sha256 \\\\\\n   -mac HMAC -macopt hexkey:$mshex -binary > /tmp/a1\\n### p(n) is hmac-sha256(key=secret, data=a(n)+seed)\\n$ cat /tmp/a1 /tmp/seed | openssl dgst -sha256 \\\\\\n   -mac HMAC -macopt hexkey:$mshex -binary > /tmp/p1\\n$ head -c 12 /tmp/p1 > /tmp/verify_data\\n$ hexdump /tmp/verify_data\\n\\n0000000 84 4d 3c 10 74 6d d7 22 f9 2f 0c 7e\"\n              }\n            }\n          ]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/TLS12/serverHello.json",
    "content": "[\n  \"服务器回复 \\\"ServerHello\\\"。服务器提供的信息包括以下内容：\",\n  {\n    \"Tag\": \"ul\",\n    \"children\": [\n      {\n        \"Tag\": \"li\",\n        \"content\": \"服务器协商的具体协议的版本\"\n      },\n      {\n        \"Tag\": \"li\",\n        \"content\": \"服务器端随机数(在随后的握手时使用)\"\n      },\n      {\n        \"Tag\": \"li\",\n        \"content\": \"会话 ID\"\n      },\n      {\n        \"Tag\": \"li\",\n        \"content\": \"服务器端选择的加解密算法\"\n      },\n      {\n        \"Tag\": \"li\",\n        \"content\": \"服务器端选择的压缩算法\"\n      },\n      {\n        \"Tag\": \"li\",\n        \"content\": \"服务器协商的扩展信息\"\n      }\n    ]\n  },\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"记录头\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"16 03 03 00 31\"\n          },\n          [\n            \"TLS 会话被分解成 \\\"记录\\\"(record) 的形式发送和接收。记录是具有类型、协议版本和长度的数据块。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"16 - 表示 TLS 记录类型 0x16(22, Handshake)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"03 03 - 协议版本 (3.3, 即 TLS 1.2)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 31 - 紧接着的数组载荷长度 0x31(49) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"TLS 握手记录头\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"02 00 00 2d\"\n          },\n          [\n            \"每个 TLS 握手消息都以一个 type 和一个 len 开始。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"02 - 握手消息类型 0x02 (ServerHello)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 2d - 紧接着的握手消息数据的长度 0x2D (45) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"服务器端 TLS 版本号\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"03 03\"\n          },\n          [\n            \"表明服务器端所支持的 TLS 版本 3,3 (TLS 1.2)。\",\n            \"不寻常的版本号(\\\"3,3\\\" 代表 TLS 1.2)是由于 TLS 1.0 是 SSL 3.0 协议的一个小修订。因此，TLS 1.0 用 \\\"3,1\\\" 表示，TLS 1.1用 \\\"3,2\\\"，以此类推。\"\n          ]\n        ],\n        [\n          \"服务器端随机数\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f\"\n          },\n          [\n            \"服务器端提供的 32 字节的随机数。这个数将在之后的会话中使用。在本文的例子中，我们暂时将一个方便记忆的字符串当作随机数。\",\n            {\n              \"children\": [\n                \"TLS 1.2 规范说，前 4 个字节应该是自 1970 年以来的秒数。但现在\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://tools.ietf.org/html/draft-mathewson-no-gmtunixtime-00\"\n                  },\n                  \"content\": \"已建议不要这样做\"\n                },\n                \"，因为这样可以对主机和服务器进行\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://nmap.org/nmap-fingerprinting-article-tw.html\"\n                  },\n                  \"content\": \"指纹识别\"\n                },\n                \"。\"\n              ]\n            }\n          ]\n        ],\n        [\n          \"会话 ID\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00\"\n          },\n          [\n            \"服务器端可以给该 TLS 会话分配一个 会话 ID，使得客户端可以在之后的协商中恢复这一会话。要做到这一点，服务器端和客户端都需要存储之前连接的密钥信息。恢复连接可以节省大量的计算和网络往返时间，所以只要有可能就会执行。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 - 0 字节的会话 ID\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"所选择的加解密算法\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"c0 13\"\n          },\n          [\n            \"服务器从客户端给出的选项列表中选择的加解密算法 0xC013(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA)。\"\n          ]\n        ],\n        [\n          \"压缩算法\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00\"\n          },\n          [\"服务器从客户端给出的选项列表中选择的压缩算法 0x00(null)。\"]\n        ],\n        [\n          \"扩展的长度\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 05\"\n          },\n          [\n            \"服务器向客户端返回的扩展有序列表的长度。因为服务器被禁止回复 ClientHello 消息中不存在的扩展，因此服务器知道客户端将理解并支持列出的所有扩展。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 05 - 扩展列表的长度为 0x05(5) 字节\"\n                }\n              ]\n            },\n            \"每个扩展将以两个字节开始，表明它是哪个扩展。然后是两个字节的内容长度字段，最后是扩展的具体内容。\"\n          ]\n        ],\n        [\n          \"扩展 - 重协商信息(Renegotiation Info)\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"ff 01 00 01 00\"\n          },\n          [\n            {\n              \"children\": [\n                \"这个扩展的存在防止了\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://blog.csdn.net/edmond999/article/details/53007991\"\n                  },\n                  \"content\": \"一种用 TLS 重协商进行的攻击(Renegotiating TLS)\"\n                },\n                \"。之后的 TLS 版本(如 TLS 1.3)由于完全移除了重协商的功能，因此这个扩展也不会出现。\"\n              ]\n            },\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"ff 01 - 表示这是 \\\"重协商信息\\\" 扩展\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 01 - 扩展信息长度为 0x01(1) 字节 \"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 - 重协商的长度为 0x0(0) 字节，因为这是一个全新连接\"\n                }\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/TLS12/serverHelloDone.json",
    "content": "[\n  \"服务器表示它已经完成了它那一半的握手过程。\",\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"记录头\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"16 03 03 00 04\"\n          },\n          [\n            \"TLS 会话被分解成 \\\"记录\\\"(record) 的形式发送和接收。记录是具有类型、协议版本和长度的数据块。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"16 - 表示 TLS 记录类型 0x16(22, Handshake)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"03 03 - 协议版本 (3.3, 即 TLS 1.2)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 04 - 紧接着的数组载荷长度 0x04(4) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"TLS 握手记录头\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"0e 00 00 00\"\n          },\n          [\n            \"每个 TLS 握手消息都以一个 type 和一个 len 开始。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"0e - 握手消息类型 0x0e (server hello done)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 00 - 紧接着的握手消息数据的长度 0x00 (0) 字节\"\n                }\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/TLS12/serverKeyExchange.json",
    "content": "[\n  \"服务器将密钥交换的公钥发送给客户端。等客户端算出自己的密钥交换后，共享的加密密钥就可以根据私钥和交换过来的公钥生成了。\",\n  \"现在双方已经同意使用 ECDHE 的密码加解密算法(ServiceHello 的 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA)。这意味着密钥对将基于选定的椭圆曲线(**E**lliptic **C**urve)，使用 **D**iffie-**H**ellman 编码，并且密钥对是基于该次连接(**E**phemeral)，而不是使用证书上的公/私钥。\",\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"记录头\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"16 03 03 01 2c\"\n          },\n          [\n            \"TLS 会话被分解成 \\\"记录\\\"(record) 的形式发送和接收。记录是具有类型、协议版本和长度的数据块。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"16 - 表示 TLS 记录类型 0x16(22, Handshake)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"03 03 - 协议版本 (3.3, 即 TLS 1.2)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"01 2c - 紧接着的数组载荷长度 0x12C(300) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"TLS 握手记录头\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"0c 00 01 28\"\n          },\n          [\n            \"每个 TLS 握手消息都以一个 type 和一个 len 开始。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"0c - 握手消息类型 0x0c (server key exchange)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 01 28 - 紧接着的握手消息数据的长度 0x128 (296) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"椭圆曲线信息\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"03 00 1d\"\n          },\n          [\n            \"表明服务器端选择的椭圆曲线。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"03 - 表明该曲线是具名的(named_curve)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 1d - 曲线 ID 0x01d(即 x25519 曲线)\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"服务器端公钥\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"20 9f d7 ad 6d cf f4 29 8d d3 f9 6d 5b 1b 2a f9 10 a0 53 5b 14 88 d7 f8 fa bb 34 9a 98 28 80 b6 15\"\n          },\n          [\n            \"\\\"服务器端准备密钥交换\\\" 步骤中生成的公钥。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 20 - 公钥长度为 0x20(32) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"9f d7 ... b6 15 - 公钥\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"签名\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"04 01 01 00 04 02 b6 61 f7 c1 91 ee 59 be 45 37 66 39 bd c3 d4 bb 81 e1 15 ca 73 c8 34 8b 52 5b 0d 23 38 aa 14 46 67 ed 94 31 02 14 12 cd 9b 84 4c ba 29 93 4a aa cc e8 73 41 4e c1 1c b0 2e 27 2d 0a d8 1f 76 7d 33 07 67 21 f1 3b f3 60 20 cf 0b 1f d0 ec b0 78 de 11 28 be ba 09 49 eb ec e1 a1 f9 6e 20 9d c3 6e 4f ff d3 6b 67 3a 7d dc 15 97 ad 44 08 e4 85 c4 ad b2 c8 73 84 12 49 37 25 23 80 9e 43 12 d0 c7 b3 52 2e f9 83 ca c1 e0 39 35 ff 13 a8 e9 6b a6 81 a6 2e 40 d3 e7 0a 7f f3 58 66 d3 d9 99 3f 9e 26 a6 34 c8 1b 4e 71 38 0f cd d6 f4 e8 35 f7 5a 64 09 c7 dc 2c 07 41 0e 6f 87 85 8c 7b 94 c0 1c 2e 32 f2 91 76 9e ac ca 71 64 3b 8b 98 a9 63 df 0a 32 9b ea 4e d6 39 7e 8c d0 1a 11 0a b3 61 ac 5b ad 1c cd 84 0a 6c 8a 6e aa 00 1a 9d 7d 87 dc 33 18 64 35 71 22 6c 4d d2 c2 ac 41 fb\"\n          },\n          [\n            \"由于服务器会为每个会话都生成短暂的密钥，所以和 TLS 之前的版本不同，密钥与证书不会有内在的联系。\",\n            \"为了证明服务器拥有服务器证书(在这个 TLS 会话中的有效性)，它需要使用证书的私钥对握手信息的哈希进行签名。而客户端可以通过使用证书的公钥来证明该签名的有效性。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"04 01 - 表明签名算法为 RSA-SHA256\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"01 00 - 表明签名算法长度为 0x100(256) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"04 02 b6 ... ac 41 fb - 签名(client_hello_random + server_hello_random + curve_info + public_key)\"\n                }\n              ]\n            },\n            {\n              \"children\": [\n                \"我们可以在命令行使用\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://tls12.xargs.org/files/server.key\"\n                  },\n                  \"content\": \"服务器的私钥\"\n                },\n                \"自己计算签名：\"\n              ]\n            },\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### client random from Client Hello\\n$ echo -en '\\\\x00\\\\x01\\\\x02\\\\x03\\\\x04\\\\x05\\\\x06\\\\x07'  > /tmp/compute\\n$ echo -en '\\\\x08\\\\x09\\\\x0a\\\\x0b\\\\x0c\\\\x0d\\\\x0e\\\\x0f' >> /tmp/compute\\n$ echo -en '\\\\x10\\\\x11\\\\x12\\\\x13\\\\x14\\\\x15\\\\x16\\\\x17' >> /tmp/compute\\n$ echo -en '\\\\x18\\\\x19\\\\x1a\\\\x1b\\\\x1c\\\\x1d\\\\x1e\\\\x1f' >> /tmp/compute\\n### server random from Server Hello\\n$ echo -en '\\\\x70\\\\x71\\\\x72\\\\x73\\\\x74\\\\x75\\\\x76\\\\x77' >> /tmp/compute\\n$ echo -en '\\\\x78\\\\x79\\\\x7a\\\\x7b\\\\x7c\\\\x7d\\\\x7e\\\\x7f' >> /tmp/compute\\n$ echo -en '\\\\x80\\\\x81\\\\x82\\\\x83\\\\x84\\\\x85\\\\x86\\\\x87' >> /tmp/compute\\n$ echo -en '\\\\x88\\\\x89\\\\x8a\\\\x8b\\\\x8c\\\\x8d\\\\x8e\\\\x8f' >> /tmp/compute\\n### the curve info section from this message\\n$ echo -en '\\\\x03\\\\x00\\\\x1d' >> /tmp/compute\\n### the public key sections from this msg\\n$ echo -en '\\\\x20\\\\x9f\\\\xd7\\\\xad\\\\x6d\\\\xcf\\\\xf4\\\\x29' >> /tmp/compute\\n$ echo -en '\\\\x8d\\\\xd3\\\\xf9\\\\x6d\\\\x5b\\\\x1b\\\\x2a\\\\xf9' >> /tmp/compute\\n$ echo -en '\\\\x10\\\\xa0\\\\x53\\\\x5b\\\\x14\\\\x88\\\\xd7\\\\xf8' >> /tmp/compute\\n$ echo -en '\\\\xfa\\\\xbb\\\\x34\\\\x9a\\\\x98\\\\x28\\\\x80\\\\xb6\\\\x15' >> /tmp/compute\\n$ openssl dgst -sign server.key -sha256 /tmp/compute | hexdump\\n\\n0000000 04 02 b6 61 f7 c1 91 ee 59 be 45 37 66 39 bd c3\\n... snip ...\\n00000f0 7d 87 dc 33 18 64 35 71 22 6c 4d d2 c2 ac 41 fb\"\n              }\n            }\n          ]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/TLS12/serverKeyExchangeGeneration.json",
    "content": "[\n  \"服务器端需要生成一个用于密钥交换的自己的“私钥/公钥”对。密钥交换(Key exchange)是一种技术，双方可以在同一数字上达成一致，而窃听者却无法知道这个数字是什么。\",\n  {\n    \"Tag\": \"p\",\n    \"children\": [\n      \"学习 TLS 并不需要深入了解，但你可以从\",\n      {\n        \"Tag\": \"a\",\n        \"props\": { \"href\": \"https://cangsdarm.github.io/illustrate/x25519\" },\n        \"content\": \"X25519 密钥交换算法\"\n      },\n      \"获取涉及到的密钥交换算法的具体解释。\"\n    ]\n  },\n  [\n    \"**私钥**是 0 到 \",\n    {\n      \"Tag\": \"Math\",\n      \"content\": \"2^256-1\"\n    },\n    \" 之间的一个随机整数(32bytes, 256bits)\",\n    \"。为方便后续解释，假设我们生成的私钥是：\"\n  ],\n  {\n    \"Tag\": \"pre\",\n    \"props\": {\n      \"className\": \"ind2\"\n    },\n    \"children\": [\n      {\n        \"Tag\": \"code\",\n        \"props\": { \"className\": \"longboi\" },\n        \"content\": \"909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeaf\"\n      }\n    ]\n  },\n  \"**公钥**由上面提到的 X25519 密钥交换算法生成。例子中计算出的公钥应如下所示：\",\n  {\n    \"Tag\": \"pre\",\n    \"props\": {\n      \"className\": \"ind2\"\n    },\n    \"children\": [\n      {\n        \"Tag\": \"code\",\n        \"props\": { \"className\": \"longboi\" },\n        \"content\": \"9fd7ad6dcff4298dd3f96d5b1b2af910a0535b1488d7f8fabb349a982880b615\"\n      }\n    ]\n  },\n  \"公钥的计算也可以在命令行中运行以下命令得到：\",\n  {\n    \"Tag\": \"CodeSample\",\n    \"props\": {\n      \"code\": \"### requires openssl 1.1.0 or higher\\n$ openssl pkey -noout -text < server-ephemeral-private.key\\n\\nX25519 Private-Key:\\npriv:\\n    90:91:92:93:94:95:96:97:98:99:9a:9b:9c:9d:9e:\\n    9f:a0:a1:a2:a3:a4:a5:a6:a7:a8:a9:aa:ab:ac:ad:\\n    ae:af\\npub:\\n    9f:d7:ad:6d:cf:f4:29:8d:d3:f9:6d:5b:1b:2a:f9:\\n    10:a0:53:5b:14:88:d7:f8:fa:bb:34:9a:98:28:80:\\n    b6:15\"\n    }\n  }\n]\n"
  },
  {
    "path": "src/TLS13/clientApplicationData.json",
    "content": "[\n  \"客户端发送数据：字符串\\\"ping\\\"\",\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"数据\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"70 69 6e 67\"\n          },\n          [\"字符串\\\"ping\\\"\"]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/TLS13/clientApplicationKeysCalc.json",
    "content": "[\n  \"客户端现在也可以计算应用会话时的密钥了。如果计算正确，结果应和服务器端的一致：\",\n  {\n    \"Tag\": \"ul\",\n    \"children\": [\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"客户端会话密钥: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"de2f4c7672723a692319873e5c227606691a32d1c59d8b9f51dbb9352e9ca9cc\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"客户端会话向量: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"bb007956f474b25de902432f\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"服务器端会话密钥: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"01f78623f17e3edcc09e944027ba3218d57c8e0db93cd3ac419309274700ac27\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"服务器端会话向量: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"196a750b0c5049c0cc51a541\"\n          }\n        ]\n      }\n    ]\n  }\n]\n"
  },
  {
    "path": "src/TLS13/clientChangeCipherSpec.json",
    "content": "[\n  \"这条记录是用于旧版本 TLS 的。TLS 1.3 虽然不会产生效果，但是在开启\\\"中间件兼容模式\\\"时，该记录仍然会被发送，以帮助将 TLS 1.3 的会话伪装成 TLS 1.2 的会话。\",\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"记录头\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"14 03 03 00 01\"\n          },\n          [\n            \"TLS 会话被分解成 \\\"记录\\\"(record) 的形式发送和接收。记录是具有类型、协议版本和长度的数据块。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"14 - 表示 TLS 记录类型 0x14(20, ChangeCipherSpec)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"03 03 - 协议版本 (3.3, 即 TLS 1.2)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 01 - 紧接着的数组载荷长度 0x01(1) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"数据载荷\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"01\"\n          },\n          []\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/TLS13/clientHandshakeFinished.json",
    "content": "[\n  \"为了验证握手成功且没有被篡改过，客户端和服务器端一样，需要创建一些验证数据给服务器端确认。验证数据是基于所有握手信息的哈希值计算得到。\",\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"握手消息头\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"14 00 00 30\"\n          },\n          [\n            \"每个握手消息都以一个 type 和一个 len 开始。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"14 - 握手消息类型 0x14 (finished)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 30 - 紧接着的握手消息数据的长度 0x30 (48) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"验证数据\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"bf f5 6a 67 1b 6c 65 9d 0a 7c 5d d1 84 28 f5 8b dd 38 b1 84 a3 ce 34 2d 9f de 95 cb d5 05 6f 7d a7 91 8e e3 20 ea b7 a9 3a bd 8f 1c 02 45 4d 27\"\n          },\n          [\n            \"使用 \\\"客户端生成握手密钥\\\" 步骤中的客户端密文和在这之前的每个握手记录(ClientHello 到 服务器端握手结束)的 SHA256 哈希值生成。\",\n            {\n              \"Tag\": \"pre\",\n              \"children\": [\n                {\n                  \"Tag\": \"code\",\n                  \"props\": { \"className\": \"longboi\" },\n                  \"content\": \"finished_key = HKDF-Expand-Label(key: client_secret, label: \\\"finished\\\", ctx: \\\"\\\", len: 48)\\nfinished_hash = SHA384(Client Hello ... Server Finished)\\nverify_data = HMAC-SHA384(key: finished_key, msg: finished_hash)\"\n                }\n              ]\n            },\n            {\n              \"children\": [\n                \"在命令行中使用\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://tls13.xargs.org/files/hkdf-384.sh\"\n                  },\n                  \"content\": \"原作者制作的 HKDF 命令行脚本\"\n                },\n                \"，你也可以自己试试：\"\n              ]\n            },\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### find the hash of the conversation to this point, excluding\\n### 5-byte record headers or 1-byte wrapped record trailers\\n$ fin_hash=$((\\n    tail -c +6 clienthello;\\n    tail -c +6 serverhello;\\n    perl -pe 's/.$// if eof' serverextensions;\\n    perl -pe 's/.$// if eof' servercert;\\n    perl -pe 's/.$// if eof' servercertverify;\\n    perl -pe 's/.$// if eof' serverfinished) | openssl sha384)\\n$ cht_secret=db89d2d6df0e84fed74a2288f8fd4d0959f790ff23946cdf4c26d85e51bebd42ae184501972f8d30c4a3e4a3693d0ef0\\n$ fin_key=$(./hkdf-384 expandlabel $cht_secret \\\"finished\\\" \\\"\\\" 48)\\n$ echo $fin_hash | xxd -r -p \\\\\\n    | openssl dgst -sha384 -mac HMAC -macopt hexkey:$fin_key\\n\\nbff56a671b6c659d0a7c5dd18428f58bdd38b184a3ce342d9fde95cbd5056f7da7918ee320eab7a93abd8f1c02454d27\"\n              }\n            }\n          ]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/TLS13/clientHandshakeKeysCalc.json",
    "content": "[\n  \"客户端现在也拥有了用于计算剩余握手步骤的加密密钥的所有信息。在这个计算中，客户端使用了以下信息：\",\n  {\n    \"Tag\": \"ul\",\n    \"children\": [\n      {\n        \"Tag\": \"li\",\n        \"content\": \"服务器端公钥(提取自 ServerHello)\"\n      },\n      {\n        \"Tag\": \"li\",\n        \"content\": \"客户端私钥(之前准备密钥交换时生成)\"\n      },\n      {\n        \"Tag\": \"li\",\n        \"content\": \"ClientHello 和 ServerHello 的 SHA256 哈希值\"\n      }\n    ]\n  },\n  \"首先，客户端需要找到共享的密文(shared secret)，即密钥交换步骤的最终值。客户端通过使用 curve25519 算法将服务器端的公钥乘以客户端的私钥(椭圆曲线乘法的特性将使得计算结果与服务器在其计算中得到的结果一致)，即可发现 32 字节的最终值是：\",\n  {\n    \"Tag\": \"pre\",\n    \"props\": {\n      \"className\": \"ind2\"\n    },\n    \"children\": [\n      {\n        \"Tag\": \"code\",\n        \"props\": { \"className\": \"longboi\" },\n        \"content\": \"df4a291baa1eb7cfa6934b29b474baad2697e29f1f920dcc77c8a0a088447624\"\n      }\n    ]\n  },\n  {\n    \"children\": [\n      \"你可以使用\",\n      {\n        \"Tag\": \"a\",\n        \"props\": { \"href\": \"https://quic.xargs.org/files/curve25519-mult.c\" },\n        \"content\": \"原作者的脚本\"\n      },\n      \"快速验证结果：\"\n    ]\n  },\n  {\n    \"Tag\": \"CodeSample\",\n    \"props\": {\n      \"code\": \"$ cc -o curve25519-mult curve25519-mult.c\\n$ ./curve25519-mult client-ephemeral-private.key \\\\\\n                    server-ephemeral-public.key | hexdump\\n\\n0000000 df 4a 29 1b aa 1e b7 cf a6 93 4b 29 b4 74 ba ad\\n0000010 26 97 e2 9f 1f 92 0d cc 77 c8 a0 a0 88 44 76 24\"\n    }\n  },\n  \"由于上面的计算结果与服务器端计算结果相同，且剩下的计算过程也相同，因此算出来的数值也应该是相同的：\",\n  {\n    \"Tag\": \"ul\",\n    \"children\": [\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"客户端握手时密钥: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"1135b4826a9a70257e5a391ad93093dfd7c4214812f493b3e3daae1eb2b1ac69\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"客户端握手时向量: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"4256d2e0e88babdd05eb2f27\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"客户端握手时流量密文(handshake traffic secret): \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"84b3c21cacaf9f54c885e9a506459079\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"服务器端握手时密钥: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"9f13575ce3f8cfc1df64a77ceaffe89700b492ad31b4fab01c4792be1b266b7f\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"服务器端握手时向量: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"9563bc8b590f671f488d2da3\"\n          }\n        ]\n      }\n    ]\n  }\n]\n"
  },
  {
    "path": "src/TLS13/clientHello.json",
    "content": "[\n  \"TLS 会话以 \\\"ClientHello\\\" 开始。客户端提供的信息包括以下内容：\",\n  {\n    \"Tag\": \"ul\",\n    \"children\": [\n      {\n        \"Tag\": \"li\",\n        \"content\": \"客户端随机数(用于之后的握手过程)\"\n      },\n      {\n        \"Tag\": \"li\",\n        \"content\": \"一个客户端支持的有序的加解密算法数组\"\n      },\n      {\n        \"Tag\": \"li\",\n        \"content\": \"用于密钥交换的公钥\"\n      },\n      {\n        \"Tag\": \"li\",\n        \"content\": \"客户端支持的协议版本列表\"\n      }\n    ]\n  },\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"TLS 数据头\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"16 03 01 00 f8\"\n          },\n          [\n            \"TLS 会话被分解成 \\\"记录\\\"(record) 的形式发送和接收。记录是具有类型、协议版本和长度的数据块。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"16 - 表示 DTLS 记录类型 0x16(22, Handshake)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"03 01 - 协议版本 (3.1, 即 TLS 1.0)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 f8 - 紧接着的数组载荷长度 0xf8(248) 字节\"\n                }\n              ]\n            },\n            \"有趣的是，这里的版本号并不是预期的 3.3(TLS 1.2)，而是 3.1(TLS 1.0)。TLS 1.2 中也是这样。这样做是为了提高与早期实现的其他互联网组件的互操作性。\"\n          ]\n        ],\n        [\n          \"TLS 握手记录头\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"01 00 00 f4\"\n          },\n          [\n            \"每个 TLS 握手消息都以一个 type 和一个 len 开始。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"01 - 握手消息类型 0x01 (ClientHello)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 f4 - 紧接着的握手消息数据的长度 0xf4 (244) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"客户端 TLS 版本号\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"03 03\"\n          },\n          [\n            \"协议版本为 \\\"3,3\\\"(指 TLS 1.2)。因为被广泛部署的中间件(middleboxes)并不允许他们不承认的协议版本，所以所有的 TLS 1.3 会话在这个字段中都以 TLS 1.2 版本表示。也因为如此，这个字段不再被使用，而是使用下面的 \\\"支持的版本\\\" 扩展来进行版本协商。\",\n            \"而不寻常的版本号(\\\"3,3\\\" 代表 TLS 1.2)是由于 TLS 1.0 是 SSL 3.0 协议的一个小修订。因此，TLS 1.0 用 \\\"3,1\\\" 表示，TLS 1.1 用 \\\"3,2\\\"，以此类推。\"\n          ]\n        ],\n        [\n          \"客户端随机数\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f\"\n          },\n          [\n            \"客户端提供的 32 字节的随机数。这个数将在之后的会话中使用。在本文的例子中，我们暂时将一个方便记忆的字符串当作随机数。\"\n          ]\n        ],\n        [\n          \"会话 ID\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"20 e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff\"\n          },\n          [\n            \"在之前的 TLS 版本中，客户端可以提供一个之前会话的会话 ID。ID 可以让服务器端和客户端跳过协商新密钥的时间并减少其他潜在的成本。\",\n            \"而在 TLS 1.3 中，这种 \\\"会话恢复\\\" 是通过更灵活的 PSK(预共享密钥) 机制完成的，所以这个字段不再被用于该目的。相反，这个字段的非空值被用来触发\\\"中间件兼容模式\\\"，这有助于将 TLS 1.3 会话伪装成正在尝试恢复的 TLS 1.2 会话。\",\n            \"此时，客户端用随机数据来填充这个字段。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"20 - 0x20(32) 字节的会话 ID\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"e0 e1 ... fe ff - 假的会话 ID\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"加解密算法列表\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 08 13 02 13 03 13 01 00 ff\"\n          },\n          [\n            \"客户端提供一个有序的列表，以说明它支持哪些加解密算法进行加密。该列表是按照客户端的偏好顺序排列的，以最高偏好为先。\",\n            \"在 TLS 1.3 中，可能选用的算法数量已经大大减少。其中剩下的所有算法都包含 AEAD 算法支持。AEAD 提供了比旧算法更强大的加密保证，并且能更容易地与其他算法兼容，实现 All-in-one。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 08 - 8 字节的加解密算法列表数据\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"13 02 - 代表 TLS_AES_256_GCM_SHA384\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"13 03 - 代表 TLS_CHACHA20_POLY1305_SHA256\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"13 01 - 代表 TLS_AES_128_GCM_SHA256\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"13 01 - 代表 TLS_EMPTY_RENEGOTIATION_INFO_SCSV\"\n                }\n              ]\n            },\n            {\n              \"children\": [\n                \"完整加解密算法列表请查看 \",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml\"\n                  },\n                  \"content\": \"IANA tls-parameters\"\n                },\n                \" 的 TLS Cipher Suites 部分。\"\n              ]\n            }\n          ]\n        ],\n        [\n          \"压缩算法\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"01 00\"\n          },\n          [\n            {\n              \"children\": [\n                \"以前的 TLS 版本支持压缩(以及对应延伸出的 DTLS 版本)，这被发现会泄露加密数据的信息(见 \",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://zhuanlan.zhihu.com/p/333314023\"\n                  },\n                  \"content\": \"CRIME/BREACH 攻击\"\n                },\n                \")。\"\n              ]\n            },\n            \"TLS 1.3 不再允许压缩。所以这个字段不会变化：采用 \\\"null\\\" 压缩算法，对数据不做任何改变。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"01 - 1 字节的压缩算法长度\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 - 代表 \\\"没有\\\"(null) 压缩算法\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"扩展的长度\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 a3\"\n          },\n          [\n            \"客户端提供了一个可选的扩展列表，服务器可以根据它来采取某些行动或启用某些特性。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 a3 - 扩展列表的长度为 0xA3(163) 字节\"\n                }\n              ]\n            },\n            \"每个扩展将以两个字节开始，表明它是哪个扩展。然后是两个字节的内容长度字段，最后是扩展的具体内容。\"\n          ]\n        ],\n        [\n          \"扩展 - 服务器名称指示\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 00 00 18 00 16 00 00 13 65 78 61 6d 70 6c 65 2e 75 6c 66 68 65 69 6d 2e 6e 65 74\"\n          },\n          [\n            \"(例子中的)客户端提供了它想要联系的服务器的名称，也被称为 SNI(服务器名称指示)。\",\n            \"如果没有这个扩展，HTTPS 服务器将无法为一个 IP 地址上的多个主机名(虚拟主机)提供服务。因为它在 TLS 会话协商结束、HTTP 请求发出后才知道要发送哪个主机名的证书。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 - 表示这是 \\\"服务器名称指示\\\" 扩展\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 18 - 扩展信息长度为 0x18(24) 字节 \"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 16 - 第一个(也是唯一的)列表条目长度为 0x16(22) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 - 0x00 表示这是 \\\"主机名\\\" 列表\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 13 - 主机名列表的长度为 0x13(19) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"65 78 61 ... 6e 65 74 - 主机名：\\\"example.ulfheim.net\\\"\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"扩展 - 椭圆曲线参数指示(EC Point Formats)\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 0b 00 04 03 00 01 02\"\n          },\n          [\n            \"在椭圆曲线(EC)加密过程中，客户端和服务器通过这个扩展协商椭圆曲线相关的信息(如选择的起始点和是否压缩)。\",\n            \"TLS 1.3 并不支持协商点的能力，相反，每条曲线都有一个预选的点。但在版本协商成功之前(因此结果有可能是 TLS 1.2)，如果 EC 被启用，那么这个扩展总是被添加。(细节请参考文后参考一节)\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 0b - 表示这是 \\\"椭圆曲线参数指示\\\" 扩展\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 04 - 扩展信息长度为 0x04(4) 字节 \"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"03 - 指示长度为 0x03(3) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 - 表示未压缩\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"01 - 表示曲线格式为 ansiX962_compressed_prime\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"02 - 表示曲线格式为 ansiX962_compressed_char2\"\n                }\n              ]\n            },\n            \"译注：TLS 1.3 如果用 OpenSSL 协商的话，00 01 02 是默认值。(大概率也不会有人瞎改，因此也是绝大部分场景实际出现的值)\"\n          ]\n        ],\n        [\n          \"扩展 - 支持的组\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 0a 00 16 00 14 00 1d 00 17 00 1e 00 19 00 18 01 00 01 01 01 02 01 03 01 04\"\n          },\n          [\n            \"(例子中的)客户端表示它支持十种类型的椭圆曲线算法(ECC)。为了使这个扩展更加通用，未来可以支持其他的密码学类型，因此称这些为 \\\"支持的组\\\" 而不是 \\\"支持的曲线\\\"。\",\n            \"这个列表同样是按照客户端的偏好降序排列的。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 0a - 表示这是 \\\"支持的组\\\" 扩展\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 16 - 扩展信息长度为 0x16(22) 字节 \"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 14 - 曲线列表条目长度为 0x14(20) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 1d - 代表 x25519 曲线\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 17 - 代表 secp256r1 曲线\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 1e - 代表 x448 曲线\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 19 - 代表 secp521r1 曲线\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 18 - 代表 secp384r1 曲线\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"10 00 - 代表 ffdhe2048 曲线\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"10 01 - 代表 ffdhe3072 曲线\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"10 02 - 代表 ffdhe4096 曲线\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"10 03 - 代表 ffdhe6144 曲线\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"10 04 - 代表 ffdhe8192 曲线\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"扩展 - 会话记录单(Session Ticket)\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 23 00 00\"\n          },\n          [\n            \"(例子中的)客户端表明它不会为本次连接提供会话记录单。\",\n            \"会话记录单用只有服务端知道的安全密钥加密过的会话信息，保存在客户端，只要服务器能成功解密就可以完成快速握手。\",\n            {\n              \"children\": [\n                \"但会话记录单的引入，\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://halfrost.com/tls_1-3_0-rtt/\"\n                  },\n                  \"content\": \"破坏了 TLS 的安全模型。\"\n                }\n              ]\n            },\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 23 - 表示这是 \\\"会话记录单\\\" 扩展\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 - 扩展信息长度为 0x00(0) 字节 \"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"扩展 - Encrypt-then-MAC\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 16 00 00\"\n          },\n          [\n            {\n              \"children\": [\n                \"客户端表示其支持 EtM。EtM 可以防止早期版本的 TLS 和 DTLS 的\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://iacr.org/archive/crypto2001/21390309.pdf\"\n                  },\n                  \"content\": \"某些漏洞\"\n                },\n                \"。在 TLS 1.3 中，这个机制始终被开启，因此这个扩展在会话中没有影响。\"\n              ]\n            },\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 16 - 表示这是 \\\"EtM\\\" 扩展\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 - 扩展信息长度为 0x00(0) 字节 \"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"扩展 - 扩展主密钥(Extended Master Secret)\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 17 00 00\"\n          },\n          [\n            {\n              \"children\": [\n                \"客户端表示支持额外的密码学操作，以防止早期版本 TLS 出现的漏洞（详见 \",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://iacr.org/archive/crypto2001/21390309.pdf\"\n                  },\n                  \"content\": \"RFC 7627\"\n                },\n                \"）。在 TLS 1.3 中，这些漏洞不再存在，所以这个扩展在这个会话中没有影响。\"\n              ]\n            },\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 17 - 表示这是 \\\"扩展主密钥\\\" 扩展\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 - 扩展信息长度为 0x00(0) 字节 \"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"扩展 - 签名算法列表\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 0d 00 1e 00 1c 04 03 05 03 06 03 08 07 08 08 08 09 08 0a 08 0b 08 04 08 05 08 06 04 01 05 01 06 01\"\n          },\n          [\n            \"这个扩展表示客户端支持哪些签名算法。这可能会影响到服务器提交给客户端的证书，以及服务器在 CertificateVerify 记录中发送的签名。\",\n            \"这个列表同样是按照客户端的偏好降序排列的。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 0d - 表示这是 \\\"签名算法列表\\\" 扩展\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 1e - 扩展信息长度为 0x1E(30) 字节 \"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 1c - 算法列表长度为 0x1C(28) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"04 03 - 代表 ECDSA-SECP256r1-SHA256 算法\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"05 03 - 代表 ECDSA-SECP384r1-SHA384 算法\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"06 03 - 代表 ECDSA-SECP521r1-SHA512 算法\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"08 07 - 代表 ED25519 算法\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"08 08 - 代表 ED448 算法\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"08 09 - 代表 RSA-PSS-PSS-SHA256 算法\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"08 0a - 代表 RSA-PSS-PSS-SHA384 算法\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"08 0b - 代表 RSA-PSS-PSS-SHA512 算法\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"08 04 - 代表 RSA-PSS-RSAE-SHA256 算法\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"08 05 - 代表 RSA-PSS-RSAE-SHA384 算法\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"08 06 - 代表 RSA-PSS-RSAE-SHA512 算法\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"04 01 - 代表 RSA-PKCS1-SHA256 算法\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"05 01 - 代表 RSA-PKCS1-SHA384 算法\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"06 01 - 代表 RSA-PKCS1-SHA512 算法\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"扩展 - 支持的版本\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 2b 00 03 02 03 04\"\n          },\n          [\n            \"客户端表明其支持 TLS 1.3。由于兼容性的原因(可能协商结果为 TLS 1.2)，这被放在一个扩展中，而不是上面的客户端版本字段。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 2b - 表示这是 \\\"支持的版本\\\" 扩展\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 03 - 扩展信息长度为 0x03(3) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"02 - TLS 版本长度为 0x02(2) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"03 04 - 代表 TLS 1.3\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"扩展 - PSK 密钥交换模式(PSK Key Exchange Modes)\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 2d 00 02 01 01\"\n          },\n          [\n            \"客户端仅支持使用该扩展列表所列出的模式的 PSK。由于我们在这个会话中不使用 PSK，这个扩展没有影响。\",\n            \"如果客户端提供了 \\\"PSK\\\" 扩展，那么它必须也要提供 \\\"PSK 密钥交换模式\\\" 扩展。如果客户端未提供含有该扩展的 \\\"PSK\\\"，服务器端必须立即中止握手。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 2d - 表示这是 \\\"PSK 密钥交换模式\\\" 扩展\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 02 - 扩展信息长度为 0x02(2) 字节 \"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"01 - 密钥交换的模式长度为 0x01(1) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"01 - 代表 \\\"PSK with (EC)DHE key establishment\\\"\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"扩展 - 算法公钥列表(Key Share)\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 33 00 26 00 24 00 1d 00 20 35 80 72 d6 36 58 80 d1 ae ea 32 9a df 91 21 38 38 51 ed 21 a2 8e 3b 75 e9 65 d0 d2 cd 16 62 54\"\n          },\n          [\n            \"客户端会给某些它认为服务器也会支持的算法发送对应的短暂的公钥。这允许在 ClientHello 和 ServerHello 消息之后的其余握手记录被加密。而不用像以前的协议版本，以透明的方式发送握手记录。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 33 - 表示这是 \\\"算法公钥列表\\\" 扩展\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 26 - 扩展信息长度为 0x26(38) 字节 \"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 24 - 算法公钥列表长度为 0x24(36) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 1d - 代表 x25519 算法(例子中为通过 curve25519 算法进行密钥交换)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 20 - 公钥长度为 0x20(32) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"35 80 ... 62 54 - \\\"客户端准备密钥交换\\\" 步骤中生成的公钥\"\n                }\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/TLS13/clientKeyExchangeGeneration.json",
    "content": "[\n  \"连接开始时，客户端生成一个用于密钥交换的“私钥/公钥”对。密钥交换(Key exchange)是一种技术，双方可以在同一数字上达成一致，而窃听者却无法知道这个数字是什么。\",\n  {\n    \"Tag\": \"p\",\n    \"children\": [\n      \"学习 TLS 并不需要深入了解，但你可以从\",\n      {\n        \"Tag\": \"a\",\n        \"props\": { \"href\": \"https://cangsdarm.github.io/illustrate/x25519\" },\n        \"content\": \"X25519 密钥交换算法\"\n      },\n      \"获取涉及到的密钥交换算法的具体解释。\"\n    ]\n  },\n  [\n    \"**私钥**是 0 到 \",\n    {\n      \"Tag\": \"Math\",\n      \"content\": \"2^256-1\"\n    },\n    \" 之间的一个随机整数(32bytes, 256bits)\",\n    \"。为方便后续解释，假设我们生成的私钥是：\"\n  ],\n  {\n    \"Tag\": \"pre\",\n    \"props\": {\n      \"className\": \"ind2\"\n    },\n    \"children\": [\n      {\n        \"Tag\": \"code\",\n        \"props\": { \"className\": \"longboi\" },\n        \"content\": \"202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f\"\n      }\n    ]\n  },\n  \"**公钥**由上面提到的 X25519 密钥交换算法生成。例子中计算出的公钥应如下所示：\",\n  {\n    \"Tag\": \"pre\",\n    \"props\": {\n      \"className\": \"ind2\"\n    },\n    \"children\": [\n      {\n        \"Tag\": \"code\",\n        \"props\": { \"className\": \"longboi\" },\n        \"content\": \"358072d6365880d1aeea329adf9121383851ed21a28e3b75e965d0d2cd166254\"\n      }\n    ]\n  },\n  \"公钥的计算也可以在命令行中运行以下命令得到：\",\n  {\n    \"Tag\": \"CodeSample\",\n    \"props\": {\n      \"code\": \"### requires openssl 1.1.0 or higher\\n$ openssl pkey -noout -text < client-ephemeral-private.key\\n\\nX25519 Private-Key:\\npriv:\\n    20:21:22:23:24:25:26:27:28:29:2a:2b:2c:2d:2e:\\n    2f:30:31:32:33:34:35:36:37:38:39:3a:3b:3c:3d:\\n    3e:3f\\npub:\\n    35:80:72:d6:36:58:80:d1:ae:ea:32:9a:df:91:21:\\n    38:38:51:ed:21:a2:8e:3b:75:e9:65:d0:d2:cd:16:\\n    62:54\"\n    }\n  },\n  \"此时，网络连接仍在准备中，没有任何数据被传输。\"\n]\n"
  },
  {
    "path": "src/TLS13/index.js",
    "content": "const data = {\n  intro: {\n    title: \"图解 TLS 1.3 连接\",\n    subtitle: \"对每一个字节的解释和再现\",\n    desc: '这里演示的连接是基于 OpenSSL 3.0.1 并开启\"中间件兼容模式\"后的效果。具体内容请查看注释。',\n    intro:\n      '在这个演示中，客户端通过 TLS 1.3 加密协商连接服务器。客户端发送\"ping\"、接收\"pong\"后终止连接。点击下面开始探索。',\n  },\n  sections: [\n    {\n      type: \"RecOuter\",\n      tags: [\"calculation\", \"client\"],\n      label: \"客户端准备密钥交换\",\n      illustration: {\n        src: \"https://quic.xargs.org/images/key6.png\",\n        width: \"105\",\n        height: \"250\",\n      },\n      json: () => import(\"./clientKeyExchangeGeneration.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"client\"],\n      label: \"ClientHello\",\n      illustration: {\n        src: \"https://quic.xargs.org/images/key1.png\",\n        width: \"135\",\n        height: \"250\",\n      },\n      json: () => import(\"./clientHello.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"calculation\", \"server\"],\n      label: \"服务器端准备密钥交换\",\n      illustration: {\n        src: \"https://quic.xargs.org/images/key4.png\",\n        width: \"106\",\n        height: \"250\",\n      },\n      json: () => import(\"./serverKeyExchangeGeneration.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"server\"],\n      label: \"ServerHello\",\n      illustration: {\n        src: \"https://quic.xargs.org/images/key2.png\",\n        width: \"124\",\n        height: \"250\",\n      },\n      json: () => import(\"./serverHello.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"calculation\", \"server\"],\n      label: \"服务器端生成握手密钥\",\n      illustration: {\n        src: \"https://quic.xargs.org/images/key9.png\",\n        width: \"97\",\n        height: \"250\",\n      },\n      json: () => import(\"./serverHandshakeKeysCalc.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"calculation\", \"client\"],\n      label: \"客户端生成握手密钥\",\n      illustration: {\n        src: \"https://quic.xargs.org/images/key8.png\",\n        width: \"97\",\n        height: \"250\",\n      },\n      json: () => import(\"./clientHandshakeKeysCalc.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"server\"],\n      label: \"服务器端秘钥规格变更\",\n      json: () => import(\"./serverChangeCipherSpec.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"server\"],\n      label: \"服务器端伪装记录\",\n      id: \"wrappedRecord1\",\n      json: () => import(\"./wrappedRecord1.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"server\", \"embedded\"],\n      label: \"服务器端加密后的额外扩展\",\n      json: () => import(\"./serverEncryptedExtensions.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"server\"],\n      label: \"服务器端伪装记录\",\n      id: \"wrappedRecord2\",\n      illustration: {\n        src: \"https://quic.xargs.org/images/key5.png\",\n        width: \"124\",\n        height: \"250\",\n      },\n      json: () => import(\"./wrappedRecord2.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"server\", \"embedded\"],\n      label: \"服务器端证书\",\n      json: () => import(\"./serverCertificate.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"server\"],\n      label: \"服务器端伪装记录\",\n      id: \"wrappedRecord3\",\n      illustration: {\n        src: \"https://quic.xargs.org/images/key5.png\",\n        width: \"124\",\n        height: \"250\",\n      },\n      json: () => import(\"./wrappedRecord3.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"server\", \"embedded\"],\n      label: \"服务器端证书验证数据\",\n      json: () => import(\"./serverCertVerify.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"server\"],\n      label: \"服务器端伪装记录\",\n      id: \"wrappedRecord4\",\n      illustration: {\n        src: \"https://quic.xargs.org/images/key5.png\",\n        width: \"124\",\n        height: \"250\",\n      },\n      json: () => import(\"./wrappedRecord4.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"server\", \"embedded\"],\n      label: \"服务器端握手完成\",\n      json: () => import(\"./serverHandshakeFinished.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"calculation\", \"server\"],\n      label: \"服务器端生成会话密钥(application keys)\",\n      illustration: {\n        src: \"https://quic.xargs.org/images/key9.png\",\n        width: \"97\",\n        height: \"250\",\n      },\n      json: () => import(\"./serverApplicationKeysCalc.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"calculation\", \"client\"],\n      label: \"客户端生成会话密钥\",\n      illustration: {\n        src: \"https://quic.xargs.org/images/key8.png\",\n        width: \"97\",\n        height: \"250\",\n      },\n      json: () => import(\"./clientApplicationKeysCalc.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"client\"],\n      label: \"客户端秘钥规格变更\",\n      json: () => import(\"./clientChangeCipherSpec.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"client\"],\n      label: \"客户端伪装记录\",\n      id: \"wrappedRecord5\",\n      json: () => import(\"./wrappedRecord5.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"client\", \"embedded\"],\n      label: \"客户端握手完成\",\n      json: () => import(\"./clientHandshakeFinished.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"client\"],\n      label: \"客户端伪装记录\",\n      id: \"wrappedRecord6\",\n      json: () => import(\"./wrappedRecord6.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"client\", \"embedded\"],\n      label: \"客户端会话数据\",\n      json: () => import(\"./clientApplicationData.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"server\"],\n      label: \"服务器端伪装记录\",\n      id: \"wrappedRecord7\",\n      json: () => import(\"./wrappedRecord7.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"server\", \"embedded\"],\n      label: \"服务器端第一个会话记录单(New Session Ticket 1)\",\n      json: () => import(\"./serverNewSessionTicket1.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"server\"],\n      label: \"服务器端伪装记录\",\n      id: \"wrappedRecord8\",\n      json: () => import(\"./wrappedRecord8.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"server\", \"embedded\"],\n      label: \"服务器端第二个会话记录单(New Session Ticket 2)\",\n      json: () => import(\"./serverNewSessionTicket2.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"server\"],\n      label: \"服务器端伪装记录\",\n      id: \"wrappedRecord9\",\n      json: () => import(\"./wrappedRecord9.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"record\", \"server\", \"embedded\"],\n      label: \"服务器会话数据\",\n      json: () => import(\"./serverApplicationData.json\"),\n    },\n  ],\n  ending: {\n    mother: \"https://tls13.xargs.org/\",\n    references: [\n      {\n        title: \"TLS Parameters\",\n        href: \"https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml\",\n      },\n      {\n        title: \"TLS 1.3 RFC\",\n        href: \"https://www.ietf.org/rfc/rfc8446.html\",\n      },\n      {\n        title: \"OpenSSL 邮件列表: 如何在 ClientHello 中关闭 EC Point Formats\",\n        href: \"https://mta.openssl.org/pipermail/openssl-users/2019-November/011572.html\",\n      },\n      {\n        title: \"为何 openssl -enc 不支持 AEAD?\",\n        href: \"https://github.com/openssl/openssl/issues/12220\",\n      },\n      {\n        title: \"TLS 1.3 的伪装相关\",\n        href: \"https://github.com/openssl/openssl/issues/17654#issuecomment-1031744534\",\n      },\n      {\n        title: \"从字节层面查看基于 TLS 1.3 的页面请求及响应\",\n        href: \"https://github.com/jawj/subtls\"\n      }\n    ],\n  },\n};\n\nexport default data;\n"
  },
  {
    "path": "src/TLS13/serverApplicationData.json",
    "content": "[\n  \"服务器端响应数据：字符串\\\"pong\\\"\",\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"数据\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"70 6f 6e 67\"\n          },\n          [\"字符串\\\"pong\\\"\"]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/TLS13/serverApplicationKeysCalc.json",
    "content": "[\n  \"服务器端现在可以计算应用会话时的密钥了。在这个计算中，服务器使用了以下信息：\",\n  {\n    \"Tag\": \"ul\",\n    \"children\": [\n      {\n        \"Tag\": \"li\",\n        \"content\": \"服务器端密文(之前准备握手密钥时生成)\"\n      },\n      {\n        \"Tag\": \"li\",\n        \"content\": \"ClientHello 到 服务器握手结束 所有握手时包的 SHA256 哈希值\"\n      }\n    ]\n  },\n  \"这个哈希值包括：ClientHello、ServerHello、[未伪装部分]额外扩展、[未伪装部分]服务器证书、[未伪装部分]服务器证书验证、[未伪装部分]服务器握手完成。且散列的输入不包括 ClientHello 和 ServerHello 的 5 字节 \\\"TLS 记录\\\" 头；同时也不包括未伪装的记录的尾部 1 字节的 \\\"记录类型\\\"。\",\n  \"这个 32 字节的 \\\"handshake_hash\\\" 最终值是：\",\n  {\n    \"Tag\": \"pre\",\n    \"props\": {\n      \"className\": \"ind2\"\n    },\n    \"children\": [\n      {\n        \"Tag\": \"code\",\n        \"props\": { \"className\": \"longboi\" },\n        \"content\": \"fa6800169a6baac19159524fa7b9721b41be3c9db6f3f93fa5ff7e3db3ece204d2b456c51046e40ec5312c55a86126f5\"\n      }\n    ]\n  },\n  {\n    \"Tag\": \"CodeSample\",\n    \"props\": {\n      \"code\": \"# strip first 5 bytes of hello records, and trailing byte of unwrapped records\\n$ (tail -c +6 clienthello; tail -c +6 serverhello; \\\\\\n   perl -pe 's/.$// if eof' serverextensions; \\\\\\n   perl -pe 's/.$// if eof' servercert; \\\\\\n   perl -pe 's/.$// if eof' servercertverify; \\\\\\n   perl -pe 's/.$// if eof' serverfinished) | openssl sha384\\n\\nfa6800169a6baac19159524fa7b9721b41be3c9db6f3f93fa5ff7e3db3ece204d2b456c51046e40ec5312c55a86126f5\\n    \"\n    }\n  },\n  \"然后，我们将哈希值和共享的签名信息进行一些密钥派生操作(key derivation operations)，以防止可能的攻击：\",\n  {\n    \"Tag\": \"pre\",\n    \"children\": [\n      {\n        \"Tag\": \"code\",\n        \"props\": { \"className\": \"longboi\" },\n        \"content\": \"empty_hash = SHA384(\\\"\\\")\\nderived_secret = HKDF-Expand-Label(key: handshake_secret, label: \\\"derived\\\", ctx: empty_hash, len: 48)\\nmaster_secret = HKDF-Extract(salt: derived_secret, key: 00...)\\nclient_secret = HKDF-Expand-Label(key: master_secret, label: \\\"c ap traffic\\\", ctx: handshake_hash, len: 48)\\nserver_secret = HKDF-Expand-Label(key: master_secret, label: \\\"s ap traffic\\\", ctx: handshake_hash, len: 48)\\nclient_application_key = HKDF-Expand-Label(key: client_secret, label: \\\"key\\\", ctx: \\\"\\\", len: 32)\\nserver_application_key = HKDF-Expand-Label(key: server_secret, label: \\\"key\\\", ctx: \\\"\\\", len: 32)\\nclient_application_iv = HKDF-Expand-Label(key: client_secret, label: \\\"iv\\\", ctx: \\\"\\\", len: 12)\\nserver_application_iv = HKDF-Expand-Label(key: server_secret, label: \\\"iv\\\", ctx: \\\"\\\", len: 12)\"\n      }\n    ]\n  },\n  {\n    \"children\": [\n      \"在命令行中使用\",\n      {\n        \"Tag\": \"a\",\n        \"props\": { \"href\": \"https://tls13.xargs.org/files/hkdf-384.sh\" },\n        \"content\": \"原作者制作的 HKDF 命令行脚本\"\n      },\n      \"，你也可以自己试试：\"\n    ]\n  },\n  {\n    \"Tag\": \"CodeSample\",\n    \"props\": {\n      \"code\": \"$ handshake_hash=fa6800169a6baac19159524fa7b9721b41be3c9db6f3f93fa5ff7e3db3ece204d2b456c51046e40ec5312c55a86126f5\\n$ handshake_secret=bdbbe8757494bef20de932598294ea65b5e6bf6dc5c02a960a2de2eaa9b07c929078d2caa0936231c38d1725f179d299\\n$ zero_key=000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\\n$ empty_hash=$(openssl sha384 < /dev/null | sed -e 's/.* //')\\n$ derived_secret=$(./hkdf-384 expandlabel $handshake_secret \\\"derived\\\" $empty_hash 48)\\n$ master_secret=$(./hkdf-384 extract $derived_secret $zero_key)\\n$ csecret=$(./hkdf-384 expandlabel $master_secret \\\"c ap traffic\\\" $handshake_hash 48)\\n$ ssecret=$(./hkdf-384 expandlabel $master_secret \\\"s ap traffic\\\" $handshake_hash 48)\\n$ client_application_key=$(./hkdf-384 expandlabel $csecret \\\"key\\\" \\\"\\\" 32)\\n$ server_application_key=$(./hkdf-384 expandlabel $ssecret \\\"key\\\" \\\"\\\" 32)\\n$ client_application_iv=$(./hkdf-384 expandlabel $csecret \\\"iv\\\" \\\"\\\" 12)\\n$ server_application_iv=$(./hkdf-384 expandlabel $ssecret \\\"iv\\\" \\\"\\\" 12)\\n$ echo skey: $server_application_key\\n$ echo siv: $server_application_iv\\n$ echo ckey: $client_application_key\\n$ echo civ: $client_application_iv\\n\\nskey: 01f78623f17e3edcc09e944027ba3218d57c8e0db93cd3ac419309274700ac27\\nsiv: 196a750b0c5049c0cc51a541\\nckey: de2f4c7672723a692319873e5c227606691a32d1c59d8b9f51dbb9352e9ca9cc\\nciv: bb007956f474b25de902432f\"\n    }\n  },\n  \"由此我们可以得到以下密钥以及向量：\",\n  {\n    \"Tag\": \"ul\",\n    \"children\": [\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"客户端会话密钥: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"de2f4c7672723a692319873e5c227606691a32d1c59d8b9f51dbb9352e9ca9cc\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"客户端会话向量: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"bb007956f474b25de902432f\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"服务器端会话密钥: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"01f78623f17e3edcc09e944027ba3218d57c8e0db93cd3ac419309274700ac27\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"服务器端会话向量: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"196a750b0c5049c0cc51a541\"\n          }\n        ]\n      }\n    ]\n  }\n]\n"
  },
  {
    "path": "src/TLS13/serverCertVerify.json",
    "content": "[\n  \"服务器提供证书验证相关的数据，用以验证服务器密钥交换生成过程中产生的短暂公钥与证书私钥的所有权是否一致。\",\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"握手消息头\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"0f 00 01 04\"\n          },\n          [\n            \"每个握手消息都以一个 type 和一个 len 开始。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"0f - 握手消息类型 0x0f (certificate verify)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 01 04 - 紧接着的握手消息数据的长度 0x104 (260) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"签名\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"08 04 01 00 5c bb 24 c0 40 93 32 da a9 20 bb ab bd b9 bd 50 17 0b e4 9c fb e0 a4 10 7f ca 6f fb 10 68 e6 5f 96 9e 6d e7 d4 f9 e5 60 38 d6 7c 69 c0 31 40 3a 7a 7c 0b cc 86 83 e6 57 21 a0 c7 2c c6 63 40 19 ad 1d 3a d2 65 a8 12 61 5b a3 63 80 37 20 84 f5 da ec 7e 63 d3 f4 93 3f 27 22 74 19 a6 11 03 46 44 dc db c7 be 3e 74 ff ac 47 3f aa ad de 8c 2f c6 5f 32 65 77 3e 7e 62 de 33 86 1f a7 05 d1 9c 50 6e 89 6c 8d 82 f5 bc f3 5f ec e2 59 b7 15 38 11 5e 9c 8c fb a6 2e 49 bb 84 74 f5 85 87 b1 1b 8a e3 17 c6 33 e9 c7 6c 79 1d 46 62 84 ad 9c 4f f7 35 a6 d2 e9 63 b5 9b bc a4 40 a3 07 09 1a 1b 4e 46 bc c7 a2 f9 fb 2f 1c 89 8e cb 19 91 8b e4 12 1d 7e 8e d0 4c d5 0c 9a 59 e9 87 98 01 07 bb bf 29 9c 23 2e 7f db e1 0a 4c fd ae 5c 89 1c 96 af df f9 4b 54 cc d2 bc 19 d3 cd aa 66 44 85 9c\"\n          },\n          [\n            \"由于服务器会为每个会话都生成短暂的密钥(在 TLS 1.2 中是可选的，在 TLS 1.3 中是必须的)，所以和 TLS 之前的版本不同，密钥与证书不会有内在的联系。\",\n            \"为了证明服务器拥有服务器证书(在这个 TLS 会话中的有效性)，它需要使用证书的私钥对握手信息的哈希进行签名。而客户端可以通过使用证书的公钥来证明该签名的有效性。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"08 04 - 表明签名算法为 RSA-PSS-RSAE-SHA256\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"01 00 - 表明签名算法长度为 0x100(256) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"2c 76 3d ... 4f 9d 65 - 签名\"\n                }\n              ]\n            },\n            \"签名过程不能在命令行中逐字逐句地复制，因为签名工具在签名中引入了随机或变化的数据。\",\n            {\n              \"children\": [\n                \"相反，我们可以像客户端那样，在命令行使用\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://tls13.xargs.org/files/server.crt\"\n                  },\n                  \"content\": \"服务器的证书\"\n                },\n                \"提供的公钥来验证签名：\"\n              ]\n            },\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### find the hash of the conversation to this point, excluding\\n### 5-byte record headers or 1-byte wrapped record trailers\\n$ handshake_hash=$((\\n   tail -c +6 clienthello;\\n   tail -c +6 serverhello;\\n   perl -pe 's/.$// if eof' serverextensions;\\n   perl -pe 's/.$// if eof' servercert) | openssl sha384)\\n\\n### build the data that was signed:\\n### 1. add 64 space characters\\n$ echo -n '                                ' > /tmp/tosign\\n$ echo -n '                                ' >> /tmp/tosign\\n### 2. add this fixed string\\n$ echo -n 'TLS 1.3, server CertificateVerify' >> /tmp/tosign\\n### 3. add a single null character\\n$ echo -en '\\\\0' >> /tmp/tosign\\n### 4. add hash of handshake to this point\\n$ echo $handshake_hash | xxd -r -p >> /tmp/tosign\\n\\n### copy the signature that we want to verify\\n$ echo \\\"5c bb 24 c0 40 93 32 da a9 20 bb ab bd b9 bd 50 17 0b e4 9c\\n  fb e0 a4 10 7f ca 6f fb 10 68 e6 5f 96 9e 6d e7 d4 f9 e5 60 38 d6\\n  7c 69 c0 31 40 3a 7a 7c 0b cc 86 83 e6 57 21 a0 c7 2c c6 63 40 19\\n  ad 1d 3a d2 65 a8 12 61 5b a3 63 80 37 20 84 f5 da ec 7e 63 d3 f4\\n  93 3f 27 22 74 19 a6 11 03 46 44 dc db c7 be 3e 74 ff ac 47 3f aa\\n  ad de 8c 2f c6 5f 32 65 77 3e 7e 62 de 33 86 1f a7 05 d1 9c 50 6e\\n  89 6c 8d 82 f5 bc f3 5f ec e2 59 b7 15 38 11 5e 9c 8c fb a6 2e 49\\n  bb 84 74 f5 85 87 b1 1b 8a e3 17 c6 33 e9 c7 6c 79 1d 46 62 84 ad\\n  9c 4f f7 35 a6 d2 e9 63 b5 9b bc a4 40 a3 07 09 1a 1b 4e 46 bc c7\\n  a2 f9 fb 2f 1c 89 8e cb 19 91 8b e4 12 1d 7e 8e d0 4c d5 0c 9a 59\\n  e9 87 98 01 07 bb bf 29 9c 23 2e 7f db e1 0a 4c fd ae 5c 89 1c 96\\n  af df f9 4b 54 cc d2 bc 19 d3 cd aa 66 44 85 9c\\\" | xxd -r -p > /tmp/sig\\n\\n### extract the public key from the certificate\\n$ openssl x509 -pubkey -noout -in server.crt > server.pub\\n\\n### verify the signature\\n$ cat /tmp/tosign | openssl dgst -verify server.pub -sha256 \\\\\\n    -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:-1 -signature /tmp/sig\\n\\nVerified OK\"\n              }\n            }\n          ]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/TLS13/serverCertificate.json",
    "content": "[\n  \"服务器会发送一个或多个证书：\",\n  {\n    \"Tag\": \"ul\",\n    \"children\": [\n      {\n        \"Tag\": \"li\",\n        \"content\": \"该主机的证书，包含主机名、公钥和第三方的签名(签名证明该证书主机名的所有者持有该证书的私钥)。\"\n      },\n      {\n        \"Tag\": \"li\",\n        \"content\": \"其他证书的可选列表，从主机证书一直到预先安装在客户端上的可信证书。其中每一个都对前一个证书进行签名，形成一个信任链。\"\n      }\n    ]\n  },\n  {\n    \"children\": [\n      \"为了让这个例子不至于太过于庞大，我们只发送一个主机证书。证书以一种叫做 \",\n      {\n        \"Tag\": \"a\",\n        \"props\": {\n          \"href\": \"https://www.cnblogs.com/flydean/p/16541577.html\"\n        },\n        \"content\": \"DER\"\n      },\n      \" 的二进制格式书写。\"\n    ]\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"TLS 握手记录头\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"0b 00 03 2e\"\n          },\n          [\n            \"每个握手消息都以一个 type 和一个 len 开始。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"0b - 握手消息类型 0x0b (certificate)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 03 2e - 紧接着的握手消息数据的长度 0x32E (814) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"请求上下文\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00\"\n          },\n          [\n            \"数据为空，因为该证书不是响应证书请求而发送。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 - 没有数据\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"所有证书长度\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 03 2a\"\n          },\n          [\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 03 2a - 紧接着的证书的长度 0x32A (810) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"第一个证书长度\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 03 25\"\n          },\n          [\n            \"此时同时也是唯一一个\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 03 25 - 紧接着的证书的长度 0x325 (805) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"证书\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"30 82 03 21 30 82 02 09 a0 03 02 01 02 02 08 15 5a 92 ad c2 04 8f 90 30 0d 06 09 2a 86 48 86 f7 0d 01 01 0b 05 00 30 22 31 0b 30 09 06 03 55 04 06 13 02 55 53 31 13 30 11 06 03 55 04 0a 13 0a 45 78 61 6d 70 6c 65 20 43 41 30 1e 17 0d 31 38 31 30 30 35 30 31 33 38 31 37 5a 17 0d 31 39 31 30 30 35 30 31 33 38 31 37 5a 30 2b 31 0b 30 09 06 03 55 04 06 13 02 55 53 31 1c 30 1a 06 03 55 04 03 13 13 65 78 61 6d 70 6c 65 2e 75 6c 66 68 65 69 6d 2e 6e 65 74 30 82 01 22 30 0d 06 09 2a 86 48 86 f7 0d 01 01 01 05 00 03 82 01 0f 00 30 82 01 0a 02 82 01 01 00 c4 80 36 06 ba e7 47 6b 08 94 04 ec a7 b6 91 04 3f f7 92 bc 19 ee fb 7d 74 d7 a8 0d 00 1e 7b 4b 3a 4a e6 0f e8 c0 71 fc 73 e7 02 4c 0d bc f4 bd d1 1d 39 6b ba 70 46 4a 13 e9 4a f8 3d f3 e1 09 59 54 7b c9 55 fb 41 2d a3 76 52 11 e1 f3 dc 77 6c aa 53 37 6e ca 3a ec be c3 aa b7 3b 31 d5 6c b6 52 9c 80 98 bc c9 e0 28 18 e2 0b f7 f8 a0 3a fd 17 04 50 9e ce 79 bd 9f 39 f1 ea 69 ec 47 97 2e 83 0f b5 ca 95 de 95 a1 e6 04 22 d5 ee be 52 79 54 a1 e7 bf 8a 86 f6 46 6d 0d 9f 16 95 1a 4c f7 a0 46 92 59 5c 13 52 f2 54 9e 5a fb 4e bf d7 7a 37 95 01 44 e4 c0 26 87 4c 65 3e 40 7d 7d 23 07 44 01 f4 84 ff d0 8f 7a 1f a0 52 10 d1 f4 f0 d5 ce 79 70 29 32 e2 ca be 70 1f df ad 6b 4b b7 11 01 f4 4b ad 66 6a 11 13 0f e2 ee 82 9e 4d 02 9d c9 1c dd 67 16 db b9 06 18 86 ed c1 ba 94 21 02 03 01 00 01 a3 52 30 50 30 0e 06 03 55 1d 0f 01 01 ff 04 04 03 02 05 a0 30 1d 06 03 55 1d 25 04 16 30 14 06 08 2b 06 01 05 05 07 03 02 06 08 2b 06 01 05 05 07 03 01 30 1f 06 03 55 1d 23 04 18 30 16 80 14 89 4f de 5b cc 69 e2 52 cf 3e a3 00 df b1 97 b8 1d e1 c1 46 30 0d 06 09 2a 86 48 86 f7 0d 01 01 0b 05 00 03 82 01 01 00 59 16 45 a6 9a 2e 37 79 e4 f6 dd 27 1a ba 1c 0b fd 6c d7 55 99 b5 e7 c3 6e 53 3e ff 36 59 08 43 24 c9 e7 a5 04 07 9d 39 e0 d4 29 87 ff e3 eb dd 09 c1 cf 1d 91 44 55 87 0b 57 1d d1 9b df 1d 24 f8 bb 9a 11 fe 80 fd 59 2b a0 39 8c de 11 e2 65 1e 61 8c e5 98 fa 96 e5 37 2e ef 3d 24 8a fd e1 74 63 eb bf ab b8 e4 d1 ab 50 2a 54 ec 00 64 e9 2f 78 19 66 0d 3f 27 cf 20 9e 66 7f ce 5a e2 e4 ac 99 c7 c9 38 18 f8 b2 51 07 22 df ed 97 f3 2e 3e 93 49 d4 c6 6c 9e a6 39 6d 74 44 62 a0 6b 42 c6 d5 ba 68 8e ac 3a 01 7b dd fc 8e 2c fc ad 27 cb 69 d3 cc dc a2 80 41 44 65 d3 ae 34 8c e0 f3 4a b2 fb 9c 61 83 71 31 2b 19 10 41 64 1c 23 7f 11 a5 d6 5c 84 4f 04 04 84 99 38 71 2b 95 9e d6 85 bc 5c 5d d6 45 ed 19 90 94 73 40 29 26 dc b4 0e 34 69 a1 59 41 e8 e2 cc a8 4b b6 08 46 36 a0\"\n          },\n          [\n            {\n              \"children\": [\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://tls13.xargs.org/files/server.crt\"\n                  },\n                  \"content\": \"该证书\"\n                },\n                \"采用 ASN.1 DER 编码。可以在命令行中使用以下命令转换为二进制数据：\"\n              ]\n            },\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"$ openssl x509 -outform der < server.crt | hexdump\\n\\n0000000 30 82 03 21 30 82 02 09 a0 03 02 01 02 02 08 15\\n0000010 5a 92 ad c2 04 8f 90 30 0d 06 09 2a 86 48 86 f7\\n... snip ...\"\n              }\n            }\n          ]\n        ],\n        [\n          \"证书的扩展\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 00\"\n          },\n          [\n            \"服务器可以提供证书需要的扩展信息。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 - 没有扩展信息\"\n                }\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/TLS13/serverChangeCipherSpec.json",
    "content": "[\n  \"这条记录是用于旧版本 TLS 的。TLS 1.3 虽然不会产生效果，但是在开启\\\"中间件兼容模式\\\"(middlebox compatibility mode)时，该记录仍然会被发送，以帮助将 TLS 1.3 的会话伪装成 TLS 1.2 的会话。\",\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"记录头\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"14 03 03 00 01\"\n          },\n          [\n            \"TLS 会话被分解成 \\\"记录\\\"(record) 的形式发送和接收。记录是具有类型、协议版本和长度的数据块。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"14 - 表示 TLS 记录类型 0x14(20, ChangeCipherSpec)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"03 03 - 协议版本 (3.3, 即 TLS 1.2)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 01 - 紧接着的数组载荷长度 0x01(1) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"数据载荷\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"01\"\n          },\n          []\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/TLS13/serverEncryptedExtensions.json",
    "content": "[\n  \"任何不需要协商其他加密密钥的扩展都会列在这里，以便对窃听者和中间件隐藏它们。\",\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"TLS 握手记录头\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"08 00 00 02\"\n          },\n          [\n            \"每个 TLS 握手消息都以一个 type 和一个 len 开始。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"08 - 握手消息类型 0x08 (encrypted extensions)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 02 - 紧接着的握手消息数据的长度 0x02(2) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"扩展的长度\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 00\"\n          },\n          [\n            \"服务器向客户端返回的扩展有序列表的长度。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 - 扩展列表的长度为 0x00(0) 字节\"\n                }\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/TLS13/serverHandshakeFinished.json",
    "content": "[\n  \"为了验证握手成功且没有被篡改过，服务器会创建一些验证数据给客户端确认。验证数据是基于所有握手信息的哈希值计算得到。\",\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"握手消息头\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"14 00 00 30\"\n          },\n          [\n            \"每个握手消息都以一个 type 和一个 len 开始。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"14 - 握手消息类型 0x14 (finished)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 30 - 紧接着的握手消息数据的长度 0x30 (48) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"验证数据\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"7e 30 ee cc b6 b2 3b e6 c6 ca 36 39 92 e8 42 da 87 7e e6 47 15 ae 7f c0 cf 87 f9 e5 03 21 82 b5 bb 48 d1 e3 3f 99 79 05 5a 16 0c 8d bb b1 56 9c\"\n          },\n          [\n            \"使用 \\\"服务器端生成握手密钥\\\" 步骤中的服务器端握手时流量密文和在这之前的每个握手记录(ClientHello 到 证书验证数据)的 SHA256 哈希值生成。\",\n            {\n              \"Tag\": \"pre\",\n              \"children\": [\n                {\n                  \"Tag\": \"code\",\n                  \"props\": { \"className\": \"longboi\" },\n                  \"content\": \"finished_key = HKDF-Expand-Label(key: server_secret, label: \\\"finished\\\", ctx: \\\"\\\", len: 48)\\nfinished_hash = SHA384(Client Hello ... Server Cert Verify)\\nverify_data = HMAC-SHA384(key: finished_key, msg: finished_hash)\"\n                }\n              ]\n            },\n            {\n              \"children\": [\n                \"在命令行中使用\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://tls13.xargs.org/files/hkdf-384.sh\"\n                  },\n                  \"content\": \"原作者制作的 HKDF 命令行脚本\"\n                },\n                \"，你也可以自己试试：\"\n              ]\n            },\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### find the hash of the conversation to this point, excluding\\n### 5-byte record headers or 1-byte wrapped record trailers\\n$ fin_hash=$((\\n    tail -c +6 clienthello;\\n    tail -c +6 serverhello;\\n    perl -pe 's/.$// if eof' serverextensions;\\n    perl -pe 's/.$// if eof' servercert;\\n    perl -pe 's/.$// if eof' servercertverify) | openssl sha384)\\n$ sht_secret=23323da031634b241dd37d61032b62a4f450584d1f7f47983ba2f7cc0cdcc39a68f481f2b019f9403a3051908a5d1622\\n$ fin_key=$(./hkdf-384 expandlabel $sht_secret \\\"finished\\\" \\\"\\\" 48)\\n$ echo $fin_hash | xxd -r -p \\\\\\n    | openssl dgst -sha384 -mac HMAC -macopt hexkey:$fin_key\\n\\n7e30eeccb6b23be6c6ca363992e842da877ee64715ae7fc0cf87f9e5032182b5bb48d1e33f9979055a160c8dbbb1569c\"\n              }\n            }\n          ]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/TLS13/serverHandshakeKeysCalc.json",
    "content": "[\n  \"服务器现在拥有了用于计算握手时的加密密钥的所有信息。在这个计算中，服务器使用了以下信息：\",\n  {\n    \"Tag\": \"ul\",\n    \"children\": [\n      {\n        \"Tag\": \"li\",\n        \"content\": \"客户端公钥(提取自 ClientHello)\"\n      },\n      {\n        \"Tag\": \"li\",\n        \"content\": \"服务器端私钥(之前准备密钥交换时生成)\"\n      },\n      {\n        \"Tag\": \"li\",\n        \"content\": \"ClientHello 和 ServerHello 的 SHA256 哈希值\"\n      }\n    ]\n  },\n  \"首先，服务器需要找到共享的密文(shared secret)，即密钥交换步骤的最终值。服务器通过使用 curve25519 算法将客户端的公钥乘以服务器的私钥，即可发现 32 字节的最终值是：\",\n  {\n    \"Tag\": \"pre\",\n    \"props\": {\n      \"className\": \"ind2\"\n    },\n    \"children\": [\n      {\n        \"Tag\": \"code\",\n        \"props\": { \"className\": \"longboi\" },\n        \"content\": \"df4a291baa1eb7cfa6934b29b474baad2697e29f1f920dcc77c8a0a088447624\"\n      }\n    ]\n  },\n  {\n    \"children\": [\n      \"你可以使用\",\n      {\n        \"Tag\": \"a\",\n        \"props\": { \"href\": \"https://quic.xargs.org/files/curve25519-mult.c\" },\n        \"content\": \"原作者的脚本\"\n      },\n      \"快速验证结果：\"\n    ]\n  },\n  {\n    \"Tag\": \"CodeSample\",\n    \"props\": {\n      \"code\": \"$ cc -o curve25519-mult curve25519-mult.c\\n$ ./curve25519-mult server-ephemeral-private.key \\\\\\n                    client-ephemeral-public.key | hexdump\\n\\n0000000 df 4a 29 1b aa 1e b7 cf a6 93 4b 29 b4 74 ba ad\\n0000010 26 97 e2 9f 1f 92 0d cc 77 c8 a0 a0 88 44 76 24\"\n    }\n  },\n  \"然后，服务器计算到此为止的所有握手信息(ClientHello 和 ServerHello)的 SHA256 哈希值(不包括 5 字节的 记录头)。这个 \\\"hello_hash\\\" 是\",\n  {\n    \"Tag\": \"pre\",\n    \"props\": {\n      \"className\": \"ind2\"\n    },\n    \"children\": [\n      {\n        \"Tag\": \"code\",\n        \"props\": { \"className\": \"longboi\" },\n        \"content\": \"e05f64fcd082bdb0dce473adf669c2769f257a1c75a51b7887468b5e0e7a7de4f4d34555112077f16e079019d5a845bd\"\n      }\n    ]\n  },\n  {\n    \"Tag\": \"CodeSample\",\n    \"props\": {\n      \"code\": \"$ (tail -c +6 clienthello; tail -c +6 serverhello) | openssl sha384\\ne05f64fcd082bdb0dce473adf669c2769f257a1c75a51b7887468b5e0e7a7de4f4d34555112077f16e079019d5a845bd\"\n    }\n  },\n  \"然后，我们将哈希值和共享的签名信息进行一些密钥派生操作(key derivation operations)，以防止可能的攻击：\",\n  {\n    \"Tag\": \"pre\",\n    \"children\": [\n      {\n        \"Tag\": \"code\",\n        \"props\": { \"className\": \"longboi\" },\n        \"content\": \"early_secret = HKDF-Extract(salt: 00, key: 00...)\\nempty_hash = SHA384(\\\"\\\")\\nderived_secret = HKDF-Expand-Label(key: early_secret, label: \\\"derived\\\", ctx: empty_hash, len: 48)\\nhandshake_secret = HKDF-Extract(salt: derived_secret, key: shared_secret)\\nclient_secret = HKDF-Expand-Label(key: handshake_secret, label: \\\"c hs traffic\\\", ctx: hello_hash, len: 48)\\nserver_secret = HKDF-Expand-Label(key: handshake_secret, label: \\\"s hs traffic\\\", ctx: hello_hash, len: 48)\\nclient_handshake_key = HKDF-Expand-Label(key: client_secret, label: \\\"key\\\", ctx: \\\"\\\", len: 32)\\nserver_handshake_key = HKDF-Expand-Label(key: server_secret, label: \\\"key\\\", ctx: \\\"\\\", len: 32)\\nclient_handshake_iv = HKDF-Expand-Label(key: client_secret, label: \\\"iv\\\", ctx: \\\"\\\", len: 12)\\nserver_handshake_iv = HKDF-Expand-Label(key: server_secret, label: \\\"iv\\\", ctx: \\\"\\\", len: 12)\"\n      }\n    ]\n  },\n  \"这引出了两个密码学的新概念：\",\n  {\n    \"Tag\": \"ul\",\n    \"children\": [\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          {\n            \"Tag\": \"a\",\n            \"props\": {\n              \"href\": \"https://hxndg.github.io/2020/08/27/HKDF-Extract%E4%B8%8EHKDF-Expand/\"\n            },\n            \"content\": \"HKDF-Extract\"\n          },\n          \" - 给定一个盐值和一些字节的密钥材料，派生出一个 32bytes(256bits) 固定长度的新密钥材料，输入的密钥材料的熵均匀地分布在输出中。\"\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          {\n            \"Tag\": \"a\",\n            \"props\": {\n              \"href\": \"https://hxndg.github.io/2020/08/27/HKDF-Extract%E4%B8%8EHKDF-Expand/\"\n            },\n            \"content\": \"HKDF-Expand-Label\"\n          },\n          \" - 给出伪随机密钥、标签和上下文数据的输入，拓展为一个要求长度的密钥。\"\n        ]\n      }\n    ]\n  },\n  {\n    \"children\": [\n      \"在命令行中使用\",\n      {\n        \"Tag\": \"a\",\n        \"props\": { \"href\": \"https://quic.xargs.org/files/hkdf.sh\" },\n        \"content\": \"原作者制作的 HKDF 命令行脚本\"\n      },\n      \"，你也可以自己试试：\"\n    ]\n  },\n  {\n    \"Tag\": \"CodeSample\",\n    \"props\": {\n      \"code\": \"$ hello_hash=e05f64fcd082bdb0dce473adf669c2769f257a1c75a51b7887468b5e0e7a7de4f4d34555112077f16e079019d5a845bd\\n$ shared_secret=df4a291baa1eb7cfa6934b29b474baad2697e29f1f920dcc77c8a0a088447624\\n$ zero_key=000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\\n$ early_secret=$(./hkdf-384 extract 00 $zero_key)\\n$ empty_hash=$(openssl sha384 < /dev/null | sed -e 's/.* //')\\n$ derived_secret=$(./hkdf-384 expandlabel $early_secret \\\"derived\\\" $empty_hash 48)\\n$ handshake_secret=$(./hkdf-384 extract $derived_secret $shared_secret)\\n$ csecret=$(./hkdf-384 expandlabel $handshake_secret \\\"c hs traffic\\\" $hello_hash 48)\\n$ ssecret=$(./hkdf-384 expandlabel $handshake_secret \\\"s hs traffic\\\" $hello_hash 48)\\n$ client_handshake_key=$(./hkdf-384 expandlabel $csecret \\\"key\\\" \\\"\\\" 32)\\n$ server_handshake_key=$(./hkdf-384 expandlabel $ssecret \\\"key\\\" \\\"\\\" 32)\\n$ client_handshake_iv=$(./hkdf-384 expandlabel $csecret \\\"iv\\\" \\\"\\\" 12)\\n$ server_handshake_iv=$(./hkdf-384 expandlabel $ssecret \\\"iv\\\" \\\"\\\" 12)\\n$ echo hssec: $handshake_secret\\n$ echo ssec: $ssecret\\n$ echo csec: $csecret\\n$ echo skey: $server_handshake_key\\n$ echo siv: $server_handshake_iv\\n$ echo ckey: $client_handshake_key\\n$ echo civ: $client_handshake_iv\\n\\nhssec: bdbbe8757494bef20de932598294ea65b5e6bf6dc5c02a960a2de2eaa9b07c929078d2caa0936231c38d1725f179d299\\nssec: 23323da031634b241dd37d61032b62a4f450584d1f7f47983ba2f7cc0cdcc39a68f481f2b019f9403a3051908a5d1622\\ncsec: db89d2d6df0e84fed74a2288f8fd4d0959f790ff23946cdf4c26d85e51bebd42ae184501972f8d30c4a3e4a3693d0ef0\\nskey: 9f13575ce3f8cfc1df64a77ceaffe89700b492ad31b4fab01c4792be1b266b7f\\nsiv: 9563bc8b590f671f488d2da3\\nckey: 1135b4826a9a70257e5a391ad93093dfd7c4214812f493b3e3daae1eb2b1ac69\\nciv: 4256d2e0e88babdd05eb2f27\"\n    }\n  },\n  \"由此我们可以得到以下密钥以及向量：\",\n  {\n    \"Tag\": \"ul\",\n    \"children\": [\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"客户端握手时密钥: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"1135b4826a9a70257e5a391ad93093dfd7c4214812f493b3e3daae1eb2b1ac69\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"客户端握手时向量: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"4256d2e0e88babdd05eb2f27\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"客户端握手时流量密文(handshake traffic secret): \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"84b3c21cacaf9f54c885e9a506459079\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"握手时密文(handshake secret)：\",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"bdbbe8757494bef20de932598294ea65b5e6bf6dc5c02a960a2de2eaa9b07c929078d2caa0936231c38d1725f179d299\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"服务器端握手时密钥: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"9f13575ce3f8cfc1df64a77ceaffe89700b492ad31b4fab01c4792be1b266b7f\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"服务器端握手时向量: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"9563bc8b590f671f488d2da3\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"服务器端握手时流量密文: \",\n          {\n            \"Tag\": \"code\",\n            \"content\": \"23323da031634b241dd37d61032b62a4f450584d1f7f47983ba2f7cc0cdcc39a68f481f2b019f9403a3051908a5d1622\"\n          }\n        ]\n      }\n    ]\n  },\n  {\n    \"children\": [\n      \"握手时流量密文(handshake traffic secret)之后用于生成握手完成时的验证数据。而握手时密文(handshake secret)之后用于会话密钥的计算。\",\n      \"详情见 \",\n      {\n        \"Tag\": \"a\",\n        \"props\": {\n          \"href\": \"https://www.ietf.org/rfc/rfc8446.html#section-7.1\"\n        },\n        \"content\": \"TLS 1.3 RFC 的 7.1 节\"\n      }\n    ]\n  }\n]\n"
  },
  {
    "path": "src/TLS13/serverHello.json",
    "content": "[\n  \"服务器回复 \\\"ServerHello\\\"。服务器提供的信息包括以下内容：\",\n  {\n    \"Tag\": \"ul\",\n    \"children\": [\n      {\n        \"Tag\": \"li\",\n        \"content\": \"服务器端随机数(在随后的握手时使用)\"\n      },\n      {\n        \"Tag\": \"li\",\n        \"content\": \"服务器端选择的加解密算法\"\n      },\n      {\n        \"Tag\": \"li\",\n        \"content\": \"服务器端密钥交换的公钥\"\n      },\n      {\n        \"Tag\": \"li\",\n        \"content\": \"服务器协商的具体协议的版本\"\n      }\n    ]\n  },\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"记录头\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"16 03 03 00 7a\"\n          },\n          [\n            \"TLS 会话被分解成 \\\"记录\\\"(record) 的形式发送和接收。记录是具有类型、协议版本和长度的数据块。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"16 - 表示 TLS 记录类型 0x16(22, Handshake)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"03 03 - 协议版本 (3.3, 即 TLS 1.2)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 7a - 紧接着的数组载荷长度 0x7A(122) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"TLS 握手记录头\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"02 00 00 76\"\n          },\n          [\n            \"每个 TLS 握手消息都以一个 type 和一个 len 开始。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"02 - 握手消息类型 0x02 (ServerHello)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 76 - 紧接着的握手消息数据的长度 0x76 (118) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"服务器端 TLS 版本号\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"03 03\"\n          },\n          [\n            \"协议版本为 \\\"3,3\\\"(指 TLS 1.2)。因为被广泛部署的中间件(middleboxes)并不允许他们不承认的协议版本，所以所有的 TLS 1.3 会话在这个字段中都以 TLS 1.2 版本表示。也因为如此，这个字段不再被使用，而是使用下面的 \\\"支持的版本\\\" 扩展来进行版本协商。\",\n            \"而不寻常的版本号(\\\"3,3\\\" 代表 TLS 1.2)是由于 TLS 1.0 是 SSL 3.0 协议的一个小修订。因此，TLS 1.0 用 \\\"3,1\\\" 表示，TLS 1.1 用 \\\"3,2\\\"，以此类推。\"\n          ]\n        ],\n        [\n          \"服务器端随机数\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f\"\n          },\n          [\n            \"服务器端提供的 32 字节的随机数。这个数将在之后的会话中使用。在本文的例子中，我们暂时将一个方便记忆的字符串当作随机数。\"\n          ]\n        ],\n        [\n          \"会话 ID\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"20 e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff\"\n          },\n          [\n            \"在之前的 TLS 版本中，客户端可以提供一个之前会话的会话 ID。ID 可以让服务器端和客户端跳过协商新密钥的时间并减少其他潜在的成本。\",\n            \"而在 TLS 1.3 中，这种 \\\"会话恢复\\\" 是通过更灵活的 PSK(预共享密钥) 机制完成的，所以这个字段不再被用于该目的。相反，这个字段的非空值被用来触发\\\"中间件兼容模式\\\"，这有助于将 TLS 1.3 会话伪装成正在尝试恢复的 TLS 1.2 会话。\",\n            \"此时，服务器直接复制客户端的\\\"ID\\\"来填充这个字段。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"20 - 0x20(32) 字节的会话 ID\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"e0 e1 ... fe ff - 假的会话 ID\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"所选择的加解密算法\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"13 02\"\n          },\n          [\n            \"服务器从客户端给出的选项列表中选择的加解密算法 0x1302(TLS_AES_256_GCM_SHA384)。\"\n          ]\n        ],\n        [\n          \"压缩算法\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00\"\n          },\n          [\"服务器从客户端给出的选项列表中选择的压缩算法 0x00(null)。\"]\n        ],\n        [\n          \"扩展的长度\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 2e\"\n          },\n          [\n            \"服务器向客户端返回的扩展有序列表的长度。因为服务器被禁止回复 ClientHello 消息中不存在的扩展，因此服务器知道客户端将理解并支持列出的所有扩展。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 2e - 扩展列表的长度为 0x2E(46) 字节\"\n                }\n              ]\n            },\n            \"每个扩展将以两个字节开始，表明它是哪个扩展。然后是两个字节的内容长度字段，最后是扩展的具体内容。\"\n          ]\n        ],\n        [\n          \"扩展 - 支持的版本\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 2b 00 02 03 04\"\n          },\n          [\n            \"服务器端表明其支持 TLS 1.3。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 2b - 表示这是 \\\"支持的版本\\\" 扩展\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 02 - 扩展信息长度为 0x02(2) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"03 04 - 代表 TLS 1.3\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"扩展 - 算法公钥列表(Key Share)\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 33 00 24 00 1d 00 20 9f d7 ad 6d cf f4 29 8d d3 f9 6d 5b 1b 2a f9 10 a0 53 5b 14 88 d7 f8 fa bb 34 9a 98 28 80 b6 15\"\n          },\n          [\n            \"服务器使用和客户端发送公钥时相同的算法发送一个自己的公钥。一旦这个被发送，加密密钥就可以被计算出来。其余的握手则将被加密。而不用像以前的协议版本，以透明的方式发送握手记录。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 33 - 表示这是 \\\"算法公钥列表\\\" 扩展\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 24 - 算法公钥列表长度为 0x24(36) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 1d - 代表 x25519 算法(例子中为通过 curve25519 算法进行密钥交换)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 20 - 公钥长度为 0x20(32) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"9f d7 ... b6 15 - \\\"服务器端准备密钥交换\\\" 步骤中生成的公钥\"\n                }\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/TLS13/serverKeyExchangeGeneration.json",
    "content": "[\n  \"服务器端需要生成一个用于密钥交换的自己的“私钥/公钥”对。密钥交换(Key exchange)是一种技术，双方可以在同一数字上达成一致，而窃听者却无法知道这个数字是什么。\",\n  {\n    \"Tag\": \"p\",\n    \"children\": [\n      \"学习 TLS 并不需要深入了解，但你可以从\",\n      {\n        \"Tag\": \"a\",\n        \"props\": { \"href\": \"https://cangsdarm.github.io/illustrate/x25519\" },\n        \"content\": \"X25519 密钥交换算法\"\n      },\n      \"获取涉及到的密钥交换算法的具体解释。\"\n    ]\n  },\n  [\n    \"**私钥**是 0 到 \",\n    {\n      \"Tag\": \"Math\",\n      \"content\": \"2^256-1\"\n    },\n    \" 之间的一个随机整数(32bytes, 256bits)\",\n    \"。为方便后续解释，假设我们生成的私钥是：\"\n  ],\n  {\n    \"Tag\": \"pre\",\n    \"props\": {\n      \"className\": \"ind2\"\n    },\n    \"children\": [\n      {\n        \"Tag\": \"code\",\n        \"props\": { \"className\": \"longboi\" },\n        \"content\": \"909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeaf\"\n      }\n    ]\n  },\n  \"**公钥**由上面提到的 X25519 密钥交换算法生成。例子中计算出的公钥应如下所示：\",\n  {\n    \"Tag\": \"pre\",\n    \"props\": {\n      \"className\": \"ind2\"\n    },\n    \"children\": [\n      {\n        \"Tag\": \"code\",\n        \"props\": { \"className\": \"longboi\" },\n        \"content\": \"9fd7ad6dcff4298dd3f96d5b1b2af910a0535b1488d7f8fabb349a982880b615\"\n      }\n    ]\n  },\n  \"公钥的计算也可以在命令行中运行以下命令得到：\",\n  {\n    \"Tag\": \"CodeSample\",\n    \"props\": {\n      \"code\": \"### requires openssl 1.1.0 or higher\\n$ openssl pkey -noout -text < server-ephemeral-private.key\\n\\nX25519 Private-Key:\\npriv:\\n    90:91:92:93:94:95:96:97:98:99:9a:9b:9c:9d:9e:\\n    9f:a0:a1:a2:a3:a4:a5:a6:a7:a8:a9:aa:ab:ac:ad:\\n    ae:af\\npub:\\n    9f:d7:ad:6d:cf:f4:29:8d:d3:f9:6d:5b:1b:2a:f9:\\n    10:a0:53:5b:14:88:d7:f8:fa:bb:34:9a:98:28:80:\\n    b6:15\"\n    }\n  }\n]\n"
  },
  {
    "path": "src/TLS13/serverNewSessionTicket1.json",
    "content": "[\n  \"服务器在协商末尾阶段会提供会话记录单，客户端可以在之后使用它以启动一个新的会话。以这种方式成功恢复连接将跳过会话启动中的大部分计算和网络延迟。\",\n  \"因为每个会话记录单都是一次性的，而服务器希望对等端可以快速打开多个连接，所以它会根据大小和速度做出决定，为每个协商的会话向客户端提供两个会话记录单。这是第一张。\",\n  {\n    \"children\": [\n      \"译注：\",\n      {\n        \"Tag\": \"a\",\n        \"props\": {\n          \"href\": \"https://halfrost.com/tls_1-3_0-rtt/\"\n        },\n        \"content\": \"在使用会话记录单进行 0-RTT 的协商时如果没有额外处理很容易发生重放攻击。\"\n      }\n    ]\n  },\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"握手消息头\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"04 00 00 d5\"\n          },\n          [\n            \"每个握手消息都以一个 type 和一个 len 开始。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"04 - 握手消息类型 0x04 (new session ticket)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 d5 - 紧接着的握手消息数据的长度 0xD5 (213) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"记录单生命周期\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 00 1c 20\"\n          },\n          [\"表明该记录单的最长可使用的时间点为 0x1C20(7200) 秒，即 2 小时。\"]\n        ],\n        [\n          \"记录单时间戳偏移(Ticket Age Add)\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 00 00 00\"\n          },\n          [\n            \"当将此记录单被使用并发送回服务器时，它必须将此毫秒数添加到记录单的生成时间戳中。这种做法可以防止攻击者将恢复的会话与生成记录单的会话关联起来(防止长时间窗口的重放攻击)。\"\n          ]\n        ],\n        [\n          \"记录单随机数(Ticket Nonce)\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"08 00 00 00 00 00 00 00 00\"\n          },\n          [\n            \"在该会话期间为每个记录单生成的唯一的随机数。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"08 - 随机数长度 0x08 (8) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 00 00 00 00 00 - 随机数本身\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"会话记录单\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 c0 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 00 49 56 44 41 54 41 49 56 44 41 54 41 00 41 45 53 cb 11 9d 4d bd 2a 21 ec c2 26 a6 09 0e e8 ca 58 df 09 03 9b 35 96 f4 de 79 98 0e a3 25 d5 14 62 5c 0c 21 c5 0f 03 26 1d c4 2c e7 c5 97 0c 4c 01 ea 33 1c ff c8 99 66 ef 54 8b e4 df 9a 8b a4 38 5b eb 86 80 fd 0b 78 df b8 e9 8e fc 8f cc d8 14 fe cd 1d 9b ce 89 ca 05 dc 28 c2 49 e5 bd 61 d0 3a 56 8f 9a 0a 46 fb fd 05 30 2d b6 b2 f7 a3 13 e3 32 67 bf 0b cb dc ec fb 04 a4 d8 2f 5a 69 45 1f 56 7a b5 19 9b b2 6c 5c f2 00 72 f0 45 03 73 02 8f e0 71 d4 f4 1d 8f 61 ae 02 4d 69 bb ae 4c\"\n          },\n          [\n            \"这是可以发送到服务器以恢复会话的会话记录单。其中的数据对服务器是有意义的。该数据可能有包含足够的信息，服务器以此可以安全地恢复连接，而无需在服务器上存储任何信息(例如在内存中)。但该信息对客户端来说没有意义或无法理解。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 c0 - 会话记录单长度 0xC0 (192) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"41 42 ... ae 4c - 会话记录单\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"记录单扩展\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 00\"\n          },\n          [\n            \"服务器可以包含扩展，以提供有关记录单的更多信息或请求客户端进一步的对协商记录单的更改。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 - 扩展长度 0x00 (0) 字节\"\n                }\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/TLS13/serverNewSessionTicket2.json",
    "content": "[\n  \"服务器在协商末尾阶段会提供会话记录单，客户端可以在之后使用它以启动一个新的会话。以这种方式成功恢复连接将跳过会话启动中的大部分计算和网络延迟。\",\n  \"因为每个会话记录单都是一次性的，而服务器希望对等端可以快速打开多个连接，所以它会根据大小和速度做出决定，为每个协商的会话向客户端提供两个会话记录单。这是第二张。\",\n  {\n    \"children\": [\n      \"译注：\",\n      {\n        \"Tag\": \"a\",\n        \"props\": {\n          \"href\": \"https://halfrost.com/tls_1-3_0-rtt/\"\n        },\n        \"content\": \"在使用会话记录单进行 0-RTT 的协商时如果没有额外处理很容易发生重放攻击。\"\n      }\n    ]\n  },\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"握手消息头\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"04 00 00 d5\"\n          },\n          [\n            \"每个握手消息都以一个 type 和一个 len 开始。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"04 - 握手消息类型 0x04 (new session ticket)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 d5 - 紧接着的握手消息数据的长度 0xD5 (213) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"记录单生命周期\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 00 1c 20\"\n          },\n          [\"表明该记录单的最长可使用的时间点为 0x1C20(7200) 秒，即 2 小时。\"]\n        ],\n        [\n          \"记录单时间戳偏移(Ticket Age Add)\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 00 00 00\"\n          },\n          [\n            \"当将此记录单被使用并发送回服务器时，它必须将此毫秒数添加到记录单的生成时间戳中。这种做法可以防止攻击者将恢复的会话与生成记录单的会话关联起来(防止长时间窗口的重放攻击)。\"\n          ]\n        ],\n        [\n          \"记录单随机数(Ticket Nonce)\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"08 00 00 00 00 00 00 00 01\"\n          },\n          [\n            \"在该会话期间为每个记录单生成的唯一的随机数。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"08 - 随机数长度 0x08 (8) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 00 00 00 00 01 - 随机数本身\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"会话记录单\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 c0 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 00 49 56 44 41 54 41 49 56 44 41 54 41 00 41 45 53 cb 11 9d 4d bd 2a 21 ec c2 26 a6 09 0e e8 ca 58 df 09 03 9b 35 96 f4 de 79 98 0e a3 25 d5 14 62 5c 0c 21 c5 0f 03 26 1d c4 2c e7 c5 97 0c 4c 01 16 06 fb 99 8a 86 c3 fa 30 e5 5e ea 91 f1 ff f3 18 fc 7b d5 88 31 bf 49 c8 8d 7b 59 05 91 a6 5c 7d e8 cf c6 77 46 8a 54 fd be c0 d8 53 be 20 21 c8 bb fc db e5 1f 5d 9a 0c 70 85 84 1a 01 e4 95 85 f6 8b 4a fe e1 d7 07 e2 cb b1 a0 b4 23 aa 7e 32 d5 60 7b d9 9d d4 db 3c 9a aa ed 43 d3 5d 26 b4 b1 c6 84 71 71 ea a0 7a 9b c8 cb f7 58 49 9a\"\n          },\n          [\n            \"这是可以发送到服务器以恢复会话的会话记录单。其中的数据对服务器是有意义的。该数据可能有包含足够的信息，服务器以此可以安全地恢复连接，而无需在服务器上存储任何信息(例如在内存中)。但该信息对客户端来说没有意义或无法理解。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 c0 - 会话记录单长度 0xC0 (192) 字节\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"41 42 ... 49 9a - 会话记录单\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"记录单扩展\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"00 00\"\n          },\n          [\n            \"服务器可以包含扩展，以提供有关记录单的更多信息或请求客户端进一步的对协商记录单的更改。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 00 - 扩展长度 0x00 (0) 字节\"\n                }\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/TLS13/wrappedRecord1.json",
    "content": "[\n  \"连接(包括握手)的数据从这时候起就能够被加密了。加密握手数据是 TLS 1.3 的新特性。\",\n  \"为了减少连接被无法识别新 TLS 协议的中间件阻断的问题，加密的 TLS 1.3 记录都需要伪装成一个会话恢复成功的 TLS 1.2 会话记录(之所以不是伪装成握手记录，因为握手记录过长会使得兼容实现变得异常复杂)。\",\n  \"包装的实际记录内容将在下面单独列出。\",\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"记录头\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"17 03 03 00 17\"\n          },\n          [\n            \"此 TLS 1.3 记录被伪装为 TLS 1.2 的\\\"会话记录\\\"。因此内容会以会话记录的规格展示。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"17 - 表示 TLS 记录类型 0x17(23, application data)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"03 03 - 协议版本 (3.3, 即 TLS 1.2)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 17 - 紧接着的数组载荷长度 0x17(23) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"加密的数据载荷\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"6b e0 2f 9d a7 c2 dc\"\n          },\n          [\"这个数据是用服务器端的握手时密钥加密的。\"]\n        ],\n        [\n          \"AEAD 鉴别标签\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"9d de f5 6f 24 68 b9 0a df a2 51 01 ab 03 44 ae\"\n          },\n          [\n            {\n              \"children\": [\n                \"这是 \",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://zhuanlan.zhihu.com/p/28566058\"\n                  },\n                  \"content\": \"AEAD 算法\"\n                },\n                \"的鉴别标签，用以确认加密数据和数据包头的完整性。它由加密算法产生，并由解密算法消耗。\"\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"data\": [\n        [\n          \"\",\n          {\n            \"props\": {\n              \"className\": \"decryption-header\"\n            },\n            \"content\": \"解密后的数据载荷\"\n          },\n          [\n            { \"Tag\": \"h4\", \"content\": \"解密\" },\n            \"数据被 \\\"服务器端生成握手密钥\\\" 步骤中产生的握手时密钥和握手时向量(IVs)加密。IVs 通过密钥和已经用密钥加密的记录长度进行异或操作生成。在例子中 IV 为 0。\",\n            \"数据包开头的 5 字节(记录头)还会作为解密过程解密成功时必须满足的认证条件。\",\n            {\n              \"children\": [\n                \"openssl 命令行工具还不支持 AEAD 算法加解密(AEAD ciphers)，你可以使用作者的命令行工具来\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://tls13.xargs.org/files/aes_256_gcm_decrypt.c\"\n                  },\n                  \"content\": \"解密\"\n                },\n                \"和\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://tls13.xargs.org/files/aes_256_gcm_encrypt.c\"\n                  },\n                  \"content\": \"加密\"\n                },\n                \"这些数据。\"\n              ]\n            },\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### from the \\\"Server Handshake Keys Calc\\\" step\\n$ key=9f13575ce3f8cfc1df64a77ceaffe89700b492ad31b4fab01c4792be1b266b7f\\n$ iv=9563bc8b590f671f488d2da3\\n### from this record\\n$ recdata=1703030017\\n$ authtag=9ddef56f2468b90adfa25101ab0344ae\\n$ recordnum=0\\n### may need to add -I and -L flags for include and lib dirs\\n$ cc -o aes_256_gcm_decrypt aes_256_gcm_decrypt.c -lssl -lcrypto\\n$ echo \\\"6b e0 2f 9d a7 c2 dc\\\" | xxd -r -p > /tmp/msg1\\n$ cat /tmp/msg1 \\\\\\n  | ./aes_256_gcm_decrypt $iv $recordnum $key $recdata $authtag \\\\\\n  | hexdump -C\\n\\n00000000  08 00 00 02 00 00 16                              |.......|\"\n              }\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"加密的数据载荷\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"08 00 00 02 00 00\"\n          },\n          [\"该载荷的解释详见下面的\\\"服务器端加密后的额外扩展\\\"部分\"]\n        ],\n        [\n          \"记录类型\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"16\"\n          },\n          [\n            \"每一个伪装的 TLS 1.3 记录的最后一个字节都需要表明其真正的记录类型\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"16 - 类型为 0x16(22, handshake record)\"\n                }\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/TLS13/wrappedRecord2.json",
    "content": "[\n  \"为了减少连接被无法识别新 TLS 协议的中间件阻断的问题，加密的 TLS 1.3 记录都需要伪装成一个会话恢复成功的 TLS 1.2 会话记录(之所以不是伪装成握手记录，因为握手记录过长会使得兼容实现变得异常复杂)。\",\n  \"包装的实际记录内容将在下面单独列出。\",\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"记录头\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"17 03 03 03 43\"\n          },\n          [\n            \"此 TLS 1.3 记录被伪装为 TLS 1.2 的\\\"会话记录\\\"。因此内容会以会话记录的规格展示。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"17 - 表示 TLS 记录类型 0x17(23, application data)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"03 03 - 协议版本 (3.3, 即 TLS 1.2)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"03 43 - 紧接着的数组载荷长度 0x343(835) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"加密的数据载荷\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"ba f0 0a 9b e5 0f 3f 23 07 e7 26 ed cb da cb e4 b1 86 16 44 9d 46 c6 20 7a f6 e9 95 3e e5 d2 41 1b a6 5d 31 fe af 4f 78 76 4f 2d 69 39 87 18 6c c0 13 29 c1 87 a5 e4 60 8e 8d 27 b3 18 e9 8d d9 47 69 f7 73 9c e6 76 83 92 ca ca 8d cc 59 7d 77 ec 0d 12 72 23 37 85 f6 e6 9d 6f 43 ef fa 8e 79 05 ed fd c4 03 7e ee 59 33 e9 90 a7 97 2f 20 69 13 a3 1e 8d 04 93 13 66 d3 d8 bc d6 a4 a4 d6 47 dd 4b d8 0b 0f f8 63 ce 35 54 83 3d 74 4c f0 e0 b9 c0 7c ae 72 6d d2 3f 99 53 df 1f 1c e3 ac eb 3b 72 30 87 1e 92 31 0c fb 2b 09 84 86 f4 35 38 f8 e8 2d 84 04 e5 c6 c2 5f 66 a6 2e be 3c 5f 26 23 26 40 e2 0a 76 91 75 ef 83 48 3c d8 1e 6c b1 6e 78 df ad 4c 1b 71 4b 04 b4 5f 6a c8 d1 06 5a d1 8c 13 45 1c 90 55 c4 7d a3 00 f9 35 36 ea 56 f5 31 98 6d 64 92 77 53 93 c4 cc b0 95 46 70 92 a0 ec 0b 43 ed 7a 06 87 cb 47 0c e3 50 91 7b 0a c3 0c 6e 5c 24 72 5a 78 c4 5f 9f 5f 29 b6 62 68 67 f6 f7 9c e0 54 27 35 47 b3 6d f0 30 bd 24 af 10 d6 32 db a5 4f c4 e8 90 bd 05 86 92 8c 02 06 ca 2e 28 e4 4e 22 7a 2d 50 63 19 59 35 df 38 da 89 36 09 2e ef 01 e8 4c ad 2e 49 d6 2e 47 0a 6c 77 45 f6 25 ec 39 e4 fc 23 32 9c 79 d1 17 28 76 80 7c 36 d7 36 ba 42 bb 69 b0 04 ff 55 f9 38 50 dc 33 c1 f9 8a bb 92 85 83 24 c7 6f f1 eb 08 5d b3 c1 fc 50 f7 4e c0 44 42 e6 22 97 3e a7 07 43 41 87 94 c3 88 14 0b b4 92 d6 29 4a 05 40 e5 a5 9c fa e6 0b a0 f1 48 99 fc a7 13 33 31 5e a0 83 a6 8e 1d 7c 1e 4c dc 2f 56 bc d6 11 96 81 a4 ad bc 1b bf 42 af d8 06 c3 cb d4 2a 07 6f 54 5d ee 4e 11 8d 0b 39 67 54 be 2b 04 2a 68 5d d4 72 7e 89 c0 38 6a 94 d3 cd 6e cb 98 20 e9 d4 9a fe ed 66 c4 7e 6f c2 43 ea be bb cb 0b 02 45 38 77 f5 ac 5d bf bd f8 db 10 52 a3 c9 94 b2 24 cd 9a aa f5 6b 02 6b b9 ef a2 e0 13 02 b3 64 01 ab 64 94 e7 01 8d 6e 5b 57 3b d3 8b ce f0 23 b1 fc 92 94 6b bc a0 20 9c a5 fa 92 6b 49 70 b1 00 91 03 64 5c b1 fc fe 55 23 11 ff 73 05 58 98 43 70 03 8f d2 cc e2 a9 1f c7 4d 6f 3e 3e a9 f8 43 ee d3 56 f6 f8 2d 35 d0 3b c2 4b 81 b5 8c eb 1a 43 ec 94 37 e6 f1 e5 0e b6 f5 55 e3 21 fd 67 c8 33 2e b1 b8 32 aa 8d 79 5a 27 d4 79 c6 e2 7d 5a 61 03 46 83 89 19 03 f6 64 21 d0 94 e1 b0 0a 9a 13 8d 86 1e 6f 78 a2 0a d3 e1 58 00 54 d2 e3 05 25 3c 71 3a 02 fe 1e 28 de ee 73 36 24 6f 6a e3 43 31 80 6b 46 b4 7b 83 3c 39 b9 d3 1c d3 00 c2 a6 ed 83 13 99 77 6d 07 f5 70 ea f0 05 9a 2c 68 a5 f3 ae 16 b6 17 40 4a f7 b7 23 1a 4d 94 27 58 fc 02 0b 3f 23 ee 8c 15 e3 60 44 cf d6 7c d6 40 99 3b 16 20 75 97 fb f3 85 ea 7a 4d 99 e8 d4 56 ff 83 d4 1f 7b 8b 4f 06 9b 02 8a 2a 63 a9 19 a7 0e 3a 10 e3 08 41\"\n          },\n          [\"这个数据是用服务器端的握手时密钥加密的。\"]\n        ],\n        [\n          \"AEAD 鉴别标签\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"58 fa a5 ba fa 30 18 6c 6b 2f 23 8e b5 30 c7 3e\"\n          },\n          [\n            {\n              \"children\": [\n                \"这是 \",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://zhuanlan.zhihu.com/p/28566058\"\n                  },\n                  \"content\": \"AEAD 算法\"\n                },\n                \"的鉴别标签，用以确认加密数据和数据包头的完整性。它由加密算法产生，并由解密算法消耗。\"\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"data\": [\n        [\n          \"\",\n          {\n            \"props\": {\n              \"className\": \"decryption-header\"\n            },\n            \"content\": \"解密后的数据载荷\"\n          },\n          [\n            { \"Tag\": \"h4\", \"content\": \"解密\" },\n            \"数据被 \\\"服务器端生成握手密钥\\\" 步骤中产生的握手时密钥和握手时向量(IVs)加密。IVs 通过密钥和已经用密钥加密的记录长度进行异或操作生成。在例子中 IV 为 1。\",\n            \"数据包开头的 5 字节(记录头)还会作为解密过程解密成功时必须满足的认证条件。\",\n            {\n              \"children\": [\n                \"openssl 命令行工具还不支持 AEAD 算法加解密(AEAD ciphers)，你可以使用作者的命令行工具来\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://tls13.xargs.org/files/aes_256_gcm_decrypt.c\"\n                  },\n                  \"content\": \"解密\"\n                },\n                \"和\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://tls13.xargs.org/files/aes_256_gcm_encrypt.c\"\n                  },\n                  \"content\": \"加密\"\n                },\n                \"这些数据。\"\n              ]\n            },\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### from the \\\"Server Handshake Keys Calc\\\" step\\n$ key=9f13575ce3f8cfc1df64a77ceaffe89700b492ad31b4fab01c4792be1b266b7f\\n$ iv=9563bc8b590f671f488d2da3\\n### from this record\\n$ recdata=1703030343\\n$ authtag=58faa5bafa30186c6b2f238eb530c73e\\n$ recordnum=1\\n### may need to add -I and -L flags for include and lib dirs\\n$ cc -o aes_256_gcm_decrypt aes_256_gcm_decrypt.c -lssl -lcrypto\\n$ echo \\\"ba f0 0a 9b e5 0f 3f 23 07 e7 26 ed cb da cb e4 b1 86 16\\n  ... snip ...\\n  a9 19 a7 0e 3a 10 e3 08 41\\\" | xxd -r -p > /tmp/msg1\\n$ cat /tmp/msg1 \\\\\\n  | ./aes_256_gcm_decrypt $iv $recordnum $key $recdata $authtag \\\\\\n  | hexdump -C\\n\\n00000000  08 00 00 02 00 00 0b 00  03 2e 00 00 03 2a 00 03  |.............*..|\\n00000010  25 30 82 03 21 30 82 02  09 a0 03 02 01 02 02 08  |58..!0..........|\\n00000020  15 5a 92 ad c2 04 8f 90  30 0d 06 09 2a 86 48 86  |.Z......0...*.H.|\\n00000000  0b 00 03 2e 00 00 03 2a  00 03 25 30 82 03 21 30  |.......*..58..!0|\\n00000010  82 02 09 a0 03 02 01 02  02 08 15 5a 92 ad c2 04  |...........Z....|\\n00000020  8f 90 30 0d 06 09 2a 86  48 86 f7 0d 01 01 0b 05  |..0...*.H.......|\\n00000030  00 30 22 31 0b 30 09 06  03 55 04 06 13 02 55 53  |.0\\\"1.0...U....US|\\n00000040  31 13 30 11 06 03 55 04  0a 13 0a 45 78 61 6d 70  |1.0...U....Examp|\\n... snip ...\"\n              }\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"加密的数据载荷\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"0b 00 03 2e 00 00 03 2a 00 03 25 30 82 03 21 30 82 02 09 a0 03 02 01 02 02 08 15 5a 92 ad c2 04 8f 90 30 0d 06 09 2a 86 48 86 f7 0d 01 01 0b 05 00 30 22 31 0b 30 09 06 03 55 04 06 13 02 55 53 31 13 30 11 06 03 55 04 0a 13 0a 45 78 61 6d 70 6c 65 20 43 41 30 1e 17 0d 31 38 31 30 30 35 30 31 33 38 31 37 5a 17 0d 31 39 31 30 30 35 30 31 33 38 31 37 5a 30 2b 31 0b 30 09 06 03 55 04 06 13 02 55 53 31 1c 30 1a 06 03 55 04 03 13 13 65 78 61 6d 70 6c 65 2e 75 6c 66 68 65 69 6d 2e 6e 65 74 30 82 01 22 30 0d 06 09 2a 86 48 86 f7 0d 01 01 01 05 00 03 82 01 0f 00 30 82 01 0a 02 82 01 01 00 c4 80 36 06 ba e7 47 6b 08 94 04 ec a7 b6 91 04 3f f7 92 bc 19 ee fb 7d 74 d7 a8 0d 00 1e 7b 4b 3a 4a e6 0f e8 c0 71 fc 73 e7 02 4c 0d bc f4 bd d1 1d 39 6b ba 70 46 4a 13 e9 4a f8 3d f3 e1 09 59 54 7b c9 55 fb 41 2d a3 76 52 11 e1 f3 dc 77 6c aa 53 37 6e ca 3a ec be c3 aa b7 3b 31 d5 6c b6 52 9c 80 98 bc c9 e0 28 18 e2 0b f7 f8 a0 3a fd 17 04 50 9e ce 79 bd 9f 39 f1 ea 69 ec 47 97 2e 83 0f b5 ca 95 de 95 a1 e6 04 22 d5 ee be 52 79 54 a1 e7 bf 8a 86 f6 46 6d 0d 9f 16 95 1a 4c f7 a0 46 92 59 5c 13 52 f2 54 9e 5a fb 4e bf d7 7a 37 95 01 44 e4 c0 26 87 4c 65 3e 40 7d 7d 23 07 44 01 f4 84 ff d0 8f 7a 1f a0 52 10 d1 f4 f0 d5 ce 79 70 29 32 e2 ca be 70 1f df ad 6b 4b b7 11 01 f4 4b ad 66 6a 11 13 0f e2 ee 82 9e 4d 02 9d c9 1c dd 67 16 db b9 06 18 86 ed c1 ba 94 21 02 03 01 00 01 a3 52 30 50 30 0e 06 03 55 1d 0f 01 01 ff 04 04 03 02 05 a0 30 1d 06 03 55 1d 25 04 16 30 14 06 08 2b 06 01 05 05 07 03 02 06 08 2b 06 01 05 05 07 03 01 30 1f 06 03 55 1d 23 04 18 30 16 80 14 89 4f de 5b cc 69 e2 52 cf 3e a3 00 df b1 97 b8 1d e1 c1 46 30 0d 06 09 2a 86 48 86 f7 0d 01 01 0b 05 00 03 82 01 01 00 59 16 45 a6 9a 2e 37 79 e4 f6 dd 27 1a ba 1c 0b fd 6c d7 55 99 b5 e7 c3 6e 53 3e ff 36 59 08 43 24 c9 e7 a5 04 07 9d 39 e0 d4 29 87 ff e3 eb dd 09 c1 cf 1d 91 44 55 87 0b 57 1d d1 9b df 1d 24 f8 bb 9a 11 fe 80 fd 59 2b a0 39 8c de 11 e2 65 1e 61 8c e5 98 fa 96 e5 37 2e ef 3d 24 8a fd e1 74 63 eb bf ab b8 e4 d1 ab 50 2a 54 ec 00 64 e9 2f 78 19 66 0d 3f 27 cf 20 9e 66 7f ce 5a e2 e4 ac 99 c7 c9 38 18 f8 b2 51 07 22 df ed 97 f3 2e 3e 93 49 d4 c6 6c 9e a6 39 6d 74 44 62 a0 6b 42 c6 d5 ba 68 8e ac 3a 01 7b dd fc 8e 2c fc ad 27 cb 69 d3 cc dc a2 80 41 44 65 d3 ae 34 8c e0 f3 4a b2 fb 9c 61 83 71 31 2b 19 10 41 64 1c 23 7f 11 a5 d6 5c 84 4f 04 04 84 99 38 71 2b 95 9e d6 85 bc 5c 5d d6 45 ed 19 90 94 73 40 29 26 dc b4 0e 34 69 a1 59 41 e8 e2 cc a8 4b b6 08 46 36 a0 00 00\"\n          },\n          [\"该载荷的解释详见下面的\\\"服务器端证书\\\"部分\"]\n        ],\n        [\n          \"记录类型\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"16\"\n          },\n          [\n            \"每一个伪装的 TLS 1.3 记录的最后一个字节都需要表明其真正的记录类型\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"16 - 类型为 0x16(22, handshake record)\"\n                }\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/TLS13/wrappedRecord3.json",
    "content": "[\n  \"为了减少连接被无法识别新 TLS 协议的中间件阻断的问题，加密的 TLS 1.3 记录都需要伪装成一个会话恢复成功的 TLS 1.2 会话记录(之所以不是伪装成握手记录，因为握手记录过长会使得兼容实现变得异常复杂)。\",\n  \"包装的实际记录内容将在下面单独列出。\",\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"记录头\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"17 03 03 01 19\"\n          },\n          [\n            \"此 TLS 1.3 记录被伪装为 TLS 1.2 的\\\"会话记录\\\"。因此内容会以会话记录的规格展示。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"17 - 表示 TLS 记录类型 0x17(23, application data)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"03 03 - 协议版本 (3.3, 即 TLS 1.2)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"01 19 - 紧接着的数组载荷长度 0x119(281) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"加密的数据载荷\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"73 71 9f ce 07 ec 2f 6d 3b ba 02 92 a0 d4 0b 27 70 c0 6a 27 17 99 a5 33 14 f6 f7 7f c9 5c 5f e7 b9 a4 32 9f d9 54 8c 67 0e be ea 2f 2d 5c 35 1d d9 35 6e f2 dc d5 2e b1 37 bd 3a 67 65 22 f8 cd 0f b7 56 07 89 ad 7b 0e 3c ab a2 e3 7e 6b 41 99 c6 79 3b 33 46 ed 46 cf 74 0a 9f a1 fe c4 14 dc 71 5c 41 5c 60 e5 75 70 3c e6 a3 4b 70 b5 19 1a a6 a6 1a 18 fa ff 21 6c 68 7a d8 d1 7e 12 a7 e9 99 15 a6 11 bf c1 a2 be fc 15 e6 e9 4d 78 46 42 e6 82 fd 17 38 2a 34 8c 30 10 56 b9 40 c9 84 72 00 40 8b ec 56 c8 1e a3 d7 21 7a b8 e8 5a 88 71 53 95 89 9c 90 58 7f 72 e8 dd d7 4b 26 d8 ed c1 c7 c8 37 d9 f2 eb bc 26 09 62 21 90 38 b0 56 54 a6 3a 0b 12 99 9b 4a 83 06 a3 dd cc 0e 17 c5 3b a8 f9 c8 03 63 f7 84 13 54 d2 91 b4 ac e0 c0 f3 30 c0 fc d5 aa 9d ee f9 69 ae 8a b2 d9 8d a8 8e bb 6e a8 0a 3a 11 f0 0e a2\"\n          },\n          [\"这个数据是用服务器端的握手时密钥加密的。\"]\n        ],\n        [\n          \"AEAD 鉴别标签\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"96 a3 23 23 67 ff 07 5e 1c 66 dd 9c be dc 47 13\"\n          },\n          [\n            {\n              \"children\": [\n                \"这是 \",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://zhuanlan.zhihu.com/p/28566058\"\n                  },\n                  \"content\": \"AEAD 算法\"\n                },\n                \"的鉴别标签，用以确认加密数据和数据包头的完整性。它由加密算法产生，并由解密算法消耗。\"\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"data\": [\n        [\n          \"\",\n          {\n            \"props\": {\n              \"className\": \"decryption-header\"\n            },\n            \"content\": \"解密后的数据载荷\"\n          },\n          [\n            { \"Tag\": \"h4\", \"content\": \"解密\" },\n            \"数据被 \\\"服务器端生成握手密钥\\\" 步骤中产生的握手时密钥和握手时向量(IVs)加密。IVs 通过密钥和已经用密钥加密的记录长度进行异或操作生成。在例子中 IV 为 2。\",\n            \"数据包开头的 5 字节(记录头)还会作为解密过程解密成功时必须满足的认证条件。\",\n            {\n              \"children\": [\n                \"openssl 命令行工具还不支持 AEAD 算法加解密(AEAD ciphers)，你可以使用作者的命令行工具来\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://tls13.xargs.org/files/aes_256_gcm_decrypt.c\"\n                  },\n                  \"content\": \"解密\"\n                },\n                \"和\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://tls13.xargs.org/files/aes_256_gcm_encrypt.c\"\n                  },\n                  \"content\": \"加密\"\n                },\n                \"这些数据。\"\n              ]\n            },\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### from the \\\"Server Handshake Keys Calc\\\" step\\n$ key=9f13575ce3f8cfc1df64a77ceaffe89700b492ad31b4fab01c4792be1b266b7f\\n$ iv=9563bc8b590f671f488d2da3\\n### from this record\\n$ recdata=1703030119\\n$ authtag=96a3232367ff075e1c66dd9cbedc4713\\n$ recordnum=2\\n### may need to add -I and -L flags for include and lib dirs\\n$ cc -o aes_256_gcm_decrypt aes_256_gcm_decrypt.c -lssl -lcrypto\\n$ echo \\\"73 71 9f ce 07 ec 2f 6d 3b ba 02 92 a0 d4 0b 27 70 c0 6a 27\\n  ... snip ...\\n  d9 8d a8 8e bb 6e a8 0a 3a 11 f0 0e a2\\\" | xxd -r -p > /tmp/msg1\\n$ cat /tmp/msg1 \\\\\\n  | ./aes_256_gcm_decrypt $iv $recordnum $key $recdata $authtag \\\\\\n  | hexdump -C\\n\\n00000000  0f 00 01 04 08 04 01 00  5c bb 24 c0 40 93 32 da  |........\\\\.$.@.2.|\\n00000010  a9 20 bb ab bd b9 bd 50  17 0b e4 9c fb e0 a4 10  |. .....P........|\\n00000020  7f ca 6f fb 10 68 e6 5f  96 9e 6d e7 d4 f9 e5 60  |..o..h._..m....`|\\n... snip ...\"\n              }\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"加密的数据载荷\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"0f 00 01 04 08 04 01 00 5c bb 24 c0 40 93 32 da a9 20 bb ab bd b9 bd 50 17 0b e4 9c fb e0 a4 10 7f ca 6f fb 10 68 e6 5f 96 9e 6d e7 d4 f9 e5 60 38 d6 7c 69 c0 31 40 3a 7a 7c 0b cc 86 83 e6 57 21 a0 c7 2c c6 63 40 19 ad 1d 3a d2 65 a8 12 61 5b a3 63 80 37 20 84 f5 da ec 7e 63 d3 f4 93 3f 27 22 74 19 a6 11 03 46 44 dc db c7 be 3e 74 ff ac 47 3f aa ad de 8c 2f c6 5f 32 65 77 3e 7e 62 de 33 86 1f a7 05 d1 9c 50 6e 89 6c 8d 82 f5 bc f3 5f ec e2 59 b7 15 38 11 5e 9c 8c fb a6 2e 49 bb 84 74 f5 85 87 b1 1b 8a e3 17 c6 33 e9 c7 6c 79 1d 46 62 84 ad 9c 4f f7 35 a6 d2 e9 63 b5 9b bc a4 40 a3 07 09 1a 1b 4e 46 bc c7 a2 f9 fb 2f 1c 89 8e cb 19 91 8b e4 12 1d 7e 8e d0 4c d5 0c 9a 59 e9 87 98 01 07 bb bf 29 9c 23 2e 7f db e1 0a 4c fd ae 5c 89 1c 96 af df f9 4b 54 cc d2 bc 19 d3 cd aa 66 44 85 9c\"\n          },\n          [\"该载荷的解释详见下面的\\\"服务器端证书验证数据\\\"部分\"]\n        ],\n        [\n          \"记录类型\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"16\"\n          },\n          [\n            \"每一个伪装的 TLS 1.3 记录的最后一个字节都需要表明其真正的记录类型\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"16 - 类型为 0x16(22, handshake record)\"\n                }\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/TLS13/wrappedRecord4.json",
    "content": "[\n  \"为了减少连接被无法识别新 TLS 协议的中间件阻断的问题，加密的 TLS 1.3 记录都需要伪装成一个会话恢复成功的 TLS 1.2 会话记录(之所以不是伪装成握手记录，因为握手记录过长会使得兼容实现变得异常复杂)。\",\n  \"包装的实际记录内容将在下面单独列出。\",\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"记录头\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"17 03 03 00 45\"\n          },\n          [\n            \"此 TLS 1.3 记录被伪装为 TLS 1.2 的\\\"会话记录\\\"。因此内容会以会话记录的规格展示。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"17 - 表示 TLS 记录类型 0x17(23, application data)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"03 03 - 协议版本 (3.3, 即 TLS 1.2)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 45 - 紧接着的数组载荷长度 0x045(69) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"加密的数据载荷\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"10 61 de 27 e5 1c 2c 9f 34 29 11 80 6f 28 2b 71 0c 10 63 2c a5 00 67 55 88 0d bf 70 06 00 2d 0e 84 fe d9 ad f2 7a 43 b5 19 23 03 e4 df 5c 28 5d 58 e3 c7 62 24\"\n          },\n          [\"这个数据是用服务器端的握手时密钥加密的。\"]\n        ],\n        [\n          \"AEAD 鉴别标签\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"07 84 40 c0 74 23 74 74 4a ec f2 8c f3 18 2f d0\"\n          },\n          [\n            {\n              \"children\": [\n                \"这是 \",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://zhuanlan.zhihu.com/p/28566058\"\n                  },\n                  \"content\": \"AEAD 算法\"\n                },\n                \"的鉴别标签，用以确认加密数据和数据包头的完整性。它由加密算法产生，并由解密算法消耗。\"\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"data\": [\n        [\n          \"\",\n          {\n            \"props\": {\n              \"className\": \"decryption-header\"\n            },\n            \"content\": \"解密后的数据载荷\"\n          },\n          [\n            { \"Tag\": \"h4\", \"content\": \"解密\" },\n            \"数据被 \\\"服务器端生成握手密钥\\\" 步骤中产生的握手时密钥和握手时向量(IVs)加密。IVs 通过密钥和已经用密钥加密的记录长度进行异或操作生成。在例子中 IV 为 3。\",\n            \"数据包开头的 5 字节(记录头)还会作为解密过程解密成功时必须满足的认证条件。\",\n            {\n              \"children\": [\n                \"openssl 命令行工具还不支持 AEAD 算法加解密(AEAD ciphers)，你可以使用作者的命令行工具来\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://tls13.xargs.org/files/aes_256_gcm_decrypt.c\"\n                  },\n                  \"content\": \"解密\"\n                },\n                \"和\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://tls13.xargs.org/files/aes_256_gcm_encrypt.c\"\n                  },\n                  \"content\": \"加密\"\n                },\n                \"这些数据。\"\n              ]\n            },\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### from the \\\"Server Handshake Keys Calc\\\" step\\n$ key=9f13575ce3f8cfc1df64a77ceaffe89700b492ad31b4fab01c4792be1b266b7f\\n$ iv=9563bc8b590f671f488d2da3\\n### from this record\\n$ recdata=1703030045\\n$ authtag=078440c0742374744aecf28cf3182fd0\\n$ recordnum=3\\n### may need to add -I and -L flags for include and lib dirs\\n$ cc -o aes_256_gcm_decrypt aes_256_gcm_decrypt.c -lssl -lcrypto\\n$ echo \\\"10 61 de 27 e5 1c 2c 9f 34 29 11 80 6f 28 2b 71 0c 10 63 2c a5 00 67 55 88 0d bf 70 06 00 2d 0e 84 fe d9 ad f2 7a 43 b5 19\\n  23 03 e4 df 5c 28 5d 58 e3 c7 62 24\\\" | xxd -r -p > /tmp/msg1\\n$ cat /tmp/msg1 \\\\\\n  | ./aes_256_gcm_decrypt $iv $recordnum $key $recdata $authtag \\\\\\n  | hexdump -C\\n\\n00000000  14 00 00 30 7e 30 ee cc  b6 b2 3b e6 c6 ca 36 39  |...0~0....;...69|\\n00000010  92 e8 42 da 87 7e e6 47  15 ae 7f c0 cf 87 f9 e5  |..B..~.G........|\\n00000020  03 21 82 b5 bb 48 d1 e3  3f 99 79 05 5a 16 0c 8d  |.!...H..?.y.Z...|\\n00000030  bb b1 56 9c 16                                    |..V..|\"\n              }\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"加密的数据载荷\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"14 00 00 30 7e 30 ee cc b6 b2 3b e6 c6 ca 36 39 92 e8 42 da 87 7e e6 47 15 ae 7f c0 cf 87 f9 e5 03 21 82 b5 bb 48 d1 e3 3f 99 79 05 5a 16 0c 8d bb b1 56 9c\"\n          },\n          [\"该载荷的解释详见下面的\\\"服务器端握手完成\\\"部分\"]\n        ],\n        [\n          \"记录类型\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"16\"\n          },\n          [\n            \"每一个伪装的 TLS 1.3 记录的最后一个字节都需要表明其真正的记录类型\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"16 - 类型为 0x16(22, handshake record)\"\n                }\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/TLS13/wrappedRecord5.json",
    "content": "[\n  \"为了减少连接被无法识别新 TLS 协议的中间件阻断的问题，加密的 TLS 1.3 记录都需要伪装成一个会话恢复成功的 TLS 1.2 会话记录(之所以不是伪装成握手记录，因为握手记录过长会使得兼容实现变得异常复杂)。\",\n  \"包装的实际记录内容将在下面单独列出。\",\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"记录头\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"17 03 03 00 45\"\n          },\n          [\n            \"此 TLS 1.3 记录被伪装为 TLS 1.2 的\\\"会话记录\\\"。因此内容会以会话记录的规格展示。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"17 - 表示 TLS 记录类型 0x17(23, application data)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"03 03 - 协议版本 (3.3, 即 TLS 1.2)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 45 - 紧接着的数组载荷长度 0x045(69) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"加密的数据载荷\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"9f f9 b0 63 17 51 77 32 2a 46 dd 98 96 f3 c3 bb 82 0a b5 17 43 eb c2 5f da dd 53 45 4b 73 de b5 4c c7 24 8d 41 1a 18 bc cf 65 7a 96 08 24 e9 a1 93 64 83 7c 35\"\n          },\n          [\"这个数据是用客户端的握手时密钥加密的。\"]\n        ],\n        [\n          \"AEAD 鉴别标签\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"0a 69 a8 8d 4b f6 35 c8 5e b8 74 ae bc 9d fd e8\"\n          },\n          [\n            {\n              \"children\": [\n                \"这是 \",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://zhuanlan.zhihu.com/p/28566058\"\n                  },\n                  \"content\": \"AEAD 算法\"\n                },\n                \"的鉴别标签，用以确认加密数据和数据包头的完整性。它由加密算法产生，并由解密算法消耗。\"\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"data\": [\n        [\n          \"\",\n          {\n            \"props\": {\n              \"className\": \"decryption-header\"\n            },\n            \"content\": \"解密后的数据载荷\"\n          },\n          [\n            { \"Tag\": \"h4\", \"content\": \"解密\" },\n            \"数据被 \\\"客户端生成握手密钥\\\" 步骤中产生的握手时密钥和握手时向量(IVs)加密。IVs 通过密钥和已经用密钥加密的记录长度进行异或操作生成。在例子中 IV 为 0。\",\n            \"数据包开头的 5 字节(记录头)还会作为解密过程解密成功时必须满足的认证条件。\",\n            {\n              \"children\": [\n                \"openssl 命令行工具还不支持 AEAD 算法加解密(AEAD ciphers)，你可以使用作者的命令行工具来\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://tls13.xargs.org/files/aes_256_gcm_decrypt.c\"\n                  },\n                  \"content\": \"解密\"\n                },\n                \"和\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://tls13.xargs.org/files/aes_256_gcm_encrypt.c\"\n                  },\n                  \"content\": \"加密\"\n                },\n                \"这些数据。\"\n              ]\n            },\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### from the \\\"Client Handshake Keys Calc\\\" step\\n$ key=1135b4826a9a70257e5a391ad93093dfd7c4214812f493b3e3daae1eb2b1ac69\\n$ iv=4256d2e0e88babdd05eb2f27\\n### from this record\\n$ recdata=1703030045\\n$ authtag=0a69a88d4bf635c85eb874aebc9dfde8\\n$ recordnum=0\\n### may need to add -I and -L flags for include and lib dirs\\n$ cc -o aes_256_gcm_decrypt aes_256_gcm_decrypt.c -lssl -lcrypto\\n$ echo \\\"9f f9 b0 63 17 51 77 32 2a 46 dd 98 96 f3 c3 bb 82 0a b5\\n  17 43 eb c2 5f da dd 53 45 4b 73 de b5 4c c7 24 8d 41 1a 18 bc\\n  cf 65 7a 96 08 24 e9 a1 93 64 83 7c 35\\\" | xxd -r -p > /tmp/msg2\\n$ cat /tmp/msg2 \\\\\\n  | ./aes_256_gcm_decrypt $iv $recordnum $key $recdata $authtag \\\\\\n  | hexdump -C\\n\\n00000000  14 00 00 30 bf f5 6a 67  1b 6c 65 9d 0a 7c 5d d1  |...0..jg.le..|].|\\n00000010  84 28 f5 8b dd 38 b1 84  a3 ce 34 2d 9f de 95 cb  |.(...8....4-....|\\n00000020  d5 05 6f 7d a7 91 8e e3  20 ea b7 a9 3a bd 8f 1c  |..o}.... ...:...|\\n00000030  02 45 4d 27 16                                    |.EM'.|\"\n              }\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"加密的数据载荷\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"14 00 00 30 bf f5 6a 67 1b 6c 65 9d 0a 7c 5d d1 84 28 f5 8b dd 38 b1 84 a3 ce 34 2d 9f de 95 cb d5 05 6f 7d a7 91 8e e3 20 ea b7 a9 3a bd 8f 1c 02 45 4d 27\"\n          },\n          [\"该载荷的解释详见下面的\\\"客户端握手完成\\\"部分\"]\n        ],\n        [\n          \"记录类型\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"16\"\n          },\n          [\n            \"每一个伪装的 TLS 1.3 记录的最后一个字节都需要表明其真正的记录类型\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"16 - 类型为 0x16(22, handshake record)\"\n                }\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/TLS13/wrappedRecord6.json",
    "content": "[\n  \"为了减少连接被无法识别新 TLS 协议的中间件阻断的问题，加密的 TLS 1.3 记录都需要伪装成一个会话恢复成功的 TLS 1.2 会话记录(之所以不是伪装成握手记录，因为握手记录过长会使得兼容实现变得异常复杂)。\",\n  \"包装的实际记录内容将在下面单独列出。\",\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"记录头\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"17 03 03 00 15\"\n          },\n          [\n            \"此 TLS 1.3 记录被伪装为 TLS 1.2 的\\\"会话记录\\\"。因此内容会以会话记录的规格展示。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"17 - 表示 TLS 记录类型 0x17(23, application data)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"03 03 - 协议版本 (3.3, 即 TLS 1.2)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 15 - 紧接着的数组载荷长度 0x015(21) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"加密的数据载荷\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"82 81 39 cb 7b\"\n          },\n          [\"这个数据是用客户端的会话时密钥加密的。\"]\n        ],\n        [\n          \"AEAD 鉴别标签\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"73 aa ab f5 b8 2f bf 9a 29 61 bc de 10 03 8a 32\"\n          },\n          [\n            {\n              \"children\": [\n                \"这是 \",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://zhuanlan.zhihu.com/p/28566058\"\n                  },\n                  \"content\": \"AEAD 算法\"\n                },\n                \"的鉴别标签，用以确认加密数据和数据包头的完整性。它由加密算法产生，并由解密算法消耗。\"\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"data\": [\n        [\n          \"\",\n          {\n            \"props\": {\n              \"className\": \"decryption-header\"\n            },\n            \"content\": \"解密后的数据载荷\"\n          },\n          [\n            { \"Tag\": \"h4\", \"content\": \"解密\" },\n            \"数据被 \\\"客户端生成会话密钥\\\" 步骤中产生的会话时密钥和会话时向量(IVs)加密。IVs 通过密钥和已经用密钥加密的记录长度进行异或操作生成。在例子中 IV 为 0。\",\n            \"数据包开头的 5 字节(记录头)还会作为解密过程解密成功时必须满足的认证条件。\",\n            {\n              \"children\": [\n                \"openssl 命令行工具还不支持 AEAD 算法加解密(AEAD ciphers)，你可以使用作者的命令行工具来\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://tls13.xargs.org/files/aes_256_gcm_decrypt.c\"\n                  },\n                  \"content\": \"解密\"\n                },\n                \"和\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://tls13.xargs.org/files/aes_256_gcm_encrypt.c\"\n                  },\n                  \"content\": \"加密\"\n                },\n                \"这些数据。\"\n              ]\n            },\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### from the \\\"Client Application Keys Calc\\\" step\\n$ key=de2f4c7672723a692319873e5c227606691a32d1c59d8b9f51dbb9352e9ca9cc\\n$ iv=bb007956f474b25de902432f\\n### from this record\\n$ recdata=1703030015\\n$ authtag=73aaabf5b82fbf9a2961bcde10038a32\\n$ recordnum=0\\n### may need to add -I and -L flags for include and lib dirs\\n$ cc -o aes_256_gcm_decrypt aes_256_gcm_decrypt.c -lssl -lcrypto\\n$ echo \\\"82 81 39 cb 7b\\\" | xxd -r -p > /tmp/msg3\\n$ cat /tmp/msg3 \\\\\\n  | ./aes_256_gcm_decrypt $iv $recordnum $key $recdata $authtag \\\\\\n  | hexdump -C\\n\\n00000000  70 69 6e 67 17                                    |ping.|\"\n              }\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"加密的数据载荷\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"70 69 6e 67\"\n          },\n          [\"该载荷的解释详见下面的\\\"客户端会话数据\\\"部分\"]\n        ],\n        [\n          \"记录类型\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"17\"\n          },\n          [\n            \"每一个伪装的 TLS 1.3 记录的最后一个字节都需要表明其真正的记录类型\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"17 - 类型为 0x17(23, application data)\"\n                }\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/TLS13/wrappedRecord7.json",
    "content": "[\n  \"为了减少连接被无法识别新 TLS 协议的中间件阻断的问题，加密的 TLS 1.3 记录都需要伪装成一个会话恢复成功的 TLS 1.2 会话记录(之所以不是伪装成握手记录，因为握手记录过长会使得兼容实现变得异常复杂)。\",\n  \"包装的实际记录内容将在下面单独列出。\",\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"记录头\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"17 03 03 00 ea\"\n          },\n          [\n            \"此 TLS 1.3 记录被伪装为 TLS 1.2 的\\\"会话记录\\\"。因此内容会以会话记录的规格展示。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"17 - 表示 TLS 记录类型 0x17(23, application data)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"03 03 - 协议版本 (3.3, 即 TLS 1.2)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 ea - 紧接着的数组载荷长度 0x0ea(234) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"加密的数据载荷\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"38 2d 8c 19 a4 7f 4e 8d 9b 0c 51 0b c3 48 db 2c c9 9b 24 1c d0 d1 8b 31 d0 ca 1a c1 2d c1 e3 03 c5 8d 0c 7e 9e 27 29 4c 6b 0e 31 98 f7 d3 19 eb 14 62 2e c4 8b 6a c8 f8 66 d7 49 4f a7 75 c8 80 ff 43 ad 4b 1a f5 3a 03 ca 19 77 95 77 8f ff 2f fe 1d 3b 99 b3 4d e7 82 a7 6a bf a8 40 e6 36 6c d7 34 9d 9b cf f6 41 f5 e0 df f9 5e 40 d7 2e 09 ef fe 18 ee 64 67 2c b9 60 05 40 44 88 ad 18 96 c4 4a 5f d1 74 99 8e 9b 00 94 d8 e6 d8 4d 29 29 b7 88 3d c9 a3 c3 c7 31 3a 87 29 3f 31 b6 1d 24 d9 90 97 c8 85 3b fb eb 95 d1 d0 1f 99 ca 05 b0 50 18 59 cf 63 40 e8 37 70 75 97 01 52 fa 94 f5 f5 be 29 06 e7 2a 15 e4 08 36 a4 1f 4c d3 db e7 d5 13 c1 6e 88 61 1d 3e ae 93\"\n          },\n          [\"这个数据是用服务器端的会话时密钥加密的。\"]\n        ],\n        [\n          \"AEAD 鉴别标签\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"38 d9 db 1f 91 ca 3d 58 42 60 2a 61 0b 43 a4 63\"\n          },\n          [\n            {\n              \"children\": [\n                \"这是 \",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://zhuanlan.zhihu.com/p/28566058\"\n                  },\n                  \"content\": \"AEAD 算法\"\n                },\n                \"的鉴别标签，用以确认加密数据和数据包头的完整性。它由加密算法产生，并由解密算法消耗。\"\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"data\": [\n        [\n          \"\",\n          {\n            \"props\": {\n              \"className\": \"decryption-header\"\n            },\n            \"content\": \"解密后的数据载荷\"\n          },\n          [\n            { \"Tag\": \"h4\", \"content\": \"解密\" },\n            \"数据被 \\\"服务器端生成会话密钥\\\" 步骤中产生的握手时密钥和握手时向量(IVs)加密。IVs 通过密钥和已经用密钥加密的记录长度进行异或操作生成。在例子中 IV 为 0。\",\n            \"数据包开头的 5 字节(记录头)还会作为解密过程解密成功时必须满足的认证条件。\",\n            {\n              \"children\": [\n                \"openssl 命令行工具还不支持 AEAD 算法加解密(AEAD ciphers)，你可以使用作者的命令行工具来\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://tls13.xargs.org/files/aes_256_gcm_decrypt.c\"\n                  },\n                  \"content\": \"解密\"\n                },\n                \"和\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://tls13.xargs.org/files/aes_256_gcm_encrypt.c\"\n                  },\n                  \"content\": \"加密\"\n                },\n                \"这些数据。\"\n              ]\n            },\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### from the \\\"Server Application Keys Calc\\\" step\\n$ key=01f78623f17e3edcc09e944027ba3218d57c8e0db93cd3ac419309274700ac27\\n$ iv=196a750b0c5049c0cc51a541\\n### from this record\\n$ recdata=17030300ea\\n$ authtag=38d9db1f91ca3d5842602a610b43a463\\n$ recordnum=0\\n### may need to add -I and -L flags for include and lib dirs\\n$ cc -o aes_256_gcm_decrypt aes_256_gcm_decrypt.c -lssl -lcrypto\\n$ echo \\\"38 2d 8c 19 a4 7f 4e 8d 9b 0c 51 0b c3 48 db 2c c9 9b 24\\n  ... snip ...\\n  13 c1 6e 88 61 1d 3e ae 93\\\" | xxd -r -p > /tmp/msg5\\n$ cat /tmp/msg5 \\\\\\n  | ./aes_256_gcm_decrypt $iv $recordnum $key $recdata $authtag \\\\\\n  | hexdump -C\\n\\n00000000  04 00 00 d5 00 00 1c 20  00 00 00 00 08 00 00 00  |....... ........|\\n00000010  00 00 00 00 00 00 c0 41  42 43 44 45 46 47 48 49  |.......ABCDEFGHI|\\n... snip ...\"\n              }\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"加密的数据载荷\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"04 00 00 d5 00 00 1c 20 00 00 00 00 08 00 00 00 00 00 00 00 00 00 c0 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 00 49 56 44 41 54 41 49 56 44 41 54 41 00 41 45 53 cb 11 9d 4d bd 2a 21 ec c2 26 a6 09 0e e8 ca 58 df 09 03 9b 35 96 f4 de 79 98 0e a3 25 d5 14 62 5c 0c 21 c5 0f 03 26 1d c4 2c e7 c5 97 0c 4c 01 ea 33 1c ff c8 99 66 ef 54 8b e4 df 9a 8b a4 38 5b eb 86 80 fd 0b 78 df b8 e9 8e fc 8f cc d8 14 fe cd 1d 9b ce 89 ca 05 dc 28 c2 49 e5 bd 61 d0 3a 56 8f 9a 0a 46 fb fd 05 30 2d b6 b2 f7 a3 13 e3 32 67 bf 0b cb dc ec fb 04 a4 d8 2f 5a 69 45 1f 56 7a b5 19 9b b2 6c 5c f2 00 72 f0 45 03 73 02 8f e0 71 d4 f4 1d 8f 61 ae 02 4d 69 bb ae 4c 00 00\"\n          },\n          [\"该载荷的解释详见下面的\\\"服务器端第一个会话记录单\\\"部分\"]\n        ],\n        [\n          \"记录类型\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"16\"\n          },\n          [\n            \"每一个伪装的 TLS 1.3 记录的最后一个字节都需要表明其真正的记录类型\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"16 - 类型为 0x16(22, handshake record)\"\n                }\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/TLS13/wrappedRecord8.json",
    "content": "[\n  \"为了减少连接被无法识别新 TLS 协议的中间件阻断的问题，加密的 TLS 1.3 记录都需要伪装成一个会话恢复成功的 TLS 1.2 会话记录(之所以不是伪装成握手记录，因为握手记录过长会使得兼容实现变得异常复杂)。\",\n  \"包装的实际记录内容将在下面单独列出。\",\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"记录头\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"17 03 03 00 ea\"\n          },\n          [\n            \"此 TLS 1.3 记录被伪装为 TLS 1.2 的\\\"会话记录\\\"。因此内容会以会话记录的规格展示。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"17 - 表示 TLS 记录类型 0x17(23, application data)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"03 03 - 协议版本 (3.3, 即 TLS 1.2)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 ea - 紧接着的数组载荷长度 0x0ea(234) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"加密的数据载荷\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"38 ad fb 1d 01 fd 95 a6 03 85 e8 bb f1 fd 8d cb 46 70 98 97 e7 d6 74 c2 f7 37 0e c1 1d 8e 33 eb 4f 4f e7 f5 4b f4 dc 0b 92 fa e7 42 1c 33 c6 45 3c eb c0 73 15 96 10 a0 97 40 ab 2d 05 6f 8d 51 cf a2 62 00 7d 40 12 36 da fc 2f 72 92 ff 0c c8 86 a4 ef 38 9f 2c ed 12 26 c6 b4 dc f6 9d 99 4f f9 14 8e f9 69 bc 77 d9 43 3a b1 d3 a9 32 54 21 82 82 9f 88 9a d9 5f 04 c7 52 f9 4a ce 57 14 6a 5d 84 b0 42 bf b3 48 5a 64 e7 e9 57 b0 89 80 cd 08 ba f9 69 8b 89 29 98 6d 11 74 d4 aa 6d d7 a7 e8 c0 86 05 2c 3c 76 d8 19 34 bd f5 9b 96 6e 39 20 31 f3 47 1a de bd dd db e8 4f cf 1f f4 08 84 6a e9 b2 8c a4 a9 e7 28 84 4a 49 3d 80 45 5d 6e af f2 05 b4 0a 1e f1 85 74 ef\"\n          },\n          [\"这个数据是用服务器端的会话时密钥加密的。\"]\n        ],\n        [\n          \"AEAD 鉴别标签\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"c0 b9 6a d3 83 af bd 8d fc 86 f8 08 7c 1f 7d c8\"\n          },\n          [\n            {\n              \"children\": [\n                \"这是 \",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://zhuanlan.zhihu.com/p/28566058\"\n                  },\n                  \"content\": \"AEAD 算法\"\n                },\n                \"的鉴别标签，用以确认加密数据和数据包头的完整性。它由加密算法产生，并由解密算法消耗。\"\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"data\": [\n        [\n          \"\",\n          {\n            \"props\": {\n              \"className\": \"decryption-header\"\n            },\n            \"content\": \"解密后的数据载荷\"\n          },\n          [\n            { \"Tag\": \"h4\", \"content\": \"解密\" },\n            \"数据被 \\\"服务器端生成会话密钥\\\" 步骤中产生的握手时密钥和握手时向量(IVs)加密。IVs 通过密钥和已经用密钥加密的记录长度进行异或操作生成。在例子中 IV 为 0。\",\n            \"数据包开头的 5 字节(记录头)还会作为解密过程解密成功时必须满足的认证条件。\",\n            {\n              \"children\": [\n                \"openssl 命令行工具还不支持 AEAD 算法加解密(AEAD ciphers)，你可以使用作者的命令行工具来\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://tls13.xargs.org/files/aes_256_gcm_decrypt.c\"\n                  },\n                  \"content\": \"解密\"\n                },\n                \"和\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://tls13.xargs.org/files/aes_256_gcm_encrypt.c\"\n                  },\n                  \"content\": \"加密\"\n                },\n                \"这些数据。\"\n              ]\n            },\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### from the \\\"Server Application Keys Calc\\\" step\\n$ key=01f78623f17e3edcc09e944027ba3218d57c8e0db93cd3ac419309274700ac27\\n$ iv=196a750b0c5049c0cc51a541\\n### from this record\\n$ recdata=17030300ea\\n$ authtag=c0b96ad383afbd8dfc86f8087c1f7dc8\\n$ recordnum=1\\n### may need to add -I and -L flags for include and lib dirs\\n$ cc -o aes_256_gcm_decrypt aes_256_gcm_decrypt.c -lssl -lcrypto\\n$ echo \\\"38 ad fb 1d 01 fd 95 a6 03 85 e8 bb f1 fd 8d cb 46 70\\n  ... snip ...\\n  b4 0a 1e f1 85 74 ef\\\" | xxd -r -p > /tmp/msg5\\n$ cat /tmp/msg5 \\\\\\n  | ./aes_256_gcm_decrypt $iv $recordnum $key $recdata $authtag \\\\\\n  | hexdump -C\\n\\n00000000  04 00 00 d5 00 00 1c 20  00 00 00 00 08 00 00 00  |....... ........|\\n00000010  00 00 00 00 01 00 c0 41  42 43 44 45 46 47 48 49  |.......ABCDEFGHI|\\n... snip ...\"\n              }\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"加密的数据载荷\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"04 00 00 d5 00 00 1c 20 00 00 00 00 08 00 00 00 00 00 00 00 01 00 c0 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 00 49 56 44 41 54 41 49 56 44 41 54 41 00 41 45 53 cb 11 9d 4d bd 2a 21 ec c2 26 a6 09 0e e8 ca 58 df 09 03 9b 35 96 f4 de 79 98 0e a3 25 d5 14 62 5c 0c 21 c5 0f 03 26 1d c4 2c e7 c5 97 0c 4c 01 16 06 fb 99 8a 86 c3 fa 30 e5 5e ea 91 f1 ff f3 18 fc 7b d5 88 31 bf 49 c8 8d 7b 59 05 91 a6 5c 7d e8 cf c6 77 46 8a 54 fd be c0 d8 53 be 20 21 c8 bb fc db e5 1f 5d 9a 0c 70 85 84 1a 01 e4 95 85 f6 8b 4a fe e1 d7 07 e2 cb b1 a0 b4 23 aa 7e 32 d5 60 7b d9 9d d4 db 3c 9a aa ed 43 d3 5d 26 b4 b1 c6 84 71 71 ea a0 7a 9b c8 cb f7 58 49 9a 00 00\"\n          },\n          [\"该载荷的解释详见下面的\\\"服务器端第二个会话记录单\\\"部分\"]\n        ],\n        [\n          \"记录类型\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"16\"\n          },\n          [\n            \"每一个伪装的 TLS 1.3 记录的最后一个字节都需要表明其真正的记录类型\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"16 - 类型为 0x16(22, handshake record)\"\n                }\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/TLS13/wrappedRecord9.json",
    "content": "[\n  \"为了减少连接被无法识别新 TLS 协议的中间件阻断的问题，加密的 TLS 1.3 记录都需要伪装成一个会话恢复成功的 TLS 1.2 会话记录(之所以不是伪装成握手记录，因为握手记录过长会使得兼容实现变得异常复杂)。\",\n  \"包装的实际记录内容将在下面单独列出。\",\n  {\n    \"Tag\": \"AnnotationToggler\"\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"记录头\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"17 03 03 00 15\"\n          },\n          [\n            \"此 TLS 1.3 记录被伪装为 TLS 1.2 的\\\"会话记录\\\"。因此内容会以会话记录的规格展示。\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"17 - 表示 TLS 记录类型 0x17(23, application data)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"03 03 - 协议版本 (3.3, 即 TLS 1.2)\"\n                },\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"00 15 - 紧接着的数组载荷长度 0x15(21) 字节\"\n                }\n              ]\n            }\n          ]\n        ],\n        [\n          \"加密的数据载荷\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"0c da 85 f1 44\"\n          },\n          [\"这个数据是用服务器端的会话时密钥加密的。\"]\n        ],\n        [\n          \"AEAD 鉴别标签\",\n          {\n            \"props\": {\n              \"className\": \"bytes\"\n            },\n            \"content\": \"7a e2 3f a6 6d 56 f4 c5 40 84 82 b1 b1 d4 c9 98\"\n          },\n          [\n            {\n              \"children\": [\n                \"这是 \",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://zhuanlan.zhihu.com/p/28566058\"\n                  },\n                  \"content\": \"AEAD 算法\"\n                },\n                \"的鉴别标签，用以确认加密数据和数据包头的完整性。它由加密算法产生，并由解密算法消耗。\"\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"data\": [\n        [\n          \"\",\n          {\n            \"props\": {\n              \"className\": \"decryption-header\"\n            },\n            \"content\": \"解密后的数据载荷\"\n          },\n          [\n            { \"Tag\": \"h4\", \"content\": \"解密\" },\n            \"数据被 \\\"服务器端生成会话密钥\\\" 步骤中产生的握手时密钥和握手时向量(IVs)加密。IVs 通过密钥和已经用密钥加密的记录长度进行异或操作生成。在例子中 IV 为 0。\",\n            \"数据包开头的 5 字节(记录头)还会作为解密过程解密成功时必须满足的认证条件。\",\n            {\n              \"children\": [\n                \"openssl 命令行工具还不支持 AEAD 算法加解密(AEAD ciphers)，你可以使用作者的命令行工具来\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://tls13.xargs.org/files/aes_256_gcm_decrypt.c\"\n                  },\n                  \"content\": \"解密\"\n                },\n                \"和\",\n                {\n                  \"Tag\": \"a\",\n                  \"props\": {\n                    \"href\": \"https://tls13.xargs.org/files/aes_256_gcm_encrypt.c\"\n                  },\n                  \"content\": \"加密\"\n                },\n                \"这些数据。\"\n              ]\n            },\n            {\n              \"Tag\": \"CodeSample\",\n              \"props\": {\n                \"code\": \"### from the \\\"Server Application Keys Calc\\\" step\\n$ key=01f78623f17e3edcc09e944027ba3218d57c8e0db93cd3ac419309274700ac27\\n$ iv=196a750b0c5049c0cc51a541\\n### from this record\\n$ recdata=1703030015\\n$ authtag=7ae23fa66d56f4c5408482b1b1d4c998\\n$ recordnum=2\\n### may need to add -I and -L flags for include and lib dirs\\n$ cc -o aes_256_gcm_decrypt aes_256_gcm_decrypt.c -lssl -lcrypto\\n$ echo \\\"0c da 85 f1 44\\\" | xxd -r -p > /tmp/msg4\\n$ cat /tmp/msg4 \\\\\\n  | ./aes_256_gcm_decrypt $iv $recordnum $key $recdata $authtag \\\\\\n  | hexdump -C\\n\\n00000000  70 6f 6e 67 17                                    |pong.|\"\n              }\n            }\n          ]\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"Annotations\",\n    \"props\": {\n      \"type\": \"record-data\",\n      \"data\": [\n        [\n          \"加密的数据载荷\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"70 6f 6e 67\"\n          },\n          [\"该载荷的解释详见下面的\\\"服务器端会话数据\\\"部分\"]\n        ],\n        [\n          \"记录类型\",\n          {\n            \"props\": {\n              \"className\": \"bytes encrypted\"\n            },\n            \"content\": \"17\"\n          },\n          [\n            \"每一个伪装的 TLS 1.3 记录的最后一个字节都需要表明其真正的记录类型\",\n            {\n              \"Tag\": \"ul\",\n              \"children\": [\n                {\n                  \"Tag\": \"li\",\n                  \"content\": \"17 - 类型为 0x17(23, application data)\"\n                }\n              ]\n            }\n          ]\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/X25519/handsOn.json",
    "content": "[\n  \"让我们用一些真实的数字来试一试 X25519 算法。这个计算器允许我们基点 P 上做标量乘法，P 点为曲线上 x=9 的点。我们讨论的计算仅需要 nP 的 x 坐标，而 y 坐标不影响计算且并不需要使用。\",\n  {\n    \"Tag\": \"Calculator\",\n    \"props\": {\n      \"type\": \"SecretKeyMultiplier\"\n    }\n  },\n  \"让我们假设一次密钥交换的情形。给 Alice 一个密钥，比如 \\\"0x7\\\"，然后用上面的计算器找到 Alice 的公钥。给 Bob 一个不同的密钥，比如 \\\"0x5\\\"，然后用计算器找到 Bob 的公钥。\",\n  {\n    \"Tag\": \"ul\",\n    \"children\": [\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"Alice 的密钥：\",\n          {\n            \"Tag\": \"Math\",\n            \"content\": \"k_a = 7\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"Alice 的公钥：\",\n          {\n            \"Tag\": \"Math\",\n            \"content\": \"k_aP (7P) = 0daf32e7ed8099122b2dfa4c1d8c4a20c0972a1538bf0575338aae0fe0841828\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"Bob 的密钥：\",\n          {\n            \"Tag\": \"Math\",\n            \"content\": \"k_b = 5\"\n          }\n        ]\n      },\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"Bob 的公钥：\",\n          {\n            \"Tag\": \"Math\",\n            \"content\": \"k_bP (5P) = 41b6ec3c50ee7af203c0026e5e079e7fa8cbc9bc581d49cb0d537d5778497c87\"\n          }\n        ]\n      }\n    ]\n  },\n  [\n    \"现在假设我们就是 Alice。将 Alice 的密钥(\",\n    {\n      \"Tag\": \"Math\",\n      \"content\": \"k_a\"\n    },\n    \")带入下面的计算器中，用它乘以 Bob 的公钥(\",\n    {\n      \"Tag\": \"Math\",\n      \"content\": \"k_bP\"\n    },\n    \")\"\n  ],\n  {\n    \"Tag\": \"ul\",\n    \"children\": [\n      {\n        \"Tag\": \"li\",\n        \"children\": [\n          \"共享的密文：\",\n          {\n            \"Tag\": \"Math\",\n            \"content\": \"k_ak_bP (35P) = 1b9b715c415e547a13ca98a9f561d54499cc7402a1098414eddcfaf83ffbe3f8\"\n          }\n        ]\n      }\n    ]\n  },\n  {\n    \"Tag\": \"Calculator\",\n    \"props\": {\n      \"type\": \"PublicKeyMultiplier\"\n    }\n  },\n  [\n    \"之后对 Bob 的数据依法炮制。将 Bob 的密钥(\",\n    {\n      \"Tag\": \"Math\",\n      \"content\": \"k_b\"\n    },\n    \")带入计算器，并用它乘以 Alice 的公钥(\",\n    {\n      \"Tag\": \"Math\",\n      \"content\": \"k_aP\"\n    },\n    \")。结果和上面的一致，因为 \",\n    {\n      \"Tag\": \"Math\",\n      \"content\": \"k_ak_bP = k_bk_aP = 35P\"\n    }\n  ],\n  \"由于窃听者无法看到 Alice 或 Bob 的秘钥，因此他们无法计算出相同的结果。\"\n]\n"
  },
  {
    "path": "src/X25519/index.js",
    "content": "const data = {\n  intro: {\n    title: \"图解 X25519 密钥交换算法\",\n    subtitle: \"\",\n  },\n  sections: [\n    {\n      type: \"RecOuter\",\n      tags: [\"server\", \"stem\"],\n      label: \"总览\",\n      eagerLoad: true,\n      json: () => import(\"./overview.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"server\", \"stem\"],\n      label: \"椭圆曲线\",\n      eagerLoad: true,\n      json: () => import(\"./mathOnTheCurve.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"server\", \"stem\"],\n      label: \"上手试一试\",\n      eagerLoad: true,\n      json: () => import(\"./handsOn.json\"),\n    },\n    {\n      type: \"RecOuter\",\n      tags: [\"server\", \"stem\"],\n      label: \"Q & A\",\n      eagerLoad: true,\n      json: () => import(\"./q&a.json\"),\n    },\n  ],\n  ending: {\n    mother: \"https://x25519.xargs.org/\",\n    references: [\n      {\n        title: \"各种椭圆曲线的比较\",\n        href: \"https://safecurves.cr.yp.to/\",\n      },\n      {\n        title: \"深入理解 X25519 (PDF, 中文)\",\n        href: \"https://crypto-in-action.github.io/intro-ed25519/190902-intro-x25519.pdf\",\n      },\n    ],\n  },\n};\n\nexport default data;\n"
  },
  {
    "path": "src/X25519/mathOnTheCurve.json",
    "content": "[\n  {\n    \"children\": [\n      \"这一节的内容是可选的。这一节仅大致描述了椭圆曲线的数学内容。对相关内容的深入的动画解释可以从\",\n      {\n        \"Tag\": \"a\",\n        \"props\": {\n          \"href\": \"https://curves.xargs.org/\"\n        },\n        \"content\": \"这个页面\"\n      },\n      \"上找到。\"\n    ]\n  },\n  {\n    \"Tag\": \"Flex\",\n    \"props\": {\n      \"RightProps\": {\n        \"className\": \"text-center\"\n      },\n      \"Right\": [\n        {\n          \"Tag\": \"img\",\n          \"props\": {\n            \"height\": \"250\",\n            \"width\": \"260\",\n            \"src\": \"https://x25519.xargs.org/images/add-points.png\",\n            \"alt\": \"Visual demonstration of point addition\"\n          }\n        },\n        {\n          \"Tag\": \"small\",\n          \"content\": \"**Point Addition: P+Q=R**\"\n        }\n      ]\n    },\n    \"children\": [\n      \"**基础运算**(对曲线上的点操作):\",\n      {\n        \"Tag\": \"ul\",\n        \"children\": [\n          {\n            \"Tag\": \"li\",\n            \"content\": \"几何加法: 作一条直线通过曲线上的点 P 和 Q，交于曲线上一点 -R。取该点的在曲线上的对称点，得 R。则称 P + Q = R\"\n          },\n          {\n            \"Tag\": \"li\",\n            \"children\": [\n              \"标量积: 对曲线上一个点的重复进行加法运算得到标量乘法:\",\n              {\n                \"Tag\": \"br\"\n              },\n              {\n                \"Tag\": \"Math\",\n                \"content\": \"2P=P+P (此时几何加法的直线在 P 点相切于椭圆曲线)\"\n              },\n              {\n                \"Tag\": \"br\"\n              },\n              {\n                \"Tag\": \"Math\",\n                \"content\": \"3P=P+P+P\"\n              },\n              {\n                \"Tag\": \"br\"\n              },\n              {\n                \"Tag\": \"Math\",\n                \"content\": \"6P=P+P+P+P+P+P\"\n              },\n              {\n                \"Tag\": \"br\"\n              },\n              \"...etc.\"\n            ]\n          },\n          {\n            \"Tag\": \"li\",\n            \"children\": [\n              \"加法满足结合律:\",\n              {\n                \"Tag\": \"br\"\n              },\n              {\n                \"Tag\": \"Math\",\n                \"content\": \"P+P+P+P = 3P+P = 2P+2P = 4P\"\n              }\n            ]\n          }\n        ]\n      }\n    ]\n  },\n  {\n    \"Tag\": \"Flex\",\n    \"props\": {\n      \"style\": {\n        \"marginTop\": 20\n      },\n      \"RightProps\": {\n        \"className\": \"text-center\",\n        \"style\": {\n          \"width\": 260\n        }\n      },\n      \"Right\": [\n        \"**例子** (p = 23)\",\n        {\n          \"Tag\": \"Math\",\n          \"content\": \"加法: 16+15 = 31mod23 = 8\"\n        },\n        {\n          \"Tag\": \"br\"\n        },\n        {\n          \"Tag\": \"Math\",\n          \"content\": \"减法: 8-13 = -5mod23 = 18\"\n        },\n        {\n          \"Tag\": \"br\"\n        },\n        {\n          \"Tag\": \"Math\",\n          \"content\": \"乘法: 4∗7 = 28mod23 = 5\"\n        },\n        {\n          \"Tag\": \"br\"\n        },\n        {\n          \"Tag\": \"Math\",\n          \"content\": \"乘法逆元: 7∗7^-1mod23 = 1 → 7∗10mod23=1; 7^-1 = 10\"\n        },\n        {\n          \"Tag\": \"br\"\n        },\n        {\n          \"Tag\": \"Math\",\n          \"content\": \"除法: 4/7 → 4∗7^-1mod23 → 4∗10mod23 = 17\"\n        }\n      ]\n    },\n    \"children\": [\n      \"**模运算**(计算上述的加法和乘法时需要):\",\n      {\n        \"Tag\": \"ul\",\n        \"children\": [\n          {\n            \"Tag\": \"li\",\n            \"children\": [\n              \"加法、减法和乘法都在素数 \",\n              {\n                \"Tag\": \"Math\",\n                \"content\": \"P = 2^255 - 19\"\n              },\n              \" 处\\\"循环\\\"取模(wrap around)。(25519的名字由来)\"\n            ]\n          },\n          {\n            \"Tag\": \"li\",\n            \"children\": [\n              \"除法使用乘法的逆代替:\",\n              {\n                \"Tag\": \"br\"\n              },\n              \"找到满足 \",\n              {\n                \"Tag\": \"Math\",\n                \"content\": \"x ∗ x^-1 ≡ 1 (mod p), x 和 p 互质\"\n              },\n              \" 关系的数 \",\n              {\n                \"Tag\": \"Math\",\n                \"content\": \"x^-1\"\n              },\n              \" 然后乘以它。\",\n              {\n                \"Tag\": \"br\"\n              },\n              \"译注：可由\",\n              {\n                \"Tag\": \"a\",\n                \"props\": {\n                  \"href\": \"https://zh.wikipedia.org/wiki/%E6%89%A9%E5%B1%95%E6%AC%A7%E5%87%A0%E9%87%8C%E5%BE%97%E7%AE%97%E6%B3%95\"\n                },\n                \"content\": \"扩展欧几里得算法\"\n              },\n              \"带入求出\"\n            ]\n          }\n        ]\n      }\n    ]\n  }\n]\n"
  },
  {
    "path": "src/X25519/overview.json",
    "content": "[\n  \"密钥交换(Key exchange)是一种技术，双方(为方便叙述假设为 Alice 和 Bob)可以在同一数字上达成一致，而窃听者却无法知道这个数字是什么。X25519 是密钥交换其中一种方法的名称。具体一点是：通过做如下的 Curve25519 椭圆曲线运算：\",\n  {\n    \"Tag\": \"div\",\n    \"props\": {\n      \"className\": \"text-center\"\n    },\n    \"children\": [\n      {\n        \"Tag\": \"Math\",\n        \"content\": \"y^2=x^3+486662x^2+x\"\n      }\n    ]\n  },\n  \"并假设密钥交换函数为：\",\n  {\n    \"Tag\": \"div\",\n    \"props\": {\n      \"className\": \"text-center\"\n    },\n    \"children\": [\n      {\n        \"Tag\": \"Math\",\n        \"content\": \"k_b∗(k_a∗P) = k_a∗(k_b∗P)\"\n      }\n    ]\n  },\n  \"其中：\",\n  {\n    \"Tag\": \"Table\",\n    \"props\": {\n      \"style\": { \"marginLeft\": \"2rem\" },\n      \"dataProps\": [{ \"style\": { \"textAlign\": \"right\" } }],\n      \"data\": [\n        [{ \"Tag\": \"Math\", \"content\": \"∗\" }, \":\", \"椭圆曲线运算\"],\n        [\n          {\n            \"Tag\": \"Math\",\n            \"content\": \"k_a\"\n          },\n          \":\",\n          \"Alice 的密钥 ~ 即一个 255bit(32byte) 的随机数\"\n        ],\n        [\n          {\n            \"Tag\": \"Math\",\n            \"content\": \"k_b\"\n          },\n          \":\",\n          \"Bob 的密钥 ~ 即一个 255bit(32byte) 的随机数\"\n        ],\n        [\n          {\n            \"Tag\": \"Math\",\n            \"content\": \"P\"\n          },\n          \":\",\n          [\n            \"椭圆曲线上\",\n            {\n              \"Tag\": \"Math\",\n              \"content\": \" x = 9 \"\n            },\n            \"的点\"\n          ]\n        ],\n        [\n          {\n            \"Tag\": \"Math\",\n            \"content\": \"k_a∗P\"\n          },\n          \":\",\n          \"Alice 的公钥\"\n        ],\n        [\n          {\n            \"Tag\": \"Math\",\n            \"content\": \"k_b∗P\"\n          },\n          \":\",\n          \"Bob 的公钥\"\n        ],\n        [\n          {\n            \"Tag\": \"Math\",\n            \"content\": \"k_a∗k_b∗P\"\n          },\n          \":\",\n          \"Alice 和 Bob 共享的密文\"\n        ]\n      ]\n    }\n  },\n  \"可以看出，只要 Alice 和 Bob 有了共享密文后，他们就可以以此为基础，构建足够可信的加密通讯。并且由于椭圆曲线的特性，Alice 和 Bob 可以保证连接完成后，对方不会假冒自己。\"\n]\n"
  },
  {
    "path": "src/X25519/q&a.json",
    "content": "[\n  {\n    \"Tag\": \"QA\",\n    \"props\": {\n      \"content\": [\n        [\"为什么我不能在 openssl 或其他工具中重现这些结果?\"],\n        [\n          \"在实际的 Curve25519 使用时，私钥会提前做一些细微修改，以此来避免某些特定的攻击(参见 Martin Kleppmann 关于实现 X25519 的论文\",\n          {\n            \"Tag\": \"a\",\n            \"props\": {\n              \"href\": \"https://martin.kleppmann.com/papers/curve25519.pdf\"\n            },\n            \"content\": \" 4.7 节 \\\"箝位\\\"(Clamping)\"\n          },\n          \")。因此，下面所列出的五个位(bit)会被强制箝箍到预先确定的值(可以在\\\"上手试一试\\\"章节中点击 \\\"Clamp\\\" 按钮以检查结果)：\",\n          {\n            \"Tag\": \"Table\",\n            \"props\": {\n              \"style\": { \"marginLeft\": \"2rem\", \"marginTop\": \"1rem\" },\n              \"headers\": [\"位 bit\", \"目标值\", \"缘由\"],\n              \"data\": [\n                [\n                  \"0, 1, 2\",\n                  \"0\",\n                  [\n                    \"避免\",\n                    {\n                      \"Tag\": \"a\",\n                      \"props\": {\n                        \"href\": \"https://www.cnblogs.com/chuaner/p/15495967.html\"\n                      },\n                      \"content\": \"小子群攻击(Small Subgroup Attack)\"\n                    }\n                  ]\n                ],\n                [\n                  \"254\",\n                  \"1\",\n                  [\n                    \"避免\",\n                    {\n                      \"Tag\": \"a\",\n                      \"props\": {\n                        \"href\": \"https://en.wikipedia.org/wiki/Timing_attack\"\n                      },\n                      \"content\": \"计时攻击(Timing leak Attack)\"\n                    }\n                  ]\n                ],\n                [\n                  \"255\",\n                  \"0\",\n                  [\"保证\", { \"Tag\": \"Math\", \"content\": \"n < 2^255\" }]\n                ]\n              ]\n            }\n          },\n          \"你可能会发现你实际的密钥对和在这里测试的不太一致。这是因为 X25519 的密钥对是以小端顺序存储和传输，但是为了易读性，这个页面会将它们显示为普通数字。翻转字节的顺序应该就能够匹配。\"\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"QA\",\n    \"props\": {\n      \"content\": [\n        [\"如果这些是 xy 曲线上的点，y 坐标在哪里?\"],\n        [\n          \"X25519 不需要每个点的 y 坐标。为了简单起见，我们并不计算它们。只使用 x 坐标也减少了公钥的长度，因为 y 坐标不是密钥的一部分。\",\n          \"在该曲线上的每一个 x 都对应着满足 \",\n          {\n            \"Tag\": \"Math\",\n            \"content\": \"sqrt(y^2)\"\n          },\n          \" 的两个 y。\",\n          {\n            \"Tag\": \"Calculator\",\n            \"props\": {\n              \"type\": \"YCoordinate\"\n            }\n          },\n          \"只有一半的 x 值在 Curve25519 上是有效的。回到我们的曲线方程，当且仅当表达式 \",\n          {\n            \"Tag\": \"Math\",\n            \"content\": \"y^2 = x^3 + 486662x^2 + x\"\n          },\n          \" 满足 \",\n          {\n            \"Tag\": \"Math\",\n            \"content\": \"sqrt(y^2) mod 2^255 - 19\"\n          },\n          \" 的结果存在时，才称 x 有效。\"\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"QA\",\n    \"props\": {\n      \"content\": [\n        [\"Curve25519 长什么样?\"],\n        [\n          \"在实数域下的 Curve25519 曲线:\",\n          {\n            \"Tag\": \"img\",\n            \"props\": {\n              \"src\": \"https://x25519.xargs.org/images/curve25519-real.png\",\n              \"fit\": \"true\"\n            }\n          },\n          {\n            \"Tag\": \"Math\",\n            \"props\": {\n              \"style\": {\n                \"textAlign\": \"center\",\n                \"display\": \"block\",\n                \"fontSize\": \"0.75em\",\n                \"marginBottom\": \"1em\"\n              }\n            },\n            \"content\": \"y^2 = x^3 + 486662x^2 + x\"\n          },\n          \"下图是计算曲线上\",\n          {\n            \"Tag\": \"Math\",\n            \"content\": \"𝔽_p (mod p)\"\n          },\n          \"第一个点(\",\n          {\n            \"Tag\": \"Math\",\n            \"content\": \"x ∈ [0, 2^255-19]\"\n          },\n          \")时的可能结果:\",\n          {\n            \"Tag\": \"img\",\n            \"props\": {\n              \"src\": \"https://x25519.xargs.org/images/curve25519-field.png\",\n              \"fit\": \"true\"\n            }\n          },\n          {\n            \"Tag\": \"Math\",\n            \"props\": {\n              \"style\": {\n                \"textAlign\": \"center\",\n                \"display\": \"block\",\n                \"fontSize\": \"0.75em\"\n              }\n            },\n            \"content\": \"y^2 = x^3 + 486662x^2 + x in 𝔽_p\"\n          }\n        ]\n      ]\n    }\n  },\n  {\n    \"Tag\": \"QA\",\n    \"props\": {\n      \"content\": [\n        [\"你能给我更多关于椭圆曲线运算的参考或细节吗?\"],\n        [\n          \"原作者推荐的内容(英文):\",\n          {\n            \"Tag\": \"ul\",\n            \"children\": [\n              {\n                \"Tag\": \"li\",\n                \"children\": [\n                  {\n                    \"Tag\": \"a\",\n                    \"props\": {\n                      \"href\": \"https://cr.yp.to/ecdh/curve25519-20060209.pdf\"\n                    },\n                    \"content\": \"Curve25519: new Diffie-Hellman speed records\"\n                  },\n                  \" - 解释曲线及其推导过程\"\n                ]\n              },\n              {\n                \"Tag\": \"li\",\n                \"children\": [\n                  {\n                    \"Tag\": \"a\",\n                    \"props\": {\n                      \"href\": \"https://curves.xargs.org/\"\n                    },\n                    \"content\": \"The Animated Elliptic Curve\"\n                  },\n                  \" - 通过动画探索椭圆曲线加密背后的概念\"\n                ]\n              },\n              {\n                \"Tag\": \"li\",\n                \"children\": [\n                  {\n                    \"Tag\": \"a\",\n                    \"props\": {\n                      \"href\": \"https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication\"\n                    },\n                    \"content\": \"Wikipedia: EC point multiplication\"\n                  },\n                  \" - 尤其是关于 Montgomery ladder 的部分\"\n                ]\n              },\n              {\n                \"Tag\": \"li\",\n                \"children\": [\n                  {\n                    \"Tag\": \"a\",\n                    \"props\": {\n                      \"href\": \"https://en.wikipedia.org/wiki/Montgomery_curve#Montgomery_arithmetic\"\n                    },\n                    \"content\": \"Wikipedia: Montgomery arithmetic\"\n                  },\n                  \" - 包括点的运算方程\"\n                ]\n              },\n              {\n                \"Tag\": \"li\",\n                \"children\": [\n                  {\n                    \"Tag\": \"a\",\n                    \"props\": {\n                      \"href\": \"https://datatracker.ietf.org/doc/html/rfc7748\"\n                    },\n                    \"content\": \"RFC 7748\"\n                  },\n                  \" - 在常数时间内的完成 X25519 with Montgomery ladder 的常用算法列表\"\n                ]\n              },\n              {\n                \"Tag\": \"li\",\n                \"children\": [\n                  {\n                    \"Tag\": \"a\",\n                    \"props\": {\n                      \"href\": \"https://martin.kleppmann.com/papers/curve25519.pdf\"\n                    },\n                    \"content\": \"PDF: Implementing Curve25519/X25519\"\n                  },\n                  \" - 前面提到过的 Martin Kleppmann 的论文\"\n                ]\n              },\n              {\n                \"Tag\": \"li\",\n                \"children\": [\n                  {\n                    \"Tag\": \"a\",\n                    \"props\": {\n                      \"href\": \"https://andrea.corbellini.name/2015/05/17/elliptic-curve-cryptography-a-gentle-introduction/\"\n                    },\n                    \"content\": \"Elliptic Curve Cryptography: a gentle introduction\"\n                  }\n                ]\n              }\n            ]\n          }\n        ]\n      ]\n    }\n  }\n]\n"
  },
  {
    "path": "src/common.css",
    "content": ":root {\n  --chunky-text: #777;\n  --server-bg: hsl(190, 60%, 80%);\n  --client-bg: hsl(142, 61%, 82%);\n  --server-bg-hover: hsl(288, 28%, 83%);\n  --client-bg-hover: hsl(288, 31%, 80%);\n\n  --prot-text: #555;\n  --prot-text-shadow: #555;\n\n  --sam-btn0: hsl(190, 30%, 45%);\n  --sam-btn9: hsl(190, 25%, 40%);\n  --sam-hov-btn0: hsl(190, 30%, 40%);\n  --sam-hov-btn9: hsl(190, 25%, 35%);\n  --sam-btn-shadow: hsl(180, 41%, 28%);\n  --sam-btn-inset: hsl(180, 45%, 40%);\n  --sam-btn-border: hsl(161, 10%, 37%);\n\n  --act-btn0: hsl(37, 96%, 54%);\n  --act-btn9: hsl(25, 91%, 54%);\n  --act-hov-btn0: hsl(32, 94%, 52%);\n  --act-hov-btn9: hsl(21, 88%, 51%);\n  --act-btn-shadow: hsl(25, 91%, 40%);\n  --act-btn-border: hsl(33, 90%, 45%);\n}\n\n/* remove focus rings for non-keyboard users */\nbody:not(.user-is-tabbing) button:focus,\nbody:not(.user-is-tabbing) input:focus,\nbody:not(.user-is-tabbing) select:focus,\nbody:not(.user-is-tabbing) textarea:focus {\n  outline: none;\n}\n\nbody {\n  /* printmode */\n  position: relative;\n}\n\n.container {\n  /* printmode */\n  overflow: auto;\n}\n\npre {\n  margin: 0.5em 0;\n  padding: 1em;\n  overflow: auto;\n  max-height: 30em;\n  background: #f4f3ef;\n}\n\npre code {\n  font-size: 1em;\n  text-align: left;\n  white-space: pre;\n  word-spacing: normal;\n  word-break: normal;\n  word-wrap: normal;\n  line-height: 1.5;\n  background-color: unset;\n  padding: 0;\n  margin: 0;\n}\n\ncode {\n  background-color: #e6eaed;\n  margin: 0 0.2em;\n  padding: 0.1em;\n  border-radius: 0.3em;\n  white-space: normal;\n  word-break: break-word;\n}\n\ntable {\n  border-spacing: 0;\n}\ntable thead th {\n  color: #495057;\n  min-width: 4rem;\n  text-align: left;\n  vertical-align: bottom;\n  border-bottom: 1px solid #aaa4;\n}\ntable tbody td {\n  border: 1px solid transparent;\n  padding: 0 0.25rem;\n}\n\nol,\nul,\ndl {\n  margin: 1rem 0;\n}\n\ndl {\n  list-style-type: circle;\n}\n\n@media (prefers-reduced-motion: no-preference) {\n  html {\n    scroll-behavior: smooth;\n  }\n}\n"
  },
  {
    "path": "src/context/slugger.js",
    "content": "import React from \"react\";\nimport { slug as githubSlug } from \"github-slugger\";\nimport { jump } from \"../utils\";\n\nconst SluggerContext = React.createContext();\n\nexport const SluggerContextProvider = ({ children }) => {\n  const [curSlug, setCurSlug] = React.useState(\"\");\n\n  React.useEffect(() => {\n    const href = window.location.hash.substring(1);\n    setCurSlug(window.decodeURI(href));\n  }, []);\n\n  return (\n    <SluggerContext.Provider\n      value={{\n        curSlug,\n        changeCurSlug: (slug) => {\n          setCurSlug(slug);\n\n          let href = window.location.href.replace(/#.*/, \"\");\n          if (slug) {\n            jump(`${href}#${slug}`);\n          } else {\n            jump(href);\n          }\n        },\n        getSlug: (text) => githubSlug(text),\n      }}\n    >\n      {children}\n    </SluggerContext.Provider>\n  );\n};\n\nexport const useSluggerContext = () => {\n  return React.useContext(SluggerContext);\n};\n"
  },
  {
    "path": "src/hard-encoded.css",
    "content": "code.longboi {\n  word-break: break-all;\n}\n.ind1 {\n  padding-left: 1em;\n}\n.ind2 {\n  padding-left: 2em;\n}\n.decryption-header::before {\n  content: \"⇩ \";\n  font-weight: bold;\n  color: var(--chunky-text);\n}\n.decryption-header::after {\n  content: \" ⇩\";\n  font-weight: bold;\n  color: var(--chunky-text);\n}\n.decryption-header {\n  margin: 1em 0;\n  text-align: center;\n  display: block;\n}\n.encrypted.bytes {\n  text-shadow: 1px 1px 0 red;\n}\n.decrypted.bytes {\n  text-shadow: 1px 1px 0 green;\n}\n.bytes:hover {\n  background-color: var(--client-bg-hover);\n}\n.bytes:hover {\n  background-color: var(--server-bg-hover);\n}\n.bytes {\n  padding: 0.2em 0;\n  position: relative;\n}\n.bytes::after {\n  content: \" \";\n  width: 1ex;\n  display: inline;\n}\n.bytes.protected {\n  color: var(--prot-text);\n  text-shadow: 1px 1px 3px var(--prot-text-shadow);\n}\n.bytes.unprotected {\n  display: none;\n}\n.bytes.protected {\n  display: inline;\n}\n.bytes.unprotected.hp-disabled {\n  display: inline;\n}\n.bytes.protected.hp-disabled {\n  display: none;\n}\n.text-center {\n  text-align: center;\n}"
  },
  {
    "path": "src/illustrated.css",
    "content": "/***** processblock (not used in quic, delete?) *****/\n\nprocessblock {\n  display: block;\n  position: relative;\n  margin: 1em;\n  padding-left: 1em;\n  overflow: hidden;\n  transition: all 0.3s;\n  cursor: pointer;\n  font-size: 0.8em;\n}\n\nprocessblock:before {\n  content: \" \";\n  width: 100%;\n  height: 100%;\n  position: absolute;\n  left: 0;\n  top: 0;\n  pointer-events: none;\n}\n\nprocessblock pre {\n  margin: 0;\n}\n\n/***** header protection toggle button (TODO) *****/\nbutton.hp-toggle {\n  margin-bottom: 1em;\n\n  display: inline-block;\n  outline: none;\n  cursor: pointer;\n  text-align: center;\n  text-decoration: none;\n  font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n  font-size: 1em;\n  font-weight: bold;\n  padding: 0.4em 1.7em 0.45em;\n  text-shadow: 0 1px 0 var(--act-btn-shadow);\n  border-radius: 0.5em;\n  box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.3);\n\n  color: #fff;\n  border: solid 1px var(--act-btn-border);\n  background: linear-gradient(180deg, var(--act-btn0) 1%, var(--act-btn9) 100%);\n}\nbutton.hp-toggle:hover {\n  text-decoration: none;\n  background: linear-gradient(\n    180deg,\n    var(--act-hov-btn0) 1%,\n    var(--act-hov-btn9) 100%\n  );\n}\nbutton.hp-toggle:active {\n  position: relative;\n  top: 1px;\n}\n\n/***** print mode *****/\n.print-mode {\n  cursor: pointer;\n  position: absolute;\n  padding: 5px;\n  bottom: 0;\n  right: 0;\n}\n\n@media (max-width: 600px) {\n  .print-mode {\n    display: none;\n  }\n}\n"
  },
  {
    "path": "src/index.js",
    "content": "import { createRoot } from \"react-dom/client\";\nimport App from \"./App\";\nimport { Provider } from \"react-wrap-balancer\";\n\nconst container = document.getElementById(\"app\");\nconst root = createRoot(container);\nroot.render(\n  <Provider>\n    <App />\n  </Provider>\n);\n"
  },
  {
    "path": "src/router.js",
    "content": "export const base = \"/illustrate\";\n\nexport const routers = [\n  {\n    label: \"QUIC\",\n    href: \"/quic\",\n    title: \"图解 QUIC\",\n    json: () => import(\"./QUIC/index.js\"),\n  },\n  {\n    label: \"DTLS\",\n    href: \"/dtls\",\n    title: \"图解 DTLS\",\n    json: () => import(\"./DTLS/index.js\"),\n  },\n  {\n    label: \"TLS 1.3\",\n    href: \"/tls13\",\n    title: \"图解 TLS 1.3\",\n    json: () => import(\"./TLS13/index.js\"),\n  },\n  {\n    label: \"TLS 1.2\",\n    href: \"/tls12\",\n    title: \"图解 TLS 1.2\",\n    json: () => import(\"./TLS12/index.js\"),\n  },\n  {\n    label: \"X25519\",\n    href: \"/x25519\",\n    title: \"图解 X25519\",\n    json: () => import(\"./X25519/index.js\"),\n  },\n].map((rt) => ({ ...rt, href: base + rt.href }));\n"
  },
  {
    "path": "src/utils.js",
    "content": "export const jump = (href) => {\n  if (globalThis.location.pathname.startsWith(href)) return;\n\n  globalThis.history.pushState({}, \"\", globalThis.location.pathname);\n  globalThis.history.replaceState({}, \"\", href);\n};\n"
  }
]