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 +4 -4
- data/LICENSE +1 -0
- data/bin/tls-map +31 -12
- data/data/extended.marshal +0 -0
- data/lib/tls_map.rb +45 -15
- data/lib/tls_map/app/cipher/cipher.rb +151 -0
- data/lib/tls_map/{ciphersuiteinfo.rb → app/extended/ciphersuiteinfo.rb} +50 -8
- data/lib/tls_map/{extractor.rb → app/extractor/extractor.rb} +2 -2
- data/lib/tls_map/{gnutls.rb → app/gnutls.rb} +0 -0
- data/lib/tls_map/{iana.rb → app/iana.rb} +0 -0
- data/lib/tls_map/{nss.rb → app/nss.rb} +0 -0
- data/lib/tls_map/{openssl.rb → app/openssl.rb} +0 -0
- data/lib/tls_map/{output.rb → app/output.rb} +0 -0
- data/lib/tls_map/cli/cli.rb +116 -0
- data/lib/tls_map/{utils.rb → utils/utils.rb} +0 -0
- data/lib/tls_map/version.rb +1 -1
- metadata +23 -21
- data/lib/tls_map/cli.rb +0 -57
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cb7893544cb7037e6076051880f66d3140c2a0b4218446ca40a2616b81cf2fbd
|
4
|
+
data.tar.gz: bfea4a2849792217d32b3fee7c5c426748302b40291bf16e4ba966be44489915
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 521b9a33d845ae0af2101e581937154f491bd8eaa8d8ab57f6378b02994c35973ac1b41f53a080307c51325899ce89e714abe99bc92af001cb29269800f34694
|
7
|
+
data.tar.gz: dd096edd552d51a1e510e8bb0a82293f1ac67090626e88daab11578eb9f34ad6f11faa51216f8300472d189c051f879aca0a17d1e4957b38f148c295ed718e97
|
data/LICENSE
CHANGED
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 <
|
20
|
-
tls-map bulk <
|
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
|
-
<
|
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
|
-
<
|
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['<
|
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['<
|
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
|
-
|
107
|
-
|
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
|
-
|
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
|
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(
|
52
|
+
def search(criteria, term, output = :all)
|
48
53
|
@tls_map.each do |alg|
|
49
|
-
term = term.upcase if
|
50
|
-
next unless alg[
|
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
|
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(
|
97
|
+
def bulk_search(criteria, file, output = :all)
|
68
98
|
res = []
|
69
99
|
File.foreach(file) do |line|
|
70
|
-
res.push(search(
|
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
|
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
|
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
|
-
#
|
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
|
-
# @
|
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
|
-
|
63
|
-
|
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://
|
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
|
data/lib/tls_map/version.rb
CHANGED
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:
|
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-
|
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/
|
73
|
-
- lib/tls_map/
|
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/
|
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://
|
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/
|
88
|
-
changelog_uri: https://github.com/
|
89
|
-
documentation_uri: https://
|
90
|
-
homepage_uri: https://
|
91
|
-
source_code_uri: https://github.com/
|
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.
|
113
|
+
rubygems_version: 3.2.22
|
111
114
|
signing_key:
|
112
115
|
specification_version: 4
|
113
|
-
summary:
|
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
|