mail-gpg 0.0.6 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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