tls-map 1.3.0 → 2.1.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 +32 -13
- data/data/extended.marshal +0 -0
- data/lib/tls_map/app/cipher/cipher.rb +151 -0
- data/lib/tls_map/{ciphersuiteinfo.rb → app/extended/ciphersuiteinfo.rb} +51 -12
- data/lib/tls_map/{extractor.rb → app/extractor/extractor.rb} +26 -5
- 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/utils.rb +29 -0
- data/lib/tls_map/version.rb +1 -1
- data/lib/tls_map.rb +50 -20
- metadata +25 -168
- data/lib/tls_map/cli.rb +0 -57
- data/lib/tls_map/utils.rb +0 -24
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1d94873d0b6f9c1274ca7ae4b4510cbbfb5351fb71b31d5fd6b7b51a700810b5
|
|
4
|
+
data.tar.gz: c706973ad2b5b944ad41b72447ad2b5b6fd0d6cd8e7697ed2e0441598c905157
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 36abc411fbfb1139a1abce1795dc76dad40baf48a801ff52302e0f13a5e8d62f484a68e024523417f6d926b9a175043d0256b15a3840f5b1ed3e1a90d26a7f65
|
|
7
|
+
data.tar.gz: c0274a56daae44a2192e761bc782ec87ec71d2616738ed8053f8e1e2beb8a6bce7d6e95d694356f72d3ae891c69d5b210e78f9b84f2bd308016205f70b9e852d
|
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,10 +96,10 @@ 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
|
-
puts
|
|
102
|
+
puts Paint[h[args['--output2'].to_sym], :green]
|
|
97
103
|
end
|
|
98
104
|
elsif args['export']
|
|
99
105
|
cli = TLSmap::CLI.new(args['--force'])
|
|
@@ -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
|
|
@@ -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,26 +44,65 @@ 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
|
|
47
54
|
|
|
48
|
-
|
|
49
|
-
|
|
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
|
|
50
58
|
|
|
51
59
|
# Will automatically fetch source files and parse them.
|
|
52
60
|
def initialize
|
|
53
|
-
@tech_file = tmpfile('tech', TECH_DATA)
|
|
54
|
-
@vuln_file = tmpfile('vuln', VULN_DATA)
|
|
61
|
+
@tech_file = Utils.tmpfile('tech', TECH_DATA)
|
|
62
|
+
@vuln_file = Utils.tmpfile('vuln', VULN_DATA)
|
|
55
63
|
@tech = parse_tech
|
|
56
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!)
|
|
74
|
+
end
|
|
75
|
+
|
|
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
|
|
57
86
|
end
|
|
58
87
|
|
|
59
|
-
# Retrieve advanced about a cipher on Cipher Suite Info API and enhanced it
|
|
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.
|
|
60
90
|
# @param iana_name [String] IANA cipher name
|
|
61
|
-
# @
|
|
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
|
|
62
94
|
# except `vulns` which is an array of hashes containing two keys: `:severity` (integer) and `:description`
|
|
63
95
|
# (string). Each hash in `vulns` correspond to a vulnerability.
|
|
64
|
-
def extend(iana_name) # rubocop:disable Metrics/MethodLength
|
|
65
|
-
|
|
66
|
-
|
|
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
|
+
|
|
67
106
|
out.store('vulns', [])
|
|
68
107
|
%w[openssl_name gnutls_name hex_byte_1 hex_byte_2].each do |key|
|
|
69
108
|
out.delete(key)
|
|
@@ -117,7 +156,7 @@ module TLSmap
|
|
|
117
156
|
nil
|
|
118
157
|
end
|
|
119
158
|
|
|
120
|
-
protected :parse_tech, :parse_vuln
|
|
159
|
+
protected :parse_tech, :parse_vuln, :fetch_ciphersuite
|
|
121
160
|
end
|
|
122
161
|
end
|
|
123
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
|
|
|
@@ -80,6 +80,26 @@ module TLSmap
|
|
|
80
80
|
def parse(tool, file)
|
|
81
81
|
# Convert string to class
|
|
82
82
|
@ciphers = Object.const_get("TLSmap::App::Extractor::#{normalize(tool)}").parse(file)
|
|
83
|
+
rescue StandardError
|
|
84
|
+
warn helper(tool)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Commands for {helper}
|
|
88
|
+
CMD = {
|
|
89
|
+
'sslyze' => 'sslyze --json_out=example.org.json example.org',
|
|
90
|
+
'sslscan2' => 'sslscan2 --show-cipher-ids --xml=example.org.xml example.org',
|
|
91
|
+
'testssl' => 'testssl --jsonfile-pretty example.org.json --mapping no-openssl --cipher-per-proto example.org',
|
|
92
|
+
'ssllabs-scan' => 'ssllabs-scan --quiet example.org > example.org.json'
|
|
93
|
+
}.freeze
|
|
94
|
+
|
|
95
|
+
# Get the external tool command used to generate the expected result format
|
|
96
|
+
# @param tool [String] Possible values: `sslyze`, `sslscan2`, `testssl`, `ssllabs-scan`
|
|
97
|
+
# @return [String] external tool command used to generate the expected result format used in input of the extract
|
|
98
|
+
# command (CLI) / {parse} method (library)
|
|
99
|
+
def helper(tool)
|
|
100
|
+
intro = 'You may not be provinding the right format.'
|
|
101
|
+
outro = 'See https://noraj.github.io/tls-map/yard/TLSmap/App/Extractor'
|
|
102
|
+
"#{intro}\nUse this command: #{CMD[tool]}\n#{outro}"
|
|
83
103
|
end
|
|
84
104
|
|
|
85
105
|
# Convert cmdline tool name to Class name
|
|
@@ -87,7 +107,8 @@ module TLSmap
|
|
|
87
107
|
tool.split('-').map(&:capitalize).join
|
|
88
108
|
end
|
|
89
109
|
|
|
90
|
-
protected :normalize
|
|
110
|
+
protected :normalize, :helper
|
|
111
|
+
private_constant :CMD
|
|
91
112
|
|
|
92
113
|
# Parsing SSLyze
|
|
93
114
|
class Sslyze
|
|
@@ -97,7 +118,7 @@ module TLSmap
|
|
|
97
118
|
# See {TLSmap::App::Extractor}
|
|
98
119
|
# @return [Array<String>] Cipher array (IANA names)
|
|
99
120
|
def parse(file)
|
|
100
|
-
data =
|
|
121
|
+
data = Utils.json_load_file(file)
|
|
101
122
|
extract_cipher(data)
|
|
102
123
|
end
|
|
103
124
|
|
|
@@ -164,7 +185,7 @@ module TLSmap
|
|
|
164
185
|
# See {TLSmap::App::Extractor}
|
|
165
186
|
# @return [Array<String>] Cipher array (IANA names)
|
|
166
187
|
def parse(file)
|
|
167
|
-
data =
|
|
188
|
+
data = Utils.json_load_file(file)
|
|
168
189
|
extract_cipher(data)
|
|
169
190
|
end
|
|
170
191
|
|
|
@@ -214,7 +235,7 @@ module TLSmap
|
|
|
214
235
|
# See {TLSmap::App::Extractor}
|
|
215
236
|
# @return [Array<String>] Cipher array (IANA names)
|
|
216
237
|
def parse(file)
|
|
217
|
-
data =
|
|
238
|
+
data = Utils.json_load_file(file)
|
|
218
239
|
extract_cipher(data)
|
|
219
240
|
end
|
|
220
241
|
|
|
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
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Ruby internal
|
|
4
|
+
require 'net/http'
|
|
5
|
+
require 'tempfile'
|
|
6
|
+
require 'json'
|
|
7
|
+
|
|
8
|
+
# TLS map module
|
|
9
|
+
module TLSmap
|
|
10
|
+
# Generic utilities
|
|
11
|
+
module Utils
|
|
12
|
+
def self.tmpfile(name, url)
|
|
13
|
+
tmp = Tempfile.new(name)
|
|
14
|
+
tmp.write(Net::HTTP.get(URI(url)))
|
|
15
|
+
tmp.close
|
|
16
|
+
tmp
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# bring JSON.load_file before ruby 3.0.0
|
|
20
|
+
# https://ruby-doc.org/stdlib-3.0.0/libdoc/json/rdoc/JSON.html#method-i-load_file
|
|
21
|
+
def self.json_load_file(filespec, opts = {})
|
|
22
|
+
if RUBY_VERSION < '3.0.0'
|
|
23
|
+
JSON.parse(File.read(filespec), opts)
|
|
24
|
+
else
|
|
25
|
+
JSON.load_file(filespec, opts)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
data/lib/tls_map/version.rb
CHANGED
data/lib/tls_map.rb
CHANGED
|
@@ -4,26 +4,31 @@
|
|
|
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
|
-
@iana_file = tmpfile('iana', IANA_URL)
|
|
23
|
-
@openssl_file = tmpfile('openssl', OPENSSL_URL)
|
|
24
|
-
@openssl_file2 = tmpfile('openssl', OPENSSL_URL2)
|
|
25
|
-
@gnutls_file = tmpfile('gnutls', GNUTLS_URL)
|
|
26
|
-
@nss_file = tmpfile('nss', NSS_URL)
|
|
27
|
+
@iana_file = Utils.tmpfile('iana', IANA_URL)
|
|
28
|
+
@openssl_file = Utils.tmpfile('openssl', OPENSSL_URL)
|
|
29
|
+
@openssl_file2 = Utils.tmpfile('openssl', OPENSSL_URL2)
|
|
30
|
+
@gnutls_file = Utils.tmpfile('gnutls', GNUTLS_URL)
|
|
31
|
+
@nss_file = Utils.tmpfile('nss', NSS_URL)
|
|
27
32
|
|
|
28
33
|
@tls_map = []
|
|
29
34
|
parse
|
|
@@ -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
|
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.
|
|
4
|
+
version: 2.1.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:
|
|
11
|
+
date: 2022-01-31 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: docopt
|
|
@@ -52,154 +52,9 @@ dependencies:
|
|
|
52
52
|
- - "~>"
|
|
53
53
|
- !ruby/object:Gem::Version
|
|
54
54
|
version: '3.2'
|
|
55
|
-
- !ruby/object:Gem::Dependency
|
|
56
|
-
name: bundler
|
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
|
58
|
-
requirements:
|
|
59
|
-
- - ">="
|
|
60
|
-
- !ruby/object:Gem::Version
|
|
61
|
-
version: 2.1.0
|
|
62
|
-
- - "<"
|
|
63
|
-
- !ruby/object:Gem::Version
|
|
64
|
-
version: '2.3'
|
|
65
|
-
type: :development
|
|
66
|
-
prerelease: false
|
|
67
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
68
|
-
requirements:
|
|
69
|
-
- - ">="
|
|
70
|
-
- !ruby/object:Gem::Version
|
|
71
|
-
version: 2.1.0
|
|
72
|
-
- - "<"
|
|
73
|
-
- !ruby/object:Gem::Version
|
|
74
|
-
version: '2.3'
|
|
75
|
-
- !ruby/object:Gem::Dependency
|
|
76
|
-
name: commonmarker
|
|
77
|
-
requirement: !ruby/object:Gem::Requirement
|
|
78
|
-
requirements:
|
|
79
|
-
- - "~>"
|
|
80
|
-
- !ruby/object:Gem::Version
|
|
81
|
-
version: '0.21'
|
|
82
|
-
type: :development
|
|
83
|
-
prerelease: false
|
|
84
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
85
|
-
requirements:
|
|
86
|
-
- - "~>"
|
|
87
|
-
- !ruby/object:Gem::Version
|
|
88
|
-
version: '0.21'
|
|
89
|
-
- !ruby/object:Gem::Dependency
|
|
90
|
-
name: github-markup
|
|
91
|
-
requirement: !ruby/object:Gem::Requirement
|
|
92
|
-
requirements:
|
|
93
|
-
- - "~>"
|
|
94
|
-
- !ruby/object:Gem::Version
|
|
95
|
-
version: '4.0'
|
|
96
|
-
type: :development
|
|
97
|
-
prerelease: false
|
|
98
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
99
|
-
requirements:
|
|
100
|
-
- - "~>"
|
|
101
|
-
- !ruby/object:Gem::Version
|
|
102
|
-
version: '4.0'
|
|
103
|
-
- !ruby/object:Gem::Dependency
|
|
104
|
-
name: minitest
|
|
105
|
-
requirement: !ruby/object:Gem::Requirement
|
|
106
|
-
requirements:
|
|
107
|
-
- - "~>"
|
|
108
|
-
- !ruby/object:Gem::Version
|
|
109
|
-
version: '5.12'
|
|
110
|
-
type: :development
|
|
111
|
-
prerelease: false
|
|
112
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
113
|
-
requirements:
|
|
114
|
-
- - "~>"
|
|
115
|
-
- !ruby/object:Gem::Version
|
|
116
|
-
version: '5.12'
|
|
117
|
-
- !ruby/object:Gem::Dependency
|
|
118
|
-
name: minitest-skip
|
|
119
|
-
requirement: !ruby/object:Gem::Requirement
|
|
120
|
-
requirements:
|
|
121
|
-
- - "~>"
|
|
122
|
-
- !ruby/object:Gem::Version
|
|
123
|
-
version: '0.0'
|
|
124
|
-
type: :development
|
|
125
|
-
prerelease: false
|
|
126
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
127
|
-
requirements:
|
|
128
|
-
- - "~>"
|
|
129
|
-
- !ruby/object:Gem::Version
|
|
130
|
-
version: '0.0'
|
|
131
|
-
- !ruby/object:Gem::Dependency
|
|
132
|
-
name: rake
|
|
133
|
-
requirement: !ruby/object:Gem::Requirement
|
|
134
|
-
requirements:
|
|
135
|
-
- - "~>"
|
|
136
|
-
- !ruby/object:Gem::Version
|
|
137
|
-
version: '13.0'
|
|
138
|
-
type: :development
|
|
139
|
-
prerelease: false
|
|
140
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
141
|
-
requirements:
|
|
142
|
-
- - "~>"
|
|
143
|
-
- !ruby/object:Gem::Version
|
|
144
|
-
version: '13.0'
|
|
145
|
-
- !ruby/object:Gem::Dependency
|
|
146
|
-
name: redcarpet
|
|
147
|
-
requirement: !ruby/object:Gem::Requirement
|
|
148
|
-
requirements:
|
|
149
|
-
- - "~>"
|
|
150
|
-
- !ruby/object:Gem::Version
|
|
151
|
-
version: '3.5'
|
|
152
|
-
type: :development
|
|
153
|
-
prerelease: false
|
|
154
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
155
|
-
requirements:
|
|
156
|
-
- - "~>"
|
|
157
|
-
- !ruby/object:Gem::Version
|
|
158
|
-
version: '3.5'
|
|
159
|
-
- !ruby/object:Gem::Dependency
|
|
160
|
-
name: rubocop
|
|
161
|
-
requirement: !ruby/object:Gem::Requirement
|
|
162
|
-
requirements:
|
|
163
|
-
- - "~>"
|
|
164
|
-
- !ruby/object:Gem::Version
|
|
165
|
-
version: '1.10'
|
|
166
|
-
type: :development
|
|
167
|
-
prerelease: false
|
|
168
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
169
|
-
requirements:
|
|
170
|
-
- - "~>"
|
|
171
|
-
- !ruby/object:Gem::Version
|
|
172
|
-
version: '1.10'
|
|
173
|
-
- !ruby/object:Gem::Dependency
|
|
174
|
-
name: webrick
|
|
175
|
-
requirement: !ruby/object:Gem::Requirement
|
|
176
|
-
requirements:
|
|
177
|
-
- - "~>"
|
|
178
|
-
- !ruby/object:Gem::Version
|
|
179
|
-
version: '1.7'
|
|
180
|
-
type: :development
|
|
181
|
-
prerelease: false
|
|
182
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
183
|
-
requirements:
|
|
184
|
-
- - "~>"
|
|
185
|
-
- !ruby/object:Gem::Version
|
|
186
|
-
version: '1.7'
|
|
187
|
-
- !ruby/object:Gem::Dependency
|
|
188
|
-
name: yard
|
|
189
|
-
requirement: !ruby/object:Gem::Requirement
|
|
190
|
-
requirements:
|
|
191
|
-
- - "~>"
|
|
192
|
-
- !ruby/object:Gem::Version
|
|
193
|
-
version: '0.9'
|
|
194
|
-
type: :development
|
|
195
|
-
prerelease: false
|
|
196
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
197
|
-
requirements:
|
|
198
|
-
- - "~>"
|
|
199
|
-
- !ruby/object:Gem::Version
|
|
200
|
-
version: '0.9'
|
|
201
55
|
description: 'CLI & library for mapping TLS cipher algorithm names: IANA, OpenSSL,
|
|
202
|
-
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'
|
|
203
58
|
email: alexandre.zanni@engineer.com
|
|
204
59
|
executables:
|
|
205
60
|
- tls-map
|
|
@@ -210,31 +65,34 @@ files:
|
|
|
210
65
|
- LICENSE
|
|
211
66
|
- bin/tls-map
|
|
212
67
|
- bin/tls-map_console
|
|
68
|
+
- data/extended.marshal
|
|
213
69
|
- data/mapping.json
|
|
214
70
|
- data/mapping.marshal
|
|
215
71
|
- data/mapping.md
|
|
216
72
|
- data/mapping.min.json
|
|
217
73
|
- lib/tls_map.rb
|
|
218
|
-
- lib/tls_map/
|
|
219
|
-
- lib/tls_map/
|
|
220
|
-
- lib/tls_map/extractor.rb
|
|
221
|
-
- lib/tls_map/gnutls.rb
|
|
222
|
-
- lib/tls_map/iana.rb
|
|
223
|
-
- lib/tls_map/nss.rb
|
|
224
|
-
- lib/tls_map/openssl.rb
|
|
225
|
-
- lib/tls_map/output.rb
|
|
226
|
-
- 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
|
|
227
84
|
- lib/tls_map/version.rb
|
|
228
|
-
homepage: https://
|
|
85
|
+
homepage: https://noraj.github.io/tls-map/
|
|
229
86
|
licenses:
|
|
230
87
|
- MIT
|
|
231
88
|
metadata:
|
|
232
89
|
yard.run: yard
|
|
233
|
-
bug_tracker_uri: https://github.com/
|
|
234
|
-
changelog_uri: https://github.com/
|
|
235
|
-
documentation_uri: https://
|
|
236
|
-
homepage_uri: https://
|
|
237
|
-
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/
|
|
95
|
+
rubygems_mfa_required: 'true'
|
|
238
96
|
post_install_message:
|
|
239
97
|
rdoc_options: []
|
|
240
98
|
require_paths:
|
|
@@ -246,16 +104,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
246
104
|
version: 2.6.0
|
|
247
105
|
- - "<"
|
|
248
106
|
- !ruby/object:Gem::Version
|
|
249
|
-
version: '3.
|
|
107
|
+
version: '3.2'
|
|
250
108
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
251
109
|
requirements:
|
|
252
110
|
- - ">="
|
|
253
111
|
- !ruby/object:Gem::Version
|
|
254
112
|
version: '0'
|
|
255
113
|
requirements: []
|
|
256
|
-
rubygems_version: 3.
|
|
114
|
+
rubygems_version: 3.3.3
|
|
257
115
|
signing_key:
|
|
258
116
|
specification_version: 4
|
|
259
|
-
summary:
|
|
260
|
-
NSS'
|
|
117
|
+
summary: CLI & library for TLS cipher suites manipulation
|
|
261
118
|
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
|
data/lib/tls_map/utils.rb
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
# Ruby internal
|
|
4
|
-
require 'net/http'
|
|
5
|
-
require 'tempfile'
|
|
6
|
-
|
|
7
|
-
# TLS map module
|
|
8
|
-
module TLSmap
|
|
9
|
-
# Generic utilities
|
|
10
|
-
module Utils
|
|
11
|
-
def tmpfile(name, url)
|
|
12
|
-
tmp = Tempfile.new(name)
|
|
13
|
-
tmp.write(Net::HTTP.get(URI(url)))
|
|
14
|
-
tmp.close
|
|
15
|
-
tmp
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
# TLS mapping
|
|
20
|
-
class App
|
|
21
|
-
include Utils
|
|
22
|
-
protected :tmpfile
|
|
23
|
-
end
|
|
24
|
-
end
|