evervault 2.0.0 → 3.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/.changeset/config.json +15 -0
- data/.github/dependabot.yml +11 -0
- data/.github/workflows/e2e.yml +25 -0
- data/.github/workflows/linters.yml +23 -0
- data/.github/workflows/release.yml +51 -32
- data/.github/workflows/run-tests.yml +5 -5
- data/.gitignore +7 -1
- data/.rubocop.yml +3 -0
- data/CHANGELOG.md +43 -0
- data/Gemfile +11 -8
- data/LICENSE.txt +1 -1
- data/README.md +1 -152
- data/Rakefile +21 -4
- data/bin/console +7 -4
- data/evervault.gemspec +18 -12
- data/lib/evervault/client.rb +66 -49
- data/lib/evervault/config.rb +17 -0
- data/lib/evervault/crypto/client.rb +151 -81
- data/lib/evervault/crypto/curves/base.rb +20 -10
- data/lib/evervault/crypto/curves/koblitz.rb +26 -0
- data/lib/evervault/crypto/curves/p256.rb +12 -9
- data/lib/evervault/errors/error_map.rb +24 -36
- data/lib/evervault/errors/errors.rb +15 -22
- data/lib/evervault/errors/legacy_error_map.rb +52 -0
- data/lib/evervault/http/relay_outbound_config.rb +25 -23
- data/lib/evervault/http/request.rb +26 -31
- data/lib/evervault/http/request_handler.rb +22 -25
- data/lib/evervault/http/request_intercept.rb +39 -42
- data/lib/evervault/threading/repeated_timer.rb +12 -10
- data/lib/evervault/utils/validation_utils.rb +17 -18
- data/lib/evervault/version.rb +4 -2
- data/lib/evervault.rb +35 -19
- data/package.json +11 -0
- metadata +66 -9
data/lib/evervault/client.rb
CHANGED
@@ -1,74 +1,91 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require_relative
|
4
|
-
require_relative
|
5
|
-
require_relative
|
6
|
-
require_relative
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'http/request'
|
4
|
+
require_relative 'http/request_handler'
|
5
|
+
require_relative 'http/request_intercept'
|
6
|
+
require_relative 'http/relay_outbound_config'
|
7
|
+
require_relative 'threading/repeated_timer'
|
8
|
+
require_relative 'crypto/client'
|
7
9
|
|
8
10
|
module Evervault
|
9
11
|
class Client
|
12
|
+
attr_accessor :config
|
10
13
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
function_run_url: "https://run.evervault.com/",
|
17
|
-
relay_url: "https://relay.evervault.com:8443",
|
18
|
-
ca_host: "https://ca.evervault.com",
|
19
|
-
request_timeout: 30,
|
20
|
-
curve: 'prime256v1'
|
21
|
-
)
|
22
|
-
@function_run_url = function_run_url
|
23
|
-
@request = Evervault::Http::Request.new(timeout: request_timeout, app_uuid: app_uuid, api_key: api_key)
|
24
|
-
@intercept = Evervault::Http::RequestIntercept.new(
|
25
|
-
request: @request, ca_host: ca_host, api_key: api_key, base_url: base_url, relay_url: relay_url
|
26
|
-
)
|
27
|
-
@request_handler =
|
28
|
-
Evervault::Http::RequestHandler.new(
|
29
|
-
request: @request, base_url: base_url, cert: @intercept
|
30
|
-
)
|
31
|
-
@crypto_client = Evervault::Crypto::Client.new(request_handler: @request_handler, curve: curve)
|
32
|
-
@intercept.setup()
|
14
|
+
def initialize(app_uuid:, api_key:, **options, &block)
|
15
|
+
@config = Evervault::Config.new(app_id: app_uuid, api_key: api_key)
|
16
|
+
configure_via_options(**options) if options.any?
|
17
|
+
intercept.setup
|
18
|
+
configure(&block) if block_given?
|
33
19
|
end
|
34
20
|
|
35
|
-
def encrypt(data)
|
36
|
-
|
21
|
+
def encrypt(data, role = nil)
|
22
|
+
crypto_client.encrypt(data, role)
|
37
23
|
end
|
38
24
|
|
39
25
|
def decrypt(data)
|
40
26
|
unless data.is_a?(String) || data.is_a?(Array) || data.is_a?(Hash)
|
41
|
-
raise Evervault::Errors::
|
27
|
+
raise Evervault::Errors::EvervaultError, 'data is of invalid type'
|
42
28
|
end
|
29
|
+
|
43
30
|
payload = { data: data }
|
44
|
-
response =
|
45
|
-
response[
|
31
|
+
response = request_handler.post('decrypt', payload, true, Evervault::Errors::ErrorMap)
|
32
|
+
response['data']
|
46
33
|
end
|
47
34
|
|
48
|
-
def
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
@request_handler.post(function_name, payload, optional_headers, @function_run_url)
|
35
|
+
def create_token(action, data, expiry = nil)
|
36
|
+
payload = { payload: data, expiry: expiry, action: action }
|
37
|
+
request_handler.post('client-side-tokens', payload, true, Evervault::Errors::ErrorMap)
|
38
|
+
end
|
39
|
+
|
40
|
+
def run(function_name, payload)
|
41
|
+
payload = { payload: payload }
|
42
|
+
res = request_handler.post("functions/#{function_name}/runs", payload, true, Evervault::Errors::ErrorMap)
|
43
|
+
|
44
|
+
return res if res['status'] == 'success'
|
45
|
+
|
46
|
+
Evervault::Errors::ErrorMap.raise_function_error_on_failure(res)
|
61
47
|
end
|
62
48
|
|
63
49
|
def create_run_token(function_name, data = {})
|
64
|
-
|
50
|
+
request_handler.post("v2/functions/#{function_name}/run-token", data)
|
65
51
|
end
|
66
52
|
|
67
53
|
def enable_outbound_relay(decryption_domains = nil)
|
68
54
|
if decryption_domains.nil?
|
69
|
-
|
55
|
+
intercept.setup_outbound_relay_config
|
70
56
|
else
|
71
|
-
|
57
|
+
intercept.setup_decryption_domains(decryption_domains)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def configure
|
62
|
+
yield(config)
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def request
|
68
|
+
@request || Evervault::Http::Request.new(config: config)
|
69
|
+
end
|
70
|
+
|
71
|
+
def intercept
|
72
|
+
@intercept || Evervault::Http::RequestIntercept.new(request: request, config: config)
|
73
|
+
end
|
74
|
+
|
75
|
+
def request_handler
|
76
|
+
@request_handler || Evervault::Http::RequestHandler.new(request: request, config: config, cert: intercept)
|
77
|
+
end
|
78
|
+
|
79
|
+
def crypto_client
|
80
|
+
@crypto_client || Evervault::Crypto::Client.new(request_handler: request_handler, config: config)
|
81
|
+
end
|
82
|
+
|
83
|
+
def configure_via_options(**options)
|
84
|
+
warn '[DEPRECATION] configuration via Evervault::Client.new arguments is deprecated. Pass a block or use the'\
|
85
|
+
'`.configure` instead.'
|
86
|
+
|
87
|
+
options.each do |key, value|
|
88
|
+
config.send("#{key}=", value)
|
72
89
|
end
|
73
90
|
end
|
74
91
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Evervault
|
4
|
+
class Config
|
5
|
+
attr_accessor :app_id, :api_key, :base_url, :relay_url, :ca_host, :request_timeout, :curve
|
6
|
+
|
7
|
+
def initialize(app_id:, api_key:)
|
8
|
+
@app_id = app_id
|
9
|
+
@api_key = api_key
|
10
|
+
@base_url = 'https://api.evervault.com/'
|
11
|
+
@relay_url = 'https://relay.evervault.com:8443'
|
12
|
+
@ca_host = 'https://ca.evervault.com'
|
13
|
+
@request_timeout = 30
|
14
|
+
@curve = 'prime256v1'
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -1,126 +1,196 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require_relative
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../errors/errors'
|
4
|
+
require_relative 'curves/p256'
|
5
|
+
require_relative 'curves/koblitz'
|
6
|
+
require_relative '../version'
|
7
|
+
require 'openssl'
|
8
|
+
require 'base64'
|
9
|
+
require 'json'
|
10
|
+
require 'securerandom'
|
11
|
+
require 'time'
|
8
12
|
|
9
13
|
module Evervault
|
10
14
|
module Crypto
|
11
15
|
class Client
|
12
|
-
attr_reader :
|
13
|
-
|
14
|
-
|
15
|
-
@
|
16
|
-
@
|
17
|
-
|
18
|
-
|
19
|
-
response = request_handler.get(
|
20
|
-
key =
|
16
|
+
attr_reader :config
|
17
|
+
|
18
|
+
def initialize(request_handler:, config: ::Evervault::Config.new)
|
19
|
+
@config = config
|
20
|
+
@p256 = Evervault::Crypto::Curves::P256.new
|
21
|
+
@koblitz = Evervault::Crypto::Curves::Koblitz.new
|
22
|
+
@ev_version = EV_VERSION[config.curve]
|
23
|
+
response = request_handler.get('cages/key')
|
24
|
+
key = config.curve == 'secp256k1' ? 'ecdhKey' : 'ecdhP256Key'
|
21
25
|
@team_key = response[key]
|
22
26
|
end
|
23
27
|
|
24
|
-
def encrypt(data)
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
+
def encrypt(data, role = nil)
|
29
|
+
if data.nil? || (data.instance_of?(String) && data.empty?)
|
30
|
+
raise Evervault::Errors::EvervaultError, 'Data is required for encryption'
|
31
|
+
end
|
32
|
+
|
33
|
+
unless encryptable_data?(data) || data.instance_of?(Hash) || data.instance_of?(Array)
|
34
|
+
raise Evervault::Errors::EvervaultError, "Encryption is not supported for #{data.class}"
|
35
|
+
end
|
36
|
+
|
37
|
+
traverse_and_encrypt(data, role)
|
38
|
+
end
|
39
|
+
|
40
|
+
def generate_metadata(role)
|
41
|
+
buffer = []
|
42
|
+
|
43
|
+
# Binary representation of a fixed map with 2 or 3 items, followed by the key-value pairs
|
44
|
+
buffer.push(0x80 | (role.nil? ? 2 : 3))
|
45
|
+
|
46
|
+
if role
|
47
|
+
# Binary representation for a fixed string of length 2, followed by `dr` (for "data role")
|
48
|
+
buffer.push(0xA2)
|
49
|
+
buffer.push(*'dr'.bytes)
|
50
|
+
# Binary representation for a fixed string of role name length, followed by the role name itself
|
51
|
+
buffer.push(0xA0 | role.length)
|
52
|
+
buffer.push(*role.bytes)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Binary representation for a fixed string of length 2, followed by `eo` (for "encryption origin")
|
56
|
+
buffer.push(0xA2)
|
57
|
+
buffer.push(*'eo'.bytes)
|
58
|
+
# Binary representation for the integer 8 (Ruby SDK)
|
59
|
+
buffer.push(8)
|
60
|
+
|
61
|
+
# Binary representation for a fixed string of length 2, followed by `et` (for "encryption timestamp")
|
62
|
+
buffer.push(0xA2)
|
63
|
+
buffer.push(*'et'.bytes)
|
64
|
+
# Binary representation for a 4-byte unsigned integer (uint 32), followed by the epoch time (big-endian)
|
65
|
+
buffer.push(0xCE)
|
66
|
+
buffer.push(*[Time.now.to_i].pack('I!>').bytes)
|
67
|
+
|
68
|
+
buffer.pack('C*')
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def create_v2_aad(data_type, ephemeral_public_key_bytes, app_public_key_bytes)
|
74
|
+
data_type_number = case data_type
|
75
|
+
when 'number' then 1
|
76
|
+
when 'boolean' then 2
|
77
|
+
else 0 # Default to String
|
78
|
+
end
|
79
|
+
|
80
|
+
version_number = case @ev_version
|
81
|
+
when 'S0lS' then 0
|
82
|
+
when 'QkTC' then 1
|
83
|
+
end
|
84
|
+
|
85
|
+
raise 'This encryption version does not have a version number for AAD' if version_number.nil?
|
86
|
+
|
87
|
+
config_byte_size = 1
|
88
|
+
total_size = config_byte_size + ephemeral_public_key_bytes.bytesize + app_public_key_bytes.bytesize
|
89
|
+
aad = "\x00" * total_size # Create a binary string of zeros
|
90
|
+
|
91
|
+
# Set the configuration byte
|
92
|
+
b = 0x00 | (data_type_number << 4) | version_number
|
93
|
+
aad.setbyte(0, b)
|
94
|
+
|
95
|
+
# Copy ephemeral public key bytes into aad
|
96
|
+
aad[config_byte_size, ephemeral_public_key_bytes.bytesize] = ephemeral_public_key_bytes
|
97
|
+
|
98
|
+
# Copy application public key bytes into aad
|
99
|
+
start_index = config_byte_size + ephemeral_public_key_bytes.bytesize
|
100
|
+
aad[start_index, app_public_key_bytes.bytesize] = app_public_key_bytes
|
101
|
+
|
102
|
+
aad
|
103
|
+
end
|
104
|
+
|
105
|
+
def encryptable_data?(data)
|
106
|
+
data.instance_of?(String) || [true, false].include?(data) ||
|
107
|
+
data.instance_of?(Integer) || data.instance_of?(Float)
|
108
|
+
end
|
109
|
+
|
110
|
+
def generate_shared_key
|
111
|
+
ec = OpenSSL::PKey::EC.generate(@config.curve)
|
112
|
+
@ephemeral_public_key = ec.public_key
|
113
|
+
|
114
|
+
decoded_team_key = OpenSSL::BN.new(Base64.strict_decode64(@team_key), 2)
|
115
|
+
group_for_team_key = OpenSSL::PKey::EC::Group.new(config.curve)
|
116
|
+
team_key_point = OpenSSL::PKey::EC::Point.new(group_for_team_key, decoded_team_key)
|
117
|
+
|
118
|
+
shared_key = ec.dh_compute_key(team_key_point)
|
119
|
+
|
120
|
+
# Perform KDF
|
121
|
+
encoded_ephemeral_key = if config.curve == 'prime256v1'
|
122
|
+
@p256.encode(decompressed_key: @ephemeral_public_key
|
123
|
+
.to_bn(:uncompressed).to_s(16)).to_der
|
124
|
+
else
|
125
|
+
@koblitz.encode(decompressed_key: @ephemeral_public_key
|
126
|
+
.to_bn(:uncompressed).to_s(16)).to_der
|
127
|
+
end
|
128
|
+
hash_input = shared_key + [0o0, 0o0, 0o0, 0o1].pack('C*') + encoded_ephemeral_key
|
129
|
+
hash = OpenSSL::Digest.new('SHA256')
|
130
|
+
hash.digest(hash_input)
|
131
|
+
end
|
28
132
|
|
29
|
-
|
30
|
-
|
31
|
-
) if !(encryptable_data?(data) || data.instance_of?(Hash) || data.instance_of?(Array))
|
32
|
-
|
33
|
-
traverse_and_encrypt(data)
|
133
|
+
def base_64_remove_padding(str)
|
134
|
+
str.gsub(/={1,2}$/, '')
|
34
135
|
end
|
35
136
|
|
36
|
-
|
137
|
+
def encrypt_string(data_to_encrypt, role)
|
37
138
|
cipher = OpenSSL::Cipher.new('aes-256-gcm').encrypt
|
38
139
|
|
39
|
-
shared_key = generate_shared_key
|
140
|
+
shared_key = generate_shared_key
|
40
141
|
cipher.key = shared_key
|
41
142
|
|
42
143
|
iv = cipher.random_iv
|
43
144
|
cipher.iv = iv
|
44
145
|
|
45
|
-
|
46
|
-
|
47
|
-
|
146
|
+
header_type = header_type(data_to_encrypt)
|
147
|
+
cipher.auth_data = create_v2_aad(header_type, @ephemeral_public_key.to_octet_string(:compressed),
|
148
|
+
Base64.decode64(@team_key))
|
48
149
|
|
49
|
-
|
150
|
+
metadata = generate_metadata(role)
|
151
|
+
metadata_offset = [metadata.length].pack('v') # 'v' specifies 16-bit unsigned little-endian
|
152
|
+
payload = metadata_offset + metadata + data_to_encrypt.to_s
|
153
|
+
|
154
|
+
encrypted_data = cipher.update(payload.to_s) + cipher.final + cipher.auth_tag
|
50
155
|
|
51
156
|
ephemeral_key_compressed_string = @ephemeral_public_key.to_octet_string(:compressed)
|
52
157
|
|
53
|
-
format(header_type(data_to_encrypt), Base64.strict_encode64(iv),
|
158
|
+
format(header_type(data_to_encrypt), Base64.strict_encode64(iv),
|
159
|
+
Base64.strict_encode64(ephemeral_key_compressed_string), Base64.strict_encode64(encrypted_data))
|
54
160
|
end
|
55
161
|
|
56
|
-
|
162
|
+
def traverse_and_encrypt(data, role)
|
57
163
|
if encryptable_data?(data)
|
58
|
-
return encrypt_string(data)
|
164
|
+
return encrypt_string(data, role)
|
59
165
|
elsif data.instance_of?(Hash)
|
60
166
|
encrypted_data = {}
|
61
|
-
data.each { |key, value| encrypted_data[key] = traverse_and_encrypt(value) }
|
167
|
+
data.each { |key, value| encrypted_data[key] = traverse_and_encrypt(value, role) }
|
62
168
|
return encrypted_data
|
63
169
|
elsif data.instance_of?(Array)
|
64
|
-
encrypted_data = data.map { |value| traverse_and_encrypt(value) }
|
170
|
+
encrypted_data = data.map { |value| traverse_and_encrypt(value, role) }
|
65
171
|
return encrypted_data
|
66
172
|
else
|
67
|
-
raise Evervault::Errors::
|
68
|
-
"Encryption is not supported for #{data.class}"
|
69
|
-
)
|
173
|
+
raise Evervault::Errors::EvervaultError, "Encryption is not supported for #{data.class}"
|
70
174
|
end
|
71
|
-
data
|
72
|
-
end
|
73
175
|
|
74
|
-
|
75
|
-
data.instance_of?(String) || [true, false].include?(data) ||
|
76
|
-
data.instance_of?(Integer) || data.instance_of?(Float)
|
77
|
-
end
|
78
|
-
|
79
|
-
private def generate_shared_key()
|
80
|
-
ec = OpenSSL::PKey::EC.generate(@curve)
|
81
|
-
@ephemeral_public_key = ec.public_key
|
82
|
-
|
83
|
-
decoded_team_key = OpenSSL::BN.new(Base64.strict_decode64(@team_key), 2)
|
84
|
-
group_for_team_key = OpenSSL::PKey::EC::Group.new(@curve)
|
85
|
-
team_key_point = OpenSSL::PKey::EC::Point.new(group_for_team_key, decoded_team_key)
|
86
|
-
|
87
|
-
shared_key = ec.dh_compute_key(team_key_point)
|
88
|
-
|
89
|
-
if @curve == 'prime256v1'
|
90
|
-
# Perform KDF
|
91
|
-
encoded_ephemeral_key = @p256.encode(decompressed_key: @ephemeral_public_key.to_bn(:uncompressed).to_s(16)).to_der
|
92
|
-
hash_input = shared_key + [00, 00, 00, 01].pack('C*') + encoded_ephemeral_key
|
93
|
-
hash = OpenSSL::Digest::SHA256.new()
|
94
|
-
digest = hash.digest(hash_input)
|
95
|
-
return digest
|
96
|
-
end
|
97
|
-
|
98
|
-
shared_key
|
176
|
+
data
|
99
177
|
end
|
100
178
|
|
101
|
-
|
179
|
+
def format(datatype, iv, team_key, encrypted_data)
|
102
180
|
"ev:#{@ev_version}#{
|
103
|
-
datatype != 'string' ?
|
181
|
+
datatype != 'string' ? ":#{datatype}" : ''
|
104
182
|
}:#{base_64_remove_padding(iv)}:#{base_64_remove_padding(
|
105
183
|
team_key
|
106
184
|
)}:#{base_64_remove_padding(encrypted_data)}:$"
|
107
185
|
end
|
108
186
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
private def header_type(data)
|
114
|
-
if data.instance_of?(Array)
|
115
|
-
return "Array"
|
116
|
-
elsif [true, false].include?(data)
|
117
|
-
return "boolean"
|
118
|
-
elsif data.instance_of?(Hash)
|
119
|
-
return "object"
|
187
|
+
def header_type(data)
|
188
|
+
if [true, false].include?(data)
|
189
|
+
'boolean'
|
120
190
|
elsif data.instance_of?(Float) || data.instance_of?(Integer)
|
121
|
-
|
191
|
+
'number'
|
122
192
|
elsif data.instance_of?(String)
|
123
|
-
|
193
|
+
'string'
|
124
194
|
end
|
125
195
|
end
|
126
196
|
end
|
@@ -1,9 +1,10 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'openssl'
|
2
4
|
|
3
5
|
module Evervault
|
4
6
|
module Crypto
|
5
7
|
class CurveBase
|
6
|
-
|
7
8
|
def initialize(curve_name:, curve_values:)
|
8
9
|
@asn1Encoder = buildEncoder(curve_values: curve_values)
|
9
10
|
end
|
@@ -12,13 +13,19 @@ module Evervault
|
|
12
13
|
@asn1Encoder.call(decompressed_key)
|
13
14
|
end
|
14
15
|
|
15
|
-
private
|
16
|
+
private
|
17
|
+
|
18
|
+
def buildEncoder(curve_values:)
|
16
19
|
a = OpenSSL::ASN1::OctetString.new([curve_values::A].pack('H*'))
|
17
20
|
b = OpenSSL::ASN1::OctetString.new([curve_values::B].pack('H*'))
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
21
|
+
if !curve_values::SEED.nil?
|
22
|
+
seed = OpenSSL::ASN1::BitString.new([curve_values::SEED].pack('H*'))
|
23
|
+
curve = OpenSSL::ASN1::Sequence.new([a, b, seed])
|
24
|
+
else
|
25
|
+
curve = OpenSSL::ASN1::Sequence.new([a, b])
|
26
|
+
end
|
27
|
+
|
28
|
+
field_type = OpenSSL::ASN1::ObjectId.new('1.2.840.10045.1.1')
|
22
29
|
parameters = OpenSSL::ASN1::Integer.new(curve_values::P.to_i(16))
|
23
30
|
field_id = OpenSSL::ASN1::Sequence.new([field_type, parameters])
|
24
31
|
|
@@ -28,11 +35,14 @@ module Evervault
|
|
28
35
|
cofactor = OpenSSL::ASN1::Integer.new(curve_values::H.to_i(16))
|
29
36
|
ec_parameters = OpenSSL::ASN1::Sequence.new([version, field_id, curve, base, order, cofactor])
|
30
37
|
|
31
|
-
algorithm = OpenSSL::ASN1::ObjectId.new(
|
38
|
+
algorithm = OpenSSL::ASN1::ObjectId.new('1.2.840.10045.2.1')
|
32
39
|
algorithm_identifier = OpenSSL::ASN1::Sequence.new([algorithm, ec_parameters])
|
33
40
|
|
34
|
-
|
41
|
+
lambda { |public_key|
|
42
|
+
OpenSSL::ASN1::Sequence.new([algorithm_identifier,
|
43
|
+
OpenSSL::ASN1::BitString.new([public_key].pack('H*'))])
|
44
|
+
}
|
35
45
|
end
|
36
46
|
end
|
37
47
|
end
|
38
|
-
end
|
48
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'base'
|
4
|
+
|
5
|
+
module KOBLITZ_CONSTANTS
|
6
|
+
A = '0000000000000000000000000000000000000000000000000000000000000000'
|
7
|
+
B = '0000000000000000000000000000000000000000000000000000000000000007'
|
8
|
+
SEED = nil
|
9
|
+
P = 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F'
|
10
|
+
GENERATOR = '0479BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798483ADA7726A3C4655DA4FBFC0E1108A8FD17B'\
|
11
|
+
'448A68554199C47D08FFB10D4B8'
|
12
|
+
N = 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141'
|
13
|
+
H = '01'
|
14
|
+
end
|
15
|
+
|
16
|
+
module Evervault
|
17
|
+
module Crypto
|
18
|
+
module Curves
|
19
|
+
class Koblitz < CurveBase
|
20
|
+
def initialize
|
21
|
+
super(curve_name: 'secp256k1', curve_values: KOBLITZ_CONSTANTS)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -1,22 +1,25 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'base'
|
2
4
|
|
3
5
|
# https://neuromancer.sk/std/x962/prime256v1
|
4
6
|
|
5
7
|
module P256_CONSTANTS
|
6
|
-
A =
|
7
|
-
B =
|
8
|
-
SEED =
|
9
|
-
P =
|
10
|
-
GENERATOR =
|
11
|
-
|
12
|
-
|
8
|
+
A = 'FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC'
|
9
|
+
B = '5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B'
|
10
|
+
SEED = 'C49D360886E704936A6678E1139D26B7819F7E90'
|
11
|
+
P = 'FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF'
|
12
|
+
GENERATOR = '046B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C2964FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE3'\
|
13
|
+
'3576B315ECECBB6406837BF51F5'
|
14
|
+
N = 'FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551'
|
15
|
+
H = '01'
|
13
16
|
end
|
14
17
|
|
15
18
|
module Evervault
|
16
19
|
module Crypto
|
17
20
|
module Curves
|
18
21
|
class P256 < CurveBase
|
19
|
-
def initialize
|
22
|
+
def initialize
|
20
23
|
super(curve_name: 'prime256v1', curve_values: P256_CONSTANTS)
|
21
24
|
end
|
22
25
|
end
|
@@ -1,47 +1,35 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'errors'
|
2
4
|
|
3
5
|
module Evervault
|
4
6
|
module Errors
|
5
7
|
class ErrorMap
|
6
|
-
def self.raise_errors_on_failure(
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
else
|
19
|
-
raise AuthenticationError.new("Forbidden")
|
20
|
-
end
|
21
|
-
when 500
|
22
|
-
raise ServerError.new("Server Error")
|
23
|
-
when 502
|
24
|
-
raise BadGatewayError.new("Bad Gateway Error")
|
25
|
-
when 503
|
26
|
-
raise ServiceUnavailableError.new("Service Unavailable")
|
8
|
+
def self.raise_errors_on_failure(_status_code, body, _headers)
|
9
|
+
parsed_body = JSON.parse(body)
|
10
|
+
code = parsed_body['code']
|
11
|
+
detail = parsed_body['detail']
|
12
|
+
|
13
|
+
case code
|
14
|
+
when 'functions/request-timeout'
|
15
|
+
raise FunctionTimeoutError, detail
|
16
|
+
when 'functions/function-not-ready'
|
17
|
+
raise FunctionNotReadyError, detail
|
18
|
+
when 'functions/forbidden-ip'
|
19
|
+
raise ForbiddenIPError, detail
|
27
20
|
else
|
28
|
-
raise
|
29
|
-
self.message_for_unexpected_error_without_type(body)
|
30
|
-
)
|
21
|
+
raise EvervaultError, detail
|
31
22
|
end
|
32
23
|
end
|
33
24
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
"An unexpected error occured. It occurred with the message: #{
|
43
|
-
message
|
44
|
-
} and http_code: '#{status_code}'. Please contact Evervault support"
|
25
|
+
def self.raise_function_error_on_failure(body)
|
26
|
+
error = body['error']
|
27
|
+
raise EvervaultError, 'An unexpected error occurred. Please contact Evervault support' unless error
|
28
|
+
|
29
|
+
message = error['message']
|
30
|
+
stack = error['stack']
|
31
|
+
id = body['id']
|
32
|
+
raise FunctionRuntimeError.new(message, stack, id)
|
45
33
|
end
|
46
34
|
end
|
47
35
|
end
|