echspec 0.0.3 → 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/README.md +12 -6
- data/Rakefile +0 -41
- data/docs/echspec-demo.png +0 -0
- 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/utils.rb +4 -0
- data/lib/echspec/version.rb +1 -1
- metadata +3 -1
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/README.md
CHANGED
|
@@ -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]
|
|
@@ -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/Rakefile
CHANGED
|
@@ -1,49 +1,8 @@
|
|
|
1
1
|
require 'bundler/gem_tasks'
|
|
2
|
-
require 'ech_config'
|
|
3
|
-
require 'hpke'
|
|
4
|
-
require 'openssl'
|
|
5
2
|
require 'rspec/core/rake_task'
|
|
6
3
|
require 'rubocop/rake_task'
|
|
7
4
|
|
|
8
5
|
RuboCop::RakeTask.new
|
|
9
6
|
RSpec::Core::RakeTask.new(:spec)
|
|
10
7
|
|
|
11
|
-
TMP_DIR = "#{__dir__}/tmp".freeze
|
|
12
|
-
ECHCONFIGS = "#{TMP_DIR}/echconfigs.pem".freeze
|
|
13
|
-
|
|
14
|
-
directory TMP_DIR
|
|
15
|
-
|
|
16
|
-
file ECHCONFIGS => TMP_DIR do
|
|
17
|
-
puts "generate #{ECHCONFIGS}..."
|
|
18
|
-
|
|
19
|
-
key = OpenSSL::PKey.generate_key('X25519')
|
|
20
|
-
echconfigs = ECHConfigList.new(
|
|
21
|
-
[
|
|
22
|
-
ECHConfig.new(
|
|
23
|
-
"\xfe\x0d".b,
|
|
24
|
-
ECHConfig::ECHConfigContents.new(
|
|
25
|
-
ECHConfig::ECHConfigContents::HpkeKeyConfig.new(
|
|
26
|
-
123,
|
|
27
|
-
ECHConfig::ECHConfigContents::HpkeKeyConfig::HpkeKemId.new(HPKE::DHKEM_X25519_HKDF_SHA256),
|
|
28
|
-
ECHConfig::ECHConfigContents::HpkeKeyConfig::HpkePublicKey.new(key.raw_public_key),
|
|
29
|
-
[
|
|
30
|
-
ECHConfig::ECHConfigContents::HpkeKeyConfig::HpkeSymmetricCipherSuite.new(
|
|
31
|
-
ECHConfig::ECHConfigContents::HpkeKeyConfig::HpkeSymmetricCipherSuite::HpkeKdfId.new(HPKE::HKDF_SHA256),
|
|
32
|
-
ECHConfig::ECHConfigContents::HpkeKeyConfig::HpkeSymmetricCipherSuite::HpkeAeadId.new(HPKE::AES_128_GCM)
|
|
33
|
-
)
|
|
34
|
-
]
|
|
35
|
-
),
|
|
36
|
-
32,
|
|
37
|
-
'localhost'.b,
|
|
38
|
-
ECHConfig::ECHConfigContents::Extensions.new('')
|
|
39
|
-
)
|
|
40
|
-
)
|
|
41
|
-
]
|
|
42
|
-
)
|
|
43
|
-
File.write(ECHCONFIGS, key.private_to_pem + echconfigs.to_pem)
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
desc 'generate echconfigs file'
|
|
47
|
-
task gen_echconfigs: ECHCONFIGS
|
|
48
|
-
|
|
49
8
|
task default: %i[rubocop spec]
|
data/docs/echspec-demo.png
CHANGED
|
Binary file
|
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/utils.rb
CHANGED
data/lib/echspec/version.rb
CHANGED
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
|
|
@@ -101,6 +101,8 @@ files:
|
|
|
101
101
|
- fixtures/server.key
|
|
102
102
|
- lib/echspec.rb
|
|
103
103
|
- lib/echspec/cli.rb
|
|
104
|
+
- lib/echspec/cli/gen_configs.rb
|
|
105
|
+
- lib/echspec/cli/run.rb
|
|
104
106
|
- lib/echspec/error.rb
|
|
105
107
|
- lib/echspec/log.rb
|
|
106
108
|
- lib/echspec/result.rb
|