sym-crypt 1.0.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 +7 -0
- data/.codeclimate.yml +30 -0
- data/.document +2 -0
- data/.gitignore +15 -0
- data/.rspec +3 -0
- data/.rubocop.yml +1156 -0
- data/.travis.yml +23 -0
- data/.yardopts +4 -0
- data/Gemfile +2 -0
- data/LICENSE +22 -0
- data/README.md +121 -0
- data/Rakefile +28 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/sym/crypt.rb +119 -0
- data/lib/sym/crypt/cipher_handler.rb +37 -0
- data/lib/sym/crypt/configuration.rb +44 -0
- data/lib/sym/crypt/data.rb +24 -0
- data/lib/sym/crypt/data/decoder.rb +31 -0
- data/lib/sym/crypt/data/encoder.rb +25 -0
- data/lib/sym/crypt/data/wrapper_struct.rb +44 -0
- data/lib/sym/crypt/errors.rb +14 -0
- data/lib/sym/crypt/extensions/class_methods.rb +15 -0
- data/lib/sym/crypt/extensions/instance_methods.rb +123 -0
- data/lib/sym/crypt/version.rb +17 -0
- data/sym-crypt.gemspec +37 -0
- metadata +189 -0
data/.travis.yml
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
language: ruby
|
2
|
+
cache: bundler
|
3
|
+
env:
|
4
|
+
CODECLIMATE_REPO_TOKEN=40b229d2d4857d67d832b6e0d5110537accb4e954961a8ce59beebf41e7d79b7
|
5
|
+
rvm:
|
6
|
+
- 2.2.7
|
7
|
+
- 2.3.4
|
8
|
+
- 2.4.1
|
9
|
+
- jruby-9.1.9.0
|
10
|
+
notifications:
|
11
|
+
email:
|
12
|
+
recipients:
|
13
|
+
on_success: change
|
14
|
+
on_failure: always
|
15
|
+
slack:
|
16
|
+
rooms:
|
17
|
+
secure: GcACCHmcmo99lYE5dgt2TmY4YIPMWr75q0Bkm4Lddegq/1eMXgcZDz00j8iHWtnveyJSjEUpZFQ9uyx+P7dvxT7D5gl43arKZEylS4vMRg9QRgZu7/ROrXbJtrUyKulFRUTdFrmOiK9nDKpAzlvLvMKC4kaQnn88Xtu9pYMU8y4n5fCtl29gZM9ZjVbrBBvV/SumnTDLla2oQ7oFgUZdZhQZ1qsevYknxNq4eIcvPDjbon+bovfsIISEVRSNo2C3yjO+mqWKed3jfuCA4taAZ8/aVVFEKURMH10HFms1CiC3lE/gF4YfBXsqKpjnB+bKjBcWox2FQrp96FulMEIfkKQrnuncO96o7MGHX9oNqKSMoycf3+SZFwNa+5Mfq7SBepbHYCoU7/ow31UTyStrc5idG19XzYgI85yjQHdzQN299Xf0O8cGf9q9qAu7rS7JlM3ekR+3rZYO+OxNYTF8rcW1fYIj6GKcbsV8BvuhAVQRFOsk/ibcIb2N7rGVd/ZbU4HAk3RE1ywt+XowinpIHktu2PQUPDNpgRQwM6xqZHO/7s9r7oW0ULmZCMLZ9CMKNjKzrQ9wfh90+v+FiEiUKsaVj4cCwF3PvhVYvxU2AKbCeHtSEb7K6to86+DYNoIKnGH1ryKoyCtyOTZpNMLGFt34Msv7WfEylhG+DpVMvkM=
|
18
|
+
addons:
|
19
|
+
code_climate:
|
20
|
+
repo_token:
|
21
|
+
# regular test configuration
|
22
|
+
after_success:
|
23
|
+
- bundle exec codeclimate-test-reporter
|
data/.yardopts
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright © 2016 Konstantin Gredeskoul, all rights reserved.
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
# SymCrypt
|
2
|
+
|
3
|
+
## Simple Symmetric Encryption Using OpenSSL
|
4
|
+
|
5
|
+
[](https://badge.fury.io/rb/sym-crypt)
|
6
|
+
[](https://rubygems.org/gems/sym-crypt)
|
7
|
+
|
8
|
+
[](https://travis-ci.org/kigster/sym-crypt)
|
9
|
+
[](https://codeclimate.com/github/kigster/sym-crypt)
|
10
|
+
[](https://codeclimate.com/github/kigster/sym-crypt/coverage)
|
11
|
+
[](https://codeclimate.com/github/kigster/sym-crypt)
|
12
|
+
|
13
|
+
[](https://gitter.im/kigster/sym)
|
14
|
+
|
15
|
+
---
|
16
|
+
|
17
|
+
### Summary
|
18
|
+
|
19
|
+
The `sym-crypt` core library offers simple wrappers around OpenSSL with the following features:
|
20
|
+
|
21
|
+
* Symmetric data encryption with either:
|
22
|
+
1. A generated or supplied/known 256-bit key, and using the Cipher `AES-256-cBC`, which is the standard Cipher used by the US Government
|
23
|
+
2. A user-provided password is used to construct a 128-bit key, and then encrypt/decrypt with the `AES-128-CBC` Cipher.
|
24
|
+
* Automatic compression of the data upon encryption
|
25
|
+
* Automatic base64 encryption to make all encrypted strings fit onto a single line.
|
26
|
+
* This makes the format suitable for YAML or JSON configuration files, where only the values are encrypted.
|
27
|
+
* Note: the generated key is a *base64-encoded* string of about 45 characters long. The *decoded* key is always 32 characters (or 256 bytes) long.
|
28
|
+
|
29
|
+
### Usage
|
30
|
+
|
31
|
+
This library is a "wrapper" that allows you to take advantage of the
|
32
|
+
symmetric encryption functionality provided by the {OpenSSL} gem (and the
|
33
|
+
underlying C library). In order to use the library in your ruby classes, you
|
34
|
+
should _include_ the module `Sym::Crypt`.
|
35
|
+
|
36
|
+
Any class that includes `Sym::Crypt` is decorated with four instance methods `[:encr, :decr, :encr_password, :decr_password]`, and three class methods `[:create_private_key, :private_key, :private_key=]`.
|
37
|
+
|
38
|
+
#### Symmetric Encryption with a 256-bit Private Key
|
39
|
+
|
40
|
+
In the example below, we read a previously generated key from the environment variable. The key must be stored away from the data for obvious reasons.
|
41
|
+
|
42
|
+
```ruby
|
43
|
+
require 'sym/crypt'
|
44
|
+
|
45
|
+
class TopSecret
|
46
|
+
include Sym::Crypt
|
47
|
+
# read the key from environmant variable and assign to this class.
|
48
|
+
private_key ENV['PRIVATE_KEY']
|
49
|
+
|
50
|
+
def sensitive_value=(value)
|
51
|
+
@sensitive_value = encr(value, self.class.private_key)
|
52
|
+
end
|
53
|
+
|
54
|
+
def sensitive_value
|
55
|
+
decr(@sensitive_value, self.class.private_key)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
```
|
59
|
+
|
60
|
+
#### Symmetric Encryption with a Password
|
61
|
+
|
62
|
+
In this example we encrypt sensitive value with a provided password. Password must not be nil or blank.
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
require 'sym/crypt'
|
66
|
+
|
67
|
+
class SensitiveStuff < Struct.new(:password)
|
68
|
+
include Sym::Crypt
|
69
|
+
|
70
|
+
def sensitive_value=(value)
|
71
|
+
@sensitive_value = encr_password(value, password)
|
72
|
+
end
|
73
|
+
|
74
|
+
def sensitive_value
|
75
|
+
decr_password(@sensitive_value, password)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
```
|
79
|
+
|
80
|
+
#### Generating an Encryption Key
|
81
|
+
|
82
|
+
You can create a new key within any class that includes `Sym::Crypt` by calling the `#create_private_key` class method, which returns a new key every time it's called.
|
83
|
+
|
84
|
+
Classes that include `Sym::Crypt` are also decorated with a class instance variable `@private_key` and corresponding accessors `#private_key` and `#private_key=`. The writer assigns the key passed via the argument, while the reader returns a previously assigned key, or creates a new one, and assigns it. Subsequent calls will, thus, return the same key as the first call.
|
85
|
+
|
86
|
+
## Development
|
87
|
+
|
88
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
89
|
+
|
90
|
+
To install this gem onto your local machine, run `bundle exec rake install`.
|
91
|
+
|
92
|
+
To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
93
|
+
|
94
|
+
#### Contributing
|
95
|
+
|
96
|
+
Bug reports and pull requests are welcome on GitHub at [https://github.com/kigster/sym-crypt](https://github.com/kigster/sym-crypt)
|
97
|
+
|
98
|
+
### License
|
99
|
+
|
100
|
+
**sym** and **sym-crypt** library is © 2016-2017 Konstantin Gredeskoul.
|
101
|
+
|
102
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT). The library is designed to be a layer on top of [`OpenSSL`](https://www.openssl.org/), distributed under the [Apache Style license](https://www.openssl.org/source/license.txt).
|
103
|
+
|
104
|
+
### Acknowledgements
|
105
|
+
|
106
|
+
* The blog post [(Symmetric) Encryption With Ruby (and Rails)](http://stuff-things.net/2015/02/12/symmetric-encryption-with-ruby-and-rails/) provided the inspiration for this gem.
|
107
|
+
* We'd like to thank [Spike Ilacqua](http://stuff-things.net/spike/), the author of the [strongbox](https://github.com/spikex/strongbox) gem, for providing very easy-to-read code examples of symmetric encryption.
|
108
|
+
|
109
|
+
#### Contributors:
|
110
|
+
|
111
|
+
Contributions of any kind are very much welcome from anyone.
|
112
|
+
|
113
|
+
Any pull requests will be reviewed promptly.
|
114
|
+
|
115
|
+
Please submit feature requests, bugs, or donations :)
|
116
|
+
|
117
|
+
* [Konstantin Gredeskoul](http:/kig.re) (primary developer)
|
118
|
+
* [Barry Anderson](https://twitter.com/z3ndrag0n) (sanity checking, review)
|
119
|
+
|
120
|
+
|
121
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
require 'rspec/core/rake_task'
|
3
|
+
require 'yard'
|
4
|
+
|
5
|
+
def shell(*args)
|
6
|
+
puts "running: #{args.join(' ')}"
|
7
|
+
system(args.join(' '))
|
8
|
+
end
|
9
|
+
|
10
|
+
task :permissions do
|
11
|
+
shell('rm -rf pkg/')
|
12
|
+
shell("chmod -v o+r,g+r * */* */*/* */*/*/* */*/*/*/* */*/*/*/*/*")
|
13
|
+
shell("find . -type d -exec chmod o+x,g+x {} \\;")
|
14
|
+
end
|
15
|
+
|
16
|
+
task :build => :permissions
|
17
|
+
|
18
|
+
YARD::Rake::YardocTask.new(:doc) do |t|
|
19
|
+
t.files = %w(lib/**/*.rb exe/*.rb - README.md LICENSE)
|
20
|
+
t.options.unshift('--title','"Sym – Symmetric Key Encryption for Your Data"')
|
21
|
+
t.after = ->() { exec('open doc/index.html') }
|
22
|
+
end
|
23
|
+
|
24
|
+
RSpec::Core::RakeTask.new(:spec)
|
25
|
+
|
26
|
+
task :default => :spec
|
27
|
+
|
28
|
+
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'sym-crypt'
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require 'irb'
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/lib/sym/crypt.rb
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
require 'zlib'
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
require 'sym/crypt/configuration'
|
5
|
+
require 'sym/crypt/version'
|
6
|
+
require 'sym/crypt/errors'
|
7
|
+
|
8
|
+
Sym::Crypt::Configuration.configure do |config|
|
9
|
+
config.password_cipher = 'AES-128-CBC'
|
10
|
+
config.data_cipher = 'AES-256-CBC'
|
11
|
+
config.private_key_cipher = config.data_cipher
|
12
|
+
config.compression_enabled = true
|
13
|
+
config.compression_level = Zlib::BEST_COMPRESSION
|
14
|
+
end
|
15
|
+
|
16
|
+
require 'sym/crypt/extensions/class_methods'
|
17
|
+
require 'sym/crypt/extensions/instance_methods'
|
18
|
+
|
19
|
+
#
|
20
|
+
# == Using Sym Library
|
21
|
+
#
|
22
|
+
# This library is a "wrapper" that allows you to take advantage of the
|
23
|
+
# symmetric encryption functionality provided by the {OpenSSL} gem (and the
|
24
|
+
# underlying C library). In order to use the library in your ruby classes, you
|
25
|
+
# should _include_ the module {Sym}.
|
26
|
+
#
|
27
|
+
# The including class is decorated with four instance methods from the
|
28
|
+
# module {Sym::Crypt::Extensions::InstanceMethods} and two class methods from
|
29
|
+
# {Sym::Crypt::Extensions::ClassMethods} – for specifics, please refer there.
|
30
|
+
#
|
31
|
+
# The two main instance methods are +#encr+ and +#decr+, which as the name
|
32
|
+
# implies, perform two-way symmetric encryption and decryption of any Ruby object
|
33
|
+
# that can be +marshaled+.
|
34
|
+
#
|
35
|
+
# Two additional instance methods +#encr_password+ and +#decr_password+ turn on
|
36
|
+
# password-based encryption, which actually uses a password to construct a 128-bit
|
37
|
+
# long private key, and then uses that in the encryption of the data.
|
38
|
+
# You could use them to encrypt data with a password instead of a randomly
|
39
|
+
# generated private key.
|
40
|
+
#
|
41
|
+
# Create a new key with +#create_private_key+ class method, which returns a new
|
42
|
+
# key every time it's called, or with +#private_key+ class method, which either
|
43
|
+
# assigns, or creates and caches the private key at a class level.
|
44
|
+
#
|
45
|
+
# == Example
|
46
|
+
#
|
47
|
+
# require 'sym/crypt'
|
48
|
+
#
|
49
|
+
# class TestClass
|
50
|
+
# include Sym::Crypt
|
51
|
+
# # read the key from environmant variable and assign to this class.
|
52
|
+
# private_key ENV['PRIVATE_KEY']
|
53
|
+
#
|
54
|
+
# def sensitive_value=(value)
|
55
|
+
# @sensitive_value = encr(value, self.class.private_key)
|
56
|
+
# end
|
57
|
+
#
|
58
|
+
# def sensitive_value
|
59
|
+
# decr(@sensitive_value, self.class.private_key)
|
60
|
+
# end
|
61
|
+
# end
|
62
|
+
#
|
63
|
+
# == Private Key
|
64
|
+
#
|
65
|
+
# They private key can be generated by +TestClass.create_private_key+
|
66
|
+
# which returns but does not store a new random 256-bit key.
|
67
|
+
#
|
68
|
+
# The key can be assigned and saved, or auto-generated and saved using the
|
69
|
+
# +#private_key+ method on the class that includes the +Sym+ module.
|
70
|
+
#
|
71
|
+
# Each class including the +Sym+ module would get their own +#private_key#
|
72
|
+
# class-instance variable accessor, and a possible value.
|
73
|
+
#
|
74
|
+
|
75
|
+
module Sym
|
76
|
+
module Crypt
|
77
|
+
NEW_CIPHER_PROC = ->(name) { ::OpenSSL::Cipher.new(name) }
|
78
|
+
|
79
|
+
def self.included(klass)
|
80
|
+
klass.instance_eval do
|
81
|
+
include ::Sym::Crypt::Extensions::InstanceMethods
|
82
|
+
extend ::Sym::Crypt::Extensions::ClassMethods
|
83
|
+
class << self
|
84
|
+
def private_key(value = nil)
|
85
|
+
if value
|
86
|
+
@private_key= value
|
87
|
+
elsif @private_key
|
88
|
+
@private_key
|
89
|
+
else
|
90
|
+
@private_key= self.create_private_key
|
91
|
+
end
|
92
|
+
@private_key
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
class << self
|
99
|
+
def config
|
100
|
+
Sym::Crypt::Configuration.config
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
|
107
|
+
|
108
|
+
class Object
|
109
|
+
unless self.methods.include?(:present?)
|
110
|
+
def present?
|
111
|
+
return false if self.nil?
|
112
|
+
if self.is_a?(String)
|
113
|
+
return false if self == ''
|
114
|
+
end
|
115
|
+
true
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'sym/crypt'
|
3
|
+
|
4
|
+
module Sym
|
5
|
+
module Crypt
|
6
|
+
|
7
|
+
# {Sym::Crypt::CipherHandler} contains cipher-related utilities necessary to create
|
8
|
+
# ciphers, and seed them with the salt or iV vector. It also defines the
|
9
|
+
# internal structure {Sym::Crypt::CipherHandler::CipherStruct} which is a key
|
10
|
+
# struct used in constructing cipher and saving it with the data packet.
|
11
|
+
module CipherHandler
|
12
|
+
|
13
|
+
CipherStruct = Struct.new(:cipher,
|
14
|
+
:iv,
|
15
|
+
:salt)
|
16
|
+
|
17
|
+
def create_cipher(direction:,
|
18
|
+
cipher_name:,
|
19
|
+
iv: nil,
|
20
|
+
salt: nil)
|
21
|
+
|
22
|
+
cipher = NEW_CIPHER_PROC.call(cipher_name)
|
23
|
+
cipher.send(direction)
|
24
|
+
iv ||= cipher.random_iv
|
25
|
+
cipher.iv = iv
|
26
|
+
CipherStruct.new(cipher, iv, salt)
|
27
|
+
end
|
28
|
+
|
29
|
+
def update_cipher(cipher, value)
|
30
|
+
data = cipher.update(value)
|
31
|
+
data << cipher.final
|
32
|
+
data
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Sym
|
2
|
+
module Crypt
|
3
|
+
# This class encapsulates application configuration, and exports
|
4
|
+
# a familiar method +#configure+ for defining configuration in a
|
5
|
+
# block.
|
6
|
+
#
|
7
|
+
# It's values are requested by the library upon encryption or
|
8
|
+
# decryption, or any other operation.
|
9
|
+
#
|
10
|
+
# == Example
|
11
|
+
#
|
12
|
+
# The following is an actual initialization from the code of this
|
13
|
+
# library. You may override any of the value defined below in your
|
14
|
+
# code, but _before_ you _use_ library's encryption methods.
|
15
|
+
#
|
16
|
+
# Sym::Crypt::Configuration.configure do |config|
|
17
|
+
# config.password_cipher = 'AES-128-CBC'
|
18
|
+
# config.data_cipher = 'AES-256-CBC'
|
19
|
+
# config.private_key_cipher = config.data_cipher
|
20
|
+
# config.compression_enabled = true
|
21
|
+
# config.compression_level = Zlib::BEST_COMPRESSION
|
22
|
+
# end
|
23
|
+
|
24
|
+
class Configuration
|
25
|
+
class << self
|
26
|
+
attr_accessor :config
|
27
|
+
|
28
|
+
def configure
|
29
|
+
self.config ||= Configuration.new
|
30
|
+
yield config if block_given?
|
31
|
+
end
|
32
|
+
|
33
|
+
def property(name)
|
34
|
+
self.config.send(name)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# See file +lib/sym/crypt.rb+ where these values are defined.
|
39
|
+
|
40
|
+
attr_accessor :data_cipher, :password_cipher, :private_key_cipher
|
41
|
+
attr_accessor :compression_enabled, :compression_level
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'zlib'
|
3
|
+
|
4
|
+
require 'sym/crypt/data/encoder'
|
5
|
+
require 'sym/crypt/data/decoder'
|
6
|
+
require 'sym/crypt/data/wrapper_struct'
|
7
|
+
|
8
|
+
module Sym
|
9
|
+
module Crypt
|
10
|
+
# This module is responsible for taking arbitrary data of any format, and safely compressing
|
11
|
+
# the result of `Marshal.dump(data)` using Zlib, and then doing `#urlsafe_encode64` encoding
|
12
|
+
# to convert it to a string,
|
13
|
+
module Data
|
14
|
+
def encode(data, compress = true)
|
15
|
+
Encoder.new(data, compress).data_encoded
|
16
|
+
end
|
17
|
+
|
18
|
+
def decode(data_encoded, compress = nil)
|
19
|
+
Decoder.new(data_encoded, compress).data
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|