{"id":15561995,"url":"https://github.com/thekuwayama/echspec","last_synced_at":"2026-03-09T03:31:15.744Z","repository":{"id":218759662,"uuid":"738489606","full_name":"thekuwayama/echspec","owner":"thekuwayama","description":"A conformance testing tool for ECH implementation.","archived":false,"fork":false,"pushed_at":"2025-01-12T19:07:49.000Z","size":911,"stargazers_count":2,"open_issues_count":3,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-04-23T22:55:11.297Z","etag":null,"topics":["cli","ech","tls13"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/thekuwayama.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-01-03T10:48:36.000Z","updated_at":"2025-01-12T19:07:53.000Z","dependencies_parsed_at":"2024-03-04T10:26:44.929Z","dependency_job_id":"a43ce731-2059-4403-91e0-ad62b94ea887","html_url":"https://github.com/thekuwayama/echspec","commit_stats":{"total_commits":146,"total_committers":1,"mean_commits":146.0,"dds":0.0,"last_synced_commit":"87498ad66d11c320c7ea93ef3ccf384d492dddfb"},"previous_names":["thekuwayama/echspec"],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thekuwayama%2Fechspec","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thekuwayama%2Fechspec/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thekuwayama%2Fechspec/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thekuwayama%2Fechspec/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thekuwayama","download_url":"https://codeload.github.com/thekuwayama/echspec/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250528701,"owners_count":21445511,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cli","ech","tls13"],"created_at":"2024-10-02T16:10:54.117Z","updated_at":"2026-03-09T03:31:15.733Z","avatar_url":"https://github.com/thekuwayama.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# echspec\n\n[![Gem Version](https://badge.fury.io/rb/echspec.svg)](https://badge.fury.io/rb/echspec)\n[![Actions Status](https://github.com/thekuwayama/echspec/actions/workflows/ci.yml/badge.svg)](https://github.com/thekuwayama/echspec/actions/workflows/ci.yml)\n[![license](https://img.shields.io/badge/license-MIT-brightgreen.svg)](LICENSE.txt)\n\n`echspec` is a conformance testing tool for ECH implementation.\n\n![echspec demo](docs/echspec-demo.png)\n\n- https://datatracker.ietf.org/doc/html/rfc9849\n\n\n## Installation\n\nThe gem is available at [rubygems.org](https://rubygems.org/gems/echspec). You can install it the following:\n\n```sh-session\n$ gem install echspec\n```\n\n\n## Usage\n\n```sh-session\n$ echspec --help\nUsage: echspec [OPTIONS] \u003cHOSTNAME\u003e\n    -f, --file FILE                  path to ECHConfigs PEM file       (default resolve ECHConfigs via DNS)\n    -p, --port VALUE                 server port number                (default 443)\n    -n, --not-force-compliant-hpke   not force compliant ECHConfig HPKE cipher suite\n    -v, --verbose                    verbose mode; prints message stack if raised an error\n    -s, --sections SECTIONS          sections to test; by the default, test all sections\n```\n\nYou can run it the following:\n\n```sh-session\n$ echspec research.cloudflare.com\nTLS Encrypted Client Hello Server\n        ✔ MUST implement the following HPKE cipher suite: KEM: DHKEM(X25519, HKDF-SHA256), KDF: HKDF-SHA256 and AEAD: AES-128-GCM. [9]\n        ✔ MUST abort with an \"illegal_parameter\" alert, if EncodedClientHelloInner is padded with non-zero values. [5.1-9]\n        ✔ MUST abort with an \"illegal_parameter\" alert, if any referenced extension is missing in ClientHelloOuter. [5.1-10]\n        ✔ MUST abort with an \"illegal_parameter\" alert, if any extension is referenced in OuterExtensions more than once. [5.1-10]\n        ✔ MUST abort with an \"illegal_parameter\" alert, if \"encrypted_client_hello\" is referenced in OuterExtensions. [5.1-10]\n        ✔ MUST abort with an \"illegal_parameter\" alert, if the extensions in ClientHelloOuter corresponding to those in OuterExtensions do not occur in the same order. [5.1-10]\n        ✔ MUST abort with an \"illegal_parameter\" alert, if ECHClientHello.type is not a valid ECHClientHelloType in ClientHelloInner. [7-5]\n        x MUST abort with an \"illegal_parameter\" alert, if ECHClientHello.type is not a valid ECHClientHelloType in ClientHelloOuter. [7-5]\n        ✔ MUST abort with an \"illegal_parameter\" alert, if ClientHelloInner offers TLS 1.2 or below. [7.1-11]\n        ✔ MUST include the \"encrypted_client_hello\" extension in its EncryptedExtensions with the \"retry_configs\" field set to one or more ECHConfig. [7.1-14.2.1]\n        ✔ MUST abort with a \"missing_extension\" alert, if 2nd ClientHelloOuter does not contains the \"encrypted_client_hello\" extension. [7.1.1-2]\n        ✔ MUST abort with an \"illegal_parameter\" alert, if 2nd ClientHelloOuter \"encrypted_client_hello\" enc is empty. [7.1.1-2]\n        ✔ MUST abort with a \"decrypt_error\" alert, if fails to decrypt 2nd ClientHelloOuter. [7.1.1-5]\n\nFailures:\n\n        1) MUST abort with an \"illegal_parameter\" alert, if ECHClientHello.type is not a valid ECHClientHelloType in ClientHelloOuter. [7-5]\n                https://datatracker.ietf.org/doc/html/rfc9849#section-7-5\n                did not send expected alert: illegal_parameter\n\n1 failure\n```\n\nBy default, `echspec` retrieves ECHConfigs via HTTPS records. By using the `-f, --file FILE` option, you can specify an ECHConfig pem file. If you need to test the server on localhost, you can run it the following:\n\n```sh-session\n$ echspec -f fixtures/echconfigs.pem -p 4433 localhost\n```\n\nBy default, `echspec` uses the following HPKE cipher suite\n\n- KEM\n  - DHKEM(X25519, HKDF-SHA256)\n- KDF\n  - HKDF-SHA256\n- AEAD\n  - AES-128-GCM\n\nUsing the `-n` or `--not-force-compliant-hpke`, you can not enforce the HPKE cipher suite.\n\n```sh-session\n$ echspec -f fixtures/echconfigs.pem -p 4433 -n localhost\n```\n\nIf you specify the SECTIONS, you can run only SECTIONS the following:\n\n```sh-session\n$ echspec -f fixtures/echconfigs.pem -p 4433 -n -s 7.1.1-2,7.1.1-5 localhost\nTLS Encrypted Client Hello Server\n        ✔ MUST abort with a \"missing_extension\" alert, if 2nd ClientHelloOuter does not contains the \"encrypted_client_hello\" extension. [7.1.1-2]\n        ✔ MUST abort with an \"illegal_parameter\" alert, if 2nd ClientHelloOuter \"encrypted_client_hello\" enc is empty. [7.1.1-2]\n        ✔ MUST abort with a \"decrypt_error\" alert, if fails to decrypt 2nd ClientHelloOuter. [7.1.1-5]\n```\n\nUsing the `-v` or `--verbose` option provides a message stack if an error occurs. The message stack is formatted as JSON.\n\n```sh-session\n$ echspec -s 7-5 -v research.cloudflare.com 2\u003e\u00261 \u003e /dev/null | jq .\n````\n\n\u003cdetails\u003e\n\n```json\n{\n  \"Alert\": {\n    \"level\": \"0x02\",\n    \"description\": \"0x32\"\n  },\n  \"ClientHello\": {\n    \"msg_type\": \"0x01\",\n    \"legacy_version\": \"0x0303\",\n    \"random\": \"0x29142f95eb55066cdb496267d3154628685ad1dbbe5b877e66eda4af20df2c69\",\n    \"legacy_session_id\": \"0x9c557bc381f62d73ba3b99629f8fe6e347787be66c56fa99db4f6bc6fd06fd5f\",\n    \"cipher_suites\": [\n      \"0x1302\",\n      \"0x1303\",\n      \"0x1301\"\n    ],\n    \"legacy_compression_methods\": [\n      \"0x00\"\n    ],\n    \"extensions\": {\n      \"0x0000\": {\n        \"extension_type\": \"0x0000\",\n        \"server_name\": \"0x636c6f7564666c6172652d6563682e636f6d\"\n      },\n      \"0x002b\": {\n        \"extension_type\": \"0x002b\",\n        \"msg_type\": \"0x01\",\n        \"versions\": [\n          \"0x0304\"\n        ]\n      },\n      \"0x000d\": {\n        \"extension_type\": \"0x000d\",\n        \"supported_signature_algorithms\": [\n          \"0x0403\",\n          \"0x0503\",\n          \"0x0603\",\n          \"0x0804\",\n          \"0x0805\",\n          \"0x0806\",\n          \"0x0401\",\n          \"0x0501\",\n          \"0x0601\"\n        ]\n      },\n      \"0x000a\": {\n        \"extension_type\": \"0x000a\",\n        \"named_group_list\": [\n          \"0x0017\",\n          \"0x0018\",\n          \"0x0019\"\n        ]\n      },\n      \"0x0033\": {\n        \"extension_type\": \"0x0033\",\n        \"msg_type\": \"0x01\",\n        \"key_share_entry\": [\n          {\n            \"group\": \"0x0017\",\n            \"key_exchange\": \"0x0421747aa4234dbefc61906c165b8f1050b3346bb67f2c4ad8af9f58135888354a631b9b5c68f8ec1b6d6e67485a971bd3ff0ba6ab46da08f1524d7a4a3578c110\"\n          },\n          {\n            \"group\": \"0x0018\",\n            \"key_exchange\": \"0x046d374a61f2b75717b28b47b3fa227b51e09bb7a4ce0ea24b3cd3c946e9d4da2d54186b76812a74eb53adaa8a4451573201613b2f6301c05efb79bb6a782e88150dc3ac14aad702b8268aa8f3435d1a404166133216467aeb933247f994035fa0\"\n          },\n          {\n            \"group\": \"0x0019\",\n            \"key_exchange\": \"0x0401c90f95f64dc1e8d7d3f29f8a9103308835a2b56f5581ffc2837a1f9fcf6b23db824f01d6e4efccec78d6858aaad2fde8f02c2acc66a463c14b78e5e323b23d1b1301725b4f757811b7a31b4b5ee8016891ecb55b3fb997dd5738f6e610388f9bc85a4515efd04ab1d456e56d4e6e4a3a1e58a58ed6cf86f5dc9d7ace20afcd0af6b23b\"\n          }\n        ]\n      },\n      \"0xfe0d\": {\n        \"extension_type\": \"0xfe0d\",\n        \"type\": \"0x02\",\n        \"cipher_suite\": {\n          \"kdf_id\": {\n            \"uint16\": 1\n          },\n          \"aead_id\": {\n            \"uint16\": 1\n          }\n        },\n        \"config_id\": 9,\n        \"enc\": \"0xe951022abd85cc53f16c92378ba736ea9d28b3a4106a6f2865323ea04fdf4075\",\n        \"payload\": \"0xbeef20fb3063fd807f6327213a5c5e37a0d355fc67c4c3a10362f947c7b72f06514db0e6bf470efb87d0db30669331caa3723441fb2850190851a9179b8b42e1e7f78bd0d281daf631872d9f7008624e24baef48ac8e18951fed9ad7d80def0b1bec492d5c5c2c532c4c8ec32b6dd3a34522c70c64e21ca639c7f54d2c3ad72c65ffda0dc1f82df5abf0857586eb17f0df08a15770a91d5c3640cff59b49b0fb3d19cf77137cd27416470e19db21519751c3d0ae417a62641903731408b4b81e008fff641d22a3300f92c8b9330d260055677c545a8d561076ecc3a4de5639120f6c67736df87a464ca221373c3bbe8e74dfe795eb43593f1d03d8668d49c4a9a73ed6d3264d7126bcfc93a975c8848170c57322b7c1bef210235bc79fd58bb4ff202d9e75bec3d2251a2429e11e5c7876edcca0685d52e1b99f51b46d0e723975a7c9d894e5674ef8debf380e799d75bff93c13e4917296242e2cb7b99bfcb0fc7cd8f98f414ceb0ef3a63fa29efc722194b91beb354efff5215b1804b9c555d4aad36a85e6eb3536b0b66fea50c9b055fa8441a36fc61a2228e0d4cb3c5ecb1662c641adf30a70c3b1104fb9b0f9b2c3130dc52939e9695a470774bee6dabc2691d06f870d01fe249199d831258583\"\n      }\n    }\n  },\n  \"ClientHelloInner\": {\n    \"msg_type\": \"0x01\",\n    \"legacy_version\": \"0x0303\",\n    \"random\": \"0x99f21aebeff5838b88011e581ade4de4eb334e5528efaf223dfa33c55d7bc20b\",\n    \"legacy_session_id\": \"0x9c557bc381f62d73ba3b99629f8fe6e347787be66c56fa99db4f6bc6fd06fd5f\",\n    \"cipher_suites\": [\n      \"0x1302\",\n      \"0x1303\",\n      \"0x1301\"\n    ],\n    \"legacy_compression_methods\": [\n      \"0x00\"\n    ],\n    \"extensions\": {\n      \"0x0000\": {\n        \"extension_type\": \"0x0000\",\n        \"server_name\": \"0x72657365617263682e636c6f7564666c6172652e636f6d\"\n      },\n      \"0x002b\": {\n        \"extension_type\": \"0x002b\",\n        \"msg_type\": \"0x01\",\n        \"versions\": [\n          \"0x0304\"\n        ]\n      },\n      \"0x000d\": {\n        \"extension_type\": \"0x000d\",\n        \"supported_signature_algorithms\": [\n          \"0x0403\",\n          \"0x0503\",\n          \"0x0603\",\n          \"0x0804\",\n          \"0x0805\",\n          \"0x0806\",\n          \"0x0401\",\n          \"0x0501\",\n          \"0x0601\"\n        ]\n      },\n      \"0x000a\": {\n        \"extension_type\": \"0x000a\",\n        \"named_group_list\": [\n          \"0x0017\",\n          \"0x0018\",\n          \"0x0019\"\n        ]\n      },\n      \"0x0033\": {\n        \"extension_type\": \"0x0033\",\n        \"msg_type\": \"0x01\",\n        \"key_share_entry\": [\n          {\n            \"group\": \"0x0017\",\n            \"key_exchange\": \"0x0421747aa4234dbefc61906c165b8f1050b3346bb67f2c4ad8af9f58135888354a631b9b5c68f8ec1b6d6e67485a971bd3ff0ba6ab46da08f1524d7a4a3578c110\"\n          },\n          {\n            \"group\": \"0x0018\",\n            \"key_exchange\": \"0x046d374a61f2b75717b28b47b3fa227b51e09bb7a4ce0ea24b3cd3c946e9d4da2d54186b76812a74eb53adaa8a4451573201613b2f6301c05efb79bb6a782e88150dc3ac14aad702b8268aa8f3435d1a404166133216467aeb933247f994035fa0\"\n          },\n          {\n            \"group\": \"0x0019\",\n            \"key_exchange\": \"0x0401c90f95f64dc1e8d7d3f29f8a9103308835a2b56f5581ffc2837a1f9fcf6b23db824f01d6e4efccec78d6858aaad2fde8f02c2acc66a463c14b78e5e323b23d1b1301725b4f757811b7a31b4b5ee8016891ecb55b3fb997dd5738f6e610388f9bc85a4515efd04ab1d456e56d4e6e4a3a1e58a58ed6cf86f5dc9d7ace20afcd0af6b23b\"\n          }\n        ]\n      },\n      \"0xfe0d\": {\n        \"extension_type\": \"0xfe0d\",\n        \"type\": \"0x01\",\n        \"cipher_suite\": null,\n        \"config_id\": null,\n        \"enc\": null,\n        \"payload\": null\n      }\n    }\n  }\n}\n```\n\n\u003c/details\u003e\n\n\n## Note\n\n`echspec` is inspired by:\n\n- https://github.com/summerwind/h2spec\n\n\n## License\n\n`echspec` is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthekuwayama%2Fechspec","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthekuwayama%2Fechspec","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthekuwayama%2Fechspec/lists"}