mail-gpg 0.4.5 → 0.4.6

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c21d99f46e1cf61f5f6b528d75189d90dd0543c6937c346d0435ad5dc23b9d25
4
- data.tar.gz: a33073e63f6353184d87188a81996147ffcff43a98818fc46c07bcf9ac2e5d7b
3
+ metadata.gz: 3acca2c34af794c59ff717e0f29d27b26ed1d1e095dd388a6238e0ef31366644
4
+ data.tar.gz: c1161da992e780ecf3c6bea38977c9b8d0c86b2a5530f2095e398f868e7a70c9
5
5
  SHA512:
6
- metadata.gz: edc5a48bad65750933dba1cc7d50c157c3c38a96d101e4106af284389199ea74564ed92689583a17a537f357a823dec9c8c301f7aca437c6f5dc9caf3030e109
7
- data.tar.gz: e6040cb3fb89303a145a4a5158134437e66b52beca5628e6b154baf41560b53386b3e60bb93b915af4bc228b173b0d8f4f45e9cbbb4185f1387120d2e111159f
6
+ metadata.gz: ac9858c3c4838fa909cec047658a37945fa37ebf08a6b8a005ca336aab4543caed549f4a4c54a614195084772b9baa728978e2db80f2e1582767ea9a888ac743
7
+ data.tar.gz: b082facfd7d4c84a62e40bcf3aaba4866121daa0a65d8a962164031f7989eca572b5811187fb8f41dfb4948dbd7191aea4f1cce44d768c488721334d70073223
@@ -10,37 +10,20 @@ jobs:
10
10
  test:
11
11
  runs-on: ubuntu-latest
12
12
 
13
- strategy:
14
- fail-fast: false
15
- matrix:
16
- ruby-version: ["2.7", "3.0", "3.1", "3.2", "3.3", "3.4"]
17
-
18
- steps:
19
- - uses: actions/checkout@v4
20
-
21
- - name: Install GPG
22
- run: sudo apt-get update && sudo apt-get install -y gnupg
23
-
24
- - name: Set up Ruby ${{ matrix.ruby-version }}
25
- uses: ruby/setup-ruby@v1
26
- with:
27
- ruby-version: ${{ matrix.ruby-version }}
28
- bundler-cache: true
29
-
30
- - name: Run tests
31
- run: bundle exec rake
32
-
33
- test-with-rails:
34
- runs-on: ubuntu-latest
35
-
36
13
  strategy:
37
14
  fail-fast: false
38
15
  matrix:
39
16
  include:
17
+ - ruby-version: "2.7.8"
18
+ rails-version: "5.2.8.1"
40
19
  - ruby-version: "3.2"
41
- rails-version: "7.1"
20
+ rails-version: "8.0"
42
21
  - ruby-version: "3.4"
43
- rails-version: "7.2"
22
+ rails-version: "8.0"
23
+
24
+ # picked up by the Gemfile: gem 'actionmailer', "~> #{ENV['RAILS']}"
25
+ env:
26
+ RAILS: ${{ matrix.rails-version }}
44
27
 
45
28
  steps:
46
29
  - uses: actions/checkout@v4
@@ -54,7 +37,5 @@ jobs:
54
37
  ruby-version: ${{ matrix.ruby-version }}
55
38
  bundler-cache: true
56
39
 
57
- - name: Run tests with Rails ${{ matrix.rails-version }}
58
- env:
59
- RAILS: ${{ matrix.rails-version }}
60
- run: bundle update && bundle exec rake
40
+ - name: Run tests
41
+ run: bundle exec rake
data/History.txt CHANGED
@@ -1,3 +1,17 @@
1
+ == 0.4.6 2026-08-13
2
+
3
+ * fix double transfer-decoding of inline signed / encrypted mails: the
4
+ rebuilt message kept the original Content-Transfer-Encoding header while
5
+ its body was set to the already decoded (verified / decrypted) text, so
6
+ reading the body would corrupt it (e.g. base64-decode plain text)
7
+ * modernize test setup: RSA 2048 test keys generated without passphrase
8
+ protection (current GnuPG refuses to sign with the old DSA 1024 keys, and
9
+ interactive pinentry prompts no longer get in the way); bad passphrase
10
+ handling is now tested with a dedicated protected key via loopback
11
+ pinentry
12
+ * CI runs on GitHub Actions with Ruby 2.7.8 / Rails 5.2.8.1 and
13
+ Ruby 3.2 / 3.4 with Rails 8
14
+
1
15
  == 0.4.5 2026-01-20
2
16
 
3
17
  * fix HKP client for Ruby 3.0+ (replaced deprecated URI.escape/decode)
@@ -59,7 +59,9 @@ module Mail
59
59
  else
60
60
  p.content_type part.content_type
61
61
  p.content_transfer_encoding part.content_transfer_encoding
62
- p.body part.body.to_s
62
+ # keep the raw (still encoded) body to match the transfer
63
+ # encoding copied above - body.to_s would decode it
64
+ p.body part.body.raw_source
63
65
  end
64
66
  end
65
67
  end
@@ -72,6 +74,10 @@ module Mail
72
74
  cipher_mail.header.fields.each do |field|
73
75
  header[field.name] = field.value
74
76
  end
77
+ # the decrypted body set below is not transfer encoded, so an
78
+ # encoding copied over from the original mail would lead to
79
+ # double decoding
80
+ header['Content-Transfer-Encoding'] = nil
75
81
  body decrypted.to_s
76
82
  verify_result decrypted.verify_result if options[:verify] && '' != decrypted
77
83
  end
@@ -17,6 +17,10 @@ module Mail
17
17
  success, vr = GpgmeHelper.inline_verify(signed_text, options)
18
18
  p = VerifiedPart.new(part)
19
19
  if success
20
+ # the body set here is already decoded, so a transfer
21
+ # encoding copied over from the original part would lead to
22
+ # double decoding
23
+ p.header['Content-Transfer-Encoding'] = nil
20
24
  p.body self.class.strip_inline_signature signed_text
21
25
  end
22
26
  p.verify_result vr
@@ -33,6 +37,9 @@ module Mail
33
37
  signed_mail.header.fields.each do |field|
34
38
  header[field.name] = field.value
35
39
  end
40
+ # the body set below is already decoded, so a transfer encoding
41
+ # copied over from the original mail would lead to double decoding
42
+ header['Content-Transfer-Encoding'] = nil
36
43
  signed_text = signed_mail.body.to_s
37
44
  success, vr = GpgmeHelper.inline_verify(signed_text, options)
38
45
  if success
@@ -1,5 +1,5 @@
1
1
  module Mail
2
2
  module Gpg
3
- VERSION = "0.4.5"
3
+ VERSION = "0.4.6"
4
4
  end
5
5
  end
@@ -31,6 +31,24 @@ class InlineDecryptedMessageTest < MailGpgTestCase
31
31
  assert sig.to_s=~ /Joe/
32
32
  assert sig.valid?
33
33
  end
34
+
35
+ should "decrypt base64 encoded body" do
36
+ mail = Mail.new(@mail)
37
+ cipher_text = InlineDecryptedMessageTest.encrypt(mail, mail.body.to_s)
38
+ mail.content_transfer_encoding 'base64'
39
+ mail.body Mail::Encodings::Base64.encode(cipher_text)
40
+ mail = Mail.new(mail.encoded)
41
+
42
+ assert !mail.multipart?
43
+ assert mail.encrypted?
44
+ assert decrypted = mail.decrypt(:password => 'abc', verify: true)
45
+ assert_equal 'i am unencrypted', decrypted.body.decoded,
46
+ "body was double decoded: #{decrypted.body.decoded.inspect}"
47
+ assert vr = decrypted.verify_result
48
+ assert sig = vr.signatures.first
49
+ assert sig.to_s=~ /Joe/
50
+ assert sig.valid?
51
+ end
34
52
  end
35
53
 
36
54
  context "multipart/alternative message" do
@@ -77,6 +95,35 @@ class InlineDecryptedMessageTest < MailGpgTestCase
77
95
  end
78
96
  end
79
97
 
98
+ context "base64 encoded cleartext part and encrypted attachment message" do
99
+ should "decrypt attachment and not double decode text part" do
100
+ rakefile = File.open('Rakefile') { |file| file.read }
101
+ mail = Mail.new(@mail)
102
+ mail.content_type = 'multipart/mixed'
103
+ mail.body = ''
104
+ mail.part do |p|
105
+ p.content_type 'text/plain'
106
+ p.content_transfer_encoding 'base64'
107
+ p.body Mail::Encodings::Base64.encode('i am unencrypted')
108
+ end
109
+ mail.part do |p|
110
+ p.content_type 'application/octet-stream; name=Rakefile.pgp'
111
+ p.content_transfer_encoding Mail::Encodings::Base64
112
+ p.content_disposition 'attachment; filename="Rakefile.pgp"'
113
+ p.body Mail::Encodings::Base64::encode(InlineDecryptedMessageTest.encrypt(mail, rakefile, false))
114
+ end
115
+
116
+ assert mail.multipart?
117
+ assert mail.encrypted?
118
+ assert decrypted = mail.decrypt(password: 'abc')
119
+ assert !decrypted.encrypted?
120
+ assert_equal 2, decrypted.parts.length
121
+ assert_equal 'i am unencrypted', decrypted.parts[0].body.decoded,
122
+ "part was double decoded: #{decrypted.parts[0].body.decoded.inspect}"
123
+ assert_equal rakefile, decrypted.parts[1].body.decoded
124
+ end
125
+ end
126
+
80
127
  context "cleartext body and encrypted attachment message" do
81
128
  should "decrypt and verify attachment" do
82
129
  rakefile = File.open('Rakefile') { |file| file.read }
@@ -54,9 +54,54 @@ class InlineSignedMessageTest < MailGpgTestCase
54
54
  assert !sig.valid?
55
55
  end
56
56
 
57
+ should "verify base64 encoded body" do
58
+ mail = Mail.new(@mail)
59
+ signed_text = self.class.inline_sign(mail, mail.body.to_s)
60
+ mail.content_transfer_encoding 'base64'
61
+ mail.body Mail::Encodings::Base64.encode(signed_text)
62
+ mail = Mail.new(mail.encoded)
63
+ assert !mail.multipart?
64
+ assert mail.signed?
65
+ assert verified = mail.verify
66
+ assert verified.signature_valid?
67
+ assert verified.body.decoded.include?('i am unencrypted'),
68
+ "body was double decoded: #{verified.body.decoded.inspect}"
69
+ end
70
+
71
+ should "keep base64 encoded body readable when signature is invalid" do
72
+ mail = Mail.new(@mail)
73
+ signed_text = self.class.inline_sign(mail, mail.body.to_s).gsub /i am/, 'i was'
74
+ mail.content_transfer_encoding 'base64'
75
+ mail.body Mail::Encodings::Base64.encode(signed_text)
76
+ mail = Mail.new(mail.encoded)
77
+ assert !mail.multipart?
78
+ assert mail.signed?
79
+ assert verified = mail.verify
80
+ assert !verified.signature_valid?
81
+ assert verified.body.decoded.include?('i was unencrypted'),
82
+ "body was double decoded: #{verified.body.decoded.inspect}"
83
+ end
84
+
57
85
  end
58
86
 
59
87
  context "message with signed attachment" do
88
+ should "check base64 encoded attachment signature" do
89
+ mail = Mail.new(@mail)
90
+ mail.body = 'foobar'
91
+ signed_text = self.class.inline_sign(mail, 'sign me!')
92
+ mail.part do |p|
93
+ p.content_transfer_encoding 'base64'
94
+ p.body Mail::Encodings::Base64.encode(signed_text)
95
+ end
96
+ mail = Mail.new(mail.encoded)
97
+ assert mail.multipart?
98
+ assert mail.signed?
99
+ assert verified = mail.verify
100
+ assert verified.signature_valid?
101
+ assert verified.parts.last.body.decoded.include?('sign me!'),
102
+ "part was double decoded: #{verified.parts.last.body.decoded.inspect}"
103
+ end
104
+
60
105
  should "check attachment signature" do
61
106
  mail = Mail.new(@mail)
62
107
  mail.body = 'foobar'
data/test/message_test.rb CHANGED
@@ -298,24 +298,26 @@ class MessageTest < MailGpgTestCase
298
298
  end
299
299
 
300
300
  should "raise bad passphrase on decrypt" do
301
- assert_equal 1, @mails.size
302
- assert m = @mails.first
301
+ # patty's key is passphrase protected; loopback pinentry keeps
302
+ # gpg-agent from prompting interactively
303
+ mail = Mail.new do
304
+ from 'joe@foo.bar'
305
+ to 'patty@foo.bar'
306
+ subject 'test'
307
+ body 'i am unencrypted'
308
+ gpg encrypt: true
309
+ end
310
+ mail.deliver
311
+ assert m = @mails.last
303
312
  assert_equal 'test', m.subject
313
+ assert m.encrypted?
304
314
  # incorrect passphrase
305
- if @gpg_utils.preset_passphrases?
306
- set_passphrase('incorrect')
307
- # expected_exception = GPGME::Error::DecryptFailed
308
- # I dont know why.
309
- expected_exception = EOFError
310
- else
311
- expected_exception = GPGME::Error::BadPassphrase
312
- end
313
- assert_raises(expected_exception){
314
- m.decrypt(:password => 'incorrect')
315
+ assert_raises(GPGME::Error::BadPassphrase){
316
+ m.decrypt(password: 'incorrect', pinentry_mode: GPGME::PINENTRY_MODE_LOOPBACK)
315
317
  }
316
- # no passphrase
317
- assert_raises(expected_exception){
318
- m.decrypt
318
+ # no passphrase - gpg gets no data from the loopback pinentry
319
+ assert_raises(GPGME::Error::NoData){
320
+ m.decrypt(pinentry_mode: GPGME::PINENTRY_MODE_LOOPBACK)
319
321
  }
320
322
  end
321
323
  end
data/test/test_helper.rb CHANGED
@@ -71,31 +71,46 @@ class GPGTestUtils
71
71
  def gen_keys
72
72
  puts "setting up keydir #{@home}"
73
73
  FileUtils.mkdir_p @home
74
- (File.open(File.join(@home, "gpg-agent.conf"), "wb") << "allow-preset-passphrase\nbatch\n").close
74
+ (File.open(File.join(@home, "gpg-agent.conf"), "wb") << "allow-preset-passphrase\n").close
75
75
  GPGME::Ctx.new do |gpg|
76
76
  gpg.generate_key <<-END
77
77
  <GnupgKeyParms format="internal">
78
- Key-Type: DSA
79
- Key-Length: 1024
80
- Subkey-Type: ELG-E
81
- Subkey-Length: 1024
78
+ %no-protection
79
+ Key-Type: RSA
80
+ Key-Length: 2048
81
+ Subkey-Type: RSA
82
+ Subkey-Length: 2048
82
83
  Name-Real: Joe Tester
83
84
  Name-Comment: with stupid passphrase
84
85
  Name-Email: joe@foo.bar
85
86
  Expire-Date: 0
86
- Passphrase: abc
87
87
  </GnupgKeyParms>
88
88
  END
89
89
  gpg.generate_key <<-END
90
90
  <GnupgKeyParms format="internal">
91
- Key-Type: DSA
92
- Key-Length: 1024
93
- Subkey-Type: ELG-E
94
- Subkey-Length: 1024
91
+ %no-protection
92
+ Key-Type: RSA
93
+ Key-Length: 2048
94
+ Subkey-Type: RSA
95
+ Subkey-Length: 2048
95
96
  Name-Real: Jane Doe
96
97
  Name-Comment: with stupid passphrase
97
98
  Name-Email: jane@foo.bar
98
99
  Expire-Date: 0
100
+ </GnupgKeyParms>
101
+ END
102
+ # passphrase protected key for testing bad passphrase handling with
103
+ # loopback pinentry
104
+ gpg.generate_key <<-END
105
+ <GnupgKeyParms format="internal">
106
+ Key-Type: RSA
107
+ Key-Length: 2048
108
+ Subkey-Type: RSA
109
+ Subkey-Length: 2048
110
+ Name-Real: Patty Protected
111
+ Name-Comment: with stupid passphrase
112
+ Name-Email: patty@foo.bar
113
+ Expire-Date: 0
99
114
  Passphrase: abc
100
115
  </GnupgKeyParms>
101
116
  END
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mail-gpg
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.5
4
+ version: 0.4.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jens Kraemer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-01-20 00:00:00.000000000 Z
11
+ date: 2026-08-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: mail
@@ -190,11 +190,6 @@ files:
190
190
  - test/decrypted_part_test.rb
191
191
  - test/encrypted_part_test.rb
192
192
  - test/gpg_test.rb
193
- - test/gpghome/gpg-agent.conf
194
- - test/gpghome/pubring.gpg
195
- - test/gpghome/pubring.gpg~
196
- - test/gpghome/secring.gpg
197
- - test/gpghome/trustdb.gpg
198
193
  - test/gpgme_helper_test.rb
199
194
  - test/hkp_test.rb
200
195
  - test/inline_decrypted_message_test.rb
@@ -231,11 +226,6 @@ test_files:
231
226
  - test/decrypted_part_test.rb
232
227
  - test/encrypted_part_test.rb
233
228
  - test/gpg_test.rb
234
- - test/gpghome/gpg-agent.conf
235
- - test/gpghome/pubring.gpg
236
- - test/gpghome/pubring.gpg~
237
- - test/gpghome/secring.gpg
238
- - test/gpghome/trustdb.gpg
239
229
  - test/gpgme_helper_test.rb
240
230
  - test/hkp_test.rb
241
231
  - test/inline_decrypted_message_test.rb
@@ -1,2 +0,0 @@
1
- allow-preset-passphrase
2
- batch
Binary file
Binary file
Binary file
Binary file