aesx 0.1.4 → 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 +4 -4
- data/lib/aesx.rb +32 -30
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4df3e7ebfe10780119148530407dffbc8b4d76ff0e60665405a1096932f4d4f5
|
4
|
+
data.tar.gz: 8579a2f88160d2686bd8b94284986beba974e11a1ba6f2daeef9f755de5001c2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: da63a66ca2fd099dd4473800f17cb2318a7b17fa17651a2ae8a9b438410d24c8255b63046bb1820f3de3ca82a0aecec53c90207a44995d2bbde5ac2d3944dfce
|
7
|
+
data.tar.gz: 980b512d63c2c4f36a33f926e542c449cfcc5f516c4eefa7a85e1bdc67113276f0ca3ef48e1027011621c7f66d74856deacd6fa0f2af1f237e89914aceaa95d2
|
data/lib/aesx.rb
CHANGED
@@ -8,8 +8,8 @@ require_relative 'compression'
|
|
8
8
|
|
9
9
|
# AESX - AES eXtended encryption library
|
10
10
|
#
|
11
|
-
# A lightweight encryption library that provides an extended version of
|
12
|
-
# the popular AES gem interface with modern ciphers. The default cipher
|
11
|
+
# A lightweight encryption library that provides an extended version of
|
12
|
+
# the popular AES gem interface with modern ciphers. The default cipher
|
13
13
|
# is AES-256-GCM.
|
14
14
|
#
|
15
15
|
# @example Basic usage
|
@@ -49,7 +49,7 @@ module AESX
|
|
49
49
|
# @return [Array<String>] List of available cipher names
|
50
50
|
def cipher_list
|
51
51
|
openssl_ciphers = OpenSSL::Cipher.ciphers.map(&:upcase)
|
52
|
-
CIPHER_SPECS.keys & openssl_ciphers
|
52
|
+
CIPHER_SPECS.keys & openssl_ciphers
|
53
53
|
end
|
54
54
|
|
55
55
|
# Encrypts plaintext using the specified key and options
|
@@ -97,7 +97,7 @@ module AESX
|
|
97
97
|
def key(length = nil, format = :plain, cipher: 'AES-256-GCM')
|
98
98
|
key_length = length ? length / 8 : CIPHER_SPECS[cipher.upcase][0]
|
99
99
|
key = OpenSSL::Random.random_bytes(key_length)
|
100
|
-
format == :base_64 ? Base64.
|
100
|
+
format == :base_64 ? Base64.strict_encode64(key).chomp : key
|
101
101
|
end
|
102
102
|
|
103
103
|
# Generates a random initialization vector of appropriate length for the specified cipher
|
@@ -109,16 +109,16 @@ module AESX
|
|
109
109
|
def iv(format = :plain, cipher: 'AES-256-GCM')
|
110
110
|
iv_length = CIPHER_SPECS[cipher.upcase][1]
|
111
111
|
iv = OpenSSL::Random.random_bytes(iv_length)
|
112
|
-
format == :base_64 ? Base64.
|
112
|
+
format == :base_64 ? Base64.strict_encode64(iv).chomp : iv
|
113
113
|
end
|
114
|
-
|
114
|
+
|
115
115
|
# Returns the default compression algorithm
|
116
116
|
#
|
117
117
|
# @return [Symbol, nil] The symbol representing the default algorithm, or nil if none available
|
118
118
|
def default_compression
|
119
119
|
AESCompression.default_algorithm
|
120
120
|
end
|
121
|
-
|
121
|
+
|
122
122
|
# Returns an array of available compression algorithms
|
123
123
|
#
|
124
124
|
# @return [Array<Symbol>] Symbols representing available compression algorithms
|
@@ -148,7 +148,7 @@ module AESX
|
|
148
148
|
# allow laziness
|
149
149
|
if opts.key?(:compress)
|
150
150
|
opts[:compression] = opts.delete(:compress)
|
151
|
-
end
|
151
|
+
end
|
152
152
|
@options = {
|
153
153
|
format: :base_64, # Default output format for encryption
|
154
154
|
cipher: "AES-256-GCM", # GCM mode
|
@@ -198,7 +198,7 @@ module AESX
|
|
198
198
|
|
199
199
|
# Get compression option from opts or fallback to options
|
200
200
|
compression = opts.key?(:compression) ? opts[:compression] : @options[:compression]
|
201
|
-
|
201
|
+
|
202
202
|
# If compression is a symbol or truthy value (but not true), use it as the algorithm
|
203
203
|
if compression.is_a?(Symbol) || (compression && compression != true)
|
204
204
|
# Check if specified algorithm is available
|
@@ -218,13 +218,13 @@ module AESX
|
|
218
218
|
fmt = opts[:format] || @options[:format]
|
219
219
|
case fmt
|
220
220
|
when :base_64
|
221
|
-
iv_b64 = Base64.
|
222
|
-
ciphertext_b64 = Base64.
|
223
|
-
auth_tag_b64 = auth_tag ? Base64.
|
224
|
-
|
221
|
+
iv_b64 = Base64.strict_encode64(iv).chomp
|
222
|
+
ciphertext_b64 = Base64.strict_encode64(ciphertext).chomp
|
223
|
+
auth_tag_b64 = auth_tag ? Base64.strict_encode64(auth_tag).chomp : nil
|
224
|
+
|
225
225
|
# Add compression flag
|
226
226
|
comp_flag = compression_algorithm ? AESCompression::ALGORITHM_IDS[compression_algorithm].to_s : "0"
|
227
|
-
|
227
|
+
|
228
228
|
if auth_tag_b64
|
229
229
|
result = "#{iv_b64}$#{ciphertext_b64}$#{auth_tag_b64}$#{comp_flag}"
|
230
230
|
else
|
@@ -236,10 +236,10 @@ module AESX
|
|
236
236
|
# auth_tag length is 0-16, variable in CCM
|
237
237
|
auth_tag_size = auth_tag ? auth_tag.bytesize : 0
|
238
238
|
packed_lengths = ((iv.bytesize - 7) << 5) | (auth_tag_size & 0x1F)
|
239
|
-
|
239
|
+
|
240
240
|
# Add a second byte for compression algorithm
|
241
241
|
compression_byte = AESCompression::ALGORITHM_IDS[compression_algorithm] || 0
|
242
|
-
|
242
|
+
|
243
243
|
if auth_tag
|
244
244
|
pack_format = "CC a#{iv.bytesize} a* a#{auth_tag.bytesize}"
|
245
245
|
[packed_lengths, compression_byte, iv, ciphertext, auth_tag].pack(pack_format)
|
@@ -277,7 +277,9 @@ module AESX
|
|
277
277
|
# unless it's Base64 encoded?
|
278
278
|
parts = encrypted_data.split('$')
|
279
279
|
if parts.size.between?(3, 4)
|
280
|
-
all_base64 = parts.all?
|
280
|
+
all_base64 = parts.all? do |str|
|
281
|
+
str.nil? || str.empty? || str.gsub(/\s+/, '') =~ /\A[A-Za-z0-9+\/=]*\z/
|
282
|
+
end
|
281
283
|
if all_base64
|
282
284
|
opts[:format] = :base_64
|
283
285
|
end
|
@@ -291,11 +293,11 @@ module AESX
|
|
291
293
|
ciphertext_b64 = parts[1]
|
292
294
|
auth_tag_b64 = parts[2] if parts.size >= 3 && !parts[2].nil? && !parts[2].empty?
|
293
295
|
compression_code = parts[3] if parts.size >= 4
|
294
|
-
|
295
|
-
iv = Base64.decode64(iv_b64)
|
296
|
-
ciphertext = Base64.decode64(ciphertext_b64)
|
297
|
-
auth_tag = auth_tag_b64 ? Base64.decode64(auth_tag_b64) : nil
|
298
|
-
|
296
|
+
|
297
|
+
iv = Base64.decode64(iv_b64.gsub(/\s+/, ''))
|
298
|
+
ciphertext = Base64.decode64(ciphertext_b64.gsub(/\s+/, ''))
|
299
|
+
auth_tag = auth_tag_b64 ? Base64.decode64(auth_tag_b64.gsub(/\s+/, '')) : nil
|
300
|
+
|
299
301
|
# Determine compression algorithm from the code
|
300
302
|
if compression_code && compression_code != "0"
|
301
303
|
algorithm_id = compression_code.to_i
|
@@ -304,17 +306,17 @@ module AESX
|
|
304
306
|
when :binary
|
305
307
|
# Extract the first byte which contains IV and auth tag lengths
|
306
308
|
lengths = encrypted_data.unpack1('C')
|
307
|
-
|
309
|
+
|
308
310
|
# Extract the second byte which contains compression info
|
309
311
|
compression_byte = encrypted_data.unpack('CC')[1]
|
310
|
-
|
312
|
+
|
311
313
|
# Calculate IV length and auth tag length
|
312
314
|
iv_len = ((lengths >> 5) & 0x07) + 7
|
313
315
|
tag_len = lengths & 0x1F
|
314
|
-
|
316
|
+
|
315
317
|
# Extract IV, ciphertext, and auth tag
|
316
318
|
iv = encrypted_data[2, iv_len] # 2 bytes of header now
|
317
|
-
|
319
|
+
|
318
320
|
if tag_len > 0
|
319
321
|
auth_tag = encrypted_data[-tag_len, tag_len]
|
320
322
|
# Ciphertext starts after header and IV, ends before auth tag
|
@@ -323,7 +325,7 @@ module AESX
|
|
323
325
|
auth_tag = nil
|
324
326
|
ciphertext = encrypted_data[(2 + iv_len)..]
|
325
327
|
end
|
326
|
-
|
328
|
+
|
327
329
|
# Get compression algorithm
|
328
330
|
compression_algorithm = AESCompression::ID_TO_ALGORITHM[compression_byte] if compression_byte != 0
|
329
331
|
else
|
@@ -340,7 +342,7 @@ module AESX
|
|
340
342
|
@cipher.padding = opts[:padding] || @options[:padding]
|
341
343
|
|
342
344
|
decrypted_data = @cipher.update(ciphertext) + @cipher.final
|
343
|
-
|
345
|
+
|
344
346
|
# Apply decompression if data was compressed
|
345
347
|
if compression_algorithm
|
346
348
|
begin
|
@@ -374,7 +376,7 @@ module AESX
|
|
374
376
|
def normalize_key(key, cipher = 'AES-256-GCM', iterations: 10000)
|
375
377
|
key_length = CIPHER_SPECS[cipher.upcase][0]
|
376
378
|
return key if key.bytesize == key_length
|
377
|
-
|
379
|
+
|
378
380
|
if key.match?(/\A[0-9a-fA-F]+\z/) # is it a hex string?
|
379
381
|
key = key.unpack('a2' * key_length).map { |x| x.hex }.pack('c' * key_length)
|
380
382
|
else
|
@@ -383,7 +385,7 @@ module AESX
|
|
383
385
|
# Use PBKDF2 for key derivation
|
384
386
|
key = OpenSSL::PKCS5.pbkdf2_hmac(key,salt,iterations,key_length,OpenSSL::Digest::SHA256.new)
|
385
387
|
end
|
386
|
-
|
388
|
+
|
387
389
|
key
|
388
390
|
end
|
389
391
|
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: aesx
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tom lahti
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: openssl
|
@@ -100,7 +100,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
100
100
|
- !ruby/object:Gem::Version
|
101
101
|
version: '0'
|
102
102
|
requirements: []
|
103
|
-
rubygems_version: 3.6.
|
103
|
+
rubygems_version: 3.6.8
|
104
104
|
specification_version: 4
|
105
105
|
summary: AES gem, but with GCM/CTR ciphers, compression, and more
|
106
106
|
test_files: []
|