1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
|
require 'mail/gpg/gpgme_ext'
# GPGME methods for encryption/decryption/signing
module Mail
module Gpg
class GpgmeHelper
def self.encrypt(plain, options = {})
options = options.merge({armor: true})
plain_data = GPGME::Data.new(plain)
cipher_data = GPGME::Data.new(options[:output])
recipient_keys = keys_for_data options[:recipients], options.delete(:keys)
if recipient_keys.empty?
raise MissingKeysError.new('No keys to encrypt to!')
end
flags = 0
flags |= GPGME::ENCRYPT_ALWAYS_TRUST if options[:always_trust]
GPGME::Ctx.new(options) do |ctx|
begin
if options[:sign]
if options[:signers] && options[:signers].size > 0
signers = GPGME::Key.find(:secret, options[:signers], :sign)
ctx.add_signer(*signers)
end
ctx.encrypt_sign(recipient_keys, plain_data, cipher_data, flags)
else
ctx.encrypt(recipient_keys, plain_data, cipher_data, flags)
end
rescue GPGME::Error::UnusablePublicKey => exc
exc.keys = ctx.encrypt_result.invalid_recipients
raise exc
rescue GPGME::Error::UnusableSecretKey => exc
exc.keys = ctx.sign_result.invalid_signers
raise exc
end
end
cipher_data.seek(0)
cipher_data
end
def self.decrypt(cipher, options = {})
cipher_data = GPGME::Data.new(cipher)
plain_data = GPGME::Data.new(options[:output])
GPGME::Ctx.new(options) do |ctx|
begin
if options[:verify]
ctx.decrypt_verify(cipher_data, plain_data)
plain_data.verify_result = ctx.verify_result
else
ctx.decrypt(cipher_data, plain_data)
end
rescue GPGME::Error::UnsupportedAlgorithm => exc
exc.algorithm = ctx.decrypt_result.unsupported_algorithm
raise exc
rescue GPGME::Error::WrongKeyUsage => exc
exc.key_usage = ctx.decrypt_result.wrong_key_usage
raise exc
end
end
plain_data.seek(0)
plain_data
end
def self.sign(plain, options = {})
options.merge!({
armor: true,
signer: options.delete(:sign_as),
mode: GPGME::SIG_MODE_DETACH
})
crypto = GPGME::Crypto.new
crypto.sign GPGME::Data.new(plain), options
end
# returns [success(bool), VerifyResult(from gpgme)]
# success will be true when there is at least one sig and no invalid sig
def self.sign_verify(plain, signature, options = {})
signed_data = GPGME::Data.new(plain)
signature = GPGME::Data.new(signature)
success = verify_result = nil
GPGME::Ctx.new(options) do |ctx|
ctx.verify signature, signed_data, nil
verify_result = ctx.verify_result
signatures = verify_result.signatures
success = signatures &&
signatures.size > 0 &&
signatures.detect{|s| !s.valid? }.nil?
end
return [success, verify_result]
end
def self.inline_verify(signed_text, options = {})
signed_data = GPGME::Data.new(signed_text)
success = verify_result = nil
GPGME::Ctx.new(options) do |ctx|
ctx.verify signed_data, nil
verify_result = ctx.verify_result
signatures = verify_result.signatures
success = signatures &&
signatures.size > 0 &&
signatures.detect{|s| !s.valid? }.nil?
end
return [success, verify_result]
end
private
# normalizes the list of recipients' emails, key ids and key data to a
# list of Key objects
#
# if key_data is given, _only_ key material from there is used,
# and eventually already imported keys in the keychain are ignored.
def self.keys_for_data(emails_or_shas_or_keys, key_data = nil)
if key_data
# in this case, emails_or_shas_or_keys is supposed to be the list of
# recipients, and key_data the key material to be used.
# We now map these to whatever we find in key_data for each of these
# addresses.
[emails_or_shas_or_keys].flatten.map do |r|
k = key_data[r]
key_id = case k
when GPGME::Key
# assuming this is already imported
k.fingerprint
when nil, ''
# nothing
nil
when /-----BEGIN PGP/
# ASCII key data
GPGME::Key.import(k).imports.map(&:fpr)
else
# key id or fingerprint
k
end
unless key_id.nil? || key_id.empty?
GPGME::Key.find(:public, key_id, :encrypt)
end
end.flatten.compact
elsif emails_or_shas_or_keys and emails_or_shas_or_keys.size > 0
# key lookup in keychain for all receivers
GPGME::Key.find :public, emails_or_shas_or_keys, :encrypt
else
# empty array given
[]
end
end
end
end
end
|