Class: Mail::Gpg::GpgmeHelper

Inherits:
Object
  • Object
show all
Defined in:
lib/mail/gpg/gpgme_helper.rb

Class Method Summary collapse

Class Method Details

.decrypt(cipher, options = {}) ⇒ Object



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# 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 = {}) ⇒ Object



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
# 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 = {}) ⇒ Object



100
101
102
103
104
105
106
107
108
109
110
111
112
# 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

.keys_for_data(emails_or_shas_or_keys, key_data = nil) ⇒ Object

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.



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
# 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

.sign(plain, options = {}) ⇒ Object



72
73
74
75
76
77
78
79
80
# 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 = {}) ⇒ Object

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



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# 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