diff options
author | elijah <elijah@riseup.net> | 2012-12-24 02:02:24 -0800 |
---|---|---|
committer | elijah <elijah@riseup.net> | 2012-12-24 02:02:24 -0800 |
commit | 457c2bf135be68b3c49ff20d3f23a6f8507aeda5 (patch) | |
tree | 73afc8568c6a83706111b19d35ba04d0cd94b2e0 /lib/nickserver/hkp |
initial commit
Diffstat (limited to 'lib/nickserver/hkp')
-rw-r--r-- | lib/nickserver/hkp/fetch_key.rb | 51 | ||||
-rw-r--r-- | lib/nickserver/hkp/fetch_key_info.rb | 66 | ||||
-rw-r--r-- | lib/nickserver/hkp/key_info.rb | 69 |
3 files changed, 186 insertions, 0 deletions
diff --git a/lib/nickserver/hkp/fetch_key.rb b/lib/nickserver/hkp/fetch_key.rb new file mode 100644 index 0000000..c72ee11 --- /dev/null +++ b/lib/nickserver/hkp/fetch_key.rb @@ -0,0 +1,51 @@ +require 'em-http' + +module Nickserver; module HKP + + class FetchKey + include EM::Deferrable + + def get(uid) + FetchKeyInfo.new.search(uid).callback {|key_info_list| + best = pick_best_key(key_info_list) + get_key_by_fingerprint(best.keyid) {|key| + self.succeed key + } + }.errback {|msg| + self.fail msg + } + self + end + + # + # fetches ascii armored OpenPGP public key from the keyserver + # + def get_key_by_fingerprint(key_id) + params = {:op => 'get', :search => "0x" + key_id, :exact => 'on', :options => 'mr'} + http = EventMachine::HttpRequest.new(Config.sks_url).get(:query => params) + http.callback { + if http.response_header.status != 200 + self.fail http.response_header.status #"Request failed with #{http.response_header.status}: #{http.response}" + else + yield http.response + end + } + http.errback { + self.fail http.error + } + end + + protected + + # + # for now, just pick the newest key. + # + # in the future, we should perhaps pick the newest key + # that is signed by the oldest key. + # + def pick_best_key(key_info_list) + key_info_list.sort {|a,b| a.creationdate <=> b.creationdate}.last + end + end + +end; end
\ No newline at end of file diff --git a/lib/nickserver/hkp/fetch_key_info.rb b/lib/nickserver/hkp/fetch_key_info.rb new file mode 100644 index 0000000..48ef48a --- /dev/null +++ b/lib/nickserver/hkp/fetch_key_info.rb @@ -0,0 +1,66 @@ +require 'em-http' + +# +# used to fetch an array of KeyInfo objects that match the given uid. +# + +module Nickserver; module HKP + class FetchKeyInfo + include EM::Deferrable + + # for this regexp to work, the source text must end in a trailing "\n", + # which the output of sks does. + MATCH_PUB_KEY = /(^pub:.+?\n(^uid:.+?\n)+)/m + + def search(uid) + # in practice, exact=on seems to have no effect + params = {:op => 'vindex', :search => uid, :exact => 'on', :options => 'mr', :fingerprint => 'on'} + EventMachine::HttpRequest.new(Config.sks_url).get(:query => params).callback {|http| + if http.response_header.status != 200 + self.fail http.response_header.status + else + self.succeed parse(uid, http.response) + end + }.errback {|http| + self.fail http.error + } + self + end + + # + # input: + # uid -- uid to search for + # vindex_result -- raw output from a vindex hkp query (machine readable) + # + # returns: + # an array of eligible keys (as HKPKeyInfo objects) matching uid. + # + # keys are eliminated from eligibility for a number of reasons, including expiration, + # revocation, uid match, key length, and so on... + # + def parse(uid, vindex_result) + keys = [] + now = Time.now + vindex_result.scan(MATCH_PUB_KEY).each do |match| + key_info = KeyInfo.new(match[0]) + if key_info.uids.include?(uid) + if key_info.keylen <= 1024 + #puts 'key length is too short' + elsif key_info.expired? + #puts 'ignoring expired key' + elsif key_info.revoked? + #puts 'ignoring revoked key' + elsif key_info.disabled? + #puts 'ignoring disabled key' + elsif key_info.expirationdate && key_info.expirationdate < now + #puts 'ignoring expired key' + else + keys << key_info + end + end + end + keys + end + end + +end; end
\ No newline at end of file diff --git a/lib/nickserver/hkp/key_info.rb b/lib/nickserver/hkp/key_info.rb new file mode 100644 index 0000000..adb75d8 --- /dev/null +++ b/lib/nickserver/hkp/key_info.rb @@ -0,0 +1,69 @@ +require 'cgi' + +# +# Class to represent the key information result from a query to a key server +# (but not the key itself). +# +# The initialize method parses the hkp 'machine readable' output. +# +# format definition of machine readable index output is here: +# http://tools.ietf.org/html/draft-shaw-openpgp-hkp-00#section-5.2 +# +module Nickserver; module HKP + class KeyInfo + attr_accessor :uids, :keyid, :algo, :keylen, :creationdate, :expirationdate, :flags + + def initialize(hkp_record) + uid_lines = hkp_record.split("\n") + pub_line = uid_lines.shift + @keyid, @algo, @keylen_s, @creationdate_s, @expirationdate_s, @flags = pub_line.split(':')[1..-1] + @uids = [] + uid_lines.each do |uid_line| + uid, creationdate, expirationdate, flags = uid_line.split(':')[1..-1] + # for now, ignore the expirationdate and flags of uids. sks does return them anyway + @uids << CGI.unescape(uid.sub(/.*<(.+)>.*/, '\1')) + end + end + + def keylen + @keylen ||= @keylen_s.to_i + end + + def creationdate + @creationdate ||= begin + if @creationdate_s + Time.at(@creationdate_s.to_i) + end + end + end + + def expirationdate + @expirationdate ||= begin + if @expirationdate_s + Time.at(@expirationdate_s.to_i) + end + end + end + + def rsa? + @algo == "1" + end + + def dsa? + @algo == "17" + end + + def revoked? + @flags =~ /r/ + end + + def disabled? + @flags =~ /d/ + end + + def expired? + @flags =~ /e/ + end + end + +end; end |