module SimpleHKP::Keys

Public Instance Methods

indexKeys(queryString) click to toggle source
# File lib/simpleHKP/keys.rb, line 184
def indexKeys(queryString)
  return replyBadRequest("No search field in queryString") unless
    queryString.has_key?('search')
  #
  # normalize the search string
  #
  searchString = queryString['search']
  searchString.gsub!(/0x/,'')
  searchRegexp = Regexp.new(searchString,
                            Regexp::IGNORECASE | Regexp::MULTILINE)
  puts searchRegexp if @debug
  #
  # accumulate the keyIDs of any keys that match the serach
  #
  keys = Array.new
  @keyLookupData.each_pair do | key, keyData |
    next unless keyData['colonData'] =~ searchRegexp
    puts "FOUND #{key} (#{keyData})" if @debug
    keys.push(key)
  end

  if queryString.has_key?('options') &&
    queryString['options'] == 'mr' then
    #
    # return a machine readable list of the keys found
    #
    @header = { 'Content-Type' => 'text/plain' }
    @body << "info:1:#{keys.size}\n"
    keys.each do | aKey |
      next unless @keyLookupData.has_key?(aKey)
      keyData   = @keyLookupData[aKey]
      pubData = [ "pub" ]
      pubData << aKey
      pubData << keyData['algorithm']
      pubData << keyData['len']
      pubData << keyData['created']
      pubData << keyData['expires']
      pubData << keyData['flags']
      @body << pubData.join(':')
      @body << "\n"
      uidData = [ "uid" ]
      uidData << keyData['userID'].gsub(/:/, '\x3a')
      uidData << keyData['created']
      uidData << keyData['expires']
      uidData << keyData['flags']
      @body << uidData.join(':')
      @body << "\n"
    end
  else
    #
    # return a (simple) human readable list of the keys found
    #
    @body << @headerHtml
    @body << @lookupKeysForm
    @body << '<h1 class="simpleHKP-searchString">Keys matching: ['+searchString+']</h1><ul class="simpleHKP-searchList">'
    keys.each do | aKey |
      next unless @keyLookupData.has_key?(aKey)
      keyData   = @keyLookupData[aKey]
      keyStr = "  <li class=\"simpleHKP-searchItem\"><a href=\"lookup?op=get&search=#{aKey}\">#{aKey}:&nbsp;"
      keyStr << keyData['userID']
      keyStr <<  "</a></li>\n"
      @body << keyStr
    end
    @body << '</ul>'
    @body << @footer
  end
  pp @body if @debug
end
loadKeys() click to toggle source
# File lib/simpleHKP/keys.rb, line 15
def loadKeys
  @keyLookupData = Hash.new
  puts "SimpleHKP: loading keys" if @debug
  gpg2cmd = "gpg2 --homedir #{@keyDir} --with-fingerprint --with-colons --list-sigs"
  puts gpg2cmd if @debug
  lastKey = Hash.new
  gpgData = ""
  IO.popen(gpg2cmd, 'r') do | gpgPipe |
    gpgData = gpgPipe.read
  end
  pp gpgData if @debug
  gpgData.each_line do | aLine |
    next if aLine =~ /^tru/
    if aLine =~ /^pub/ then
      saveLastKey(lastKey)
      lastKey = Hash.new
      keyData = aLine.split(/:/)
      lastKey['userID']    = keyData[9].gsub(/\\x3a/,':') # user ID
      lastKey['keyID']     = keyData[4] #  key ID
      lastKey['algorithm'] = keyData[3] #  key type (algorithm)
      lastKey['len']       = keyData[2] #  key length
      lastKey['created']   = keyData[5] #  creation date
      lastKey['expires']   = keyData[6] #  expiration date
      lastKey['flags']     = keyData[1] #  flags
      lastKey['colonData'] = aLine
      next
    end
    lastKey['userID'] = aLine.split(/:/)[9].gsub(/\\x3a/,':') if
      aLine =~ /^uid/ && lastKey['userID'].empty?
    lastKey['colonData'] << aLine
  end
  saveLastKey(lastKey)
  puts "finished initial loading of keys" if @debug
  pp @keyLookupData if @debug
  @keyLookupData.each_pair do | key, value |
    puts "looking up human data for #{key}"
    pp value
    gpg2Cmd = "gpg2 --homedir #{@keyDir} --list-sig #{key}"
    puts gpg2Cmd if @debug
    IO.popen(gpg2Cmd) do | gpgPipe |
      value['humanData'] = gpgPipe.read
    end
  end
  pp @keyLookupData if @debug
  puts "SimpleHKP: finished loading keys" if @debug
end
lookUpKey(queryString) click to toggle source
# File lib/simpleHKP/keys.rb, line 129
def lookUpKey(queryString)
  return replyBadRequest("No search field in queryString") unless
    queryString.has_key?('search')
  #
  # normalize the search string
  #
  searchString = queryString['search']
  searchString.gsub!(/0x/,'')
  searchRegexp = Regexp.new(searchString,
                            Regexp::IGNORECASE | Regexp::MULTILINE)
  puts searchRegexp if @debug
  #
  # (linearly) look through the hash of known keys
  # looking for the FIRST match
  #
  keyID = nil
  @keyLookupData.each_pair do | key, keyData |
    next unless keyData['colonData'] =~ searchRegexp
    puts "FOUND #{key} (#{keyData})" if @debug
    keyID = key
    break
  end
  return replyNotFound if keyID.nil?

  if queryString.has_key?('options') &&
    queryString['options'] == 'mr' then
    #
    # return the key data in machine readable format
    #
    @header = {
      'Content-Type' => 'application/pgp-keys; charset=utf-8',
      'Content-Disposition' =>
        'attachment; filename=' + keyID
    }
    puts @header if @debug
    @body << `gpg2 --homedir #{@keyDir} --armor --export #{keyID}`
  else
    #
    # return the key data for a human to read
    #
    @body << @headerHtml
    @body << @lookupKeysForm
    @body << '<h1 class="simpleHKP-keyAsckeyId">Key: '+keyID+'</h1>'
    @body << '<h2 class="simpleHKP-keyDataTitle">Key data:</h2>'
    @body << '<pre class="simpleHKP-keyData">'
    @body << @keyLookupData[keyID]['humanData']
    @body << '</pre>'
    @body << '<h2 class="simpleHKP-keyAscTitle">Key contents:</h2>'
    @body << '<pre class="simpleHKP-keyAsc">'
    @body << `gpg2 --homedir #{@keyDir} --armor --export #{keyID}`
    @body << '</pre>'
    @body << @footer
  end
end
saveLastKey(lastKey) click to toggle source
# File lib/simpleHKP/keys.rb, line 6
def saveLastKey(lastKey)
  puts "saving last key" if @debug
  lastKey['colonData'].gsub!(/\\x3a/,':') if 
    lastKey.has_key?('colonData')
  pp lastKey if @debug
  @keyLookupData[lastKey['keyID']] = lastKey unless 
    lastKey.empty? || lastKey['keyID'].empty?
end
storeKey(env) click to toggle source
# File lib/simpleHKP/keys.rb, line 62
def storeKey(env)
  #
  # decode the post data
  #
  keys = URI.decode_www_form(env['rack.input'].read)
  #
  # ensure we are being sent a key
  #
  return replyBadRequest("No keytext field in post data") unless
    keys[0][0] =~ /keytext/
  pp keys[0][1] if @debug
  #
  # send the key data through gpg2 to import it
  #
  gpgStdErr  = ""
  Open3.popen3("gpg2 --homedir #{@keyDir} --import") do | stdin, stdout, stderr, wait_thread |
    stdin.write(keys[0][1])
    stdin.close
    gpgStdErr  = stderr.read
  end
  #
  # check to see if the key has already been imported and/or changed
  #
  puts "[[#{gpgStdErr}]]" if @debug
  keyChanged = "changed"
  if gpgStdErr =~ /not\s+changed/ then
    keyChanged = "unchanged"
  else
    # something has changed to re-load the keys
    loadKeys
  end
  #
  # send the key data through gpg2 (again ;-( to extract the keyID
  #
  gpgKeyData = ""
  gpg2Cmd = "gpg2 --homedir #{@keyDir} --with-fingerprint --with-colons -"
  puts gpg2Cmd if @debug
  IO.popen(gpg2Cmd, 'r+') do | pipe |
    pipe.write(keys[0][1])
    pipe.close_write
    gpgKeyData = pipe.read
  end
  puts gpgKeyData if @debug
  #
  # look for the keyID of the public key
  #
  keyID = nil
  gpgKeyData.each_line do | aLine |
    keyData = aLine.split(/:/)
    next unless keyData[0] =~ /pub/
    keyID = keyData[4]
    break
  end
  return replyBadRequest("No keyID found in gpg2 result using uploaded keytext data") if keyID.nil?
  #
  # return OK
  #
  @statusCode = 200
  @body << @headerHtml
  @body << "<h1 class=\"simpleHKP-storedKey\">Stored key: [#{keyID}]</h1>"
  @body << "<p>key #{keyChanged}</p>"
  @body << "<h2 class=\"simpleHKP-keyDataKeyID\">Key data for key: [#{keyID}]</h2>"
  @body << '<pre class="simpleHKP-keyData">'+@keyLookupData[keyID]['colonData']+"</pre>\n"
  @body << '<pre class="simpleHKP-keyData">'+@keyLookupData[keyID]['humanData']+"</pre>\n"
  @body << @footer
end