3
3
require "openssl"
4
4
require "base64"
5
5
require "active_support/core_ext/module/attribute_accessors"
6
+ require "active_support/messages/codec"
7
+ require "active_support/messages/rotator"
6
8
require "active_support/message_verifier"
7
- require "active_support/messages/metadata"
8
9
9
10
module ActiveSupport
10
11
# MessageEncryptor is a simple way to encrypt values which get stored
@@ -84,8 +85,7 @@ module ActiveSupport
84
85
# the above should be combined into:
85
86
#
86
87
# crypt.rotate old_secret, cipher: "aes-256-cbc"
87
- class MessageEncryptor
88
- include Messages ::Metadata
88
+ class MessageEncryptor < Messages ::Codec
89
89
prepend Messages ::Rotator ::Encryptor
90
90
91
91
cattr_accessor :use_authenticated_message_encryption , instance_accessor : false , default : false
@@ -111,16 +111,6 @@ def self.dump(value)
111
111
end
112
112
end
113
113
114
- module NullVerifier # :nodoc:
115
- def self . verify ( value )
116
- value
117
- end
118
-
119
- def self . generate ( value )
120
- value
121
- end
122
- end
123
-
124
114
class InvalidMessage < StandardError ; end
125
115
OpenSSLCipherError = OpenSSL ::Cipher ::CipherError
126
116
@@ -147,21 +137,13 @@ class InvalidMessage < StandardError; end
147
137
# * <tt>:url_safe</tt> - Whether to encode messages using a URL-safe
148
138
# encoding. Default is +false+ for backward compatibility.
149
139
def initialize ( secret , sign_secret = nil , cipher : nil , digest : nil , serializer : nil , url_safe : false )
140
+ super ( serializer : serializer || @@default_message_encryptor_serializer , url_safe : url_safe )
150
141
@secret = secret
151
- @sign_secret = sign_secret
152
142
@cipher = cipher || self . class . default_cipher
153
143
@aead_mode = new_cipher . authenticated?
154
- @digest = digest || "SHA1" unless aead_mode?
155
- @serializer = serializer ||
156
- if @@default_message_encryptor_serializer . equal? ( :marshal )
157
- Marshal
158
- elsif @@default_message_encryptor_serializer . equal? ( :hybrid )
159
- JsonWithMarshalFallback
160
- elsif @@default_message_encryptor_serializer . equal? ( :json )
161
- JSON
162
- end
163
- @url_safe = url_safe
164
- @verifier = resolve_verifier
144
+ @verifier = if !@aead_mode
145
+ MessageVerifier . new ( sign_secret || secret , digest : digest || "SHA1" , serializer : NullSerializer , url_safe : url_safe )
146
+ end
165
147
end
166
148
167
149
# Encrypt and sign a message. We need to sign the message in order to avoid
@@ -191,8 +173,8 @@ def initialize(secret, sign_secret = nil, cipher: nil, digest: nil, serializer:
191
173
# The purpose of the message. If specified, the same purpose must be
192
174
# specified when verifying the message; otherwise, verification will fail.
193
175
# (See #decrypt_and_verify.)
194
- def encrypt_and_sign ( value , expires_at : nil , expires_in : nil , purpose : nil )
195
- verifier . generate ( _encrypt ( value , expires_at : expires_at , expires_in : expires_in , purpose : purpose ) )
176
+ def encrypt_and_sign ( value , ** options )
177
+ sign ( encrypt ( serialize_with_metadata ( value , ** options ) ) )
196
178
end
197
179
198
180
# Decrypt and verify a message. We need to verify the message in order to
@@ -212,8 +194,10 @@ def encrypt_and_sign(value, expires_at: nil, expires_in: nil, purpose: nil)
212
194
# encryptor.decrypt_and_verify(message) # => "bye"
213
195
# encryptor.decrypt_and_verify(message, purpose: "greeting") # => nil
214
196
#
215
- def decrypt_and_verify ( data , purpose : nil , **)
216
- _decrypt ( verifier . verify ( data ) , purpose )
197
+ def decrypt_and_verify ( message , **options )
198
+ deserialize_with_metadata ( decrypt ( verify ( message ) ) , **options )
199
+ rescue TypeError , ArgumentError , ::JSON ::ParserError
200
+ raise InvalidMessage
217
201
end
218
202
219
203
# Given a cipher, returns the key length of the cipher to help generate the key of desired size
@@ -222,17 +206,15 @@ def self.key_len(cipher = default_cipher)
222
206
end
223
207
224
208
private
225
- attr_reader :serializer
226
-
227
- def encode ( data )
228
- @url_safe ? ::Base64 . urlsafe_encode64 ( data , padding : false ) : ::Base64 . strict_encode64 ( data )
209
+ def sign ( data )
210
+ @verifier ? @verifier . generate ( data ) : data
229
211
end
230
212
231
- def decode ( data )
232
- @url_safe ? :: Base64 . urlsafe_decode64 ( data ) : :: Base64 . strict_decode64 ( data )
213
+ def verify ( data )
214
+ @verifier ? @verifier . verify ( data ) : data
233
215
end
234
216
235
- def _encrypt ( value , ** metadata_options )
217
+ def encrypt ( data )
236
218
cipher = new_cipher
237
219
cipher . encrypt
238
220
cipher . key = @secret
@@ -241,16 +223,16 @@ def _encrypt(value, **metadata_options)
241
223
iv = cipher . random_iv
242
224
cipher . auth_data = "" if aead_mode?
243
225
244
- encrypted_data = cipher . update ( serialize_with_metadata ( value , ** metadata_options ) )
226
+ encrypted_data = cipher . update ( data )
245
227
encrypted_data << cipher . final
246
228
247
229
parts = [ encrypted_data , iv ]
248
230
parts << cipher . auth_tag ( AUTH_TAG_LENGTH ) if aead_mode?
249
231
250
- parts . map! { | part | encode ( part ) } . join ( SEPARATOR )
232
+ join_parts ( parts )
251
233
end
252
234
253
- def _decrypt ( encrypted_message , purpose )
235
+ def decrypt ( encrypted_message )
254
236
cipher = new_cipher
255
237
encrypted_data , iv , auth_tag = extract_parts ( encrypted_message )
256
238
@@ -269,9 +251,7 @@ def _decrypt(encrypted_message, purpose)
269
251
270
252
decrypted_data = cipher . update ( encrypted_data )
271
253
decrypted_data << cipher . final
272
-
273
- deserialize_with_metadata ( decrypted_data , purpose : purpose )
274
- rescue OpenSSLCipherError , TypeError , ArgumentError , ::JSON ::ParserError
254
+ rescue OpenSSLCipherError
275
255
raise InvalidMessage
276
256
end
277
257
@@ -291,6 +271,10 @@ def length_of_encoded_auth_tag
291
271
@length_of_encoded_auth_tag ||= length_after_encode ( AUTH_TAG_LENGTH )
292
272
end
293
273
274
+ def join_parts ( parts )
275
+ parts . map! { |part | encode ( part ) } . join ( SEPARATOR )
276
+ end
277
+
294
278
def extract_part ( encrypted_message , rindex , length )
295
279
index = rindex - length
296
280
@@ -322,15 +306,7 @@ def new_cipher
322
306
OpenSSL ::Cipher . new ( @cipher )
323
307
end
324
308
325
- attr_reader :verifier , : aead_mode
309
+ attr_reader :aead_mode
326
310
alias :aead_mode? :aead_mode
327
-
328
- def resolve_verifier
329
- if aead_mode?
330
- NullVerifier
331
- else
332
- MessageVerifier . new ( @sign_secret || @secret , digest : @digest , serializer : NullSerializer , url_safe : @url_safe )
333
- end
334
- end
335
311
end
336
312
end
0 commit comments