tls-map 1.3.2 → 2.0.0

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: 0eadde9b90bc4cfb3dc23e8b6263fdaf5fc1815a262ee8c6f2db3dfc0fe54db7
4
- data.tar.gz: 7c80ee6fa969f77643e8256cc0d4a33f1de8bf1f540fefe7d1641e3691e76044
3
+ metadata.gz: cb7893544cb7037e6076051880f66d3140c2a0b4218446ca40a2616b81cf2fbd
4
+ data.tar.gz: bfea4a2849792217d32b3fee7c5c426748302b40291bf16e4ba966be44489915
5
5
  SHA512:
6
- metadata.gz: a7303c826807b76c35d3899b02ce0af2874471e7dc55f19d6dcef85447270c5ea8fbb67e7bdb21fa5acf9a352f5355adcaa83e23bc4aeb83f42ca0d2149964aa
7
- data.tar.gz: c148a6770e6619bb7d32a435d7ad314e55871f4188cbff01c1f21c8e02e58497e3c6d32ec007bb27b6b9020c85bbd596a32e99575c7a1fab3cd4d224f7969184
6
+ metadata.gz: 521b9a33d845ae0af2101e581937154f491bd8eaa8d8ab57f6378b02994c35973ac1b41f53a080307c51325899ce89e714abe99bc92af001cb29269800f34694
7
+ data.tar.gz: dd096edd552d51a1e510e8bb0a82293f1ac67090626e88daab11578eb9f34ad6f11faa51216f8300472d189c051f879aca0a17d1e4957b38f148c295ed718e97
data/LICENSE CHANGED
@@ -1,5 +1,6 @@
1
1
  MIT License
2
2
 
3
+ Copyright (c) 2021 Alexandre ZANNI
3
4
  Copyright (c) 2021 Alexandre ZANNI at SEC-IT
4
5
 
5
6
  Permission is hereby granted, free of charge, to any person obtaining a copy
data/bin/tls-map CHANGED
@@ -5,7 +5,7 @@
5
5
  require 'pp'
6
6
  # Project internal
7
7
  require 'tls_map'
8
- require 'tls_map/cli'
8
+ require 'tls_map/cli/cli'
9
9
  # External
10
10
  require 'docopt'
11
11
  require 'paint'
@@ -16,23 +16,23 @@ doc = <<~DOCOPT
16
16
  TLS map #{TLSmap::VERSION}
17
17
 
18
18
  Usage:
19
- tls-map search <critera> <term> [-o <output> --force -e -a] [--no-color --debug]
20
- tls-map bulk <critera> <file> [-q <output> --force] [--no-color --debug]
19
+ tls-map search <criteria> <term> [-o <output> --force -e -a] [--no-color --debug]
20
+ tls-map bulk <criteria> <file> [-q <output> --force] [--no-color --debug]
21
21
  tls-map export <filename> <format> [--force] [--debug]
22
- tls-map extract <filename> <format> [--no-color --debug]
23
- tls-map update [--debug]
22
+ tls-map extract <filename> <format> [--no-color --debug [--only-weak | --hide-weak]]
23
+ tls-map update [--with-extended] [--debug]
24
24
  tls-map -h | --help
25
25
  tls-map --version
26
26
 
27
27
  Search options: (offline) search and translate cipher names between SSL/TLS libraries
28
- <critera> The type of term. Accepted values: codepoint, iana, openssl, gnutls, nss.
28
+ <criteria> The type of term. Accepted values: codepoint, iana, openssl, gnutls, nss.
29
29
  <term> The cipher algorithm name.
30
30
  -o, --output <output> Displayed fields. Accepted values: all, codepoint, iana, openssl, gnutls, nss. [default: all]
31
31
  -e, --extended (Online) Display additional information about the cipher (requires output = all or iana)
32
32
  -a, --acronym (Online) Display full acronym name (requires -e / --extended option)
33
33
 
34
34
  Bulk options: (offline) search and translate cipher names between SSL/TLS libraries in bulk
35
- <critera> The type of term. Accepted values: codepoint, iana, openssl, gnutls, nss.
35
+ <criteria> The type of term. Accepted values: codepoint, iana, openssl, gnutls, nss.
36
36
  <file> File containing the cipher algorithm names, one per line.
37
37
  -q, --output2 <output> Displayed fields. Accepted values: codepoint, iana, openssl, gnutls, nss. [default: iana]
38
38
 
@@ -43,8 +43,11 @@ doc = <<~DOCOPT
43
43
  Extract options: (offline) extract ciphers from external tools output file
44
44
  <filename> The external tool output file
45
45
  <format> Supported formats: sslyze, sslscan2, testssl, ssllabs-scan (check the documentation for the expected file format)
46
+ --only-weak Show only ciphers with a security level equal to weak or insecure (hide secure and recommended) (work only with TLS not SSL).
47
+ --hide-weak Hide ciphers with a security level equal to weak or insecure (show only secure and recommended) (work only with TLS not SSL).
46
48
 
47
49
  Update options: (online) DANGEROUS, will break database integrity, force option will be required
50
+ --with-extended (Online) Also save extended information used by search --extended option.
48
51
 
49
52
  Other options:
50
53
  --force Force parsing even if integrity check failed (DANGEROUS, may result in command execution vulnerability)
@@ -60,7 +63,7 @@ begin
60
63
  pp args if args['--debug']
61
64
  if args['search']
62
65
  cli = TLSmap::CLI.new(args['--force'])
63
- res = cli.search(args['<critera>'].to_sym, args['<term>'], args['--output'].to_sym)
66
+ res = cli.search(args['<criteria>'].to_sym, args['<term>'], args['--output'].to_sym)
64
67
  puts Paint['No match found', :red] if res.empty?
65
68
  res.each do |k, v|
66
69
  puts "#{Paint[k, :green]}: #{Paint[v, :white]}"
@@ -71,6 +74,7 @@ begin
71
74
  ext = tmext_i.extend(res[:iana])
72
75
  dic = tmext::DICO
73
76
  sev = tmext::VULN_SEVERITY
77
+ sec_lvl = tmext::SECURITY_LEVEL
74
78
  ext.each do |k, v|
75
79
  case k
76
80
  when 'vulns'
@@ -81,6 +85,8 @@ begin
81
85
  end
82
86
  when 'tls_version'
83
87
  puts "#{Paint[dic[k], :magenta]}: #{Paint[v.join(', '), :white]}"
88
+ when 'security'
89
+ puts "#{Paint[dic[k], :magenta]}: #{Paint[v, sec_lvl[v][:color]]}"
84
90
  else
85
91
  print "#{Paint[dic[k], :magenta]}: #{Paint[v, :white]}"
86
92
  print " (#{tmext_i.translate_acronym(v)})" if args['--acronym'] && !tmext_i.translate_acronym(v).nil? # rubocop:disable Metrics/BlockNesting
@@ -90,7 +96,7 @@ begin
90
96
  end
91
97
  elsif args['bulk']
92
98
  cli = TLSmap::CLI.new(args['--force'])
93
- res = cli.bulk_search(args['<critera>'].to_sym, args['<file>'], args['--output2'].to_sym)
99
+ res = cli.bulk_search(args['<criteria>'].to_sym, args['<file>'], args['--output2'].to_sym)
94
100
  puts Paint['No match found', :red] if res.empty?
95
101
  res.each do |h|
96
102
  puts Paint[h[args['--output2'].to_sym], :green]
@@ -103,13 +109,26 @@ begin
103
109
  extractor = TLSmap::App::Extractor.new
104
110
  ciphers = extractor.parse(args['<format>'], args['<filename>'])
105
111
  ciphers.each do |k, v|
106
- puts Paint[k, :blue] unless v.empty?
107
- puts Paint[v.join("\n"), :white] unless v.empty?
112
+ if args['--only-weak'] || args['--hide-weak']
113
+ cliext = TLSmap::CLI::Extended.new
114
+ v.each do |alg|
115
+ ci = TLSmap::App::Cipher.new(:iana, alg, enhanced_data: cliext.enhanced_data)
116
+ puts Paint[alg, :white] if (args['--only-weak'] && !ci.should_i_use?) ||
117
+ (args['--hide-weak'] && ci.should_i_use?)
118
+ end
119
+ else
120
+ puts Paint[k, :blue] unless v.empty?
121
+ puts Paint[v.join("\n"), :white] unless v.empty?
122
+ end
108
123
  end
109
124
  elsif args['update']
110
125
  cli = TLSmap::CLI.new
111
126
  cli.update
112
- puts 'Database updated'
127
+ if args['--with-extended']
128
+ cliext = TLSmap::CLI::Extended.new
129
+ cliext.update
130
+ end
131
+ puts 'Database(s) updated'
113
132
  end
114
133
  rescue Docopt::Exit => e
115
134
  puts e.message
Binary file
data/lib/tls_map.rb CHANGED
@@ -4,19 +4,24 @@
4
4
  require 'pathname'
5
5
  # Project internal
6
6
  require 'tls_map/version'
7
- require 'tls_map/utils'
8
- require 'tls_map/iana'
9
- require 'tls_map/openssl'
10
- require 'tls_map/gnutls'
11
- require 'tls_map/nss'
12
- require 'tls_map/output'
13
- require 'tls_map/ciphersuiteinfo'
14
- require 'tls_map/extractor'
7
+ require 'tls_map/utils/utils'
8
+ require 'tls_map/app/iana'
9
+ require 'tls_map/app/openssl'
10
+ require 'tls_map/app/gnutls'
11
+ require 'tls_map/app/nss'
12
+ require 'tls_map/app/output'
13
+ require 'tls_map/app/extended/ciphersuiteinfo'
14
+ require 'tls_map/app/extractor/extractor'
15
+ require 'tls_map/app/cipher/cipher'
15
16
 
16
17
  # TLS map module
17
18
  module TLSmap
18
19
  # TLS mapping
19
20
  class App
21
+ # Get the mapping of all TLS cipher suites
22
+ # @return [Hash] mapping of all TLS cipher suites
23
+ attr_reader :tls_map
24
+
20
25
  # Will automatically fetch source files and parse them.
21
26
  def initialize
22
27
  @iana_file = Utils.tmpfile('iana', IANA_URL)
@@ -37,17 +42,42 @@ module TLSmap
37
42
  end
38
43
 
39
44
  # Search for corresponding cipher algorithms in other libraries
40
- # @param critera [Symbol] The type of `term`.
45
+ # @param criteria [Symbol] The type of `term`.
41
46
  # Accepted values: `:codepoint`, `:iana`, `:openssl`, `:gnutls`, `:nss`.
42
47
  # @param term [String] The cipher algorithm name.
43
48
  # @param output [Symbol] The corresponding type to be included in the return value.
44
49
  # Accepted values: `:all` (default), `:codepoint`, `:iana`, `:openssl`,
45
50
  # `:gnutls`, `:nss`.
46
51
  # @return [Hash] The corresponding type matching `term`.
47
- def search(critera, term, output = :all)
52
+ def search(criteria, term, output = :all)
48
53
  @tls_map.each do |alg|
49
- term = term.upcase if critera == :codepoint
50
- next unless alg[critera] == term
54
+ term = term.upcase if criteria == :codepoint
55
+ next unless alg[criteria] == term
56
+ return alg if output == :all
57
+
58
+ return { output => alg[output] }
59
+ end
60
+ {}
61
+ end
62
+
63
+ # Stateless version of {App#search}.
64
+ # @param tls_map [Hash] mapping of all TLS cipher suites returned by {tls_map}.
65
+ # @param criteria [Symbol] Same as `criteria` from {TLSmap::App#search}
66
+ # @param term [String] Same as `term` from {TLSmap::App#search}
67
+ # @param output [Symbol] Same as `output` from {TLSmap::App#search}
68
+ # @see App#search
69
+ # @example
70
+ # tm = TLSmap::App.new
71
+ # TLSmap::App.search(tm.tls_map, :iana, 'TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256')
72
+ # # => {:codepoint=>"CCA9", :iana=>"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
73
+ # :openssl=>"ECDHE-ECDSA-CHACHA20-POLY1305", :gnutls=>"ECDHE_ECDSA_CHACHA20_POLY1305",
74
+ # :nss=>"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256"}
75
+ # # or to use with the Cipher class
76
+ # ci = TLSmap::App::Cipher.new(:iana, 'TLS_DH_anon_WITH_RC4_128_MD5', tm.tls_map)
77
+ def self.search(tls_map, criteria, term, output = :all)
78
+ tls_map.each do |alg|
79
+ term = term.upcase if criteria == :codepoint
80
+ next unless alg[criteria] == term
51
81
  return alg if output == :all
52
82
 
53
83
  return { output => alg[output] }
@@ -56,7 +86,7 @@ module TLSmap
56
86
  end
57
87
 
58
88
  # Search for corresponding cipher algorithms in other libraries in bulk
59
- # @param critera [Symbol] The type of `term`.
89
+ # @param criteria [Symbol] The type of `term`.
60
90
  # Accepted values: `:codepoint`, `:iana`, `:openssl`, `:gnutls`, `:nss`.
61
91
  # @param file [String] File containing the cipher algorithm names, one per line.
62
92
  # @param output [Symbol] The corresponding type to be included in the return value.
@@ -64,10 +94,10 @@ module TLSmap
64
94
  # `:gnutls`, `:nss`.
65
95
  # @return [Array<Hash>] The corresponding type, same as {search} return value
66
96
  # but one per line stored in an array.
67
- def bulk_search(critera, file, output = :all)
97
+ def bulk_search(criteria, file, output = :all)
68
98
  res = []
69
99
  File.foreach(file) do |line|
70
- res.push(search(critera, line.chomp, output))
100
+ res.push(search(criteria, line.chomp, output))
71
101
  end
72
102
  res
73
103
  end
@@ -0,0 +1,151 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Project internal
4
+ require 'tls_map/cli/cli'
5
+
6
+ module TLSmap
7
+ # TLS mapping
8
+ class App
9
+ # Manipulate cipher suite information
10
+ class Cipher
11
+ # Get the hexadecimal codepoint of the cipher suite
12
+ # @return [String] Hexadecimal codepoint
13
+ attr_reader :codepoint
14
+
15
+ # Get the IANA name of the cipher suite
16
+ # @return [String] IANA name
17
+ attr_reader :iana
18
+
19
+ # Get the OpenSSL name of the cipher suite
20
+ # @return [String] OpenSSL name
21
+ attr_reader :openssl
22
+
23
+ # Get the GnuTLS name of the cipher suite
24
+ # @return [String] GnuTLS name
25
+ attr_reader :gnutls
26
+
27
+ # Get the NSS name of the cipher suite
28
+ # @return [String] NSS name
29
+ attr_reader :nss
30
+
31
+ # Get extended information
32
+ # @!attribute [r] extended
33
+ # @return [Hash]
34
+ # @example
35
+ # ci = TLSmap::App::Cipher.new(:iana, 'TLS_RSA_WITH_SEED_CBC_SHA')
36
+ # ci.extended
37
+ # # =>
38
+ # # {"protocol_version"=>"TLS",
39
+ # # "kex_algorithm"=>"RSA",
40
+ # # "auth_algorithm"=>"RSA",
41
+ # # "enc_algorithm"=>"SEED CBC",
42
+ # # "hash_algorithm"=>"SHA",
43
+ # # "security"=>"weak",
44
+ # # "tls_version"=>["TLS1.0", "TLS1.1", "TLS1.2"],
45
+ # # "vulns"=>
46
+ # # [{:severity=>1, :description=>"This key exchange algorithm does not support Perfect Forward Secrecy (PFS)
47
+ # # which is recommended, so attackers cannot decrypt the complete communication stream."},
48
+ # # {:severity=>1,
49
+ # # :description=>
50
+ # # "In 2013, researchers demonstrated a timing attack against several TLS implementations using the CBC
51
+ # # encryption algorithm (see [isg.rhul.ac.uk](http://www.isg.rhul.ac.uk/tls/Lucky13.html)). Additionally,
52
+ # # the CBC mode is vulnerable to plain-text attacks in TLS 1.0, SSL 3.0 and lower. A fix has been
53
+ # # introduced with TLS 1.2 in form of the GCM mode which is not vulnerable to the BEAST attack. GCM should
54
+ # # be preferred over CBC."},
55
+ # # {:severity=>1, :description=>"The Secure Hash Algorithm 1 has been proven to be insecure as of 2017 (see
56
+ # # [shattered.io](https://shattered.io))."}],
57
+ # # "url"=>"https://ciphersuite.info/cs/TLS_RSA_WITH_SEED_CBC_SHA/"}
58
+ def extended
59
+ fetch_extended
60
+ @extended
61
+ end
62
+
63
+ # Initialize {TLSmap::App::Cipher} instance
64
+ # @param type [Symbol] Same as `criteria` from {TLSmap::App#search}
65
+ # @param value [String] Same as `term` from {TLSmap::App#search}
66
+ # @param opts [Hash] the option hash
67
+ # @option opts [Hash] :tls_map mapping of all TLS cipher suites returned by {App#tls_map}.
68
+ # (better performance for batch usage)
69
+ # @option opts [Hash] :enhanced_data enhanced information of all cipher suites returned by
70
+ # {Extended#enhanced_data}. (better performance for batch usage)
71
+ # @example
72
+ # # Offline TLS data + online extended data
73
+ # ci = TLSmap::App::Cipher.new(:iana, 'TLS_DH_anon_WITH_RC4_128_MD5')
74
+ # # Online TLS data + online extended data
75
+ # tm = TLSmap::App.new
76
+ # ci = TLSmap::App::Cipher.new(:iana, 'TLS_DH_anon_WITH_RC4_128_MD5', tls_map: tm.tls_map)
77
+ # # Offline TLS data + online extended data but more efficient for batch requesting
78
+ # tmext = TLSmap::App::Extended.new
79
+ # tmext.enhance_all
80
+ # ci = TLSmap::App::Cipher.new(:iana, 'TLS_DH_anon_WITH_RC4_128_MD5', enhanced_data: tmext.enhanced_data)
81
+ # # Offline TLS data + offline extended data (better performance but may be outdated)
82
+ # cliext = TLSmap::CLI::Extended.new
83
+ # ci = TLSmap::App::Cipher.new(:iana, 'TLS_DH_anon_WITH_RC4_128_MD5', enhanced_data: cliext.enhanced_data)
84
+ def initialize(type, value, opts = {}) # rubocop:disable Metrics/MethodLength
85
+ res = if opts[:tls_map].nil?
86
+ TLSmap::CLI.new.search(type, value)
87
+ else
88
+ TLSmap::App.search(opts[:tls_map], type, value)
89
+ end
90
+ @codepoint = res[:codepoint]
91
+ @iana = res[:iana]
92
+ @openssl = res[:openssl]
93
+ @gnutls = res[:gnutls]
94
+ @nss = res[:nss]
95
+ @extended = opts.dig(:enhanced_data, @iana)
96
+ end
97
+
98
+ # Retrieve extended data by using #{App:Extended}
99
+ def fetch_extended
100
+ return unless @extended.nil?
101
+
102
+ tmext = TLSmap::App::Extended.new
103
+ @extended = tmext.extend(@iana)
104
+ end
105
+
106
+ # Is the security level defined to `weak`?
107
+ # @return [Boolean]
108
+ def weak?
109
+ fetch_extended
110
+ @extended['security'] == 'weak'
111
+ end
112
+
113
+ # Is the security level defined to `insecure`?
114
+ # @return [Boolean]
115
+ def insecure?
116
+ fetch_extended
117
+ @extended['security'] == 'insecure'
118
+ end
119
+
120
+ # Is the security level defined to `secure`?
121
+ # @return [Boolean]
122
+ def secure?
123
+ fetch_extended
124
+ @extended['security'] == 'secure'
125
+ end
126
+
127
+ # Is the security level defined to `recommended`?
128
+ # @return [Boolean]
129
+ def recommended?
130
+ fetch_extended
131
+ @extended['security'] == 'recommended'
132
+ end
133
+
134
+ # Is the security level defined to `secure` or `recommended`?
135
+ # It will return `false` for `weak` and `insecure` cipher suites.
136
+ # @return [Boolean]
137
+ def should_i_use?
138
+ recommended? || secure?
139
+ end
140
+
141
+ # Is the cipher suite vulnerable?
142
+ # @return [Boolean] `true` if one (or more) vulnerability is declared
143
+ def vulnerable?
144
+ fetch_extended
145
+ !@extended['vulns'].empty?
146
+ end
147
+
148
+ protected :fetch_extended
149
+ end
150
+ end
151
+ end
@@ -22,9 +22,9 @@ module TLSmap
22
22
  ROOT = 'https://ciphersuite.info/'
23
23
  # Root URL of Cipher Suite Info API
24
24
  API_ROOT = "#{ROOT}api/"
25
- # URL of the data file containig vulnerabilities information
25
+ # URL of the data file containing vulnerabilities information
26
26
  VULN_DATA = 'https://raw.githubusercontent.com/hcrudolph/ciphersuite.info/master/directory/fixtures/00_vulnerabilities.yaml'
27
- # URL of the data file containig technologies information
27
+ # URL of the data file containing technologies information
28
28
  TECH_DATA = 'https://raw.githubusercontent.com/hcrudolph/ciphersuite.info/master/directory/fixtures/01_technologies.yaml'
29
29
  # Hash mapping API key and display name for CLI
30
30
  DICO = {
@@ -44,6 +44,17 @@ module TLSmap
44
44
  1 => { title: 'Medium', color: 'orange' },
45
45
  2 => { title: 'High', color: :red }
46
46
  }.freeze
47
+ # Hash mapping the security level used by the API and color for the CLI
48
+ SECURITY_LEVEL = {
49
+ 'recommended' => { color: :green },
50
+ 'secure' => { color: :green },
51
+ 'weak' => { color: 'orange' },
52
+ 'insecure' => { color: :red }
53
+ }.freeze
54
+
55
+ # Get the enhanced information of all cipher suites returned by {enhance_all}.
56
+ # @return [Hash] Enhanced information of all cipher suites
57
+ attr_reader :enhanced_data
47
58
 
48
59
  # Will automatically fetch source files and parse them.
49
60
  def initialize
@@ -51,16 +62,47 @@ module TLSmap
51
62
  @vuln_file = Utils.tmpfile('vuln', VULN_DATA)
52
63
  @tech = parse_tech
53
64
  @vuln = parse_vuln
65
+ @ciphersuite_all = nil
66
+ @enhanced_data = nil
67
+ end
68
+
69
+ # Fetch all cipher suite data from ciphersuite.info and store it in the instance attribute for batch usage.
70
+ def fetch_ciphersuite
71
+ return unless @ciphersuite_all.nil?
72
+
73
+ @ciphersuite_all = JSON.parse(Net::HTTP.get(URI("#{API_ROOT}cs/")))['ciphersuites'].reduce(:merge!)
54
74
  end
55
75
 
56
- # Retrieve advanced about a cipher on Cipher Suite Info API and enhanced it
76
+ # Enhance data from ciphersuite.info for all cipher suites and store it
77
+ # for batch usage.
78
+ # The data will be available through {enhanced_data}.
79
+ def enhance_all
80
+ fetch_ciphersuite
81
+ out = {}
82
+ @ciphersuite_all.each do |k, _v|
83
+ out.store(k, extend(k, true))
84
+ end
85
+ @enhanced_data = out
86
+ end
87
+
88
+ # Retrieve advanced information about a cipher on Cipher Suite Info API and enhanced it.
89
+ # Fetch only the requested cipher suite, small network footprint, ideal for low bandwidth or punctual use.
57
90
  # @param iana_name [String] IANA cipher name
58
- # @return [Hash] Hash containing advanced information. The keys are the same as {DICO}. All valeus are string
91
+ # @param caching [Boolean] if true will fetch info for all cipher suites the 1st time and used the cached value
92
+ # for further requests
93
+ # @return [Hash] Hash containing advanced information. The keys are the same as {DICO}. All values are string
59
94
  # except `vulns` which is an array of hashes containing two keys: `:severity` (integer) and `:description`
60
95
  # (string). Each hash in `vulns` correspond to a vulnerability.
61
- def extend(iana_name) # rubocop:disable Metrics/MethodLength
62
- obj = Net::HTTP.get(URI("#{API_ROOT}cs/#{iana_name}/"))
63
- out = JSON.parse(obj)[iana_name]
96
+ def extend(iana_name, caching = false) # rubocop:disable Metrics/MethodLength
97
+ if caching
98
+ fetch_ciphersuite
99
+ out = @ciphersuite_all[iana_name]
100
+ else
101
+ obj = Net::HTTP.get(URI("#{API_ROOT}cs/#{iana_name}/"))
102
+ out = JSON.parse(obj)[iana_name]
103
+ end
104
+ return {} if out.nil?
105
+
64
106
  out.store('vulns', [])
65
107
  %w[openssl_name gnutls_name hex_byte_1 hex_byte_2].each do |key|
66
108
  out.delete(key)
@@ -114,7 +156,7 @@ module TLSmap
114
156
  nil
115
157
  end
116
158
 
117
- protected :parse_tech, :parse_vuln
159
+ protected :parse_tech, :parse_vuln, :fetch_ciphersuite
118
160
  end
119
161
  end
120
162
  end
@@ -3,7 +3,7 @@
3
3
  # Ruby internal
4
4
  require 'json'
5
5
  # Project internal
6
- require 'tls_map/cli'
6
+ require 'tls_map/cli/cli'
7
7
  # External
8
8
  require 'rexml/document'
9
9
 
@@ -98,7 +98,7 @@ module TLSmap
98
98
  # command (CLI) / {parse} method (library)
99
99
  def helper(tool)
100
100
  intro = 'You may not be provinding the right format.'
101
- outro = 'See https://sec-it.github.io/tls-map/yard/TLSmap/App/Extractor'
101
+ outro = 'See https://noraj.github.io/tls-map/yard/TLSmap/App/Extractor'
102
102
  "#{intro}\nUse this command: #{CMD[tool]}\n#{outro}"
103
103
  end
104
104
 
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Ruby internal
4
+ require 'digest'
5
+
6
+ # TLS map module
7
+ module TLSmap
8
+ # Offline version of {App}
9
+ class CLI < App
10
+ INTEGRITY = '42e44f89550365da2bc8d33d87f88b65d85d6474e90f9edb65e0ea6c78f61a53' # sha2-256
11
+
12
+ # Load and parse data from marshalized hash (`data/mapping.marshal`).
13
+ # It must match the integrity check for security purpose.
14
+ # @param force [Boolean] Force parsing even if integrity check failed (DANGEROUS,
15
+ # may result in command execution vulnerability)
16
+ def initialize(force = false) # rubocop:disable Lint/MissingSuper
17
+ @storage_location = 'data/'
18
+ @database_path = absolute_db_path('mapping.marshal')
19
+ database_exists?
20
+ @tls_map = []
21
+ parse(force)
22
+ end
23
+
24
+ # Find the absolute path of the a data file from its relative location
25
+ # @param filename [String] file name
26
+ # @return [String] absolute filename of the data file
27
+ def absolute_db_path(filename)
28
+ pn = Pathname.new(__FILE__)
29
+ install_dir = pn.dirname.parent.parent.parent.to_s + Pathname::SEPARATOR_LIST
30
+ install_dir + @storage_location + filename
31
+ end
32
+
33
+ # Check if the TLS database DB exists
34
+ # @return [Boolean] `true` if the file exists
35
+ def database_exists?
36
+ exists = File.file?(@database_path)
37
+ raise "Database does not exist: #{@database_path}" unless exists
38
+
39
+ exists
40
+ end
41
+
42
+ def parse(force = false)
43
+ if Digest::SHA256.file(@database_path).hexdigest == INTEGRITY || force # rubocop:disable Style/GuardClause
44
+ @tls_map = Marshal.load(File.read(@database_path)) # rubocop:disable Security/MarshalLoad
45
+ else
46
+ raise 'Integrity check failed, maybe be due to unvalidated database after update'
47
+ end
48
+ end
49
+
50
+ def update
51
+ tm = TLSmap::App.new
52
+ tm.export(@database_path, :marshal)
53
+ end
54
+
55
+ protected :database_exists?, :absolute_db_path, :parse
56
+
57
+ # Offline version of {App::Extended}
58
+ class Extended < App::Extended
59
+ INTEGRITY = '6573b7208668485e6bdd4495627f5fdbdd7f80040b277603bc42f20d16a665e7' # sha2-256
60
+
61
+ # Load and parse data from marshalized hash (`data/extended.marshal`).
62
+ # It must match the integrity check for security purpose.
63
+ # @param force [Boolean] Force parsing even if integrity check failed (DANGEROUS,
64
+ # may result in command execution vulnerability)
65
+ def initialize(force = false) # rubocop:disable Lint/MissingSuper
66
+ @storage_location = 'data/'
67
+ @extended_path = absolute_db_path('extended.marshal')
68
+ @enhanced_data = {}
69
+ extended_exists?
70
+ parse(force)
71
+ end
72
+
73
+ # Find the absolute path of the a data file from its relative location
74
+ # @param filename [String] file name
75
+ # @return [String] absolute filename of the data file
76
+ def absolute_db_path(filename)
77
+ pn = Pathname.new(__FILE__)
78
+ install_dir = pn.dirname.parent.parent.parent.to_s + Pathname::SEPARATOR_LIST
79
+ install_dir + @storage_location + filename
80
+ end
81
+
82
+ # Check if the extended DB exists
83
+ # @return [Boolean] `true` if the files exists
84
+ def extended_exists?
85
+ exists = File.file?(@extended_path)
86
+ raise "Database does not exist: #{@extended_path}" unless exists
87
+
88
+ exists
89
+ end
90
+
91
+ def parse(force = false)
92
+ if Digest::SHA256.file(@extended_path).hexdigest == INTEGRITY || force # rubocop:disable Style/GuardClause
93
+ @enhanced_data = Marshal.load(File.read(@extended_path)) # rubocop:disable Security/MarshalLoad
94
+ else
95
+ raise 'Integrity check failed, maybe be due to unvalidated database after update'
96
+ end
97
+ end
98
+
99
+ def update
100
+ tmext = TLSmap::App::Extended.new
101
+ tmext.enhance_all
102
+ File.write(@extended_path, Marshal.dump(tmext.enhanced_data))
103
+ end
104
+
105
+ # Same as {App::Extended} but loading data from offline database, so there
106
+ # is no caching option.
107
+ # @see App::Extended
108
+ def extend(iana_name)
109
+ @enhanced_data[iana_name]
110
+ end
111
+
112
+ protected :extended_exists?, :absolute_db_path, :parse
113
+ undef_method :enhance_all, :fetch_ciphersuite, :parse_tech, :parse_vuln
114
+ end
115
+ end
116
+ end
File without changes
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TLSmap
4
- VERSION = '1.3.2'
4
+ VERSION = '2.0.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tls-map
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.2
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexandre ZANNI
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-07-22 00:00:00.000000000 Z
11
+ date: 2021-08-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: docopt
@@ -53,7 +53,8 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: '3.2'
55
55
  description: 'CLI & library for mapping TLS cipher algorithm names: IANA, OpenSSL,
56
- GnuTLS, NSS'
56
+ GnuTLS, NSS;get information and vulnerabilities about cipher suites;extract cipher
57
+ suites from external tools: SSLyze, sslscan2, testssl.sh, ssllabs-scan'
57
58
  email: alexandre.zanni@engineer.com
58
59
  executables:
59
60
  - tls-map
@@ -64,31 +65,33 @@ files:
64
65
  - LICENSE
65
66
  - bin/tls-map
66
67
  - bin/tls-map_console
68
+ - data/extended.marshal
67
69
  - data/mapping.json
68
70
  - data/mapping.marshal
69
71
  - data/mapping.md
70
72
  - data/mapping.min.json
71
73
  - lib/tls_map.rb
72
- - lib/tls_map/ciphersuiteinfo.rb
73
- - lib/tls_map/cli.rb
74
- - lib/tls_map/extractor.rb
75
- - lib/tls_map/gnutls.rb
76
- - lib/tls_map/iana.rb
77
- - lib/tls_map/nss.rb
78
- - lib/tls_map/openssl.rb
79
- - lib/tls_map/output.rb
80
- - lib/tls_map/utils.rb
74
+ - lib/tls_map/app/cipher/cipher.rb
75
+ - lib/tls_map/app/extended/ciphersuiteinfo.rb
76
+ - lib/tls_map/app/extractor/extractor.rb
77
+ - lib/tls_map/app/gnutls.rb
78
+ - lib/tls_map/app/iana.rb
79
+ - lib/tls_map/app/nss.rb
80
+ - lib/tls_map/app/openssl.rb
81
+ - lib/tls_map/app/output.rb
82
+ - lib/tls_map/cli/cli.rb
83
+ - lib/tls_map/utils/utils.rb
81
84
  - lib/tls_map/version.rb
82
- homepage: https://sec-it.github.io/tls-map/
85
+ homepage: https://noraj.github.io/tls-map/
83
86
  licenses:
84
87
  - MIT
85
88
  metadata:
86
89
  yard.run: yard
87
- bug_tracker_uri: https://github.com/sec-it/tls-map/issues
88
- changelog_uri: https://github.com/sec-it/tls-map/blob/master/docs/CHANGELOG.md
89
- documentation_uri: https://sec-it.github.io/tls-map/yard/
90
- homepage_uri: https://sec-it.github.io/tls-map/
91
- source_code_uri: https://github.com/sec-it/tls-map/
90
+ bug_tracker_uri: https://github.com/noraj/tls-map/issues
91
+ changelog_uri: https://github.com/noraj/tls-map/blob/master/docs/CHANGELOG.md
92
+ documentation_uri: https://noraj.github.io/tls-map/yard/
93
+ homepage_uri: https://noraj.github.io/tls-map/
94
+ source_code_uri: https://github.com/noraj/tls-map/
92
95
  post_install_message:
93
96
  rdoc_options: []
94
97
  require_paths:
@@ -107,9 +110,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
107
110
  - !ruby/object:Gem::Version
108
111
  version: '0'
109
112
  requirements: []
110
- rubygems_version: 3.2.15
113
+ rubygems_version: 3.2.22
111
114
  signing_key:
112
115
  specification_version: 4
113
- summary: 'CLI & library for mapping TLS cipher algorithm names: IANA, OpenSSL, GnuTLS,
114
- NSS'
116
+ summary: CLI & library for TLS cipher suites manipulation
115
117
  test_files: []
data/lib/tls_map/cli.rb DELETED
@@ -1,57 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Ruby internal
4
- require 'digest'
5
-
6
- # TLS map module
7
- module TLSmap
8
- # TLS mapping
9
- class CLI < App
10
- INTEGRITY = '42e44f89550365da2bc8d33d87f88b65d85d6474e90f9edb65e0ea6c78f61a53' # sha2-256
11
-
12
- # Load and parse data from marshalized hash (`data/mapping.marshal`).
13
- # It must match the integrity check for security purpose.
14
- # @param force [Boolean] Force parsing even if intigrity check failed (DANGEROUS,
15
- # may result in command execution vulnerability)
16
- def initialize(force = false) # rubocop:disable Lint/MissingSuper
17
- @storage_location = 'data/'
18
- @database_name = 'mapping.marshal'
19
- @database_path = absolute_db_path
20
- database_exists?
21
- @tls_map = []
22
- parse(force)
23
- end
24
-
25
- # Find the absolute path of the DB from its relative location
26
- # @return [String] absolute filename of the DB
27
- def absolute_db_path
28
- pn = Pathname.new(__FILE__)
29
- install_dir = pn.dirname.parent.parent.to_s + Pathname::SEPARATOR_LIST
30
- install_dir + @storage_location + @database_name
31
- end
32
-
33
- # Check if the password database exists
34
- # @return [Boolean] `true` if the file exists
35
- def database_exists?
36
- exists = File.file?(@database_path)
37
- raise "Database does not exist: #{@database_path}" unless exists
38
-
39
- exists
40
- end
41
-
42
- def parse(force = false)
43
- if Digest::SHA256.file(@database_path).hexdigest == INTEGRITY || force # rubocop:disable Style/GuardClause
44
- @tls_map = Marshal.load(File.read(@database_path)) # rubocop:disable Security/MarshalLoad
45
- else
46
- raise 'Integry check failed, maybe be due to unavalidated database after update'
47
- end
48
- end
49
-
50
- def update
51
- tm = TLSmap::App.new
52
- tm.export(@database_path, :marshal)
53
- end
54
-
55
- protected :database_exists?, :absolute_db_path, :parse
56
- end
57
- end