mail-gpg 0.1.7 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/History.txt +11 -0
- data/README.md +22 -6
- data/lib/mail/gpg.rb +46 -2
- data/lib/mail/gpg/decrypted_part.rb +3 -1
- data/lib/mail/gpg/gpgme_ext.rb +9 -0
- data/lib/mail/gpg/gpgme_helper.rb +35 -5
- data/lib/mail/gpg/inline_decrypted_message.rb +9 -2
- data/lib/mail/gpg/inline_signed_message.rb +72 -0
- data/lib/mail/gpg/message_patch.rb +18 -2
- data/lib/mail/gpg/mime_signed_message.rb +30 -0
- data/lib/mail/gpg/missing_keys_error.rb +6 -0
- data/lib/mail/gpg/sign_part.rb +6 -0
- data/lib/mail/gpg/verified_part.rb +10 -0
- data/lib/mail/gpg/verify_result_attribute.rb +31 -0
- data/lib/mail/gpg/version.rb +1 -1
- data/test/decrypted_part_test.rb +6 -0
- data/test/gpg_test.rb +18 -3
- data/test/inline_decrypted_message_test.rb +22 -6
- data/test/inline_signed_message_test.rb +121 -0
- data/test/message_test.rb +64 -7
- metadata +29 -39
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 431a878dcca05522a519c9edca6a42e658c9b0c2
|
4
|
+
data.tar.gz: 18f9aeb942581062f8644e14391520d825829473
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: baf5ef3a729d141a0d76d1fe1466a8cbbdc954e373af0c23e58020d64933af087cf3839a1ff6bdc9ccfebaa539aa2af5026b6c543848998912344361718a3163
|
7
|
+
data.tar.gz: 0052629db9f5c1a595c10d2dbf491be5f9544ae0fdb37b7541437bb2bf4866f559a9f7cd98d235d9cd414494e049905069c7dd730181d9fb749deec277943eaa
|
data/History.txt
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
== 0.2.1 2014-07-23
|
2
|
+
|
3
|
+
* keep original message id if set
|
4
|
+
|
5
|
+
== 0.2.0 2014-07-16
|
6
|
+
|
7
|
+
* implement signature verification for inline signed messages
|
8
|
+
* make signature verification more consistent with decryption by providing
|
9
|
+
Mail::Message#verify which strips raw signature data and initializes verify_result member.
|
10
|
+
* add Mail::Message#signatures as an easy way to retrieve all signatures on a Message
|
11
|
+
|
1
12
|
== 0.1.7 2014-06-03
|
2
13
|
|
3
14
|
* preserve References: header
|
data/README.md
CHANGED
@@ -119,14 +119,28 @@ Receive the mail as usual. Check if it is signed using the `signed?` method. Che
|
|
119
119
|
```ruby
|
120
120
|
mail = Mail.first
|
121
121
|
if !mail.encrypted? && mail.signed?
|
122
|
-
|
123
|
-
|
124
|
-
puts "
|
122
|
+
verified = mail.verify
|
123
|
+
puts "signature(s) valid: #{verified.signature_valid?}"
|
124
|
+
puts "message signed by: #{verified.signatures.map{|sig|sig.from}.join("\n")}"
|
125
125
|
end
|
126
126
|
```
|
127
127
|
|
128
|
-
Note that for encrypted mails the signatures can not be checked using these
|
129
|
-
the `:verify` option for the `decrypt` operation
|
128
|
+
Note that for encrypted mails the signatures can not be checked using these
|
129
|
+
methods. For encrypted mails the `:verify` option for the `decrypt` operation
|
130
|
+
must be used instead:
|
131
|
+
|
132
|
+
```ruby
|
133
|
+
if mail.encrypted?
|
134
|
+
decrypted = mail.decrypt(verify: true, password: 's3cr3t')
|
135
|
+
puts "signature(s) valid: #{decrypted.signature_valid?}"
|
136
|
+
puts "message signed by: #{decrypted.signatures.map{|sig|sig.from}.join("\n")}"
|
137
|
+
end
|
138
|
+
```
|
139
|
+
|
140
|
+
It's important to actually use the information contained in the `signatures`
|
141
|
+
array to check if the message really has been signed by the person that you (or
|
142
|
+
your users) think is the sender - usually by comparing the key id of the
|
143
|
+
signature with the key id of the expected sender.
|
130
144
|
|
131
145
|
### Key import from public key servers
|
132
146
|
|
@@ -201,5 +215,7 @@ around with your personal gpg keychain.
|
|
201
215
|
|
202
216
|
Thanks to:
|
203
217
|
|
218
|
+
* [Planio GmbH](https://plan.io) for sponsoring the ongoing maintenance and development of this library
|
204
219
|
* [morten-andersen](https://github.com/morten-andersen) for implementing decryption support for PGP/MIME and inline encrypted messages
|
205
|
-
* [FewKinG](https://github.com/FewKinG) for implementing the sign only
|
220
|
+
* [FewKinG](https://github.com/FewKinG) for implementing the sign only feature and keyserver url lookup
|
221
|
+
* [Fup Duck](https://github.com/duckdalbe) for various tweaks and fixes
|
data/lib/mail/gpg.rb
CHANGED
@@ -3,6 +3,7 @@ require 'mail/message'
|
|
3
3
|
require 'gpgme'
|
4
4
|
|
5
5
|
require 'mail/gpg/version'
|
6
|
+
require 'mail/gpg/missing_keys_error'
|
6
7
|
require 'mail/gpg/version_part'
|
7
8
|
require 'mail/gpg/decrypted_part'
|
8
9
|
require 'mail/gpg/encrypted_part'
|
@@ -11,6 +12,8 @@ require 'mail/gpg/gpgme_helper'
|
|
11
12
|
require 'mail/gpg/message_patch'
|
12
13
|
require 'mail/gpg/rails'
|
13
14
|
require 'mail/gpg/signed_part'
|
15
|
+
require 'mail/gpg/mime_signed_message'
|
16
|
+
require 'mail/gpg/inline_signed_message'
|
14
17
|
|
15
18
|
module Mail
|
16
19
|
module Gpg
|
@@ -71,6 +74,8 @@ module Mail
|
|
71
74
|
def self.signature_valid?(signed_mail, options = {})
|
72
75
|
if signed_mime?(signed_mail)
|
73
76
|
signature_valid_pgp_mime?(signed_mail, options)
|
77
|
+
elsif signed_inline?(signed_mail)
|
78
|
+
signature_valid_inline?(signed_mail, options)
|
74
79
|
else
|
75
80
|
raise EncodingError, "Unsupported signature format '#{signed_mail.content_type}'"
|
76
81
|
end
|
@@ -108,6 +113,9 @@ module Mail
|
|
108
113
|
self.header[field] = h.value
|
109
114
|
end
|
110
115
|
end
|
116
|
+
if cleartext_mail.message_id
|
117
|
+
header['Message-ID'] = cleartext_mail['Message-ID'].value
|
118
|
+
end
|
111
119
|
cleartext_mail.header.fields.each do |field|
|
112
120
|
if MORE_HEADERS.include?(field.name) or field.name =~ /^X-/
|
113
121
|
header[field.name] = field.value
|
@@ -126,7 +134,8 @@ module Mail
|
|
126
134
|
if !VersionPart.isVersionPart? encrypted_mail.parts[0]
|
127
135
|
raise EncodingError, "RFC 3136 first part not a valid version part '#{encrypted_mail.parts[0]}'"
|
128
136
|
end
|
129
|
-
|
137
|
+
decrypted = DecryptedPart.new(encrypted_mail.parts[1], options)
|
138
|
+
Mail.new(decrypted) do
|
130
139
|
%w(from to cc bcc subject reply_to in_reply_to).each do |field|
|
131
140
|
send field, encrypted_mail.send(field)
|
132
141
|
end
|
@@ -136,6 +145,7 @@ module Mail
|
|
136
145
|
encrypted_mail.header.fields.each do |field|
|
137
146
|
header[field.name] = field.value if field.name =~ /^X-/ && header[field.name].nil?
|
138
147
|
end
|
148
|
+
verify_result decrypted.verify_result if options[:verify]
|
139
149
|
end
|
140
150
|
end
|
141
151
|
|
@@ -144,15 +154,49 @@ module Mail
|
|
144
154
|
InlineDecryptedMessage.new(encrypted_mail, options)
|
145
155
|
end
|
146
156
|
|
157
|
+
def self.verify(signed_mail, options = {})
|
158
|
+
if signed_mime?(signed_mail)
|
159
|
+
Mail::Gpg::MimeSignedMessage.new signed_mail, options
|
160
|
+
elsif signed_inline?(signed_mail)
|
161
|
+
Mail::Gpg::InlineSignedMessage.new signed_mail, options
|
162
|
+
else
|
163
|
+
signed_mail
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
147
167
|
# check signature for PGP/MIME (RFC 3156, section 5) signed mail
|
148
168
|
def self.signature_valid_pgp_mime?(signed_mail, options)
|
149
169
|
# MUST contain exactly two body parts
|
150
170
|
if signed_mail.parts.length != 2
|
151
171
|
raise EncodingError, "RFC 3136 mandates exactly two body parts, found '#{signed_mail.parts.length}'"
|
152
172
|
end
|
153
|
-
SignPart.
|
173
|
+
result, verify_result = SignPart.verify_signature(signed_mail.parts[0], signed_mail.parts[1], options)
|
174
|
+
signed_mail.verify_result = verify_result
|
175
|
+
return result
|
154
176
|
end
|
155
177
|
|
178
|
+
# check signature for inline signed mail
|
179
|
+
def self.signature_valid_inline?(signed_mail, options)
|
180
|
+
result = nil
|
181
|
+
if signed_mail.multipart?
|
182
|
+
signed_mail.parts.each do |part|
|
183
|
+
if signed_inline?(part)
|
184
|
+
if result.nil?
|
185
|
+
result = true
|
186
|
+
signed_mail.verify_result = []
|
187
|
+
end
|
188
|
+
result &= signature_valid_inline?(part, options)
|
189
|
+
signed_mail.verify_result << part.verify_result
|
190
|
+
end
|
191
|
+
end
|
192
|
+
else
|
193
|
+
result, verify_result = GpgmeHelper.inline_verify(signed_mail.body.to_s, options)
|
194
|
+
signed_mail.verify_result = verify_result
|
195
|
+
end
|
196
|
+
return result
|
197
|
+
end
|
198
|
+
|
199
|
+
|
156
200
|
# check if PGP/MIME encrypted (RFC 3156)
|
157
201
|
def self.encrypted_mime?(mail)
|
158
202
|
mail.has_content_type? &&
|
@@ -1,6 +1,7 @@
|
|
1
|
+
require 'mail/gpg/verified_part'
|
1
2
|
module Mail
|
2
3
|
module Gpg
|
3
|
-
class DecryptedPart <
|
4
|
+
class DecryptedPart < VerifiedPart
|
4
5
|
|
5
6
|
# options are:
|
6
7
|
#
|
@@ -11,6 +12,7 @@ module Mail
|
|
11
12
|
end
|
12
13
|
|
13
14
|
decrypted = GpgmeHelper.decrypt(cipher_part.body.decoded, options)
|
15
|
+
self.verify_result = decrypted.verify_result if options[:verify]
|
14
16
|
super(decrypted)
|
15
17
|
end
|
16
18
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'mail/gpg/gpgme_ext'
|
2
|
+
|
1
3
|
# GPGME methods for encryption/decryption/signing
|
2
4
|
module Mail
|
3
5
|
module Gpg
|
@@ -11,6 +13,10 @@ module Mail
|
|
11
13
|
|
12
14
|
recipient_keys = keys_for_data options[:recipients], options.delete(:keys)
|
13
15
|
|
16
|
+
if recipient_keys.empty?
|
17
|
+
raise MissingKeysError.new('No keys to encrypt to!')
|
18
|
+
end
|
19
|
+
|
14
20
|
flags = 0
|
15
21
|
flags |= GPGME::ENCRYPT_ALWAYS_TRUST if options[:always_trust]
|
16
22
|
|
@@ -46,6 +52,7 @@ module Mail
|
|
46
52
|
begin
|
47
53
|
if options[:verify]
|
48
54
|
ctx.decrypt_verify(cipher_data, plain_data)
|
55
|
+
plain_data.verify_result = ctx.verify_result
|
49
56
|
else
|
50
57
|
ctx.decrypt(cipher_data, plain_data)
|
51
58
|
end
|
@@ -72,13 +79,36 @@ module Mail
|
|
72
79
|
crypto.sign GPGME::Data.new(plain), options
|
73
80
|
end
|
74
81
|
|
82
|
+
# returns [success(bool), VerifyResult(from gpgme)]
|
83
|
+
# success will be true when there is at least one sig and no invalid sig
|
75
84
|
def self.sign_verify(plain, signature, options = {})
|
76
|
-
|
77
|
-
GPGME::
|
78
|
-
|
79
|
-
|
85
|
+
signed_data = GPGME::Data.new(plain)
|
86
|
+
signature = GPGME::Data.new(signature)
|
87
|
+
|
88
|
+
success = verify_result = nil
|
89
|
+
GPGME::Ctx.new(options) do |ctx|
|
90
|
+
ctx.verify signature, signed_data, nil
|
91
|
+
verify_result = ctx.verify_result
|
92
|
+
signatures = verify_result.signatures
|
93
|
+
success = signatures &&
|
94
|
+
signatures.size > 0 &&
|
95
|
+
signatures.detect{|s| !s.valid? }.nil?
|
96
|
+
end
|
97
|
+
return [success, verify_result]
|
98
|
+
end
|
99
|
+
|
100
|
+
def self.inline_verify(signed_text, options = {})
|
101
|
+
signed_data = GPGME::Data.new(signed_text)
|
102
|
+
success = verify_result = nil
|
103
|
+
GPGME::Ctx.new(options) do |ctx|
|
104
|
+
ctx.verify signed_data, nil
|
105
|
+
verify_result = ctx.verify_result
|
106
|
+
signatures = verify_result.signatures
|
107
|
+
success = signatures &&
|
108
|
+
signatures.size > 0 &&
|
109
|
+
signatures.detect{|s| !s.valid? }.nil?
|
80
110
|
end
|
81
|
-
return
|
111
|
+
return [success, verify_result]
|
82
112
|
end
|
83
113
|
|
84
114
|
private
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'mail/gpg/verified_part'
|
2
|
+
|
1
3
|
# decryption of the so called 'PGP-Inline' message types
|
2
4
|
# this is not a standard, so the implementation is based on the notes
|
3
5
|
# here http://binblog.info/2008/03/12/know-your-pgp-implementation/
|
@@ -17,11 +19,12 @@ module Mail
|
|
17
19
|
header[field.name] = field.value
|
18
20
|
end
|
19
21
|
cipher_mail.parts.each do |part|
|
20
|
-
|
22
|
+
p = VerifiedPart.new do |p|
|
21
23
|
if part.has_content_type? && /application\/(?:octet-stream|pgp-encrypted)/ =~ part.mime_type
|
22
24
|
# encrypted attachment, we set the content_type to the generic 'application/octet-stream'
|
23
25
|
# and remove the .pgp/gpg/asc from name/filename in header fields
|
24
26
|
decrypted = GpgmeHelper.decrypt(part.decoded, options)
|
27
|
+
p.verify_result decrypted.verify_result if options[:verify]
|
25
28
|
p.content_type part.content_type.sub(/application\/(?:octet-stream|pgp-encrypted)/, 'application/octet-stream')
|
26
29
|
.sub(/name=(?:"')?(.*)\.(?:pgp|gpg|asc)(?:"')?/, 'name="\1"')
|
27
30
|
p.content_disposition part.content_disposition.sub(/filename=(?:"')?(.*)\.(?:pgp|gpg|asc)(?:"')?/, 'filename="\1"')
|
@@ -29,7 +32,9 @@ module Mail
|
|
29
32
|
p.body Mail::Encodings::Base64::encode(decrypted.to_s)
|
30
33
|
else
|
31
34
|
if part.body.include?('-----BEGIN PGP MESSAGE-----')
|
32
|
-
|
35
|
+
decrypted = GpgmeHelper.decrypt(part.decoded, options)
|
36
|
+
p.verify_result decrypted.verify_result if options[:verify]
|
37
|
+
p.body decrypted.to_s
|
33
38
|
else
|
34
39
|
p.content_type part.content_type
|
35
40
|
p.content_transfer_encoding part.content_transfer_encoding
|
@@ -37,6 +42,7 @@ module Mail
|
|
37
42
|
end
|
38
43
|
end
|
39
44
|
end
|
45
|
+
add_part p
|
40
46
|
end
|
41
47
|
end # of multipart
|
42
48
|
else
|
@@ -46,6 +52,7 @@ module Mail
|
|
46
52
|
header[field.name] = field.value
|
47
53
|
end
|
48
54
|
body decrypted.to_s
|
55
|
+
verify_result decrypted.verify_result if options[:verify] && '' != decrypted
|
49
56
|
end
|
50
57
|
end
|
51
58
|
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'mail/gpg/verified_part'
|
2
|
+
|
3
|
+
module Mail
|
4
|
+
module Gpg
|
5
|
+
class InlineSignedMessage < Mail::Message
|
6
|
+
|
7
|
+
def initialize(signed_mail, options = {})
|
8
|
+
if signed_mail.multipart?
|
9
|
+
super() do
|
10
|
+
global_verify_result = []
|
11
|
+
signed_mail.header.fields.each do |field|
|
12
|
+
header[field.name] = field.value
|
13
|
+
end
|
14
|
+
signed_mail.parts.each do |part|
|
15
|
+
if Mail::Gpg.signed_inline?(part)
|
16
|
+
signed_text = part.body.to_s
|
17
|
+
success, vr = GpgmeHelper.inline_verify(signed_text, options)
|
18
|
+
p = VerifiedPart.new(part)
|
19
|
+
if success
|
20
|
+
p.body self.class.strip_inline_signature signed_text
|
21
|
+
end
|
22
|
+
p.verify_result vr
|
23
|
+
global_verify_result << vr
|
24
|
+
add_part p
|
25
|
+
else
|
26
|
+
add_part part
|
27
|
+
end
|
28
|
+
end
|
29
|
+
verify_result global_verify_result
|
30
|
+
end # of multipart
|
31
|
+
else
|
32
|
+
super() do
|
33
|
+
signed_mail.header.fields.each do |field|
|
34
|
+
header[field.name] = field.value
|
35
|
+
end
|
36
|
+
signed_text = signed_mail.body.to_s
|
37
|
+
success, vr = GpgmeHelper.inline_verify(signed_text, options)
|
38
|
+
if success
|
39
|
+
body self.class.strip_inline_signature signed_text
|
40
|
+
else
|
41
|
+
body signed_text
|
42
|
+
end
|
43
|
+
verify_result vr
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
END_SIGNED_TEXT = '-----END PGP SIGNED MESSAGE-----'
|
49
|
+
END_SIGNED_TEXT_RE = /^#{END_SIGNED_TEXT}\s*$/
|
50
|
+
INLINE_SIG_RE = Regexp.new('^-----BEGIN PGP SIGNATURE-----\s*$.*^-----END PGP SIGNATURE-----\s*$', Regexp::MULTILINE)
|
51
|
+
BEGIN_SIG_RE = /^(-----BEGIN PGP SIGNATURE-----)\s*$/
|
52
|
+
|
53
|
+
|
54
|
+
# utility method to remove inline signature and related pgp markers
|
55
|
+
def self.strip_inline_signature(signed_text)
|
56
|
+
if signed_text =~ INLINE_SIG_RE
|
57
|
+
signed_text = signed_text.dup
|
58
|
+
if signed_text !~ END_SIGNED_TEXT_RE
|
59
|
+
# insert the 'end of signed text' marker in case it is missing
|
60
|
+
signed_text = signed_text.gsub BEGIN_SIG_RE, "-----END PGP SIGNED MESSAGE-----\n\\1"
|
61
|
+
end
|
62
|
+
signed_text.gsub! INLINE_SIG_RE, ''
|
63
|
+
signed_text.strip!
|
64
|
+
end
|
65
|
+
signed_text
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'mail/gpg/delivery_handler'
|
2
|
+
require 'mail/gpg/verify_result_attribute'
|
2
3
|
|
3
4
|
module Mail
|
4
5
|
module Gpg
|
@@ -7,6 +8,7 @@ module Mail
|
|
7
8
|
def self.included(base)
|
8
9
|
base.class_eval do
|
9
10
|
attr_accessor :raise_encryption_errors
|
11
|
+
include VerifyResultAttribute
|
10
12
|
end
|
11
13
|
end
|
12
14
|
|
@@ -51,21 +53,35 @@ module Mail
|
|
51
53
|
end
|
52
54
|
end
|
53
55
|
|
56
|
+
# true if this mail is encrypted
|
54
57
|
def encrypted?
|
55
58
|
Mail::Gpg.encrypted?(self)
|
56
59
|
end
|
57
60
|
|
61
|
+
# returns the decrypted mail object.
|
62
|
+
#
|
63
|
+
# pass verify: true to verify signatures as well. The gpgme verification
|
64
|
+
# result will be available via decrypted_mail.verify_result
|
58
65
|
def decrypt(options = {})
|
59
66
|
Mail::Gpg.decrypt(self, options)
|
60
67
|
end
|
61
68
|
|
69
|
+
# true if this mail is signed (but not encrypted)
|
62
70
|
def signed?
|
63
71
|
Mail::Gpg.signed?(self)
|
64
72
|
end
|
65
73
|
|
66
|
-
|
67
|
-
|
74
|
+
# verify signatures. returns a new mail object with signatures removed and
|
75
|
+
# populated verify_result.
|
76
|
+
#
|
77
|
+
# verified = signed_mail.verify()
|
78
|
+
# verified.signature_valid?
|
79
|
+
# signers = mail.signatures.map{|sig| sig.from}
|
80
|
+
def verify(options = {})
|
81
|
+
Mail::Gpg.verify(self, options)
|
68
82
|
end
|
83
|
+
|
84
|
+
|
69
85
|
end
|
70
86
|
end
|
71
87
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'mail/gpg/verified_part'
|
2
|
+
|
3
|
+
module Mail
|
4
|
+
module Gpg
|
5
|
+
class MimeSignedMessage < Mail::Message
|
6
|
+
|
7
|
+
def initialize(signed_mail, options = {})
|
8
|
+
content_part, signature = signed_mail.parts
|
9
|
+
success, vr = SignPart.verify_signature(content_part, signature, options)
|
10
|
+
super() do
|
11
|
+
verify_result vr
|
12
|
+
signed_mail.header.fields.each do |field|
|
13
|
+
header[field.name] = field.value
|
14
|
+
end
|
15
|
+
content_part.header.fields.each do |field|
|
16
|
+
header[field.name] = field.value
|
17
|
+
end
|
18
|
+
if content_part.multipart?
|
19
|
+
content_part.parts.each{|part| add_part part}
|
20
|
+
else
|
21
|
+
body content_part.body.to_s
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
|
data/lib/mail/gpg/sign_part.rb
CHANGED
@@ -12,7 +12,13 @@ module Mail
|
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
|
+
# true if all signatures are valid
|
15
16
|
def self.signature_valid?(plain_part, signature_part, options = {})
|
17
|
+
verify_signature(plain_part, signature_part, options)[0]
|
18
|
+
end
|
19
|
+
|
20
|
+
# will return [success(boolean), verify_result(as returned by gpgme)]
|
21
|
+
def self.verify_signature(plain_part, signature_part, options = {})
|
16
22
|
if !(signature_part.has_content_type? &&
|
17
23
|
('application/pgp-signature' == signature_part.mime_type))
|
18
24
|
return false
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Mail
|
2
|
+
module Gpg
|
3
|
+
module VerifyResultAttribute
|
4
|
+
|
5
|
+
# the result of signature verification, as provided by GPGME
|
6
|
+
def verify_result(result = nil)
|
7
|
+
if result
|
8
|
+
self.verify_result = result
|
9
|
+
else
|
10
|
+
@verify_result
|
11
|
+
end
|
12
|
+
end
|
13
|
+
def verify_result=(result)
|
14
|
+
@verify_result = result
|
15
|
+
end
|
16
|
+
|
17
|
+
# checks validity of signatures (true / false)
|
18
|
+
def signature_valid?
|
19
|
+
sigs = self.signatures
|
20
|
+
sigs.any? && sigs.detect{|s|!s.valid?}.blank?
|
21
|
+
end
|
22
|
+
|
23
|
+
# list of all signatures from verify_result
|
24
|
+
def signatures
|
25
|
+
[verify_result].flatten.compact.map do |vr|
|
26
|
+
vr.signatures
|
27
|
+
end.flatten.compact
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/mail/gpg/version.rb
CHANGED
data/test/decrypted_part_test.rb
CHANGED
@@ -26,6 +26,10 @@ class DecryptedPartTest < Test::Unit::TestCase
|
|
26
26
|
assert mail == @mail
|
27
27
|
assert mail.message_id == @mail.message_id
|
28
28
|
assert mail.message_id != @part.message_id
|
29
|
+
assert vr = mail.verify_result
|
30
|
+
assert sig = vr.signatures.first
|
31
|
+
assert sig.to_s=~ /Joe/
|
32
|
+
assert sig.valid?
|
29
33
|
end
|
30
34
|
|
31
35
|
should 'raise encoding error for non gpg mime type' do
|
@@ -33,5 +37,7 @@ class DecryptedPartTest < Test::Unit::TestCase
|
|
33
37
|
part.content_type = 'text/plain'
|
34
38
|
assert_raise(EncodingError) { Mail::Gpg::DecryptedPart.new(part) }
|
35
39
|
end
|
40
|
+
|
41
|
+
|
36
42
|
end
|
37
43
|
end
|
data/test/gpg_test.rb
CHANGED
@@ -36,6 +36,11 @@ class GpgTest < Test::Unit::TestCase
|
|
36
36
|
assert sig.valid?
|
37
37
|
end
|
38
38
|
assert Mail::Gpg.signature_valid?(signed)
|
39
|
+
assert verified = signed.verify
|
40
|
+
assert verified.verify_result.present?
|
41
|
+
assert verified.verify_result.signatures.any?
|
42
|
+
assert verified.signatures.any?
|
43
|
+
assert verified.signature_valid?
|
39
44
|
end
|
40
45
|
|
41
46
|
def check_mime_structure_signed(mail = @mail, signed = @signed)
|
@@ -250,6 +255,10 @@ class GpgTest < Test::Unit::TestCase
|
|
250
255
|
should 'decrypt and verify' do
|
251
256
|
assert mail = Mail::Gpg.decrypt(@encrypted, { :verify => true, :password => 'abc' })
|
252
257
|
assert mail == @mail
|
258
|
+
assert mail.verify_result
|
259
|
+
assert sig = mail.signatures.first
|
260
|
+
assert sig.to_s =~ /Joe/
|
261
|
+
assert sig.valid?
|
253
262
|
end
|
254
263
|
end
|
255
264
|
|
@@ -330,7 +339,7 @@ class GpgTest < Test::Unit::TestCase
|
|
330
339
|
context 'multipart mail' do
|
331
340
|
setup do
|
332
341
|
@mail.add_file 'Rakefile'
|
333
|
-
@encrypted = Mail::Gpg.encrypt(@mail)
|
342
|
+
@encrypted = Mail::Gpg.encrypt(@mail, sign: true, password: 'abc')
|
334
343
|
end
|
335
344
|
|
336
345
|
should 'have same recipients and subject' do
|
@@ -355,10 +364,16 @@ class GpgTest < Test::Unit::TestCase
|
|
355
364
|
assert_match /Rakefile/, m.parts.last.content_disposition
|
356
365
|
end
|
357
366
|
|
358
|
-
should 'decrypt' do
|
359
|
-
assert mail = Mail::Gpg.decrypt(@encrypted, { :password => 'abc' })
|
367
|
+
should 'decrypt and verify' do
|
368
|
+
assert mail = Mail::Gpg.decrypt(@encrypted, { :verify => true, :password => 'abc' })
|
360
369
|
assert mail == @mail
|
361
370
|
assert mail.parts[1] == @mail.parts[1]
|
371
|
+
assert mail.verify_result
|
372
|
+
assert signatures = mail.signatures
|
373
|
+
assert_equal 1, signatures.size
|
374
|
+
assert sig = signatures[0]
|
375
|
+
assert sig.to_s =~ /Joe/
|
376
|
+
assert sig.valid?
|
362
377
|
end
|
363
378
|
end
|
364
379
|
end
|
@@ -17,15 +17,19 @@ class InlineDecryptedMessageTest < Test::Unit::TestCase
|
|
17
17
|
end
|
18
18
|
|
19
19
|
context "inline message" do
|
20
|
-
should "decrypt body" do
|
20
|
+
should "decrypt and verify body" do
|
21
21
|
mail = Mail.new(@mail)
|
22
22
|
mail.body = InlineDecryptedMessageTest.encrypt(mail, mail.body.to_s)
|
23
23
|
|
24
24
|
assert !mail.multipart?
|
25
25
|
assert mail.encrypted?
|
26
|
-
assert decrypted = mail.decrypt(:password => 'abc')
|
26
|
+
assert decrypted = mail.decrypt(:password => 'abc', verify: true)
|
27
27
|
assert decrypted == @mail
|
28
28
|
assert !decrypted.encrypted?
|
29
|
+
assert vr = decrypted.verify_result
|
30
|
+
assert sig = vr.signatures.first
|
31
|
+
assert sig.to_s=~ /Joe/
|
32
|
+
assert sig.valid?
|
29
33
|
end
|
30
34
|
end
|
31
35
|
|
@@ -55,7 +59,7 @@ class InlineDecryptedMessageTest < Test::Unit::TestCase
|
|
55
59
|
end
|
56
60
|
|
57
61
|
context "cleartext body and encrypted attachment message" do
|
58
|
-
should "decrypt attachment" do
|
62
|
+
should "decrypt and verify attachment" do
|
59
63
|
rakefile = File.open('Rakefile') { |file| file.read }
|
60
64
|
mail = Mail.new(@mail)
|
61
65
|
mail.content_type = 'multipart/mixed'
|
@@ -68,7 +72,7 @@ class InlineDecryptedMessageTest < Test::Unit::TestCase
|
|
68
72
|
|
69
73
|
assert mail.multipart?
|
70
74
|
assert mail.encrypted?
|
71
|
-
assert decrypted = mail.decrypt(:
|
75
|
+
assert decrypted = mail.decrypt(password: 'abc', verify: true)
|
72
76
|
assert !decrypted.encrypted?
|
73
77
|
check_headers(@mail, decrypted)
|
74
78
|
assert_equal 2, decrypted.parts.length
|
@@ -76,11 +80,17 @@ class InlineDecryptedMessageTest < Test::Unit::TestCase
|
|
76
80
|
assert /application\/octet-stream; (?:charset=UTF-8; )?name=Rakefile/ =~ decrypted.parts[1].content_type
|
77
81
|
assert_equal 'attachment; filename=Rakefile', decrypted.parts[1].content_disposition
|
78
82
|
assert_equal rakefile, decrypted.parts[1].body.decoded
|
83
|
+
|
84
|
+
assert_nil decrypted.parts[0].verify_result
|
85
|
+
assert vr = decrypted.parts[1].verify_result
|
86
|
+
assert sig = vr.signatures.first
|
87
|
+
assert sig.to_s=~ /Joe/
|
88
|
+
assert sig.valid?
|
79
89
|
end
|
80
90
|
end
|
81
91
|
|
82
92
|
context "encrypted body and attachment message" do
|
83
|
-
should "decrypt" do
|
93
|
+
should "decrypt and verify" do
|
84
94
|
rakefile = File.open('Rakefile') { |file| file.read }
|
85
95
|
mail = Mail.new(@mail)
|
86
96
|
mail.content_type = 'multipart/mixed'
|
@@ -94,7 +104,7 @@ class InlineDecryptedMessageTest < Test::Unit::TestCase
|
|
94
104
|
|
95
105
|
assert mail.multipart?
|
96
106
|
assert mail.encrypted?
|
97
|
-
assert decrypted = mail.decrypt(:
|
107
|
+
assert decrypted = mail.decrypt(password: 'abc', verify: true)
|
98
108
|
assert !decrypted.encrypted?
|
99
109
|
check_headers(@mail, decrypted)
|
100
110
|
assert_equal 2, decrypted.parts.length
|
@@ -102,6 +112,12 @@ class InlineDecryptedMessageTest < Test::Unit::TestCase
|
|
102
112
|
assert /application\/octet-stream; (?:charset=UTF-8; )?name=Rakefile/ =~ decrypted.parts[1].content_type
|
103
113
|
assert_equal 'attachment; filename=Rakefile', decrypted.parts[1].content_disposition
|
104
114
|
assert_equal rakefile, decrypted.parts[1].body.decoded
|
115
|
+
decrypted.parts.each do |part|
|
116
|
+
assert vr = part.verify_result
|
117
|
+
assert sig = vr.signatures.first
|
118
|
+
assert sig.to_s=~ /Joe/
|
119
|
+
assert sig.valid?
|
120
|
+
end
|
105
121
|
end
|
106
122
|
end
|
107
123
|
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
# test cases for PGP inline signed messages (i.e. non-mime)
|
4
|
+
class InlineSignedMessageTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
context "InlineSignedMessage" do
|
7
|
+
|
8
|
+
setup do
|
9
|
+
(@mails = Mail::TestMailer.deliveries).clear
|
10
|
+
@mail = Mail.new do
|
11
|
+
to 'jane@foo.bar'
|
12
|
+
from 'joe@foo.bar'
|
13
|
+
subject 'test'
|
14
|
+
body 'i am unencrypted'
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
context 'strip_inline_signature' do
|
19
|
+
should 'strip signature from signed text' do
|
20
|
+
body = self.class.inline_sign(@mail, 'i am signed')
|
21
|
+
assert stripped_body = Mail::Gpg::InlineSignedMessage.strip_inline_signature(body)
|
22
|
+
assert_equal "-----BEGIN PGP SIGNED MESSAGE-----\nHash: SHA1\n\ni am signed\n-----END PGP SIGNED MESSAGE-----", stripped_body
|
23
|
+
end
|
24
|
+
|
25
|
+
should 'not change unsigned text' do
|
26
|
+
assert stripped_body = Mail::Gpg::InlineSignedMessage.strip_inline_signature("foo\nbar\n")
|
27
|
+
assert_equal "foo\nbar\n", stripped_body
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context "signed message" do
|
32
|
+
should "verify body" do
|
33
|
+
mail = Mail.new(@mail)
|
34
|
+
mail.body = self.class.inline_sign(mail, mail.body.to_s)
|
35
|
+
assert !mail.multipart?
|
36
|
+
assert mail.signed?
|
37
|
+
assert verified = mail.verify
|
38
|
+
assert verified.signature_valid?
|
39
|
+
assert sig = verified.signatures.first
|
40
|
+
assert sig.to_s=~ /Joe/
|
41
|
+
assert sig.valid?
|
42
|
+
end
|
43
|
+
|
44
|
+
should "detect invalid sig" do
|
45
|
+
mail = Mail.new(@mail)
|
46
|
+
mail.body = self.class.inline_sign(mail, mail.body.to_s).gsub /i am/, 'i was'
|
47
|
+
assert !mail.multipart?
|
48
|
+
assert mail.signed?
|
49
|
+
assert verified = mail.verify
|
50
|
+
assert !verified.signature_valid?
|
51
|
+
assert vr = verified.verify_result
|
52
|
+
assert sig = verified.signatures.first
|
53
|
+
assert sig.to_s=~ /Joe/
|
54
|
+
assert !sig.valid?
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
context "message with signed attachment" do
|
60
|
+
should "check attachment signature" do
|
61
|
+
mail = Mail.new(@mail)
|
62
|
+
mail.body = 'foobar'
|
63
|
+
mail.part do |p|
|
64
|
+
p.body = self.class.inline_sign(mail, 'sign me!')
|
65
|
+
end
|
66
|
+
assert mail.multipart?
|
67
|
+
assert mail.signed?
|
68
|
+
assert verified = mail.verify
|
69
|
+
assert verified.signature_valid?
|
70
|
+
assert vr = verified.parts.last.verify_result
|
71
|
+
assert !verified.parts.first.signed?
|
72
|
+
assert verified.parts.last.signed?
|
73
|
+
assert Mail::Gpg.signed_inline?(verified.parts.last)
|
74
|
+
assert_equal [vr], verified.verify_result
|
75
|
+
assert sig = verified.signatures.first
|
76
|
+
assert sig.to_s=~ /Joe/
|
77
|
+
assert sig.valid?
|
78
|
+
end
|
79
|
+
|
80
|
+
should "detect invalid sig" do
|
81
|
+
mail = Mail.new(@mail)
|
82
|
+
mail.body = 'foobar'
|
83
|
+
mail.part do |p|
|
84
|
+
p.body = self.class.inline_sign(mail, 'i am signed!').gsub /i am/, 'i was'
|
85
|
+
end
|
86
|
+
mail.part do |p|
|
87
|
+
p.body = self.class.inline_sign(mail, 'i am signed!')
|
88
|
+
end
|
89
|
+
|
90
|
+
assert mail.multipart?
|
91
|
+
assert mail.signed?
|
92
|
+
assert verified = mail.verify
|
93
|
+
assert !verified.signature_valid?
|
94
|
+
assert vr = verified.verify_result
|
95
|
+
assert_equal 2, vr.size
|
96
|
+
|
97
|
+
invalid = verified.parts[1]
|
98
|
+
assert !invalid.signature_valid?
|
99
|
+
assert sig = invalid.verify_result.signatures.first
|
100
|
+
assert sig.to_s=~ /Joe/
|
101
|
+
assert !sig.valid?
|
102
|
+
|
103
|
+
valid = verified.parts[2]
|
104
|
+
assert valid.signature_valid?
|
105
|
+
assert sig = valid.verify_result.signatures.first
|
106
|
+
assert sig.to_s=~ /Joe/
|
107
|
+
assert sig.valid?
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
|
113
|
+
def self.inline_sign(mail, plain, armor = true)
|
114
|
+
GPGME::Crypto.new.clearsign(plain,
|
115
|
+
password: 'abc',
|
116
|
+
signers: mail.from,
|
117
|
+
armor: armor).to_s
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
|
data/test/message_test.rb
CHANGED
@@ -42,6 +42,35 @@ class MessageTest < Test::Unit::TestCase
|
|
42
42
|
@mail.gpg sign: true, password: 'abc'
|
43
43
|
end
|
44
44
|
|
45
|
+
context 'with multiple parts' do
|
46
|
+
setup do
|
47
|
+
p = Mail::Part.new do
|
48
|
+
body 'and another part'
|
49
|
+
end
|
50
|
+
@mail.add_part p
|
51
|
+
p = Mail::Part.new do
|
52
|
+
body 'and a third part'
|
53
|
+
end
|
54
|
+
@mail.add_part p
|
55
|
+
|
56
|
+
@mail.deliver
|
57
|
+
@signed = @mails.first
|
58
|
+
@verified = @signed.verify
|
59
|
+
end
|
60
|
+
|
61
|
+
should 'verify signature' do
|
62
|
+
assert @verified.signature_valid?
|
63
|
+
end
|
64
|
+
|
65
|
+
should 'have original three parts' do
|
66
|
+
assert_equal 3, @mail.parts.size
|
67
|
+
assert_equal 3, @verified.parts.size
|
68
|
+
assert_equal 'i am unencrypted', @verified.parts[0].body.to_s
|
69
|
+
assert_equal 'and another part', @verified.parts[1].body.to_s
|
70
|
+
assert_equal 'and a third part', @verified.parts[2].body.to_s
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
45
74
|
context "" do
|
46
75
|
setup do
|
47
76
|
@mail.header['Auto-Submitted'] = 'foo'
|
@@ -59,14 +88,18 @@ class MessageTest < Test::Unit::TestCase
|
|
59
88
|
assert !m.encrypted?
|
60
89
|
assert m.signed?
|
61
90
|
assert m.multipart?
|
62
|
-
assert m.signature_valid?
|
63
91
|
assert sign_part = m.parts.last
|
64
|
-
#assert m = Mail::Message.new(m.parts.first)
|
65
|
-
#assert !m.multipart?
|
66
92
|
GPGME::Crypto.new.verify(sign_part.body.to_s, signed_text: m.parts.first.encoded) do |sig|
|
67
93
|
assert sig.valid?
|
68
94
|
end
|
69
|
-
|
95
|
+
end
|
96
|
+
|
97
|
+
should 'verify signed mail' do
|
98
|
+
assert m = @mails.first
|
99
|
+
assert verified = m.verify
|
100
|
+
assert verified.signature_valid?
|
101
|
+
assert !verified.multipart?
|
102
|
+
assert_equal 'i am unencrypted', verified.body.to_s
|
70
103
|
end
|
71
104
|
|
72
105
|
should "fail signature on tampered body" do
|
@@ -76,13 +109,36 @@ class MessageTest < Test::Unit::TestCase
|
|
76
109
|
assert !m.encrypted?
|
77
110
|
assert m.signed?
|
78
111
|
assert m.multipart?
|
79
|
-
assert m.
|
112
|
+
assert verified = m.verify
|
113
|
+
assert verified.signature_valid?
|
80
114
|
m.parts.first.body = 'replaced body'
|
81
|
-
assert
|
115
|
+
assert verified = m.verify
|
116
|
+
assert !verified.signature_valid?
|
82
117
|
end
|
83
118
|
end
|
84
119
|
end
|
85
120
|
|
121
|
+
context 'with encryption and signing' do
|
122
|
+
setup do
|
123
|
+
@mail.gpg encrypt: true, sign: true, password: 'abc'
|
124
|
+
@mail.deliver
|
125
|
+
end
|
126
|
+
|
127
|
+
should 'decrypt and check signature' do
|
128
|
+
assert_equal 1, @mails.size
|
129
|
+
assert m = @mails.first
|
130
|
+
assert_equal 'test', m.subject
|
131
|
+
assert m.multipart?
|
132
|
+
assert m.encrypted?
|
133
|
+
assert decrypted = m.decrypt(:password => 'abc', verify: true)
|
134
|
+
assert_equal 'test', decrypted.subject
|
135
|
+
assert decrypted == @mail
|
136
|
+
assert_equal 'i am unencrypted', decrypted.body.to_s
|
137
|
+
assert decrypted.signature_valid?
|
138
|
+
assert_equal 1, decrypted.signatures.size
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
86
142
|
context "with gpg turned on" do
|
87
143
|
setup do
|
88
144
|
@mail.gpg encrypt: true
|
@@ -94,7 +150,7 @@ class MessageTest < Test::Unit::TestCase
|
|
94
150
|
end
|
95
151
|
|
96
152
|
should "raise encryption error" do
|
97
|
-
assert_raises(
|
153
|
+
assert_raises(Mail::Gpg::MissingKeysError){
|
98
154
|
@mail.deliver
|
99
155
|
}
|
100
156
|
end
|
@@ -106,6 +162,7 @@ class MessageTest < Test::Unit::TestCase
|
|
106
162
|
end
|
107
163
|
end
|
108
164
|
|
165
|
+
|
109
166
|
context "" do
|
110
167
|
setup do
|
111
168
|
@mail.deliver
|
metadata
CHANGED
@@ -1,64 +1,58 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mail-gpg
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1
|
5
|
-
prerelease:
|
4
|
+
version: 0.2.1
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Jens Kraemer
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date: 2014-
|
11
|
+
date: 2014-07-23 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: mail
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
17
|
- - ~>
|
20
18
|
- !ruby/object:Gem::Version
|
21
19
|
version: '2.5'
|
22
|
-
- -
|
20
|
+
- - '>='
|
23
21
|
- !ruby/object:Gem::Version
|
24
22
|
version: 2.5.3
|
25
23
|
type: :runtime
|
26
24
|
prerelease: false
|
27
25
|
version_requirements: !ruby/object:Gem::Requirement
|
28
|
-
none: false
|
29
26
|
requirements:
|
30
27
|
- - ~>
|
31
28
|
- !ruby/object:Gem::Version
|
32
29
|
version: '2.5'
|
33
|
-
- -
|
30
|
+
- - '>='
|
34
31
|
- !ruby/object:Gem::Version
|
35
32
|
version: 2.5.3
|
36
33
|
- !ruby/object:Gem::Dependency
|
37
34
|
name: gpgme
|
38
35
|
requirement: !ruby/object:Gem::Requirement
|
39
|
-
none: false
|
40
36
|
requirements:
|
41
37
|
- - ~>
|
42
38
|
- !ruby/object:Gem::Version
|
43
39
|
version: '2.0'
|
44
|
-
- -
|
40
|
+
- - '>='
|
45
41
|
- !ruby/object:Gem::Version
|
46
42
|
version: 2.0.2
|
47
43
|
type: :runtime
|
48
44
|
prerelease: false
|
49
45
|
version_requirements: !ruby/object:Gem::Requirement
|
50
|
-
none: false
|
51
46
|
requirements:
|
52
47
|
- - ~>
|
53
48
|
- !ruby/object:Gem::Version
|
54
49
|
version: '2.0'
|
55
|
-
- -
|
50
|
+
- - '>='
|
56
51
|
- !ruby/object:Gem::Version
|
57
52
|
version: 2.0.2
|
58
53
|
- !ruby/object:Gem::Dependency
|
59
54
|
name: bundler
|
60
55
|
requirement: !ruby/object:Gem::Requirement
|
61
|
-
none: false
|
62
56
|
requirements:
|
63
57
|
- - ~>
|
64
58
|
- !ruby/object:Gem::Version
|
@@ -66,7 +60,6 @@ dependencies:
|
|
66
60
|
type: :development
|
67
61
|
prerelease: false
|
68
62
|
version_requirements: !ruby/object:Gem::Requirement
|
69
|
-
none: false
|
70
63
|
requirements:
|
71
64
|
- - ~>
|
72
65
|
- !ruby/object:Gem::Version
|
@@ -74,7 +67,6 @@ dependencies:
|
|
74
67
|
- !ruby/object:Gem::Dependency
|
75
68
|
name: minitest
|
76
69
|
requirement: !ruby/object:Gem::Requirement
|
77
|
-
none: false
|
78
70
|
requirements:
|
79
71
|
- - ~>
|
80
72
|
- !ruby/object:Gem::Version
|
@@ -82,7 +74,6 @@ dependencies:
|
|
82
74
|
type: :development
|
83
75
|
prerelease: false
|
84
76
|
version_requirements: !ruby/object:Gem::Requirement
|
85
|
-
none: false
|
86
77
|
requirements:
|
87
78
|
- - ~>
|
88
79
|
- !ruby/object:Gem::Version
|
@@ -90,55 +81,48 @@ dependencies:
|
|
90
81
|
- !ruby/object:Gem::Dependency
|
91
82
|
name: rake
|
92
83
|
requirement: !ruby/object:Gem::Requirement
|
93
|
-
none: false
|
94
84
|
requirements:
|
95
|
-
- -
|
85
|
+
- - '>='
|
96
86
|
- !ruby/object:Gem::Version
|
97
87
|
version: '0'
|
98
88
|
type: :development
|
99
89
|
prerelease: false
|
100
90
|
version_requirements: !ruby/object:Gem::Requirement
|
101
|
-
none: false
|
102
91
|
requirements:
|
103
|
-
- -
|
92
|
+
- - '>='
|
104
93
|
- !ruby/object:Gem::Version
|
105
94
|
version: '0'
|
106
95
|
- !ruby/object:Gem::Dependency
|
107
96
|
name: actionmailer
|
108
97
|
requirement: !ruby/object:Gem::Requirement
|
109
|
-
none: false
|
110
98
|
requirements:
|
111
|
-
- -
|
99
|
+
- - '>='
|
112
100
|
- !ruby/object:Gem::Version
|
113
101
|
version: 3.2.0
|
114
102
|
type: :development
|
115
103
|
prerelease: false
|
116
104
|
version_requirements: !ruby/object:Gem::Requirement
|
117
|
-
none: false
|
118
105
|
requirements:
|
119
|
-
- -
|
106
|
+
- - '>='
|
120
107
|
- !ruby/object:Gem::Version
|
121
108
|
version: 3.2.0
|
122
109
|
- !ruby/object:Gem::Dependency
|
123
110
|
name: pry-nav
|
124
111
|
requirement: !ruby/object:Gem::Requirement
|
125
|
-
none: false
|
126
112
|
requirements:
|
127
|
-
- -
|
113
|
+
- - '>='
|
128
114
|
- !ruby/object:Gem::Version
|
129
115
|
version: '0'
|
130
116
|
type: :development
|
131
117
|
prerelease: false
|
132
118
|
version_requirements: !ruby/object:Gem::Requirement
|
133
|
-
none: false
|
134
119
|
requirements:
|
135
|
-
- -
|
120
|
+
- - '>='
|
136
121
|
- !ruby/object:Gem::Version
|
137
122
|
version: '0'
|
138
123
|
- !ruby/object:Gem::Dependency
|
139
124
|
name: shoulda-context
|
140
125
|
requirement: !ruby/object:Gem::Requirement
|
141
|
-
none: false
|
142
126
|
requirements:
|
143
127
|
- - ~>
|
144
128
|
- !ruby/object:Gem::Version
|
@@ -146,15 +130,13 @@ dependencies:
|
|
146
130
|
type: :development
|
147
131
|
prerelease: false
|
148
132
|
version_requirements: !ruby/object:Gem::Requirement
|
149
|
-
none: false
|
150
133
|
requirements:
|
151
134
|
- - ~>
|
152
135
|
- !ruby/object:Gem::Version
|
153
136
|
version: '1.1'
|
154
|
-
description:
|
155
|
-
|
156
|
-
This tiny gem adds GPG capabilities to Mail::Message and ActionMailer::Base. Because
|
157
|
-
privacy matters.'
|
137
|
+
description: |-
|
138
|
+
GPG/MIME encryption plugin for the Ruby Mail Library
|
139
|
+
This tiny gem adds GPG capabilities to Mail::Message and ActionMailer::Base. Because privacy matters.
|
158
140
|
email:
|
159
141
|
- jk@jkraemer.net
|
160
142
|
executables: []
|
@@ -174,13 +156,19 @@ files:
|
|
174
156
|
- lib/mail/gpg/decrypted_part.rb
|
175
157
|
- lib/mail/gpg/delivery_handler.rb
|
176
158
|
- lib/mail/gpg/encrypted_part.rb
|
159
|
+
- lib/mail/gpg/gpgme_ext.rb
|
177
160
|
- lib/mail/gpg/gpgme_helper.rb
|
178
161
|
- lib/mail/gpg/inline_decrypted_message.rb
|
162
|
+
- lib/mail/gpg/inline_signed_message.rb
|
179
163
|
- lib/mail/gpg/message_patch.rb
|
164
|
+
- lib/mail/gpg/mime_signed_message.rb
|
165
|
+
- lib/mail/gpg/missing_keys_error.rb
|
180
166
|
- lib/mail/gpg/rails.rb
|
181
167
|
- lib/mail/gpg/rails/action_mailer_base_patch.rb
|
182
168
|
- lib/mail/gpg/sign_part.rb
|
183
169
|
- lib/mail/gpg/signed_part.rb
|
170
|
+
- lib/mail/gpg/verified_part.rb
|
171
|
+
- lib/mail/gpg/verify_result_attribute.rb
|
184
172
|
- lib/mail/gpg/version.rb
|
185
173
|
- lib/mail/gpg/version_part.rb
|
186
174
|
- mail-gpg.gemspec
|
@@ -195,6 +183,7 @@ files:
|
|
195
183
|
- test/gpghome/trustdb.gpg
|
196
184
|
- test/hkp_test.rb
|
197
185
|
- test/inline_decrypted_message_test.rb
|
186
|
+
- test/inline_signed_message_test.rb
|
198
187
|
- test/message_test.rb
|
199
188
|
- test/sign_part_test.rb
|
200
189
|
- test/test_helper.rb
|
@@ -202,27 +191,26 @@ files:
|
|
202
191
|
homepage: https://github.com/jkraemer/mail-gpg
|
203
192
|
licenses:
|
204
193
|
- MIT
|
194
|
+
metadata: {}
|
205
195
|
post_install_message:
|
206
196
|
rdoc_options: []
|
207
197
|
require_paths:
|
208
198
|
- lib
|
209
199
|
required_ruby_version: !ruby/object:Gem::Requirement
|
210
|
-
none: false
|
211
200
|
requirements:
|
212
|
-
- -
|
201
|
+
- - '>='
|
213
202
|
- !ruby/object:Gem::Version
|
214
203
|
version: '0'
|
215
204
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
216
|
-
none: false
|
217
205
|
requirements:
|
218
|
-
- -
|
206
|
+
- - '>='
|
219
207
|
- !ruby/object:Gem::Version
|
220
208
|
version: '0'
|
221
209
|
requirements: []
|
222
210
|
rubyforge_project:
|
223
|
-
rubygems_version:
|
211
|
+
rubygems_version: 2.2.2
|
224
212
|
signing_key:
|
225
|
-
specification_version:
|
213
|
+
specification_version: 4
|
226
214
|
summary: GPG/MIME encryption plugin for the Ruby Mail Library
|
227
215
|
test_files:
|
228
216
|
- test/action_mailer_test.rb
|
@@ -236,7 +224,9 @@ test_files:
|
|
236
224
|
- test/gpghome/trustdb.gpg
|
237
225
|
- test/hkp_test.rb
|
238
226
|
- test/inline_decrypted_message_test.rb
|
227
|
+
- test/inline_signed_message_test.rb
|
239
228
|
- test/message_test.rb
|
240
229
|
- test/sign_part_test.rb
|
241
230
|
- test/test_helper.rb
|
242
231
|
- test/version_part_test.rb
|
232
|
+
has_rdoc:
|