echspec 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/workflows/ci.yml +30 -0
- data/.gitignore +17 -0
- data/.rubocop.yml +36 -0
- data/.ruby-version +1 -0
- data/Gemfile +13 -0
- data/LICENSE.txt +21 -0
- data/README.md +272 -0
- data/Rakefile +8 -0
- data/docs/echspec-demo.png +0 -0
- data/echspec.gemspec +26 -0
- data/exe/echspec +7 -0
- data/fixtures/echconfigs.pem +7 -0
- data/fixtures/server.crt +20 -0
- data/fixtures/server.key +27 -0
- data/lib/echspec/cli.rb +97 -0
- data/lib/echspec/error.rb +9 -0
- data/lib/echspec/log.rb +85 -0
- data/lib/echspec/result.rb +26 -0
- data/lib/echspec/spec/5.1-10.rb +222 -0
- data/lib/echspec/spec/5.1-9.rb +108 -0
- data/lib/echspec/spec/7-5.rb +242 -0
- data/lib/echspec/spec/7.1-11.rb +93 -0
- data/lib/echspec/spec/7.1-14.2.1.rb +133 -0
- data/lib/echspec/spec/7.1.1-2.rb +142 -0
- data/lib/echspec/spec/7.1.1-5.rb +83 -0
- data/lib/echspec/spec/9.rb +113 -0
- data/lib/echspec/spec.rb +170 -0
- data/lib/echspec/spec_case.rb +10 -0
- data/lib/echspec/spec_group.rb +10 -0
- data/lib/echspec/tls13_client.rb +167 -0
- data/lib/echspec/utils.rb +21 -0
- data/lib/echspec/version.rb +3 -0
- data/lib/echspec.rb +16 -0
- data/spec/9_spec.rb +13 -0
- data/spec/log_spec.rb +58 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/with_socket_spec.rb +77 -0
- metadata +141 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 942fd582bb741ca465bb5255b458ef3cbf3ff8cd09f20b45957df8c64d32f55a
|
4
|
+
data.tar.gz: e5748a921bc1ab45c053884690fe0e7f5718b8847c6376f87100f22173f8dd9c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a9597e9295d2b6a8d2b836a59a9ec1013edb42b2fc605e2dacd9ba274510ccb9982f1971d6aa4d092f1896da19a4d2317386d72f9d690639049e288e96fe1fe5
|
7
|
+
data.tar.gz: d104c73ca8124af91cbaeb172757d8353e3a328668c89a9d1b66f01281ac6d8e5bacd298ac96b05fa27d1b70dadceb9a216c8b873db9607286a7e0767f304c58
|
@@ -0,0 +1,30 @@
|
|
1
|
+
name: lint & test
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches:
|
6
|
+
- main
|
7
|
+
pull_request:
|
8
|
+
branches:
|
9
|
+
- '*'
|
10
|
+
|
11
|
+
jobs:
|
12
|
+
ci:
|
13
|
+
runs-on: ubuntu-latest
|
14
|
+
strategy:
|
15
|
+
matrix:
|
16
|
+
ruby-version: ['3.2', '3.3', '3.4']
|
17
|
+
steps:
|
18
|
+
- uses: actions/checkout@v4
|
19
|
+
- name: Set up Ruby
|
20
|
+
uses: ruby/setup-ruby@v1
|
21
|
+
with:
|
22
|
+
ruby-version: ${{ matrix.ruby-version }}
|
23
|
+
- name: Install dependencies
|
24
|
+
run: |
|
25
|
+
gem --version
|
26
|
+
gem install bundler
|
27
|
+
bundle --version
|
28
|
+
bundle install
|
29
|
+
- name: Run rubocop & rspec
|
30
|
+
run: bundle exec rake
|
data/.gitignore
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
AllCops:
|
2
|
+
TargetRubyVersion: 3.2
|
3
|
+
|
4
|
+
Layout/LineLength:
|
5
|
+
Max: 200
|
6
|
+
|
7
|
+
Metrics/AbcSize:
|
8
|
+
Max: 30
|
9
|
+
|
10
|
+
Metrics/BlockLength:
|
11
|
+
Exclude:
|
12
|
+
- 'spec/*.rb'
|
13
|
+
|
14
|
+
Metrics/ClassLength:
|
15
|
+
Max: 200
|
16
|
+
|
17
|
+
Metrics/MethodLength:
|
18
|
+
Max: 30
|
19
|
+
|
20
|
+
Naming/MethodParameterName:
|
21
|
+
MinNameLength: 1
|
22
|
+
|
23
|
+
Naming/FileName:
|
24
|
+
Enabled: false
|
25
|
+
|
26
|
+
Naming/ClassAndModuleCamelCase:
|
27
|
+
Enabled: false
|
28
|
+
|
29
|
+
Semicolon:
|
30
|
+
AllowAsExpressionSeparator: true
|
31
|
+
|
32
|
+
Style/Documentation:
|
33
|
+
Enabled: false
|
34
|
+
|
35
|
+
Style/FrozenStringLiteralComment:
|
36
|
+
Enabled: false
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
3.3.6
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2024 Tomoya Kuwayama
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,272 @@
|
|
1
|
+
# echspec
|
2
|
+
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/echspec.svg)](https://badge.fury.io/rb/echspec)
|
4
|
+
[![Actions Status](https://github.com/thekuwayama/echspec/actions/workflows/ci.yml/badge.svg)](https://github.com/thekuwayama/echspec/actions/workflows/ci.yml)
|
5
|
+
[![license](https://img.shields.io/badge/license-MIT-brightgreen.svg)](LICENSE.txt)
|
6
|
+
|
7
|
+
`echspec` is a conformance testing tool for ECH implementation.
|
8
|
+
|
9
|
+
![echspec demo](docs/echspec-demo.png)
|
10
|
+
|
11
|
+
- https://datatracker.ietf.org/doc/html/draft-ietf-tls-esni-22
|
12
|
+
|
13
|
+
|
14
|
+
## Initial Setup
|
15
|
+
|
16
|
+
The gem is available at [rubygems.org](https://rubygems.org/gems/echspec). You can install it the following:
|
17
|
+
|
18
|
+
```sh-session
|
19
|
+
$ gem install echspec
|
20
|
+
```
|
21
|
+
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
```sh-session
|
26
|
+
$ echspec --help
|
27
|
+
Usage: echspec [OPTIONS] <HOSTNAME>
|
28
|
+
-f, --file FILE path to ECHConfigs PEM file (default resolve ECHConfigs via DNS)
|
29
|
+
-p, --port VALUE server port number (default 443)
|
30
|
+
-n, --not-force-compliant-hpke not force compliant ECHConfig HPKE cipher suite
|
31
|
+
-v, --verbose verbose mode; prints message stack if raised an error
|
32
|
+
-s, --sections SECTIONS sections to test; by the default, test all sections
|
33
|
+
```
|
34
|
+
|
35
|
+
You can run it the following:
|
36
|
+
|
37
|
+
```sh-session
|
38
|
+
$ echspec research.cloudflare.com
|
39
|
+
TLS Encrypted Client Hello Server
|
40
|
+
✔ MUST implement the following HPKE cipher suite: KEM: DHKEM(X25519, HKDF-SHA256), KDF: HKDF-SHA256 and AEAD: AES-128-GCM. [9]
|
41
|
+
✔ MUST abort with an "illegal_parameter" alert, if EncodedClientHelloInner is padded with non-zero values. [5.1-9]
|
42
|
+
✔ MUST abort with an "illegal_parameter" alert, if any referenced extension is missing in ClientHelloOuter. [5.1-10]
|
43
|
+
✔ MUST abort with an "illegal_parameter" alert, if any extension is referenced in OuterExtensions more than once. [5.1-10]
|
44
|
+
✔ MUST abort with an "illegal_parameter" alert, if "encrypted_client_hello" is referenced in OuterExtensions. [5.1-10]
|
45
|
+
✔ 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]
|
46
|
+
✔ MUST abort with an "illegal_parameter" alert, if ECHClientHello.type is not a valid ECHClientHelloType in ClientHelloInner. [7-5]
|
47
|
+
✔ MUST abort with an "illegal_parameter" alert, if ECHClientHello.type is not a valid ECHClientHelloType in ClientHelloOuter. [7-5]
|
48
|
+
✔ MUST abort with an "illegal_parameter" alert, if ClientHelloInner offers TLS 1.2 or below. [7.1-11]
|
49
|
+
✔ 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]
|
50
|
+
✔ MUST abort with a "missing_extension" alert, if 2nd ClientHelloOuter does not contains the "encrypted_client_hello" extension. [7.1.1-2]
|
51
|
+
✔ MUST abort with an "illegal_parameter" alert, if 2nd ClientHelloOuter "encrypted_client_hello" enc is empty. [7.1.1-2]
|
52
|
+
✔ MUST abort with a "decrypt_error" alert, if fails to decrypt 2nd ClientHelloOuter. [7.1.1-5]
|
53
|
+
```
|
54
|
+
|
55
|
+
By 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:
|
56
|
+
|
57
|
+
```sh-session
|
58
|
+
$ echspec -f fixtures/echconfigs.pem -p 4433 localhost
|
59
|
+
```
|
60
|
+
|
61
|
+
By default, `echspec` uses the following HPKE cipher suite
|
62
|
+
|
63
|
+
- KEM
|
64
|
+
- DHKEM(X25519, HKDF-SHA256)
|
65
|
+
- KDF
|
66
|
+
- HKDF-SHA256
|
67
|
+
- AEAD
|
68
|
+
- AES-128-GCM
|
69
|
+
|
70
|
+
Using the `-n` or `--not-force-compliant-hpke`, you can not enforce the HPKE cipher suite.
|
71
|
+
|
72
|
+
```sh-session
|
73
|
+
$ echspec -f fixtures/echconfigs.pem -p 4433 -n localhost
|
74
|
+
```
|
75
|
+
|
76
|
+
If you specify the SECTIONS, you can run only SECTIONS the following:
|
77
|
+
|
78
|
+
```sh-session
|
79
|
+
$ echspec -f fixtures/echconfigs.pem -p 4433 -n -s 7.1.1-2,7.1.1-5 localhost
|
80
|
+
TLS Encrypted Client Hello Server
|
81
|
+
✔ MUST abort with a "missing_extension" alert, if 2nd ClientHelloOuter does not contains the "encrypted_client_hello" extension. [7.1.1-2]
|
82
|
+
✔ MUST abort with an "illegal_parameter" alert, if 2nd ClientHelloOuter "encrypted_client_hello" enc is empty. [7.1.1-2]
|
83
|
+
✔ MUST abort with a "decrypt_error" alert, if fails to decrypt 2nd ClientHelloOuter. [7.1.1-5]
|
84
|
+
```
|
85
|
+
|
86
|
+
Using the `-v` or `--verbose` option provides a message stack if an error occurs. The message stack is formatted as JSON.
|
87
|
+
|
88
|
+
```sh-session
|
89
|
+
$ echspec -s 7-5 -v research.cloudflare.com 2>&1 > /dev/null | jq .
|
90
|
+
````
|
91
|
+
|
92
|
+
<details>
|
93
|
+
|
94
|
+
```json
|
95
|
+
{
|
96
|
+
"Alert": {
|
97
|
+
"level": "0x02",
|
98
|
+
"description": "0x32"
|
99
|
+
},
|
100
|
+
"ClientHello": {
|
101
|
+
"msg_type": "0x01",
|
102
|
+
"legacy_version": "0x0303",
|
103
|
+
"random": "0x29142f95eb55066cdb496267d3154628685ad1dbbe5b877e66eda4af20df2c69",
|
104
|
+
"legacy_session_id": "0x9c557bc381f62d73ba3b99629f8fe6e347787be66c56fa99db4f6bc6fd06fd5f",
|
105
|
+
"cipher_suites": [
|
106
|
+
"0x1302",
|
107
|
+
"0x1303",
|
108
|
+
"0x1301"
|
109
|
+
],
|
110
|
+
"legacy_compression_methods": [
|
111
|
+
"0x00"
|
112
|
+
],
|
113
|
+
"extensions": {
|
114
|
+
"0x0000": {
|
115
|
+
"extension_type": "0x0000",
|
116
|
+
"server_name": "0x636c6f7564666c6172652d6563682e636f6d"
|
117
|
+
},
|
118
|
+
"0x002b": {
|
119
|
+
"extension_type": "0x002b",
|
120
|
+
"msg_type": "0x01",
|
121
|
+
"versions": [
|
122
|
+
"0x0304"
|
123
|
+
]
|
124
|
+
},
|
125
|
+
"0x000d": {
|
126
|
+
"extension_type": "0x000d",
|
127
|
+
"supported_signature_algorithms": [
|
128
|
+
"0x0403",
|
129
|
+
"0x0503",
|
130
|
+
"0x0603",
|
131
|
+
"0x0804",
|
132
|
+
"0x0805",
|
133
|
+
"0x0806",
|
134
|
+
"0x0401",
|
135
|
+
"0x0501",
|
136
|
+
"0x0601"
|
137
|
+
]
|
138
|
+
},
|
139
|
+
"0x000a": {
|
140
|
+
"extension_type": "0x000a",
|
141
|
+
"named_group_list": [
|
142
|
+
"0x0017",
|
143
|
+
"0x0018",
|
144
|
+
"0x0019"
|
145
|
+
]
|
146
|
+
},
|
147
|
+
"0x0033": {
|
148
|
+
"extension_type": "0x0033",
|
149
|
+
"msg_type": "0x01",
|
150
|
+
"key_share_entry": [
|
151
|
+
{
|
152
|
+
"group": "0x0017",
|
153
|
+
"key_exchange": "0x0421747aa4234dbefc61906c165b8f1050b3346bb67f2c4ad8af9f58135888354a631b9b5c68f8ec1b6d6e67485a971bd3ff0ba6ab46da08f1524d7a4a3578c110"
|
154
|
+
},
|
155
|
+
{
|
156
|
+
"group": "0x0018",
|
157
|
+
"key_exchange": "0x046d374a61f2b75717b28b47b3fa227b51e09bb7a4ce0ea24b3cd3c946e9d4da2d54186b76812a74eb53adaa8a4451573201613b2f6301c05efb79bb6a782e88150dc3ac14aad702b8268aa8f3435d1a404166133216467aeb933247f994035fa0"
|
158
|
+
},
|
159
|
+
{
|
160
|
+
"group": "0x0019",
|
161
|
+
"key_exchange": "0x0401c90f95f64dc1e8d7d3f29f8a9103308835a2b56f5581ffc2837a1f9fcf6b23db824f01d6e4efccec78d6858aaad2fde8f02c2acc66a463c14b78e5e323b23d1b1301725b4f757811b7a31b4b5ee8016891ecb55b3fb997dd5738f6e610388f9bc85a4515efd04ab1d456e56d4e6e4a3a1e58a58ed6cf86f5dc9d7ace20afcd0af6b23b"
|
162
|
+
}
|
163
|
+
]
|
164
|
+
},
|
165
|
+
"0xfe0d": {
|
166
|
+
"extension_type": "0xfe0d",
|
167
|
+
"type": "0x02",
|
168
|
+
"cipher_suite": {
|
169
|
+
"kdf_id": {
|
170
|
+
"uint16": 1
|
171
|
+
},
|
172
|
+
"aead_id": {
|
173
|
+
"uint16": 1
|
174
|
+
}
|
175
|
+
},
|
176
|
+
"config_id": 9,
|
177
|
+
"enc": "0xe951022abd85cc53f16c92378ba736ea9d28b3a4106a6f2865323ea04fdf4075",
|
178
|
+
"payload": "0xbeef20fb3063fd807f6327213a5c5e37a0d355fc67c4c3a10362f947c7b72f06514db0e6bf470efb87d0db30669331caa3723441fb2850190851a9179b8b42e1e7f78bd0d281daf631872d9f7008624e24baef48ac8e18951fed9ad7d80def0b1bec492d5c5c2c532c4c8ec32b6dd3a34522c70c64e21ca639c7f54d2c3ad72c65ffda0dc1f82df5abf0857586eb17f0df08a15770a91d5c3640cff59b49b0fb3d19cf77137cd27416470e19db21519751c3d0ae417a62641903731408b4b81e008fff641d22a3300f92c8b9330d260055677c545a8d561076ecc3a4de5639120f6c67736df87a464ca221373c3bbe8e74dfe795eb43593f1d03d8668d49c4a9a73ed6d3264d7126bcfc93a975c8848170c57322b7c1bef210235bc79fd58bb4ff202d9e75bec3d2251a2429e11e5c7876edcca0685d52e1b99f51b46d0e723975a7c9d894e5674ef8debf380e799d75bff93c13e4917296242e2cb7b99bfcb0fc7cd8f98f414ceb0ef3a63fa29efc722194b91beb354efff5215b1804b9c555d4aad36a85e6eb3536b0b66fea50c9b055fa8441a36fc61a2228e0d4cb3c5ecb1662c641adf30a70c3b1104fb9b0f9b2c3130dc52939e9695a470774bee6dabc2691d06f870d01fe249199d831258583"
|
179
|
+
}
|
180
|
+
}
|
181
|
+
},
|
182
|
+
"ClientHelloInner": {
|
183
|
+
"msg_type": "0x01",
|
184
|
+
"legacy_version": "0x0303",
|
185
|
+
"random": "0x99f21aebeff5838b88011e581ade4de4eb334e5528efaf223dfa33c55d7bc20b",
|
186
|
+
"legacy_session_id": "0x9c557bc381f62d73ba3b99629f8fe6e347787be66c56fa99db4f6bc6fd06fd5f",
|
187
|
+
"cipher_suites": [
|
188
|
+
"0x1302",
|
189
|
+
"0x1303",
|
190
|
+
"0x1301"
|
191
|
+
],
|
192
|
+
"legacy_compression_methods": [
|
193
|
+
"0x00"
|
194
|
+
],
|
195
|
+
"extensions": {
|
196
|
+
"0x0000": {
|
197
|
+
"extension_type": "0x0000",
|
198
|
+
"server_name": "0x72657365617263682e636c6f7564666c6172652e636f6d"
|
199
|
+
},
|
200
|
+
"0x002b": {
|
201
|
+
"extension_type": "0x002b",
|
202
|
+
"msg_type": "0x01",
|
203
|
+
"versions": [
|
204
|
+
"0x0304"
|
205
|
+
]
|
206
|
+
},
|
207
|
+
"0x000d": {
|
208
|
+
"extension_type": "0x000d",
|
209
|
+
"supported_signature_algorithms": [
|
210
|
+
"0x0403",
|
211
|
+
"0x0503",
|
212
|
+
"0x0603",
|
213
|
+
"0x0804",
|
214
|
+
"0x0805",
|
215
|
+
"0x0806",
|
216
|
+
"0x0401",
|
217
|
+
"0x0501",
|
218
|
+
"0x0601"
|
219
|
+
]
|
220
|
+
},
|
221
|
+
"0x000a": {
|
222
|
+
"extension_type": "0x000a",
|
223
|
+
"named_group_list": [
|
224
|
+
"0x0017",
|
225
|
+
"0x0018",
|
226
|
+
"0x0019"
|
227
|
+
]
|
228
|
+
},
|
229
|
+
"0x0033": {
|
230
|
+
"extension_type": "0x0033",
|
231
|
+
"msg_type": "0x01",
|
232
|
+
"key_share_entry": [
|
233
|
+
{
|
234
|
+
"group": "0x0017",
|
235
|
+
"key_exchange": "0x0421747aa4234dbefc61906c165b8f1050b3346bb67f2c4ad8af9f58135888354a631b9b5c68f8ec1b6d6e67485a971bd3ff0ba6ab46da08f1524d7a4a3578c110"
|
236
|
+
},
|
237
|
+
{
|
238
|
+
"group": "0x0018",
|
239
|
+
"key_exchange": "0x046d374a61f2b75717b28b47b3fa227b51e09bb7a4ce0ea24b3cd3c946e9d4da2d54186b76812a74eb53adaa8a4451573201613b2f6301c05efb79bb6a782e88150dc3ac14aad702b8268aa8f3435d1a404166133216467aeb933247f994035fa0"
|
240
|
+
},
|
241
|
+
{
|
242
|
+
"group": "0x0019",
|
243
|
+
"key_exchange": "0x0401c90f95f64dc1e8d7d3f29f8a9103308835a2b56f5581ffc2837a1f9fcf6b23db824f01d6e4efccec78d6858aaad2fde8f02c2acc66a463c14b78e5e323b23d1b1301725b4f757811b7a31b4b5ee8016891ecb55b3fb997dd5738f6e610388f9bc85a4515efd04ab1d456e56d4e6e4a3a1e58a58ed6cf86f5dc9d7ace20afcd0af6b23b"
|
244
|
+
}
|
245
|
+
]
|
246
|
+
},
|
247
|
+
"0xfe0d": {
|
248
|
+
"extension_type": "0xfe0d",
|
249
|
+
"type": "0x01",
|
250
|
+
"cipher_suite": null,
|
251
|
+
"config_id": null,
|
252
|
+
"enc": null,
|
253
|
+
"payload": null
|
254
|
+
}
|
255
|
+
}
|
256
|
+
}
|
257
|
+
}
|
258
|
+
```
|
259
|
+
|
260
|
+
</details>
|
261
|
+
|
262
|
+
|
263
|
+
## Note
|
264
|
+
|
265
|
+
`echspec` is inspired by:
|
266
|
+
|
267
|
+
- https://github.com/summerwind/h2spec
|
268
|
+
|
269
|
+
|
270
|
+
## License
|
271
|
+
|
272
|
+
`echspec` is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
Binary file
|
data/echspec.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
lib = File.expand_path('lib', __dir__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require 'echspec/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = 'echspec'
|
7
|
+
spec.version = EchSpec::VERSION
|
8
|
+
spec.authors = ['thekuwayama']
|
9
|
+
spec.email = ['thekuwayama@gmail.com']
|
10
|
+
spec.summary = 'A conformance testing tool for ECH implementation'
|
11
|
+
spec.description = spec.summary
|
12
|
+
spec.homepage = 'https://github.com/thekuwayama/echspec'
|
13
|
+
spec.license = 'MIT'
|
14
|
+
spec.required_ruby_version = '>=3.2'
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
17
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
|
+
spec.require_paths = ['lib']
|
19
|
+
spec.bindir = 'exe'
|
20
|
+
spec.executables = ['echspec']
|
21
|
+
|
22
|
+
spec.add_development_dependency 'bundler'
|
23
|
+
spec.add_dependency 'base64'
|
24
|
+
spec.add_dependency 'resolv', '> 0.4.0'
|
25
|
+
spec.add_dependency 'tttls1.3', '~> 0.3.4'
|
26
|
+
end
|
data/exe/echspec
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
-----BEGIN PRIVATE KEY-----
|
2
|
+
MC4CAQAwBQYDK2VuBCIEICjd4yGRdsoP9gU7YT7My8DHx1Tjme8GYDXrOMCi8v1V
|
3
|
+
-----END PRIVATE KEY-----
|
4
|
+
-----BEGIN ECHCONFIG-----
|
5
|
+
AD7+DQA65wAgACA8wVN2BtscOl3vQheUzHeIkVmKIiydUhDCliA4iyQRCwAEAAEA
|
6
|
+
AQALZXhhbXBsZS5jb20AAA==
|
7
|
+
-----END ECHCONFIG-----
|
data/fixtures/server.crt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
2
|
+
MIIDOzCCAiOgAwIBAgIJAOOHBGEtaVlUMA0GCSqGSIb3DQEBCwUAMBwxGjAYBgNV
|
3
|
+
BAMMEXRlc3QtaW50ZXJtZWRpYXRlMB4XDTI0MDUwMzIyNTgzOVoXDTI1MDUwMzIy
|
4
|
+
NTgzOVowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOC
|
5
|
+
AQ8AMIIBCgKCAQEAyJ0/RY8esNtOCfdi3iR1VhPKpteACMybSZL+yvCr7HVWSIAF
|
6
|
+
TK+sCN5ODdG+rnICvmmaLLF1aVwz9Jn0uW+MX3tejsDftw6CH7z/xblqPmEl2gTW
|
7
|
+
vOdDSfJttaqSf1F+XQMadVov4YFweRAAI8uY4BTV4TLEfIK06xNNFqcMv5EmUyrv
|
8
|
+
XEVW3ki7+7nOqMUH7nfNymR8ENU1YX8y4WpyfEyKw1Gk3PQH3iIQ5J7gqoDqlBYG
|
9
|
+
083eLJau5qVDM/Q9Ro002tfmdpnJg+g59q9KstOQbTHKBYTP8YIotJhDOCl/fgoP
|
10
|
+
g+r3gP6EAnMaE4XjsQTp7YDRiVnvF2lMqYC0QwIDAQABo4GHMIGEMAkGA1UdEwQC
|
11
|
+
MAAwCwYDVR0PBAQDAgWgMBQGA1UdEQQNMAuCCWxvY2FsaG9zdDBUBggrBgEFBQcB
|
12
|
+
AQRIMEYwIQYIKwYBBQUHMAKGFWh0dHA6Ly9sb2NhbGhvc3Q6ODA4MDAhBggrBgEF
|
13
|
+
BQcwAYYVaHR0cDovL2xvY2FsaG9zdDo4MDgwMA0GCSqGSIb3DQEBCwUAA4IBAQA2
|
14
|
+
ZuJuzDr3JARfqpi5C2yktW0Hiv0Dzx3g1vLeOGl6ZPPIxChxHKQ79BACM2FsVYcu
|
15
|
+
ZGK7qGXpCMuzOuMjoCcfydXwjmoMlLtjAwXSKv5/ZT+SXWkO4weMKVQsovfBYDvr
|
16
|
+
ktiHjJJV1ToPa1R4FsXIyNQDTByyT8D+8Fj3+et/I+JyxNrm65Rv6l+qapP6J5Fn
|
17
|
+
oAVxoE7JFLcqvvRLH7/ggqMhUgftN4olzhUT2d6sThevsA9D6PWkcRuGvPWFMXkY
|
18
|
+
R1ABD5sQynFvkcjjCcl+m7tOyqcmvwFFlS9X+ql4LLT8MHOOXhAGfxcD6qquVmvL
|
19
|
+
Is1FlMw33rZEkugXwT0w
|
20
|
+
-----END CERTIFICATE-----
|
data/fixtures/server.key
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
-----BEGIN RSA PRIVATE KEY-----
|
2
|
+
MIIEpAIBAAKCAQEAyJ0/RY8esNtOCfdi3iR1VhPKpteACMybSZL+yvCr7HVWSIAF
|
3
|
+
TK+sCN5ODdG+rnICvmmaLLF1aVwz9Jn0uW+MX3tejsDftw6CH7z/xblqPmEl2gTW
|
4
|
+
vOdDSfJttaqSf1F+XQMadVov4YFweRAAI8uY4BTV4TLEfIK06xNNFqcMv5EmUyrv
|
5
|
+
XEVW3ki7+7nOqMUH7nfNymR8ENU1YX8y4WpyfEyKw1Gk3PQH3iIQ5J7gqoDqlBYG
|
6
|
+
083eLJau5qVDM/Q9Ro002tfmdpnJg+g59q9KstOQbTHKBYTP8YIotJhDOCl/fgoP
|
7
|
+
g+r3gP6EAnMaE4XjsQTp7YDRiVnvF2lMqYC0QwIDAQABAoIBAAzYZ2e9zUTyazcf
|
8
|
+
fFjQVCRfKg0FeXDsIPlUtzR+tT4ebpg/0kG3HI3eJiNOYyY/rfChSbpDi/VjblQ4
|
9
|
+
KL+tkSW//C2p0z6hEts+D9E07m+batVglUYNN3VxrMKtbv9F/uWtFYb0GmLBwKuP
|
10
|
+
xw0uXoCNSlCcHpFWZofdeYR8zR0q7L64P8EsqjrYGOTeyQWKshHIxAAks6Xb19zW
|
11
|
+
h4f3aBTZ3tWHCMAZj8LsGVG4O2Nx9esuKwybe0vBMkv/esyEVXEPm+RDjQtlKGqQ
|
12
|
+
i4V03ykI6LbIwi8bk3H7jItGKX8fNK9CNMi0ZEyYgvVe5HQciJBtcaWUiBUEHhum
|
13
|
+
N0Np7uECgYEA6jsG72P5GleyRIBAUNrN/a1yIc9b9RXZS63lCb5UMMW6rYuP8wRe
|
14
|
+
1ARzCNhkzYoKxay3ZQHPW8deDasTXCPiIkJqRDfDRr464OQ29qBEWx4aWtVbAsuH
|
15
|
+
8/SJK45YpvdCSxT3wBzTzzff4bAIF7yFYBJjVBEs948TrtGePlvpyBMCgYEA20Jl
|
16
|
+
xpQUorhEXn4JOjC65FHj3hJgPus8JgPSLpjpuDs9WZBN3mRt7GIZ0Tb6gA/Yv2wn
|
17
|
+
Ps0u2iCe7uGKC5XEQ84KrtcM+rk7mRGsdWxQqnJp6tbZbHqGqF0Y9uBqOgRiJJTm
|
18
|
+
i9L1QiHnkrtlwMdlQRXQvPvwG4Z5OU/W4D7/SRECgYEAr/wZgdPDXZ92OTGDITzE
|
19
|
+
eEzQ68Y4eTQpR0soQuHVr69gSvQI+7XU6cdOBt9PHX8SCON0B1gMzBBHAk3/BcOQ
|
20
|
+
K91qqkabWZOj+UR+Z16S/ULo2kZjUv5I72pThX417XzpOjBO1PDT02VPuOnhqrPi
|
21
|
+
IgSuzIL7HiVJzJeCJag5RjECgYBhAiuNhI7sv6JgPFtQx6aoxiKPaonyzJk8KIyh
|
22
|
+
2T3vKSanrdUGBGEuKOlLS4vhhSFc8Dkc7CNClxQ6lMdDAOxpI4xOdw9jDvlzbAJl
|
23
|
+
oZq/DwgVwyFHgZ56d1ZIRFo7eR0DGm42hwvESsPug8MtXAtMlJ5aPw2o4AJafRyQ
|
24
|
+
8s54QQKBgQCnaQcbPdoNrnn1giz6s1XNKMayIfod8tPS+xnKJpGzjyDVQzEDWA05
|
25
|
+
pEp1GFMaNXjUa8ICj/DeZ630ZfODDboTp3JVKLe+QW3U89H60AJz/0UnZ8k/fNwA
|
26
|
+
39CfIk3ukQS4oAWpMua2s2e9Vf7zXHM/sm07R8BKo0wtoUEg3RtCYQ==
|
27
|
+
-----END RSA PRIVATE KEY-----
|
data/lib/echspec/cli.rb
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
module EchSpec
|
2
|
+
class CLI
|
3
|
+
# rubocop: disable Metrics/AbcSize
|
4
|
+
# rubocop: disable Metrics/MethodLength
|
5
|
+
def parse_options(argv = ARGV)
|
6
|
+
op = OptionParser.new
|
7
|
+
|
8
|
+
# default value
|
9
|
+
fpath = nil
|
10
|
+
port = 443
|
11
|
+
force_compliant = true
|
12
|
+
verbose = false
|
13
|
+
sections = nil
|
14
|
+
|
15
|
+
op.on(
|
16
|
+
'-f',
|
17
|
+
'--file FILE',
|
18
|
+
'path to ECHConfigs PEM file (default resolve ECHConfigs via DNS)'
|
19
|
+
) do |v|
|
20
|
+
fpath = v
|
21
|
+
end
|
22
|
+
|
23
|
+
op.on(
|
24
|
+
'-p',
|
25
|
+
'--port VALUE',
|
26
|
+
"server port number (default #{port})"
|
27
|
+
) do |v|
|
28
|
+
port = v
|
29
|
+
end
|
30
|
+
|
31
|
+
op.on(
|
32
|
+
'-n',
|
33
|
+
'--not-force-compliant-hpke',
|
34
|
+
'not force compliant ECHConfig HPKE cipher suite'
|
35
|
+
) do
|
36
|
+
force_compliant = false
|
37
|
+
end
|
38
|
+
|
39
|
+
op.on(
|
40
|
+
'-v',
|
41
|
+
'--verbose',
|
42
|
+
'verbose mode; prints message stack if raised an error'
|
43
|
+
) do
|
44
|
+
verbose = true
|
45
|
+
end
|
46
|
+
|
47
|
+
op.on(
|
48
|
+
'-s',
|
49
|
+
'--sections SECTIONS',
|
50
|
+
'sections to test; by the default, test all sections'
|
51
|
+
) do |v|
|
52
|
+
sections = v.split(',')
|
53
|
+
end
|
54
|
+
|
55
|
+
op.banner = 'Usage: echspec [OPTIONS] <HOSTNAME>'
|
56
|
+
begin
|
57
|
+
args = op.parse(argv)
|
58
|
+
rescue OptionParser::InvalidOption, OptionParser::MissingArgument => e
|
59
|
+
warn op
|
60
|
+
warn "** #{e.message}"
|
61
|
+
exit 1
|
62
|
+
end
|
63
|
+
|
64
|
+
if !fpath.nil? && !File.exist?(fpath)
|
65
|
+
warn '** <FILE> is not found'
|
66
|
+
exit 1
|
67
|
+
end
|
68
|
+
|
69
|
+
unknowns = sections.nil? ? [] : sections - Spec.sections
|
70
|
+
unless unknowns.empty?
|
71
|
+
warn "** #{unknowns} are unknown sections"
|
72
|
+
exit 1
|
73
|
+
end
|
74
|
+
|
75
|
+
if args.length != 1
|
76
|
+
warn op
|
77
|
+
warn '** <HOSTNAME> argument is not specified'
|
78
|
+
exit 1
|
79
|
+
end
|
80
|
+
hostname = args[0]
|
81
|
+
|
82
|
+
[fpath, port, force_compliant, verbose, hostname, sections]
|
83
|
+
end
|
84
|
+
# rubocop: enable Metrics/AbcSize
|
85
|
+
# rubocop: enable Metrics/MethodLength
|
86
|
+
|
87
|
+
def run
|
88
|
+
fpath, port, force_compliant, verbose, hostname, sections = parse_options
|
89
|
+
|
90
|
+
if sections.nil?
|
91
|
+
Spec.run(fpath, port, hostname, force_compliant, verbose)
|
92
|
+
else
|
93
|
+
Spec.run_only(fpath, port, hostname, sections, verbose)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
module EchSpec
|
2
|
+
module Error
|
3
|
+
# Generic error, common for all classes under EchSpec::Error module.
|
4
|
+
class Error < StandardError; end
|
5
|
+
|
6
|
+
# Raised if the server behaves unintended before the target situation.
|
7
|
+
class BeforeTargetSituationError < Error; end
|
8
|
+
end
|
9
|
+
end
|