evervault 2.0.0 → 3.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|