mail-gpg 0.3.1 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/.gitignore +1 -1
- data/.travis.yml +14 -11
- data/History.txt +34 -0
- data/README.md +61 -46
- data/Rakefile +3 -46
- data/lib/mail/gpg.rb +8 -5
- data/lib/mail/gpg/delivery_handler.rb +1 -1
- data/lib/mail/gpg/encrypted_part.rb +4 -1
- data/lib/mail/gpg/gpgme_helper.rb +29 -7
- data/lib/mail/gpg/inline_decrypted_message.rb +22 -0
- data/lib/mail/gpg/mime_signed_message.rb +1 -3
- data/lib/mail/gpg/sign_part.rb +1 -1
- data/lib/mail/gpg/version.rb +1 -1
- data/mail-gpg.gemspec +1 -1
- data/test/action_mailer_test.rb +1 -1
- data/test/decrypted_part_test.rb +4 -2
- data/test/encrypted_part_test.rb +8 -55
- data/test/gpg_test.rb +1 -1
- data/test/gpgme_helper_test.rb +160 -0
- data/test/hkp_test.rb +1 -1
- data/test/inline_decrypted_message_test.rb +20 -1
- data/test/inline_signed_message_test.rb +1 -1
- data/test/message_test.rb +5 -3
- data/test/sign_part_test.rb +1 -1
- data/test/test_helper.rb +128 -36
- data/test/version_part_test.rb +5 -5
- metadata +7 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: b7e6ef944bc641fa51f189b379859528044939a7eeca16d44cdeab8f46fdbc0c
|
4
|
+
data.tar.gz: aaa67237e62a803b4d369f81a70d71511e55b175c4ba95714137d6a0aa649cfe
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3c5224711ba09fe1b59cfb6076fe4c8c09838e0915411411fe044b5b04d76949bb74eb81e8e9a0242c8996a5be6c380c43d2662942fe58714c1d838c71ecf756
|
7
|
+
data.tar.gz: 25e41d97a17fc966d4839f96b7c827432c1e7f60905d9675238633660efa5a930c4ff9028c91c84ab8db66f0884601fb0ff5fc8b760c194ceae37fab261f09e8
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
@@ -1,18 +1,21 @@
|
|
1
1
|
language: ruby
|
2
2
|
rvm:
|
3
|
-
- 2.3
|
4
|
-
- 2.
|
5
|
-
- 2.
|
3
|
+
- 2.6.3
|
4
|
+
- 2.5.5
|
5
|
+
- 2.4.6
|
6
6
|
env:
|
7
|
-
- RAILS=
|
8
|
-
- RAILS=4.
|
9
|
-
- RAILS=
|
10
|
-
- RAILS=5.
|
7
|
+
- RAILS=4.2.11.1 GPG_BIN=/usr/bin/gpg2
|
8
|
+
- RAILS=4.2.11.1 GPG_BIN=/usr/bin/gpg
|
9
|
+
- RAILS=5.2.3 GPG_BIN=/usr/bin/gpg2
|
10
|
+
- RAILS=5.2.3 GPG_BIN=/usr/bin/gpg
|
11
11
|
matrix:
|
12
12
|
exclude:
|
13
|
-
- rvm: 2.1.9
|
14
|
-
env: RAILS=5.0.0.1
|
15
13
|
before_install:
|
16
|
-
- gem
|
17
|
-
sudo
|
14
|
+
- gem install bundler -v "~> 2.0"
|
15
|
+
- sudo apt install -y gnupg gnupg2
|
18
16
|
cache: bundler
|
17
|
+
dist: xenial
|
18
|
+
addons:
|
19
|
+
apt:
|
20
|
+
update: true
|
21
|
+
|
data/History.txt
CHANGED
@@ -1,3 +1,37 @@
|
|
1
|
+
== 0.4.2 2019-09-02
|
2
|
+
|
3
|
+
* do not die on invalid content-transfer encodings when checking if a message
|
4
|
+
is inline-signed or encrypted
|
5
|
+
|
6
|
+
== 0.4.1 2019-07-08
|
7
|
+
|
8
|
+
* do not modify argument hash #61
|
9
|
+
* fix tests on travis and run them with both gpg < 2.0 and >= 2.1
|
10
|
+
* gpg 2.0.x apparently has no way of preseeding passphrases and thus will only
|
11
|
+
ever work with passphraseless keys.
|
12
|
+
|
13
|
+
== 0.4.0 2018-05-19
|
14
|
+
|
15
|
+
* [MIGHT BREAK THINGS] changes to the way keys are looked up #55
|
16
|
+
Previously, keys that were not explicitly mentioned but already present in
|
17
|
+
the key chain for one of the recipient addresses would have been used
|
18
|
+
silently. This is no longer the case, if the :keys option is given, all
|
19
|
+
necessary keys have to be specified as either key data, key id, fingerprint
|
20
|
+
or GPGME::Key object.
|
21
|
+
* fix error when calling encrypt with actual key objects #60
|
22
|
+
|
23
|
+
== 0.3.3 2018-04-01
|
24
|
+
|
25
|
+
* fix broken GpgmeHelper#keys_for_data #59
|
26
|
+
|
27
|
+
== 0.3.2 2018-03-30
|
28
|
+
|
29
|
+
* do not attempt to decrypt inline-encrypted HTML parts #52
|
30
|
+
* fixes possible double decoding of quoted printable bodies #57
|
31
|
+
* fixes a bug which occured in some environments that led to encryption with
|
32
|
+
all available public keys (#31, #55)
|
33
|
+
|
34
|
+
|
1
35
|
== 0.3.1 2017-04-13
|
2
36
|
|
3
37
|
* fixes a bug with signature verification that only surfaced in environments
|
data/README.md
CHANGED
@@ -14,7 +14,9 @@ wild as described in this [Know your PGP implementation](http://binblog.info/200
|
|
14
14
|
|
15
15
|
Add this line to your application's Gemfile:
|
16
16
|
|
17
|
-
|
17
|
+
```ruby
|
18
|
+
gem 'mail-gpg'
|
19
|
+
```
|
18
20
|
|
19
21
|
And then execute:
|
20
22
|
|
@@ -31,54 +33,64 @@ Or install it yourself as:
|
|
31
33
|
Construct your Mail object as usual and specify you want it to be encrypted
|
32
34
|
with the gpg method:
|
33
35
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
# encrypt message, no signing
|
42
|
-
gpg encrypt: true
|
36
|
+
```ruby
|
37
|
+
Mail.new do
|
38
|
+
to 'jane@doe.net'
|
39
|
+
from 'john@doe.net'
|
40
|
+
subject 'gpg test'
|
41
|
+
body "encrypt me!"
|
42
|
+
add_file "some_attachment.zip"
|
43
43
|
|
44
|
-
|
45
|
-
|
46
|
-
gpg encrypt: true, sign: true, password: 'secret'
|
44
|
+
# encrypt message, no signing
|
45
|
+
gpg encrypt: true
|
47
46
|
|
48
|
-
|
49
|
-
|
47
|
+
# encrypt and sign message with sender's private key, using the given
|
48
|
+
# passphrase to decrypt the key
|
49
|
+
gpg encrypt: true, sign: true, password: 'secret'
|
50
50
|
|
51
|
+
# encrypt and sign message using a different key
|
52
|
+
gpg encrypt: true, sign_as: 'joe@otherdomain.com', password: 'secret'
|
51
53
|
|
52
|
-
# encrypt and sign message and use a callback function to provide the
|
53
|
-
# passphrase.
|
54
|
-
gpg encrypt: true, sign_as: 'joe@otherdomain.com',
|
55
|
-
passphrase_callback: ->(obj, uid_hint, passphrase_info, prev_was_bad, fd){puts "Enter passphrase for #{passphrase_info}: "; (IO.for_fd(fd, 'w') << readline.chomp).flush }
|
56
|
-
end.deliver
|
57
54
|
|
55
|
+
# encrypt and sign message and use a callback function to provide the
|
56
|
+
# passphrase.
|
57
|
+
gpg encrypt: true, sign_as: 'joe@otherdomain.com',
|
58
|
+
passphrase_callback: ->(obj, uid_hint, passphrase_info, prev_was_bad, fd){puts "Enter passphrase for #{passphrase_info}: "; (IO.for_fd(fd, 'w') << readline.chomp).flush }
|
59
|
+
end.deliver
|
60
|
+
```
|
58
61
|
|
59
62
|
Make sure all recipients' public keys are present in your local gpg keychain.
|
60
63
|
You will get errors in case encryption is not possible due to missing keys.
|
61
64
|
If you collect public key data from your users, you can specify the ascii
|
62
65
|
armored key data for recipients using the `:keys` option like this:
|
63
66
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
+
```ruby
|
68
|
+
johns_key = <<-END
|
69
|
+
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
70
|
+
Version: GnuPG v1.4.12 (GNU/Linux)
|
67
71
|
|
68
|
-
|
69
|
-
|
70
|
-
|
72
|
+
mQGiBEk39msRBADw1ExmrLD1OUMdfvA7cnVVYTC7CyqfNvHUVuuBDhV7azs
|
73
|
+
....
|
74
|
+
END
|
71
75
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
+
Mail.new do
|
77
|
+
to 'john@foo.bar'
|
78
|
+
gpg encrypt: true, keys: { 'john@foo.bar' => johns_key }
|
79
|
+
end
|
80
|
+
```
|
76
81
|
|
77
82
|
The key will then be imported before actually trying to encrypt/send the mail.
|
78
83
|
In theory you only need to specify the key once like that, however doing it
|
79
84
|
every time does not hurt as gpg is clever enough to recognize known keys, only
|
80
85
|
updating it's db when necessary.
|
81
86
|
|
87
|
+
Note: Mail-Gpg in version 0.4 and up is more strict regarding the keys option:
|
88
|
+
if it is present, only key material from there (either given as key data like
|
89
|
+
above, or as key id, key fingerprint or `GPGMe::Key` object if they have been
|
90
|
+
imported before) will be used. Keys already present in the local keychain for
|
91
|
+
any of the recipients that are not explicitly mentioned in the `keys` hash will
|
92
|
+
be ignored.
|
93
|
+
|
82
94
|
You may also want to have a look at the [GPGME](https://github.com/ueno/ruby-gpgme) docs and code base for more info on the various options, especially regarding the `passphrase_callback` arguments.
|
83
95
|
|
84
96
|
|
@@ -101,16 +113,18 @@ Set the `:verify` option to `true` when calling `decrypt` to decrypt *and* verif
|
|
101
113
|
A `GPGME::Error::BadPassphrase` will be raised if the password for the private key is incorrect.
|
102
114
|
A `EncodingError` will be raised if the encrypted mails is not encoded correctly as a [RFC 3156](http://www.ietf.org/rfc/rfc3156.txt) message.
|
103
115
|
|
116
|
+
Please note that in case of a multipart/alternative-message where both parts are inline-encrypted, the HTML-part will be dropped during decryption. Handling the HTML-part would require parsing HTML and guessing about the decrypted contents, which is brittle and out of the scope of this library. If you need the HTML-part of multipart/alternative-messages, use pgp/mime-encryption.
|
104
117
|
|
105
118
|
### Signing only
|
106
119
|
|
107
120
|
Just leave the `:encrypt` option out or pass `encrypt: false`, i.e.
|
108
121
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
122
|
+
```ruby
|
123
|
+
Mail.new do
|
124
|
+
to 'jane@doe.net'
|
125
|
+
gpg sign: true
|
126
|
+
end.deliver
|
127
|
+
```
|
114
128
|
|
115
129
|
### Verify signature(s)
|
116
130
|
|
@@ -147,14 +161,14 @@ signature with the key id of the expected sender.
|
|
147
161
|
The Hkp class can be used to lookup and import public keys from public key servers.
|
148
162
|
You can specify the keyserver url when initializing the class:
|
149
163
|
|
150
|
-
```
|
164
|
+
```ruby
|
151
165
|
hkp = Hkp.new("hkp://my-key-server.de")
|
152
166
|
```
|
153
167
|
|
154
168
|
Or, if you want to override how ssl certificates should be treated in case of
|
155
169
|
TLS-secured keyservers (the default is `VERIFY_PEER`):
|
156
170
|
|
157
|
-
```
|
171
|
+
```ruby
|
158
172
|
hkp = Hkp.new(keyserver: "hkps://another.key-server.com",
|
159
173
|
ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE)
|
160
174
|
```
|
@@ -170,13 +184,13 @@ parsing the `gpg.conf` file). As a last resort, the server-pool at
|
|
170
184
|
|
171
185
|
Lookup key ids by searching the keyserver for an email address
|
172
186
|
|
173
|
-
```
|
187
|
+
```ruby
|
174
188
|
hkp.search('jane@doe.net')
|
175
189
|
```
|
176
190
|
|
177
191
|
You can lookup (and import) a specific key by its id:
|
178
192
|
|
179
|
-
```
|
193
|
+
```ruby
|
180
194
|
key = hkp.fetch(id)
|
181
195
|
GPGME::Key.import(key)
|
182
196
|
|
@@ -186,12 +200,14 @@ hkp.fetch_and_import(id)
|
|
186
200
|
|
187
201
|
## Rails / ActionMailer integration
|
188
202
|
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
203
|
+
```ruby
|
204
|
+
class MyMailer < ActionMailer::Base
|
205
|
+
default from: 'baz@bar.com'
|
206
|
+
def some_mail
|
207
|
+
mail to: 'foo@bar.com', subject: 'subject!', gpg: { encrypt: true }
|
208
|
+
end
|
209
|
+
end
|
210
|
+
```
|
195
211
|
|
196
212
|
The gpg option takes the same arguments as outlined above for the
|
197
213
|
Mail::Message#gpg method.
|
@@ -228,7 +244,6 @@ Open3.popen3(gppbin, '--preset', fpr) do |stdin, stdout, stderr|
|
|
228
244
|
end
|
229
245
|
# Hook to kill our gpg-agent when script finishes.
|
230
246
|
Signal.trap(0, proc { Process.kill('TERM', ENV['GPG_AGENT_INFO'].split(':')[1]) })
|
231
|
-
|
232
247
|
```
|
233
248
|
|
234
249
|
|
data/Rakefile
CHANGED
@@ -1,57 +1,14 @@
|
|
1
1
|
require "bundler/gem_tasks"
|
2
2
|
require 'rake/testtask'
|
3
3
|
require 'gpgme'
|
4
|
+
require 'byebug'
|
4
5
|
|
5
|
-
|
6
|
-
gpghome = File.join File.dirname(__FILE__), 'test', 'gpghome'
|
7
|
-
ENV['GNUPGHOME'] = gpghome
|
8
|
-
ENV['GPG_AGENT_INFO'] = '' # disable gpg agent
|
9
|
-
unless File.directory? gpghome
|
10
|
-
FileUtils.mkdir_p gpghome
|
11
|
-
GPGME::Ctx.new do |gpg|
|
12
|
-
gpg.generate_key <<-END
|
13
|
-
<GnupgKeyParms format="internal">
|
14
|
-
Key-Type: DSA
|
15
|
-
Key-Length: 1024
|
16
|
-
Subkey-Type: ELG-E
|
17
|
-
Subkey-Length: 1024
|
18
|
-
Name-Real: Joe Tester
|
19
|
-
Name-Comment: with stupid passphrase
|
20
|
-
Name-Email: joe@foo.bar
|
21
|
-
Expire-Date: 0
|
22
|
-
Passphrase: abc
|
23
|
-
</GnupgKeyParms>
|
24
|
-
END
|
25
|
-
gpg.generate_key <<-END
|
26
|
-
<GnupgKeyParms format="internal">
|
27
|
-
Key-Type: DSA
|
28
|
-
Key-Length: 1024
|
29
|
-
Subkey-Type: ELG-E
|
30
|
-
Subkey-Length: 1024
|
31
|
-
Name-Real: Jane Doe
|
32
|
-
Name-Comment: with stupid passphrase
|
33
|
-
Name-Email: jane@foo.bar
|
34
|
-
Expire-Date: 0
|
35
|
-
Passphrase: abc
|
36
|
-
</GnupgKeyParms>
|
37
|
-
END
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
task :default => ["mail_gpg:tests:setup", :test]
|
43
|
-
|
44
|
-
namespace :mail_gpg do
|
45
|
-
namespace :tests do
|
46
|
-
task :setup do
|
47
|
-
setup_gpghome
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
6
|
+
task :default => [:test]
|
51
7
|
|
52
8
|
Rake::TestTask.new(:test) do |test|
|
53
9
|
test.libs << 'test'
|
54
10
|
test.test_files = FileList['test/**/*_test.rb']
|
55
11
|
test.verbose = true
|
12
|
+
test.warning = false
|
56
13
|
end
|
57
14
|
|
data/lib/mail/gpg.rb
CHANGED
@@ -17,10 +17,13 @@ require 'mail/gpg/inline_signed_message'
|
|
17
17
|
|
18
18
|
module Mail
|
19
19
|
module Gpg
|
20
|
+
BEGIN_PGP_MESSAGE_MARKER = /^-----BEGIN PGP MESSAGE-----/
|
21
|
+
BEGIN_PGP_SIGNED_MESSAGE_MARKER = /^-----BEGIN PGP SIGNED MESSAGE-----/
|
22
|
+
|
20
23
|
# options are:
|
21
24
|
# :sign: sign message using the sender's private key
|
22
25
|
# :sign_as: sign using this key (give the corresponding email address or key fingerprint)
|
23
|
-
# :
|
26
|
+
# :password: passphrase for the signing key
|
24
27
|
# :keys: A hash mapping recipient email addresses to public keys or public
|
25
28
|
# key ids. Imports any keys given here that are not already part of the
|
26
29
|
# local keychain before sending the mail.
|
@@ -198,10 +201,10 @@ module Mail
|
|
198
201
|
# check if inline PGP (i.e. if any parts of the mail includes
|
199
202
|
# the PGP MESSAGE marker)
|
200
203
|
def self.encrypted_inline?(mail)
|
201
|
-
return true if mail.body.
|
204
|
+
return true if mail.body.to_s =~ BEGIN_PGP_MESSAGE_MARKER rescue nil
|
202
205
|
if mail.multipart?
|
203
206
|
mail.parts.each do |part|
|
204
|
-
return true if part.body.
|
207
|
+
return true if part.body.to_s =~ BEGIN_PGP_MESSAGE_MARKER rescue nil
|
205
208
|
return true if part.has_content_type? &&
|
206
209
|
/application\/(?:octet-stream|pgp-encrypted)/ =~ part.mime_type &&
|
207
210
|
/.*\.(?:pgp|gpg|asc)$/ =~ part.content_type_parameters[:name] &&
|
@@ -223,10 +226,10 @@ module Mail
|
|
223
226
|
# check if inline PGP (i.e. if any parts of the mail includes
|
224
227
|
# the PGP SIGNED marker)
|
225
228
|
def self.signed_inline?(mail)
|
226
|
-
return true if mail.body.to_s =~
|
229
|
+
return true if mail.body.to_s =~ BEGIN_PGP_SIGNED_MESSAGE_MARKER rescue nil
|
227
230
|
if mail.multipart?
|
228
231
|
mail.parts.each do |part|
|
229
|
-
return true if part.body.to_s =~
|
232
|
+
return true if part.body.to_s =~ BEGIN_PGP_SIGNED_MESSAGE_MARKER rescue nil
|
230
233
|
end
|
231
234
|
end
|
232
235
|
false
|
@@ -7,7 +7,7 @@ module Mail
|
|
7
7
|
encrypted_mail = nil
|
8
8
|
begin
|
9
9
|
options = TrueClass === mail.gpg ? { encrypt: true } : mail.gpg
|
10
|
-
if options
|
10
|
+
if options[:encrypt]
|
11
11
|
encrypted_mail = Mail::Gpg.encrypt(mail, options)
|
12
12
|
elsif options[:sign] || options[:sign_as]
|
13
13
|
encrypted_mail = Mail::Gpg.sign(mail, options)
|
@@ -11,7 +11,10 @@ module Mail
|
|
11
11
|
# :recipients : array of receiver addresses
|
12
12
|
# :keys : A hash mapping recipient email addresses to public keys or public
|
13
13
|
# key ids. Imports any keys given here that are not already part of the
|
14
|
-
# local keychain before sending the mail.
|
14
|
+
# local keychain before sending the mail. If this option is given, strictly
|
15
|
+
# only the key material from this hash is used, ignoring any keys for
|
16
|
+
# recipients that might have been added to the local key chain but are
|
17
|
+
# not mentioned here.
|
15
18
|
# :always_trust : send encrypted mail to untrusted receivers, true by default
|
16
19
|
# :filename : define a custom name for the encrypted file attachment
|
17
20
|
def initialize(cleartext_mail, options = {})
|
@@ -23,7 +23,7 @@ module Mail
|
|
23
23
|
GPGME::Ctx.new(options) do |ctx|
|
24
24
|
begin
|
25
25
|
if options[:sign]
|
26
|
-
if options[:signers]
|
26
|
+
if options[:signers] && options[:signers].size > 0
|
27
27
|
signers = GPGME::Key.find(:secret, options[:signers], :sign)
|
28
28
|
ctx.add_signer(*signers)
|
29
29
|
end
|
@@ -115,19 +115,41 @@ module Mail
|
|
115
115
|
|
116
116
|
# normalizes the list of recipients' emails, key ids and key data to a
|
117
117
|
# list of Key objects
|
118
|
+
#
|
119
|
+
# if key_data is given, _only_ key material from there is used,
|
120
|
+
# and eventually already imported keys in the keychain are ignored.
|
118
121
|
def self.keys_for_data(emails_or_shas_or_keys, key_data = nil)
|
119
122
|
if key_data
|
123
|
+
# in this case, emails_or_shas_or_keys is supposed to be the list of
|
124
|
+
# recipients, and key_data the key material to be used.
|
125
|
+
# We now map these to whatever we find in key_data for each of these
|
126
|
+
# addresses.
|
120
127
|
[emails_or_shas_or_keys].flatten.map do |r|
|
121
|
-
# import any given keys
|
122
128
|
k = key_data[r]
|
123
|
-
|
124
|
-
|
129
|
+
key_id = case k
|
130
|
+
when GPGME::Key
|
131
|
+
# assuming this is already imported
|
132
|
+
k.fingerprint
|
133
|
+
when nil, ''
|
134
|
+
# nothing
|
135
|
+
nil
|
136
|
+
when /-----BEGIN PGP/
|
137
|
+
# ASCII key data
|
138
|
+
GPGME::Key.import(k).imports.map(&:fpr)
|
139
|
+
else
|
140
|
+
# key id or fingerprint
|
141
|
+
k
|
142
|
+
end
|
143
|
+
unless key_id.nil? || key_id.empty?
|
144
|
+
GPGME::Key.find(:public, key_id, :encrypt)
|
125
145
|
end
|
126
|
-
|
127
|
-
|
128
|
-
else
|
146
|
+
end.flatten.compact
|
147
|
+
elsif emails_or_shas_or_keys and emails_or_shas_or_keys.size > 0
|
129
148
|
# key lookup in keychain for all receivers
|
130
149
|
GPGME::Key.find :public, emails_or_shas_or_keys, :encrypt
|
150
|
+
else
|
151
|
+
# empty array given
|
152
|
+
[]
|
131
153
|
end
|
132
154
|
end
|
133
155
|
end
|
@@ -16,6 +16,28 @@ module Mail
|
|
16
16
|
if cipher_mail.multipart?
|
17
17
|
self.new do
|
18
18
|
Mail::Gpg.copy_headers cipher_mail, self
|
19
|
+
|
20
|
+
# Drop the HTML-part of a multipart/alternative-message if it is
|
21
|
+
# inline-encrypted: that ciphertext is probably wrapped in HTML,
|
22
|
+
# which GnuPG chokes upon, so we would have to parse the HTML to
|
23
|
+
# handle the message-part properly.
|
24
|
+
# Also it's not clear how to handle the resulting plain-text: is
|
25
|
+
# it HTML or simple text? That depends on the sending MUA and
|
26
|
+
# the original input.
|
27
|
+
# In summary, that's too much complications.
|
28
|
+
if cipher_mail.mime_type == 'multipart/alternative' &&
|
29
|
+
cipher_mail.html_part.present? &&
|
30
|
+
cipher_mail.html_part.body.decoded.include?('-----BEGIN PGP MESSAGE-----')
|
31
|
+
cipher_mail.parts.delete_if do |part|
|
32
|
+
part[:content_type].content_type == 'text/html'
|
33
|
+
end
|
34
|
+
# Set the content-type of the newly generated message to
|
35
|
+
# something less confusing.
|
36
|
+
content_type 'multipart/mixed'
|
37
|
+
# Leave a marker for other code.
|
38
|
+
header['X-MailGpg-Deleted-Html-Part'] = 'true'
|
39
|
+
end
|
40
|
+
|
19
41
|
cipher_mail.parts.each do |part|
|
20
42
|
p = VerifiedPart.new do |p|
|
21
43
|
if part.has_content_type? && /application\/(?:octet-stream|pgp-encrypted)/ =~ part.mime_type
|
data/lib/mail/gpg/sign_part.rb
CHANGED
@@ -25,7 +25,7 @@ module Mail
|
|
25
25
|
end
|
26
26
|
|
27
27
|
# Work around the problem that plain_part.raw_source prefixes an
|
28
|
-
#
|
28
|
+
# erroneous CRLF, <https://github.com/mikel/mail/issues/702>.
|
29
29
|
if ! plain_part.raw_source.empty?
|
30
30
|
plaintext = [ plain_part.header.raw_source,
|
31
31
|
"\r\n\r\n",
|
data/lib/mail/gpg/version.rb
CHANGED
data/mail-gpg.gemspec
CHANGED
@@ -20,7 +20,7 @@ Gem::Specification.new do |spec|
|
|
20
20
|
|
21
21
|
spec.add_dependency "mail", "~> 2.5", ">= 2.5.3"
|
22
22
|
spec.add_dependency "gpgme", "~> 2.0", ">= 2.0.2"
|
23
|
-
spec.add_development_dependency "bundler", "~>
|
23
|
+
spec.add_development_dependency "bundler", "~> 2.0"
|
24
24
|
spec.add_development_dependency "test-unit", "~> 3.0"
|
25
25
|
spec.add_development_dependency "rake"
|
26
26
|
spec.add_development_dependency "actionmailer", ">= 3.2.0"
|
data/test/action_mailer_test.rb
CHANGED
data/test/decrypted_part_test.rb
CHANGED
@@ -2,7 +2,7 @@ require 'test_helper'
|
|
2
2
|
require 'mail/gpg/decrypted_part'
|
3
3
|
require 'mail/gpg/encrypted_part'
|
4
4
|
|
5
|
-
class DecryptedPartTest <
|
5
|
+
class DecryptedPartTest < MailGpgTestCase
|
6
6
|
context 'DecryptedPart' do
|
7
7
|
setup do
|
8
8
|
@mail = Mail.new do
|
@@ -11,7 +11,9 @@ class DecryptedPartTest < Test::Unit::TestCase
|
|
11
11
|
subject 'test'
|
12
12
|
body 'i am unencrypted'
|
13
13
|
end
|
14
|
-
@part = Mail::Gpg::EncryptedPart.new(@mail, { :
|
14
|
+
@part = Mail::Gpg::EncryptedPart.new(@mail, { recipients: ['jane@foo.bar'],
|
15
|
+
:sign => true,
|
16
|
+
:password => 'abc' })
|
15
17
|
end
|
16
18
|
|
17
19
|
should 'decrypt' do
|
data/test/encrypted_part_test.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
require 'mail/gpg/encrypted_part'
|
3
3
|
|
4
|
-
class EncryptedPartTest <
|
4
|
+
class EncryptedPartTest < MailGpgTestCase
|
5
5
|
|
6
6
|
def check_key_list(keys)
|
7
7
|
assert_equal 1, keys.size
|
@@ -17,67 +17,20 @@ class EncryptedPartTest < Test::Unit::TestCase
|
|
17
17
|
subject 'test'
|
18
18
|
body 'i am unencrypted'
|
19
19
|
end
|
20
|
-
@part = Mail::Gpg::EncryptedPart.new(mail)
|
20
|
+
@part = Mail::Gpg::EncryptedPart.new(mail, recipients: ['jane@foo.bar'])
|
21
21
|
end
|
22
22
|
|
23
|
-
|
24
|
-
|
25
|
-
@email = 'jane@foo.bar'
|
26
|
-
end
|
27
|
-
|
28
|
-
should 'resolve email to gpg keys' do
|
29
|
-
assert keys = Mail::Gpg::GpgmeHelper.send(:keys_for_data, @email)
|
30
|
-
check_key_list keys
|
31
|
-
end
|
32
|
-
|
33
|
-
should 'resolve emails to gpg keys' do
|
34
|
-
assert keys = Mail::Gpg::GpgmeHelper.send(:keys_for_data, [@email])
|
35
|
-
check_key_list keys
|
36
|
-
end
|
37
|
-
|
23
|
+
should 'have binary content type and name' do
|
24
|
+
assert_equal 'application/octet-stream; name=encrypted.asc', @part.content_type
|
38
25
|
end
|
39
26
|
|
40
|
-
|
41
|
-
|
42
|
-
@key_id = GPGME::Key.find(:public, 'jane@foo.bar').first.sha
|
43
|
-
end
|
44
|
-
|
45
|
-
should 'resolve single id gpg keys' do
|
46
|
-
assert keys = Mail::Gpg::GpgmeHelper.send(:keys_for_data, @key_id)
|
47
|
-
check_key_list keys
|
48
|
-
end
|
49
|
-
should 'resolve id list to gpg keys' do
|
50
|
-
assert keys = Mail::Gpg::GpgmeHelper.send(:keys_for_data, [@key_id])
|
51
|
-
check_key_list keys
|
52
|
-
end
|
27
|
+
should 'have description' do
|
28
|
+
assert_match(/openpgp/i, @part.content_description)
|
53
29
|
end
|
54
30
|
|
55
|
-
|
56
|
-
|
57
|
-
@key_fpr = GPGME::Key.find(:public, 'jane@foo.bar').first.fingerprint
|
58
|
-
end
|
59
|
-
|
60
|
-
should 'resolve single id gpg keys' do
|
61
|
-
assert keys = Mail::Gpg::GpgmeHelper.send(:keys_for_data, @key_fpr)
|
62
|
-
check_key_list keys
|
63
|
-
end
|
64
|
-
should 'resolve id list to gpg keys' do
|
65
|
-
assert keys = Mail::Gpg::GpgmeHelper.send(:keys_for_data, [@key_fpr])
|
66
|
-
check_key_list keys
|
67
|
-
end
|
31
|
+
should 'have inline disposition and default filename' do
|
32
|
+
assert_equal 'inline; filename=encrypted.asc', @part.content_disposition
|
68
33
|
end
|
69
34
|
|
70
|
-
context 'with emails and key data' do
|
71
|
-
setup do
|
72
|
-
@key = GPGME::Key.find(:public, 'jane@foo.bar').first.export(armor: true).to_s
|
73
|
-
@emails = ['jane@foo.bar']
|
74
|
-
@key_data = { 'jane@foo.bar' => @key }
|
75
|
-
end
|
76
|
-
|
77
|
-
should 'resolve to gpg keys' do
|
78
|
-
assert keys = Mail::Gpg::GpgmeHelper.send(:keys_for_data, @emails, @key_data)
|
79
|
-
check_key_list keys
|
80
|
-
end
|
81
|
-
end
|
82
35
|
end
|
83
36
|
end
|
data/test/gpg_test.rb
CHANGED
@@ -0,0 +1,160 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class GpgmeHelperTest < MailGpgTestCase
|
4
|
+
|
5
|
+
def check_key_list(keys)
|
6
|
+
assert_equal 1, keys.size
|
7
|
+
assert_equal GPGME::Key, keys.first.class
|
8
|
+
assert_equal 'jane@foo.bar', keys.first.email
|
9
|
+
end
|
10
|
+
|
11
|
+
context 'GpgmeHelper' do
|
12
|
+
|
13
|
+
should 'handle empty email list' do
|
14
|
+
assert_equal [], Mail::Gpg::GpgmeHelper.send(:keys_for_data, nil)
|
15
|
+
assert_equal [], Mail::Gpg::GpgmeHelper.send(:keys_for_data, [])
|
16
|
+
end
|
17
|
+
|
18
|
+
# no keys given, assuming they are already in the keychain
|
19
|
+
context 'with email address' do
|
20
|
+
setup do
|
21
|
+
@email = 'jane@foo.bar'
|
22
|
+
end
|
23
|
+
|
24
|
+
should 'resolve email to gpg keys' do
|
25
|
+
assert keys = Mail::Gpg::GpgmeHelper.send(:keys_for_data, @email)
|
26
|
+
check_key_list keys
|
27
|
+
end
|
28
|
+
|
29
|
+
should 'resolve emails to gpg keys' do
|
30
|
+
assert keys = Mail::Gpg::GpgmeHelper.send(:keys_for_data, [@email])
|
31
|
+
check_key_list keys
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# this is a use case we do not really need but it works due to the way
|
36
|
+
# Gpgme looks up keys
|
37
|
+
context 'with key id' do
|
38
|
+
setup do
|
39
|
+
@key_id = GPGME::Key.find(:public, 'jane@foo.bar').first.sha
|
40
|
+
end
|
41
|
+
|
42
|
+
should 'resolve single id gpg keys' do
|
43
|
+
assert keys = Mail::Gpg::GpgmeHelper.send(:keys_for_data, @key_id)
|
44
|
+
check_key_list keys
|
45
|
+
end
|
46
|
+
should 'resolve id list to gpg keys' do
|
47
|
+
assert keys = Mail::Gpg::GpgmeHelper.send(:keys_for_data, [@key_id])
|
48
|
+
check_key_list keys
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# this is a use case we do not really need but it works due to the way
|
53
|
+
# Gpgme looks up keys
|
54
|
+
context 'with key fingerprint' do
|
55
|
+
setup do
|
56
|
+
@key_fpr = GPGME::Key.find(:public, 'jane@foo.bar').first.fingerprint
|
57
|
+
end
|
58
|
+
|
59
|
+
should 'resolve single id gpg keys' do
|
60
|
+
assert keys = Mail::Gpg::GpgmeHelper.send(:keys_for_data, @key_fpr)
|
61
|
+
check_key_list keys
|
62
|
+
end
|
63
|
+
should 'resolve id list to gpg keys' do
|
64
|
+
assert keys = Mail::Gpg::GpgmeHelper.send(:keys_for_data, [@key_fpr])
|
65
|
+
check_key_list keys
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context 'with email addresses' do
|
70
|
+
setup do
|
71
|
+
@key = GPGME::Key.find(:public, 'jane@foo.bar').first
|
72
|
+
@emails = ['jane@foo.bar']
|
73
|
+
end
|
74
|
+
|
75
|
+
# probably the most common use case - one or more recipient addresses and a
|
76
|
+
# hash mapping them to public key data that the user pasted into a text
|
77
|
+
# field at some point
|
78
|
+
context 'and key data' do
|
79
|
+
setup do
|
80
|
+
@key = @key.export(armor: true).to_s
|
81
|
+
@key_data = { 'jane@foo.bar' => @key }
|
82
|
+
end
|
83
|
+
|
84
|
+
should 'resolve to gpg key for single address' do
|
85
|
+
assert keys = Mail::Gpg::GpgmeHelper.send(:keys_for_data, @emails.first, @key_data)
|
86
|
+
check_key_list keys
|
87
|
+
end
|
88
|
+
|
89
|
+
should 'resolve to gpg keys' do
|
90
|
+
assert keys = Mail::Gpg::GpgmeHelper.send(:keys_for_data, @emails, @key_data)
|
91
|
+
check_key_list keys
|
92
|
+
end
|
93
|
+
|
94
|
+
should 'ignore unknown addresses' do
|
95
|
+
assert keys = Mail::Gpg::GpgmeHelper.send(:keys_for_data, ['john@doe.com'], @key_data)
|
96
|
+
assert keys.blank?
|
97
|
+
end
|
98
|
+
|
99
|
+
should 'ignore invalid key data and not use existing key' do
|
100
|
+
assert keys = Mail::Gpg::GpgmeHelper.send(:keys_for_data, ['jane@foo.bar'], { 'jane@foo.bar' => "-----BEGIN PGP\ninvalid key data" })
|
101
|
+
assert keys.blank?
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
context 'and key id or fpr' do
|
106
|
+
setup do
|
107
|
+
@key_id = @key.sha
|
108
|
+
@key_fpr = @key.fingerprint
|
109
|
+
@email = @emails.first
|
110
|
+
end
|
111
|
+
|
112
|
+
should 'resolve id to gpg key for single address' do
|
113
|
+
assert keys = Mail::Gpg::GpgmeHelper.send(:keys_for_data, @emails.first, { @email => @key_id })
|
114
|
+
check_key_list keys
|
115
|
+
end
|
116
|
+
|
117
|
+
should 'resolve id to gpg key' do
|
118
|
+
assert keys = Mail::Gpg::GpgmeHelper.send(:keys_for_data, @emails, { @email => @key_id })
|
119
|
+
check_key_list keys
|
120
|
+
end
|
121
|
+
|
122
|
+
should 'resolve fpr to gpg key' do
|
123
|
+
assert keys = Mail::Gpg::GpgmeHelper.send(:keys_for_data, @emails, { @email => @key_fpr })
|
124
|
+
check_key_list keys
|
125
|
+
end
|
126
|
+
|
127
|
+
should 'ignore unknown addresses' do
|
128
|
+
assert keys = Mail::Gpg::GpgmeHelper.send(:keys_for_data, ['john@doe.com'], { @email => @key_fpr })
|
129
|
+
assert keys.blank?
|
130
|
+
end
|
131
|
+
|
132
|
+
should 'ignore invalid key id and not use existing key' do
|
133
|
+
assert keys = Mail::Gpg::GpgmeHelper.send(:keys_for_data, @emails, { @email => "invalid key id" })
|
134
|
+
assert keys.blank?
|
135
|
+
end
|
136
|
+
|
137
|
+
end
|
138
|
+
|
139
|
+
# mapping email addresses to already retrieved key objects or
|
140
|
+
# key fingerprints is also possible.
|
141
|
+
context 'and key object' do
|
142
|
+
setup do
|
143
|
+
@key_data = { 'jane@foo.bar' => @key }
|
144
|
+
end
|
145
|
+
|
146
|
+
should 'resolve to gpg keys for these addresses' do
|
147
|
+
assert keys = Mail::Gpg::GpgmeHelper.send(:keys_for_data, @emails, @key_data)
|
148
|
+
check_key_list keys
|
149
|
+
end
|
150
|
+
|
151
|
+
should 'ignore unknown addresses' do
|
152
|
+
assert keys = Mail::Gpg::GpgmeHelper.send(:keys_for_data, ['john@doe.com'], @key_data)
|
153
|
+
assert keys.blank?
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
data/test/hkp_test.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
3
|
# test cases for PGP inline messages (i.e. non-mime)
|
4
|
-
class InlineDecryptedMessageTest <
|
4
|
+
class InlineDecryptedMessageTest < MailGpgTestCase
|
5
5
|
|
6
6
|
context "InlineDecryptedMessage" do
|
7
7
|
|
@@ -33,6 +33,25 @@ class InlineDecryptedMessageTest < Test::Unit::TestCase
|
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
+
context "multipart/alternative message" do
|
37
|
+
should "have dropped HTML-part" do
|
38
|
+
mail = Mail.new(@mail)
|
39
|
+
mail.body = InlineDecryptedMessageTest.encrypt(mail, mail.body.to_s)
|
40
|
+
mail.html_part do |p|
|
41
|
+
p.body "<pre>#{InlineDecryptedMessageTest.encrypt(mail, mail.body.to_s)}</pre>"
|
42
|
+
end
|
43
|
+
|
44
|
+
assert mail.multipart?
|
45
|
+
assert mail.encrypted?
|
46
|
+
assert decrypted = mail.decrypt(:password => 'abc', verify: true)
|
47
|
+
assert !decrypted.encrypted?
|
48
|
+
assert decrypted.mime_type == 'multipart/mixed'
|
49
|
+
assert decrypted.parts.size == 1
|
50
|
+
assert decrypted.parts.first.mime_type == 'text/plain'
|
51
|
+
assert decrypted.header['X-MailGpg-Deleted-Html-Part'].value == 'true'
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
36
55
|
context "attachment message" do
|
37
56
|
should "decrypt attachment" do
|
38
57
|
rakefile = File.open('Rakefile') { |file| file.read }
|
data/test/message_test.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
|
-
class MessageTest <
|
3
|
+
class MessageTest < MailGpgTestCase
|
4
4
|
|
5
5
|
context "Mail::Message" do
|
6
6
|
|
@@ -213,9 +213,11 @@ class MessageTest < Test::Unit::TestCase
|
|
213
213
|
assert m = @mails.first
|
214
214
|
assert_equal 'test', m.subject
|
215
215
|
# incorrect passphrase
|
216
|
-
if
|
216
|
+
if @gpg_utils.preset_passphrases?
|
217
217
|
set_passphrase('incorrect')
|
218
|
-
expected_exception = GPGME::Error::DecryptFailed
|
218
|
+
# expected_exception = GPGME::Error::DecryptFailed
|
219
|
+
# I dont know why.
|
220
|
+
expected_exception = EOFError
|
219
221
|
else
|
220
222
|
expected_exception = GPGME::Error::BadPassphrase
|
221
223
|
end
|
data/test/sign_part_test.rb
CHANGED
data/test/test_helper.rb
CHANGED
@@ -4,54 +4,146 @@ require 'shoulda/context'
|
|
4
4
|
require 'mail-gpg'
|
5
5
|
require 'action_mailer'
|
6
6
|
require 'securerandom'
|
7
|
-
|
8
|
-
begin
|
9
|
-
require 'pry-nav'
|
10
|
-
rescue LoadError
|
11
|
-
end
|
7
|
+
require 'byebug'
|
12
8
|
|
13
9
|
Mail.defaults do
|
14
10
|
delivery_method :test
|
15
11
|
end
|
16
12
|
ActionMailer::Base.delivery_method = :test
|
17
13
|
|
18
|
-
|
19
|
-
|
20
|
-
|
14
|
+
class MailGpgTestCase < Test::Unit::TestCase
|
15
|
+
def setup
|
16
|
+
@gpg_utils = GPGTestUtils.new(ENV['GPG_BIN'])
|
17
|
+
@gpg_utils.setup
|
18
|
+
end
|
21
19
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
GPG21 = true
|
26
|
-
libexecdir = `gpgconf --list-dir`.lines.grep(/^libexecdir:/).first.split(':').last.strip
|
27
|
-
GPPBIN = File.join(libexecdir, 'gpg-preset-passphrase')
|
28
|
-
KEYGRIP_JANE = get_keygrip('jane@foo.bar')
|
29
|
-
KEYGRIP_JOE = get_keygrip('joe@foo.bar')
|
30
|
-
else
|
31
|
-
GPG21 = false
|
20
|
+
def set_passphrase(*args)
|
21
|
+
@gpg_utils.set_passphrase(*args)
|
22
|
+
end
|
32
23
|
end
|
33
24
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
25
|
+
class GPGTestUtils
|
26
|
+
attr_reader :gpg_engine
|
27
|
+
|
28
|
+
def initialize(gpg_bin = nil)
|
29
|
+
@home = File.join File.dirname(__FILE__), 'gpghome'
|
30
|
+
@gpg_bin = gpg_bin
|
31
|
+
|
32
|
+
ENV['GPG_AGENT_INFO'] = '' # disable gpg agent
|
33
|
+
ENV['GNUPGHOME'] = @home
|
34
|
+
|
35
|
+
if @gpg_bin
|
36
|
+
GPGME::Engine.set_info(GPGME::PROTOCOL_OpenPGP, @gpg_bin, @home)
|
37
|
+
else
|
38
|
+
GPGME::Engine.home_dir = @home
|
39
|
+
end
|
40
|
+
|
41
|
+
@gpg_engine = GPGME::Engine.info.find {|e| e.protocol == GPGME::PROTOCOL_OpenPGP }
|
42
|
+
@gpg_bin ||= @gpg_engine.file_name
|
43
|
+
|
44
|
+
if Gem::Version.new(@gpg_engine.version) >= Gem::Version.new("2.1.0")
|
45
|
+
@preset_passphrases = true
|
46
|
+
else
|
47
|
+
@preset_passphrases = false
|
48
|
+
end
|
40
49
|
end
|
41
|
-
end
|
42
50
|
|
43
|
-
def
|
44
|
-
|
45
|
-
# gpg-preset-passphrase is calling).
|
46
|
-
output = `gpgconf --launch gpg-agent 2>&1`
|
47
|
-
if ! output.empty?
|
48
|
-
$stderr.puts "Launching gpg-agent returned: #{output}"
|
51
|
+
def preset_passphrases?
|
52
|
+
!!@preset_passphrases
|
49
53
|
end
|
50
|
-
end
|
51
54
|
|
52
|
-
def
|
53
|
-
|
54
|
-
|
55
|
-
|
55
|
+
def setup
|
56
|
+
gen_keys unless File.directory? @home
|
57
|
+
|
58
|
+
if @preset_passphrases
|
59
|
+
libexecdir = `gpgconf --list-dir`.lines.grep(/^libexecdir:/).first.split(':').last.strip
|
60
|
+
@gpp_bin = File.join(libexecdir, 'gpg-preset-passphrase')
|
61
|
+
@keygrip_jane = get_keygrip('jane@foo.bar')
|
62
|
+
@keygrip_joe = get_keygrip('joe@foo.bar')
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
def gen_keys
|
68
|
+
puts "setting up keydir #{@home}"
|
69
|
+
FileUtils.mkdir_p @home
|
70
|
+
(File.open(File.join(@home, "gpg-agent.conf"), "wb") << "allow-preset-passphrase\nbatch\n").close
|
71
|
+
GPGME::Ctx.new do |gpg|
|
72
|
+
gpg.generate_key <<-END
|
73
|
+
<GnupgKeyParms format="internal">
|
74
|
+
Key-Type: DSA
|
75
|
+
Key-Length: 1024
|
76
|
+
Subkey-Type: ELG-E
|
77
|
+
Subkey-Length: 1024
|
78
|
+
Name-Real: Joe Tester
|
79
|
+
Name-Comment: with stupid passphrase
|
80
|
+
Name-Email: joe@foo.bar
|
81
|
+
Expire-Date: 0
|
82
|
+
Passphrase: abc
|
83
|
+
</GnupgKeyParms>
|
84
|
+
END
|
85
|
+
gpg.generate_key <<-END
|
86
|
+
<GnupgKeyParms format="internal">
|
87
|
+
Key-Type: DSA
|
88
|
+
Key-Length: 1024
|
89
|
+
Subkey-Type: ELG-E
|
90
|
+
Subkey-Length: 1024
|
91
|
+
Name-Real: Jane Doe
|
92
|
+
Name-Comment: with stupid passphrase
|
93
|
+
Name-Email: jane@foo.bar
|
94
|
+
Expire-Date: 0
|
95
|
+
Passphrase: abc
|
96
|
+
</GnupgKeyParms>
|
97
|
+
END
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# Put passphrase into gpg-agent (required with GnuPG v2).
|
102
|
+
def set_passphrase(passphrase)
|
103
|
+
if preset_passphrases?
|
104
|
+
ensure_gpg_agent
|
105
|
+
call_gpp(@keygrip_jane, passphrase)
|
106
|
+
call_gpp(@keygrip_joe, passphrase)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
private
|
111
|
+
|
112
|
+
def get_keygrip(uid)
|
113
|
+
output = `#{@gpg_bin} --list-secret-keys --with-keygrip --with-colons #{uid} 2>&1`
|
114
|
+
if line = output.lines.grep(/^grp/).first
|
115
|
+
line.split(':')[9]
|
116
|
+
else
|
117
|
+
puts "malformed key list output:\n#{output}"
|
118
|
+
raise
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def ensure_gpg_agent
|
123
|
+
# Make sure the gpg-agent is running (doesn't start automatically when
|
124
|
+
# gpg-preset-passphrase is calling).
|
125
|
+
output = `gpgconf --launch gpg-agent 2>&1`
|
126
|
+
if ! output.empty?
|
127
|
+
$stderr.puts "Launching gpg-agent returned: #{output}"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def call_gpp(keygrip, passphrase)
|
132
|
+
output, status = Open3.capture2e(@gpp_bin, '--homedir', ENV['GNUPGHOME'], '--preset', keygrip, {stdin_data: passphrase})
|
133
|
+
if ! output.empty?
|
134
|
+
$stderr.puts "#{@gpp_bin} returned status #{status.exitstatus}: #{output}"
|
135
|
+
end
|
56
136
|
end
|
57
137
|
end
|
138
|
+
|
139
|
+
gpg_utils = GPGTestUtils.new(ENV['GPG_BIN'])
|
140
|
+
v = Gem::Version.new(gpg_utils.gpg_engine.version)
|
141
|
+
if v >= Gem::Version.new("2.1.0")
|
142
|
+
puts "Running with GPG >= 2.1"
|
143
|
+
elsif v >= Gem::Version.new("2.0.0")
|
144
|
+
puts "Running with GPG 2.0, this isn't going well since we cannot set passphrases non-interactively"
|
145
|
+
else
|
146
|
+
puts "Running with GPG < 2.0"
|
147
|
+
end
|
148
|
+
gpg_utils.setup
|
149
|
+
|
data/test/version_part_test.rb
CHANGED
@@ -1,32 +1,32 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
require 'mail/gpg/version_part'
|
3
3
|
|
4
|
-
class VersionPartTest <
|
4
|
+
class VersionPartTest < MailGpgTestCase
|
5
5
|
context 'VersionPart' do
|
6
6
|
|
7
7
|
should 'roundtrip successfully' do
|
8
8
|
part = Mail::Gpg::VersionPart.new()
|
9
9
|
assert Mail::Gpg::VersionPart.isVersionPart?(part)
|
10
10
|
end
|
11
|
-
|
11
|
+
|
12
12
|
should 'return false for non gpg mime type' do
|
13
13
|
part = Mail::Gpg::VersionPart.new()
|
14
14
|
part.content_type = 'text/plain'
|
15
15
|
assert !Mail::Gpg::VersionPart.isVersionPart?(part)
|
16
16
|
end
|
17
|
-
|
17
|
+
|
18
18
|
should 'return false for empty body' do
|
19
19
|
part = Mail::Gpg::VersionPart.new()
|
20
20
|
part.body = nil
|
21
21
|
assert !Mail::Gpg::VersionPart.isVersionPart?(part)
|
22
22
|
end
|
23
|
-
|
23
|
+
|
24
24
|
should 'return false for foul body' do
|
25
25
|
part = Mail::Gpg::VersionPart.new()
|
26
26
|
part.body = 'non gpg body'
|
27
27
|
assert !Mail::Gpg::VersionPart.isVersionPart?(part)
|
28
28
|
end
|
29
|
-
|
29
|
+
|
30
30
|
should 'return true for body with extra content' do
|
31
31
|
part = Mail::Gpg::VersionPart.new()
|
32
32
|
part.body = "#{part.body} extra content"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mail-gpg
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jens Kraemer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-09-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: mail
|
@@ -56,14 +56,14 @@ dependencies:
|
|
56
56
|
requirements:
|
57
57
|
- - "~>"
|
58
58
|
- !ruby/object:Gem::Version
|
59
|
-
version: '
|
59
|
+
version: '2.0'
|
60
60
|
type: :development
|
61
61
|
prerelease: false
|
62
62
|
version_requirements: !ruby/object:Gem::Requirement
|
63
63
|
requirements:
|
64
64
|
- - "~>"
|
65
65
|
- !ruby/object:Gem::Version
|
66
|
-
version: '
|
66
|
+
version: '2.0'
|
67
67
|
- !ruby/object:Gem::Dependency
|
68
68
|
name: test-unit
|
69
69
|
requirement: !ruby/object:Gem::Requirement
|
@@ -182,6 +182,7 @@ files:
|
|
182
182
|
- test/gpghome/random_seed
|
183
183
|
- test/gpghome/secring.gpg
|
184
184
|
- test/gpghome/trustdb.gpg
|
185
|
+
- test/gpgme_helper_test.rb
|
185
186
|
- test/hkp_test.rb
|
186
187
|
- test/inline_decrypted_message_test.rb
|
187
188
|
- test/inline_signed_message_test.rb
|
@@ -209,7 +210,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
209
210
|
version: '0'
|
210
211
|
requirements: []
|
211
212
|
rubyforge_project:
|
212
|
-
rubygems_version: 2.
|
213
|
+
rubygems_version: 2.7.6
|
213
214
|
signing_key:
|
214
215
|
specification_version: 4
|
215
216
|
summary: GPG/MIME encryption plugin for the Ruby Mail Library
|
@@ -224,6 +225,7 @@ test_files:
|
|
224
225
|
- test/gpghome/random_seed
|
225
226
|
- test/gpghome/secring.gpg
|
226
227
|
- test/gpghome/trustdb.gpg
|
228
|
+
- test/gpgme_helper_test.rb
|
227
229
|
- test/hkp_test.rb
|
228
230
|
- test/inline_decrypted_message_test.rb
|
229
231
|
- test/inline_signed_message_test.rb
|