class Mail::Gpg::GpgmeHelper

Public Class Methods

decrypt(cipher, options = {}) click to toggle source
# File lib/mail/gpg/gpgme_helper.rb, line 47
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
encrypt(plain, options = {}) click to toggle source
# File lib/mail/gpg/gpgme_helper.rb, line 8
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
inline_verify(signed_text, options = {}) click to toggle source
# File lib/mail/gpg/gpgme_helper.rb, line 100
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
sign(plain, options = {}) click to toggle source
# File lib/mail/gpg/gpgme_helper.rb, line 72
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
sign_verify(plain, signature, options = {}) click to toggle source

returns [success(bool), VerifyResult(from gpgme)] success will be true when there is at least one sig and no invalid sig

# File lib/mail/gpg/gpgme_helper.rb, line 84
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

Private Class Methods

keys_for_data(emails_or_shas_or_keys, key_data = nil) click to toggle source

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.

# File lib/mail/gpg/gpgme_helper.rb, line 121
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