ubiq-security 1.0.0 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +4 -6
- data/lib/ubiq-security.rb +5 -6
- data/lib/ubiq/algo.rb +44 -40
- data/lib/ubiq/auth.rb +72 -71
- data/lib/ubiq/credentials.rb +75 -65
- data/lib/ubiq/decrypt.rb +220 -210
- data/lib/ubiq/encrypt.rb +156 -146
- data/lib/ubiq/host.rb +1 -1
- data/lib/ubiq/version.rb +1 -2
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a7988726fc44204d81c5fd65b343c14d34ff24e2fc6883b6fed4acdfc71afb98
|
4
|
+
data.tar.gz: c22cc110f282e5b93dd42fe49be499a1dff208d6fce0726329499a5b0b0fb93a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 91e17a9b5b5d52dc2f107585884bdda8e3a12cf2e405329b5204835ac28a9dd416bd044fc0fb530e6b43382d1ec6ecc9b0b9771b4317744fcfef6ae7bd9b9757
|
7
|
+
data.tar.gz: a428124dc92b6b956a8f5be93d19e00bc4541144894b73f3ee692f6db22bb8514e2be77f8977b140ed04f8f0ed3504d20523c5bec1d5f08399dda4654218afcc
|
data/README.md
CHANGED
@@ -7,7 +7,7 @@ to encrypt and decrypt data
|
|
7
7
|
|
8
8
|
## Documentation
|
9
9
|
|
10
|
-
See the [Ruby API docs]
|
10
|
+
See the [Ruby API docs][apidocs].
|
11
11
|
|
12
12
|
|
13
13
|
|
@@ -78,8 +78,7 @@ credentials = Credentials(access_key_id = "...", secret_signing_key = "...", sec
|
|
78
78
|
|
79
79
|
### Encrypt a simple block of data
|
80
80
|
|
81
|
-
Pass credentials and data into the encryption function. The encrypted data
|
82
|
-
will be returned.
|
81
|
+
Pass credentials and data into the encryption function. The encrypted data will be returned.
|
83
82
|
|
84
83
|
|
85
84
|
```ruby
|
@@ -92,8 +91,7 @@ encrypted_data = encrypt(credentials, plaintext_data)
|
|
92
91
|
|
93
92
|
### Decrypt a simple block of data
|
94
93
|
|
95
|
-
Pass credentials and encrypted data into the decryption function. The plaintext data
|
96
|
-
will be returned.
|
94
|
+
Pass credentials and encrypted data into the decryption function. The plaintext data will be returned.
|
97
95
|
|
98
96
|
```ruby
|
99
97
|
require "ubiq-security"
|
@@ -183,6 +181,6 @@ BLOCK_SIZE = 1024 * 1024
|
|
183
181
|
[gem]: https://rubygems.org/gems/uniq-security
|
184
182
|
[dashboard]:https://dev.ubiqsecurity.com/docs/dashboard
|
185
183
|
[credentials]:https://dev.ubiqsecurity.com/docs/how-to-create-api-keys
|
186
|
-
|
184
|
+
[apidocs]:https://dev.ubiqsecurity.com/docs/api
|
187
185
|
|
188
186
|
|
data/lib/ubiq-security.rb
CHANGED
@@ -13,10 +13,9 @@
|
|
13
13
|
#
|
14
14
|
# https://ubiqsecurity.com/legal
|
15
15
|
|
16
|
-
|
17
|
-
require "ubiq/encrypt"
|
18
|
-
require "ubiq/decrypt"
|
19
|
-
require "ubiq/credentials"
|
20
|
-
|
21
|
-
|
16
|
+
# frozen_string_literal: true
|
22
17
|
|
18
|
+
require 'ubiq/version'
|
19
|
+
require 'ubiq/encrypt'
|
20
|
+
require 'ubiq/decrypt'
|
21
|
+
require 'ubiq/credentials'
|
data/lib/ubiq/algo.rb
CHANGED
@@ -14,56 +14,60 @@
|
|
14
14
|
# https://ubiqsecurity.com/legal
|
15
15
|
#
|
16
16
|
|
17
|
-
|
17
|
+
# frozen_string_literal: true
|
18
|
+
|
19
|
+
require 'active_support/all'
|
18
20
|
require 'openssl'
|
19
21
|
|
20
22
|
module Ubiq
|
23
|
+
# Class to provide some basic information mapping between an
|
24
|
+
# encryption algorithm name and the cooresponding
|
25
|
+
# key size, initialization vector length, and tag
|
26
|
+
class Algo
|
27
|
+
def set_algo
|
28
|
+
@algorithm = {
|
29
|
+
'aes-256-gcm' => {
|
30
|
+
id: 0,
|
31
|
+
algorithm: OpenSSL::Cipher::AES256,
|
32
|
+
mode: OpenSSL::Cipher::AES256.new(:GCM),
|
33
|
+
key_length: 32,
|
34
|
+
iv_length: 12,
|
35
|
+
tag_length: 16
|
36
|
+
}
|
37
|
+
}
|
38
|
+
end
|
21
39
|
|
22
|
-
|
40
|
+
def get_algo(name)
|
41
|
+
set_algo[name]
|
42
|
+
end
|
23
43
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
mode: OpenSSL::Cipher::AES256.new(:GCM),
|
30
|
-
key_length: 32,
|
31
|
-
iv_length: 12,
|
32
|
-
tag_length: 16
|
33
|
-
},
|
34
|
-
}
|
35
|
-
end
|
44
|
+
def encryptor(obj, key, iv = nil)
|
45
|
+
# key : A byte string containing the key to be used with this encryption
|
46
|
+
# If the caller specifies the initialization vector, it must be
|
47
|
+
# the correct length and, if so, will be used. If it is not
|
48
|
+
# specified, the function will generate a new one
|
36
49
|
|
37
|
-
|
38
|
-
set_algo[name]
|
39
|
-
end
|
50
|
+
raise RuntimeError, 'Invalid key length' if key.length != obj[:key_length]
|
40
51
|
|
41
|
-
|
42
|
-
# key : A byte string containing the key to be used with this encryption
|
43
|
-
# If the caller specifies the initialization vector, it must be
|
44
|
-
# the correct length and, if so, will be used. If it is not
|
45
|
-
# specified, the function will generate a new one
|
52
|
+
raise RuntimeError, 'Invalid initialization vector length' if !iv.nil? && iv.length != obj[:iv_length]
|
46
53
|
|
47
|
-
|
48
|
-
|
54
|
+
cipher = obj[:mode]
|
55
|
+
cipher.encrypt
|
56
|
+
cipher.key = key
|
57
|
+
iv = cipher.random_iv
|
58
|
+
return cipher, iv
|
59
|
+
end
|
49
60
|
|
50
|
-
|
51
|
-
|
52
|
-
cipher.key = key
|
53
|
-
iv = cipher.random_iv
|
54
|
-
return cipher, iv
|
55
|
-
end
|
61
|
+
def decryptor(obj, key, iv)
|
62
|
+
raise RuntimeError, 'Invalid key length' if key.length != obj[:key_length]
|
56
63
|
|
57
|
-
|
58
|
-
cipher = obj[:mode]
|
59
|
-
raise RuntimeError, 'Invalid key length' if key.length != obj[:key_length]
|
64
|
+
raise RuntimeError, 'Invalid initialization vector length' if !iv.nil? && iv.length != obj[:iv_length]
|
60
65
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
66
|
+
cipher = obj[:mode]
|
67
|
+
cipher.decrypt
|
68
|
+
cipher.key = key
|
69
|
+
cipher.iv = iv
|
70
|
+
return cipher
|
71
|
+
end
|
67
72
|
end
|
68
73
|
end
|
69
|
-
end
|
data/lib/ubiq/auth.rb
CHANGED
@@ -13,87 +13,88 @@
|
|
13
13
|
#
|
14
14
|
# https://ubiqsecurity.com/legal
|
15
15
|
#
|
16
|
-
require "active_support/all"
|
17
16
|
|
18
|
-
|
17
|
+
# frozen_string_literal: true
|
18
|
+
|
19
|
+
require 'active_support/all'
|
19
20
|
|
20
|
-
|
21
|
+
module Ubiq
|
21
22
|
# HTTP Authentication for the Ubiq Platform
|
22
23
|
|
23
24
|
# This module implements HTTP authentication for the Ubiq platform
|
24
25
|
# via message signing as described by the IETF httpbis-message-signatures
|
25
26
|
# draft specification.
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
27
|
+
class Auth
|
28
|
+
def self.build_headers(papi, sapi, endpoint, query, host, http_method)
|
29
|
+
|
30
|
+
# This function calculates the signature for the message, adding the Signature header
|
31
|
+
# to contain the data. Certain HTTP headers are required for
|
32
|
+
# signature calculation and will be added by this code as
|
33
|
+
# necessary. The constructed headers object is returned
|
34
|
+
|
35
|
+
# the '(request-target)' is part of the signed data.
|
36
|
+
# it's value is 'http_method path?query'
|
37
|
+
reqt = "#{http_method} #{endpoint}"
|
38
|
+
|
39
|
+
# The time at which the signature was created expressed as the unix epoch
|
40
|
+
created = Time.now.to_i
|
41
|
+
|
42
|
+
# the Digest header is always included/overridden by
|
43
|
+
# this code. it is a hash of the body of the http message
|
44
|
+
# and is always present even if the body is empty
|
45
|
+
hash_sha512 = OpenSSL::Digest::SHA512.new
|
46
|
+
hash_sha512 << JSON.dump(query)
|
47
|
+
digest = 'SHA-512=' + Base64.strict_encode64(hash_sha512.digest)
|
48
|
+
|
49
|
+
# Initialize the headers object to be returned via this method
|
50
|
+
all_headers = {}
|
51
|
+
# The content type of request
|
52
|
+
all_headers['content-type'] = 'application/json'
|
53
|
+
# The request target calculated above(reqt)
|
54
|
+
all_headers['(request-target)'] = reqt
|
55
|
+
# The date and time in GMT format
|
56
|
+
all_headers['date'] = get_date
|
57
|
+
# The host specified by the caller
|
58
|
+
all_headers['host'] = get_host(host)
|
59
|
+
all_headers['(created)'] = created
|
60
|
+
all_headers['digest'] = digest
|
61
|
+
headers = ['content-type', 'date', 'host', '(created)', '(request-target)', 'digest']
|
62
|
+
|
63
|
+
# include the specified headers in the hmac calculation. each
|
64
|
+
# header is of the form 'header_name: header value\n'
|
65
|
+
# included headers are also added to an ordered list of headers
|
66
|
+
# which is included in the message
|
67
|
+
hmac = OpenSSL::HMAC.new(sapi, OpenSSL::Digest::SHA512.new)
|
68
|
+
headers.each do |header|
|
69
|
+
if all_headers.key?(header)
|
70
|
+
hmac << "#{header}: #{all_headers[header]}\n"
|
71
|
+
end
|
70
72
|
end
|
71
|
-
end
|
72
|
-
|
73
|
-
all_headers.delete('(created)')
|
74
|
-
all_headers.delete('(request-target)')
|
75
|
-
all_headers.delete('host')
|
76
|
-
|
77
|
-
# Build the Signature header itself
|
78
|
-
all_headers['signature'] = 'keyId="' + papi + '"'
|
79
|
-
all_headers['signature'] += ', algorithm="hmac-sha512"'
|
80
|
-
all_headers['signature'] += ', created=' + created.to_s
|
81
|
-
all_headers['signature'] += ', headers="' + headers.join(" ") + '"'
|
82
|
-
all_headers['signature'] += ', signature="'
|
83
|
-
all_headers['signature'] += Base64.strict_encode64(hmac.digest)
|
84
|
-
all_headers['signature'] += '"'
|
85
73
|
|
86
|
-
|
87
|
-
|
74
|
+
all_headers.delete('(created)')
|
75
|
+
all_headers.delete('(request-target)')
|
76
|
+
all_headers.delete('host')
|
77
|
+
|
78
|
+
# Build the Signature header itself
|
79
|
+
all_headers['signature'] = 'keyId="' + papi + '"'
|
80
|
+
all_headers['signature'] += ', algorithm="hmac-sha512"'
|
81
|
+
all_headers['signature'] += ', created=' + created.to_s
|
82
|
+
all_headers['signature'] += ', headers="' + headers.join(' ') + '"'
|
83
|
+
all_headers['signature'] += ', signature="'
|
84
|
+
all_headers['signature'] += Base64.strict_encode64(hmac.digest)
|
85
|
+
all_headers['signature'] += '"'
|
86
|
+
|
87
|
+
return all_headers
|
88
|
+
end
|
88
89
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
90
|
+
def self.get_host(host)
|
91
|
+
uri = URI(host)
|
92
|
+
return "#{uri.hostname}:#{uri.port}"
|
93
|
+
end
|
93
94
|
|
94
|
-
|
95
|
-
|
95
|
+
def self.get_date
|
96
|
+
DateTime.now.in_time_zone('GMT').strftime('%a, %d %b %Y') + ' ' +
|
97
|
+
DateTime.now.in_time_zone('GMT').strftime('%H:%M:%S') + ' GMT'
|
98
|
+
end
|
96
99
|
end
|
97
|
-
|
98
|
-
end
|
99
100
|
end
|
data/lib/ubiq/credentials.rb
CHANGED
@@ -13,93 +13,103 @@
|
|
13
13
|
#
|
14
14
|
# https://ubiqsecurity.com/legal
|
15
15
|
#
|
16
|
+
|
17
|
+
# frozen_string_literal: true
|
18
|
+
|
16
19
|
require 'configparser'
|
17
20
|
require 'rb-readline'
|
18
21
|
require 'byebug'
|
19
22
|
|
20
23
|
module Ubiq
|
24
|
+
# Access Credentials used by the library to validate service calls
|
25
|
+
class CredentialsInfo
|
26
|
+
def initialize(access_key_id, secret_signing_key, secret_crypto_access_key, host)
|
27
|
+
@access_key_id = access_key_id
|
28
|
+
@secret_signing_key = secret_signing_key
|
29
|
+
@secret_crypto_access_key = secret_crypto_access_key
|
30
|
+
@host = host
|
31
|
+
end
|
21
32
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
end
|
30
|
-
|
31
|
-
def set_attributes
|
32
|
-
return OpenStruct.new(access_key_id: @access_key_id, secret_signing_key: @secret_signing_key, secret_crypto_access_key: @secret_crypto_access_key, host: @host)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
|
37
|
-
class ConfigCredentials < CredentialsInfo
|
38
|
-
def initialize(config_file, profile)
|
39
|
-
# If config file is not found
|
40
|
-
if config_file != nil and !File.exists?(config_file)
|
41
|
-
raise RuntimeError, "Unable to open config file #{config_file} or contains missing values"
|
33
|
+
def set_attributes
|
34
|
+
return OpenStruct.new(
|
35
|
+
access_key_id: @access_key_id,
|
36
|
+
secret_signing_key: @secret_signing_key,
|
37
|
+
secret_crypto_access_key: @secret_crypto_access_key,
|
38
|
+
host: @host
|
39
|
+
)
|
42
40
|
end
|
41
|
+
end
|
43
42
|
|
44
|
-
|
45
|
-
|
43
|
+
# Class to load a credentials file or the default
|
44
|
+
# and read the credentials from either a supplied
|
45
|
+
# profile or use the default
|
46
|
+
class ConfigCredentials < CredentialsInfo
|
47
|
+
def initialize(config_file, profile)
|
48
|
+
# If config file is not found
|
49
|
+
if !config_file.nil? && !File.exist?(config_file)
|
50
|
+
raise RuntimeError, "Unable to open config file #{config_file} or contains missing values"
|
51
|
+
end
|
52
|
+
|
53
|
+
if config_file.nil?
|
54
|
+
config_file = '~/.ubiq/credentials'
|
55
|
+
end
|
56
|
+
|
57
|
+
# If config file is found
|
58
|
+
if File.exist?(File.expand_path(config_file))
|
59
|
+
@creds = load_config_file(config_file, profile)
|
60
|
+
end
|
46
61
|
end
|
47
62
|
|
48
|
-
|
49
|
-
|
50
|
-
@creds = load_config_file(config_file, profile)
|
63
|
+
def get_attributes
|
64
|
+
return @creds
|
51
65
|
end
|
52
|
-
end
|
53
66
|
|
54
|
-
|
55
|
-
|
56
|
-
end
|
67
|
+
def load_config_file(file, profile)
|
68
|
+
config = ConfigParser.new(File.expand_path(file))
|
57
69
|
|
58
|
-
|
59
|
-
|
70
|
+
# Create empty dictionaries for the default and supplied profile
|
71
|
+
p = {}
|
72
|
+
d = {}
|
60
73
|
|
61
|
-
|
62
|
-
|
63
|
-
|
74
|
+
# get the default profile if there is one
|
75
|
+
if config['default'].present?
|
76
|
+
d = config['default']
|
77
|
+
end
|
64
78
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
79
|
+
# get the supplied profile if there is one
|
80
|
+
if config[profile].present?
|
81
|
+
p = config[profile]
|
82
|
+
end
|
69
83
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
84
|
+
# Use given profile if it is available, otherwise use default.
|
85
|
+
access_key_id = p.key?('ACCESS_KEY_ID') ? p['ACCESS_KEY_ID'] : d['ACCESS_KEY_ID']
|
86
|
+
secret_signing_key = p.key?('SECRET_SIGNING_KEY') ? p['SECRET_SIGNING_KEY'] : d['SECRET_SIGNING_KEY']
|
87
|
+
secret_crypto_access_key = p.key?('SECRET_CRYPTO_ACCESS_KEY') ? p['SECRET_CRYPTO_ACCESS_KEY'] : d['SECRET_CRYPTO_ACCESS_KEY']
|
88
|
+
host = p.key?('SERVER') ? p['SERVER'] : d['SERVER']
|
74
89
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
host = p.key?('SERVER') ? p['SERVER'] : d['SERVER']
|
90
|
+
# If the provided host does not contain http protocol then add to it
|
91
|
+
if !host.include?('http://') && !host.include?('https://')
|
92
|
+
host = 'https://' + host
|
93
|
+
end
|
80
94
|
|
81
|
-
|
82
|
-
if !host.include?('http://') and !host.include?('https://')
|
83
|
-
host = 'https://' + host
|
95
|
+
return CredentialsInfo.new(access_key_id, secret_signing_key, secret_crypto_access_key, host).set_attributes
|
84
96
|
end
|
85
|
-
|
86
|
-
return CredentialsInfo.new(access_key_id, secret_signing_key, secret_crypto_access_key, host).set_attributes
|
87
97
|
end
|
88
|
-
end
|
89
98
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
99
|
+
# Class where the credentials can be explicitly set or
|
100
|
+
# will use the Environment variables instead
|
101
|
+
class Credentials < CredentialsInfo
|
102
|
+
def initialize(papi, sapi, srsa, host)
|
103
|
+
@access_key_id = papi.present? ? papi : ENV['UBIQ_ACCESS_KEY_ID']
|
104
|
+
@secret_signing_key = sapi.present? ? sapi : ENV['UBIQ_SECRET_SIGNING_KEY']
|
105
|
+
@secret_crypto_access_key = srsa.present? ? srsa : ENV['UBIQ_SECRET_CRYPTO_ACCESS_KEY']
|
106
|
+
@host = host.present? ? host : ENV['UBIQ_SERVER']
|
107
|
+
end
|
98
108
|
|
99
|
-
|
109
|
+
@creds = CredentialsInfo.new(@access_key_id, @secret_signing_key, @secret_crypto_access_key, @host).set_attributes
|
100
110
|
|
101
|
-
|
102
|
-
|
111
|
+
def get_attributes
|
112
|
+
return @creds
|
113
|
+
end
|
103
114
|
end
|
104
115
|
end
|
105
|
-
end
|