ubiq-security 1.0.0 → 1.0.1
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/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
|