cryptograpi_ruby 0.0.1 → 0.0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 29da0fd4bd9d3261e0c0b62a530543a528383b3e801a896954e5d212610e59d4
4
- data.tar.gz: a102a68abeef683afab3d1ebc32bf04e749c4cab891075b689aa3224fa0fff4e
3
+ metadata.gz: ba7b04c4eda2c473ba5f06e06780b132d253e2306102e0a6e06d7f13d3731a4c
4
+ data.tar.gz: 99bbff7d24a71953d31348b21f41fb58378d64aaebf73cbbb35f4531d250eac7
5
5
  SHA512:
6
- metadata.gz: 362abb4b8d8f63b60ddece6f51ef0a2149264a5e9d33a10a00f38a01b8ed1ed5ad15e0c71da050fbee384376760ae7230df62a00b3a37dc179f9cbb24166df93
7
- data.tar.gz: ab8f9a4791ffe18c341b380536b0df48bc5d8d42de48021c87cc88ee779162206a85eac9a3a708b2079f3cb9f64f74d86cf3af6359d20053c077160c13d3135e
6
+ metadata.gz: 183e28010a0cd233dda749e7998470a89b0707fdfc58115d26ee9a9377ddc4c5b611aa5a6df27ff02a3f05146687254cf35a142c3f7e61173e845e1872ab00e7
7
+ data.tar.gz: '0971e1bd891126004b3233a972152f94ea86cf4020ee79451d44ceca174b42ad619db7f3126a6f0d2c425dba145a5f957d3401bd22ebceaed31cd42bbe39d6bf'
data/Gemfile CHANGED
@@ -1,7 +1,10 @@
1
1
  # frozen_string_literal: true
2
-
3
2
  # cryptograpi_ruby/Gemfile
4
3
 
5
4
  source 'https://rubygems.org'
6
5
 
7
6
  gemspec
7
+
8
+ group :test do
9
+ gem 'tzinfo-data', platforms: %i[mingw mswin x64_mingw jruby]
10
+ end
data/Rakefile CHANGED
@@ -1,8 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'rake'
4
- require 'rubocop/rake_task'
5
4
 
5
+ begin
6
+ require 'bundler/setup'
7
+ Bundler::GemHelper.install_tasks
8
+ rescue LoadError
9
+ puts 'it is recommended to run bundler for running the tests'
10
+ end
11
+
12
+ task default: :spec
13
+
14
+ require 'rspec/core/rake_task'
15
+ RSpec::Core::RakeTask.new(:spec)
16
+
17
+ require 'rubocop/rake_task'
6
18
  RuboCop::RakeTask.new do |task|
7
19
  task.requires << 'rubocop-performance'
8
20
  task.requires << 'rubocop-rspec'
@@ -22,8 +22,23 @@ Gem::Specification.new do |spec|
22
22
  spec.add_development_dependency 'rubocop', '~> 1.18'
23
23
  spec.add_development_dependency 'rubocop-performance', '~> 1.11'
24
24
  spec.add_development_dependency 'rubocop-rspec', '~> 2.4'
25
+ spec.add_development_dependency 'codecov', '~> 0.1'
26
+ spec.add_development_dependency 'dotenv', '~> 2.5'
27
+ spec.add_development_dependency 'rake', '~> 13.0'
28
+ spec.add_development_dependency 'rspec', '~> 3.6'
29
+ spec.add_development_dependency 'simplecov', '~> 0.16'
30
+ spec.add_development_dependency 'vcr', '~> 6.0'
31
+
32
+ # Use an embedded rails app for testing
33
+ spec.add_development_dependency 'rails', '~> 6.1'
34
+ spec.add_development_dependency 'rspec-rails', '~> 4.0'
35
+
36
+
37
+
25
38
  spec.add_runtime_dependency 'activesupport', '~> 6.1'
39
+ spec.add_runtime_dependency 'configparser', '>=0.1'
26
40
  spec.add_runtime_dependency 'httparty', '~> 0.18'
27
41
  spec.add_runtime_dependency 'rb-readline', '~> 0.5'
42
+ spec.add_runtime_dependency 'tzinfo-data', '>= 1'
28
43
  spec.add_runtime_dependency 'webrick', '~> 1.7'
29
44
  end
@@ -0,0 +1,71 @@
1
+ require 'active_support/all'
2
+ require 'openssl'
3
+
4
+ module Cryptograpi
5
+ class Cipher
6
+
7
+ CRYPTOFLAG = 0b0000001
8
+
9
+ def set_algorithm
10
+ @algorithm = {
11
+ 'aes-128-gcm' => {
12
+ id: 0,
13
+ algorithm: OpenSSL::Cipher::AES128,
14
+ mode: OpenSSL::Cipher.new('aes-128-gcm'),
15
+ key_length: 16,
16
+ iv_length: 12,
17
+ tag_length: 16
18
+ },
19
+ 'aes-256-gcm' => {
20
+ id: 1,
21
+ algorithm: OpenSSL::Cipher::AES256,
22
+ mode: OpenSSL::Cipher.new('aes-256-gcm'),
23
+ key_length: 32,
24
+ iv_length: 12,
25
+ tag_length: 16
26
+ }
27
+ }
28
+ end
29
+
30
+ def find_algorithm(id)
31
+ set_algorithm.each do |k, v|
32
+ return k if v[:id] == id
33
+ end
34
+ 'unknown'
35
+ end
36
+
37
+ def get_algorithm(name)
38
+ set_algorithm[name]
39
+ end
40
+
41
+ def encryptor(obj, key, init_vector = nil)
42
+ # The 'key' parameter is a byte string that contains the key
43
+ # for this encryption operation.
44
+ # If there is an, correct length, initialization vector (init_vector)
45
+ # then it is used. If not, the function generates one.
46
+ raise 'Invalid key length' if key.length != obj[:key_length]
47
+
48
+ raise 'Invalid initialization vector length' if !init_vector.nil? && init_vector.length != obj[:init_vector_length]
49
+
50
+ cipher = obj[:mode]
51
+ cipher.encrypt
52
+ cipher.key = key
53
+ init_vector = cipher.random_init_vector
54
+
55
+ [cipher, init_vector]
56
+ end
57
+
58
+ def decryptor(obj, key, init_vector)
59
+ raise 'Invalid key length' if key.length != obj[:key_length]
60
+
61
+ raise 'Invalid initialization vector length' if !init_vector.nil? && init_vector.length != obj[:init_vector_length]
62
+
63
+ cipher = obj[:mode]
64
+ cipher.decrypt
65
+ cipher.key = key
66
+ cipher.init_vector = init_vector
67
+
68
+ cipher
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'configparser'
4
+ require 'rb-readline'
5
+ require_relative './host'
6
+
7
+ module Cryptograpi
8
+ class Info
9
+ def initialize(access_key_id, secret_access_key, signing_key, host)
10
+ @access_key_id = access_key_id
11
+ @secret_access_key = secret_access_key
12
+ @signing_key = signing_key
13
+ @host = host
14
+ end
15
+
16
+ def set_attrs
17
+ OpenStruct.new(
18
+ access_key_id: @access_key_id,
19
+ secret_access_key: @secret_access_key,
20
+ signing_key: @signing_key,
21
+ host: @host
22
+ )
23
+ end
24
+ end
25
+
26
+ # Loads and reads a credential file or uses default info
27
+ class ConfigCredentials < Info
28
+ def initialize(configuration_file, profile)
29
+ # Check if the file exists
30
+ raise "There is an error finding or reading the #{configuration_file} file" if !configuration_file.nil? && !File.exist?(configuration_file)
31
+
32
+ configuration_file = '~/.cryptograpi/credentials' if configuration_file.nil?
33
+
34
+ @credentials = load_configuration_file(configuration_file, profile) if File.exist?(File.expand_path(configuration_file))
35
+ end
36
+
37
+ def attrs
38
+ @credentials
39
+ end
40
+
41
+ def load_configuration_file(file, profile)
42
+ config = ConfigParser.new(File.expand_path(file))
43
+
44
+ # Dict for profiles
45
+ p = {}
46
+ d = {}
47
+
48
+ # If there is a default profile, get it
49
+ d = config['default'] if config['default'].present?
50
+
51
+ d['SERVER'] = Cryptograpi::CRYPTOGRAPI_HOST unless d.key?('SERVER')
52
+
53
+ # If there is a supplied profile, get it
54
+ p = config[profile] if config[profile].present?
55
+
56
+ # Use the supplied profile. Otherwise use default
57
+ access_key_id = p.key?('ACCESS_KEY_ID') ? p['ACCESS_KEY_ID'] : d['ACCESS_KEY_ID']
58
+ secret_access_key = p.key?('SECRET_ACCESS_KEY') ? p['SECRET_ACCESS_KEY'] : d['SECRET_ACCESS_KEY']
59
+ signing_key = p.key?('SIGNING_KEY') ? p['SIGNING_KEY'] : d['SIGNING_KEY']
60
+ host = p.key?('SERVER') ? p['SERVER'] : d['SERVER']
61
+
62
+ # Sanitizing the host variable to always include https
63
+ host = "https://#{host}" if !host.include?('http://') && !host.include?('https://')
64
+
65
+ Info.new(access_key_id, secret_access_key, signing_key, host).set_attrs
66
+ end
67
+ end
68
+
69
+ # Credentials can be explicitly set or
70
+ # use info from ENV Variables
71
+ class Credentials < Info
72
+ def initialize(papi, sapi, srsa, host)
73
+ super
74
+ @access_key_id = papi.present? ? papi : ENV['CRYPTOGRAPI_ACCESS_KEY_ID']
75
+ @secret_access_key = sapi.present? ? sapi : ENV['CRYPTOGRAPI_SECRET_ACCESS_KEY']
76
+ @signing_key = srsa.present? ? srsa : ENV['CRYPTOGRAPI_SIGNING_KEY']
77
+ @host = host.present? ? host : ENV['CRYPTOGRAPI_SERVER']
78
+ end
79
+
80
+ @creds = Info.new(@access_key_id, @secret_access_key, @signing_key, @host).set_attrs
81
+
82
+ def attrs
83
+ @creds
84
+ end
85
+ end
86
+ end
87
+
@@ -0,0 +1,200 @@
1
+ require 'active_support/all'
2
+ require 'httparty'
3
+ require 'rb-readline'
4
+ require 'webrick'
5
+ require_relative './cipher'
6
+ require_relative './encrypt'
7
+ require_relative './signature'
8
+
9
+ module Cryptograpi
10
+ class Decryption
11
+ def initialize(creds)
12
+ raise 'Some credentials are missing' unless validate_credentials(creds)
13
+
14
+ @papi = creds.access_key_id
15
+ @sapi = creds.signing_key
16
+ @srsa = creds.secret_access_key
17
+ @host = creds.host.blank? ? CRYPTOGRAPI_HOST : creds.host
18
+
19
+ @decryption_started = false
20
+ @decryption_ready = true
21
+ end
22
+
23
+ def begin_decryption
24
+ raise ' Decryption is not ready' unless @decryption_ready
25
+
26
+ raise ' Decryption already started' if @decryption_started
27
+
28
+ raise 'Decryption already in progress' if @key.present? && @key.key?('dec')
29
+
30
+ @decryption_started = true
31
+ @data = ''
32
+ end
33
+
34
+ def update_decryption(data)
35
+ raise ' Decryption is not started' unless @decryption_started
36
+
37
+ # Act as a buffer for data
38
+ @data += data
39
+
40
+ # If there is no key or dec member of key, a header is still being built
41
+ if !@key.present? || !@key.key?('dec')
42
+ struct_length = [1, 1, 1, 1, 1].pack('CCCCn').length
43
+ packed_struct = @data[0...struct_length]
44
+
45
+ # Does the buffer contain enough of the header to determine
46
+ # the lengths of the initialization vector and the key?
47
+ if @data.length > struct_length
48
+ version, flags, algorithm_id, iv_length, key_length = packed_struct.unpack('CCCCn')
49
+
50
+ raise 'Invalid encryption header' if (version != 0) || ((flags & ~Cipher::CRYPTOFLAG) != 0)
51
+
52
+ if @data.length > struct_length + iv_length + key_length
53
+ # Extract the initialization vector
54
+ iv = @data[struct_length...iv_length + struct_length]
55
+ # Extract the encrypted key
56
+ enc_key = @data[struct_length + iv_length..key_length + struct_length + iv_length]
57
+ # Remove the header from the buffer
58
+ @data = @data[struct_length + iv_length + key_length..-1]
59
+
60
+ # Generate a key identifier
61
+ hash_sha512 = OpenSSL::Digest.new('SHA512')
62
+ hash_sha512 << enc_key
63
+ client_id = hash_sha512.digest
64
+
65
+ if @key.present?
66
+ close if @key['client_id'] != client_id
67
+ end
68
+
69
+ unless @key.present?
70
+ url = endpoint_base + '/decrypt/key'
71
+ query = { encrypted_data_key: Base64.strict_encode64(enc_key) }
72
+ headers = Signature.headers(endpoint, @host, 'post', @papi, query, @sapi)
73
+
74
+ response = HTTParty.post(
75
+ url,
76
+ body: query.to_json,
77
+ headers: headers
78
+ )
79
+
80
+ if response.code == WEBrick::HTTPStatus::RC_OK
81
+ @key = {}
82
+ @key['finger_print'] = response[key_fingerprint]
83
+ @key['client_id'] = client_id
84
+ @key['session'] = response['encryption_session']
85
+
86
+ # Get the cipher name
87
+ @key['algorithm'] = Cipher.new.find_algorithm(algorithm_id)
88
+
89
+ encrypted_private_key = response['encrypted_private_key']
90
+ # Decrypt WDK from base64
91
+ wdk = Base64.strict_decode64(wrapped_data_key)
92
+ # Use private key to decrypt the wdk
93
+ dk = private_key.private_decrypt(wdk, OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING)
94
+
95
+ @key['raw'] = dk
96
+ @key['uses'] = 0
97
+ else
98
+ raise "HTTPError response: Expected 201 got #{response.code}"
99
+ end
100
+ end
101
+
102
+ # If the key object exists, create a new decryptor.
103
+ # Increment the key usage
104
+ if @key.present?
105
+ @cipher = Cipher.new.get_algorithm(@key['algorithm'])
106
+ @key['dec'] = Cipher.new.decryptor(@cipher, @key['raw'], iv)
107
+
108
+ if (flags & Cipher::CRYPTOFLAG) != 0
109
+ @key['dec'].auth_data = packed_struct + iv + enc_key
110
+ end
111
+ @key['uses'] += 1
112
+ end
113
+ end
114
+ end
115
+ end
116
+
117
+ plain_text = ''
118
+ if @key.present? && @key.key?('dec')
119
+ size = @data.length - @cipher[:tag_length]
120
+ if size.positive?
121
+ plain_text = @key['dec'].update(@data[0..size - 1])
122
+ @data = @data[size..-1]
123
+ end
124
+ plain_text
125
+ end
126
+ end
127
+
128
+ def finish_decryption
129
+ raise 'Decryption is not started' unless @decryption_started
130
+
131
+ # Update maintains tag-size bytes in the buffer
132
+ # When this function is called, all data must already be
133
+ # in the decryption object
134
+ sz = @data.length - @cipher[:tag_length]
135
+
136
+ raise 'Invalid Tag!' if sz.negative?
137
+
138
+ if sz.zero?
139
+ @key['dec'].auth_tag = @data
140
+ begin
141
+ plain_text = @key['dec'].final
142
+ # Delete the context
143
+ @key.delete('dec')
144
+ # Return the plain text
145
+ @decryption_started = false
146
+ plain_text
147
+ rescue Exception
148
+ print 'Invalid cipher data or tag!'
149
+ ''
150
+ end
151
+ end
152
+ end
153
+
154
+ def close_decryption
155
+ raise 'Decryption currently running' if @decryption_started
156
+
157
+ # Reset the decryption object
158
+ if @key.present?
159
+ if @key['uses'].positive?
160
+ query_url = "#{endpoint}/#{@key['finger_print']}/#{@key['session']}"
161
+ url = "#{endpoint_base}/decryption/key/#{@key['finger_print']}/#{@key['session']}"
162
+ query = { uses: @key['uses'] }
163
+ headers = Signature.headers(query_url, @host, 'patch', @papi, query, @sapi)
164
+
165
+ response = HTTParty.patch(
166
+ url,
167
+ body: query.to_json,
168
+ headers: headers
169
+ )
170
+
171
+ remove_instance_variable(:@data)
172
+ remove_instance_variable(:@key)
173
+ end
174
+ end
175
+ end
176
+
177
+ def endpoint
178
+ '/api/v0/encryption/key'
179
+ end
180
+
181
+ def endpoint_base
182
+ "#{@host}/api/v0"
183
+ end
184
+
185
+ end
186
+
187
+ def decrypt(creds, data)
188
+ begin
189
+ dec = Decryption.new(creds)
190
+ res = dec.begin_decryption + dec.update_decryption(data) + dec.finish_decryption
191
+ dec.close_decryption
192
+ rescue StandardError
193
+ dec&.close_decryption
194
+ raise
195
+ end
196
+
197
+ res
198
+ end
199
+
200
+ end
@@ -0,0 +1,165 @@
1
+ require 'active_support/all'
2
+ require 'httparty'
3
+ require 'rb-readline'
4
+ require 'webrick'
5
+ require_relative './cipher'
6
+ require_relative './signature'
7
+
8
+ module Cryptograpi
9
+ class Encryption
10
+ def initialize(creds, uses)
11
+ raise 'Some credentials are missing' unless validate_credentials(creds)
12
+
13
+ @papi = creds.access_key_id
14
+ @sapi = creds.signing_key
15
+ @srsa = creds.secret_access_key
16
+ @host = creds.host.blank? ? CRYPTOGRAPI_HOST : creds.host
17
+ url = "#{endpoint_base}/encryption/key"
18
+ query = { uses: uses }
19
+ headers = Signature.headers(endpoint, @host, 'post', @papi, query, @sapi)
20
+
21
+ @encryption_started = false
22
+ @encryption_ready = true
23
+
24
+ # First, ask for a key from the server.
25
+ # If the request fails, raise a HTTPError.
26
+
27
+ begin
28
+ response = HTTParty.post(
29
+ url,
30
+ body: query.to_json,
31
+ headers: headers
32
+ )
33
+ rescue HTTParty::Error
34
+ raise 'Server Unreachable'
35
+ end
36
+
37
+ if response.code == WEBrick::HTTPStatus::RC_CREATED
38
+ # Builds the key object
39
+ @key = {}
40
+ @key['id'] = response['key_fingerprint']
41
+ @key['session'] = response['encryption_session']
42
+ @key['security_model'] = response['security_model']
43
+ @key['algorithm'] = response['security_model']['algorithm'].downcase
44
+ @key['max_uses'] = response['max_uses']
45
+ @key['uses'] = 0
46
+ @key['encrypted'] = Base64.strict_decode64(response['encrypted_data_key'])
47
+
48
+ # get the encrypted private key from response body
49
+ encrypted_pk = response['encrypted_private_key']
50
+ # Data key from response body
51
+ wrapped_data_key = response['wrapped_data_key']
52
+ # decrypt the encrypted private key using @srsa
53
+ pk = OpenSSL::PKey::RSA.new(encrypted_pk, @srsa)
54
+ # Decode WDK from base64 format
55
+ wdk = Base64.strict_decode64(wrapped_data_key)
56
+ # Use private key to decrypt the wrapped data key
57
+ dk = pk.private_decrypt(wdk, OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING)
58
+ @key['raw'] = dk
59
+ @cipher = Cipher.new.get_algorithm(@key['algorithm'])
60
+ else
61
+ raise "HTTPError Response: Expected 201, got #{response.code}"
62
+ end
63
+ end
64
+
65
+ def begin_encryption
66
+ # Begins the encryption process
67
+ # Each time this function is called, uses increments by 1
68
+ raise 'Encryption not ready' unless @encryption_ready
69
+ # cipher already exists
70
+ raise 'Encryption in progress' if @encryption_started
71
+ # Check for max uses flag
72
+ raise 'Maximum usage exceeded' if @key['uses'] >= @key["max_uses"]
73
+
74
+ # Increase the uses counter
75
+ @key['uses'] += 1
76
+ # New context and initialization vector
77
+ @enc, @iv = Cipher.new.encryptor(@cipher, @key['raw'])
78
+ # Pack and create a byte string
79
+ struct = [0, Cipher::CRYPTOFLAG, @cipher[:id], @iv.length, @key['encrypted'].length].pack('CCCCn')
80
+
81
+ @enc.auth_data = struct + @iv + @key['encrypted']
82
+ @encryption_started = true
83
+
84
+ # Return the encrypted object
85
+ struct + @iv + @key['encrypted']
86
+ end
87
+
88
+ def update_encryption(data)
89
+ raise 'Encryption has not started yet' unless @encryption_started
90
+
91
+ @enc.update(data)
92
+ end
93
+
94
+ def finish_encryption
95
+ raise 'Encryption not started' unless @encryption_started
96
+
97
+ # Finalizes the encryption and adds any auth info required by the algorithm
98
+ res = @enc.final
99
+ if @cipher[:tag_length] != 0
100
+ # Add the tag to the cipher text
101
+ res += @enc.auth_tag
102
+ end
103
+
104
+ @encryption_started = false
105
+ # return the encrypted result
106
+ res
107
+ end
108
+
109
+ def close_encryption
110
+ raise 'Encryption currently running' if @encryption_started
111
+
112
+ if @key['uses'] < @key['max_uses']
113
+ query_url = "#{endpoint}/#{@key['id']}/#{@key['session']}"
114
+ url = "#{endpoint_base}/encryption/key/#{@key['id']}/#{@key['session']}"
115
+ query = { actual: @key['uses'], requested: @key['max_uses'] }
116
+ headers = Signature.headers(query_url, @host, 'patch', @papi, query, @sapi)
117
+
118
+ response = HTTParty.patch(
119
+ url,
120
+ body: query.to_json,
121
+ headers: headers
122
+ )
123
+ remove_instance_variable(:@key)
124
+ @encryption_ready = false
125
+ end
126
+ end
127
+
128
+ def endpoint
129
+ '/api/v0/encryption/key'
130
+ end
131
+
132
+ def endpoint_base
133
+ "#{@host}/api/v0"
134
+ end
135
+
136
+ def validate_credentials(credentials)
137
+ !credentials.access_key_id.blank? &&
138
+ !credentials.secret_access_key.blank? &&
139
+ !credentials.signing_key.blank?
140
+ end
141
+ end
142
+
143
+ # Check credentials are present and valid
144
+ def validate_credentials(credentials)
145
+ !credentials.access_key_id.blank? &&
146
+ !credentials.secret_access_key.blank? &&
147
+ !credentials.signing_key.blank?
148
+ end
149
+
150
+ # Qui e!
151
+ def encrypt(credentials, data)
152
+ begin
153
+ enc = Encryption.new(credentials, 1)
154
+ res =
155
+ enc.begin_encryption +
156
+ enc.update_encryption(data) +
157
+ enc.finish_encryption
158
+ enc.close_encryption
159
+ rescue StandardError
160
+ enc&.close_encryption
161
+ raise
162
+ end
163
+ res
164
+ end
165
+ end
@@ -0,0 +1,3 @@
1
+ module CryptograpiRuby
2
+ CRYPTOGRAPI_HOST = 'api.cryptograpi.com'.freeze
3
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/all'
4
+
5
+ module Cryptograpi
6
+ # HTTP authentication for our platform
7
+ class Signature
8
+ def self.headers(endpoint, host, http_method, papi, query, sapi)
9
+
10
+ # Request Target (http_method path?query)
11
+ req_target = "#{http_method} #{endpoint}"
12
+
13
+ # Unix time for signature creation
14
+ created_at = Time.now.to_i
15
+
16
+ # We hash the body of the HTTP message. Even if it's empty
17
+ ha_sha512 = OpenSSL::Digest.new('SHA512')
18
+ ha_sha512 << JSON.dump(query)
19
+ digest = "SHA-512=#{Base64.strict_encode64(ha_sha512.digest)}"
20
+
21
+ # Initialize headers
22
+ header_signature = {}
23
+ header_signature['user-agent'] = "cryptograpi_ruby/#{Cryptograpi::VERSION}"
24
+ header_signature['content-type'] = 'application/json'
25
+ header_signature['(request-target'] = req_target
26
+ header_signature['date'] = sign_date
27
+ header_signature['host'] = get_host(host)
28
+ header_signature['(created)'] = created_at
29
+ header_signature['digest'] = digest
30
+ headers = %w[content-type date host (created) (request-target) digest]
31
+
32
+ # Calculate HMAC including the headers
33
+ hmac = OpenSSL::HMAC.new(sapi, OpenSSL::Digest.new('SHA512'))
34
+ headers.each do |header|
35
+ hmac << "#{header}: #{header_signature[header]}\n" if header_signature.key?(header)
36
+ end
37
+
38
+ header_signature.delete('(created)')
39
+ header_signature.delete('(request-target)')
40
+ header_signature.delete('(host)')
41
+
42
+ # Build the final signature
43
+ header_signature['signature'] = "keyId=\"#{papi}\""
44
+ header_signature['signature'] += ', algorithm="hmac-sha512"'
45
+ header_signature['signature'] += ", created=#{created_at}"
46
+ header_signature['signature'] += ", headers=#{headers.join(' ')}\""
47
+ header_signature['signature'] += ', signature='
48
+ header_signature['signature'] += Base64.strict_encode64(hmac.digest)
49
+ header_signature['signature'] += '"'
50
+
51
+ header_signature
52
+ end
53
+
54
+ def self.sign_date
55
+ "#{DateTime.now.in_time_zone('GMT').strftime('%a, %d %b %Y')} #{DateTime.now.in_time_zone('GMT').strftime('%H:%M:%S')} GMT"
56
+ end
57
+
58
+ def self.get_host(host)
59
+ uri = URI(host)
60
+ ret = uri.hostname.to_s
61
+ ret += ":#{uri.port}" if /:[0-9]/.match?(host)
62
+ ret
63
+ end
64
+ end
65
+
66
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CryptograpiRuby
4
- VERSION = '0.0.1'
4
+ VERSION = '0.0.1.1'
5
5
  end
@@ -1,2 +1,18 @@
1
1
  # frozen_string_literal: true
2
- # TBC
2
+
3
+ module CryptograpiRuby
4
+ class << self
5
+ attr_accessor :api_token, :project_id
6
+ # attr_writer :locales_path
7
+
8
+ # Let's provide a straightforward way
9
+ # to provide options, like
10
+ # CryptograpiRuby.config do |c|
11
+ # c.api_token = '123'
12
+ # c.project_id = '345'
13
+ # end
14
+ def config
15
+ yield self
16
+ end
17
+ end
18
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cryptograpi_ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cryptograpi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-08-03 00:00:00.000000000 Z
11
+ date: 2021-09-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rubyzip
@@ -66,6 +66,118 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '2.4'
69
+ - !ruby/object:Gem::Dependency
70
+ name: codecov
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0.1'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0.1'
83
+ - !ruby/object:Gem::Dependency
84
+ name: dotenv
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '2.5'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '2.5'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rake
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '13.0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '13.0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rspec
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '3.6'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '3.6'
125
+ - !ruby/object:Gem::Dependency
126
+ name: simplecov
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '0.16'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '0.16'
139
+ - !ruby/object:Gem::Dependency
140
+ name: vcr
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: '6.0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: '6.0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: rails
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - "~>"
158
+ - !ruby/object:Gem::Version
159
+ version: '6.1'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - "~>"
165
+ - !ruby/object:Gem::Version
166
+ version: '6.1'
167
+ - !ruby/object:Gem::Dependency
168
+ name: rspec-rails
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - "~>"
172
+ - !ruby/object:Gem::Version
173
+ version: '4.0'
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - "~>"
179
+ - !ruby/object:Gem::Version
180
+ version: '4.0'
69
181
  - !ruby/object:Gem::Dependency
70
182
  name: activesupport
71
183
  requirement: !ruby/object:Gem::Requirement
@@ -80,6 +192,20 @@ dependencies:
80
192
  - - "~>"
81
193
  - !ruby/object:Gem::Version
82
194
  version: '6.1'
195
+ - !ruby/object:Gem::Dependency
196
+ name: configparser
197
+ requirement: !ruby/object:Gem::Requirement
198
+ requirements:
199
+ - - ">="
200
+ - !ruby/object:Gem::Version
201
+ version: '0.1'
202
+ type: :runtime
203
+ prerelease: false
204
+ version_requirements: !ruby/object:Gem::Requirement
205
+ requirements:
206
+ - - ">="
207
+ - !ruby/object:Gem::Version
208
+ version: '0.1'
83
209
  - !ruby/object:Gem::Dependency
84
210
  name: httparty
85
211
  requirement: !ruby/object:Gem::Requirement
@@ -108,6 +234,20 @@ dependencies:
108
234
  - - "~>"
109
235
  - !ruby/object:Gem::Version
110
236
  version: '0.5'
237
+ - !ruby/object:Gem::Dependency
238
+ name: tzinfo-data
239
+ requirement: !ruby/object:Gem::Requirement
240
+ requirements:
241
+ - - ">="
242
+ - !ruby/object:Gem::Version
243
+ version: '1'
244
+ type: :runtime
245
+ prerelease: false
246
+ version_requirements: !ruby/object:Gem::Requirement
247
+ requirements:
248
+ - - ">="
249
+ - !ruby/object:Gem::Version
250
+ version: '1'
111
251
  - !ruby/object:Gem::Dependency
112
252
  name: webrick
113
253
  requirement: !ruby/object:Gem::Requirement
@@ -137,6 +277,12 @@ files:
137
277
  - Rakefile
138
278
  - cryptograpi_ruby.gemspec
139
279
  - lib/cryptograpi_ruby.rb
280
+ - lib/cryptograpi_ruby/cipher.rb
281
+ - lib/cryptograpi_ruby/credentials.rb
282
+ - lib/cryptograpi_ruby/decrypt.rb
283
+ - lib/cryptograpi_ruby/encrypt.rb
284
+ - lib/cryptograpi_ruby/host.rb
285
+ - lib/cryptograpi_ruby/signature.rb
140
286
  - lib/cryptograpi_ruby/version.rb
141
287
  homepage: https://cryptograpi.com
142
288
  licenses:
@@ -157,7 +303,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
157
303
  - !ruby/object:Gem::Version
158
304
  version: '0'
159
305
  requirements: []
160
- rubygems_version: 3.2.25
306
+ rubygems_version: 3.2.26
161
307
  signing_key:
162
308
  specification_version: 4
163
309
  summary: Cryptograpi library for ruby apps