lib/io_streams/pgp.rb in iostreams-0.12.0 vs lib/io_streams/pgp.rb in iostreams-0.12.1
- old
+ new
@@ -24,11 +24,11 @@
#
# # Decrypt the file sent to `receiver@example.org` using its private key
# # Recipient must also have the senders public key to verify the signature
# IOStreams::Pgp::Reader.open('secure.gpg', passphrase: 'receiver_passphrase') do |stream|
# while !stream.eof?
- # ap stream.read(10)
+ # p stream.read(10)
# puts
# end
# end
#
# Example 2:
@@ -50,11 +50,11 @@
#
# # Decrypt the file sent to `receiver@example.org` using its private key
# # Recipient must also have the senders public key to verify the signature
# IOStreams.reader('secure.gpg') do |stream|
# while data = stream.read(10)
- # ap data
+ # p data
# end
# end
#
# FAQ:
# - If you get not trusted errors
@@ -75,20 +75,34 @@
# Input file: test.log 3.6GB
# :none: size: 3.6GB write: 52s read: 45s
# :zip: size: 411MB write: 75s read: 31s
# :zlib: size: 241MB write: 66s read: 23s ( 756KB Memory )
# :bzip2: size: 129MB write: 430s read: 130s ( 5MB Memory )
+ #
+ # Notes:
+ # - Tested against gnupg v1.4.21 and v2.0.30
+ # - Does not work yet with gnupg v2.1. Pull Requests welcome.
module Pgp
autoload :Reader, 'io_streams/pgp/reader'
autoload :Writer, 'io_streams/pgp/writer'
class Failure < StandardError
end
class UnsupportedVersion < Failure
end
+ def self.executable
+ @executable
+ end
+
+ def self.executable=(executable)
+ @executable = executable
+ end
+
+ @executable = 'gpg'
+
# Generate a new ultimate trusted local public and private key.
#
# Returns [String] the key id for the generated key.
# Raises an exception if it fails to generate the key.
#
@@ -119,11 +133,11 @@
params << "Name-Comment: #{comment}\n" if comment
params << "Name-Email: #{email}\n" if email
params << "Expire-Date: #{expire_date}\n" if expire_date
params << "Passphrase: #{passphrase}\n" if passphrase
params << '%commit'
- out, err, status = Open3.capture3('gpg --batch --gen-key', binmode: true, stdin_data: params)
+ out, err, status = Open3.capture3("#{executable} --batch --gen-key", binmode: true, stdin_data: params)
logger.debug { "IOStreams::Pgp.generate_key output:\n#{out}#{err}" } if logger
if status.success?
if match = err.match(/gpg: key ([0-9A-F]+)\s+/)
return match[1]
end
@@ -147,12 +161,12 @@
# Whether to delete the private key
# Default: false
def self.delete_keys(email:, public: true, private: false)
version_check
cmd = "for i in `gpg --with-colons --fingerprint #{email} | grep \"^fpr\" | cut -d: -f10`; do\n"
- cmd << "gpg --batch --delete-secret-keys \"$i\" ;\n" if private
- cmd << "gpg --batch --delete-keys \"$i\" ;\n" if public
+ cmd << "#{executable} --batch --delete-secret-keys \"$i\" ;\n" if private
+ cmd << "#{executable} --batch --delete-keys \"$i\" ;\n" if public
cmd << 'done'
out, err, status = Open3.capture3(cmd, binmode: true)
logger.debug { "IOStreams::Pgp.delete_keys output:\n#{err}#{out}" } if logger
@@ -182,20 +196,24 @@
# email: [String]
# Returns [] if no keys were found.
def self.list_keys(email: nil, key_id: nil, private: false)
version_check
cmd = private ? '--list-secret-keys' : '--list-keys'
- out, err, status = Open3.capture3("gpg #{cmd} #{email || key_id}", binmode: true)
+ out, err, status = Open3.capture3("#{executable} #{cmd} #{email || key_id}", binmode: true)
logger.debug { "IOStreams::Pgp.list_keys output:\n#{err}#{out}" } if logger
if status.success? && out.length > 0
- # Sample output
+ # v2.0.30 output:
# pub 4096R/3A5456F5 2017-06-07
# uid [ unknown] Joe Bloggs <j@bloggs.net>
# sub 4096R/2C9B240B 2017-06-07
+ # v1.4 output:
+ # sec 2048R/27D2E7FA 2016-10-05
+ # uid Receiver <receiver@example.org>
+ # ssb 2048R/893749EA 2016-10-05
parse_list_output(out)
else
- return [] if err =~ /(key not found|No (public|secret) key)/i
+ return [] if err =~ /(key not found|No (public|secret) key|key not available)/i
raise(Pgp::Failure, "GPG Failed calling gpg to list keys for #{email || key_id}: #{err}#{out}")
end
end
# Extract information from the supplied key.
@@ -210,11 +228,11 @@
# date: [String]
# name: [String]
# email: [String]
def self.key_info(key:)
version_check
- out, err, status = Open3.capture3('gpg', binmode: true, stdin_data: key)
+ out, err, status = Open3.capture3(executable, binmode: true, stdin_data: key)
logger.debug { "IOStreams::Pgp.key_info output:\n#{err}#{out}" } if logger
if status.success? && out.length > 0
# Sample Output:
#
# pub 4096R/3A5456F5 2017-06-07
@@ -237,13 +255,13 @@
# private: [true|false]
# Whether to export the private key
# Default: false
def self.export(email:, ascii: true, private: false)
version_check
- armor = ascii ? ' --armor' : nil
+ armor = ascii ? '--armor' : nil
cmd = private ? '--export-secret-keys' : '--export'
- out, err, status = Open3.capture3("gpg#{armor} #{cmd} #{email}", binmode: true)
+ out, err, status = Open3.capture3("#{executable} #{armor} #{cmd} #{email}", binmode: true)
logger.debug { "IOStreams::Pgp.export output:\n#{err}" } if logger
if status.success? && out.length > 0
out
else
raise(Pgp::Failure, "GPG Failed reading key: #{email}: #{err}")
@@ -265,11 +283,11 @@
# Notes:
# * Importing a new key for the same email address does not remove the prior key if any.
# * Invalidated keys must be removed manually.
def self.import(key:)
version_check
- out, err, status = Open3.capture3('gpg --import', binmode: true, stdin_data: key)
+ out, err, status = Open3.capture3("#{executable} --import", binmode: true, stdin_data: key)
logger.debug { "IOStreams::Pgp.import output:\n#{err}#{out}" } if logger
if status.success? && err.length > 0
# Sample output
#
# gpg: key C16500E3: secret key imported\n"
@@ -314,11 +332,11 @@
version_check
fingerprint = fingerprint(email: email)
return unless fingerprint
trust = "#{fingerprint}:#{level + 1}:\n"
- out, err, status = Open3.capture3('gpg --import-ownertrust', stdin_data: trust)
+ out, err, status = Open3.capture3("#{executable} --import-ownertrust", stdin_data: trust)
logger.debug { "IOStreams::Pgp.set_trust output:\n#{err}#{out}" } if logger
if status.success?
err
else
raise(Pgp::Failure, "GPG Failed trusting key: #{err} #{out}")
@@ -326,11 +344,11 @@
end
# DEPRECATED - Use key_ids instead of fingerprints
def self.fingerprint(email:)
version_check
- Open3.popen2e("gpg --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]
@@ -349,11 +367,11 @@
end
# Returns [String] the version of pgp currently installed
def self.pgp_version
@pgp_version ||= begin
- out, err, status = Open3.capture3("gpg --version")
+ out, err, status = Open3.capture3("#{executable} --version")
logger.debug { "IOStreams::Pgp.version output:\n#{err}#{out}" } if logger
if status.success?
# Sample output
# gpg (GnuPG) 2.0.30
# libgcrypt 1.7.6
@@ -393,30 +411,36 @@
def self.parse_list_output(out)
results = []
hash = {}
out.each_line do |line|
- if match = line.match(/(pub|sec)\s+(\d+)(.*)\/(\w+)\s+(\S+)/)
+ if match = line.match(/(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,
key_type: match[3],
key_id: match[4],
date: (Date.parse(match[5].to_s) rescue match[5])
}
- elsif match = line.match(/uid\s+(.+)<(.+)>/)
- name = match[1].strip
- hash[:email] = match[2].strip
- if match = name.match(/(\[(.+)\])?(.+)/)
- trust = match[2].to_s.strip
- hash[:trust] = trust unless trust.empty?
- hash[:name] = match[3].to_s.strip
- else
- hash[:name] = name
+ # Prior to gpg v2.0.30
+ if match[7]
+ hash[:name] = match[7].strip
+ hash[:email] = match[8].strip
+ results << hash
+ hash = {}
end
+ elsif match = line.match(/uid\s+(\[(.+)\]\s+)?(.+)<(.+)>/)
+ # Matches: uid [ unknown] Joe Bloggs <j@bloggs.net>
+ # Or: uid Joe Bloggs <j@bloggs.net>
+ hash[:email] = match[4].strip
+ hash[:name] = match[3].to_s.strip
+ hash[:trust] = match[2].to_s.strip if match[1]
results << hash
hash = {}
end
+
end
results
end
end