@@ -30,56 +30,96 @@ def initialize(path)
3030 end
3131
3232 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+
3437 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
3745 end
3846 end
47+
3948 [ ]
4049 end
4150
4251 private
4352
4453 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
4659
4760 def parse_file
4861 synchronize do
49- return keys if hosts_keys
62+ return if hosts_keys && hosts_hashes
5063
51- return self . hosts_keys = { } unless File . readable? ( path )
64+ unless File . readable? ( path )
65+ self . hosts_keys = { }
66+ self . hosts_hashes = [ ]
67+ return
68+ end
5269
5370 new_keys = { }
71+ new_hashes = [ ]
5472 File . open ( path ) do |file |
5573 scanner = StringScanner . new ( "" )
5674 file . each_line do |line |
5775 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
76+ parse_line ( scanner , new_keys , new_hashes )
6477 end
6578 end
66- return self . hosts_keys = new_keys
79+ self . hosts_keys = new_keys
80+ self . hosts_hashes = new_hashes
6781 end
6882 end
6983
70- def parse_line ( scanner )
84+ def parse_line ( scanner , hosts_keys , hosts_hashes )
85+ return if empty_line? ( scanner )
86+
87+ hostlist = parse_hostlist ( scanner )
88+ return unless supported_type? ( scanner )
89+ key = parse_key ( scanner )
90+
91+ if hostlist . size == 1 && hostlist . first =~ /\A \| 1(\| .+){2}\z /
92+ hosts_hashes << [ parse_host_hash ( hostlist . first ) , key ]
93+ else
94+ hostlist . each do |host |
95+ ( hosts_keys [ host ] ||= [ ] ) << key
96+ end
97+ end
98+ end
99+
100+ def parse_host_hash ( line )
101+ _ , _ , salt , hmac = line . split ( '|' )
102+ [ Base64 . decode64 ( hmac ) , Base64 . decode64 ( salt ) ]
103+ end
104+
105+ def empty_line? ( scanner )
71106 scanner . skip ( /\s */ )
72- return if scanner . match? ( /$|#/ )
107+ scanner . match? ( /$|#/ )
108+ end
73109
74- hostlist = scanner . scan ( / \S +/ ) . split ( ',' )
110+ def parse_hostlist ( scanner )
75111 scanner . skip ( /\s */ )
76- type = scanner . scan ( /\S +/ )
112+ scanner . scan ( /\S +/ ) . split ( ',' )
113+ end
77114
78- return unless Net ::SSH ::KnownHosts ::SUPPORTED_TYPE . include? ( type )
115+ def supported_type? ( scanner )
116+ scanner . skip ( /\s */ )
117+ Net ::SSH ::KnownHosts ::SUPPORTED_TYPE . include? ( scanner . scan ( /\S +/ ) )
118+ end
79119
120+ def parse_key ( scanner )
80121 scanner . skip ( /\s */ )
81- blob = scanner . rest . unpack ( "m*" ) . first
82- return hostlist , Net ::SSH ::Buffer . new ( blob ) . read_key
122+ Net ::SSH ::Buffer . new ( scanner . rest . unpack ( "m*" ) . first ) . read_key
83123 end
84124 end
85125
0 commit comments