mail-gpg 0.0.6 → 0.1.0

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.
data/README.md CHANGED
@@ -3,6 +3,13 @@
3
3
  This gem adds GPG/MIME encryption capabilities to the [Ruby Mail
4
4
  Library](https://github.com/mikel/mail)
5
5
 
6
+ For maximum interoperability the gem also supports *decryption* of messages using the non-standard 'PGP-Inline' method
7
+ as for example supported in the Mozilla Enigmail OpenPGP plugin.
8
+
9
+ There may still be GPG encrypted messages that can not be handled by the library, as there are some legacy formats used in the
10
+ wild as described in this [Know your PGP implementation](http://binblog.info/2008/03/12/know-your-pgp-implementation/) blog.
11
+
12
+
6
13
  ## Installation
7
14
 
8
15
  Add this line to your application's Gemfile:
@@ -75,11 +82,65 @@ updating it's db when necessary.
75
82
  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.
76
83
 
77
84
 
85
+ ### Decrypting
86
+
87
+ Receive the mail as usual. Check if it is encrypted using the `encrypted?` method. Get a decrypted version of the mail with the `decrypt` method:
88
+
89
+ ```ruby
90
+ mail = Mail.first
91
+ mail.subject # subject is never encrypted
92
+ if mail.encrypted?
93
+ # decrypt using your private key, protected by the given passphrase
94
+ plaintext_mail = mail.decrypt(:password => 'abc')
95
+ # the plaintext_mail, is a full Mail::Message object, just decrypted
96
+ end
97
+ ```
98
+
99
+ A `GPGME::Error::BadPassphrase` will be raised if the password for the private key is incorrect.
100
+ 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.
101
+
102
+
78
103
  ### Signing only
79
104
 
80
- This is not implemented yet
105
+ Just leave the the `:encrypt` option out or pass `encrypt: false`, i.e.
106
+
107
+
108
+ Mail.new do
109
+ to 'jane@doe.net'
110
+ gpg sign: true
111
+ end.deliver
81
112
 
82
113
 
114
+ ### Key import from public key servers
115
+
116
+ The Hkp class can be used to lookup and import public keys from public key servers.
117
+ You can specify the keyserver url when initializing the class:
118
+
119
+ ```
120
+ hkp = Hkp.new("hkp://my-key-server.de")
121
+ ```
122
+
123
+ If no url is given, this gem will try to determine the default keyserver
124
+ url from the system's gpg config (using `gpgconf` if available or by
125
+ parsing the `gpg.conf` file). As a last resort, the server-pool at
126
+ `http://pool.sks-keyservers.net:11371` will be used.
127
+
128
+ Lookup key ids by searching the keyserver for an email address
129
+
130
+ ```
131
+ hkp.search('jane@doe.net')
132
+ ```
133
+
134
+ You can lookup (and import) a specific key by its id:
135
+
136
+ ```
137
+ key = hkp.fetch(id)
138
+ GPGME::Key.import(key)
139
+
140
+ # or do both in one step
141
+ hkp.fetch_and_import(id)
142
+ ```
143
+
83
144
  ## Rails / ActionMailer integration
84
145
 
85
146
  class MyMailer < ActionMailer::Base
@@ -102,8 +163,7 @@ around with your personal gpg keychain.
102
163
 
103
164
  ## Todo
104
165
 
105
- * Signing of unencrypted mails
106
- * Decryption and signature verification for received mails
166
+ * signature verification for received mails
107
167
  * on the fly import of recipients' keys from public key servers based on email address or key id
108
168
  * handle encryption errors due to missing keys - maybe return a list of failed
109
169
  recipients
@@ -120,3 +180,9 @@ around with your personal gpg keychain.
120
180
  5. Create new Pull Request
121
181
 
122
182
 
183
+ ## Credits
184
+
185
+ Thanks to:
186
+
187
+ * [morten-andersen](https://github.com/morten-andersen) for implementing decryption support for PGP/MIME and inline encrypted messages
188
+ * [FewKinG](https://github.com/FewKinG) for implementing the sign only featur and keyserver url lookup
data/lib/hkp.rb CHANGED
@@ -3,8 +3,8 @@ require 'gpgme'
3
3
 
4
4
  # simple HKP client for public key retrieval
5
5
  class Hkp
6
- def initialize(keyserver = 'http://pool.sks-keyservers.net:11371')
7
- @keyserver = keyserver
6
+ def initialize(keyserver = nil)
7
+ @keyserver = keyserver || lookup_keyserver || 'http://pool.sks-keyservers.net:11371'
8
8
  end
9
9
 
10
10
  # hkp.search 'user@host.com'
@@ -50,4 +50,23 @@ class Hkp
50
50
  end
51
51
  end
52
52
 
53
+ def exec_cmd(cmd)
54
+ res = `#{cmd}`
55
+ return nil if $?.exitstatus != 0
56
+ res
57
+ end
58
+
59
+ def lookup_keyserver
60
+ url = nil
61
+ if res = exec_cmd("gpgconf --list-options gpgs 2>&1 | grep keyserver 2>&1")
62
+ url = URI.decode(res.split(":").last.split("\"").last.strip)
63
+ elsif res = exec_cmd("gpg --gpgconf-list 2>&1 | grep gpgconf-gpg.conf 2>&1")
64
+ conf_file = res.split(":").last.split("\"").last.strip
65
+ if res = exec_cmd("cat #{conf_file} 2>&1 | grep ^keyserver 2>&1")
66
+ url = res.split(" ").last.strip
67
+ end
68
+ end
69
+ url =~ /^(http|hkp)/ ? url : nil
70
+ end
71
+
53
72
  end
@@ -0,0 +1,18 @@
1
+ module Mail
2
+ module Gpg
3
+ class DecryptedPart < Mail::Part
4
+
5
+ # options are:
6
+ #
7
+ # :verify: decrypt and verify
8
+ def initialize(cipher_part, options = {})
9
+ if cipher_part.mime_type != EncryptedPart::CONTENT_TYPE
10
+ raise EncodingError, "RFC 3136 incorrect mime type for encrypted part '#{cipher_part.mime_type}'"
11
+ end
12
+
13
+ decrypted = GpgmeHelper.decrypt(cipher_part.body.decoded, options)
14
+ super(decrypted)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -10,7 +10,7 @@ module Mail
10
10
  if options.delete(:encrypt)
11
11
  encrypted_mail = Mail::Gpg.encrypt(mail, options)
12
12
  elsif options[:sign] || options[:sign_as]
13
- raise "signing without encryption is not supported yet"
13
+ encrypted_mail = Mail::Gpg.sign(mail, options)
14
14
  else
15
15
  # encrypt and sign are off -> do not encrypt or sign
16
16
  yield
@@ -2,10 +2,12 @@ module Mail
2
2
  module Gpg
3
3
  class EncryptedPart < Mail::Part
4
4
 
5
+ CONTENT_TYPE = 'application/octet-stream'
6
+
5
7
  # options are:
6
8
  #
7
9
  # :signers : sign using this key (give the corresponding email address)
8
- # :passphrase: passphrase for the signing key
10
+ # :password: passphrase for the signing key
9
11
  # :recipients : array of receiver addresses
10
12
  # :keys : A hash mapping recipient email addresses to public keys or public
11
13
  # key ids. Imports any keys given here that are not already part of the
@@ -14,70 +16,14 @@ module Mail
14
16
  def initialize(cleartext_mail, options = {})
15
17
  options = { always_trust: true }.merge options
16
18
 
17
- encrypted = encrypt(cleartext_mail.encoded, options)
19
+ encrypted = GpgmeHelper.encrypt(cleartext_mail.encoded, options)
18
20
  super() do
19
21
  body encrypted.to_s
20
- content_type 'application/octet-stream; name="encrypted.asc"'
22
+ content_type "#{CONTENT_TYPE}; name=\"encrypted.asc\""
21
23
  content_disposition 'inline; filename="encrypted.asc"'
22
24
  content_description 'OpenPGP encrypted message'
23
25
  end
24
26
  end
25
-
26
- private
27
-
28
- def encrypt(plain, options = {})
29
- options = options.merge({armor: true})
30
-
31
- plain_data = GPGME::Data.new(plain)
32
- cipher_data = GPGME::Data.new(options[:output])
33
-
34
- recipient_keys = keys_for_data options[:recipients], options.delete(:keys)
35
-
36
- flags = 0
37
- flags |= GPGME::ENCRYPT_ALWAYS_TRUST if options[:always_trust]
38
-
39
- GPGME::Ctx.new(options) do |ctx|
40
- begin
41
- if options[:sign]
42
- if options[:signers]
43
- signers = Key.find(:public, options[:signers], :sign)
44
- ctx.add_signer(*signers)
45
- end
46
- ctx.encrypt_sign(recipient_keys, plain_data, cipher_data, flags)
47
- else
48
- ctx.encrypt(recipient_keys, plain_data, cipher_data, flags)
49
- end
50
- rescue GPGME::Error::UnusablePublicKey => exc
51
- exc.keys = ctx.encrypt_result.invalid_recipients
52
- raise exc
53
- rescue GPGME::Error::UnusableSecretKey => exc
54
- exc.keys = ctx.sign_result.invalid_signers
55
- raise exc
56
- end
57
- end
58
-
59
- cipher_data.seek(0)
60
- cipher_data
61
- end
62
-
63
- # normalizes the list of recipients' emails, key ids and key data to a
64
- # list of Key objects
65
- def keys_for_data(emails_or_shas_or_keys, key_data = nil)
66
- if key_data
67
- [emails_or_shas_or_keys].flatten.map do |r|
68
- # import any given keys
69
- k = key_data[r]
70
- if k and k =~ /-----BEGIN PGP/
71
- k = GPGME::Key.import(k).imports.map(&:fpr)
72
- end
73
- GPGME::Key.find(:public, k || r, :encrypt)
74
- end.flatten
75
- else
76
- # key lookup in keychain for all receivers
77
- GPGME::Key.find :public, emails_or_shas_or_keys, :encrypt
78
- end
79
- end
80
-
81
27
  end
82
28
  end
83
29
  end
@@ -0,0 +1,96 @@
1
+ # GPGME methods for encryption/decryption/signing
2
+ module Mail
3
+ module Gpg
4
+ class GpgmeHelper
5
+
6
+ def self.encrypt(plain, options = {})
7
+ options = options.merge({armor: true})
8
+
9
+ plain_data = GPGME::Data.new(plain)
10
+ cipher_data = GPGME::Data.new(options[:output])
11
+
12
+ recipient_keys = keys_for_data options[:recipients], options.delete(:keys)
13
+
14
+ flags = 0
15
+ flags |= GPGME::ENCRYPT_ALWAYS_TRUST if options[:always_trust]
16
+
17
+ GPGME::Ctx.new(options) do |ctx|
18
+ begin
19
+ if options[:sign]
20
+ if options[:signers]
21
+ signers = GPGME::Key.find(:public, options[:signers], :sign)
22
+ ctx.add_signer(*signers)
23
+ end
24
+ ctx.encrypt_sign(recipient_keys, plain_data, cipher_data, flags)
25
+ else
26
+ ctx.encrypt(recipient_keys, plain_data, cipher_data, flags)
27
+ end
28
+ rescue GPGME::Error::UnusablePublicKey => exc
29
+ exc.keys = ctx.encrypt_result.invalid_recipients
30
+ raise exc
31
+ rescue GPGME::Error::UnusableSecretKey => exc
32
+ exc.keys = ctx.sign_result.invalid_signers
33
+ raise exc
34
+ end
35
+ end
36
+
37
+ cipher_data.seek(0)
38
+ cipher_data
39
+ end
40
+
41
+ def self.decrypt(cipher, options = {})
42
+ cipher_data = GPGME::Data.new(cipher)
43
+ plain_data = GPGME::Data.new(options[:output])
44
+
45
+ GPGME::Ctx.new(options) do |ctx|
46
+ begin
47
+ if options[:verify]
48
+ ctx.decrypt_verify(cipher_data, plain_data)
49
+ else
50
+ ctx.decrypt(cipher_data, plain_data)
51
+ end
52
+ rescue GPGME::Error::UnsupportedAlgorithm => exc
53
+ exc.algorithm = ctx.decrypt_result.unsupported_algorithm
54
+ raise exc
55
+ rescue GPGME::Error::WrongKeyUsage => exc
56
+ exc.key_usage = ctx.decrypt_result.wrong_key_usage
57
+ raise exc
58
+ end
59
+ end
60
+
61
+ plain_data.seek(0)
62
+ plain_data
63
+ end
64
+
65
+ def self.sign(plain, options = {})
66
+ options.merge!({
67
+ armor: true,
68
+ signer: options.delete(:sign_as),
69
+ mode: GPGME::SIG_MODE_DETACH
70
+ })
71
+ crypto = GPGME::Crypto.new
72
+ crypto.sign GPGME::Data.new(plain), options
73
+ end
74
+
75
+ private
76
+
77
+ # normalizes the list of recipients' emails, key ids and key data to a
78
+ # list of Key objects
79
+ def self.keys_for_data(emails_or_shas_or_keys, key_data = nil)
80
+ if key_data
81
+ [emails_or_shas_or_keys].flatten.map do |r|
82
+ # import any given keys
83
+ k = key_data[r]
84
+ if k and k =~ /-----BEGIN PGP/
85
+ k = GPGME::Key.import(k).imports.map(&:fpr)
86
+ end
87
+ GPGME::Key.find(:public, k || r, :encrypt)
88
+ end.flatten
89
+ else
90
+ # key lookup in keychain for all receivers
91
+ GPGME::Key.find :public, emails_or_shas_or_keys, :encrypt
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,54 @@
1
+ # decryption of the so called 'PGP-Inline' message types
2
+ # this is not a standard, so the implementation is based on the notes
3
+ # here http://binblog.info/2008/03/12/know-your-pgp-implementation/
4
+ # and on test messages generated with the Mozilla Enigmail OpenPGP
5
+ # plugin https://www.enigmail.net
6
+ module Mail
7
+ module Gpg
8
+ class InlineDecryptedMessage < Mail::Message
9
+
10
+ # options are:
11
+ #
12
+ # :verify: decrypt and verify
13
+ def initialize(cipher_mail, options = {})
14
+ if cipher_mail.multipart?
15
+ super() do
16
+ cipher_mail.header.fields.each do |field|
17
+ header[field.name] = field.value
18
+ end
19
+ cipher_mail.parts.each do |part|
20
+ part Mail::Part.new do |p|
21
+ if part.has_content_type? && /application\/(?:octet-stream|pgp-encrypted)/ =~ part.mime_type
22
+ # encrypted attachment, we set the content_type to the generic 'application/octet-stream'
23
+ # and remove the .pgp/gpg/asc from name/filename in header fields
24
+ decrypted = GpgmeHelper.decrypt(part.decoded, options)
25
+ p.content_type part.content_type.sub(/application\/(?:octet-stream|pgp-encrypted)/, 'application/octet-stream')
26
+ .sub(/name=(?:"')?(.*)\.(?:pgp|gpg|asc)(?:"')?/, 'name="\1"')
27
+ p.content_disposition part.content_disposition.sub(/filename=(?:"')?(.*)\.(?:pgp|gpg|asc)(?:"')?/, 'filename="\1"')
28
+ p.content_transfer_encoding Mail::Encodings::Base64
29
+ p.body Mail::Encodings::Base64::encode(decrypted.to_s)
30
+ else
31
+ if part.body.include?('-----BEGIN PGP MESSAGE-----')
32
+ p.body GpgmeHelper.decrypt(part.decoded, options).to_s
33
+ else
34
+ p.content_type part.content_type
35
+ p.content_transfer_encoding part.content_transfer_encoding
36
+ p.body part.body.to_s
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end # of multipart
42
+ else
43
+ decrypted = cipher_mail.body.empty? ? '' : GpgmeHelper.decrypt(cipher_mail.body.decoded, options)
44
+ super() do
45
+ cipher_mail.header.fields.each do |field|
46
+ header[field.name] = field.value
47
+ end
48
+ body decrypted.to_s
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -51,6 +51,14 @@ module Mail
51
51
  end
52
52
  end
53
53
 
54
+ def encrypted?
55
+ Mail::Gpg.encrypted?(self)
56
+ end
57
+
58
+ def decrypt(options = {})
59
+ Mail::Gpg.decrypt(self, options)
60
+ end
61
+
54
62
  end
55
63
  end
56
64
  end
@@ -0,0 +1,16 @@
1
+ module Mail
2
+ module Gpg
3
+ class SignPart < Mail::Part
4
+
5
+ def initialize(cleartext_mail, options = {})
6
+ signature = GpgmeHelper.sign(cleartext_mail.encoded, options)
7
+ super() do
8
+ body signature.to_s
9
+ content_type "application/pgp-signature; name=\"signature.asc\""
10
+ content_disposition 'attachment; filename="signature.asc"'
11
+ content_description 'OpenPGP digital signature'
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -1,5 +1,5 @@
1
1
  module Mail
2
2
  module Gpg
3
- VERSION = "0.0.6"
3
+ VERSION = "0.1.0"
4
4
  end
5
5
  end
@@ -3,11 +3,19 @@ require 'mail/part'
3
3
  module Mail
4
4
  module Gpg
5
5
  class VersionPart < Mail::Part
6
+ VERSION_1 = 'Version: 1'
7
+ CONTENT_TYPE = 'application/pgp-encrypted'
8
+ CONTENT_DESC = 'PGP/MIME Versions Identification'
9
+
6
10
  def initialize(*args)
7
11
  super
8
- body 'Version: 1'
9
- content_type 'application/pgp-encrypted'
10
- content_description 'PGP/MIME Versions Identification'
12
+ body VERSION_1
13
+ content_type CONTENT_TYPE
14
+ content_description CONTENT_DESC
15
+ end
16
+
17
+ def self.isVersionPart?(part)
18
+ part.mime_type == CONTENT_TYPE && part.body =~ /#{VERSION_1}/
11
19
  end
12
20
  end
13
21
  end
data/lib/mail/gpg.rb CHANGED
@@ -4,9 +4,13 @@ require 'gpgme'
4
4
 
5
5
  require 'mail/gpg/version'
6
6
  require 'mail/gpg/version_part'
7
+ require 'mail/gpg/decrypted_part'
7
8
  require 'mail/gpg/encrypted_part'
9
+ require 'mail/gpg/inline_decrypted_message'
10
+ require 'mail/gpg/gpgme_helper'
8
11
  require 'mail/gpg/message_patch'
9
12
  require 'mail/gpg/rails'
13
+ require 'mail/gpg/sign_part'
10
14
 
11
15
  module Mail
12
16
  module Gpg
@@ -19,18 +23,57 @@ module Mail
19
23
  # local keychain before sending the mail.
20
24
  # :always_trust : send encrypted mail to untrusted receivers, true by default
21
25
  def self.encrypt(cleartext_mail, options = {})
22
- receivers = []
23
- receivers += cleartext_mail.to if cleartext_mail.to
24
- receivers += cleartext_mail.cc if cleartext_mail.cc
25
- receivers += cleartext_mail.bcc if cleartext_mail.bcc
26
-
27
- if options[:sign_as]
28
- options[:sign] = true
29
- options[:signers] = options.delete(:sign_as)
30
- elsif options[:sign]
31
- options[:signers] = cleartext_mail.from
26
+ construct_mail(cleartext_mail, options) do
27
+ receivers = []
28
+ receivers += cleartext_mail.to if cleartext_mail.to
29
+ receivers += cleartext_mail.cc if cleartext_mail.cc
30
+ receivers += cleartext_mail.bcc if cleartext_mail.bcc
31
+
32
+ if options[:sign_as]
33
+ options[:sign] = true
34
+ options[:signers] = options.delete(:sign_as)
35
+ elsif options[:sign]
36
+ options[:signers] = cleartext_mail.from
37
+ end
38
+
39
+ add_part VersionPart.new
40
+ add_part EncryptedPart.new(cleartext_mail,
41
+ options.merge({recipients: receivers}))
42
+ content_type "multipart/encrypted; protocol=\"application/pgp-encrypted\"; boundary=#{boundary}"
43
+ body.preamble = options[:preamble] || "This is an OpenPGP/MIME encrypted message (RFC 2440 and 3156)"
32
44
  end
45
+ end
46
+
47
+ def self.sign(cleartext_mail, options = {})
48
+ construct_mail(cleartext_mail, options) do
49
+ options[:sign_as] ||= cleartext_mail.from
50
+ add_part SignPart.new(cleartext_mail, options)
51
+ add_part Mail::Part.new(cleartext_mail)
52
+
53
+ content_type "multipart/signed; micalg=pgp-sha1; protocol=\"application/pgp-signature\"; boundary=#{boundary}"
54
+ body.preamble = options[:preamble] || "This is an OpenPGP/MIME signed message (RFC 4880 and 3156)"
55
+ end
56
+ end
57
+
58
+ def self.decrypt(encrypted_mail, options = {})
59
+ if encrypted_mime?(encrypted_mail)
60
+ decrypt_pgp_mime(encrypted_mail, options)
61
+ elsif encrypted_inline?(encrypted_mail)
62
+ decrypt_pgp_inline(encrypted_mail, options)
63
+ else
64
+ raise EncodingError, "Unsupported encryption format '#{encrypted_mail.content_type}'"
65
+ end
66
+ end
67
+
68
+ def self.encrypted?(mail)
69
+ return true if encrypted_mime?(mail)
70
+ return true if encrypted_inline?(mail)
71
+ false
72
+ end
33
73
 
74
+ private
75
+
76
+ def self.construct_mail(cleartext_mail, options, &block)
34
77
  Mail.new do
35
78
  self.perform_deliveries = cleartext_mail.perform_deliveries
36
79
  %w(from to cc bcc subject reply_to in_reply_to).each do |field|
@@ -39,16 +82,47 @@ module Mail
39
82
  cleartext_mail.header.fields.each do |field|
40
83
  header[field.name] = field.value if field.name =~ /^X-/
41
84
  end
42
- add_part VersionPart.new
43
- add_part EncryptedPart.new(cleartext_mail,
44
- options.merge({recipients: receivers}))
45
- content_type "multipart/encrypted; protocol=\"application/pgp-encrypted\"; boundary=#{boundary}"
46
- body.preamble = options[:preamble] || "This is an OpenPGP/MIME encrypted message (RFC 2440 and 3156)"
85
+ instance_eval &block
86
+ end
87
+ end
88
+
89
+ # decrypts PGP/MIME (RFC 3156, section 4) encrypted mail
90
+ def self.decrypt_pgp_mime(encrypted_mail, options)
91
+ # MUST containt exactly two body parts
92
+ if encrypted_mail.parts.length != 2
93
+ raise EncodingError, "RFC 3136 mandates exactly two body parts, found '#{encrypted_mail.parts.length}'"
94
+ end
95
+ if !VersionPart.isVersionPart? encrypted_mail.parts[0]
96
+ raise EncodingError, "RFC 3136 first part not a valid version part '#{encrypted_mail.parts[0]}'"
47
97
  end
98
+ Mail.new(DecryptedPart.new(encrypted_mail.parts[1], options))
48
99
  end
49
100
 
50
- def self.decrypt(encrypted_mail, options = {})
51
- # TODO :)
101
+ # decrypts inline PGP encrypted mail
102
+ def self.decrypt_pgp_inline(encrypted_mail, options)
103
+ InlineDecryptedMessage.new(encrypted_mail, options)
104
+ end
105
+
106
+ # check if PGP/MIME (RFC 3156)
107
+ def self.encrypted_mime?(mail)
108
+ mail.has_content_type? &&
109
+ 'multipart/encrypted' == mail.mime_type &&
110
+ 'application/pgp-encrypted' == mail.content_type_parameters[:protocol]
111
+ end
112
+
113
+ # check if inline PGP (i.e. if any parts of the mail includes
114
+ # the PGP MESSAGE marker
115
+ def self.encrypted_inline?(mail)
116
+ return true if mail.body.include?('-----BEGIN PGP MESSAGE-----')
117
+ if mail.multipart?
118
+ mail.parts.each do |part|
119
+ return true if part.body.include?('-----BEGIN PGP MESSAGE-----')
120
+ return true if part.has_content_type? &&
121
+ /application\/(?:octet-stream|pgp-encrypted)/ =~ part.mime_type &&
122
+ /.*\.(?:pgp|gpg|asc)$/ =~ part.content_type_parameters[:name]
123
+ end
124
+ end
125
+ false
52
126
  end
53
127
  end
54
128
  end
@@ -11,6 +11,16 @@ class MyMailer < ActionMailer::Base
11
11
  mail subject: 'encrypted', body: 'encrypted mail', gpg: {encrypt: true}
12
12
  end
13
13
 
14
+ def signed
15
+ mail from: 'jane@foo.bar',
16
+ to: 'joe@foo.bar',
17
+ subject: 'signed',
18
+ body: 'signed mail',
19
+ gpg: {
20
+ sign: true,
21
+ password: 'abc'
22
+ }
23
+ end
14
24
 
15
25
  end
16
26
 
@@ -42,5 +52,19 @@ class ActionMailerTest < Test::Unit::TestCase
42
52
  assert_equal 'encrypted mail', m.body.to_s
43
53
  end
44
54
 
55
+ should "send signed mail" do
56
+ assert m = MyMailer.signed
57
+ assert true == m.gpg[:sign]
58
+ m.deliver
59
+ assert_equal 1, @emails.size
60
+ assert delivered = @emails.first
61
+ assert_equal 'signed', delivered.subject
62
+ assert_equal 2, delivered.parts.size
63
+ assert sign_part = delivered.parts.detect{|p| p.content_type =~ /signature\.asc/}
64
+ GPGME::Crypto.new.verify(sign_part.body.to_s, signed_text: m.encoded) do |sig|
65
+ assert true == sig.valid?
66
+ end
67
+ end
68
+
45
69
  end
46
70
  end
@@ -0,0 +1,37 @@
1
+ require 'test_helper'
2
+ require 'mail/gpg/decrypted_part'
3
+ require 'mail/gpg/encrypted_part'
4
+
5
+ class DecryptedPartTest < Test::Unit::TestCase
6
+ context 'DecryptedPart' do
7
+ setup do
8
+ @mail = Mail.new do
9
+ to 'jane@foo.bar'
10
+ from 'joe@foo.bar'
11
+ subject 'test'
12
+ body 'i am unencrypted'
13
+ end
14
+ @part = Mail::Gpg::EncryptedPart.new(@mail, { :sign => true, :password => 'abc' })
15
+ end
16
+
17
+ should 'decrypt' do
18
+ assert mail = Mail::Gpg::DecryptedPart.new(@part, { :password => 'abc' })
19
+ assert mail == @mail
20
+ assert mail.message_id == @mail.message_id
21
+ assert mail.message_id != @part.message_id
22
+ end
23
+
24
+ should 'decrypt and verify' do
25
+ assert mail = Mail::Gpg::DecryptedPart.new(@part, { :verify => true, :password => 'abc' })
26
+ assert mail == @mail
27
+ assert mail.message_id == @mail.message_id
28
+ assert mail.message_id != @part.message_id
29
+ end
30
+
31
+ should 'raise encoding error for non gpg mime type' do
32
+ part = Mail::Part.new(@part)
33
+ part.content_type = 'text/plain'
34
+ assert_raise(EncodingError) { Mail::Gpg::DecryptedPart.new(part) }
35
+ end
36
+ end
37
+ end
@@ -1,8 +1,14 @@
1
1
  require 'test_helper'
2
2
  require 'mail/gpg/encrypted_part'
3
- require 'pry-nav'
4
3
 
5
4
  class EncryptedPartTest < Test::Unit::TestCase
5
+
6
+ def check_key_list(keys)
7
+ assert_equal 1, keys.size
8
+ assert_equal GPGME::Key, keys.first.class
9
+ assert_equal 'jane@foo.bar', keys.first.email
10
+ end
11
+
6
12
  context 'EncryptedPart' do
7
13
  setup do
8
14
  mail = Mail.new do
@@ -14,24 +20,18 @@ class EncryptedPartTest < Test::Unit::TestCase
14
20
  @part = Mail::Gpg::EncryptedPart.new(mail)
15
21
  end
16
22
 
17
- def check_key_list(keys)
18
- assert_equal 1, keys.size
19
- assert_equal GPGME::Key, keys.first.class
20
- assert_equal 'jane@foo.bar', keys.first.email
21
- end
22
-
23
23
  context 'with email address' do
24
24
  setup do
25
25
  @email = 'jane@foo.bar'
26
26
  end
27
27
 
28
28
  should 'resolve email to gpg keys' do
29
- assert keys = @part.send(:keys_for_data, @email)
29
+ assert keys = Mail::Gpg::GpgmeHelper.send(:keys_for_data, @email)
30
30
  check_key_list keys
31
31
  end
32
32
 
33
33
  should 'resolve emails to gpg keys' do
34
- assert keys = @part.send(:keys_for_data, [@email])
34
+ assert keys = Mail::Gpg::GpgmeHelper.send(:keys_for_data, [@email])
35
35
  check_key_list keys
36
36
  end
37
37
 
@@ -43,11 +43,11 @@ class EncryptedPartTest < Test::Unit::TestCase
43
43
  end
44
44
 
45
45
  should 'resolve single id gpg keys' do
46
- assert keys = @part.send(:keys_for_data, @key_id)
46
+ assert keys = Mail::Gpg::GpgmeHelper.send(:keys_for_data, @key_id)
47
47
  check_key_list keys
48
48
  end
49
49
  should 'resolve id list to gpg keys' do
50
- assert keys = @part.send(:keys_for_data, [@key_id])
50
+ assert keys = Mail::Gpg::GpgmeHelper.send(:keys_for_data, [@key_id])
51
51
  check_key_list keys
52
52
  end
53
53
  end
@@ -58,11 +58,11 @@ class EncryptedPartTest < Test::Unit::TestCase
58
58
  end
59
59
 
60
60
  should 'resolve single id gpg keys' do
61
- assert keys = @part.send(:keys_for_data, @key_fpr)
61
+ assert keys = Mail::Gpg::GpgmeHelper.send(:keys_for_data, @key_fpr)
62
62
  check_key_list keys
63
63
  end
64
64
  should 'resolve id list to gpg keys' do
65
- assert keys = @part.send(:keys_for_data, [@key_fpr])
65
+ assert keys = Mail::Gpg::GpgmeHelper.send(:keys_for_data, [@key_fpr])
66
66
  check_key_list keys
67
67
  end
68
68
  end
@@ -75,7 +75,7 @@ class EncryptedPartTest < Test::Unit::TestCase
75
75
  end
76
76
 
77
77
  should 'resolve to gpg keys' do
78
- assert keys = @part.send(:keys_for_data, @emails, @key_data)
78
+ assert keys = Mail::Gpg::GpgmeHelper.send(:keys_for_data, @emails, @key_data)
79
79
  check_key_list keys
80
80
  end
81
81
  end
data/test/gpg_test.rb CHANGED
@@ -17,16 +17,38 @@ class GpgTest < Test::Unit::TestCase
17
17
  assert_equal 'application/pgp-encrypted; charset=UTF-8', v_part.content_type
18
18
 
19
19
  assert_equal 'application/octet-stream; name=encrypted.asc',
20
- enc_part.content_type
20
+ enc_part.content_type
21
21
  end
22
22
 
23
23
 
24
- def check_content
25
- assert enc = @encrypted.parts.last
24
+ def check_content(mail = @mail, encrypted = @encrypted)
25
+ assert enc = encrypted.parts.last
26
26
  assert clear = GPGME::Crypto.new.decrypt(enc.to_s, password: 'abc').to_s
27
27
  assert_match /encrypt me/, clear
28
+ assert_equal mail.to_s, clear
28
29
  end
29
30
 
31
+ def check_signature(mail = @mail, signed = @signed)
32
+ assert signature = signed.parts.detect{|p| p.content_type =~ /signature\.asc/}.body.to_s
33
+ GPGME::Crypto.new.verify(signature, signed_text: mail.encoded) do |sig|
34
+ assert true == sig.valid?
35
+ end
36
+ end
37
+
38
+ def check_mime_structure_signed(mail = @mail, signed = @signed)
39
+ assert_equal 2, signed.parts.size
40
+ sign_part, orig_part = signed.parts
41
+
42
+ assert_equal 'application/pgp-signature; name=signature.asc', sign_part.content_type
43
+ assert_equal orig_part.content_type, @mail.content_type
44
+ end
45
+
46
+ def check_headers_signed(mail = @mail, signed = @signed)
47
+ assert_equal mail.to, signed.to
48
+ assert_equal mail.cc, signed.cc
49
+ assert_equal mail.bcc, signed.bcc
50
+ assert_equal mail.subject, signed.subject
51
+ end
30
52
 
31
53
  context "gpg installation" do
32
54
  should "have keys for jane and joe" do
@@ -35,6 +57,104 @@ class GpgTest < Test::Unit::TestCase
35
57
  end
36
58
  end
37
59
 
60
+ context "gpg signed" do
61
+ setup do
62
+ @mail = Mail.new do
63
+ to 'joe@foo.bar'
64
+ from 'jane@foo.bar'
65
+ subject 'test test'
66
+ body 'sign me!'
67
+ end
68
+ end
69
+
70
+ context 'simple mail' do
71
+ setup do
72
+ @signed = Mail::Gpg.sign(@mail, password: 'abc')
73
+ end
74
+
75
+ should 'have same recipients and subject' do
76
+ check_headers_signed
77
+ end
78
+
79
+ should 'have proper gpgmime structure' do
80
+ check_mime_structure_signed
81
+ end
82
+
83
+ should 'have correct signature' do
84
+ check_signature
85
+ end
86
+ end
87
+
88
+ context 'mail with custom header' do
89
+ setup do
90
+ @mail.header['X-Custom-Header'] = 'custom value'
91
+ @signed = Mail::Gpg.sign(@mail, password: 'abc')
92
+ end
93
+
94
+ should 'have same recipients and subject' do
95
+ check_headers_signed
96
+ end
97
+
98
+ should 'have proper gpgmime structure' do
99
+ check_mime_structure_signed
100
+ end
101
+
102
+ should 'have correct signature' do
103
+ check_signature
104
+ end
105
+
106
+ should 'preserve customer header values' do
107
+ assert_equal 'custom value', @signed.header['X-Custom-Header'].to_s
108
+ end
109
+ end
110
+
111
+ context 'mail with multiple recipients' do
112
+ setup do
113
+ @mail.bcc 'jane@foo.bar'
114
+ @signed = Mail::Gpg.sign(@mail, password: 'abc')
115
+ end
116
+
117
+ should 'have same recipients and subject' do
118
+ check_headers_signed
119
+ end
120
+
121
+ should 'have proper gpgmime structure' do
122
+ check_mime_structure_signed
123
+ end
124
+
125
+ should 'have correct signature' do
126
+ check_signature
127
+ end
128
+ end
129
+
130
+ context 'multipart mail' do
131
+ setup do
132
+ @mail.add_file 'Rakefile'
133
+ @signed = Mail::Gpg.sign(@mail, password: 'abc')
134
+ end
135
+
136
+ should 'have same recipients and subject' do
137
+ check_headers_signed
138
+ end
139
+
140
+ should 'have proper gpgmime structure' do
141
+ check_mime_structure_signed
142
+ end
143
+
144
+ should 'have correct signature' do
145
+ check_signature
146
+ end
147
+
148
+ should 'have multiple parts in original content' do
149
+ assert original_part = @signed.parts.last
150
+ assert original_part.multipart?
151
+ assert_equal 2, original_part.parts.size
152
+ assert_match /sign me!/, original_part.parts.first.body.to_s
153
+ assert_match /Rakefile/, original_part.parts.last.content_disposition
154
+ end
155
+ end
156
+ end
157
+
38
158
  context "gpg encrypted" do
39
159
 
40
160
  setup do
@@ -63,7 +183,34 @@ class GpgTest < Test::Unit::TestCase
63
183
  check_content
64
184
  end
65
185
 
186
+ should 'decrypt' do
187
+ assert mail = Mail::Gpg.decrypt(@encrypted, { :password => 'abc' })
188
+ assert mail == @mail
189
+ end
66
190
  end
191
+
192
+ context 'simple mail (signed)' do
193
+ setup do
194
+ @encrypted = Mail::Gpg.encrypt(@mail, { :sign => true, :password => 'abc' })
195
+ end
196
+
197
+ should 'have same recipients and subject' do
198
+ check_headers
199
+ end
200
+
201
+ should 'have proper gpgmime structure' do
202
+ check_mime_structure
203
+ end
204
+
205
+ should 'have correctly encrypted content' do
206
+ check_content
207
+ end
208
+
209
+ should 'decrypt and verify' do
210
+ assert mail = Mail::Gpg.decrypt(@encrypted, { :verify => true, :password => 'abc' })
211
+ assert mail == @mail
212
+ end
213
+ end
67
214
 
68
215
  context 'mail with custom header' do
69
216
  setup do
@@ -86,6 +233,11 @@ class GpgTest < Test::Unit::TestCase
86
233
  should 'preserve customer header values' do
87
234
  assert_equal 'custom value', @encrypted.header['X-Custom-Header'].to_s
88
235
  end
236
+
237
+ should 'decrypt' do
238
+ assert mail = Mail::Gpg.decrypt(@encrypted, { :password => 'abc' })
239
+ assert mail == @mail
240
+ end
89
241
  end
90
242
 
91
243
  context 'mail with multiple recipients' do
@@ -110,6 +262,10 @@ class GpgTest < Test::Unit::TestCase
110
262
  assert encrypted_body = @encrypted.parts.last.to_s
111
263
  end
112
264
 
265
+ should 'decrypt' do
266
+ assert mail = Mail::Gpg.decrypt(@encrypted, { :password => 'abc' })
267
+ assert mail == @mail
268
+ end
113
269
  end
114
270
 
115
271
  context 'multipart mail' do
@@ -139,6 +295,12 @@ class GpgTest < Test::Unit::TestCase
139
295
  assert_match /encrypt me/, m.parts.first.body.to_s
140
296
  assert_match /Rakefile/, m.parts.last.content_disposition
141
297
  end
298
+
299
+ should 'decrypt' do
300
+ assert mail = Mail::Gpg.decrypt(@encrypted, { :password => 'abc' })
301
+ assert mail == @mail
302
+ assert mail.parts[1] == @mail.parts[1]
303
+ end
142
304
  end
143
305
  end
144
306
  end
Binary file
data/test/hkp_test.rb ADDED
@@ -0,0 +1,36 @@
1
+ require 'test_helper'
2
+ require 'hkp'
3
+
4
+ class HkpTest < Test::Unit::TestCase
5
+
6
+ context "keyserver setup" do
7
+
8
+ context "with url specified" do
9
+
10
+ setup do
11
+ @hkp = Hkp.new("hkp://my-key-server.net")
12
+ end
13
+
14
+ should "use specified keyserver" do
15
+ assert url = @hkp.instance_variable_get("@keyserver")
16
+ assert_equal "hkp://my-key-server.net", url
17
+ end
18
+
19
+ end
20
+
21
+ context "without url specified" do
22
+
23
+ setup do
24
+ @hkp = Hkp.new
25
+ end
26
+
27
+ should "have found a non-empty keyserver" do
28
+ assert url = @hkp.instance_variable_get("@keyserver")
29
+ assert !url.blank?
30
+ end
31
+
32
+ end
33
+
34
+ end
35
+
36
+ end
@@ -0,0 +1,126 @@
1
+ require 'test_helper'
2
+
3
+ # test cases for PGP inline messages (i.e. non-mime)
4
+ class InlineDecryptedMessageTest < Test::Unit::TestCase
5
+
6
+ context "InlineDecryptedMessage" 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
+ gpg encrypt: false
16
+ end
17
+ end
18
+
19
+ context "inline message" do
20
+ should "decrypt body" do
21
+ mail = Mail.new(@mail)
22
+ mail.body = InlineDecryptedMessageTest.encrypt(mail, mail.body.to_s)
23
+
24
+ assert !mail.multipart?
25
+ assert mail.encrypted?
26
+ assert decrypted = mail.decrypt(:password => 'abc')
27
+ assert decrypted == @mail
28
+ assert !decrypted.encrypted?
29
+ end
30
+ end
31
+
32
+ context "attachment message" do
33
+ should "decrypt attachment" do
34
+ rakefile = File.open('Rakefile') { |file| file.read }
35
+ mail = Mail.new(@mail)
36
+ mail.content_type = 'multipart/mixed'
37
+ mail.body = ''
38
+ mail.part do |p|
39
+ p.content_type 'application/octet-stream; name=Rakefile.pgp'
40
+ p.content_transfer_encoding Mail::Encodings::Base64
41
+ p.content_disposition 'attachment; filename="Rakefile.pgp"'
42
+ p.body Mail::Encodings::Base64::encode(InlineDecryptedMessageTest.encrypt(mail, rakefile, false))
43
+ end
44
+
45
+ assert mail.multipart?
46
+ assert mail.encrypted?
47
+ assert decrypted = mail.decrypt(:password => 'abc')
48
+ assert !decrypted.encrypted?
49
+ check_headers(@mail, decrypted)
50
+ assert_equal 1, decrypted.parts.length
51
+ assert /application\/octet-stream; (?:charset=UTF-8; )?name=Rakefile/ =~ decrypted.parts[0].content_type
52
+ assert_equal 'attachment; filename=Rakefile', decrypted.parts[0].content_disposition
53
+ assert_equal rakefile, decrypted.parts[0].body.decoded
54
+ end
55
+ end
56
+
57
+ context "cleartext body and encrypted attachment message" do
58
+ should "decrypt attachment" do
59
+ rakefile = File.open('Rakefile') { |file| file.read }
60
+ mail = Mail.new(@mail)
61
+ mail.content_type = 'multipart/mixed'
62
+ mail.part do |p|
63
+ p.content_type 'application/octet-stream; name=Rakefile.pgp'
64
+ p.content_transfer_encoding Mail::Encodings::Base64
65
+ p.content_disposition 'attachment; filename="Rakefile.pgp"'
66
+ p.body Mail::Encodings::Base64::encode(InlineDecryptedMessageTest.encrypt(mail, rakefile, false))
67
+ end
68
+
69
+ assert mail.multipart?
70
+ assert mail.encrypted?
71
+ assert decrypted = mail.decrypt(:password => 'abc')
72
+ assert !decrypted.encrypted?
73
+ check_headers(@mail, decrypted)
74
+ assert_equal 2, decrypted.parts.length
75
+ assert_equal @mail.body, decrypted.parts[0].body.to_s
76
+ assert /application\/octet-stream; (?:charset=UTF-8; )?name=Rakefile/ =~ decrypted.parts[1].content_type
77
+ assert_equal 'attachment; filename=Rakefile', decrypted.parts[1].content_disposition
78
+ assert_equal rakefile, decrypted.parts[1].body.decoded
79
+ end
80
+ end
81
+
82
+ context "encrypted body and attachment message" do
83
+ should "decrypt" do
84
+ rakefile = File.open('Rakefile') { |file| file.read }
85
+ mail = Mail.new(@mail)
86
+ mail.content_type = 'multipart/mixed'
87
+ mail.body = InlineDecryptedMessageTest.encrypt(mail, mail.body.to_s)
88
+ mail.part do |p|
89
+ p.content_type 'application/octet-stream; name=Rakefile.pgp'
90
+ p.content_transfer_encoding Mail::Encodings::Base64
91
+ p.content_disposition 'attachment; filename="Rakefile.pgp"'
92
+ p.body Mail::Encodings::Base64::encode(InlineDecryptedMessageTest.encrypt(mail, rakefile, false))
93
+ end
94
+
95
+ assert mail.multipart?
96
+ assert mail.encrypted?
97
+ assert decrypted = mail.decrypt(:password => 'abc')
98
+ assert !decrypted.encrypted?
99
+ check_headers(@mail, decrypted)
100
+ assert_equal 2, decrypted.parts.length
101
+ assert_equal @mail.body, decrypted.parts[0].body.to_s
102
+ assert /application\/octet-stream; (?:charset=UTF-8; )?name=Rakefile/ =~ decrypted.parts[1].content_type
103
+ assert_equal 'attachment; filename=Rakefile', decrypted.parts[1].content_disposition
104
+ assert_equal rakefile, decrypted.parts[1].body.decoded
105
+ end
106
+ end
107
+ end
108
+
109
+ def self.encrypt(mail, plain, armor = true)
110
+ GPGME::Crypto.new.encrypt(plain,
111
+ password: 'abc',
112
+ recipients: mail.to,
113
+ sign: true,
114
+ signers: mail.from,
115
+ armor: armor).to_s
116
+ end
117
+
118
+ def check_headers(expected, actual)
119
+ assert_equal expected.to, actual.to
120
+ assert_equal expected.cc, actual.cc
121
+ assert_equal expected.bcc, actual.bcc
122
+ assert_equal expected.subject, actual.subject
123
+ assert_equal expected.message_id, actual.message_id
124
+ assert_equal expected.date, actual.date
125
+ end
126
+ end
data/test/message_test.rb CHANGED
@@ -23,10 +23,46 @@ class MessageTest < Test::Unit::TestCase
23
23
  assert_equal 1, @mails.size
24
24
  assert m = @mails.first
25
25
  assert_equal 'test', m.subject
26
+ assert !m.encrypted?
26
27
  assert_equal 'i am unencrypted', m.body.to_s
27
28
  end
29
+
30
+ should "raise encoding error" do
31
+ assert_equal 1, @mails.size
32
+ assert m = @mails.first
33
+ assert_equal 'test', m.subject
34
+ assert_raises(EncodingError){
35
+ m.decrypt(:password => 'abc')
36
+ }
37
+ end
28
38
  end
29
39
 
40
+ context "with gpg signing only" do
41
+ setup do
42
+ @mail.gpg sign: true, password: 'abc'
43
+ end
44
+
45
+ context "" do
46
+ setup do
47
+ @mail.deliver
48
+ end
49
+
50
+ should "deliver signed mail" do
51
+ assert_equal 1, @mails.size
52
+ assert m = @mails.first
53
+ assert_equal 'test', m.subject
54
+ assert !m.encrypted?
55
+ assert m.multipart?
56
+ assert sign_part = m.parts.last
57
+ assert m = Mail::Message.new(m.parts.last)
58
+ assert !m.multipart?
59
+ GPGME::Crypto.new.verify(sign_part.body.to_s, signed_text: @mail.encoded) do |sig|
60
+ assert true == sig.valid?
61
+ end
62
+ end
63
+ end
64
+ end
65
+
30
66
  context "with gpg turned on" do
31
67
  setup do
32
68
  @mail.gpg encrypt: true
@@ -60,12 +96,37 @@ class MessageTest < Test::Unit::TestCase
60
96
  assert m = @mails.first
61
97
  assert_equal 'test', m.subject
62
98
  assert m.multipart?
99
+ assert m.encrypted?
63
100
  assert enc_part = m.parts.last
64
101
  assert clear = GPGME::Crypto.new.decrypt(enc_part.body.to_s, password: 'abc').to_s
65
102
  assert m = Mail::Message.new(clear)
66
103
  assert !m.multipart?
67
104
  assert_equal 'i am unencrypted', m.body.to_s
68
105
  end
106
+
107
+ should "decrypt" do
108
+ assert_equal 1, @mails.size
109
+ assert m = @mails.first
110
+ assert_equal 'test', m.subject
111
+ assert m.multipart?
112
+ assert m.encrypted?
113
+ assert decrypted = m.decrypt(:password => 'abc')
114
+ assert decrypted == @mail
115
+ end
116
+
117
+ should "raise bad passphrase on decrypt" do
118
+ assert_equal 1, @mails.size
119
+ assert m = @mails.first
120
+ assert_equal 'test', m.subject
121
+ # incorrect passphrase
122
+ assert_raises(GPGME::Error::BadPassphrase){
123
+ m.decrypt(:password => 'incorrect')
124
+ }
125
+ # no passphrase
126
+ assert_raises(GPGME::Error::BadPassphrase){
127
+ m.decrypt
128
+ }
129
+ end
69
130
  end
70
131
  end
71
132
 
@@ -0,0 +1,15 @@
1
+ require 'mail/gpg/sign_part'
2
+
3
+ class SignPartTest < Test::Unit::TestCase
4
+ context 'SignPart' do
5
+ setup do
6
+ mail = Mail.new do
7
+ to 'jane@foo.bar'
8
+ from 'joe@foo.bar'
9
+ subject 'test'
10
+ body 'i am unsigned'
11
+ end
12
+ @part = Mail::Gpg::SignPart.new(mail)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,36 @@
1
+ require 'test_helper'
2
+ require 'mail/gpg/version_part'
3
+
4
+ class VersionPartTest < Test::Unit::TestCase
5
+ context 'VersionPart' do
6
+
7
+ should 'roundtrip successfully' do
8
+ part = Mail::Gpg::VersionPart.new()
9
+ assert Mail::Gpg::VersionPart.isVersionPart?(part)
10
+ end
11
+
12
+ should 'return false for non gpg mime type' do
13
+ part = Mail::Gpg::VersionPart.new()
14
+ part.content_type = 'text/plain'
15
+ assert !Mail::Gpg::VersionPart.isVersionPart?(part)
16
+ end
17
+
18
+ should 'return false for empty body' do
19
+ part = Mail::Gpg::VersionPart.new()
20
+ part.body = nil
21
+ assert !Mail::Gpg::VersionPart.isVersionPart?(part)
22
+ end
23
+
24
+ should 'return false for foul body' do
25
+ part = Mail::Gpg::VersionPart.new()
26
+ part.body = 'non gpg body'
27
+ assert !Mail::Gpg::VersionPart.isVersionPart?(part)
28
+ end
29
+
30
+ should 'return true for body with extra content' do
31
+ part = Mail::Gpg::VersionPart.new()
32
+ part.body = "#{part.body} extra content"
33
+ assert Mail::Gpg::VersionPart.isVersionPart?(part)
34
+ end
35
+ end
36
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mail-gpg
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.6
4
+ version: 0.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-08-27 00:00:00.000000000 Z
12
+ date: 2013-11-06 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: mail
@@ -145,15 +145,20 @@ files:
145
145
  - lib/hkp.rb
146
146
  - lib/mail-gpg.rb
147
147
  - lib/mail/gpg.rb
148
+ - lib/mail/gpg/decrypted_part.rb
148
149
  - lib/mail/gpg/delivery_handler.rb
149
150
  - lib/mail/gpg/encrypted_part.rb
151
+ - lib/mail/gpg/gpgme_helper.rb
152
+ - lib/mail/gpg/inline_decrypted_message.rb
150
153
  - lib/mail/gpg/message_patch.rb
151
154
  - lib/mail/gpg/rails.rb
152
155
  - lib/mail/gpg/rails/action_mailer_base_patch.rb
156
+ - lib/mail/gpg/sign_part.rb
153
157
  - lib/mail/gpg/version.rb
154
158
  - lib/mail/gpg/version_part.rb
155
159
  - mail-gpg.gemspec
156
160
  - test/action_mailer_test.rb
161
+ - test/decrypted_part_test.rb
157
162
  - test/encrypted_part_test.rb
158
163
  - test/gpg_test.rb
159
164
  - test/gpghome/pubring.gpg
@@ -161,8 +166,12 @@ files:
161
166
  - test/gpghome/random_seed
162
167
  - test/gpghome/secring.gpg
163
168
  - test/gpghome/trustdb.gpg
169
+ - test/hkp_test.rb
170
+ - test/inline_decrypted_message_test.rb
164
171
  - test/message_test.rb
172
+ - test/sign_part_test.rb
165
173
  - test/test_helper.rb
174
+ - test/version_part_test.rb
166
175
  homepage: https://github.com/jkraemer/mail-gpg
167
176
  licenses:
168
177
  - MIT
@@ -190,6 +199,7 @@ specification_version: 3
190
199
  summary: GPG/MIME encryption plugin for the Ruby Mail Library
191
200
  test_files:
192
201
  - test/action_mailer_test.rb
202
+ - test/decrypted_part_test.rb
193
203
  - test/encrypted_part_test.rb
194
204
  - test/gpg_test.rb
195
205
  - test/gpghome/pubring.gpg
@@ -197,5 +207,9 @@ test_files:
197
207
  - test/gpghome/random_seed
198
208
  - test/gpghome/secring.gpg
199
209
  - test/gpghome/trustdb.gpg
210
+ - test/hkp_test.rb
211
+ - test/inline_decrypted_message_test.rb
200
212
  - test/message_test.rb
213
+ - test/sign_part_test.rb
201
214
  - test/test_helper.rb
215
+ - test/version_part_test.rb