mail-gpg 0.1.7 → 0.2.1
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 +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:
|