unxls 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ole/storage'
4
+ require 'openssl'
5
+ require 'set'
6
+ require 'zip'
7
+ require 'awesome_print' if $DEBUG
8
+ require 'pry' if $DEBUG
9
+
10
+ module Unxls
11
+ module Log; end # For easy output of values in different formats
12
+ class BitOps; end # For easy binary operations
13
+
14
+ module Helpers # Too small yet to move to a separate file
15
+ refine Object do
16
+ # Convert String to StringIO unless it's already StringIO
17
+ # @return [StringIO]
18
+ # @raises [RuntimeError] Unless receiver is a String or a StringIO
19
+ def to_sio
20
+ raise "Cannot convert #{self.class} to StringIO" unless self.kind_of?(StringIO) || self.instance_of?(String)
21
+ self.is_a?(StringIO) ? self : StringIO.new(self)
22
+ end
23
+ end
24
+ end
25
+
26
+ module Oshared; end # [MS-OSHARED]: Office Common Data Types and Objects Structures
27
+ module Dtyp; end # [MS-DTYP]: Windows Data Types
28
+ module Offcrypto; end # [MS-OFFCRYPTO]: Office Document Cryptography Structure
29
+
30
+ module Biff8 # Group code by BIFF version
31
+ module Constants; end # Large constants
32
+
33
+ class WorkbookStream; end # Deals with Workbook Stream (2.1.7.20)
34
+
35
+ class Record; end # Recipes for parsing of particular records (2.4)
36
+
37
+ module Structure; end # Deals with structures used in records (2.5)
38
+ module ParsedExpressions; end # Extends Structure. Deals with structures used in formulas (2.5.198). Defined in Structure's file for now.
39
+
40
+ class Browser; end
41
+ end
42
+
43
+ class Parser; end # Opens the compound file and parses the needed storages and streams (2.1.7)
44
+
45
+ end
46
+
47
+ require_relative 'version' unless defined?(Unxls::VERSION)
48
+ require_relative 'log'
49
+ require_relative 'bit_ops'
50
+ # require_relative '../../ext/bit_ops.so'
51
+ require_relative 'biff8/constants'
52
+ require_relative 'biff8/workbook_stream'
53
+ require_relative 'biff8/record'
54
+ require_relative 'biff8/structure'
55
+ require_relative 'biff8/browser'
56
+ require_relative 'oshared'
57
+ require_relative 'dtyp'
58
+ require_relative 'offcrypto'
59
+ require_relative 'parser'
@@ -0,0 +1,423 @@
1
+ # frozen_string_literal: true
2
+
3
+ # [MS-OFFCRYPTO]: Office Document Cryptography Structure
4
+ module Unxls::Offcrypto
5
+ extend self
6
+
7
+ BLOCK_SIZE = 1024
8
+
9
+ DEFAULT_PASSWORD = 'VelvetSweatshop'
10
+
11
+ #
12
+ # XOR obfuscation
13
+ #
14
+
15
+ XOR_PAD = [0xBB, 0xFF, 0xFF, 0xBA, 0xFF, 0xFF, 0xB9, 0x80, 0x00, 0xBE, 0x0F, 0x00, 0xBF, 0x0F, 0x00]
16
+
17
+ XOR_INITIAL_CODE = [
18
+ 0xE1F0, 0x1D0F, 0xCC9C, 0x84C0, 0x110C,
19
+ 0x0E10, 0xF1CE, 0x313E, 0x1872, 0xE139,
20
+ 0xD40F, 0x84F9, 0x280C, 0xA96A, 0x4EC3
21
+ ]
22
+
23
+ XOR_MATRIX = [
24
+ 0xAEFC, 0x4DD9, 0x9BB2, 0x2745, 0x4E8A, 0x9D14, 0x2A09,
25
+ 0x7B61, 0xF6C2, 0xFDA5, 0xEB6B, 0xC6F7, 0x9DCF, 0x2BBF,
26
+ 0x4563, 0x8AC6, 0x05AD, 0x0B5A, 0x16B4, 0x2D68, 0x5AD0,
27
+ 0x0375, 0x06EA, 0x0DD4, 0x1BA8, 0x3750, 0x6EA0, 0xDD40,
28
+ 0xD849, 0xA0B3, 0x5147, 0xA28E, 0x553D, 0xAA7A, 0x44D5,
29
+ 0x6F45, 0xDE8A, 0xAD35, 0x4A4B, 0x9496, 0x390D, 0x721A,
30
+ 0xEB23, 0xC667, 0x9CEF, 0x29FF, 0x53FE, 0xA7FC, 0x5FD9,
31
+ 0x47D3, 0x8FA6, 0x0F6D, 0x1EDA, 0x3DB4, 0x7B68, 0xF6D0,
32
+ 0xB861, 0x60E3, 0xC1C6, 0x93AD, 0x377B, 0x6EF6, 0xDDEC,
33
+ 0x45A0, 0x8B40, 0x06A1, 0x0D42, 0x1A84, 0x3508, 0x6A10,
34
+ 0xAA51, 0x4483, 0x8906, 0x022D, 0x045A, 0x08B4, 0x1168,
35
+ 0x76B4, 0xED68, 0xCAF1, 0x85C3, 0x1BA7, 0x374E, 0x6E9C,
36
+ 0x3730, 0x6E60, 0xDCC0, 0xA9A1, 0x4363, 0x86C6, 0x1DAD,
37
+ 0x3331, 0x6662, 0xCCC4, 0x89A9, 0x0373, 0x06E6, 0x0DCC,
38
+ 0x1021, 0x2042, 0x4084, 0x8108, 0x1231, 0x2462, 0x48C4
39
+ ]
40
+
41
+ # 2.3.7.2 Binary Document XOR Array Initialization Method 1
42
+ # FUNCTION CreateXorKey_Method1
43
+ # @param password [String]
44
+ # @return [Integer]
45
+ def _create_xor_key_method1(password)
46
+ ansi_password = _unicode_password_to_ansi(password)
47
+
48
+ xor_key = XOR_INITIAL_CODE[ansi_password.size - 1]
49
+ current_element = 0x68
50
+
51
+ ansi_password.bytes.reverse.each do |c|
52
+ 7.times do
53
+ xor_key ^= XOR_MATRIX[current_element] unless (c & 0x40).zero?
54
+ c <<= 1
55
+ current_element -= 1
56
+ end
57
+ end
58
+
59
+ xor_key
60
+ end
61
+
62
+ # 2.3.7.2 Binary Document XOR Array Initialization Method 1
63
+ # FUNCTION CreateXorArray_Method1
64
+ # @param password [String]
65
+ # @return [Array<Integer>]
66
+ def _create_xor_array_method1(password)
67
+ ansi_password = _unicode_password_to_ansi(password).bytes
68
+ xor_key = _create_xor_key_method1(password)
69
+ index = password.size
70
+ obfuscation_array = Array.new(16) { 0 }
71
+
72
+ xor_key_high = xor_key >> 8
73
+ xor_key_low = xor_key & 0xFF
74
+
75
+ if (index & 1) == 1
76
+ obfuscation_array[index] = _xor_ror(XOR_PAD[0], xor_key_high)
77
+ index -= 1
78
+ obfuscation_array[index] = _xor_ror(ansi_password[-1], xor_key_low)
79
+ end
80
+
81
+ while index > 0
82
+ index -= 1
83
+ obfuscation_array[index] = _xor_ror(ansi_password[index], xor_key_high)
84
+ index -= 1
85
+ obfuscation_array[index] = _xor_ror(ansi_password[index], xor_key_low)
86
+ end
87
+
88
+ index = 15
89
+ pad_index = 15 - password.size
90
+ while pad_index > 0
91
+ obfuscation_array[index] = _xor_ror(XOR_PAD[pad_index], xor_key_high)
92
+ index -= 1
93
+ pad_index -= 1
94
+ obfuscation_array[index] = _xor_ror(XOR_PAD[pad_index], xor_key_low)
95
+ index -= 1
96
+ pad_index -= 1
97
+ end
98
+
99
+ obfuscation_array
100
+ end
101
+
102
+ # 2.3.7.1 Binary Document Password Verifier Derivation Method 1
103
+ # FUNCTION CreatePasswordVerifier_Method1
104
+ # @param password [String]
105
+ # @return [Integer]
106
+ def _xor_password_verifier_method1(password)
107
+ verifier = 0
108
+ ansi_password = _unicode_password_to_ansi(password)
109
+
110
+ ansi_password.bytes.unshift(ansi_password.size).reverse.each do |b|
111
+ int1 = (verifier & 0b0100_0000_0000_0000).zero? ? 0 : 1
112
+ int2 = (verifier << 1) & 0b0111_1111_1111_1111
113
+ int3 = int1 | int2
114
+ verifier = int3 ^ b
115
+ end
116
+
117
+ verifier ^ 0xCE4B
118
+ end
119
+
120
+ # @param password [String]
121
+ # @param verification_bytes [String]
122
+ def _xor_password_match?(password, verification_bytes)
123
+ _xor_password_verifier_method1(password) == verification_bytes
124
+ end
125
+
126
+ # See 2.3.7.4 Binary Document Password Verifier Derivation Method 2 (p. 61)
127
+ # @note This method works reliably only for passwords consisting of ASCII characters
128
+ # @param password [String]
129
+ # @return [String]
130
+ def _unicode_password_to_ansi(password)
131
+ password.encode(Encoding::UTF_16LE).unpack('v*').map do |c|
132
+ low_byte = c & 0xFF
133
+ low_byte.zero? ? ((c >> 8) & 0xFF) : low_byte
134
+ end.pack('C*')
135
+ end
136
+
137
+ def _xor_ror(byte1, byte2)
138
+ Unxls::BitOps.new(byte1 ^ byte2).ror(8, 1)
139
+ end
140
+
141
+ # https://github.com/apache/poi/blob/trunk/src/java/org/apache/poi/poifs/crypt/CryptoFunctions.java#L445
142
+ # https://github.com/apache/poi/blob/trunk/src/java/org/apache/poi/poifs/crypt/xor/XORDecryptor.java
143
+ # @param byte [Integer]
144
+ # @param byte_index [Integer]
145
+ # @param xor_decryption_array [Array<Integer>]
146
+ # @param record_offset [Integer]
147
+ # @param record_size [Integer]
148
+ # @return [String]
149
+ def _xor_decrypt_byte(byte, byte_index, xor_decryption_array, record_offset, record_size)
150
+ # "The initial value for XorArrayIndex is as follows:
151
+ # XorArrayIndex = (FileOffset + Data.Length) % 16
152
+ # The FileOffset variable in this context is the stream offset into the Workbook stream at
153
+ # the time we are about to write each of the bytes of the record data.
154
+ # This (the value) is then incremented after each byte is written."
155
+ # From: http://social.msdn.microsoft.com/Forums/en-US/3dadbed3-0e68-4f11-8b43-3a2328d9ebd5
156
+ xor_array_index = (record_offset + Unxls::Biff8::Record::HEADER_SIZE + record_size + byte_index) & 0xF
157
+
158
+ byte ^= xor_decryption_array[xor_array_index]
159
+ byte = ((byte << 3) | (byte >> 5)) & 0xFF # rotate right 5 bits
160
+
161
+ [byte].pack('C')
162
+ end
163
+
164
+ #
165
+ # RC4
166
+ #
167
+
168
+ # 2.1.4 Version
169
+ # The Version structure specifies the version of a product or feature. It contains a major and a minor version number.
170
+ # @param data [String]
171
+ # @return [Hash]
172
+ def version(data)
173
+ v_major, v_minor = data.unpack('vv')
174
+
175
+ {
176
+ vMajor: v_major, # vMajor (2 bytes): An unsigned integer that specifies the major version number.
177
+ vMinor: v_minor # vMinor (2 bytes): An unsigned integer that specifies the minor version number.
178
+ }
179
+ end
180
+
181
+ # 2.3.6.1 RC4 Encryption Header
182
+ # @param io [StringIO]
183
+ # @return [Hash]
184
+ def rc4encryptionheader(io)
185
+ {
186
+ EncryptionVersionInfo: version(io.read(4)), # EncryptionVersionInfo (4 bytes): A Version structure (section 2.1.4), where Version.vMajor MUST be 0x0001 and Version.vMinor MUST be 0x0001.
187
+ Salt: io.read(16), # Salt (16 bytes): A randomly generated array of bytes that specifies the salt value used during password hash generation.
188
+ EncryptedVerifier: io.read(16), # EncryptedVerifier (16 bytes): An additional 16-byte verifier encrypted using a 40-bit RC4 cipher initialized as specified in section 2.3.6.2, with a block number of 0x00000000.
189
+ EncryptedVerifierHash: io.read(16) # EncryptedVerifierHash (16 bytes): A 40-bit RC4 encrypted MD5 hash of the verifier used to generate the EncryptedVerifier field.
190
+ }
191
+ end
192
+
193
+ # 2.3.6.2 Encryption Key Derivation
194
+ # @param password [String]
195
+ # @param salt [String]
196
+ # @param block_num [Integer]
197
+ # @return [String]
198
+ def _rc4_make_key(password, salt, block_num)
199
+ h0 = OpenSSL::Digest::MD5.digest(password.encode(Encoding::UTF_16LE))
200
+ buffer = (h0[0..4] + salt) * 16
201
+ h1 = OpenSSL::Digest::MD5.digest(buffer)
202
+ hfin = "#{h1[0..4]}#{[block_num].pack('V')}"
203
+ OpenSSL::Digest::MD5.digest(hfin)
204
+ end
205
+
206
+ # 2.3.6.4 Password Verification
207
+ # @param key [String]
208
+ # @param encr_verifier [String]
209
+ # @param encr_verifier_hash [String]
210
+ def _rc4_password_match?(key, encr_verifier, encr_verifier_hash)
211
+ cipher = OpenSSL::Cipher::RC4.new
212
+ cipher.decrypt
213
+ cipher.key = key
214
+ decrypted_verifier = cipher.update(encr_verifier) + cipher.final
215
+ decrypted_verifier_hash = cipher.update(encr_verifier_hash) + cipher.final
216
+ hashed_verifier = OpenSSL::Digest::MD5.digest(decrypted_verifier)
217
+ hashed_verifier == decrypted_verifier_hash
218
+ end
219
+
220
+ # @param data [String]
221
+ # @param key [String]
222
+ # @return [String]
223
+ def _rc4_decrypt(data, key)
224
+ cipher = OpenSSL::Cipher::RC4.new
225
+ cipher.decrypt
226
+ cipher.key = key
227
+ cipher.update(data) + cipher.final
228
+ end
229
+
230
+ #
231
+ # CryptoAPI
232
+ #
233
+
234
+ # 2.3.5.1 RC4 CryptoAPI Encryption Header
235
+ # @param io [StringIO]
236
+ # @return [Hash]
237
+ def rc4cryptoapiheader(io)
238
+ result = {
239
+ EncryptionVersionInfo: version(io.read(4)), # EncryptionVersionInfo (4 bytes): A Version structure (section 2.1.4) that specifies the encryption version used to create the document and the encryption version required to open the document.
240
+ EncryptionHeaderFlags: encryptionheaderflags(io.read(4).unpack('V').first) # EncryptionHeader.Flags (4 bytes): A copy of the Flags stored in the EncryptionHeader structure (section 2.3.2) that is stored in this stream.
241
+ }
242
+
243
+ encryption_header_size = io.read(4).unpack('V').first
244
+ result[:EncryptionHeaderSize] = encryption_header_size # EncryptionHeaderSize (4 bytes): An unsigned integer that specifies the size, in bytes, of the EncryptionHeader structure.
245
+ result[:EncryptionHeader] = encryptionheader(io) # EncryptionHeader (variable): An EncryptionHeader structure (section 2.3.2) used to encrypt the structure.
246
+ result[:EncryptionVerifier] = encryptionverifier(io) # EncryptionVerifier (variable): An EncryptionVerifier structure as specified in section 2.3.3 that is generated as specified in section 2.3.5.5.
247
+ result[:_encryption_algorithm] = _rc4cryptoapi_encryption_alg(result[:EncryptionHeader])
248
+ result[:_hashing_algorithm] = _rc4cryptoapi_hashing_alg(result[:EncryptionHeader])
249
+
250
+ result
251
+ end
252
+
253
+ # 2.3.2 EncryptionHeader
254
+ # The EncryptionHeader structure is used by ECMA-376 document encryption [ECMA-376] and Office binary document RC4 CryptoAPI encryption, as defined in section 2.3.5, to specify encryption properties for an encrypted stream.
255
+ # @param io [StringIO]
256
+ # @return [Hash]
257
+ def encryptionheader(io)
258
+ result = {
259
+ Flags: encryptionheaderflags(io.read(4).unpack('V').first), # Flags (4 bytes): An EncryptionHeaderFlags structure, as specified in section 2.3.1, that specifies properties of the encryption algorithm used.
260
+ SizeExtra: io.read(4).unpack('V').first, # SizeExtra (4 bytes): A field that is reserved and for which the value MUST be 0x00000000.
261
+ }
262
+
263
+ alg_id = io.read(4).unpack('l<').first # AlgID (4 bytes): A signed integer that specifies the encryption algorithm.
264
+ result[:AlgID] = alg_id
265
+ result[:AlgID_d] = {
266
+ 0x0000 => :'Determined by Flags',
267
+ 0x6801 => :RC4,
268
+ 0x660E => :AES128,
269
+ 0x660F => :AES192,
270
+ 0x6610 => :AES256
271
+ }[alg_id]
272
+
273
+ alg_id_hash, key_size, provider_type, _, _ = io.read(20).unpack('l<VV')
274
+ result[:AlgIDHash] = alg_id_hash # AlgIDHash (4 bytes): A signed integer that specifies the hashing algorithm together with the Flags.fExternal bit.
275
+ result[:KeySize] = key_size # KeySize (4 bytes): An unsigned integer that specifies the number of bits in the encryption key.
276
+ result[:ProviderType] = provider_type # ProviderType (4 bytes): An implementation-specific value that corresponds to constants accepted by the specified CSP.
277
+ # Reserved1 (4 bytes): A value that is undefined and MUST be ignored.
278
+ # Reserved2 (4 bytes): A value that MUST be 0x00000000 and MUST be ignored.
279
+
280
+ result[:CSPName] = Unxls::Oshared._db_zero_terminated(io) # CSPName (variable): A null-terminated Unicode string that specifies the CSP name.
281
+
282
+ result
283
+ end
284
+
285
+ # 2.3.1 EncryptionHeaderFlags
286
+ # The EncryptionHeaderFlags structure specifies properties of the encryption algorithm used.
287
+ # @param value [Integer]
288
+ # @return [Hash]
289
+ def encryptionheaderflags(value)
290
+ attrs = Unxls::BitOps.new(value)
291
+
292
+ {
293
+ # A – Reserved1 (1 bit): A value that MUST be 0 and MUST be ignored.
294
+ # B – Reserved2 (1 bit): A value that MUST be 0 and MUST be ignored.
295
+ fCryptoAPI: attrs.set_at?(2), # C – fCryptoAPI (1 bit): A flag that specifies whether CryptoAPI RC4 or ECMA-376 encryption [ECMA- 376] is used.
296
+ fDocProps: attrs.set_at?(3), # D – fDocProps (1 bit): A value that MUST be 0 if document properties are encrypted. The encryption of document properties is specified in section 2.3.5.4.
297
+ fExternal: attrs.set_at?(4), # E – fExternal (1 bit): A value that MUST be 1 if extensible encryption is used.
298
+ fAES: attrs.set_at?(5), # F – fAES (1 bit): A value that MUST be 1 if the protected content is an ECMA-376 document [ECMA- 376]
299
+ # Unused (26 bits): A value that is undefined and MUST be ignored.
300
+ }
301
+ end
302
+
303
+ # 2.3.3 EncryptionVerifier
304
+ # @param io [StringIO]
305
+ # @return [Hash]
306
+ def encryptionverifier(io)
307
+ {
308
+ SaltSize: io.read(4).unpack('V').first, # SaltSize (4 bytes): An unsigned integer that specifies the size of the Salt field.
309
+ Salt: io.read(16), # Salt (16 bytes): An array of bytes that specifies the salt value used during password hash generation.
310
+ EncryptedVerifier: io.read(16), # EncryptedVerifier (16 bytes): A value that MUST be the randomly generated Verifier value encrypted using the algorithm chosen by the implementation.
311
+ VerifierHashSize: io.read(4).unpack('V').first, # VerifierHashSize (4 bytes): An unsigned integer that specifies the number of bytes needed to contain the hash of the data used to generate the EncryptedVerifier field.
312
+ EncryptedVerifierHash: io.read # EncryptedVerifierHash (variable): An array of bytes that contains the encrypted form of the hash of the randomly generated Verifier value.
313
+ }
314
+ end
315
+
316
+ # See AlgIDHash description, p.33
317
+ # @param encryption_header [Hash]
318
+ # @return [Symbol]
319
+ def _rc4cryptoapi_hashing_alg(encryption_header)
320
+ combination = [
321
+ encryption_header[:AlgIDHash],
322
+ encryption_header[:Flags][:fExternal]
323
+ ]
324
+
325
+ case combination
326
+ when [0x0000, true] then :'Determined by the application'
327
+
328
+ when [0x0000, false],
329
+ [0x8004, false] then :SHA1
330
+
331
+ else raise "Unknown Flags and AlgIDHash combination: #{combination}"
332
+ end
333
+ end
334
+
335
+ # See AlgID description, p.33
336
+ # @param encryption_header [Hash]
337
+ # @return [Symbol]
338
+ def _rc4cryptoapi_encryption_alg(encryption_header)
339
+ combination = [
340
+ encryption_header[:Flags][:fCryptoAPI],
341
+ encryption_header[:Flags][:fAES],
342
+ encryption_header[:Flags][:fExternal],
343
+ encryption_header[:AlgID]
344
+ ]
345
+
346
+ case combination
347
+ when [false, false, true, 0x0000] then :'Determined by the application'
348
+
349
+ when [true, false, false, 0x0000],
350
+ [true, false, false, 0x6801] then :RC4
351
+
352
+ when [true, true, false, 0x0000],
353
+ [true, true, false, 0x660E] then :'AES-128-CBC'
354
+
355
+ when [true, true, false, 0x660F] then :'AES-192-CBC'
356
+
357
+ when [true, true, false, 0x6610] then :'AES-256-CBC'
358
+
359
+ else raise "Unknown Flags and AlgID combination: #{combination}"
360
+ end
361
+ end
362
+
363
+ # 2.3.5.2 RC4 CryptoAPI Encryption Key Generation
364
+ # @param password [String]
365
+ # @param salt [String]
366
+ # @param block_num [Integer]
367
+ # @param key_size [Integer]
368
+ # @param hash_alg [Symbol]
369
+ # @return [String]
370
+ def _rc4cryptoapi_make_key(password, salt, block_num, key_size, hash_alg)
371
+ h0_digest = _openssl_obj(:Digest, hash_alg)
372
+ h0_digest.update(salt)
373
+ h0_digest.update(password.encode(Encoding::UTF_16LE))
374
+ h0 = h0_digest.digest
375
+
376
+ h_digest = _openssl_obj(:Digest, hash_alg)
377
+ h_digest.update(h0)
378
+ h_digest.update([block_num].pack('V'))
379
+ h = h_digest.digest
380
+
381
+ h_fin = h[0..(key_size - 1)]
382
+ h_fin << ("\x00" * (16 - key_size)) if key_size < 16
383
+
384
+ h_fin
385
+ end
386
+
387
+ # 2.3.5.6 Password Verification
388
+ # @param key [String]
389
+ # @param encr_verifier [String]
390
+ # @param encr_verifier_hash [String]
391
+ # @param verifier_hash_size [Integer]
392
+ # @param hash_alg [Symbol]
393
+ # @param encr_alg [Symbol]
394
+ def _rc4cryptoapi_password_match?(key, encr_verifier, encr_verifier_hash, verifier_hash_size, hash_alg, encr_alg)
395
+ cipher = _openssl_obj(:Cipher, encr_alg)
396
+ cipher.decrypt
397
+ cipher.key = key
398
+ decrypted_verifier = cipher.update(encr_verifier) + cipher.final
399
+ decrypted_verifier_hash = cipher.update(encr_verifier_hash) + cipher.final
400
+ decrypted_verifier_hash = decrypted_verifier_hash[0..(verifier_hash_size - 1)]
401
+ hashed_verifier = _openssl_obj(:Digest, hash_alg).digest(decrypted_verifier)
402
+ hashed_verifier == decrypted_verifier_hash
403
+ end
404
+
405
+ # @param data [String]
406
+ # @param key [String]
407
+ # @param encr_alg [Symbol]
408
+ # @return [String]
409
+ def _rc4cryptoapi_decrypt(data, key, encr_alg)
410
+ cipher = _openssl_obj(:Cipher, encr_alg)
411
+ cipher.decrypt
412
+ cipher.key = key
413
+ cipher.update(data) + cipher.final
414
+ end
415
+
416
+ # @param type [Symbol] e.g. :Cipher, :Digest
417
+ # @param name [Symbol] e.g. :RC4, SHA1
418
+ # @return [OpenSSL::Cipher, OpenSSL::Digest] e.g. OpenSSL::Cipher::RC4 instance
419
+ def _openssl_obj(type, name)
420
+ Module.class_eval("OpenSSL::#{type}").new(name.to_s)
421
+ end
422
+
423
+ end