echspec 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +2 -2
- data/.ruby-version +1 -1
- data/README.md +10 -2
- data/docs/echspec-demo.png +0 -0
- data/echspec.gemspec +1 -1
- data/lib/echspec/log.rb +4 -0
- data/lib/echspec/spec/5.1-10.rb +4 -4
- data/lib/echspec/spec/7.1-14.2.1.rb +2 -7
- data/lib/echspec/spec/9.rb +4 -4
- data/lib/echspec/spec.rb +47 -25
- data/lib/echspec/tls13_client.rb +2 -2
- data/lib/echspec/utils.rb +4 -0
- data/lib/echspec/version.rb +1 -1
- metadata +5 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 664b02dfe5659032ff19242937be686085ab762f223fd950a1332da55fd69e12
|
4
|
+
data.tar.gz: 0146c242937094ff8a4cb1c494d27af14a55d34e8967c4bf174b6eb76132b8c6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ca2add160c37c4997ee2f76172c1b4edc598d85149fecfae5fd6861dce34455916fbee67d02bbf60e8d6f121e583ca284fee4bde73a215c735c05465e30b8704
|
7
|
+
data.tar.gz: 832197c0251d27bf060a4ac774126117eca42fc11615e1078edbf4db84109a24125e6ab2417cba393223a87632701deea876a90e0e1cf5d9a8d7c7b324a40dc8
|
data/.github/workflows/ci.yml
CHANGED
@@ -15,9 +15,9 @@ jobs:
|
|
15
15
|
matrix:
|
16
16
|
ruby-version: ['3.2', '3.3', '3.4']
|
17
17
|
steps:
|
18
|
-
- uses: actions/checkout@v4
|
18
|
+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
19
19
|
- name: Set up Ruby
|
20
|
-
uses: ruby/setup-ruby@v1
|
20
|
+
uses: ruby/setup-ruby@eaecf785f6a34567a6d97f686bbb7bccc1ac1e5c # v1.237.0
|
21
21
|
with:
|
22
22
|
ruby-version: ${{ matrix.ruby-version }}
|
23
23
|
- name: Install dependencies
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
3.3
|
1
|
+
3.4.3
|
data/README.md
CHANGED
@@ -11,7 +11,7 @@
|
|
11
11
|
- https://datatracker.ietf.org/doc/html/draft-ietf-tls-esni-22
|
12
12
|
|
13
13
|
|
14
|
-
##
|
14
|
+
## Installation
|
15
15
|
|
16
16
|
The gem is available at [rubygems.org](https://rubygems.org/gems/echspec). You can install it the following:
|
17
17
|
|
@@ -44,12 +44,20 @@ TLS Encrypted Client Hello Server
|
|
44
44
|
✔ MUST abort with an "illegal_parameter" alert, if "encrypted_client_hello" is referenced in OuterExtensions. [5.1-10]
|
45
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
46
|
✔ MUST abort with an "illegal_parameter" alert, if ECHClientHello.type is not a valid ECHClientHelloType in ClientHelloInner. [7-5]
|
47
|
-
|
47
|
+
x MUST abort with an "illegal_parameter" alert, if ECHClientHello.type is not a valid ECHClientHelloType in ClientHelloOuter. [7-5]
|
48
48
|
✔ MUST abort with an "illegal_parameter" alert, if ClientHelloInner offers TLS 1.2 or below. [7.1-11]
|
49
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
50
|
✔ MUST abort with a "missing_extension" alert, if 2nd ClientHelloOuter does not contains the "encrypted_client_hello" extension. [7.1.1-2]
|
51
51
|
✔ MUST abort with an "illegal_parameter" alert, if 2nd ClientHelloOuter "encrypted_client_hello" enc is empty. [7.1.1-2]
|
52
52
|
✔ MUST abort with a "decrypt_error" alert, if fails to decrypt 2nd ClientHelloOuter. [7.1.1-5]
|
53
|
+
|
54
|
+
Failures:
|
55
|
+
|
56
|
+
1) MUST abort with an "illegal_parameter" alert, if ECHClientHello.type is not a valid ECHClientHelloType in ClientHelloOuter. [7-5]
|
57
|
+
https://datatracker.ietf.org/doc/html/draft-ietf-tls-esni-22#section-7-5
|
58
|
+
did not send expected alert: illegal_parameter
|
59
|
+
|
60
|
+
1 failure
|
53
61
|
```
|
54
62
|
|
55
63
|
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:
|
data/docs/echspec-demo.png
CHANGED
Binary file
|
data/echspec.gemspec
CHANGED
data/lib/echspec/log.rb
CHANGED
@@ -34,12 +34,16 @@ module EchSpec
|
|
34
34
|
'EncryptedExtensions'
|
35
35
|
in TTTLS13::Message::Certificate
|
36
36
|
'Certificate'
|
37
|
+
in TTTLS13::Message::CompressedCertificate
|
38
|
+
'CompressedCertificate'
|
37
39
|
in TTTLS13::Message::CertificateVerify
|
38
40
|
'CertificateVerify'
|
39
41
|
in TTTLS13::Message::Finished
|
40
42
|
'Finished'
|
41
43
|
in TTTLS13::Message::EndOfEarlyData
|
42
44
|
'EndOfEarlyData'
|
45
|
+
in TTTLS13::Message::NewSessionTicket
|
46
|
+
'NewSessionTicket'
|
43
47
|
in TTTLS13::Message::Alert
|
44
48
|
'Alert'
|
45
49
|
end
|
data/lib/echspec/spec/5.1-10.rb
CHANGED
@@ -130,7 +130,7 @@ module EchSpec
|
|
130
130
|
end
|
131
131
|
|
132
132
|
class MissingReferencedExtensions < TTTLS13::Message::Extensions
|
133
|
-
# @param _ [Array
|
133
|
+
# @param _ [Array<TTTLS13::Message::ExtensionType>]
|
134
134
|
#
|
135
135
|
# @return [TTTLS13::Message::Extensions] for EncodedClientHelloInner
|
136
136
|
def remove_and_replace!(_)
|
@@ -150,7 +150,7 @@ module EchSpec
|
|
150
150
|
end
|
151
151
|
|
152
152
|
class DuplicatedOuterExtensions < TTTLS13::Message::Extensions
|
153
|
-
# @param _ [Array
|
153
|
+
# @param _ [Array<TTTLS13::Message::ExtensionType>]
|
154
154
|
#
|
155
155
|
# @return [TTTLS13::Message::Extensions] for EncodedClientHelloInner
|
156
156
|
def remove_and_replace!(_)
|
@@ -172,7 +172,7 @@ module EchSpec
|
|
172
172
|
end
|
173
173
|
|
174
174
|
class ReferencedEncryptedClientHello < TTTLS13::Message::Extensions
|
175
|
-
# @param _ [Array
|
175
|
+
# @param _ [Array<TTTLS13::Message::ExtensionType>]
|
176
176
|
#
|
177
177
|
# @return [TTTLS13::Message::Extensions] for EncodedClientHelloInner
|
178
178
|
def remove_and_replace!(_)
|
@@ -194,7 +194,7 @@ module EchSpec
|
|
194
194
|
end
|
195
195
|
|
196
196
|
class NotSameOrderExtensions < TTTLS13::Message::Extensions
|
197
|
-
# @param _ [Array
|
197
|
+
# @param _ [Array<TTTLS13::Message::ExtensionType>]
|
198
198
|
#
|
199
199
|
# @return [TTTLS13::Message::Extensions] for EncodedClientHelloInner
|
200
200
|
def remove_and_replace!(_)
|
@@ -60,7 +60,7 @@ module EchSpec
|
|
60
60
|
# send ClientHello
|
61
61
|
conn = TLS13Client::Connection.new(socket, :client)
|
62
62
|
inner_ech = TTTLS13::Message::Extension::ECHClientHello.new_inner
|
63
|
-
exs,
|
63
|
+
exs, shared_secret = TLS13Client.gen_ch_extensions(hostname)
|
64
64
|
inner = TTTLS13::Message::ClientHello.new(
|
65
65
|
cipher_suites: TTTLS13::CipherSuites.new(
|
66
66
|
[
|
@@ -98,14 +98,9 @@ module EchSpec
|
|
98
98
|
transcript[TTTLS13::SH] = [sh, sh.serialize]
|
99
99
|
kse = sh.extensions[TTTLS13::Message::ExtensionType::KEY_SHARE]
|
100
100
|
.key_share_entry.first
|
101
|
-
shared_secret = TTTLS13::Endpoint.gen_shared_secret(
|
102
|
-
kse.key_exchange,
|
103
|
-
priv_keys[kse.group],
|
104
|
-
kse.group
|
105
|
-
)
|
106
101
|
key_schedule = TTTLS13::KeySchedule.new(
|
107
102
|
psk: nil,
|
108
|
-
shared_secret
|
103
|
+
shared_secret: shared_secret.build(kse.group, kse.key_exchange),
|
109
104
|
cipher_suite: sh.cipher_suite,
|
110
105
|
transcript:
|
111
106
|
)
|
data/lib/echspec/spec/9.rb
CHANGED
@@ -41,9 +41,9 @@ module EchSpec
|
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
44
|
-
# @param ech_configs [Array
|
44
|
+
# @param ech_configs [Array<ECHConfig>]
|
45
45
|
#
|
46
|
-
# @return [EchSpec::Ok
|
46
|
+
# @return [EchSpec::Ok | Err]
|
47
47
|
def validate_compliant_ech_configs(ech_configs)
|
48
48
|
ech_config = ech_configs.find do |c|
|
49
49
|
kconfig = c.echconfig_contents.key_config
|
@@ -61,7 +61,7 @@ module EchSpec
|
|
61
61
|
|
62
62
|
# @param hostname [String]
|
63
63
|
#
|
64
|
-
# @return [EchSpec::Ok<Array
|
64
|
+
# @return [EchSpec::Ok<Array<ECHConfig>> | Err]
|
65
65
|
def resolve_ech_configs(hostname)
|
66
66
|
begin
|
67
67
|
rr = Resolv::DNS.new.getresource(
|
@@ -85,7 +85,7 @@ module EchSpec
|
|
85
85
|
|
86
86
|
# @param pem [String]
|
87
87
|
#
|
88
|
-
# @return [EchSpec::Ok<Array
|
88
|
+
# @return [EchSpec::Ok<Array<ECHConfig>> | Err]
|
89
89
|
def parse_pem(pem)
|
90
90
|
s = pem.scan(/-----BEGIN ECHCONFIG-----(.*)-----END ECHCONFIG-----/m)
|
91
91
|
.first
|
data/lib/echspec/spec.rb
CHANGED
@@ -12,20 +12,20 @@ module EchSpec
|
|
12
12
|
msg.description == TTTLS13::Message::ALERT_DESCRIPTION[desc]
|
13
13
|
end
|
14
14
|
|
15
|
-
|
15
|
+
ResultDescURL = Struct.new(:result, :desc, :url)
|
16
16
|
|
17
|
-
# @param rds [Array
|
17
|
+
# @param rds [Array<ResultDescURL>] result: EchSpec::Ok | Err, desc: String
|
18
18
|
# @param verbose [Boolean]
|
19
|
-
def print_results(
|
20
|
-
|
21
|
-
failures =
|
19
|
+
def print_results(rdus, verbose)
|
20
|
+
rdus.each { |rdu| print_summary(rdu.result, rdu.desc) }
|
21
|
+
failures = rdus.filter { |rdu| rdu.result.is_a? Err }
|
22
22
|
return if failures.empty?
|
23
23
|
|
24
24
|
puts
|
25
25
|
puts 'Failures:'
|
26
26
|
puts
|
27
27
|
failures.each
|
28
|
-
.with_index { |
|
28
|
+
.with_index { |rdu, idx| print_err_details(rdu.result, rdu.url, idx, rdu.desc, verbose) }
|
29
29
|
puts "#{failures.length} failure".red
|
30
30
|
end
|
31
31
|
|
@@ -36,20 +36,22 @@ module EchSpec
|
|
36
36
|
cross = "\u0078"
|
37
37
|
summary = case result
|
38
38
|
in Ok
|
39
|
-
"
|
39
|
+
"#{check} #{desc}".green.indent
|
40
40
|
in Err
|
41
|
-
"
|
41
|
+
"#{cross} #{desc}".red.indent
|
42
42
|
end
|
43
43
|
puts summary
|
44
44
|
end
|
45
45
|
|
46
46
|
# @param err [EchSpec::Err]
|
47
|
+
# @param url [String]
|
47
48
|
# @param idx [Integer]
|
48
49
|
# @param desc [String]
|
49
50
|
# @param verbose [Boolean]
|
50
|
-
def print_err_details(err, idx, desc, verbose)
|
51
|
-
puts "
|
52
|
-
puts
|
51
|
+
def print_err_details(err, url, idx, desc, verbose)
|
52
|
+
puts "#{idx + 1}) #{desc}".indent
|
53
|
+
puts url.indent.indent
|
54
|
+
puts err.details.indent.indent
|
53
55
|
warn err.message_stack if verbose && !err.message_stack.nil?
|
54
56
|
puts
|
55
57
|
end
|
@@ -103,7 +105,7 @@ module EchSpec
|
|
103
105
|
# @param fpath [String | NilClass]
|
104
106
|
# @param port [Integer]
|
105
107
|
# @param hostname [String]
|
106
|
-
# @param sections [Array
|
108
|
+
# @param sections [Array<String>]
|
107
109
|
# @param verbose [Boolean]
|
108
110
|
def run_only(fpath, port, hostname, sections, verbose)
|
109
111
|
targets = spec_groups.filter { |g| sections.include?(g.section) }
|
@@ -119,18 +121,34 @@ module EchSpec
|
|
119
121
|
# @param port [Integer]
|
120
122
|
# @param hostname [String]
|
121
123
|
# @param ech_config [ECHConfig]
|
122
|
-
# @param targets [Array
|
124
|
+
# @param targets [Array<EchSpec::SpecGroup>]
|
123
125
|
# @param verbose [Boolean]
|
124
126
|
def do_run(port, hostname, ech_config, targets, verbose)
|
125
|
-
|
127
|
+
rdus = targets.flat_map do |g|
|
126
128
|
g.spec_cases.map do |sc|
|
127
|
-
|
128
|
-
|
129
|
-
|
129
|
+
result = sc.method.call(hostname, port, ech_config)
|
130
|
+
desc = desc(sc.description, g.section)
|
131
|
+
url = url(g.section)
|
132
|
+
ResultDescURL.new(result:, desc:, url:)
|
130
133
|
end
|
131
134
|
end
|
132
135
|
|
133
|
-
print_results(
|
136
|
+
print_results(rdus, verbose)
|
137
|
+
end
|
138
|
+
|
139
|
+
# @param description [String]
|
140
|
+
# @param section [String]
|
141
|
+
#
|
142
|
+
# @return [String]
|
143
|
+
def desc(description, section)
|
144
|
+
"#{description} [#{section}]"
|
145
|
+
end
|
146
|
+
|
147
|
+
# @param section [String]
|
148
|
+
#
|
149
|
+
# @return [String]
|
150
|
+
def url(section)
|
151
|
+
"https://datatracker.ietf.org/doc/html/draft-ietf-tls-esni-22#section-#{section}"
|
134
152
|
end
|
135
153
|
|
136
154
|
# @param fpath [String | NilClass]
|
@@ -140,14 +158,18 @@ module EchSpec
|
|
140
158
|
# @return [ECHConfig]
|
141
159
|
def try_get_ech_config(fpath, hostname, force_compliant)
|
142
160
|
# https://datatracker.ietf.org/doc/html/draft-ietf-tls-esni-22#section-9
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
161
|
+
result = Spec9.try_get_ech_config(fpath, hostname, force_compliant)
|
162
|
+
desc = desc(Spec9.description, Spec9.section)
|
163
|
+
url = url(Spec9.section)
|
164
|
+
|
165
|
+
case result
|
166
|
+
in Ok(ech_config) if force_compliant
|
167
|
+
print_summary(result, desc)
|
168
|
+
ech_config
|
169
|
+
in Ok(ech_config)
|
170
|
+
ech_config
|
149
171
|
in Err(details, _)
|
150
|
-
|
172
|
+
print_results([ResultDescURL.new(result:, desc:, url:)], true)
|
151
173
|
exit 1
|
152
174
|
end
|
153
175
|
end
|
data/lib/echspec/tls13_client.rb
CHANGED
@@ -66,12 +66,12 @@ module EchSpec
|
|
66
66
|
exs << TTTLS13::Message::Extension::SupportedGroups.new(groups)
|
67
67
|
|
68
68
|
# key_share
|
69
|
-
key_share,
|
69
|
+
key_share, shared_secret = TTTLS13::Message::Extension::KeyShare.gen_ch_key_share(
|
70
70
|
groups
|
71
71
|
)
|
72
72
|
exs << key_share
|
73
73
|
|
74
|
-
[exs,
|
74
|
+
[exs, shared_secret]
|
75
75
|
end
|
76
76
|
|
77
77
|
# @param ch1 [TTTLS13::Message::ClientHello]
|
data/lib/echspec/utils.rb
CHANGED
data/lib/echspec/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: echspec
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- thekuwayama
|
8
|
-
autorequire:
|
9
8
|
bindir: exe
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: bundler
|
@@ -58,14 +57,14 @@ dependencies:
|
|
58
57
|
requirements:
|
59
58
|
- - "~>"
|
60
59
|
- !ruby/object:Gem::Version
|
61
|
-
version: 0.3.
|
60
|
+
version: 0.3.5
|
62
61
|
type: :runtime
|
63
62
|
prerelease: false
|
64
63
|
version_requirements: !ruby/object:Gem::Requirement
|
65
64
|
requirements:
|
66
65
|
- - "~>"
|
67
66
|
- !ruby/object:Gem::Version
|
68
|
-
version: 0.3.
|
67
|
+
version: 0.3.5
|
69
68
|
description: A conformance testing tool for ECH implementation
|
70
69
|
email:
|
71
70
|
- thekuwayama@gmail.com
|
@@ -115,7 +114,6 @@ homepage: https://github.com/thekuwayama/echspec
|
|
115
114
|
licenses:
|
116
115
|
- MIT
|
117
116
|
metadata: {}
|
118
|
-
post_install_message:
|
119
117
|
rdoc_options: []
|
120
118
|
require_paths:
|
121
119
|
- lib
|
@@ -130,8 +128,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
130
128
|
- !ruby/object:Gem::Version
|
131
129
|
version: '0'
|
132
130
|
requirements: []
|
133
|
-
rubygems_version: 3.
|
134
|
-
signing_key:
|
131
|
+
rubygems_version: 3.6.7
|
135
132
|
specification_version: 4
|
136
133
|
summary: A conformance testing tool for ECH implementation
|
137
134
|
test_files:
|