Module: Mail::Gpg
- Defined in:
- lib/mail/gpg.rb,
lib/mail/gpg/version.rb,
lib/mail/gpg/sign_part.rb,
lib/mail/gpg/signed_part.rb,
lib/mail/gpg/gpgme_helper.rb,
lib/mail/gpg/version_part.rb,
lib/mail/gpg/message_patch.rb,
lib/mail/gpg/verified_part.rb,
lib/mail/gpg/decrypted_part.rb,
lib/mail/gpg/encrypted_part.rb,
lib/mail/gpg/delivery_handler.rb,
lib/mail/gpg/missing_keys_error.rb,
lib/mail/gpg/mime_signed_message.rb,
lib/mail/gpg/inline_signed_message.rb,
lib/mail/gpg/verify_result_attribute.rb,
lib/mail/gpg/inline_decrypted_message.rb,
lib/mail/gpg/rails/action_mailer_base_patch.rb
Defined Under Namespace
Modules: MessagePatch, Rails, VerifyResultAttribute Classes: DecryptedPart, DeliveryHandler, EncryptedPart, GpgmeHelper, InlineDecryptedMessage, InlineSignedMessage, MimeSignedMessage, MissingKeysError, SignPart, SignedPart, VerifiedPart, VersionPart
Constant Summary collapse
- BEGIN_PGP_MESSAGE_MARKER =
/^-----BEGIN PGP MESSAGE-----/- BEGIN_PGP_SIGNED_MESSAGE_MARKER =
/^-----BEGIN PGP SIGNED MESSAGE-----/- VERSION =
"0.4.6"
Class Method Summary collapse
- .construct_mail(cleartext_mail, options, &block) ⇒ Object
-
.copy_headers(from, to, overwrite: true) ⇒ Object
copies all header fields from mail in first argument to that given last.
-
.decrypt(encrypted_mail, options = {}) ⇒ Object
options are: :verify: decrypt and verify.
-
.decrypt_pgp_inline(encrypted_mail, options) ⇒ Object
decrypts inline PGP encrypted mail.
-
.decrypt_pgp_mime(encrypted_mail, options) ⇒ Object
decrypts PGP/MIME (RFC 3156, section 4) encrypted mail.
-
.encrypt(cleartext_mail, options = {}) ⇒ Object
options are: :sign: sign message using the sender's private key :sign_as: sign using this key (give the corresponding email address or key fingerprint) :password: passphrase for the signing key :keys: A hash mapping recipient email addresses to public keys or public key ids.
-
.encrypted?(mail) ⇒ Boolean
true if a mail is encrypted.
-
.encrypted_inline?(mail) ⇒ Boolean
check if inline PGP (i.e. if any parts of the mail includes the PGP MESSAGE marker).
-
.encrypted_mime?(mail) ⇒ Boolean
check if PGP/MIME encrypted (RFC 3156).
- .sign(cleartext_mail, options = {}) ⇒ Object
- .signature_valid?(signed_mail, options = {}) ⇒ Boolean
-
.signature_valid_inline?(signed_mail, options) ⇒ Boolean
check signature for inline signed mail.
-
.signature_valid_pgp_mime?(signed_mail, options) ⇒ Boolean
check signature for PGP/MIME (RFC 3156, section 5) signed mail.
-
.signed?(mail) ⇒ Boolean
true if a mail is signed.
-
.signed_inline?(mail) ⇒ Boolean
check if inline PGP (i.e. if any parts of the mail includes the PGP SIGNED marker).
-
.signed_mime?(mail) ⇒ Boolean
check if PGP/MIME signed (RFC 3156).
- .verify(signed_mail, options = {}) ⇒ Object
Class Method Details
.construct_mail(cleartext_mail, options, &block) ⇒ Object
108 109 110 111 112 113 114 115 116 117 118 |
# File 'lib/mail/gpg.rb', line 108 def self.construct_mail(cleartext_mail, , &block) Mail.new do self.perform_deliveries = cleartext_mail.perform_deliveries Mail::Gpg.copy_headers cleartext_mail, self # necessary? if cleartext_mail. header['Message-ID'] = cleartext_mail['Message-ID'].value end instance_eval &block end end |
.copy_headers(from, to, overwrite: true) ⇒ Object
copies all header fields from mail in first argument to that given last
185 186 187 188 189 190 191 |
# File 'lib/mail/gpg.rb', line 185 def self.copy_headers(from, to, overwrite: true) from.header.fields.each do |field| if overwrite || to.header[field.name].nil? to.header[field.name] = field.value end end end |
.decrypt(encrypted_mail, options = {}) ⇒ Object
options are: :verify: decrypt and verify
67 68 69 70 71 72 73 74 75 |
# File 'lib/mail/gpg.rb', line 67 def self.decrypt(encrypted_mail, = {}) if encrypted_mime?(encrypted_mail) decrypt_pgp_mime(encrypted_mail, ) elsif encrypted_inline?(encrypted_mail) decrypt_pgp_inline(encrypted_mail, ) else raise EncodingError, "Unsupported encryption format '#{encrypted_mail.content_type}'" end end |
.decrypt_pgp_inline(encrypted_mail, options) ⇒ Object
decrypts inline PGP encrypted mail
138 139 140 |
# File 'lib/mail/gpg.rb', line 138 def self.decrypt_pgp_inline(encrypted_mail, ) InlineDecryptedMessage.setup(encrypted_mail, ) end |
.decrypt_pgp_mime(encrypted_mail, options) ⇒ Object
decrypts PGP/MIME (RFC 3156, section 4) encrypted mail
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
# File 'lib/mail/gpg.rb', line 121 def self.decrypt_pgp_mime(encrypted_mail, ) if encrypted_mail.parts.length < 2 raise EncodingError, "RFC 3156 mandates exactly two body parts, found '#{encrypted_mail.parts.length}'" end if !VersionPart.isVersionPart? encrypted_mail.parts[0] raise EncodingError, "RFC 3156 first part not a valid version part '#{encrypted_mail.parts[0]}'" end decrypted = DecryptedPart.new(encrypted_mail.parts[1], ) Mail.new(decrypted.raw_source) do # headers from the encrypted part (set by the initializer above) take # precedence over those from the outer mail. Mail::Gpg.copy_headers encrypted_mail, self, overwrite: false verify_result decrypted.verify_result if [:verify] end end |
.encrypt(cleartext_mail, options = {}) ⇒ Object
options are: :sign: sign message using the sender's private key :sign_as: sign using this key (give the corresponding email address or key fingerprint) :password: passphrase for the signing key :keys: A hash mapping recipient email addresses to public keys or public key ids. Imports any keys given here that are not already part of the local keychain before sending the mail. :always_trust: send encrypted mail to untrusted receivers, true by default
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
# File 'lib/mail/gpg.rb', line 31 def self.encrypt(cleartext_mail, = {}) construct_mail(cleartext_mail, ) do receivers = [] receivers += cleartext_mail.to if cleartext_mail.to receivers += cleartext_mail.cc if cleartext_mail.cc receivers += cleartext_mail.bcc if cleartext_mail.bcc if [:sign_as] [:sign] = true [:signers] = .delete(:sign_as) elsif [:sign] [:signers] = cleartext_mail.from end add_part VersionPart.new add_part EncryptedPart.new(cleartext_mail, .merge({recipients: receivers})) content_type "multipart/encrypted; protocol=\"application/pgp-encrypted\"; boundary=#{boundary}" body.preamble = [:preamble] || "This is an OpenPGP/MIME encrypted message (RFC 2440 and 3156)" end end |
.encrypted?(mail) ⇒ Boolean
true if a mail is encrypted
88 89 90 91 92 |
# File 'lib/mail/gpg.rb', line 88 def self.encrypted?(mail) return true if encrypted_mime?(mail) return true if encrypted_inline?(mail) false end |
.encrypted_inline?(mail) ⇒ Boolean
check if inline PGP (i.e. if any parts of the mail includes the PGP MESSAGE marker)
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 |
# File 'lib/mail/gpg.rb', line 203 def self.encrypted_inline?(mail) return true if mail.body.to_s =~ BEGIN_PGP_MESSAGE_MARKER rescue nil if mail.multipart? mail.parts.each do |part| return true if part.body.to_s =~ BEGIN_PGP_MESSAGE_MARKER rescue nil return true if part.has_content_type? && /application\/(?:octet-stream|pgp-encrypted)/ =~ part.mime_type && /.*\.(?:pgp|gpg|asc)$/ =~ part.content_type_parameters[:name] && 'signature.asc' != part.content_type_parameters[:name] # that last condition above prevents false positives in case e.g. # someone forwards a mime signed mail including signature. end end false end |
.encrypted_mime?(mail) ⇒ Boolean
check if PGP/MIME encrypted (RFC 3156)
195 196 197 198 199 |
# File 'lib/mail/gpg.rb', line 195 def self.encrypted_mime?(mail) mail.has_content_type? && 'multipart/encrypted' == mail.mime_type && 'application/pgp-encrypted' == mail.content_type_parameters[:protocol] end |
.sign(cleartext_mail, options = {}) ⇒ Object
53 54 55 56 57 58 59 60 61 62 63 |
# File 'lib/mail/gpg.rb', line 53 def self.sign(cleartext_mail, = {}) [:sign_as] ||= cleartext_mail.from construct_mail(cleartext_mail, ) do to_be_signed = SignedPart.build(cleartext_mail) add_part to_be_signed add_part to_be_signed.sign() content_type "multipart/signed; micalg=pgp-sha1; protocol=\"application/pgp-signature\"; boundary=#{boundary}" body.preamble = [:preamble] || "This is an OpenPGP/MIME signed message (RFC 4880 and 3156)" end end |
.signature_valid?(signed_mail, options = {}) ⇒ Boolean
77 78 79 80 81 82 83 84 85 |
# File 'lib/mail/gpg.rb', line 77 def self.signature_valid?(signed_mail, = {}) if signed_mime?(signed_mail) signature_valid_pgp_mime?(signed_mail, ) elsif signed_inline?(signed_mail) signature_valid_inline?(signed_mail, ) else raise EncodingError, "Unsupported signature format '#{signed_mail.content_type}'" end end |
.signature_valid_inline?(signed_mail, options) ⇒ Boolean
check signature for inline signed mail
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 |
# File 'lib/mail/gpg.rb', line 164 def self.signature_valid_inline?(signed_mail, ) result = nil if signed_mail.multipart? signed_mail.parts.each do |part| if signed_inline?(part) if result.nil? result = true signed_mail.verify_result = [] end result &= signature_valid_inline?(part, ) signed_mail.verify_result << part.verify_result end end else result, verify_result = GpgmeHelper.inline_verify(signed_mail.body.to_s, ) signed_mail.verify_result = verify_result end return result end |
.signature_valid_pgp_mime?(signed_mail, options) ⇒ Boolean
check signature for PGP/MIME (RFC 3156, section 5) signed mail
153 154 155 156 157 158 159 160 161 |
# File 'lib/mail/gpg.rb', line 153 def self.signature_valid_pgp_mime?(signed_mail, ) # MUST contain exactly two body parts if signed_mail.parts.length != 2 raise EncodingError, "RFC 3156 mandates exactly two body parts, found '#{signed_mail.parts.length}'" end result, verify_result = SignPart.verify_signature(signed_mail.parts[0], signed_mail.parts[1], ) signed_mail.verify_result = verify_result return result end |
.signed?(mail) ⇒ Boolean
true if a mail is signed.
throws EncodingError if called on an encrypted mail (so only call this method if encrypted? is false)
97 98 99 100 101 102 103 104 |
# File 'lib/mail/gpg.rb', line 97 def self.signed?(mail) return true if signed_mime?(mail) return true if signed_inline?(mail) if encrypted?(mail) raise EncodingError, 'Unable to determine signature on an encrypted mail, use :verify option on decrypt()' end false end |
.signed_inline?(mail) ⇒ Boolean
check if inline PGP (i.e. if any parts of the mail includes the PGP SIGNED marker)
228 229 230 231 232 233 234 235 236 |
# File 'lib/mail/gpg.rb', line 228 def self.signed_inline?(mail) return true if mail.body.to_s =~ BEGIN_PGP_SIGNED_MESSAGE_MARKER rescue nil if mail.multipart? mail.parts.each do |part| return true if part.body.to_s =~ BEGIN_PGP_SIGNED_MESSAGE_MARKER rescue nil end end false end |
.signed_mime?(mail) ⇒ Boolean
check if PGP/MIME signed (RFC 3156)
220 221 222 223 224 |
# File 'lib/mail/gpg.rb', line 220 def self.signed_mime?(mail) mail.has_content_type? && 'multipart/signed' == mail.mime_type && 'application/pgp-signature' == mail.content_type_parameters[:protocol] end |
.verify(signed_mail, options = {}) ⇒ Object
142 143 144 145 146 147 148 149 150 |
# File 'lib/mail/gpg.rb', line 142 def self.verify(signed_mail, = {}) if signed_mime?(signed_mail) Mail::Gpg::MimeSignedMessage.setup signed_mail, elsif signed_inline?(signed_mail) Mail::Gpg::InlineSignedMessage.setup signed_mail, else signed_mail end end |