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