Skip to content
This repository was archived by the owner on Jan 24, 2022. It is now read-only.

Commit 3f69b91

Browse files
author
Jonathan Claudius
authored
Merge pull request #484 from mozilla/refactor_key_handling
Refactor fingerprints for keys
2 parents 22a934c + a12898f commit 3f69b91

File tree

6 files changed

+161
-101
lines changed

6 files changed

+161
-101
lines changed

lib/ssh_scan/crypto.rb

Lines changed: 0 additions & 60 deletions
This file was deleted.

lib/ssh_scan/public_key.rb

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
require 'openssl'
2+
require 'sshkey'
3+
require 'base64'
4+
5+
module SSHScan
6+
# All cryptography related methods.
7+
module Crypto
8+
# House methods helpful in analysing SSH public keys.
9+
class PublicKey
10+
def initialize(key_string)
11+
@key_string = key_string
12+
end
13+
14+
def valid?
15+
SSHKey.valid_ssh_public_key?(@key_string)
16+
end
17+
18+
def type
19+
if @key_string.start_with?("ssh-rsa")
20+
return "rsa"
21+
elsif @key_string.start_with?("ssh-dss")
22+
return "dsa"
23+
else
24+
return "unknown"
25+
end
26+
end
27+
28+
def length
29+
SSHKey.ssh_public_key_bits(@key_string)
30+
end
31+
32+
def fingerprint_md5
33+
SSHKey.fingerprint(@key_string)
34+
end
35+
36+
def fingerprint_sha1
37+
SSHKey.sha1_fingerprint(@key_string)
38+
end
39+
40+
def fingerprint_sha256
41+
SSHKey.sha256_fingerprint(@key_string)
42+
end
43+
44+
def to_hash
45+
{
46+
self.type => {
47+
"raw" => @key_string,
48+
"length" => self.length,
49+
"fingerprints" => {
50+
"md5" => self.fingerprint_md5,
51+
"sha1" => self.fingerprint_sha1,
52+
"sha256" => self.fingerprint_sha256
53+
}
54+
}
55+
}
56+
end
57+
end
58+
end
59+
end

lib/ssh_scan/result.rb

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ module SSHScan
88
class Result
99
def initialize()
1010
@version = SSHScan::VERSION
11-
@fingerprints = nil
11+
@keys = nil
1212
@duplicate_host_key_ips = Set.new()
1313
@compliance = {}
1414
end
@@ -157,12 +157,12 @@ def auth_methods=(auth_methods)
157157
@auth_methods = auth_methods
158158
end
159159

160-
def fingerprints=(fingerprints)
161-
@fingerprints = fingerprints
160+
def keys=(keys)
161+
@keys = keys
162162
end
163163

164-
def fingerprints
165-
@fingerprints
164+
def keys
165+
@keys
166166
end
167167

168168
def duplicate_host_key_ips=(duplicate_host_key_ips)
@@ -249,8 +249,8 @@ def to_hash
249249
"languages_client_to_server" => self.languages_client_to_server,
250250
"languages_server_to_client" => self.languages_server_to_client,
251251
"auth_methods" => self.auth_methods,
252-
"fingerprints" => self.fingerprints,
253-
"duplicate_host_key_ips" => self.duplicate_host_key_ips,
252+
"keys" => self.keys,
253+
"duplicate_host_key_ips" => self.duplicate_host_key_ips.uniq,
254254
"compliance" => @compliance,
255255
"start_time" => self.start_time,
256256
"end_time" => self.end_time,

lib/ssh_scan/scan_engine.rb

Lines changed: 15 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
require 'socket'
22
require 'ssh_scan/client'
3-
require 'ssh_scan/crypto'
3+
require 'ssh_scan/public_key'
44
require 'ssh_scan/fingerprint_database'
55
require 'ssh_scan/subprocess'
66
require 'net/ssh'
@@ -119,7 +119,7 @@ def scan_target(socket, opts)
119119
end
120120

121121
# Figure out what rsa or dsa fingerprints exist
122-
fingerprints = {}
122+
keys = {}
123123

124124
output = ""
125125

@@ -136,31 +136,17 @@ def scan_target(socket, opts)
136136

137137
for i in 0..host_keys_len
138138
if host_keys[i].eql? "ssh-dss"
139-
pkey = SSHScan::Crypto::PublicKey.new(host_keys[i + 1])
140-
fingerprints.merge!({
141-
"dsa" => {
142-
"known_bad" => pkey.bad_key?.to_s,
143-
"md5" => pkey.fingerprint_md5,
144-
"sha1" => pkey.fingerprint_sha1,
145-
"sha256" => pkey.fingerprint_sha256,
146-
}
147-
})
139+
key = SSHScan::Crypto::PublicKey.new([host_keys[i], host_keys[i + 1]].join(" "))
140+
keys.merge!(key.to_hash)
148141
end
149142

150143
if host_keys[i].eql? "ssh-rsa"
151-
pkey = SSHScan::Crypto::PublicKey.new(host_keys[i + 1])
152-
fingerprints.merge!({
153-
"rsa" => {
154-
"known_bad" => pkey.bad_key?.to_s,
155-
"md5" => pkey.fingerprint_md5,
156-
"sha1" => pkey.fingerprint_sha1,
157-
"sha256" => pkey.fingerprint_sha256,
158-
}
159-
})
144+
key = SSHScan::Crypto::PublicKey.new([host_keys[i], host_keys[i + 1]].join(" "))
145+
keys.merge!(key.to_hash)
160146
end
161147
end
162148

163-
result.fingerprints = fingerprints
149+
result.keys = keys
164150
result.set_end_time
165151

166152
return result
@@ -200,33 +186,28 @@ def scan(opts)
200186
results.each do |result|
201187
fingerprint_db.clear_fingerprints(result.ip)
202188

203-
if result.fingerprints
204-
result.fingerprints.values.each do |host_key_algo|
205-
host_key_algo.each do |fingerprint|
206-
key, value = fingerprint
207-
next if key == "known_bad"
208-
fingerprint_db.add_fingerprint(value, result.ip)
189+
if result.keys
190+
result.keys.values.each do |host_key_algo|
191+
host_key_algo['fingerprints'].values.each do |fingerprint|
192+
fingerprint_db.add_fingerprint(fingerprint, result.ip)
209193
end
210194
end
211195
end
212196
end
213197

214198
# Decorate all the results with duplicate keys
215199
results.each do |result|
216-
if result.fingerprints
200+
if result.keys
217201
ip = result.ip
218202
result.duplicate_host_key_ips = []
219-
result.fingerprints.values.each do |host_key_algo|
220-
host_key_algo.each do |fingerprint|
221-
key, value = fingerprint
222-
next if key == "known_bad"
223-
fingerprint_db.find_fingerprints(value).each do |other_ip|
203+
result.keys.values.each do |host_key_algo|
204+
host_key_algo["fingerprints"].values.each do |fingerprint|
205+
fingerprint_db.find_fingerprints(fingerprint).each do |other_ip|
224206
next if ip == other_ip
225207
result.duplicate_host_key_ips << other_ip
226208
end
227209
end
228210
end
229-
result.duplicate_host_key_ips
230211
end
231212
end
232213

spec/public_key_spec.rb

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
require 'spec_helper'
2+
require 'rspec'
3+
require 'ssh_scan/public_key'
4+
5+
describe SSHScan::Crypto::PublicKey do
6+
context "when parsing an RSA key string" do
7+
it "should parse it and have the right values for each attribute" do
8+
key_string = "ssh-rsa " +
9+
"AAAAB3NzaC1yc2EAAAADAQABAAABAQCl/BNLUxR49+3AKqhf6sWKr" +
10+
"h8XXzqXV00bEPFtcJFWxyRqC5pPWo9zRRiS2jitIcqljIQVohEEZH" +
11+
"t48vZaA1hniVfe/FmrFzuCOuQOIP2fuRgLSNHu+lWVScsHoX/MuYX" +
12+
"EIxj6aW7UpFn4lD01mvPtazXFO/tJ+LRs49YBP7UvL1smIS2xoyuH" +
13+
"7kZDN17QG08YwbIB2fApMl8rXH+2Rpj5hlv+7rcZ1dqCGtmXmvsv8" +
14+
"fKGYd7BxRy0s/d7EY4e/DeDxA1qTNV9BrBTNn6jAKIedTE5s4GNRb" +
15+
"N/Q20mP2qmw70PiTGROw6xp9SBFA7N9hjjOT7iutK/pa7y1joXKjeJ"
16+
key = SSHScan::Crypto::PublicKey.new(key_string)
17+
expect(key).to be_kind_of SSHScan::Crypto::PublicKey
18+
expect(key.valid?).to be true
19+
expect(key.type).to eq("rsa")
20+
expect(key.length).to be 2048
21+
expect(key.fingerprint_md5).to eq("fc:c5:5b:0d:f0:c6:fd:fe:80:18:62:2c:05:38:20:8a")
22+
expect(key.fingerprint_sha1).to eq("e1:3c:71:49:80:37:87:32:b5:0c:e3:86:41:ef:2e:2a:2f:14:e3:58")
23+
expect(key.fingerprint_sha256).to eq("aH0wN2cs6x5Ktf9PvIzoQeFVqDBC4I484wq6vNv9XFA=")
24+
expect(key.to_hash).to eq(
25+
{
26+
"rsa" => {
27+
"fingerprints" => {
28+
"md5"=>"fc:c5:5b:0d:f0:c6:fd:fe:80:18:62:2c:05:38:20:8a",
29+
"sha1"=>"e1:3c:71:49:80:37:87:32:b5:0c:e3:86:41:ef:2e:2a:2f:14:e3:58",
30+
"sha256"=>"aH0wN2cs6x5Ktf9PvIzoQeFVqDBC4I484wq6vNv9XFA="
31+
},
32+
"length" => 2048,
33+
"raw" => "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCl/BNLUxR49+3AKqhf6sWKrh8XXzqXV00bEPFtcJFWxyRqC5pPWo9zRRiS2jitIcqljIQVohEEZHt48vZaA1hniVfe/FmrFzuCOuQOIP2fuRgLSNHu+lWVScsHoX/MuYXEIxj6aW7UpFn4lD01mvPtazXFO/tJ+LRs49YBP7UvL1smIS2xoyuH7kZDN17QG08YwbIB2fApMl8rXH+2Rpj5hlv+7rcZ1dqCGtmXmvsv8fKGYd7BxRy0s/d7EY4e/DeDxA1qTNV9BrBTNn6jAKIedTE5s4GNRbN/Q20mP2qmw70PiTGROw6xp9SBFA7N9hjjOT7iutK/pa7y1joXKjeJ",
34+
}
35+
}
36+
)
37+
end
38+
end
39+
40+
context "when parsing an DSA key string" do
41+
it "should parse it and have the right values for each attribute" do
42+
key_string = "ssh-dss " +
43+
"AAAAB3NzaC1kc3MAAACBAOXOC6kuB7xDMgHS79KFQITNeAT9tMKd2oK1" +
44+
"c6bQEHRgTSMP3sWZ1cntWVFKl5u6MEuEBBT9PZKWsy7vRE525Wwt+NbR" +
45+
"IBso3vYFF1MtxZKpAsF+gbGI7y+aZcIceXrHkkY2bz3oGb9I9MZ2DSu2" +
46+
"9crW11YHCmuOJ2FJiDcx7dV9AAAAFQC+Ws9e0KJaAsN8cj75DbTQumrd" +
47+
"JQAAAIBjn5EA5JvQg7xu8TRcNmZWhuyBLoOZczU6nk2h4i+x4pbpVMVr" +
48+
"Ch5Lr8wsH60w7IW4yKg6JvPlzmQW0ZRZAwnU9sC3YO64H1RFQg8tnmRr" +
49+
"w0I9oi6wKPEe5rLgbdr9jYHePs9tiV+ZFfUKmXh0s7srr/dwmX/gHCPI" +
50+
"whLEVa+dLQAAAIEAn/+dSyf6KXdfKNyx9MYc1l2/2YUhVuxClF26PNQX" +
51+
"0CZhcSoDyUXU/eAqaS7S6EYqtM/8FK1OZY1tzM5Nm4GWY2LLF22Q2YkK" +
52+
"ItkhfS3GaD5JeuTQ+HK0F+wQjmpqt2pUulVQXQAjvE1qoRFQ4/yeVrvh" +
53+
"VqCzFICnariQP7tMYEo="
54+
key = SSHScan::Crypto::PublicKey.new(key_string)
55+
expect(key).to be_kind_of SSHScan::Crypto::PublicKey
56+
expect(key.valid?).to be true
57+
expect(key.type).to eq("dsa")
58+
expect(key.length).to be 1024
59+
expect(key.fingerprint_md5).to eq("6b:5f:8d:57:be:2e:55:7f:e3:d7:15:d1:66:17:d8:8c")
60+
expect(key.fingerprint_sha1).to eq("49:84:7f:d7:9d:84:2a:20:61:72:10:3f:2c:b1:16:9b:12:5b:e7:07")
61+
expect(key.fingerprint_sha256).to eq("sWZzgrGxzs/aMmcU2w6FyET/Iihd6HL1qNyDZnO0NDw=")
62+
expect(key.to_hash).to eq(
63+
{
64+
"dsa" => {
65+
"fingerprints" => {
66+
"md5"=>"6b:5f:8d:57:be:2e:55:7f:e3:d7:15:d1:66:17:d8:8c",
67+
"sha1"=>"49:84:7f:d7:9d:84:2a:20:61:72:10:3f:2c:b1:16:9b:12:5b:e7:07",
68+
"sha256"=>"sWZzgrGxzs/aMmcU2w6FyET/Iihd6HL1qNyDZnO0NDw="
69+
},
70+
"length" => 1024,
71+
"raw" => "ssh-dss AAAAB3NzaC1kc3MAAACBAOXOC6kuB7xDMgHS79KFQITNeAT9tMKd2oK1c6bQEHRgTSMP3sWZ1cntWVFKl5u6MEuEBBT9PZKWsy7vRE525Wwt+NbRIBso3vYFF1MtxZKpAsF+gbGI7y+aZcIceXrHkkY2bz3oGb9I9MZ2DSu29crW11YHCmuOJ2FJiDcx7dV9AAAAFQC+Ws9e0KJaAsN8cj75DbTQumrdJQAAAIBjn5EA5JvQg7xu8TRcNmZWhuyBLoOZczU6nk2h4i+x4pbpVMVrCh5Lr8wsH60w7IW4yKg6JvPlzmQW0ZRZAwnU9sC3YO64H1RFQg8tnmRrw0I9oi6wKPEe5rLgbdr9jYHePs9tiV+ZFfUKmXh0s7srr/dwmX/gHCPIwhLEVa+dLQAAAIEAn/+dSyf6KXdfKNyx9MYc1l2/2YUhVuxClF26PNQX0CZhcSoDyUXU/eAqaS7S6EYqtM/8FK1OZY1tzM5Nm4GWY2LLF22Q2YkKItkhfS3GaD5JeuTQ+HK0F+wQjmpqt2pUulVQXQAjvE1qoRFQ4/yeVrvhVqCzFICnariQP7tMYEo=",
72+
}
73+
}
74+
)
75+
end
76+
end
77+
78+
end

spec/ssh_scan/result_spec.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
require 'spec_helper'
22
require 'rspec'
3+
require 'ssh_scan/version'
34
require 'ssh_scan/result'
45

56
describe SSHScan::Result do
@@ -32,6 +33,7 @@
3233
expect(result.start_time).to be_nil
3334
expect(result.end_time).to be_nil
3435
expect(result.scan_duration).to be_nil
36+
expect(result.keys).to be_nil
3537
end
3638

3739
context "when setting IP" do

0 commit comments

Comments
 (0)