echspec 0.0.3 → 0.0.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4ff77e42c8a7e2d787372fe6f96f3c00619fb6bcb3feea8df89c5400a838aef6
4
- data.tar.gz: d317448071982052658909b19a99f535ce99e1c9f063de65be9cecae1ab4179f
3
+ metadata.gz: bce9c266ff5ece6660c7adab52de934378be099778a00520e5f758acf3052aae
4
+ data.tar.gz: 5675044be1620e08f228fbb3b8067ababb0764522b462456d7c3534cf974c2a0
5
5
  SHA512:
6
- metadata.gz: 7c4eea37fd0a7002bf6fd2a68b0a5c299bf84b43a71933053fb6cf2df59aedc17836e3299e809d1e5889130ccfcbd6ec19a276ffda556da8cf58b9e28042e211
7
- data.tar.gz: c56a875c0547d3456958a5cbd9b8d3ec7d4e921033d176c2b2835b80657b59d9d207e9dc0342564506f89d9cee313168427e906e9d6249f6298a0e90d6a9a5cf
6
+ metadata.gz: 91cb45ce9eb4672aa3c8352f21969d1cc442cce7cf4ef924e9fd9c038a9b1e29e43880428a7b6539c16eb4a6eda4527ac1b3052b85e69466f710a24e0df1517b
7
+ data.tar.gz: 5fed5c9dc669ad9ea2766097e1f473cbb8bc834d0f8ce5be81cd81428f6b914d5b25b55b10744e77824f03b96c5fc4b0d0dd734148dcdb8fe03b732fa95c97bc
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 4.0.1
1
+ 4.0.2
data/README.md CHANGED
@@ -13,7 +13,7 @@
13
13
 
14
14
  ## Installation
15
15
 
16
- The gem is available at [rubygems.org](https://rubygems.org/gems/echspec). You can install it the following:
16
+ The gem is available at [rubygems.org](https://rubygems.org/gems/echspec). You can install it with the following:
17
17
 
18
18
  ```sh-session
19
19
  $ gem install echspec
@@ -24,7 +24,22 @@ $ gem install echspec
24
24
 
25
25
  ```sh-session
26
26
  $ echspec --help
27
- Usage: echspec [OPTIONS] <HOSTNAME>
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}
34
+
35
+ Run ECH conformance tests for a server.
36
+
37
+ Examples:
38
+
39
+ $ echspec run localhost
40
+ $ echspec run -f echconfigs.pem -p 4433 localhost
41
+
42
+ Options:
28
43
  -f, --file FILE path to ECHConfigs PEM file (default resolve ECHConfigs via DNS)
29
44
  -p, --port VALUE server port number (default 443)
30
45
  -n, --not-force-compliant-hpke not force compliant ECHConfig HPKE cipher suite
@@ -32,10 +47,10 @@ Usage: echspec [OPTIONS] <HOSTNAME>
32
47
  -s, --sections SECTIONS sections to test; by the default, test all sections
33
48
  ```
34
49
 
35
- You can run it the following:
50
+ Example output:
36
51
 
37
52
  ```sh-session
38
- $ echspec research.cloudflare.com
53
+ $ echspec run research.cloudflare.com
39
54
  TLS Encrypted Client Hello Server
40
55
  ✔ MUST implement the following HPKE cipher suite: KEM: DHKEM(X25519, HKDF-SHA256), KDF: HKDF-SHA256 and AEAD: AES-128-GCM. [9]
41
56
  ✔ MUST abort with an "illegal_parameter" alert, if EncodedClientHelloInner is padded with non-zero values. [5.1-9]
@@ -60,13 +75,13 @@ Failures:
60
75
  1 failure
61
76
  ```
62
77
 
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:
78
+ By default, `echspec` retrieves ECHConfigs via DNS HTTPS records. You can specify a local PEM file using the `-f, --file FILE` option. To test a server on localhost:
64
79
 
65
80
  ```sh-session
66
- $ echspec -f fixtures/echconfigs.pem -p 4433 localhost
81
+ $ echspec run -f fixtures/echconfigs.pem -p 4433 localhost
67
82
  ```
68
83
 
69
- By default, `echspec` uses the following HPKE cipher suite
84
+ By default, `echspec` enforces the following mandatory HPKE cipher suite:
70
85
 
71
86
  - KEM
72
87
  - DHKEM(X25519, HKDF-SHA256)
@@ -75,16 +90,16 @@ By default, `echspec` uses the following HPKE cipher suite
75
90
  - AEAD
76
91
  - AES-128-GCM
77
92
 
78
- Using the `-n` or `--not-force-compliant-hpke`, you can not enforce the HPKE cipher suite.
93
+ Use the `-n` or `--not-force-compliant-hpke` option to disable this enforcement and use the cipher suite provided in the ECHConfig.
79
94
 
80
95
  ```sh-session
81
- $ echspec -f fixtures/echconfigs.pem -p 4433 -n localhost
96
+ $ echspec run -f fixtures/echconfigs.pem -p 4433 -n localhost
82
97
  ```
83
98
 
84
- If you specify the SECTIONS, you can run only SECTIONS the following:
99
+ To run only specific test SECTIONS, use the `-s` option:
85
100
 
86
101
  ```sh-session
87
- $ echspec -f fixtures/echconfigs.pem -p 4433 -n -s 7.1.1-2,7.1.1-5 localhost
102
+ $ echspec run -f fixtures/echconfigs.pem -p 4433 -n -s 7.1.1-2,7.1.1-5 localhost
88
103
  TLS Encrypted Client Hello Server
89
104
  ✔ MUST abort with a "missing_extension" alert, if 2nd ClientHelloOuter does not contains the "encrypted_client_hello" extension. [7.1.1-2]
90
105
  ✔ MUST abort with an "illegal_parameter" alert, if 2nd ClientHelloOuter "encrypted_client_hello" enc is empty. [7.1.1-2]
@@ -94,7 +109,7 @@ TLS Encrypted Client Hello Server
94
109
  Using the `-v` or `--verbose` option provides a message stack if an error occurs. The message stack is formatted as JSON.
95
110
 
96
111
  ```sh-session
97
- $ echspec -s 7-5 -v research.cloudflare.com 2>&1 > /dev/null | jq .
112
+ $ echspec run -s 7-5 -v research.cloudflare.com 2>&1 > /dev/null | jq .
98
113
  ````
99
114
 
100
115
  <details>
@@ -267,6 +282,21 @@ $ echspec -s 7-5 -v research.cloudflare.com 2>&1 > /dev/null | jq .
267
282
 
268
283
  </details>
269
284
 
285
+ You can generate an ECHConfig PEM file the following:
286
+
287
+ ```sh-session
288
+ $ echspec gen_configs echconfigs.pem
289
+ ```
290
+ ```
291
+ -----BEGIN PRIVATE KEY-----
292
+ MC4CAQAwBQYDK2VuBCIEICjd4yGRdsoP9gU7YT7My8DHx1Tjme8GYDXrOMCi8v1V
293
+ -----END PRIVATE KEY-----
294
+ -----BEGIN ECHCONFIG-----
295
+ AD7+DQA65wAgACA8wVN2BtscOl3vQheUzHeIkVmKIiydUhDCliA4iyQRCwAEAAEA
296
+ AQALZXhhbXBsZS5jb20AAA==
297
+ -----END ECHCONFIG-----
298
+ ```
299
+
270
300
 
271
301
  ## Note
272
302
 
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]
Binary file
data/exe/echspec CHANGED
@@ -4,4 +4,4 @@ $LOAD_PATH << "#{__dir__}/../lib"
4
4
 
5
5
  require 'echspec'
6
6
 
7
- EchSpec::CLI.new.run
7
+ EchSpec::CLI.new.execute
@@ -0,0 +1,69 @@
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
13
+ Usage: echspec gen_configs {FILE}
14
+
15
+ Generate an ECHConfig PEM file.
16
+
17
+ Examples:
18
+
19
+ $ echspec gen_configs echconfigs.pem
20
+ USAGE
21
+
22
+ begin
23
+ args = op.parse(argv)
24
+ rescue OptionParser::InvalidOption, OptionParser::MissingArgument => e
25
+ warn op
26
+ warn "** #{e.message}"
27
+ exit 1
28
+ end
29
+
30
+ if args.length != 1
31
+ warn op
32
+ warn '** {FILE} argument is not specified'
33
+ exit 1
34
+ end
35
+ args[0]
36
+ end
37
+
38
+ def write(fpath)
39
+ hostname = 'localhost'
40
+
41
+ key = OpenSSL::PKey.generate_key('X25519')
42
+ echconfigs = ECHConfigList.new(
43
+ [
44
+ ECHConfig.new(
45
+ "\xfe\x0d".b,
46
+ ECHConfig::ECHConfigContents.new(
47
+ ECHConfig::ECHConfigContents::HpkeKeyConfig.new(
48
+ 123,
49
+ ECHConfig::ECHConfigContents::HpkeKeyConfig::HpkeKemId.new(HPKE::DHKEM_X25519_HKDF_SHA256),
50
+ ECHConfig::ECHConfigContents::HpkeKeyConfig::HpkePublicKey.new(key.raw_public_key),
51
+ [
52
+ ECHConfig::ECHConfigContents::HpkeKeyConfig::HpkeSymmetricCipherSuite.new(
53
+ ECHConfig::ECHConfigContents::HpkeKeyConfig::HpkeSymmetricCipherSuite::HpkeKdfId.new(HPKE::HKDF_SHA256),
54
+ ECHConfig::ECHConfigContents::HpkeKeyConfig::HpkeSymmetricCipherSuite::HpkeAeadId.new(HPKE::AES_128_GCM)
55
+ )
56
+ ]
57
+ ),
58
+ 32,
59
+ hostname.b,
60
+ ECHConfig::ECHConfigContents::Extensions.new('')
61
+ )
62
+ )
63
+ ]
64
+ )
65
+ File.write(fpath, key.private_to_pem + echconfigs.to_pem)
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,111 @@
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
67
+ Usage: echspec run [OPTIONS...] {HOSTNAME}
68
+
69
+ Run ECH conformance tests for a server.
70
+
71
+ Examples:
72
+
73
+ $ echspec run localhost
74
+ $ echspec run -f echconfigs.pem -p 4433 localhost
75
+
76
+ Options:
77
+ USAGE
78
+
79
+ begin
80
+ args = op.parse(argv)
81
+ rescue OptionParser::InvalidOption, OptionParser::MissingArgument => e
82
+ warn op
83
+ warn "** #{e.message}"
84
+ exit 1
85
+ end
86
+
87
+ if !fpath.nil? && !File.exist?(fpath)
88
+ warn '** {FILE} is not found'
89
+ exit 1
90
+ end
91
+
92
+ unknowns = sections.nil? ? [] : sections - Spec.sections
93
+ unless unknowns.empty?
94
+ warn "** #{unknowns} are unknown sections"
95
+ exit 1
96
+ end
97
+
98
+ if args.length != 1
99
+ warn op
100
+ warn '** {HOSTNAME} argument is not specified'
101
+ exit 1
102
+ end
103
+ hostname = args[0]
104
+
105
+ [fpath, port, force_compliant, verbose, hostname, sections]
106
+ end
107
+ # rubocop: enable Metrics/AbcSize
108
+ # rubocop: enable Metrics/MethodLength
109
+ end
110
+ end
111
+ 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
- # 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
6
+ using Refinements
63
7
 
64
- if !fpath.nil? && !File.exist?(fpath)
65
- warn '** <FILE> is not found'
66
- exit 1
67
- end
8
+ def execute(argv = ARGV)
9
+ subcommands = %i[run gen_configs]
68
10
 
69
- unknowns = sections.nil? ? [] : sections - Spec.sections
70
- unless unknowns.empty?
71
- warn "** #{unknowns} are unknown sections"
72
- exit 1
73
- end
11
+ op = OptionParser.new
74
12
 
75
- if args.length != 1
76
- warn op
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
- [fpath, port, force_compliant, verbose, hostname, sections]
83
- end
84
- # rubocop: enable Metrics/AbcSize
85
- # rubocop: enable Metrics/MethodLength
16
+ Available subcommands: #{subcommands.join(', ')}, version, help.
17
+ USAGE
86
18
 
87
- def run
88
- fpath, port, force_compliant, verbose, hostname, sections = parse_options
19
+ op.version = EchSpec::VERSION
20
+ op.order!(argv)
89
21
 
90
- if sections.nil?
91
- Spec.run(fpath, port, hostname, force_compliant, verbose)
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
- Spec.run_only(fpath, port, hostname, sections, verbose)
30
+ puts op
94
31
  end
95
32
  end
96
33
  end
data/lib/echspec/utils.rb CHANGED
@@ -20,6 +20,10 @@ module EchSpec
20
20
  def yellow
21
21
  colorize(33)
22
22
  end
23
+
24
+ def to_camel
25
+ gsub(/(?:^|_)(.)/) { Regexp.last_match(1).upcase }
26
+ end
23
27
  end
24
28
  end
25
29
  end
@@ -1,3 +1,3 @@
1
1
  module EchSpec
2
- VERSION = '0.0.3'.freeze
2
+ VERSION = '0.0.5'.freeze
3
3
  end
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.3
4
+ version: 0.0.5
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
@@ -136,7 +138,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
136
138
  - !ruby/object:Gem::Version
137
139
  version: '0'
138
140
  requirements: []
139
- rubygems_version: 4.0.7
141
+ rubygems_version: 4.0.8
140
142
  specification_version: 4
141
143
  summary: A conformance testing tool for ECH implementation
142
144
  test_files: []