lib/io_streams/pgp.rb in iostreams-0.20.3 vs lib/io_streams/pgp.rb in iostreams-1.0.0.beta

- old
+ new

@@ -214,11 +214,11 @@ # date: [String] # name: [String] # email: [String] def self.key_info(key:) version_check - command = "#{executable}" + command = executable.to_s out, err, status = Open3.capture3(command, binmode: true, stdin_data: key) logger.debug { "IOStreams::Pgp.key_info: #{command}\n#{err}#{out}" } if logger if status.success? && out.length > 0 # Sample Output: @@ -273,12 +273,12 @@ def self.import(key:) version_check command = "#{executable} --import" out, err, status = Open3.capture3(command, binmode: true, stdin_data: key) - logger.debug { "IOStreams::Pgp.import: #{command}\n#{err}#{out}" } if logger - if status.success? && err.length > 0 + logger&.debug { "IOStreams::Pgp.import: #{command}\n#{err}#{out}" } + if status.success? && !err.empty? # Sample output # # gpg: key C16500E3: secret key imported\n" # gpg: key C16500E3: public key "Joe Bloggs <pgp_test@iostreams.net>" imported # gpg: Total number processed: 1 @@ -304,14 +304,30 @@ end end results else return [] if err =~ /already in secret keyring/ + raise(Pgp::Failure, "GPG Failed importing key: #{err}#{out}") end end + # Returns [String] email for the supplied after importing and trusting the key + # + # Notes: + # - If the same email address has multiple keys then only the first is currently trusted. + def self.import_and_trust(key:) + raise(ArgumentError, "Key cannot be empty") if key.nil? || (key == '') + + email = key_info(key: key).first.fetch(:email) + raise(ArgumentError, "Recipient email cannot be extracted from supplied key") unless email + + import(key: key) + set_trust(email: email) + email + end + # Set the trust level for an existing key. # # Returns [String] output if the trust was successfully updated # Returns nil if the email was not found # @@ -323,32 +339,33 @@ return unless fingerprint command = "#{executable} --import-ownertrust" trust = "#{fingerprint}:#{level + 1}:\n" out, err, status = Open3.capture3(command, stdin_data: trust) - logger.debug { "IOStreams::Pgp.set_trust: #{command}\n#{err}#{out}" } if logger + logger&.debug { "IOStreams::Pgp.set_trust: #{command}\n#{err}#{out}" } if status.success? err else raise(Pgp::Failure, "GPG Failed trusting key: #{err} #{out}") end end # DEPRECATED - Use key_ids instead of fingerprints def self.fingerprint(email:) version_check - Open3.popen2e("#{executable} --list-keys --fingerprint --with-colons #{email}") do |stdin, out, waith_thr| + Open3.popen2e("#{executable} --list-keys --fingerprint --with-colons #{email}") do |_stdin, out, waith_thr| output = out.read.chomp if waith_thr.value.success? output.each_line do |line| if match = line.match(/\Afpr.*::([^\:]*):\Z/) return match[1] end end nil else return if output =~ /(public key not found|No public key)/i + raise(Pgp::Failure, "GPG Failed calling #{executable} to list keys for #{email}: #{output}") end end end @@ -359,11 +376,11 @@ # Returns [String] the version of pgp currently installed def self.pgp_version @pgp_version ||= begin command = "#{executable} --version" out, err, status = Open3.capture3(command) - logger.debug { "IOStreams::Pgp.version: #{command}\n#{err}#{out}" } if logger + logger&.debug { "IOStreams::Pgp.version: #{command}\n#{err}#{out}" } if status.success? # Sample output # #{executable} (GnuPG) 2.0.30 # libgcrypt 1.7.6 # Copyright (C) 2015 Free Software Foundation, Inc. @@ -381,10 +398,11 @@ if match = out.lines.first.match(/(\d+\.\d+.\d+)/) match[1] end else return [] if err =~ /(key not found|No (public|secret) key)/i + raise(Pgp::Failure, "GPG Failed calling #{executable} to list keys for #{email || key_id}: #{err}#{out}") end end end @@ -395,11 +413,13 @@ def self.logger @logger end def self.version_check - raise(Pgp::UnsupportedVersion, "Version #{pgp_version} of #{executable} is not yet supported. You are welcome to submit a Pull Request.") if pgp_version.to_f >= 2.3 + if pgp_version.to_f >= 2.3 + raise(Pgp::UnsupportedVersion, "Version #{pgp_version} of #{executable} is not yet supported. You are welcome to submit a Pull Request.") + end end # v2.2.1 output: # pub rsa1024 2017-10-24 [SCEA] # 18A0FC1C09C0D8AE34CE659257DC4AE323C7368C @@ -423,11 +443,11 @@ private: match[1] == 'sec', key_length: match[3].to_s.to_i, key_type: match[2], date: (Date.parse(match[4].to_s) rescue match[4]) } - elsif match = line.match(/(pub|sec)\s+(\d+)(.*)\/(\w+)\s+(\d+-\d+-\d+)(\s+(.+)<(.+)>)?/) + elsif match = line.match(%r{(pub|sec)\s+(\d+)(.*)/(\w+)\s+(\d+-\d+-\d+)(\s+(.+)<(.+)>)?}) # Matches: pub 2048R/C7F9D9CB 2016-10-26 # Or: pub 2048R/C7F9D9CB 2016-10-26 Receiver <receiver@example.org> hash = { private: match[1] == 'sec', key_length: match[2].to_s.to_i, @@ -453,11 +473,10 @@ hash = {} elsif match = line.match(/([A-Z0-9]+)/) # v2.2 18A0FC1C09C0D8AE34CE659257DC4AE323C7368C hash[:key_id] ||= match[1] end - end results end def self.delete_public_or_private_keys(email:, private: false) @@ -465,18 +484,23 @@ list = list_keys(email: email, private: private) return false if list.empty? list.each do |key_info| - if key_id = key_info[:key_id] - command = "#{executable} --batch --no-tty --yes --delete-#{keys} #{key_id}" - out, err, status = Open3.capture3(command, binmode: true) - logger.debug { "IOStreams::Pgp.delete_keys: #{command}\n#{err}#{out}" } if logger + key_id = key_info[:key_id] + next unless key_id - raise(Pgp::Failure, "GPG Failed calling #{executable} to delete #{keys} for #{email}: #{err}: #{out}") unless status.success? - raise(Pgp::Failure, "GPG Failed to delete #{keys} for #{email} #{err.strip}:#{out}") if out.include?('error') + command = "#{executable} --batch --no-tty --yes --delete-#{keys} #{key_id}" + out, err, status = Open3.capture3(command, binmode: true) + logger&.debug { "IOStreams::Pgp.delete_keys: #{command}\n#{err}#{out}" } + + unless status.success? + raise(Pgp::Failure, "GPG Failed calling #{executable} to delete #{keys} for #{email}: #{err}: #{out}") end + if out.include?('error') + raise(Pgp::Failure, "GPG Failed to delete #{keys} for #{email} #{err.strip}:#{out}") + end end true end def self.delete_public_or_private_keys_v1(email:, private: false) @@ -485,15 +509,19 @@ command = "for i in `#{executable} --list-#{keys} --with-colons --fingerprint #{email} | grep \"^fpr\" | cut -d: -f10`; do\n" command << "#{executable} --batch --no-tty --yes --delete-#{keys} \"$i\" ;\n" command << 'done' out, err, status = Open3.capture3(command, binmode: true) - logger.debug { "IOStreams::Pgp.delete_keys: #{command}\n#{err}: #{out}" } if logger + logger&.debug { "IOStreams::Pgp.delete_keys: #{command}\n#{err}: #{out}" } return false if err =~ /(not found|no public key)/i - raise(Pgp::Failure, "GPG Failed calling #{executable} to delete #{keys} for #{email}: #{err}: #{out}") unless status.success? - raise(Pgp::Failure, "GPG Failed to delete #{keys} for #{email} #{err.strip}: #{out}") if out.include?('error') + unless status.success? + raise(Pgp::Failure, "GPG Failed calling #{executable} to delete #{keys} for #{email}: #{err}: #{out}") + end + if out.include?('error') + raise(Pgp::Failure, "GPG Failed to delete #{keys} for #{email} #{err.strip}: #{out}") + end + true end - end end