keystok 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/.gitignore +20 -0
- data/.rspec +4 -0
- data/.rubocop.yml +4 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +1 -0
- data/README.md +74 -0
- data/Rakefile +8 -0
- data/keystok.gemspec +34 -0
- data/lib/generators/keystok/install_generator.rb +25 -0
- data/lib/generators/keystok/templates/keystok.rb +6 -0
- data/lib/generators/keystok/templates/keystok.yml.erb +10 -0
- data/lib/keystok/aes_crypto.rb +49 -0
- data/lib/keystok/cache.rb +40 -0
- data/lib/keystok/client.rb +198 -0
- data/lib/keystok/errors.rb +20 -0
- data/lib/keystok/railtie.rb +13 -0
- data/lib/keystok/version.rb +9 -0
- data/lib/keystok.rb +26 -0
- data/spec/fixtures/encrypted_data_00.data +1 -0
- data/spec/fixtures/response_data_00.data +8 -0
- data/spec/functional/aes_crypto_spec.rb +90 -0
- data/spec/functional/cache_spec.rb +119 -0
- data/spec/functional/client_spec.rb +605 -0
- data/spec/functional/keystok_spec.rb +33 -0
- data/spec/integration/rails_integration_spec.rb +82 -0
- data/spec/spec_helper.rb +17 -0
- metadata +233 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 9d3ce263f46ece47e3d9451f0c46b18df5064eb2
|
4
|
+
data.tar.gz: 2c9e6f747bc934a6cfbc4c3e24dc89426d512021
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b553e40de08d9bc179121066bfd1d99b9952d9293283c1eedbcb9363a2e579a893a02f0926c242f1168db88a4b2d03f04e1fcea923ce489b9c34d76457540737
|
7
|
+
data.tar.gz: b7de1606a6be130ded57c50248b0f1f646c17cf69fc4a716cd91594d872d0ffdad7fa8da5321cfaa3ef8ec74d7bff9c44f7942bf4d1c8256950ccd61a7ddc3f7
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
Copyright (c) 2014 GitDock Oy
|
data/README.md
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# Keystok
|
2
|
+
|
3
|
+

|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
$ gem install keystok
|
8
|
+
|
9
|
+
## Usage
|
10
|
+
|
11
|
+
require 'keystok'
|
12
|
+
require 'yaml' # Config is then easier
|
13
|
+
config = YAML.load_file('keystok.yml')
|
14
|
+
# or config can be just hash:
|
15
|
+
# config = { connection_string: 'das21312312_connection_string',
|
16
|
+
# api_host: 'https://api_custom_domain.keystok.com'
|
17
|
+
# }
|
18
|
+
keystok = Keystok::Client.new(config)
|
19
|
+
# get hash with all keys
|
20
|
+
keystok.keys
|
21
|
+
# get one key
|
22
|
+
keystok.get(key_id)
|
23
|
+
|
24
|
+
### Rails integration
|
25
|
+
|
26
|
+
Add keystok to Gemfile:
|
27
|
+
|
28
|
+
gem 'keystok'
|
29
|
+
|
30
|
+
Keystok have generator that can create config, initializer and .gitignore entry for you
|
31
|
+
|
32
|
+
rails g keystok:install connection_string_value
|
33
|
+
|
34
|
+
In Rails env Keystok will use Rails.logger. Without Rails it will default to logging to STDOUT.
|
35
|
+
If you would like to define your own logger you can do it like:
|
36
|
+
|
37
|
+
Keystok.logger = your_logger
|
38
|
+
|
39
|
+
## Config options description
|
40
|
+
|
41
|
+
### api_host, auth_host
|
42
|
+
|
43
|
+
Those values are not needed by default. It can be required if you are using separated Keystok servers
|
44
|
+
|
45
|
+
### connection_string
|
46
|
+
|
47
|
+
This value is visible in app page in Keystok service
|
48
|
+
|
49
|
+
### eager_fetching
|
50
|
+
|
51
|
+
When *true* client will fetch all keys from API even when one was requested. This will save time
|
52
|
+
when another key will be requested, because client will not have to fetch it from API.
|
53
|
+
However in some cases like huge amount of keys or when *volatile* is set,
|
54
|
+
this option should be set to *false*. Default value is *true*
|
55
|
+
|
56
|
+
### no_cache
|
57
|
+
|
58
|
+
If you don't want to use cache functionality, you can disable cache writing by setting *no_cache* to *true* in config
|
59
|
+
|
60
|
+
### tmp_dir
|
61
|
+
|
62
|
+
This dir will be used to store encrypted cache which can be used when API can't be contacted
|
63
|
+
|
64
|
+
### volatile
|
65
|
+
|
66
|
+
Normally when asking about key that is in local store, SDK will not perform API request to provide quicker response.
|
67
|
+
Request can be forced on per method call basis by second parameter set to *true* (default is *false*)
|
68
|
+
|
69
|
+
keystok.get('this_can_change_often', true)
|
70
|
+
|
71
|
+
However if you want SDK to make request on every request without adding *true* to every *get* or *keys*,
|
72
|
+
you can set *volatile* to *true* in config. This will force SDK to ask API on every request.
|
73
|
+
Please note that this will may decrese performance of you app since it will require API request
|
74
|
+
and cache writing on every *get* and *keys* call
|
data/Rakefile
ADDED
data/keystok.gemspec
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'keystok/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'keystok'
|
8
|
+
spec.version = Keystok::VERSION
|
9
|
+
spec.authors = ['Piotr Sokolowski']
|
10
|
+
spec.email = ['piotr@keystok.com']
|
11
|
+
spec.summary = %q{Keystok API client}
|
12
|
+
spec.description = <<-EOS
|
13
|
+
Ruby client for Keystok service. https://keystok.com
|
14
|
+
EOS
|
15
|
+
spec.homepage = 'https://keystok.com'
|
16
|
+
spec.license = 'GitDock Oy'
|
17
|
+
spec.files = `git ls-files -z`.split("\x0")
|
18
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
19
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
20
|
+
spec.require_paths = ['lib']
|
21
|
+
|
22
|
+
spec.add_dependency 'faraday'
|
23
|
+
spec.add_dependency 'oauth2'
|
24
|
+
|
25
|
+
spec.add_development_dependency 'bundler'
|
26
|
+
spec.add_development_dependency 'fuubar'
|
27
|
+
spec.add_development_dependency 'pry'
|
28
|
+
spec.add_development_dependency 'rails', '>= 3.0'
|
29
|
+
spec.add_development_dependency 'rake'
|
30
|
+
spec.add_development_dependency 'rspec'
|
31
|
+
spec.add_development_dependency 'rspec_junit_formatter'
|
32
|
+
spec.add_development_dependency 'simplecov'
|
33
|
+
spec.add_development_dependency 'webmock'
|
34
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# This file is distributed under GitDock Oy license terms.
|
3
|
+
# See https://bitbucket.org/keystok/keystok-client-ruby/raw/master/LICENSE.txt
|
4
|
+
# for details.
|
5
|
+
|
6
|
+
module Keystok
|
7
|
+
module Generators
|
8
|
+
# Generator that creates config file and initializer
|
9
|
+
# It also add config file to .gitignore
|
10
|
+
class InstallGenerator < Rails::Generators::Base
|
11
|
+
argument :connection_string,
|
12
|
+
type: :string,
|
13
|
+
banner: 'connection_string'
|
14
|
+
|
15
|
+
source_root File.expand_path('../templates', __FILE__)
|
16
|
+
|
17
|
+
desc 'Create initializer'
|
18
|
+
|
19
|
+
def create_initializer_and_config_file
|
20
|
+
template 'keystok.yml.erb', 'config/keystok.yml'
|
21
|
+
template 'keystok.rb', 'config/initializers/keystok.rb'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# This file is distributed under GitDock Oy license terms.
|
3
|
+
# See https://bitbucket.org/keystok/keystok-client-ruby/raw/master/LICENSE.txt
|
4
|
+
# for details.
|
5
|
+
|
6
|
+
require 'base64'
|
7
|
+
require 'json'
|
8
|
+
require 'openssl'
|
9
|
+
|
10
|
+
module Keystok
|
11
|
+
# Module handling data encryption and decryption
|
12
|
+
# It also handles raw data from API unpacking
|
13
|
+
module AESCrypto
|
14
|
+
PREFIX = ':aes256:'
|
15
|
+
|
16
|
+
def cipher(plain_text, key, iv, key_size = 256, block_mode = :CBC)
|
17
|
+
cipher = OpenSSL::Cipher::AES.new(key_size, block_mode)
|
18
|
+
cipher.encrypt
|
19
|
+
cipher.key = key
|
20
|
+
cipher.iv = iv
|
21
|
+
cipher.update(plain_text) + cipher.final
|
22
|
+
end
|
23
|
+
|
24
|
+
def decipher(encrypted, key, iv, key_size = 256, block_mode = :CBC)
|
25
|
+
decipher = OpenSSL::Cipher::AES.new(key_size, block_mode)
|
26
|
+
decipher.decrypt
|
27
|
+
decipher.key = key
|
28
|
+
decipher.iv = iv
|
29
|
+
decipher.update(encrypted) + decipher.final
|
30
|
+
end
|
31
|
+
|
32
|
+
def decrypt_key(encrypted_key, config = nil)
|
33
|
+
config ||= @config || {}
|
34
|
+
fail Error::ConfigError, 'No decryption key in config' unless config[:dk]
|
35
|
+
unless encrypted_key.start_with?(PREFIX)
|
36
|
+
fail Error::UnsupportedDataFormat, 'Wrong encryption algorithm'
|
37
|
+
end
|
38
|
+
encrypted_data = Base64.decode64(encrypted_key.sub(/^:[^:]+:/, ''))
|
39
|
+
json_data = JSON.parse(encrypted_data)
|
40
|
+
key = OpenSSL::PKCS5.pbkdf2_hmac(config[:dk],
|
41
|
+
Base64.decode64(json_data['salt']),
|
42
|
+
json_data['iter'],
|
43
|
+
json_data['ks'] / 8,
|
44
|
+
OpenSSL::Digest::SHA1.new)
|
45
|
+
decipher(Base64.decode64(json_data['ct']), key,
|
46
|
+
Base64.decode64(json_data['iv']), json_data['ks'])
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# This file is distributed under GitDock Oy license terms.
|
3
|
+
# See https://bitbucket.org/keystok/keystok-client-ruby/raw/master/LICENSE.txt
|
4
|
+
# for details.
|
5
|
+
|
6
|
+
module Keystok
|
7
|
+
# Module for handling cache write and read operations
|
8
|
+
module Cache
|
9
|
+
def cache_file_path(tmp_dir = nil)
|
10
|
+
tmp_dir ||= (@config && @config[:tmp_dir]) ? @config[:tmp_dir] : '.'
|
11
|
+
@cache_file_path ||= File.join(tmp_dir, 'keystok_cache.data')
|
12
|
+
end
|
13
|
+
|
14
|
+
def cache_file_exists?
|
15
|
+
File.exists?(cache_file_path)
|
16
|
+
end
|
17
|
+
|
18
|
+
def cache_stream(method = :read)
|
19
|
+
case method
|
20
|
+
when :write
|
21
|
+
mode = 'wb'
|
22
|
+
else
|
23
|
+
mode = 'r'
|
24
|
+
end
|
25
|
+
File.open(cache_file_path, mode)
|
26
|
+
end
|
27
|
+
|
28
|
+
def load_cache(iostream = nil)
|
29
|
+
Keystok.logger.warn('Loading data from cache')
|
30
|
+
iostream ||= cache_stream
|
31
|
+
iostream.read
|
32
|
+
end
|
33
|
+
|
34
|
+
def write_cache(cache_data, iostream = nil)
|
35
|
+
iostream ||= cache_stream(:write)
|
36
|
+
iostream.write(cache_data)
|
37
|
+
iostream.flush
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,198 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# This file is distributed under GitDock Oy license terms.
|
3
|
+
# See https://bitbucket.org/keystok/keystok-client-ruby/raw/master/LICENSE.txt
|
4
|
+
# for details.
|
5
|
+
|
6
|
+
require 'faraday'
|
7
|
+
require 'json'
|
8
|
+
require 'oauth2'
|
9
|
+
|
10
|
+
require 'keystok/aes_crypto'
|
11
|
+
require 'keystok/cache'
|
12
|
+
|
13
|
+
module Keystok
|
14
|
+
# Keystok client class
|
15
|
+
class Client
|
16
|
+
include AESCrypto
|
17
|
+
include Cache
|
18
|
+
|
19
|
+
API_HOST = 'https://api.keystok.com'
|
20
|
+
AUTH_HOST = 'https://keystok.com'
|
21
|
+
DEFAULT_CONFIG = { eager_fetching: true }
|
22
|
+
REQUEST_TRY_COUNT = 3
|
23
|
+
|
24
|
+
def initialize(config = {})
|
25
|
+
@config = DEFAULT_CONFIG.merge(keys_to_symbols(decode_config(config)))
|
26
|
+
@keys_store = {}
|
27
|
+
end
|
28
|
+
|
29
|
+
def access_token
|
30
|
+
@access_token ||= begin
|
31
|
+
fail Error::ConfigError, 'SDK not configured' if @config[:rt].nil?
|
32
|
+
refresh_token = OAuth2::AccessToken.new(oauth_client, nil,
|
33
|
+
refresh_token:
|
34
|
+
@config[:rt])
|
35
|
+
access_token = nil
|
36
|
+
REQUEST_TRY_COUNT.times do |try_count|
|
37
|
+
begin
|
38
|
+
access_token = refresh_token.refresh!
|
39
|
+
break
|
40
|
+
rescue Faraday::Error::ClientError => exception
|
41
|
+
Keystok.logger.warn(
|
42
|
+
"Exception during token refresh: #{exception.message}")
|
43
|
+
raise if try_count == (REQUEST_TRY_COUNT - 1)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
access_token
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def configured?
|
51
|
+
check_config
|
52
|
+
true
|
53
|
+
rescue Error::ConfigError
|
54
|
+
false
|
55
|
+
end
|
56
|
+
|
57
|
+
def get(key_name_or_symbol, force_reload = false)
|
58
|
+
key_name = key_name_or_symbol.to_s
|
59
|
+
if force_reload || @config[:volatile] || @keys_store[key_name].nil?
|
60
|
+
load_keys(key_name_or_symbol)
|
61
|
+
end
|
62
|
+
@keys_store[key_name]
|
63
|
+
end
|
64
|
+
|
65
|
+
def keys(force_reload = false)
|
66
|
+
load_keys if force_reload || @config[:volatile] || @keys_store.empty?
|
67
|
+
@keys_store
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def check_config
|
73
|
+
%w(rt dk id).map(&:to_sym).each do |key|
|
74
|
+
if @config[key].nil?
|
75
|
+
fail Error::ConfigError, "Config key: #{key} is missing!"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def connection
|
81
|
+
@connection ||= Faraday.new(url: @config[:api_host] || API_HOST,
|
82
|
+
ssl: { verify: true })
|
83
|
+
end
|
84
|
+
|
85
|
+
def decode_config(connection_string)
|
86
|
+
config_hash = connection_string
|
87
|
+
case
|
88
|
+
when connection_string.kind_of?(Hash)
|
89
|
+
when connection_string.kind_of?(String)
|
90
|
+
config_hash = decode_string_config(connection_string)
|
91
|
+
else
|
92
|
+
fail Error::ConfigError, 'Unknown config format'
|
93
|
+
end
|
94
|
+
if config_hash[:connection_string]
|
95
|
+
decoded_hash = decode_string_config(config_hash[:connection_string])
|
96
|
+
config_hash.delete(:connection_string)
|
97
|
+
config_hash = decoded_hash.merge(config_hash)
|
98
|
+
end
|
99
|
+
config_hash
|
100
|
+
end
|
101
|
+
|
102
|
+
def decode_json_string_config(connection_string)
|
103
|
+
parsed_config = nil
|
104
|
+
begin
|
105
|
+
parsed_config = JSON.parse(connection_string)
|
106
|
+
# rubocop:disable HandleExceptions
|
107
|
+
rescue JSON::ParserError
|
108
|
+
# rubocop:enable HandleExceptions
|
109
|
+
# Whatever may fail, we don't want to raise it.
|
110
|
+
end
|
111
|
+
parsed_config
|
112
|
+
end
|
113
|
+
|
114
|
+
def decode_string_config(connection_string)
|
115
|
+
return {} if connection_string == ''
|
116
|
+
parsed_config = decode_json_string_config(connection_string)
|
117
|
+
unless parsed_config
|
118
|
+
decoded_string = Base64.decode64(connection_string)
|
119
|
+
parsed_config = decode_json_string_config(decoded_string)
|
120
|
+
end
|
121
|
+
fail Error::ConfigError, 'Unknown config format' unless parsed_config
|
122
|
+
parsed_config
|
123
|
+
end
|
124
|
+
|
125
|
+
def fetch_data(key = nil)
|
126
|
+
response = nil
|
127
|
+
if key.nil? || @config[:eager_fetching]
|
128
|
+
key_path_part = ''
|
129
|
+
else
|
130
|
+
key_path_part = "/#{key}"
|
131
|
+
end
|
132
|
+
REQUEST_TRY_COUNT.times do |try_count|
|
133
|
+
begin
|
134
|
+
response = connection.get do |request|
|
135
|
+
request.url ['/apps/', @config[:id],
|
136
|
+
'/deploy', key_path_part,
|
137
|
+
'?access_token=', access_token.token].join
|
138
|
+
request.options[:open_timeout] = 5
|
139
|
+
request.options[:timeout] = 5
|
140
|
+
end
|
141
|
+
if response.status == 200
|
142
|
+
break
|
143
|
+
else
|
144
|
+
Keystok.logger.warn(
|
145
|
+
"Keystok API response status: #{response.status}")
|
146
|
+
@access_token = nil
|
147
|
+
end
|
148
|
+
rescue Faraday::Error::ClientError => exception
|
149
|
+
Keystok.logger.warn(
|
150
|
+
"Exception during Keystok API data fetch: #{exception.message}")
|
151
|
+
raise if try_count == (REQUEST_TRY_COUNT - 1)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
response
|
155
|
+
end
|
156
|
+
|
157
|
+
def keys_to_symbols(input_hash)
|
158
|
+
Hash[input_hash.map do |key, val|
|
159
|
+
[key.kind_of?(String) ? key.to_s.to_sym : key, val]
|
160
|
+
end]
|
161
|
+
end
|
162
|
+
|
163
|
+
def load_keys(key = nil)
|
164
|
+
begin
|
165
|
+
response = fetch_data(key)
|
166
|
+
if response.status == 200
|
167
|
+
response_data = response.body
|
168
|
+
elsif cache_file_exists?
|
169
|
+
response_data = load_cache
|
170
|
+
else
|
171
|
+
fail Keystok::Error::ConnectionError,
|
172
|
+
"No cache data and response code:\n" +
|
173
|
+
"#{response.status} with body: #{response.body}"
|
174
|
+
end
|
175
|
+
rescue Faraday::Error::ClientError => exception
|
176
|
+
if cache_file_exists?
|
177
|
+
response_data = load_cache
|
178
|
+
else
|
179
|
+
raise Keystok::Error::ConnectionError,
|
180
|
+
"No cache data and connection error:\n" +
|
181
|
+
"#{exception.class} with message: #{exception.message}"
|
182
|
+
end
|
183
|
+
end
|
184
|
+
@keys_store = {}
|
185
|
+
JSON.parse(response_data).each do |key_id, key_info|
|
186
|
+
@keys_store[key_id] = decrypt_key(key_info['key'])
|
187
|
+
end
|
188
|
+
write_cache(response_data) unless @config[:no_cache]
|
189
|
+
@keys_store
|
190
|
+
end
|
191
|
+
|
192
|
+
def oauth_client
|
193
|
+
@oauth_client ||= begin
|
194
|
+
OAuth2::Client.new(nil, nil, site: @config[:auth_host] || AUTH_HOST)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# This file is distributed under GitDock Oy license terms.
|
3
|
+
# See https://bitbucket.org/keystok/keystok-client-ruby/raw/master/LICENSE.txt
|
4
|
+
# for details.
|
5
|
+
|
6
|
+
module Keystok
|
7
|
+
module Error
|
8
|
+
class CacheCorrupted < StandardError
|
9
|
+
end
|
10
|
+
|
11
|
+
class ConfigError < StandardError
|
12
|
+
end
|
13
|
+
|
14
|
+
class ConnectionError < StandardError
|
15
|
+
end
|
16
|
+
|
17
|
+
class UnsupportedDataFormat < StandardError
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# This file is distributed under GitDock Oy license terms.
|
3
|
+
# See https://bitbucket.org/keystok/keystok-client-ruby/raw/master/LICENSE.txt
|
4
|
+
# for details.
|
5
|
+
|
6
|
+
module Keystok
|
7
|
+
# Class used to integrate with Rails
|
8
|
+
class Railtie < Rails::Railtie
|
9
|
+
initializer 'keystok.configure_rails_initialization' do
|
10
|
+
Keystok.logger = Rails.logger
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
data/lib/keystok.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# This file is distributed under GitDock Oy license terms.
|
3
|
+
# See https://bitbucket.org/keystok/keystok-client-ruby/raw/master/LICENSE.txt
|
4
|
+
# for details.
|
5
|
+
|
6
|
+
require 'faraday'
|
7
|
+
require 'json'
|
8
|
+
require 'logger'
|
9
|
+
require 'yaml'
|
10
|
+
|
11
|
+
require 'keystok/version'
|
12
|
+
require 'keystok/errors'
|
13
|
+
require 'keystok/client'
|
14
|
+
|
15
|
+
#:nodoc:
|
16
|
+
module Keystok
|
17
|
+
class << self
|
18
|
+
attr_accessor :logger
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
if defined? Rails::Railtie
|
23
|
+
require 'keystok/railtie'
|
24
|
+
else
|
25
|
+
Keystok.logger = Logger.new(STDOUT)
|
26
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
:aes256:eyJpdiI6IkwyRGEzVFg5bnczMWswNVc0RnBpaFE9PSIsInYiOjEsIml0ZXIiOjEwMDAsImtzIjoyNTYsInRzIjo2NCwibW9kZSI6ImNiYyIsImFkYXRhIjoiIiwiY2lwaGVyIjoiYWVzIiwic2FsdCI6ImM0czhpZ3FIOFA0PSIsImN0IjoiZ0FWYVNNaDZINFIzdnl6TnNoelJ1Zz09In0=
|
@@ -0,0 +1,8 @@
|
|
1
|
+
{
|
2
|
+
"key_1": {
|
3
|
+
"key": ":aes256:eyJpdiI6IkwyRGEzVFg5bnczMWswNVc0RnBpaFE9PSIsInYiOjEsIml0ZXIiOjEwMDAsImtzIjoyNTYsInRzIjo2NCwibW9kZSI6ImNiYyIsImFkYXRhIjoiIiwiY2lwaGVyIjoiYWVzIiwic2FsdCI6ImM0czhpZ3FIOFA0PSIsImN0IjoiZ0FWYVNNaDZINFIzdnl6TnNoelJ1Zz09In0="
|
4
|
+
},
|
5
|
+
"key_2": {
|
6
|
+
"key": ":aes256:eyJpdiI6Ijd0N1ArNGhEV2cwL2pZeVBqdTBQV3c9PSIsInYiOjEsIml0ZXIiOjEwMDAsImtzIjoyNTYsInRzIjo2NCwibW9kZSI6ImNiYyIsImFkYXRhIjoiIiwiY2lwaGVyIjoiYWVzIiwic2FsdCI6ImM0czhpZ3FIOFA0PSIsImN0IjoiL1h2ZXF4dlIwOU1kSmc4UGd3eHJoZz09In0="
|
7
|
+
}
|
8
|
+
}
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# This file is distributed under GitDock Oy license terms.
|
3
|
+
# See https://bitbucket.org/keystok/keystok-client-ruby/raw/master/LICENSE.txt
|
4
|
+
# for details.
|
5
|
+
|
6
|
+
require 'spec_helper'
|
7
|
+
|
8
|
+
describe Keystok::AESCrypto do
|
9
|
+
let(:dummy_class) { Class.new { include Keystok::AESCrypto } }
|
10
|
+
let(:dummy) { dummy_class.new }
|
11
|
+
|
12
|
+
describe 'cipher' do
|
13
|
+
it 'works without any errors' do
|
14
|
+
dummy.cipher('plain text', 'key_' * 10, 'vector_' * 10, 256, :CBC)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'works without passing key_size and block_mode' do
|
18
|
+
dummy.cipher('plain text', 'key_' * 10, 'vector_' * 10)
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'is passing OpenSSL exceptions' do
|
22
|
+
expect do
|
23
|
+
dummy.cipher('plain text', 'a', 'vector_' * 10)
|
24
|
+
end.to raise_error(OpenSSL::Cipher::CipherError)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe 'decipher' do
|
29
|
+
let(:encrypted) { "A.\xFC\xDDW=\xB7\xCF\x02\x88\xE3:sA\x90S" }
|
30
|
+
|
31
|
+
it 'works without any errors' do
|
32
|
+
dummy.decipher(encrypted, 'key_' * 10, 'vector_' * 10, 256, :CBC)
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'works without passing key_size and block_mode' do
|
36
|
+
dummy.decipher(encrypted, 'key_' * 10, 'vector_' * 10)
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'is passing OpenSSL exceptions' do
|
40
|
+
expect do
|
41
|
+
dummy.decipher('encrypted', 'a', 'vector_' * 10)
|
42
|
+
end.to raise_error(OpenSSL::Cipher::CipherError, 'key length too short')
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe 'sanity check' do
|
47
|
+
it 'decryption of encrypted data returns proper value' do
|
48
|
+
plain_text = 'plain text'
|
49
|
+
encrypted = dummy.cipher(plain_text, 'key_' * 10, 'vector_' * 10)
|
50
|
+
decrypted_text = dummy.decipher(encrypted, 'key_' * 10, 'vector_' * 10)
|
51
|
+
expect(decrypted_text).to eq(plain_text)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe 'decrypt_key' do
|
56
|
+
let(:encrypted_data) do
|
57
|
+
File.open(File.expand_path('../../fixtures/encrypted_data_00.data',
|
58
|
+
__FILE__)).read
|
59
|
+
end
|
60
|
+
let(:config) do
|
61
|
+
{ dk:
|
62
|
+
'965391f2bba37b4586431b8690d7044d5cdc73adf7dda539f8dd3a60cb3e9b12' }
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'properly decrypts data' do
|
66
|
+
expect(dummy.decrypt_key(encrypted_data, config)).to eq('Value 1')
|
67
|
+
end
|
68
|
+
|
69
|
+
it "raise UnsupportedDataFormat when data prefix is no ':aes256'" do
|
70
|
+
expect do
|
71
|
+
dummy.decrypt_key(encrypted_data.sub(':aes256:', ':cesar:'), config)
|
72
|
+
end.to raise_error(Keystok::Error::UnsupportedDataFormat,
|
73
|
+
'Wrong encryption algorithm')
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'raise ConfigError when no decryption key is provided' do
|
77
|
+
expect do
|
78
|
+
dummy.decrypt_key(encrypted_data, {})
|
79
|
+
end.to raise_error(Keystok::Error::ConfigError,
|
80
|
+
'No decryption key in config')
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'is passing OpenSSL exceptions' do
|
84
|
+
expect do
|
85
|
+
dummy.decrypt_key(encrypted_data, dk: 'a')
|
86
|
+
end.to raise_error(OpenSSL::Cipher::CipherError,
|
87
|
+
'bad decrypt')
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|