enmail 0.1.0 → 0.2.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.
- checksums.yaml +5 -5
- data/.editorconfig +21 -0
- data/.gitignore +164 -9
- data/.gitmodules +3 -0
- data/.rubocop.yml +26 -627
- data/.travis.yml +86 -3
- data/.yardopts +6 -0
- data/Gemfile +10 -1
- data/LICENSE.txt +21 -0
- data/README.adoc +150 -0
- data/Rakefile +119 -0
- data/bin/rspec +1 -0
- data/bin/setup +18 -1
- data/ci/install_botan.sh +28 -0
- data/ci/install_json_c.sh +32 -0
- data/ci/install_rnp.sh +31 -0
- data/docs/GPGMEAdapter.adoc +68 -0
- data/docs/RNPAdapter.adoc +45 -0
- data/enmail.gemspec +21 -8
- data/lib/enmail.rb +25 -4
- data/lib/enmail/adapters/base.rb +14 -0
- data/lib/enmail/adapters/gpgme.rb +81 -0
- data/lib/enmail/adapters/gpgme_requirements.rb +3 -0
- data/lib/enmail/adapters/rnp.rb +109 -0
- data/lib/enmail/adapters/rnp_requirements.rb +3 -0
- data/lib/enmail/dependency_constraints.rb +9 -0
- data/lib/enmail/extensions/message_transport_encoding_restrictions.rb +20 -0
- data/lib/enmail/helpers/message_manipulation.rb +73 -0
- data/lib/enmail/helpers/rfc1847.rb +103 -0
- data/lib/enmail/helpers/rfc3156.rb +60 -0
- data/lib/enmail/version.rb +4 -1
- metadata +99 -24
- data/README.md +0 -115
- data/lib/enmail/certificate_finder.rb +0 -75
- data/lib/enmail/config.rb +0 -21
- data/lib/enmail/configuration.rb +0 -80
- data/lib/enmail/enmailable.rb +0 -43
- data/lib/enmail/key.rb +0 -53
- data/lib/enmail/mail_ext/message.rb +0 -18
- data/lib/mail/secure/mail_interceptors/pgp.rb +0 -53
- data/lib/mail/secure/models/key.rb +0 -5
- data/lib/mail/secure/pgp_mailable.rb +0 -107
@@ -0,0 +1,20 @@
|
|
1
|
+
# (c) Copyright 2018 Ribose Inc.
|
2
|
+
#
|
3
|
+
|
4
|
+
module EnMail
|
5
|
+
module Extensions
|
6
|
+
module MessageTransportEncodingRestrictions
|
7
|
+
def identify_and_set_transfer_encoding
|
8
|
+
if @enmail_rfc18467_encoding_restrictions && !multipart?
|
9
|
+
str = body.raw_source
|
10
|
+
self.content_transfer_encoding = [
|
11
|
+
::Mail::Encodings::Base64,
|
12
|
+
::Mail::Encodings::QuotedPrintable,
|
13
|
+
].min { |a, b| a.cost(str) <=> b.cost(str) }
|
14
|
+
else
|
15
|
+
super
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# (c) Copyright 2018 Ribose Inc.
|
2
|
+
#
|
3
|
+
|
4
|
+
module EnMail
|
5
|
+
module Helpers
|
6
|
+
# A toolbox with common operations for manipulating and reading message
|
7
|
+
# properties, potentially useful for all adapters.
|
8
|
+
module MessageManipulation
|
9
|
+
protected
|
10
|
+
|
11
|
+
# Returns a new +Mail::Part+ with the same content and MIME headers
|
12
|
+
# as the message passed as an argument.
|
13
|
+
#
|
14
|
+
# Although Mail gem provides +Mail::Message#convert_to_multipart+ method,
|
15
|
+
# it works correctly for non-multipart text/plain messages only. This
|
16
|
+
# method is more robust, and handles messages containing any content type,
|
17
|
+
# be they multipart or not.
|
18
|
+
#
|
19
|
+
# The message passed as an argument is not altered.
|
20
|
+
#
|
21
|
+
# TODO Copy MIME headers (ones which start with "Content-")
|
22
|
+
# TODO Preserve Content-Transfer-Encoding when possible
|
23
|
+
# TODO Set some safe Content-Transfer-Encoding, like quoted-printable
|
24
|
+
def body_to_part(message)
|
25
|
+
part = ::Mail::Part.new
|
26
|
+
part.content_type = message.content_type
|
27
|
+
if message.multipart?
|
28
|
+
message.body.parts.each { |p| part.add_part p.dup }
|
29
|
+
else
|
30
|
+
part.body = message.body.decoded
|
31
|
+
end
|
32
|
+
part
|
33
|
+
end
|
34
|
+
|
35
|
+
# Detects a list of e-mails which should be used to define a list of
|
36
|
+
# recipients of encrypted message. All is simply taken from the message
|
37
|
+
# +To:+ field.
|
38
|
+
#
|
39
|
+
# @param [Mail::Message] message
|
40
|
+
# @return [Array] an array of e-mails
|
41
|
+
def find_recipients_for(message)
|
42
|
+
message.to_addrs
|
43
|
+
end
|
44
|
+
|
45
|
+
# Detects e-mail which should be used to find a message signer key.
|
46
|
+
# Basically, it is taken from the message +From:+ field, but may be
|
47
|
+
# overwritten by +:signer+ adapter option.
|
48
|
+
#
|
49
|
+
# @param [Mail::Message] message
|
50
|
+
# @return [String] an e-mail
|
51
|
+
def find_signer_for(message)
|
52
|
+
options[:signer] || message.from_addrs.first
|
53
|
+
end
|
54
|
+
|
55
|
+
# Replaces a message body. Clears all the existing body, be it multipart
|
56
|
+
# or not, and then appends parts passed as an argument.
|
57
|
+
#
|
58
|
+
# @param [Mail::Message] message
|
59
|
+
# Message which body is expected to be replaced.
|
60
|
+
# @param [String] content_type
|
61
|
+
# A new content type for message, required, must be kinda multipart.
|
62
|
+
# @param [Array] parts
|
63
|
+
# List of parts which the new message body is expected to be composed
|
64
|
+
# from.
|
65
|
+
# @return undefined
|
66
|
+
def rewrite_body(message, content_type:, parts: [])
|
67
|
+
message.body = nil
|
68
|
+
message.content_type = content_type
|
69
|
+
parts.each { |p| message.add_part(p) }
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# (c) Copyright 2018 Ribose Inc.
|
2
|
+
#
|
3
|
+
|
4
|
+
module EnMail
|
5
|
+
module Helpers
|
6
|
+
# Common interface for building adapters conforming to RFC 1847 "Security
|
7
|
+
# Multiparts for MIME: Multipart/Signed and Multipart/Encrypted".
|
8
|
+
# It provides +sign+ and +encrypt+ public methods.
|
9
|
+
module RFC1847
|
10
|
+
include MessageManipulation
|
11
|
+
|
12
|
+
# Encrypts a message in a +multipart/encrypted+ fashion as defined
|
13
|
+
# in RFC 1847.
|
14
|
+
#
|
15
|
+
# @param [Mail::Message] message
|
16
|
+
# Message which is expected to be encrypted.
|
17
|
+
def encrypt(message)
|
18
|
+
source_part = body_to_part(message)
|
19
|
+
recipients = find_recipients_for(message)
|
20
|
+
encrypted = encrypt_string(source_part.encoded, recipients).to_s
|
21
|
+
encrypted_part = build_encrypted_part(encrypted)
|
22
|
+
control_part = build_encryption_control_part
|
23
|
+
|
24
|
+
rewrite_body(
|
25
|
+
message,
|
26
|
+
content_type: multipart_encrypted_content_type,
|
27
|
+
parts: [control_part, encrypted_part],
|
28
|
+
)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Signs a message in a +multipart/signed+ fashion as defined in RFC 1847.
|
32
|
+
#
|
33
|
+
# @param [Mail::Message] message
|
34
|
+
# Message which is expected to be signed.
|
35
|
+
def sign(message)
|
36
|
+
source_part = body_to_part(message)
|
37
|
+
restrict_encoding(source_part)
|
38
|
+
signer = find_signer_for(message)
|
39
|
+
micalg, signature = compute_signature(source_part.encoded, signer)
|
40
|
+
signature_part = build_signature_part(signature)
|
41
|
+
|
42
|
+
rewrite_body(
|
43
|
+
message,
|
44
|
+
content_type: multipart_signed_content_type(micalg: micalg),
|
45
|
+
parts: [source_part, signature_part],
|
46
|
+
)
|
47
|
+
end
|
48
|
+
|
49
|
+
protected
|
50
|
+
|
51
|
+
def restrict_encoding(part)
|
52
|
+
if part.multipart?
|
53
|
+
part.parts.each { |p| restrict_encoding(p) }
|
54
|
+
else
|
55
|
+
ivar = "@enmail_rfc18467_encoding_restrictions"
|
56
|
+
part.instance_variable_set(ivar, true)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Builds a mail part containing the encrypted message, that is
|
61
|
+
# the 2nd subpart of +multipart/encrypted+ as defined in RFC 1847.
|
62
|
+
def build_encrypted_part(encrypted)
|
63
|
+
part = ::Mail::Part.new
|
64
|
+
part.content_type = encrypted_message_content_type
|
65
|
+
part.body = encrypted
|
66
|
+
part
|
67
|
+
end
|
68
|
+
|
69
|
+
# Builds a mail part containing the control information for encrypted
|
70
|
+
# message, that is the 1st subpart of +multipart/encrypted+ as defined in
|
71
|
+
# RFC 1847.
|
72
|
+
def build_encryption_control_part
|
73
|
+
part = ::Mail::Part.new
|
74
|
+
part.content_type = encryption_protocol
|
75
|
+
part.body = encryption_control_information
|
76
|
+
part
|
77
|
+
end
|
78
|
+
|
79
|
+
# Builds a mail part containing the digital signature, that is
|
80
|
+
# the 2nd subpart of +multipart/signed+ as defined in RFC 1847.
|
81
|
+
def build_signature_part(signature)
|
82
|
+
part = ::Mail::Part.new
|
83
|
+
part.content_type = sign_protocol
|
84
|
+
part.body = signature
|
85
|
+
part
|
86
|
+
end
|
87
|
+
|
88
|
+
def multipart_signed_content_type(protocol: sign_protocol, micalg:)
|
89
|
+
%[multipart/signed; protocol="#{protocol}"; micalg="#{micalg}"]
|
90
|
+
end
|
91
|
+
|
92
|
+
def multipart_encrypted_content_type(protocol: encryption_protocol)
|
93
|
+
%[multipart/encrypted; protocol="#{protocol}"]
|
94
|
+
end
|
95
|
+
|
96
|
+
# The encrypted message must have content type +application/octet-stream+,
|
97
|
+
# as defined in RFC 1847 p. 6.
|
98
|
+
def encrypted_message_content_type
|
99
|
+
"application/octet-stream"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# (c) Copyright 2018 Ribose Inc.
|
2
|
+
#
|
3
|
+
|
4
|
+
module EnMail
|
5
|
+
module Helpers
|
6
|
+
# Common interface for building adapters conforming to RFC 3156 "MIME
|
7
|
+
# Security with OpenPGP", which is an implementation of RFC 1847
|
8
|
+
# "Security Multiparts for MIME: Multipart/Signed and Multipart/Encrypted".
|
9
|
+
#
|
10
|
+
# See: https://tools.ietf.org/html/rfc3156
|
11
|
+
module RFC3156
|
12
|
+
include RFC1847
|
13
|
+
|
14
|
+
# The RFC 3156 explicitly allows for signing and encrypting data in
|
15
|
+
# a single OpenPGP message.
|
16
|
+
# See: https://tools.ietf.org/html/rfc3156#section-6.2
|
17
|
+
#
|
18
|
+
# rubocop:disable Metrics/MethodLength
|
19
|
+
def sign_and_encrypt_combined(message)
|
20
|
+
source_part = body_to_part(message)
|
21
|
+
restrict_encoding(source_part)
|
22
|
+
signer = find_signer_for(message)
|
23
|
+
recipients = find_recipients_for(message)
|
24
|
+
encrypted =
|
25
|
+
sign_and_encrypt_string(source_part.encoded, signer, recipients).to_s
|
26
|
+
encrypted_part = build_encrypted_part(encrypted)
|
27
|
+
control_part = build_encryption_control_part
|
28
|
+
|
29
|
+
rewrite_body(
|
30
|
+
message,
|
31
|
+
content_type: multipart_encrypted_content_type,
|
32
|
+
parts: [control_part, encrypted_part],
|
33
|
+
)
|
34
|
+
end
|
35
|
+
# rubocop:enable Metrics/MethodLength
|
36
|
+
|
37
|
+
# The RFC 3156 requires that the message is first signed, then encrypted.
|
38
|
+
# See: https://tools.ietf.org/html/rfc3156#section-6.1
|
39
|
+
def sign_and_encrypt_encapsulated(message)
|
40
|
+
sign(message)
|
41
|
+
encrypt(message)
|
42
|
+
end
|
43
|
+
|
44
|
+
protected
|
45
|
+
|
46
|
+
def sign_protocol
|
47
|
+
"application/pgp-signature"
|
48
|
+
end
|
49
|
+
|
50
|
+
def encryption_protocol
|
51
|
+
"application/pgp-encrypted"
|
52
|
+
end
|
53
|
+
|
54
|
+
# As defined in RFC 3156
|
55
|
+
def encryption_control_information
|
56
|
+
"Version: 1"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/lib/enmail/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: enmail
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
-
|
7
|
+
- Ribose Inc.
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-03-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: mail
|
@@ -28,30 +28,96 @@ dependencies:
|
|
28
28
|
name: bundler
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '1.14'
|
34
|
+
- - "<"
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: '3.0'
|
34
37
|
type: :development
|
35
38
|
prerelease: false
|
36
39
|
version_requirements: !ruby/object:Gem::Requirement
|
37
40
|
requirements:
|
38
|
-
- - "
|
41
|
+
- - ">="
|
39
42
|
- !ruby/object:Gem::Version
|
40
43
|
version: '1.14'
|
44
|
+
- - "<"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '3.0'
|
41
47
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
48
|
+
name: gpgme
|
43
49
|
requirement: !ruby/object:Gem::Requirement
|
44
50
|
requirements:
|
45
51
|
- - "~>"
|
46
52
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
53
|
+
version: '2.0'
|
48
54
|
type: :development
|
49
55
|
prerelease: false
|
50
56
|
version_requirements: !ruby/object:Gem::Requirement
|
51
57
|
requirements:
|
52
58
|
- - "~>"
|
53
59
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
60
|
+
version: '2.0'
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: pry
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: 0.10.3
|
68
|
+
- - "<"
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '0.12'
|
71
|
+
type: :development
|
72
|
+
prerelease: false
|
73
|
+
version_requirements: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 0.10.3
|
78
|
+
- - "<"
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '0.12'
|
81
|
+
- !ruby/object:Gem::Dependency
|
82
|
+
name: rake
|
83
|
+
requirement: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - ">="
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '10'
|
88
|
+
- - "<"
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '13'
|
91
|
+
type: :development
|
92
|
+
prerelease: false
|
93
|
+
version_requirements: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - ">="
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '10'
|
98
|
+
- - "<"
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: '13'
|
101
|
+
- !ruby/object:Gem::Dependency
|
102
|
+
name: rnp
|
103
|
+
requirement: !ruby/object:Gem::Requirement
|
104
|
+
requirements:
|
105
|
+
- - ">="
|
106
|
+
- !ruby/object:Gem::Version
|
107
|
+
version: 1.0.1
|
108
|
+
- - "<"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '2'
|
111
|
+
type: :development
|
112
|
+
prerelease: false
|
113
|
+
version_requirements: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 1.0.1
|
118
|
+
- - "<"
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
version: '2'
|
55
121
|
- !ruby/object:Gem::Dependency
|
56
122
|
name: rspec
|
57
123
|
requirement: !ruby/object:Gem::Requirement
|
@@ -67,50 +133,60 @@ dependencies:
|
|
67
133
|
- !ruby/object:Gem::Version
|
68
134
|
version: '3.0'
|
69
135
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
136
|
+
name: rspec-pgp_matchers
|
71
137
|
requirement: !ruby/object:Gem::Requirement
|
72
138
|
requirements:
|
73
139
|
- - "~>"
|
74
140
|
- !ruby/object:Gem::Version
|
75
|
-
version: 0.
|
141
|
+
version: 0.1.1
|
76
142
|
type: :development
|
77
143
|
prerelease: false
|
78
144
|
version_requirements: !ruby/object:Gem::Requirement
|
79
145
|
requirements:
|
80
146
|
- - "~>"
|
81
147
|
- !ruby/object:Gem::Version
|
82
|
-
version: 0.
|
148
|
+
version: 0.1.1
|
83
149
|
description: Encrypted Email in Ruby
|
84
150
|
email:
|
85
|
-
-
|
151
|
+
- open.source@ribose.com
|
86
152
|
executables: []
|
87
153
|
extensions: []
|
88
154
|
extra_rdoc_files: []
|
89
155
|
files:
|
156
|
+
- ".editorconfig"
|
90
157
|
- ".gitignore"
|
158
|
+
- ".gitmodules"
|
91
159
|
- ".hound.yml"
|
92
160
|
- ".rubocop.yml"
|
93
161
|
- ".travis.yml"
|
162
|
+
- ".yardopts"
|
94
163
|
- CODE_OF_CONDUCT.md
|
95
164
|
- Gemfile
|
96
|
-
-
|
165
|
+
- LICENSE.txt
|
166
|
+
- README.adoc
|
97
167
|
- REQUIREMENTS.md
|
98
168
|
- Rakefile
|
99
169
|
- bin/console
|
100
170
|
- bin/rspec
|
101
171
|
- bin/setup
|
172
|
+
- ci/install_botan.sh
|
173
|
+
- ci/install_json_c.sh
|
174
|
+
- ci/install_rnp.sh
|
175
|
+
- docs/GPGMEAdapter.adoc
|
176
|
+
- docs/RNPAdapter.adoc
|
102
177
|
- enmail.gemspec
|
103
178
|
- lib/enmail.rb
|
104
|
-
- lib/enmail/
|
105
|
-
- lib/enmail/
|
106
|
-
- lib/enmail/
|
107
|
-
- lib/enmail/
|
108
|
-
- lib/enmail/
|
109
|
-
- lib/enmail/
|
179
|
+
- lib/enmail/adapters/base.rb
|
180
|
+
- lib/enmail/adapters/gpgme.rb
|
181
|
+
- lib/enmail/adapters/gpgme_requirements.rb
|
182
|
+
- lib/enmail/adapters/rnp.rb
|
183
|
+
- lib/enmail/adapters/rnp_requirements.rb
|
184
|
+
- lib/enmail/dependency_constraints.rb
|
185
|
+
- lib/enmail/extensions/message_transport_encoding_restrictions.rb
|
186
|
+
- lib/enmail/helpers/message_manipulation.rb
|
187
|
+
- lib/enmail/helpers/rfc1847.rb
|
188
|
+
- lib/enmail/helpers/rfc3156.rb
|
110
189
|
- lib/enmail/version.rb
|
111
|
-
- lib/mail/secure/mail_interceptors/pgp.rb
|
112
|
-
- lib/mail/secure/models/key.rb
|
113
|
-
- lib/mail/secure/pgp_mailable.rb
|
114
190
|
homepage: https://github.com/riboseinc/enmail
|
115
191
|
licenses:
|
116
192
|
- MIT
|
@@ -130,8 +206,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
130
206
|
- !ruby/object:Gem::Version
|
131
207
|
version: '0'
|
132
208
|
requirements: []
|
133
|
-
|
134
|
-
rubygems_version: 2.5.2
|
209
|
+
rubygems_version: 3.0.1
|
135
210
|
signing_key:
|
136
211
|
specification_version: 4
|
137
212
|
summary: Encrypted Email in Ruby
|