echspec 0.0.2 → 0.0.4
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/.rubocop.yml +2 -1
- data/.ruby-version +1 -1
- data/Gemfile +2 -1
- data/README.md +14 -8
- data/docs/echspec-demo.png +0 -0
- data/echspec.gemspec +8 -4
- data/exe/echspec +1 -1
- data/lib/echspec/cli/gen_configs.rb +61 -0
- data/lib/echspec/cli/run.rb +100 -0
- data/lib/echspec/cli.rb +21 -84
- data/lib/echspec/spec/5.1-10.rb +2 -2
- data/lib/echspec/spec/5.1-9.rb +5 -5
- data/lib/echspec/spec/7-5.rb +7 -3
- data/lib/echspec/spec/7.1-11.rb +1 -1
- data/lib/echspec/spec/7.1-14.2.1.rb +3 -3
- data/lib/echspec/spec/7.1.1-2.rb +2 -2
- data/lib/echspec/spec/7.1.1-5.rb +3 -3
- data/lib/echspec/spec/9.rb +5 -5
- data/lib/echspec/spec.rb +5 -5
- data/lib/echspec/utils.rb +4 -0
- data/lib/echspec/version.rb +1 -1
- data/lib/echspec.rb +11 -10
- metadata +22 -16
- data/.github/workflows/ci.yml +0 -30
- data/.gitignore +0 -17
- data/spec/9_spec.rb +0 -13
- data/spec/log_spec.rb +0 -58
- data/spec/spec_helper.rb +0 -12
- data/spec/with_socket_spec.rb +0 -77
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9da986ad5207e26d816ebe08d7f8620981836c13d0dd9d695447269276a13453
|
|
4
|
+
data.tar.gz: ad7effc41c7349b011fc386ec4195d1d2dd86d51af68317d4034ad9538a9d31f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 53a725b8f4e8404f579b4b87b7d434c6146fa054f80a67147a2df2477102ecd3f7ca32db9b8827a5f00bfd1c51c0c70ec2c1575747c8a53716f682111a6d1c23
|
|
7
|
+
data.tar.gz: a0c8940990d04ff038e1ecdc4f1505a8b27f4ca87232daf08daecc9263f0a34c0cb8f14fee896d254a729102cc01d1cb7bb127f754c3ad9119b6bfb39c1487e7
|
data/.rubocop.yml
CHANGED
data/.ruby-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
4.0.1
|
data/Gemfile
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
source 'https://rubygems.org'
|
|
2
2
|
|
|
3
3
|
gem 'base64'
|
|
4
|
+
gem 'ech_config', github: 'thekuwayama/ech_config'
|
|
4
5
|
gem 'resolv', '> 0.4.0'
|
|
5
6
|
gem 'tttls1.3', github: 'thekuwayama/tttls1.3'
|
|
6
7
|
|
|
7
8
|
group :development do
|
|
8
9
|
gem 'rake', '13.2.1'
|
|
9
10
|
gem 'rspec'
|
|
10
|
-
gem 'rubocop', '1.
|
|
11
|
+
gem 'rubocop', '1.82.1'
|
|
11
12
|
end
|
|
12
13
|
|
|
13
14
|
gemspec
|
data/README.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|

|
|
10
10
|
|
|
11
|
-
- https://datatracker.ietf.org/doc/html/
|
|
11
|
+
- https://datatracker.ietf.org/doc/html/rfc9849
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
## Installation
|
|
@@ -24,7 +24,13 @@ $ gem install echspec
|
|
|
24
24
|
|
|
25
25
|
```sh-session
|
|
26
26
|
$ echspec --help
|
|
27
|
-
Usage: echspec
|
|
27
|
+
Usage: echspec {SUBCOMMAND}
|
|
28
|
+
|
|
29
|
+
Available subcommands: run, gen_configs, version, help.
|
|
30
|
+
```
|
|
31
|
+
```sh-session
|
|
32
|
+
$ echspec run --help
|
|
33
|
+
Usage: echspec run [OPTIONS...] {HOSTNAME}
|
|
28
34
|
-f, --file FILE path to ECHConfigs PEM file (default resolve ECHConfigs via DNS)
|
|
29
35
|
-p, --port VALUE server port number (default 443)
|
|
30
36
|
-n, --not-force-compliant-hpke not force compliant ECHConfig HPKE cipher suite
|
|
@@ -35,7 +41,7 @@ Usage: echspec [OPTIONS] <HOSTNAME>
|
|
|
35
41
|
You can run it the following:
|
|
36
42
|
|
|
37
43
|
```sh-session
|
|
38
|
-
$ echspec research.cloudflare.com
|
|
44
|
+
$ echspec run research.cloudflare.com
|
|
39
45
|
TLS Encrypted Client Hello Server
|
|
40
46
|
✔ MUST implement the following HPKE cipher suite: KEM: DHKEM(X25519, HKDF-SHA256), KDF: HKDF-SHA256 and AEAD: AES-128-GCM. [9]
|
|
41
47
|
✔ MUST abort with an "illegal_parameter" alert, if EncodedClientHelloInner is padded with non-zero values. [5.1-9]
|
|
@@ -54,7 +60,7 @@ TLS Encrypted Client Hello Server
|
|
|
54
60
|
Failures:
|
|
55
61
|
|
|
56
62
|
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/
|
|
63
|
+
https://datatracker.ietf.org/doc/html/rfc9849#section-7-5
|
|
58
64
|
did not send expected alert: illegal_parameter
|
|
59
65
|
|
|
60
66
|
1 failure
|
|
@@ -63,7 +69,7 @@ Failures:
|
|
|
63
69
|
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:
|
|
64
70
|
|
|
65
71
|
```sh-session
|
|
66
|
-
$ echspec -f fixtures/echconfigs.pem -p 4433 localhost
|
|
72
|
+
$ echspec run -f fixtures/echconfigs.pem -p 4433 localhost
|
|
67
73
|
```
|
|
68
74
|
|
|
69
75
|
By default, `echspec` uses the following HPKE cipher suite
|
|
@@ -78,13 +84,13 @@ By default, `echspec` uses the following HPKE cipher suite
|
|
|
78
84
|
Using the `-n` or `--not-force-compliant-hpke`, you can not enforce the HPKE cipher suite.
|
|
79
85
|
|
|
80
86
|
```sh-session
|
|
81
|
-
$ echspec -f fixtures/echconfigs.pem -p 4433 -n localhost
|
|
87
|
+
$ echspec run -f fixtures/echconfigs.pem -p 4433 -n localhost
|
|
82
88
|
```
|
|
83
89
|
|
|
84
90
|
If you specify the SECTIONS, you can run only SECTIONS the following:
|
|
85
91
|
|
|
86
92
|
```sh-session
|
|
87
|
-
$ echspec -f fixtures/echconfigs.pem -p 4433 -n -s 7.1.1-2,7.1.1-5 localhost
|
|
93
|
+
$ echspec run -f fixtures/echconfigs.pem -p 4433 -n -s 7.1.1-2,7.1.1-5 localhost
|
|
88
94
|
TLS Encrypted Client Hello Server
|
|
89
95
|
✔ MUST abort with a "missing_extension" alert, if 2nd ClientHelloOuter does not contains the "encrypted_client_hello" extension. [7.1.1-2]
|
|
90
96
|
✔ MUST abort with an "illegal_parameter" alert, if 2nd ClientHelloOuter "encrypted_client_hello" enc is empty. [7.1.1-2]
|
|
@@ -94,7 +100,7 @@ TLS Encrypted Client Hello Server
|
|
|
94
100
|
Using the `-v` or `--verbose` option provides a message stack if an error occurs. The message stack is formatted as JSON.
|
|
95
101
|
|
|
96
102
|
```sh-session
|
|
97
|
-
$ echspec -s 7-5 -v research.cloudflare.com 2>&1 > /dev/null | jq .
|
|
103
|
+
$ echspec run -s 7-5 -v research.cloudflare.com 2>&1 > /dev/null | jq .
|
|
98
104
|
````
|
|
99
105
|
|
|
100
106
|
<details>
|
data/docs/echspec-demo.png
CHANGED
|
Binary file
|
data/echspec.gemspec
CHANGED
|
@@ -11,16 +11,20 @@ Gem::Specification.new do |spec|
|
|
|
11
11
|
spec.description = spec.summary
|
|
12
12
|
spec.homepage = 'https://github.com/thekuwayama/echspec'
|
|
13
13
|
spec.license = 'MIT'
|
|
14
|
-
spec.required_ruby_version = '>=
|
|
14
|
+
spec.required_ruby_version = '>=4.0'
|
|
15
15
|
|
|
16
|
-
spec.files
|
|
17
|
-
|
|
16
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
|
17
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
|
18
|
+
(f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
|
|
19
|
+
end
|
|
20
|
+
end
|
|
18
21
|
spec.require_paths = ['lib']
|
|
19
22
|
spec.bindir = 'exe'
|
|
20
23
|
spec.executables = ['echspec']
|
|
21
24
|
|
|
22
25
|
spec.add_development_dependency 'bundler'
|
|
23
26
|
spec.add_dependency 'base64'
|
|
27
|
+
spec.add_dependency 'ech_config', '~> 0.0.4'
|
|
24
28
|
spec.add_dependency 'resolv', '> 0.4.0'
|
|
25
|
-
spec.add_dependency 'tttls1.3', '~> 0.3.
|
|
29
|
+
spec.add_dependency 'tttls1.3', '~> 0.3.6'
|
|
26
30
|
end
|
data/exe/echspec
CHANGED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
module EchSpec
|
|
2
|
+
class CLI
|
|
3
|
+
class GenConfigs
|
|
4
|
+
def execute(argv)
|
|
5
|
+
fpath = parse_options(argv)
|
|
6
|
+
write(fpath)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def parse_options(argv)
|
|
10
|
+
op = OptionParser.new
|
|
11
|
+
|
|
12
|
+
op.banner = 'Usage: echspec gen_configs {FILE}'
|
|
13
|
+
|
|
14
|
+
begin
|
|
15
|
+
args = op.parse(argv)
|
|
16
|
+
rescue OptionParser::InvalidOption, OptionParser::MissingArgument => e
|
|
17
|
+
warn op
|
|
18
|
+
warn "** #{e.message}"
|
|
19
|
+
exit 1
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
if args.length != 1
|
|
23
|
+
warn op
|
|
24
|
+
warn '** {FILE} argument is not specified'
|
|
25
|
+
exit 1
|
|
26
|
+
end
|
|
27
|
+
args[0]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def write(fpath)
|
|
31
|
+
hostname = 'localhost'
|
|
32
|
+
|
|
33
|
+
key = OpenSSL::PKey.generate_key('X25519')
|
|
34
|
+
echconfigs = ECHConfigList.new(
|
|
35
|
+
[
|
|
36
|
+
ECHConfig.new(
|
|
37
|
+
"\xfe\x0d".b,
|
|
38
|
+
ECHConfig::ECHConfigContents.new(
|
|
39
|
+
ECHConfig::ECHConfigContents::HpkeKeyConfig.new(
|
|
40
|
+
123,
|
|
41
|
+
ECHConfig::ECHConfigContents::HpkeKeyConfig::HpkeKemId.new(HPKE::DHKEM_X25519_HKDF_SHA256),
|
|
42
|
+
ECHConfig::ECHConfigContents::HpkeKeyConfig::HpkePublicKey.new(key.raw_public_key),
|
|
43
|
+
[
|
|
44
|
+
ECHConfig::ECHConfigContents::HpkeKeyConfig::HpkeSymmetricCipherSuite.new(
|
|
45
|
+
ECHConfig::ECHConfigContents::HpkeKeyConfig::HpkeSymmetricCipherSuite::HpkeKdfId.new(HPKE::HKDF_SHA256),
|
|
46
|
+
ECHConfig::ECHConfigContents::HpkeKeyConfig::HpkeSymmetricCipherSuite::HpkeAeadId.new(HPKE::AES_128_GCM)
|
|
47
|
+
)
|
|
48
|
+
]
|
|
49
|
+
),
|
|
50
|
+
32,
|
|
51
|
+
hostname.b,
|
|
52
|
+
ECHConfig::ECHConfigContents::Extensions.new('')
|
|
53
|
+
)
|
|
54
|
+
)
|
|
55
|
+
]
|
|
56
|
+
)
|
|
57
|
+
File.write(fpath, key.private_to_pem + echconfigs.to_pem)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
module EchSpec
|
|
2
|
+
class CLI
|
|
3
|
+
class Run
|
|
4
|
+
def execute(argv)
|
|
5
|
+
fpath, port, force_compliant, verbose, hostname, sections = parse_options(argv)
|
|
6
|
+
|
|
7
|
+
if sections.nil?
|
|
8
|
+
Spec.run(fpath, port, hostname, force_compliant, verbose)
|
|
9
|
+
else
|
|
10
|
+
Spec.run_only(fpath, port, hostname, sections, verbose)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# rubocop: disable Metrics/AbcSize
|
|
15
|
+
# rubocop: disable Metrics/MethodLength
|
|
16
|
+
def parse_options(argv)
|
|
17
|
+
op = OptionParser.new
|
|
18
|
+
|
|
19
|
+
# default value
|
|
20
|
+
fpath = nil
|
|
21
|
+
port = 443
|
|
22
|
+
force_compliant = true
|
|
23
|
+
verbose = false
|
|
24
|
+
sections = nil
|
|
25
|
+
|
|
26
|
+
op.on(
|
|
27
|
+
'-f',
|
|
28
|
+
'--file FILE',
|
|
29
|
+
'path to ECHConfigs PEM file (default resolve ECHConfigs via DNS)'
|
|
30
|
+
) do |v|
|
|
31
|
+
fpath = v
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
op.on(
|
|
35
|
+
'-p',
|
|
36
|
+
'--port VALUE',
|
|
37
|
+
"server port number (default #{port})"
|
|
38
|
+
) do |v|
|
|
39
|
+
port = v
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
op.on(
|
|
43
|
+
'-n',
|
|
44
|
+
'--not-force-compliant-hpke',
|
|
45
|
+
'not force compliant ECHConfig HPKE cipher suite'
|
|
46
|
+
) do
|
|
47
|
+
force_compliant = false
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
op.on(
|
|
51
|
+
'-v',
|
|
52
|
+
'--verbose',
|
|
53
|
+
'verbose mode; prints message stack if raised an error'
|
|
54
|
+
) do
|
|
55
|
+
verbose = true
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
op.on(
|
|
59
|
+
'-s',
|
|
60
|
+
'--sections SECTIONS',
|
|
61
|
+
'sections to test; by the default, test all sections'
|
|
62
|
+
) do |v|
|
|
63
|
+
sections = v.split(',')
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
op.banner = 'Usage: echspec run [OPTIONS...] {HOSTNAME}'
|
|
67
|
+
|
|
68
|
+
begin
|
|
69
|
+
args = op.parse(argv)
|
|
70
|
+
rescue OptionParser::InvalidOption, OptionParser::MissingArgument => e
|
|
71
|
+
warn op
|
|
72
|
+
warn "** #{e.message}"
|
|
73
|
+
exit 1
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
if !fpath.nil? && !File.exist?(fpath)
|
|
77
|
+
warn '** {FILE} is not found'
|
|
78
|
+
exit 1
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
unknowns = sections.nil? ? [] : sections - Spec.sections
|
|
82
|
+
unless unknowns.empty?
|
|
83
|
+
warn "** #{unknowns} are unknown sections"
|
|
84
|
+
exit 1
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
if args.length != 1
|
|
88
|
+
warn op
|
|
89
|
+
warn '** {HOSTNAME} argument is not specified'
|
|
90
|
+
exit 1
|
|
91
|
+
end
|
|
92
|
+
hostname = args[0]
|
|
93
|
+
|
|
94
|
+
[fpath, port, force_compliant, verbose, hostname, sections]
|
|
95
|
+
end
|
|
96
|
+
# rubocop: enable Metrics/AbcSize
|
|
97
|
+
# rubocop: enable Metrics/MethodLength
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
data/lib/echspec/cli.rb
CHANGED
|
@@ -1,96 +1,33 @@
|
|
|
1
|
+
require_relative 'cli/gen_configs'
|
|
2
|
+
require_relative 'cli/run'
|
|
3
|
+
|
|
1
4
|
module EchSpec
|
|
2
5
|
class CLI
|
|
3
|
-
|
|
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
|
|
6
|
+
using Refinements
|
|
63
7
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
exit 1
|
|
67
|
-
end
|
|
8
|
+
def execute(argv = ARGV)
|
|
9
|
+
subcommands = %i[run gen_configs]
|
|
68
10
|
|
|
69
|
-
|
|
70
|
-
unless unknowns.empty?
|
|
71
|
-
warn "** #{unknowns} are unknown sections"
|
|
72
|
-
exit 1
|
|
73
|
-
end
|
|
11
|
+
op = OptionParser.new
|
|
74
12
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
warn '** <HOSTNAME> argument is not specified'
|
|
78
|
-
exit 1
|
|
79
|
-
end
|
|
80
|
-
hostname = args[0]
|
|
13
|
+
op.banner = <<~USAGE
|
|
14
|
+
Usage: echspec {SUBCOMMAND}
|
|
81
15
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
# rubocop: enable Metrics/AbcSize
|
|
85
|
-
# rubocop: enable Metrics/MethodLength
|
|
16
|
+
Available subcommands: #{subcommands.join(', ')}, version, help.
|
|
17
|
+
USAGE
|
|
86
18
|
|
|
87
|
-
|
|
88
|
-
|
|
19
|
+
op.version = EchSpec::VERSION
|
|
20
|
+
op.order!(argv)
|
|
89
21
|
|
|
90
|
-
|
|
91
|
-
|
|
22
|
+
subcommand = argv.shift
|
|
23
|
+
case subcommand&.to_sym
|
|
24
|
+
when :version
|
|
25
|
+
puts EchSpec::VERSION
|
|
26
|
+
when *subcommands
|
|
27
|
+
klass = self.class.const_get(subcommand.to_camel)
|
|
28
|
+
klass.new.__send__(:execute, argv)
|
|
92
29
|
else
|
|
93
|
-
|
|
30
|
+
puts op
|
|
94
31
|
end
|
|
95
32
|
end
|
|
96
33
|
end
|
data/lib/echspec/spec/5.1-10.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
module EchSpec
|
|
2
2
|
module Spec
|
|
3
3
|
class Spec5_1_10 < WithSocket
|
|
4
|
-
# Next it makes a copy of the client_hello field and copies the
|
|
4
|
+
# Next, it makes a copy of the client_hello field and copies the
|
|
5
5
|
# legacy_session_id field from ClientHelloOuter. It then looks for an
|
|
6
6
|
# "ech_outer_extensions" extension. If found, it replaces the extension
|
|
7
7
|
# with the corresponding sequence of extensions in the
|
|
@@ -14,7 +14,7 @@ module EchSpec
|
|
|
14
14
|
# * The extensions in ClientHelloOuter corresponding to those in
|
|
15
15
|
# OuterExtensions do not occur in the same order.
|
|
16
16
|
#
|
|
17
|
-
# https://datatracker.ietf.org/doc/html/
|
|
17
|
+
# https://datatracker.ietf.org/doc/html/rfc9849#section-5.1-10
|
|
18
18
|
|
|
19
19
|
# @return [EchSpec::SpecGroup]
|
|
20
20
|
def self.spec_group
|
data/lib/echspec/spec/5.1-9.rb
CHANGED
|
@@ -2,12 +2,12 @@ module EchSpec
|
|
|
2
2
|
module Spec
|
|
3
3
|
class Spec5_1_9 < WithSocket
|
|
4
4
|
# The client-facing server computes ClientHelloInner by reversing this
|
|
5
|
-
# process. First it parses EncodedClientHelloInner, interpreting all
|
|
6
|
-
# bytes after client_hello as padding. If any padding byte is non-
|
|
7
|
-
#
|
|
8
|
-
#
|
|
5
|
+
# process. First, it parses EncodedClientHelloInner, interpreting all
|
|
6
|
+
# bytes after client_hello as padding. If any padding byte is non-zero,
|
|
7
|
+
# the server MUST abort the connection with an "illegal_parameter"
|
|
8
|
+
# alert.
|
|
9
9
|
#
|
|
10
|
-
# https://datatracker.ietf.org/doc/html/
|
|
10
|
+
# https://datatracker.ietf.org/doc/html/rfc9849#section-5.1-9
|
|
11
11
|
|
|
12
12
|
# @return [EchSpec::SpecGroup]
|
|
13
13
|
def self.spec_group
|
data/lib/echspec/spec/7-5.rb
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
module EchSpec
|
|
2
2
|
module Spec
|
|
3
3
|
class Spec7_5 < WithSocket
|
|
4
|
-
#
|
|
5
|
-
#
|
|
4
|
+
# In shared mode, a server plays both roles, first decrypting the
|
|
5
|
+
# ClientHelloOuter and then using the contents of the ClientHelloInner.
|
|
6
|
+
# A shared mode server which receives a ClientHello with
|
|
7
|
+
# ECHClientHello.type of inner MUST abort with an "illegal_parameter"
|
|
8
|
+
# alert, because such a ClientHello should never be received directly
|
|
9
|
+
# from the network.
|
|
6
10
|
#
|
|
7
|
-
# https://datatracker.ietf.org/doc/html/
|
|
11
|
+
# https://datatracker.ietf.org/doc/html/rfc9849#section-7-5
|
|
8
12
|
|
|
9
13
|
# @return [EchSpec::SpecGroup]
|
|
10
14
|
def self.spec_group
|
data/lib/echspec/spec/7.1-11.rb
CHANGED
|
@@ -7,7 +7,7 @@ module EchSpec
|
|
|
7
7
|
# offer TLS 1.2 or below. If either of these checks fails, the client-
|
|
8
8
|
# facing server MUST abort with an "illegal_parameter" alert.
|
|
9
9
|
#
|
|
10
|
-
# https://datatracker.ietf.org/doc/html/
|
|
10
|
+
# https://datatracker.ietf.org/doc/html/rfc9849#section-7.1-11
|
|
11
11
|
|
|
12
12
|
# @return [SpecGroup]
|
|
13
13
|
def self.spec_group
|
|
@@ -3,8 +3,8 @@ module EchSpec
|
|
|
3
3
|
class Spec7_1_14_2_1 < WithSocket
|
|
4
4
|
# Otherwise, if all candidate ECHConfig values fail to decrypt the
|
|
5
5
|
# extension, the client-facing server MUST ignore the extension and
|
|
6
|
-
# proceed with the connection using ClientHelloOuter
|
|
7
|
-
#
|
|
6
|
+
# proceed with the connection using ClientHelloOuter with the following
|
|
7
|
+
# modifications:
|
|
8
8
|
#
|
|
9
9
|
# * If the server is configured with any ECHConfigs, it MUST include
|
|
10
10
|
# the "encrypted_client_hello" extension in its EncryptedExtensions
|
|
@@ -13,7 +13,7 @@ module EchSpec
|
|
|
13
13
|
# ECHConfig values of different versions. This allows a server to
|
|
14
14
|
# support multiple versions at once.
|
|
15
15
|
#
|
|
16
|
-
# https://datatracker.ietf.org/doc/html/
|
|
16
|
+
# https://datatracker.ietf.org/doc/html/rfc9849#section-7.1-14.2.1
|
|
17
17
|
|
|
18
18
|
# @return [EchSpec::SpecGroup]
|
|
19
19
|
def self.spec_group
|
data/lib/echspec/spec/7.1.1-2.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
module EchSpec
|
|
2
2
|
module Spec
|
|
3
3
|
class Spec7_1_1_2 < WithSocket
|
|
4
|
-
# If the client-facing server accepted ECH, it checks the second
|
|
4
|
+
# If the client-facing server accepted ECH, it checks that the second
|
|
5
5
|
# ClientHelloOuter also contains the "encrypted_client_hello"
|
|
6
6
|
# extension. If not, it MUST abort the handshake with a
|
|
7
7
|
# "missing_extension" alert. Otherwise, it checks that
|
|
@@ -9,7 +9,7 @@ module EchSpec
|
|
|
9
9
|
# unchanged, and that ECHClientHello.enc is empty. If not, it MUST
|
|
10
10
|
# abort the handshake with an "illegal_parameter" alert.
|
|
11
11
|
#
|
|
12
|
-
# https://datatracker.ietf.org/doc/html/
|
|
12
|
+
# https://datatracker.ietf.org/doc/html/rfc9849#section-7.1.1-2
|
|
13
13
|
|
|
14
14
|
# @return [EchSpec::SpecGroup]
|
|
15
15
|
def self.spec_group
|
data/lib/echspec/spec/7.1.1-5.rb
CHANGED
|
@@ -5,10 +5,10 @@ module EchSpec
|
|
|
5
5
|
# using the second ClientHelloOuter. If decryption fails, the client-
|
|
6
6
|
# facing server MUST abort the handshake with a "decrypt_error" alert.
|
|
7
7
|
# Otherwise, it reconstructs the second ClientHelloInner from the new
|
|
8
|
-
# EncodedClientHelloInner as described in Section 5.1, using the
|
|
9
|
-
#
|
|
8
|
+
# EncodedClientHelloInner as described in Section 5.1, using the second
|
|
9
|
+
# ClientHelloOuter for any referenced extensions.
|
|
10
10
|
#
|
|
11
|
-
# https://datatracker.ietf.org/doc/html/
|
|
11
|
+
# https://datatracker.ietf.org/doc/html/rfc9849#section-7.1.1-5
|
|
12
12
|
|
|
13
13
|
# @return [EchSpec::SpecGroup]
|
|
14
14
|
def self.spec_group
|
data/lib/echspec/spec/9.rb
CHANGED
|
@@ -9,7 +9,7 @@ module EchSpec
|
|
|
9
9
|
# * KDF: HKDF-SHA256 (see Section 7.2 of [HPKE])
|
|
10
10
|
# * AEAD: AES-128-GCM (see Section 7.3 of [HPKE])
|
|
11
11
|
#
|
|
12
|
-
# https://datatracker.ietf.org/doc/html/
|
|
12
|
+
# https://datatracker.ietf.org/doc/html/rfc9849#section-9
|
|
13
13
|
@section = '9'
|
|
14
14
|
@description = 'MUST implement the following HPKE cipher suite: KEM: DHKEM(X25519, HKDF-SHA256), KDF: HKDF-SHA256 and AEAD: AES-128-GCM.'
|
|
15
15
|
class << self
|
|
@@ -47,9 +47,9 @@ module EchSpec
|
|
|
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
|
|
50
|
-
valid_kem_id = kconfig.kem_id.uint16 ==
|
|
50
|
+
valid_kem_id = kconfig.kem_id.uint16 == HPKE::DHKEM_X25519_HKDF_SHA256
|
|
51
51
|
valid_cipher_suite = kconfig.cipher_suites.any? do |cs|
|
|
52
|
-
cs.kdf_id.uint16 ==
|
|
52
|
+
cs.kdf_id.uint16 == HPKE::HKDF_SHA256 && cs.aead_id.uint16 == HPKE::AES_128_GCM
|
|
53
53
|
end
|
|
54
54
|
|
|
55
55
|
valid_kem_id && valid_cipher_suite
|
|
@@ -72,7 +72,7 @@ module EchSpec
|
|
|
72
72
|
return Err.new(e.message, nil)
|
|
73
73
|
end
|
|
74
74
|
|
|
75
|
-
# https://datatracker.ietf.org/doc/html/
|
|
75
|
+
# https://datatracker.ietf.org/doc/html/rfc9934#section-3
|
|
76
76
|
ech = 5
|
|
77
77
|
return Err.new("HTTPS resource record for #{hostname} does NOT have ech SvcParams.", nil) if rr.params[ech].nil?
|
|
78
78
|
|
|
@@ -95,7 +95,7 @@ module EchSpec
|
|
|
95
95
|
ech_configs = ECHConfig.decode_vectors(b.slice(2..))
|
|
96
96
|
Ok.new(ech_configs)
|
|
97
97
|
rescue StandardError
|
|
98
|
-
# https://datatracker.ietf.org/doc/html/
|
|
98
|
+
# https://datatracker.ietf.org/doc/html/rfc9934#section-3
|
|
99
99
|
example = <<~PEM
|
|
100
100
|
-----BEGIN PRIVATE KEY-----
|
|
101
101
|
MC4CAQAwBQYDK2VuBCIEICjd4yGRdsoP9gU7YT7My8DHx1Tjme8GYDXrOMCi8v1V
|
data/lib/echspec/spec.rb
CHANGED
|
@@ -14,7 +14,7 @@ module EchSpec
|
|
|
14
14
|
|
|
15
15
|
ResultDescURL = Struct.new(:result, :desc, :url)
|
|
16
16
|
|
|
17
|
-
# @param
|
|
17
|
+
# @param rdus [Array<ResultDescURL>] result: EchSpec::Ok | Err, desc: String, url: URI
|
|
18
18
|
# @param verbose [Boolean]
|
|
19
19
|
def print_results(rdus, verbose)
|
|
20
20
|
rdus.each { |rdu| print_summary(rdu.result, rdu.desc) }
|
|
@@ -148,7 +148,7 @@ module EchSpec
|
|
|
148
148
|
#
|
|
149
149
|
# @return [String]
|
|
150
150
|
def url(section)
|
|
151
|
-
"https://datatracker.ietf.org/doc/html/
|
|
151
|
+
"https://datatracker.ietf.org/doc/html/rfc9849#section-#{section}"
|
|
152
152
|
end
|
|
153
153
|
|
|
154
154
|
# @param fpath [String | NilClass]
|
|
@@ -157,7 +157,7 @@ module EchSpec
|
|
|
157
157
|
#
|
|
158
158
|
# @return [ECHConfig]
|
|
159
159
|
def try_get_ech_config(fpath, hostname, force_compliant)
|
|
160
|
-
# https://datatracker.ietf.org/doc/html/
|
|
160
|
+
# https://datatracker.ietf.org/doc/html/rfc9849#section-9
|
|
161
161
|
result = Spec9.try_get_ech_config(fpath, hostname, force_compliant)
|
|
162
162
|
desc = desc(Spec9.description, Spec9.section)
|
|
163
163
|
url = url(Spec9.section)
|
|
@@ -175,10 +175,10 @@ module EchSpec
|
|
|
175
175
|
end
|
|
176
176
|
|
|
177
177
|
def spec_groups
|
|
178
|
-
# https://datatracker.ietf.org/doc/html/
|
|
178
|
+
# https://datatracker.ietf.org/doc/html/rfc9849#section-5
|
|
179
179
|
groups = [Spec5_1_9, Spec5_1_10]
|
|
180
180
|
|
|
181
|
-
# https://datatracker.ietf.org/doc/html/
|
|
181
|
+
# https://datatracker.ietf.org/doc/html/rfc9849#section-7
|
|
182
182
|
groups += [Spec7_5, Spec7_1_11, Spec7_1_14_2_1, Spec7_1_1_2, Spec7_1_1_5]
|
|
183
183
|
|
|
184
184
|
groups.map(&:spec_group)
|
data/lib/echspec/utils.rb
CHANGED
data/lib/echspec/version.rb
CHANGED
data/lib/echspec.rb
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
require 'base64'
|
|
2
2
|
require 'optparse'
|
|
3
|
+
require 'pp'
|
|
3
4
|
require 'resolv'
|
|
4
5
|
require 'timeout'
|
|
5
6
|
require 'tttls1.3'
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
8
|
+
require_relative 'echspec/version'
|
|
9
|
+
require_relative 'echspec/utils'
|
|
10
|
+
require_relative 'echspec/log'
|
|
11
|
+
require_relative 'echspec/error'
|
|
12
|
+
require_relative 'echspec/result'
|
|
13
|
+
require_relative 'echspec/tls13_client'
|
|
14
|
+
require_relative 'echspec/spec_case'
|
|
15
|
+
require_relative 'echspec/spec_group'
|
|
16
|
+
require_relative 'echspec/spec'
|
|
17
|
+
require_relative 'echspec/cli'
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
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.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- thekuwayama
|
|
@@ -37,6 +37,20 @@ dependencies:
|
|
|
37
37
|
- - ">="
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
39
|
version: '0'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: ech_config
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: 0.0.4
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: 0.0.4
|
|
40
54
|
- !ruby/object:Gem::Dependency
|
|
41
55
|
name: resolv
|
|
42
56
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -57,14 +71,14 @@ dependencies:
|
|
|
57
71
|
requirements:
|
|
58
72
|
- - "~>"
|
|
59
73
|
- !ruby/object:Gem::Version
|
|
60
|
-
version: 0.3.
|
|
74
|
+
version: 0.3.6
|
|
61
75
|
type: :runtime
|
|
62
76
|
prerelease: false
|
|
63
77
|
version_requirements: !ruby/object:Gem::Requirement
|
|
64
78
|
requirements:
|
|
65
79
|
- - "~>"
|
|
66
80
|
- !ruby/object:Gem::Version
|
|
67
|
-
version: 0.3.
|
|
81
|
+
version: 0.3.6
|
|
68
82
|
description: A conformance testing tool for ECH implementation
|
|
69
83
|
email:
|
|
70
84
|
- thekuwayama@gmail.com
|
|
@@ -73,8 +87,6 @@ executables:
|
|
|
73
87
|
extensions: []
|
|
74
88
|
extra_rdoc_files: []
|
|
75
89
|
files:
|
|
76
|
-
- ".github/workflows/ci.yml"
|
|
77
|
-
- ".gitignore"
|
|
78
90
|
- ".rubocop.yml"
|
|
79
91
|
- ".ruby-version"
|
|
80
92
|
- Gemfile
|
|
@@ -89,6 +101,8 @@ files:
|
|
|
89
101
|
- fixtures/server.key
|
|
90
102
|
- lib/echspec.rb
|
|
91
103
|
- lib/echspec/cli.rb
|
|
104
|
+
- lib/echspec/cli/gen_configs.rb
|
|
105
|
+
- lib/echspec/cli/run.rb
|
|
92
106
|
- lib/echspec/error.rb
|
|
93
107
|
- lib/echspec/log.rb
|
|
94
108
|
- lib/echspec/result.rb
|
|
@@ -106,10 +120,6 @@ files:
|
|
|
106
120
|
- lib/echspec/tls13_client.rb
|
|
107
121
|
- lib/echspec/utils.rb
|
|
108
122
|
- lib/echspec/version.rb
|
|
109
|
-
- spec/9_spec.rb
|
|
110
|
-
- spec/log_spec.rb
|
|
111
|
-
- spec/spec_helper.rb
|
|
112
|
-
- spec/with_socket_spec.rb
|
|
113
123
|
homepage: https://github.com/thekuwayama/echspec
|
|
114
124
|
licenses:
|
|
115
125
|
- MIT
|
|
@@ -121,18 +131,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
121
131
|
requirements:
|
|
122
132
|
- - ">="
|
|
123
133
|
- !ruby/object:Gem::Version
|
|
124
|
-
version: '
|
|
134
|
+
version: '4.0'
|
|
125
135
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
126
136
|
requirements:
|
|
127
137
|
- - ">="
|
|
128
138
|
- !ruby/object:Gem::Version
|
|
129
139
|
version: '0'
|
|
130
140
|
requirements: []
|
|
131
|
-
rubygems_version:
|
|
141
|
+
rubygems_version: 4.0.7
|
|
132
142
|
specification_version: 4
|
|
133
143
|
summary: A conformance testing tool for ECH implementation
|
|
134
|
-
test_files:
|
|
135
|
-
- spec/9_spec.rb
|
|
136
|
-
- spec/log_spec.rb
|
|
137
|
-
- spec/spec_helper.rb
|
|
138
|
-
- spec/with_socket_spec.rb
|
|
144
|
+
test_files: []
|
data/.github/workflows/ci.yml
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
|
19
|
-
- name: Set up Ruby
|
|
20
|
-
uses: ruby/setup-ruby@eaecf785f6a34567a6d97f686bbb7bccc1ac1e5c # v1.237.0
|
|
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
DELETED
data/spec/9_spec.rb
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
require_relative 'spec_helper'
|
|
2
|
-
|
|
3
|
-
RSpec.describe EchSpec::Spec::Spec9 do
|
|
4
|
-
context 'parse_pem' do
|
|
5
|
-
let(:pem) do
|
|
6
|
-
File.open("#{__dir__}/../fixtures/echconfigs.pem").read
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
it 'could parse' do
|
|
10
|
-
expect(EchSpec::Spec::Spec9.send(:parse_pem, pem)).to be_a EchSpec::Ok
|
|
11
|
-
end
|
|
12
|
-
end
|
|
13
|
-
end
|
data/spec/log_spec.rb
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
require_relative 'spec_helper'
|
|
2
|
-
|
|
3
|
-
RSpec.describe EchSpec::Log::MessageStack do
|
|
4
|
-
context 'obj2json' do
|
|
5
|
-
let(:crt) do
|
|
6
|
-
File.open("#{__dir__}/../fixtures/server.crt").read
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
it 'should convert' do
|
|
10
|
-
expect(EchSpec::Log::MessageStack.obj2json(OpenSSL::X509::Certificate.new(crt)))
|
|
11
|
-
.to eq "#{crt.split("\n").join('\n')}\\n"
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
it 'should convert' do
|
|
15
|
-
expect(EchSpec::Log::MessageStack.obj2json(1)).to eq '1'
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
it 'should convert' do
|
|
19
|
-
expect(EchSpec::Log::MessageStack.obj2json(0.1)).to eq '0.1'
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
it 'should convert' do
|
|
23
|
-
expect(EchSpec::Log::MessageStack.obj2json(true)).to eq 'true'
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
it 'should convert' do
|
|
27
|
-
expect(EchSpec::Log::MessageStack.obj2json(false)).to eq 'false'
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
it 'should convert' do
|
|
31
|
-
expect(EchSpec::Log::MessageStack.obj2json('')).to eq '""'
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
it 'should convert' do
|
|
35
|
-
expect(EchSpec::Log::MessageStack.obj2json('string')).to eq '"0x737472696e67"'
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
it 'should convert' do
|
|
39
|
-
expect(EchSpec::Log::MessageStack.obj2json(nil)).to eq 'null'
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
it 'should convert' do
|
|
43
|
-
expect(EchSpec::Log::MessageStack.obj2json([1, true, '', 'string', nil])).to eq '[1,true,"","0x737472696e67",null]'
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
it 'should convert' do
|
|
47
|
-
expect(EchSpec::Log::MessageStack.obj2json(1 => true, '' => 'string', nil => [])).to eq '{1:true,"":"0x737472696e67",null:[]}'
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
it 'should convert' do
|
|
51
|
-
expect(EchSpec::Log::MessageStack.obj2json(C.new('string'))).to eq '{"name":"0x737472696e67"}'
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
it 'should convert' do
|
|
55
|
-
expect(EchSpec::Log::MessageStack.obj2json(D.new)).to eq '"$D"'
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
end
|
data/spec/spec_helper.rb
DELETED
data/spec/with_socket_spec.rb
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
require_relative 'spec_helper'
|
|
2
|
-
|
|
3
|
-
module EchSpec
|
|
4
|
-
module Spec
|
|
5
|
-
class SpecX < WithSocket
|
|
6
|
-
def validate(hostname, port)
|
|
7
|
-
with_socket(hostname, port) do |_socket|
|
|
8
|
-
# not return
|
|
9
|
-
end
|
|
10
|
-
end
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
class SpecY < WithSocket
|
|
14
|
-
def validate(hostname, port)
|
|
15
|
-
with_socket(hostname, port) do |_socket|
|
|
16
|
-
return EchSpec::Ok.new(1)
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
class SpecZ < WithSocket
|
|
22
|
-
def validate(hostname, port)
|
|
23
|
-
with_socket(hostname, port) do |_socket|
|
|
24
|
-
msg = TTTLS13::Message::Alert.new(
|
|
25
|
-
level: TTTLS13::Message::AlertLevel::FATAL,
|
|
26
|
-
description: "\x0a"
|
|
27
|
-
)
|
|
28
|
-
return EchSpec::Err.new('details', [msg])
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
class SpecW < WithSocket
|
|
34
|
-
def validate(hostname, port)
|
|
35
|
-
with_socket(hostname, port) do |_socket|
|
|
36
|
-
raise EchSpec::Error::BeforeTargetSituationError, 'not received ClientHello'
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
RSpec.describe EchSpec::Spec::WithSocket do
|
|
44
|
-
context 'with_socket' do
|
|
45
|
-
before do
|
|
46
|
-
socket = StringIO.new
|
|
47
|
-
allow(TCPSocket).to receive(:new).and_return(socket)
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
it 'should return nil' do
|
|
51
|
-
result = EchSpec::Spec::SpecX.new.validate('localhost', 4433)
|
|
52
|
-
expect(result).to eq nil
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
it 'should return Ok(1)' do
|
|
56
|
-
result = EchSpec::Spec::SpecY.new.validate('localhost', 4433)
|
|
57
|
-
expect(result).to be_a EchSpec::Ok
|
|
58
|
-
expect(result.obj).to eq 1
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
it 'should return Err(details, message_stack)' do
|
|
62
|
-
result = EchSpec::Spec::SpecZ.new.validate('localhost', 4433)
|
|
63
|
-
expect(result).to be_a EchSpec::Err
|
|
64
|
-
expect(result.details).to eq 'details'
|
|
65
|
-
expect(result.message_stack.length).to be 1
|
|
66
|
-
expect(result.message_stack.first.level).to be TTTLS13::Message::AlertLevel::FATAL
|
|
67
|
-
expect(result.message_stack.first.description).to eq "\x0a"
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
it 'should return Err(details, message_stack), raised BeforeTargetSituationError' do
|
|
71
|
-
result = EchSpec::Spec::SpecW.new.validate('localhost', 4433)
|
|
72
|
-
expect(result).to be_a EchSpec::Err
|
|
73
|
-
expect(result.details).to eq 'not received ClientHello'
|
|
74
|
-
expect(result.message_stack).to eq '{}'
|
|
75
|
-
end
|
|
76
|
-
end
|
|
77
|
-
end
|