@@ -30,19 +30,32 @@ def initialize(path)
30
30
end
31
31
32
32
def keys_for ( hostlist )
33
- keys = hosts_keys || parse_file
33
+ keys , hashes = hosts_keys , hosts_hashes
34
+ parse_file unless keys && hashes
35
+ keys , hashes = hosts_keys , hosts_hashes
36
+
34
37
hostlist . split ( ',' ) . each do |host |
35
- if key_list = keys [ host ]
36
- return key_list
38
+ key_list = keys [ host ]
39
+ return key_list if key_list
40
+
41
+ hashes . each do |( hmac , salt ) , key |
42
+ if OpenSSL ::HMAC . digest ( sha1 , salt , host ) == hmac
43
+ return key
44
+ end
37
45
end
38
46
end
47
+
39
48
[ ]
40
49
end
41
50
42
51
private
43
52
44
53
attr_reader :path
45
- attr_accessor :hosts_keys
54
+ attr_accessor :hosts_keys , :hosts_hashes
55
+
56
+ def sha1
57
+ @sha1 ||= OpenSSL ::Digest . new ( 'sha1' )
58
+ end
46
59
47
60
def parse_file
48
61
synchronize do
@@ -51,35 +64,58 @@ def parse_file
51
64
return self . hosts_keys = { } unless File . readable? ( path )
52
65
53
66
new_keys = { }
67
+ new_hashes = [ ]
54
68
File . open ( path ) do |file |
55
69
scanner = StringScanner . new ( "" )
56
70
file . each_line do |line |
57
71
scanner . string = line
58
- hostlist , key = parse_line ( scanner )
59
- next unless key
60
-
61
- hostlist . each do |host |
62
- ( new_keys [ host ] ||= [ ] ) << key
63
- end
72
+ parse_line ( scanner , new_keys , new_hashes )
64
73
end
65
74
end
66
- return self . hosts_keys = new_keys
75
+ self . hosts_keys = new_keys
76
+ self . hosts_hashes = new_hashes
77
+ end
78
+ end
79
+
80
+ def parse_line ( scanner , hosts_keys , hosts_hashes )
81
+ return if empty_line? ( scanner )
82
+
83
+ hostlist = parse_hostlist ( scanner )
84
+ return unless supported_type? ( scanner )
85
+ key = parse_key ( scanner )
86
+
87
+ if hostlist . size == 1 && hostlist . first =~ /\A \| 1(\| .+){2}\z /
88
+ hosts_hashes << [ parse_host_hash ( hostlist . first ) , key ]
89
+ else
90
+ hostlist . each do |host |
91
+ ( hosts_keys [ host ] ||= [ ] ) << key
92
+ end
67
93
end
68
94
end
69
95
70
- def parse_line ( scanner )
96
+ def parse_host_hash ( line )
97
+ _ , _ , salt , hmac = line . split ( '|' )
98
+ [ Base64 . decode64 ( hmac ) , Base64 . decode64 ( salt ) ]
99
+ end
100
+
101
+ def empty_line? ( scanner )
71
102
scanner . skip ( /\s */ )
72
- return if scanner . match? ( /$|#/ )
103
+ scanner . match? ( /$|#/ )
104
+ end
73
105
74
- hostlist = scanner . scan ( / \S +/ ) . split ( ',' )
106
+ def parse_hostlist ( scanner )
75
107
scanner . skip ( /\s */ )
76
- type = scanner . scan ( /\S +/ )
108
+ scanner . scan ( /\S +/ ) . split ( ',' )
109
+ end
77
110
78
- return unless Net ::SSH ::KnownHosts ::SUPPORTED_TYPE . include? ( type )
111
+ def supported_type? ( scanner )
112
+ scanner . skip ( /\s */ )
113
+ Net ::SSH ::KnownHosts ::SUPPORTED_TYPE . include? ( scanner . scan ( /\S +/ ) )
114
+ end
79
115
116
+ def parse_key ( scanner )
80
117
scanner . skip ( /\s */ )
81
- blob = scanner . rest . unpack ( "m*" ) . first
82
- return hostlist , Net ::SSH ::Buffer . new ( blob ) . read_key
118
+ Net ::SSH ::Buffer . new ( scanner . rest . unpack ( "m*" ) . first ) . read_key
83
119
end
84
120
end
85
121
0 commit comments