ubiq-security 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/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +10 -0
- data/LICENSE.txt +17 -0
- data/README.md +188 -0
- data/Rakefile +6 -0
- data/lib/ubiq-security.rb +22 -0
- data/lib/ubiq/algo.rb +69 -0
- data/lib/ubiq/auth.rb +99 -0
- data/lib/ubiq/credentials.rb +105 -0
- data/lib/ubiq/decrypt.rb +268 -0
- data/lib/ubiq/encrypt.rb +207 -0
- data/lib/ubiq/host.rb +18 -0
- data/lib/ubiq/version.rb +22 -0
- data/ubiq-security.gemspec +30 -0
- metadata +101 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 20d4dacbe17d8162ab7d308069b82e2b54b89cf7a7253763919742ef350597e3
|
4
|
+
data.tar.gz: 370015b19528bc6a81678e3b0f7f270b0524b6309f23dc6087d04256edad1e27
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7ed8203d50dbe828d649af7decc40f300f1b2b5d74e912db9119e84444c840dc45a8fcc1c8e0b3fa49a8d7a6ebf73fbb9ce935e7244c1669d9d072af61674c52
|
7
|
+
data.tar.gz: ddd4e2690d2d47a737fda60d53524d9b57eec456410f14f5e4a56cb61e447e632f6c7a70bc0cc2dfc5126b95191df739dd6484089e07e6126f2d848989b28841
|
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
2
|
+
|
3
|
+
## Our Pledge
|
4
|
+
|
5
|
+
In the interest of fostering an open and welcoming environment, we as
|
6
|
+
contributors and maintainers pledge to making participation in our project and
|
7
|
+
our community a harassment-free experience for everyone, regardless of age, body
|
8
|
+
size, disability, ethnicity, gender identity and expression, level of experience,
|
9
|
+
nationality, personal appearance, race, religion, or sexual identity and
|
10
|
+
orientation.
|
11
|
+
|
12
|
+
## Our Standards
|
13
|
+
|
14
|
+
Examples of behavior that contributes to creating a positive environment
|
15
|
+
include:
|
16
|
+
|
17
|
+
* Using welcoming and inclusive language
|
18
|
+
* Being respectful of differing viewpoints and experiences
|
19
|
+
* Gracefully accepting constructive criticism
|
20
|
+
* Focusing on what is best for the community
|
21
|
+
* Showing empathy towards other community members
|
22
|
+
|
23
|
+
Examples of unacceptable behavior by participants include:
|
24
|
+
|
25
|
+
* The use of sexualized language or imagery and unwelcome sexual attention or
|
26
|
+
advances
|
27
|
+
* Trolling, insulting/derogatory comments, and personal or political attacks
|
28
|
+
* Public or private harassment
|
29
|
+
* Publishing others' private information, such as a physical or electronic
|
30
|
+
address, without explicit permission
|
31
|
+
* Other conduct which could reasonably be considered inappropriate in a
|
32
|
+
professional setting
|
33
|
+
|
34
|
+
## Our Responsibilities
|
35
|
+
|
36
|
+
Project maintainers are responsible for clarifying the standards of acceptable
|
37
|
+
behavior and are expected to take appropriate and fair corrective action in
|
38
|
+
response to any instances of unacceptable behavior.
|
39
|
+
|
40
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
41
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
42
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
43
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
44
|
+
threatening, offensive, or harmful.
|
45
|
+
|
46
|
+
## Scope
|
47
|
+
|
48
|
+
This Code of Conduct applies both within project spaces and in public spaces
|
49
|
+
when an individual is representing the project or its community. Examples of
|
50
|
+
representing a project or community include using an official project e-mail
|
51
|
+
address, posting via an official social media account, or acting as an appointed
|
52
|
+
representative at an online or offline event. Representation of a project may be
|
53
|
+
further defined and clarified by project maintainers.
|
54
|
+
|
55
|
+
## Enforcement
|
56
|
+
|
57
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
58
|
+
reported by contacting the project team at support@ubiqsecurity.com. All
|
59
|
+
complaints will be reviewed and investigated and will result in a response that
|
60
|
+
is deemed necessary and appropriate to the circumstances. The project team is
|
61
|
+
obligated to maintain confidentiality with regard to the reporter of an incident.
|
62
|
+
Further details of specific enforcement policies may be posted separately.
|
63
|
+
|
64
|
+
Project maintainers who do not follow or enforce the Code of Conduct in good
|
65
|
+
faith may face temporary or permanent repercussions as determined by other
|
66
|
+
members of the project's leadership.
|
67
|
+
|
68
|
+
## Attribution
|
69
|
+
|
70
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
71
|
+
available at [https://contributor-covenant.org/version/1/4][version]
|
72
|
+
|
73
|
+
[homepage]: https://contributor-covenant.org
|
74
|
+
[version]: https://contributor-covenant.org/version/1/4/
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
|
2
|
+
Copyright 2020 Ubiq Security, Inc., Proprietary and All Rights Reserved.
|
3
|
+
|
4
|
+
NOTICE: All information contained herein is, and remains the property
|
5
|
+
of Ubiq Security, Inc. The intellectual and technical concepts contained
|
6
|
+
herein are proprietary to Ubiq Security, Inc. and its suppliers and may be
|
7
|
+
covered by U.S. and Foreign Patents, patents in process, and are
|
8
|
+
protected by trade secret or copyright law. Dissemination of this
|
9
|
+
information or reproduction of this material is strictly forbidden
|
10
|
+
unless prior written permission is obtained from Ubiq Security, Inc.
|
11
|
+
|
12
|
+
Your use of the software is expressly conditioned upon the terms
|
13
|
+
and conditions available at:
|
14
|
+
|
15
|
+
https://ubiqsecurity.com/legal
|
16
|
+
|
17
|
+
|
data/README.md
ADDED
@@ -0,0 +1,188 @@
|
|
1
|
+
# Ubiq Security Ruby Library
|
2
|
+
|
3
|
+
The Ubiq Security Ruby library provides convenient interaction with the
|
4
|
+
Ubiq Security Platform API from applications written in the Ruby language.
|
5
|
+
It includes a pre-defined set of classes that will provide simple interfaces
|
6
|
+
to encrypt and decrypt data
|
7
|
+
|
8
|
+
## Documentation
|
9
|
+
|
10
|
+
See the [Ruby API docs](https://ubiqsecurity.com/docs/api?lang=ruby).
|
11
|
+
|
12
|
+
|
13
|
+
|
14
|
+
## Installation
|
15
|
+
|
16
|
+
|
17
|
+
To install using [Bundler][bundler] add the following to your project's Gemfile
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
gem ubiq-security
|
21
|
+
```
|
22
|
+
|
23
|
+
To manually install `ubiq-security` via [Rubygems][rubygems] simply use gem to install it:
|
24
|
+
|
25
|
+
```sh
|
26
|
+
gem install ubiq-security
|
27
|
+
```
|
28
|
+
|
29
|
+
To build and install directly from a clone of the gitlab repository source:
|
30
|
+
|
31
|
+
```sh
|
32
|
+
git clone https://gitlab.com/ubiqsecurity/ubiq-ruby.git
|
33
|
+
cd ubiq-ruby
|
34
|
+
rake install:local
|
35
|
+
```
|
36
|
+
|
37
|
+
|
38
|
+
## Usage
|
39
|
+
|
40
|
+
The library needs to be configured with your account credentials which is
|
41
|
+
available in your [Ubiq Dashboard][dashboard] [Credentials][credentials]. The credentials can be
|
42
|
+
explicitly set, set using environment variables, loaded from an explicit file
|
43
|
+
or read from the default location [~/.ubiq/credentials]
|
44
|
+
|
45
|
+
```ruby
|
46
|
+
require 'ubiq-security'
|
47
|
+
include Ubiq
|
48
|
+
```
|
49
|
+
|
50
|
+
### Read credentials from a specific file and use a specific profile
|
51
|
+
```ruby
|
52
|
+
credentials = ConfigCredentials.new( "some-credential-file", "some-profile").get_attributes
|
53
|
+
```
|
54
|
+
|
55
|
+
|
56
|
+
### Read credentials from ~/.ubiq/credentials and use the default profile
|
57
|
+
```ruby
|
58
|
+
credentials = ConfigCredentials.new().get_attributes
|
59
|
+
```
|
60
|
+
|
61
|
+
|
62
|
+
### Use the following environment variables to set the credential values
|
63
|
+
UBIQ_ACCESS_KEY_ID
|
64
|
+
UBIQ_SECRET_SIGNING_KEY
|
65
|
+
UBIQ_SECRET_CRYPTO_ACCESS_KEY
|
66
|
+
```ruby
|
67
|
+
credentials = Credentials()
|
68
|
+
```
|
69
|
+
|
70
|
+
|
71
|
+
### Explicitly set the credentials
|
72
|
+
```ruby
|
73
|
+
credentials = Credentials(access_key_id = "...", secret_signing_key = "...", secret_crypto_access_key = "...")
|
74
|
+
```
|
75
|
+
|
76
|
+
|
77
|
+
|
78
|
+
|
79
|
+
### Encrypt a simple block of data
|
80
|
+
|
81
|
+
Pass credentials and data into the encryption function. The encrypted data
|
82
|
+
will be returned.
|
83
|
+
|
84
|
+
|
85
|
+
```ruby
|
86
|
+
require "ubiq-security"
|
87
|
+
include Ubiq
|
88
|
+
|
89
|
+
encrypted_data = encrypt(credentials, plaintext_data)
|
90
|
+
```
|
91
|
+
|
92
|
+
|
93
|
+
### Decrypt a simple block of data
|
94
|
+
|
95
|
+
Pass credentials and encrypted data into the decryption function. The plaintext data
|
96
|
+
will be returned.
|
97
|
+
|
98
|
+
```ruby
|
99
|
+
require "ubiq-security"
|
100
|
+
include Ubiq
|
101
|
+
|
102
|
+
plaintext_data = decrypt(credentials, encrypted_data)
|
103
|
+
```
|
104
|
+
|
105
|
+
|
106
|
+
### Encrypt a large data element where data is loaded in chunks
|
107
|
+
|
108
|
+
- Create an encryption object using the credentials.
|
109
|
+
- Call the encryption instance begin method
|
110
|
+
- Call the encryption instance update method repeatedly until all the data is processed
|
111
|
+
- Call the encryption instance end method
|
112
|
+
- Call the encryption instance close method
|
113
|
+
|
114
|
+
|
115
|
+
```ruby
|
116
|
+
require "ubiq-security"
|
117
|
+
include Ubiq
|
118
|
+
|
119
|
+
# Process 1 MiB of plaintext data at a time
|
120
|
+
BLOCK_SIZE = 1024 * 1024
|
121
|
+
|
122
|
+
# Rest of the program
|
123
|
+
....
|
124
|
+
encryption = Encryption.new(credentials, 1)
|
125
|
+
|
126
|
+
# Write out the header information
|
127
|
+
encrypted_data = encryption.begin()
|
128
|
+
|
129
|
+
# Loop until the end of the input file is reached
|
130
|
+
until infile.eof?
|
131
|
+
chunk = infile.read BLOCK_SIZE
|
132
|
+
encrypted_data += encryption.update(chunk))
|
133
|
+
end
|
134
|
+
# Make sure any additional encrypted data is retrieved from encryption instance
|
135
|
+
encrypted_data += encryption.end()
|
136
|
+
|
137
|
+
# Make sure to release any resources used during the encryption process
|
138
|
+
encryption.close()
|
139
|
+
```
|
140
|
+
|
141
|
+
### Decrypt a large data element where data is loaded in chunks
|
142
|
+
|
143
|
+
- Create an instance of the decryption object using the credentials.
|
144
|
+
- Call the decryption instance begin method
|
145
|
+
- Call the decryption instance update method repeatedly until all the data is processed
|
146
|
+
- Call the decryption instance end method
|
147
|
+
- Call the decryption instance close method
|
148
|
+
|
149
|
+
|
150
|
+
```ruby
|
151
|
+
require "ubiq-security"
|
152
|
+
include Ubiq
|
153
|
+
|
154
|
+
# Process 1 MiB of encrypted data at a time
|
155
|
+
BLOCK_SIZE = 1024 * 1024
|
156
|
+
|
157
|
+
# Rest of the program
|
158
|
+
....
|
159
|
+
|
160
|
+
decryption = Decryption(credentials)
|
161
|
+
|
162
|
+
# Start the decryption and get any header information
|
163
|
+
plaintext_data = decryption.begin())
|
164
|
+
|
165
|
+
# Loop until the end of the input file is reached
|
166
|
+
until infile.eof?
|
167
|
+
chunk = infile.read BLOCK_SIZE
|
168
|
+
plaintext_data += decryption.update(chunk)
|
169
|
+
end
|
170
|
+
|
171
|
+
# Make sure an additional plaintext data is retrieved from decryption instance
|
172
|
+
plaintext_data += decryption.end()
|
173
|
+
|
174
|
+
# Make sure to release any resources used during the decryption process
|
175
|
+
decryption.close()
|
176
|
+
```
|
177
|
+
|
178
|
+
|
179
|
+
|
180
|
+
|
181
|
+
[bundler]: https://bundler.io
|
182
|
+
[rubygems]: https://rubygems.org
|
183
|
+
[gem]: https://rubygems.org/gems/uniq-security
|
184
|
+
[dashboard]:https://dev.ubiqsecurity.com/docs/dashboard
|
185
|
+
[credentials]:https://dev.ubiqsecurity.com/docs/how-to-create-api-keys
|
186
|
+
|
187
|
+
|
188
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# Copyright 2020 Ubiq Security, Inc., Proprietary and All Rights Reserved.
|
2
|
+
#
|
3
|
+
# NOTICE: All information contained herein is, and remains the property
|
4
|
+
# of Ubiq Security, Inc. The intellectual and technical concepts contained
|
5
|
+
# herein are proprietary to Ubiq Security, Inc. and its suppliers and may be
|
6
|
+
# covered by U.S. and Foreign Patents, patents in process, and are
|
7
|
+
# protected by trade secret or copyright law. Dissemination of this
|
8
|
+
# information or reproduction of this material is strictly forbidden
|
9
|
+
# unless prior written permission is obtained from Ubiq Security, Inc.
|
10
|
+
#
|
11
|
+
# Your use of the software is expressly conditioned upon the terms
|
12
|
+
# and conditions available at:
|
13
|
+
#
|
14
|
+
# https://ubiqsecurity.com/legal
|
15
|
+
|
16
|
+
require "ubiq/version"
|
17
|
+
require "ubiq/encrypt"
|
18
|
+
require "ubiq/decrypt"
|
19
|
+
require "ubiq/credentials"
|
20
|
+
|
21
|
+
|
22
|
+
|
data/lib/ubiq/algo.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
# Copyright 2020 Ubiq Security, Inc., Proprietary and All Rights Reserved.
|
2
|
+
#
|
3
|
+
# NOTICE: All information contained herein is, and remains the property
|
4
|
+
# of Ubiq Security, Inc. The intellectual and technical concepts contained
|
5
|
+
# herein are proprietary to Ubiq Security, Inc. and its suppliers and may be
|
6
|
+
# covered by U.S. and Foreign Patents, patents in process, and are
|
7
|
+
# protected by trade secret or copyright law. Dissemination of this
|
8
|
+
# information or reproduction of this material is strictly forbidden
|
9
|
+
# unless prior written permission is obtained from Ubiq Security, Inc.
|
10
|
+
#
|
11
|
+
# Your use of the software is expressly conditioned upon the terms
|
12
|
+
# and conditions available at:
|
13
|
+
#
|
14
|
+
# https://ubiqsecurity.com/legal
|
15
|
+
#
|
16
|
+
|
17
|
+
require "active_support/all"
|
18
|
+
require 'openssl'
|
19
|
+
|
20
|
+
module Ubiq
|
21
|
+
|
22
|
+
class Algo
|
23
|
+
|
24
|
+
def set_algo
|
25
|
+
@algorithm = {
|
26
|
+
"aes-256-gcm"=>{
|
27
|
+
id:0,
|
28
|
+
algorithm: OpenSSL::Cipher::AES256,
|
29
|
+
mode: OpenSSL::Cipher::AES256.new(:GCM),
|
30
|
+
key_length: 32,
|
31
|
+
iv_length: 12,
|
32
|
+
tag_length: 16
|
33
|
+
},
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
def get_algo(name)
|
38
|
+
set_algo[name]
|
39
|
+
end
|
40
|
+
|
41
|
+
def encryptor(obj,key, iv=nil)
|
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
|
46
|
+
|
47
|
+
cipher = obj[:mode]
|
48
|
+
raise RuntimeError, 'Invalid key length' if key.length != obj[:key_length]
|
49
|
+
|
50
|
+
raise RuntimeError, 'Invalid initialization vector length' if (iv!= nil and iv.length != obj[:iv_length])
|
51
|
+
cipher.encrypt
|
52
|
+
cipher.key = key
|
53
|
+
iv = cipher.random_iv
|
54
|
+
return cipher, iv
|
55
|
+
end
|
56
|
+
|
57
|
+
def decryptor(obj, key, iv)
|
58
|
+
cipher = obj[:mode]
|
59
|
+
raise RuntimeError, 'Invalid key length' if key.length != obj[:key_length]
|
60
|
+
|
61
|
+
raise RuntimeError, 'Invalid initialization vector length' if (iv!= nil and iv.length != obj[:iv_length])
|
62
|
+
cipher = obj[:mode]
|
63
|
+
cipher.decrypt
|
64
|
+
cipher.key = key
|
65
|
+
cipher.iv = iv
|
66
|
+
return cipher
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
data/lib/ubiq/auth.rb
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
# Copyright 2020 Ubiq Security, Inc., Proprietary and All Rights Reserved.
|
2
|
+
#
|
3
|
+
# NOTICE: All information contained herein is, and remains the property
|
4
|
+
# of Ubiq Security, Inc. The intellectual and technical concepts contained
|
5
|
+
# herein are proprietary to Ubiq Security, Inc. and its suppliers and may be
|
6
|
+
# covered by U.S. and Foreign Patents, patents in process, and are
|
7
|
+
# protected by trade secret or copyright law. Dissemination of this
|
8
|
+
# information or reproduction of this material is strictly forbidden
|
9
|
+
# unless prior written permission is obtained from Ubiq Security, Inc.
|
10
|
+
#
|
11
|
+
# Your use of the software is expressly conditioned upon the terms
|
12
|
+
# and conditions available at:
|
13
|
+
#
|
14
|
+
# https://ubiqsecurity.com/legal
|
15
|
+
#
|
16
|
+
require "active_support/all"
|
17
|
+
|
18
|
+
module Ubiq
|
19
|
+
|
20
|
+
class Auth
|
21
|
+
# HTTP Authentication for the Ubiq Platform
|
22
|
+
|
23
|
+
# This module implements HTTP authentication for the Ubiq platform
|
24
|
+
# via message signing as described by the IETF httpbis-message-signatures
|
25
|
+
# draft specification.
|
26
|
+
|
27
|
+
def self.build_headers(papi, sapi, endpoint, query, host, http_method)
|
28
|
+
|
29
|
+
# This function calculates the signature for the message, adding the Signature header
|
30
|
+
# to contain the data. Certain HTTP headers are required for
|
31
|
+
# signature calculation and will be added by this code as
|
32
|
+
# necessary. The constructed headers object is returned
|
33
|
+
|
34
|
+
# the '(request-target)' is part of the signed data.
|
35
|
+
# it's value is 'http_method path?query'
|
36
|
+
reqt = "#{http_method} #{endpoint}"
|
37
|
+
|
38
|
+
# The time at which the signature was created expressed as the unix epoch
|
39
|
+
created = Time.now.to_i
|
40
|
+
|
41
|
+
# the Digest header is always included/overridden by
|
42
|
+
# this code. it is a hash of the body of the http message
|
43
|
+
# and is always present even if the body is empty
|
44
|
+
hash_sha512 = OpenSSL::Digest::SHA512.new
|
45
|
+
hash_sha512 << JSON.dump(query)
|
46
|
+
digest = 'SHA-512='+Base64.strict_encode64(hash_sha512.digest)
|
47
|
+
|
48
|
+
# Initialize the headers object to be returned via this method
|
49
|
+
all_headers = {}
|
50
|
+
# The content type of request
|
51
|
+
all_headers['content-type'] = 'application/json'
|
52
|
+
# The request target calculated above(reqt)
|
53
|
+
all_headers['(request-target)'] = reqt
|
54
|
+
# The date and time in GMT format
|
55
|
+
all_headers['date'] = get_date
|
56
|
+
# The host specified by the caller
|
57
|
+
all_headers['host'] = get_host(host)
|
58
|
+
all_headers['(created)'] = created
|
59
|
+
all_headers['digest'] = digest
|
60
|
+
headers = ['content-type', 'date', 'host', '(created)', '(request-target)', 'digest']
|
61
|
+
|
62
|
+
# include the specified headers in the hmac calculation. each
|
63
|
+
# header is of the form 'header_name: header value\n'
|
64
|
+
# included headers are also added to an ordered list of headers
|
65
|
+
# which is included in the message
|
66
|
+
hmac = OpenSSL::HMAC.new(sapi, OpenSSL::Digest::SHA512.new)
|
67
|
+
headers.each do |header|
|
68
|
+
if all_headers.key?(header)
|
69
|
+
hmac << "#{header}: #{all_headers[header]}\n"
|
70
|
+
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
|
+
|
86
|
+
return all_headers
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.get_host(host)
|
90
|
+
uri = URI(host)
|
91
|
+
return "#{uri.hostname}:#{uri.port}"
|
92
|
+
end
|
93
|
+
|
94
|
+
def self.get_date
|
95
|
+
DateTime.now.in_time_zone('GMT').strftime("%a, %d %b %Y") + " " + DateTime.now.in_time_zone('GMT').strftime("%H:%M:%S") + " GMT"
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
# Copyright 2020 Ubiq Security, Inc., Proprietary and All Rights Reserved.
|
2
|
+
#
|
3
|
+
# NOTICE: All information contained herein is, and remains the property
|
4
|
+
# of Ubiq Security, Inc. The intellectual and technical concepts contained
|
5
|
+
# herein are proprietary to Ubiq Security, Inc. and its suppliers and may be
|
6
|
+
# covered by U.S. and Foreign Patents, patents in process, and are
|
7
|
+
# protected by trade secret or copyright law. Dissemination of this
|
8
|
+
# information or reproduction of this material is strictly forbidden
|
9
|
+
# unless prior written permission is obtained from Ubiq Security, Inc.
|
10
|
+
#
|
11
|
+
# Your use of the software is expressly conditioned upon the terms
|
12
|
+
# and conditions available at:
|
13
|
+
#
|
14
|
+
# https://ubiqsecurity.com/legal
|
15
|
+
#
|
16
|
+
require 'configparser'
|
17
|
+
require 'rb-readline'
|
18
|
+
require 'byebug'
|
19
|
+
|
20
|
+
module Ubiq
|
21
|
+
|
22
|
+
class CredentialsInfo
|
23
|
+
|
24
|
+
def initialize(access_key_id, secret_signing_key, secret_crypto_access_key, host)
|
25
|
+
@access_key_id = access_key_id
|
26
|
+
@secret_signing_key = secret_signing_key
|
27
|
+
@secret_crypto_access_key = secret_crypto_access_key
|
28
|
+
@host = host
|
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"
|
42
|
+
end
|
43
|
+
|
44
|
+
if config_file == nil
|
45
|
+
config_file = "~/.ubiq/credentials"
|
46
|
+
end
|
47
|
+
|
48
|
+
# If config file is found
|
49
|
+
if File.exists?(File.expand_path(config_file))
|
50
|
+
@creds = load_config_file(config_file, profile)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def get_attributes
|
55
|
+
return @creds
|
56
|
+
end
|
57
|
+
|
58
|
+
def load_config_file(file, profile)
|
59
|
+
config = ConfigParser.new(File.expand_path(file))
|
60
|
+
|
61
|
+
# Create empty dictionaries for the default and supplied profile
|
62
|
+
p = {}
|
63
|
+
d = {}
|
64
|
+
|
65
|
+
# get the default profile if there is one
|
66
|
+
if config['default'].present?
|
67
|
+
d = config['default']
|
68
|
+
end
|
69
|
+
|
70
|
+
# get the supplied profile if there is one
|
71
|
+
if config[profile].present?
|
72
|
+
p = config[profile]
|
73
|
+
end
|
74
|
+
|
75
|
+
# Use given profile if it is available, otherwise use default.
|
76
|
+
access_key_id = p.key?('ACCESS_KEY_ID') ? p['ACCESS_KEY_ID'] : d['ACCESS_KEY_ID']
|
77
|
+
secret_signing_key = p.key?('SECRET_SIGNING_KEY') ? p['SECRET_SIGNING_KEY'] : d['SECRET_SIGNING_KEY']
|
78
|
+
secret_crypto_access_key = p.key?('SECRET_CRYPTO_ACCESS_KEY') ? p['SECRET_CRYPTO_ACCESS_KEY'] : d['SECRET_CRYPTO_ACCESS_KEY']
|
79
|
+
host = p.key?('SERVER') ? p['SERVER'] : d['SERVER']
|
80
|
+
|
81
|
+
# If the provided host does not contain http protocol then add to it
|
82
|
+
if !host.include?('http://') and !host.include?('https://')
|
83
|
+
host = 'https://' + host
|
84
|
+
end
|
85
|
+
|
86
|
+
return CredentialsInfo.new(access_key_id, secret_signing_key, secret_crypto_access_key, host).set_attributes
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
class Credentials < CredentialsInfo
|
91
|
+
|
92
|
+
def initialize(papi, sapi, srsa, host)
|
93
|
+
@access_key_id = papi.present? ? papi : ENV['UBIQ_ACCESS_KEY_ID']
|
94
|
+
@secret_signing_key = sapi.present? ? sapi : ENV['UBIQ_SECRET_SIGNING_KEY']
|
95
|
+
@secret_crypto_access_key = srsa.present? ? srsa : ENV['UBIQ_SECRET_CRYPTO_ACCESS_KEY']
|
96
|
+
@host = host.present? ? host : ENV['UBIQ_SERVER']
|
97
|
+
end
|
98
|
+
|
99
|
+
@creds = CredentialsInfo.new(@access_key_id, @secret_signing_key, @secret_crypto_access_key, @host).set_attributes
|
100
|
+
|
101
|
+
def get_attributes
|
102
|
+
return @creds
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
data/lib/ubiq/decrypt.rb
ADDED
@@ -0,0 +1,268 @@
|
|
1
|
+
# Copyright 2020 Ubiq Security, Inc., Proprietary and All Rights Reserved.
|
2
|
+
#
|
3
|
+
# NOTICE: All information contained herein is, and remains the property
|
4
|
+
# of Ubiq Security, Inc. The intellectual and technical concepts contained
|
5
|
+
# herein are proprietary to Ubiq Security, Inc. and its suppliers and may be
|
6
|
+
# covered by U.S. and Foreign Patents, patents in process, and are
|
7
|
+
# protected by trade secret or copyright law. Dissemination of this
|
8
|
+
# information or reproduction of this material is strictly forbidden
|
9
|
+
# unless prior written permission is obtained from Ubiq Security, Inc.
|
10
|
+
#
|
11
|
+
# Your use of the software is expressly conditioned upon the terms
|
12
|
+
# and conditions available at:
|
13
|
+
#
|
14
|
+
# https://ubiqsecurity.com/legal
|
15
|
+
#
|
16
|
+
require 'rb-readline'
|
17
|
+
require 'byebug'
|
18
|
+
require 'httparty'
|
19
|
+
require "active_support/all"
|
20
|
+
require_relative './auth.rb'
|
21
|
+
require_relative './algo.rb'
|
22
|
+
require_relative './encrypt.rb'
|
23
|
+
require 'webrick'
|
24
|
+
|
25
|
+
module Ubiq
|
26
|
+
|
27
|
+
class Decryption
|
28
|
+
def initialize(creds)
|
29
|
+
# Initialize the decryption module object
|
30
|
+
# Set the credentials in instance varibales to be used among methods
|
31
|
+
# the server to which to make the request
|
32
|
+
raise RuntimeError, 'Some of your credentials are missing, please check!' if !validate_creds(creds)
|
33
|
+
@host = creds.host.blank? ? UBIQ_HOST : creds.host
|
34
|
+
|
35
|
+
# The client's public API key (used to identify the client to the server
|
36
|
+
@papi = creds.access_key_id
|
37
|
+
|
38
|
+
# The client's secret API key (used to authenticate HTTP requests)
|
39
|
+
@sapi = creds.secret_signing_key
|
40
|
+
|
41
|
+
# The client's secret RSA encryption key/password (used to decrypt the client's RSA key from the server). This key is not retained by this object.
|
42
|
+
@srsa = creds.secret_crypto_access_key
|
43
|
+
|
44
|
+
@decryption_ready = true
|
45
|
+
@decryption_started = false
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
def endpoint_base
|
50
|
+
@host + '/api/v0'
|
51
|
+
end
|
52
|
+
|
53
|
+
def endpoint
|
54
|
+
'/api/v0/decryption/key'
|
55
|
+
end
|
56
|
+
|
57
|
+
def begin
|
58
|
+
# Begin the decryption process
|
59
|
+
|
60
|
+
# This interface does not take any cipher text in its arguments
|
61
|
+
# in an attempt to maintain an API that corresponds to the
|
62
|
+
# encryption object. In doing so, the work that can take place
|
63
|
+
# in this function is limited. without any data, there is no
|
64
|
+
# way to determine which key is in use or decrypt any data.
|
65
|
+
#
|
66
|
+
# this function simply throws an error if starting an decryption
|
67
|
+
# while one is already in progress, and initializes the internal
|
68
|
+
# buffer
|
69
|
+
|
70
|
+
raise RuntimeError, 'Decryption is not ready' if !@decryption_ready
|
71
|
+
|
72
|
+
raise RuntimeError, 'Decryption Already Started' if @decryption_started
|
73
|
+
|
74
|
+
raise RuntimeError, 'Decryption already in progress' if @key.present? and @key.key?("dec")
|
75
|
+
@decryption_started = true
|
76
|
+
@data = ''
|
77
|
+
end
|
78
|
+
|
79
|
+
def update(data)
|
80
|
+
# Decryption of cipher text is performed here
|
81
|
+
# Cipher text must be passed to this function in the order in which it was output from the encryption.update function.
|
82
|
+
|
83
|
+
# Each encryption has a header on it that identifies the algorithm
|
84
|
+
# used and an encryption of the data key that was used to encrypt
|
85
|
+
# the original plain text. there is no guarantee how much of that
|
86
|
+
# data will be passed to this function or how many times this
|
87
|
+
# function will be called to process all of the data. to that end,
|
88
|
+
# this function buffers data internally, when it is unable to
|
89
|
+
# process it.
|
90
|
+
#
|
91
|
+
# The function buffers data internally until the entire header is
|
92
|
+
# received. once the header has been received, the encrypted data
|
93
|
+
# key is sent to the server for decryption. after the header has
|
94
|
+
# been successfully handled, this function always decrypts all of
|
95
|
+
# the data in its internal buffer *except* for however many bytes
|
96
|
+
# are specified by the algorithm's tag size. see the end() function
|
97
|
+
# for details.
|
98
|
+
|
99
|
+
raise RuntimeError, 'Decryption is not Started' if !@decryption_started
|
100
|
+
|
101
|
+
# Append the incoming data in the internal data buffer
|
102
|
+
@data = @data + data
|
103
|
+
|
104
|
+
# if there is no key or 'dec' member of key, then the code is still trying to build a complete header
|
105
|
+
if !@key.present? or !@key.key?("dec")
|
106
|
+
struct_length = [1,1,1,1,1].pack('CCCCn').length
|
107
|
+
packed_struct = @data[0...struct_length]
|
108
|
+
|
109
|
+
# Does the buffer contain enough of the header to
|
110
|
+
# determine the lengths of the initialization vector
|
111
|
+
# and the key?
|
112
|
+
if @data.length > struct_length
|
113
|
+
# Unpack the values packed in encryption
|
114
|
+
version, flag_for_later, algorithm_id, iv_length, key_length = packed_struct.unpack('CCCCn')
|
115
|
+
|
116
|
+
# verify flag and version are 0
|
117
|
+
raise RuntimeError, 'invalid encryption header' if version != 0 or flag_for_later != 0
|
118
|
+
|
119
|
+
# Does the buffer contain the entire header?
|
120
|
+
if @data.length > struct_length + iv_length + key_length
|
121
|
+
# Extract the initialization vector
|
122
|
+
iv = @data[struct_length...iv_length + struct_length]
|
123
|
+
# Extract the encryped key
|
124
|
+
encrypted_key = @data[struct_length + iv_length...key_length + struct_length + iv_length]
|
125
|
+
# Remove the header from the buffer
|
126
|
+
@data = @data[struct_length + iv_length + key_length..-1]
|
127
|
+
|
128
|
+
# generate a local identifier for the key
|
129
|
+
hash_sha512 = OpenSSL::Digest::SHA512.new
|
130
|
+
hash_sha512 << encrypted_key
|
131
|
+
client_id = hash_sha512.digest
|
132
|
+
|
133
|
+
if @key.present?
|
134
|
+
if @key['client_id'] != client_id
|
135
|
+
close()
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
# IF key object not exists, request a new one from the server
|
140
|
+
if !@key.present?
|
141
|
+
url = endpoint_base + "/decryption/key"
|
142
|
+
query = {encrypted_data_key: Base64.strict_encode64(encrypted_key)}
|
143
|
+
headers = Auth.build_headers(@papi, @sapi, endpoint, query, @host, 'post')
|
144
|
+
|
145
|
+
response = HTTParty.post(
|
146
|
+
url,
|
147
|
+
body: query.to_json,
|
148
|
+
headers: headers
|
149
|
+
)
|
150
|
+
|
151
|
+
# Response status is 200 OK
|
152
|
+
if response.code == WEBrick::HTTPStatus::RC_OK
|
153
|
+
@key = {}
|
154
|
+
@key['finger_print'] = response['key_fingerprint']
|
155
|
+
@key['client_id'] = client_id
|
156
|
+
@key['session'] = response['encryption_session']
|
157
|
+
|
158
|
+
@key['algorithm'] = 'aes-256-gcm'
|
159
|
+
|
160
|
+
encrypted_private_key = response['encrypted_private_key']
|
161
|
+
# Decrypt the encryped private key using SRSA
|
162
|
+
private_key = OpenSSL::PKey::RSA.new(encrypted_private_key,@srsa)
|
163
|
+
|
164
|
+
wrapped_data_key = response['wrapped_data_key']
|
165
|
+
# Decode WDK from base64 format
|
166
|
+
wdk = Base64.strict_decode64(wrapped_data_key)
|
167
|
+
# Use private key to decrypt the wrapped data key
|
168
|
+
dk = private_key.private_decrypt(wdk,OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING)
|
169
|
+
|
170
|
+
@key['raw'] = dk
|
171
|
+
@key['uses'] = 0
|
172
|
+
else
|
173
|
+
# Raise the error if response is not 200
|
174
|
+
raise RuntimeError, "HTTPError Response: Expected 201, got #{response.code}"
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
# If the key object exists, create a new decryptor
|
179
|
+
# with the initialization vector from the header and
|
180
|
+
# the decrypted key (which is either new from the
|
181
|
+
# server or cached from the previous decryption). in
|
182
|
+
# either case, increment the key usage
|
183
|
+
|
184
|
+
if @key.present?
|
185
|
+
@algo = Algo.new.get_algo(@key['algorithm'])
|
186
|
+
@key['dec'] = Algo.new.decryptor(@algo, @key['raw'], iv)
|
187
|
+
@key['uses'] += 1
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
# if the object has a key and a decryptor, then decrypt whatever
|
194
|
+
# data is in the buffer, less any data that needs to be saved to
|
195
|
+
# serve as the tag.
|
196
|
+
plain_text = ''
|
197
|
+
if @key.present? and @key.key?("dec")
|
198
|
+
size = @data.length - @algo[:tag_length]
|
199
|
+
if size > 0
|
200
|
+
plain_text = @key['dec'].update(@data[0..size-1])
|
201
|
+
@data = @data[size..-1]
|
202
|
+
end
|
203
|
+
return plain_text
|
204
|
+
end
|
205
|
+
|
206
|
+
end
|
207
|
+
|
208
|
+
def end
|
209
|
+
raise RuntimeError, 'Decryption is not Started' if !@decryption_started
|
210
|
+
# The update function always maintains tag-size bytes in
|
211
|
+
# the buffer because this function provides no data parameter.
|
212
|
+
# by the time the caller calls this function, all data must
|
213
|
+
# have already been input to the decryption object.
|
214
|
+
|
215
|
+
sz = @data.length - @algo[:tag_length]
|
216
|
+
|
217
|
+
raise RuntimeError, 'Invalid Tag!' if sz < 0
|
218
|
+
if sz == 0
|
219
|
+
@key['dec'].auth_tag = @data
|
220
|
+
begin
|
221
|
+
pt = @key['dec'].final
|
222
|
+
# Delete the decryptor context
|
223
|
+
@key.delete('dec')
|
224
|
+
# Return the decrypted plain data
|
225
|
+
@decryption_started = false
|
226
|
+
return pt
|
227
|
+
rescue Exception => e
|
228
|
+
print 'Invalid cipher data or tag!'
|
229
|
+
return ''
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
def close
|
235
|
+
raise RuntimeError, 'Decryption currently running' if @decryption_started
|
236
|
+
# Reset the internal state of the decryption object
|
237
|
+
if @key.present?
|
238
|
+
if @key['uses'] > 0
|
239
|
+
query_url = "#{endpoint}/#{@key['finger_print']}/#{@key['session']}"
|
240
|
+
url = "#{endpoint_base}/decryption/key/#{@key['finger_print']}/#{@key['session']}"
|
241
|
+
query = {uses: @key['uses']}
|
242
|
+
headers = Auth.build_headers(@papi, @sapi, query_url, query, @host, 'patch')
|
243
|
+
response = HTTParty.patch(
|
244
|
+
url,
|
245
|
+
body: query.to_json,
|
246
|
+
headers: headers
|
247
|
+
)
|
248
|
+
remove_instance_variable(:@data)
|
249
|
+
remove_instance_variable(:@key)
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
end
|
255
|
+
|
256
|
+
def decrypt(creds, data)
|
257
|
+
begin
|
258
|
+
dec = Decryption.new(creds)
|
259
|
+
res = dec.begin() + dec.update(data) + dec.end()
|
260
|
+
dec.close()
|
261
|
+
rescue
|
262
|
+
dec.close() if dec
|
263
|
+
raise
|
264
|
+
end
|
265
|
+
return res
|
266
|
+
|
267
|
+
end
|
268
|
+
end
|
data/lib/ubiq/encrypt.rb
ADDED
@@ -0,0 +1,207 @@
|
|
1
|
+
# Copyright 2020 Ubiq Security, Inc., Proprietary and All Rights Reserved.
|
2
|
+
#
|
3
|
+
# NOTICE: All information contained herein is, and remains the property
|
4
|
+
# of Ubiq Security, Inc. The intellectual and technical concepts contained
|
5
|
+
# herein are proprietary to Ubiq Security, Inc. and its suppliers and may be
|
6
|
+
# covered by U.S. and Foreign Patents, patents in process, and are
|
7
|
+
# protected by trade secret or copyright law. Dissemination of this
|
8
|
+
# information or reproduction of this material is strictly forbidden
|
9
|
+
# unless prior written permission is obtained from Ubiq Security, Inc.
|
10
|
+
#
|
11
|
+
# Your use of the software is expressly conditioned upon the terms
|
12
|
+
# and conditions available at:
|
13
|
+
#
|
14
|
+
# https://ubiqsecurity.com/legal
|
15
|
+
#
|
16
|
+
require 'rb-readline'
|
17
|
+
require 'byebug'
|
18
|
+
require 'httparty'
|
19
|
+
require "active_support/all"
|
20
|
+
require_relative './auth.rb'
|
21
|
+
require_relative './algo.rb'
|
22
|
+
require 'webrick'
|
23
|
+
|
24
|
+
module Ubiq
|
25
|
+
|
26
|
+
# Ubiq Encryption object
|
27
|
+
# This object represents a single data encryption key and can be used to encrypt several separate plain texts using the same key
|
28
|
+
class Encryption
|
29
|
+
def initialize(creds, uses)
|
30
|
+
|
31
|
+
raise RuntimeError, 'Some of your credentials are missing, please check!' if !validate_creds(creds)
|
32
|
+
|
33
|
+
# Set host, either the default or the one given by caller
|
34
|
+
@host = creds.host.blank? ? UBIQ_HOST : creds.host
|
35
|
+
|
36
|
+
# Set the credentials in instance varibales to be used among methods
|
37
|
+
# The client's public API key (used to identify the client to the server
|
38
|
+
@papi = creds.access_key_id
|
39
|
+
|
40
|
+
# The client's secret API key (used to authenticate HTTP requests)
|
41
|
+
@sapi = creds.secret_signing_key
|
42
|
+
|
43
|
+
# The client's secret RSA encryption key/password (used to decrypt the client's RSA key from the server). This key is not retained by this object.
|
44
|
+
@srsa = creds.secret_crypto_access_key
|
45
|
+
|
46
|
+
# Build the endpoint URL
|
47
|
+
url = endpoint_base + '/encryption/key'
|
48
|
+
|
49
|
+
# Build the Request Body with the number of uses of key
|
50
|
+
query = {uses: uses}
|
51
|
+
|
52
|
+
# Retrieve the necessary headers to make the request using Auth Object
|
53
|
+
headers = Auth.build_headers(@papi, @sapi, endpoint, query, @host,'post')
|
54
|
+
|
55
|
+
@encryption_started = false
|
56
|
+
@encryption_ready = true
|
57
|
+
|
58
|
+
# Request a new encryption key from the server. if the request
|
59
|
+
# fails, the function raises a HTTPError indicating
|
60
|
+
# the status code returned by the server. this exception is
|
61
|
+
# propagated back to the caller
|
62
|
+
|
63
|
+
begin
|
64
|
+
response = HTTParty.post(
|
65
|
+
url,
|
66
|
+
body: query.to_json,
|
67
|
+
headers: headers
|
68
|
+
)
|
69
|
+
rescue HTTParty::Error
|
70
|
+
raise RuntimeError, 'Cant reach server'
|
71
|
+
end
|
72
|
+
|
73
|
+
# Response status is 201 Created
|
74
|
+
if response.code == WEBrick::HTTPStatus::RC_CREATED
|
75
|
+
# The code below largely assumes that the server returns
|
76
|
+
# a json object that contains the members and is formatted
|
77
|
+
# according to the Ubiq REST specification.
|
78
|
+
|
79
|
+
# Build the key object
|
80
|
+
@key = {}
|
81
|
+
@key['id'] = response['key_fingerprint']
|
82
|
+
@key['session'] = response['encryption_session']
|
83
|
+
@key['security_model'] = response['security_model']
|
84
|
+
@key['algorithm'] = response['security_model']['algorithm'].downcase
|
85
|
+
@key['max_uses'] = response['max_uses']
|
86
|
+
@key['uses'] = 0
|
87
|
+
@key['encrypted'] = Base64.strict_decode64(response['encrypted_data_key'])
|
88
|
+
|
89
|
+
# Get encrypted private key from response body
|
90
|
+
encrypted_private_key = response['encrypted_private_key']
|
91
|
+
# Get wrapped data key from response body
|
92
|
+
wrapped_data_key = response['wrapped_data_key']
|
93
|
+
# Decrypt the encryped private key using @srsa supplied
|
94
|
+
private_key = OpenSSL::PKey::RSA.new(encrypted_private_key,@srsa)
|
95
|
+
# Decode WDK from base64 format
|
96
|
+
wdk = Base64.strict_decode64(wrapped_data_key)
|
97
|
+
# Use private key to decrypt the wrapped data key
|
98
|
+
dk = private_key.private_decrypt(wdk,OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING)
|
99
|
+
@key['raw'] = dk
|
100
|
+
# Build the algorithm object
|
101
|
+
@algo = Algo.new.get_algo(@key['algorithm'])
|
102
|
+
else
|
103
|
+
# Raise the error if response is not 201
|
104
|
+
raise RuntimeError, "HTTPError Response: Expected 201, got #{response.code}"
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
def begin
|
110
|
+
# Begin the encryption process
|
111
|
+
|
112
|
+
# When this function is called, the encryption object increments
|
113
|
+
# the number of uses of the key and creates a new internal context
|
114
|
+
# to be used to encrypt the data.
|
115
|
+
# If the encryption object is not yet ready to be used, throw an error
|
116
|
+
raise RuntimeError, 'Encryption not ready' if !@encryption_ready
|
117
|
+
|
118
|
+
# if Encryption cipher context already exists
|
119
|
+
raise RuntimeError, 'Encryption already in progress' if @encryption_started
|
120
|
+
# If max uses > uses
|
121
|
+
raise RuntimeError, 'Maximum key uses exceeded' if @key['uses'] >= @key['max_uses']
|
122
|
+
@key['uses'] += 1
|
123
|
+
# create a new Encryption context and initialization vector
|
124
|
+
@enc , @iv = Algo.new.encryptor(@algo, @key['raw'])
|
125
|
+
|
126
|
+
# Pack the result into bytes to get a byte string
|
127
|
+
struct = [0, 0, @algo[:id], @iv.length, @key['encrypted'].length].pack('CCCCn')
|
128
|
+
@encryption_started = true
|
129
|
+
return struct + @iv + @key['encrypted']
|
130
|
+
end
|
131
|
+
|
132
|
+
def update(data)
|
133
|
+
raise RuntimeError, 'Encryption is not Started' if !@encryption_started
|
134
|
+
# Encryption of some plain text is perfomed here
|
135
|
+
# Any cipher text produced by the operation is returned
|
136
|
+
@enc.update(data)
|
137
|
+
end
|
138
|
+
|
139
|
+
def end
|
140
|
+
raise RuntimeError, 'Encryption is not Started' if !@encryption_started
|
141
|
+
# This function finalizes the encryption (producing the final
|
142
|
+
# cipher text for the encryption, if necessary) and adds any
|
143
|
+
# authentication information (if required by the algorithm).
|
144
|
+
# Any data produced is returned by the function.
|
145
|
+
|
146
|
+
# Finalize an encryption
|
147
|
+
res = @enc.final
|
148
|
+
if @algo[:tag_length] != 0
|
149
|
+
# Add the tag to the cipher text
|
150
|
+
res+= @enc.auth_tag
|
151
|
+
end
|
152
|
+
@encryption_started = false
|
153
|
+
# Return the encrypted result
|
154
|
+
return res
|
155
|
+
end
|
156
|
+
|
157
|
+
def close
|
158
|
+
raise RuntimeError, 'Encryption currently running' if @encryption_started
|
159
|
+
# If the key was used less times than was requested, send an update to the server
|
160
|
+
if @key['uses'] < @key['max_uses']
|
161
|
+
query_url = "#{endpoint}/#{@key['id']}/#{@key['session']}"
|
162
|
+
url = "#{endpoint_base}/encryption/key/#{@key['id']}/#{@key['session']}"
|
163
|
+
query = {actual: @key['uses'], requested: @key['max_uses']}
|
164
|
+
headers = Auth.build_headers(@papi, @sapi, query_url, query, @host, 'patch')
|
165
|
+
response = HTTParty.patch(
|
166
|
+
url,
|
167
|
+
body: query.to_json,
|
168
|
+
headers: headers
|
169
|
+
)
|
170
|
+
remove_instance_variable(:@key)
|
171
|
+
@encryption_ready = false;
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def endpoint_base
|
176
|
+
@host + '/api/v0'
|
177
|
+
end
|
178
|
+
|
179
|
+
def endpoint
|
180
|
+
'/api/v0/encryption/key'
|
181
|
+
end
|
182
|
+
|
183
|
+
def validate_creds(credentials)
|
184
|
+
# This method checks for the presence of the credentials
|
185
|
+
!credentials.access_key_id.blank? and !credentials.secret_signing_key.blank? and !credentials.secret_crypto_access_key.blank?
|
186
|
+
end
|
187
|
+
|
188
|
+
end
|
189
|
+
|
190
|
+
def validate_creds(credentials)
|
191
|
+
# This method checks for the presence of the credentials
|
192
|
+
!credentials.access_key_id.blank? and !credentials.secret_signing_key.blank? and !credentials.secret_crypto_access_key.blank?
|
193
|
+
end
|
194
|
+
|
195
|
+
def encrypt(creds, data)
|
196
|
+
begin
|
197
|
+
enc = Encryption.new(creds, 1)
|
198
|
+
res = enc.begin() + enc.update(data) + enc.end()
|
199
|
+
enc.close()
|
200
|
+
rescue
|
201
|
+
enc.close() if enc
|
202
|
+
raise
|
203
|
+
end
|
204
|
+
return res
|
205
|
+
end
|
206
|
+
|
207
|
+
end
|
data/lib/ubiq/host.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# Copyright 2020 Ubiq Security, Inc., Proprietary and All Rights Reserved.
|
2
|
+
#
|
3
|
+
# NOTICE: All information contained herein is, and remains the property
|
4
|
+
# of Ubiq Security, Inc. The intellectual and technical concepts contained
|
5
|
+
# herein are proprietary to Ubiq Security, Inc. and its suppliers and may be
|
6
|
+
# covered by U.S. and Foreign Patents, patents in process, and are
|
7
|
+
# protected by trade secret or copyright law. Dissemination of this
|
8
|
+
# information or reproduction of this material is strictly forbidden
|
9
|
+
# unless prior written permission is obtained from Ubiq Security, Inc.
|
10
|
+
#
|
11
|
+
# Your use of the software is expressly conditioned upon the terms
|
12
|
+
# and conditions available at:
|
13
|
+
#
|
14
|
+
# https://ubiqsecurity.com/legal
|
15
|
+
#
|
16
|
+
module Ubiq
|
17
|
+
UBIQ_HOST = 'api.ubiqsecurity.com:8811'
|
18
|
+
end
|
data/lib/ubiq/version.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# Copyright 2020 Ubiq Security, Inc., Proprietary and All Rights Reserved.
|
2
|
+
#
|
3
|
+
# NOTICE: All information contained herein is, and remains the property
|
4
|
+
# of Ubiq Security, Inc. The intellectual and technical concepts contained
|
5
|
+
# herein are proprietary to Ubiq Security, Inc. and its suppliers and may be
|
6
|
+
# covered by U.S. and Foreign Patents, patents in process, and are
|
7
|
+
# protected by trade secret or copyright law. Dissemination of this
|
8
|
+
# information or reproduction of this material is strictly forbidden
|
9
|
+
# unless prior written permission is obtained from Ubiq Security, Inc.
|
10
|
+
#
|
11
|
+
# Your use of the software is expressly conditioned upon the terms
|
12
|
+
# and conditions available at:
|
13
|
+
#
|
14
|
+
# https://ubiqsecurity.com/legal
|
15
|
+
#
|
16
|
+
|
17
|
+
# frozen_string_literal: true
|
18
|
+
|
19
|
+
module Ubiq
|
20
|
+
VERSION = "1.0.0"
|
21
|
+
end
|
22
|
+
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require_relative 'lib/ubiq/version'
|
2
|
+
|
3
|
+
Gem::Specification.new do |spec|
|
4
|
+
spec.name = "ubiq-security"
|
5
|
+
spec.version = Ubiq::VERSION
|
6
|
+
spec.authors = ["Ubiq Security, Inc."]
|
7
|
+
spec.email = ["support@ubiqsecurity.com"]
|
8
|
+
|
9
|
+
spec.summary = %q{Ruby Client Library for accessing the Ubiq Platform}
|
10
|
+
spec.description = "Provide data encryption to any application with a couple of API calls. " \
|
11
|
+
"See https://www.ubiqsecurity.com for details."
|
12
|
+
spec.homepage = "https://dev.ubiqsecurity.com/docs/ruby-library"
|
13
|
+
spec.license = "Nonstandard"
|
14
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
|
15
|
+
|
16
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
17
|
+
spec.metadata["source_code_uri"] = "https://gitlab.com/ubiqsecurity/ubiq-ruby"
|
18
|
+
spec.metadata["changelog_uri"] = "https://gitlab.com/ubiqsecurity/ubiq-ruby/-/blob/master/README.md"
|
19
|
+
|
20
|
+
# Specify which files should be added to the gem when it is released.
|
21
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
22
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
23
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features|example)/}) }
|
24
|
+
end
|
25
|
+
spec.bindir = "exe"
|
26
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
27
|
+
spec.require_paths = ["lib"]
|
28
|
+
spec.add_runtime_dependency 'rb-readline', '~> 0.2', '>= 0.2'
|
29
|
+
spec.add_runtime_dependency 'httparty', '~> 0.15', '>= 0.15'
|
30
|
+
end
|
metadata
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ubiq-security
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ubiq Security, Inc.
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-08-20 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rb-readline
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.2'
|
20
|
+
- - "~>"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '0.2'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0.2'
|
30
|
+
- - "~>"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0.2'
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: httparty
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0.15'
|
40
|
+
- - "~>"
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '0.15'
|
43
|
+
type: :runtime
|
44
|
+
prerelease: false
|
45
|
+
version_requirements: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '0.15'
|
50
|
+
- - "~>"
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '0.15'
|
53
|
+
description: Provide data encryption to any application with a couple of API calls. See
|
54
|
+
https://www.ubiqsecurity.com for details.
|
55
|
+
email:
|
56
|
+
- support@ubiqsecurity.com
|
57
|
+
executables: []
|
58
|
+
extensions: []
|
59
|
+
extra_rdoc_files: []
|
60
|
+
files:
|
61
|
+
- CODE_OF_CONDUCT.md
|
62
|
+
- Gemfile
|
63
|
+
- LICENSE.txt
|
64
|
+
- README.md
|
65
|
+
- Rakefile
|
66
|
+
- lib/ubiq-security.rb
|
67
|
+
- lib/ubiq/algo.rb
|
68
|
+
- lib/ubiq/auth.rb
|
69
|
+
- lib/ubiq/credentials.rb
|
70
|
+
- lib/ubiq/decrypt.rb
|
71
|
+
- lib/ubiq/encrypt.rb
|
72
|
+
- lib/ubiq/host.rb
|
73
|
+
- lib/ubiq/version.rb
|
74
|
+
- ubiq-security.gemspec
|
75
|
+
homepage: https://dev.ubiqsecurity.com/docs/ruby-library
|
76
|
+
licenses:
|
77
|
+
- Nonstandard
|
78
|
+
metadata:
|
79
|
+
homepage_uri: https://dev.ubiqsecurity.com/docs/ruby-library
|
80
|
+
source_code_uri: https://gitlab.com/ubiqsecurity/ubiq-ruby
|
81
|
+
changelog_uri: https://gitlab.com/ubiqsecurity/ubiq-ruby/-/blob/master/README.md
|
82
|
+
post_install_message:
|
83
|
+
rdoc_options: []
|
84
|
+
require_paths:
|
85
|
+
- lib
|
86
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: 2.3.0
|
91
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0'
|
96
|
+
requirements: []
|
97
|
+
rubygems_version: 3.0.3
|
98
|
+
signing_key:
|
99
|
+
specification_version: 4
|
100
|
+
summary: Ruby Client Library for accessing the Ubiq Platform
|
101
|
+
test_files: []
|