revault_api 0.5.1-aarch64-linux → 0.6.1-aarch64-linux
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +140 -423
- data/lib/revault/binding_operations.rb +1255 -0
- data/lib/revault/domain_models.rb +808 -0
- data/lib/revault/native_library.rb +80 -0
- data/lib/revault/vault.rb +1600 -0
- data/native/linux-aarch64-gnu/librevault_api.so +0 -0
- data/native/linux-aarch64-gnu/librevault_ruby_shim.so +0 -0
- data/native/revault_ruby_shim.c +557 -0
- data/revault_api.rb +11 -0
- metadata +25 -3
|
@@ -0,0 +1,1600 @@
|
|
|
1
|
+
# The owned, class-oriented reVault API. See the
|
|
2
|
+
# {repository README}[https://github.com/onepub-dev/reVault#readme] for the
|
|
3
|
+
# security model and complete examples.
|
|
4
|
+
require_relative 'native_library'
|
|
5
|
+
|
|
6
|
+
# Encrypts lockbox content and manages local vault metadata.
|
|
7
|
+
module Revault
|
|
8
|
+
# Load an explicit, inherited, or installed native carrier.
|
|
9
|
+
def self.load(native_library_path = nil) = Runtime.new(native_library_path)
|
|
10
|
+
# Return a new runtime facade synchronously for factory operations.
|
|
11
|
+
def self.runtime = Runtime.new
|
|
12
|
+
|
|
13
|
+
# Select the process-wide native carrier and load binding operations.
|
|
14
|
+
def self.ensure_native(native_library_path = nil)
|
|
15
|
+
NativeLibrary.path(native_library_path)
|
|
16
|
+
unless const_defined?(:BindingOperations, false)
|
|
17
|
+
require_relative 'binding_operations'
|
|
18
|
+
NativeLibrary.bind_shim
|
|
19
|
+
private_constant(:BindingOperations)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Native operation failure with structured diagnostic details.
|
|
24
|
+
class RevaultError < StandardError
|
|
25
|
+
attr_reader :details
|
|
26
|
+
# Preserve the stable native message and structured details.
|
|
27
|
+
def initialize(message, details = nil)
|
|
28
|
+
super(message); @details = details
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Returns the owned handle.
|
|
33
|
+
class OwnedHandle
|
|
34
|
+
attr_reader :native_handle
|
|
35
|
+
# Returns the initialize.
|
|
36
|
+
def initialize(operations, native_handle)
|
|
37
|
+
@operations = operations
|
|
38
|
+
@native_handle = native_handle
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Primary API used to open lockboxes, manage keys and metadata, use the
|
|
43
|
+
# Session Agent, and access to the platform credential store.
|
|
44
|
+
class Runtime
|
|
45
|
+
# Open a session for an existing Vault file; retains credentials but no Vault lock.
|
|
46
|
+
def contact_exchange(path, vault_passphrase)
|
|
47
|
+
ContactExchange.new(@operations, @operations.contact_exchange_open(path, vault_passphrase))
|
|
48
|
+
end
|
|
49
|
+
attr_reader :agent, :platform
|
|
50
|
+
# Returns the initialize.
|
|
51
|
+
def initialize(native_library_path = nil)
|
|
52
|
+
Revault.ensure_native(native_library_path)
|
|
53
|
+
@operations = BindingOperations.new
|
|
54
|
+
@agent = Agent.new(@operations)
|
|
55
|
+
@platform = Platform.new(@operations)
|
|
56
|
+
end
|
|
57
|
+
# Returns the last error.
|
|
58
|
+
def last_error = @operations.last_error_message
|
|
59
|
+
# Returns the last error details.
|
|
60
|
+
def last_error_details = @operations.buffer_last_error_details()
|
|
61
|
+
|
|
62
|
+
# Returns the newest Lockbox archive format version supported by this engine.
|
|
63
|
+
def lockbox_format_version()
|
|
64
|
+
@operations.lockbox_format_version()
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Reads the format version from serialized Lockbox bytes without opening them.
|
|
68
|
+
def lockbox_probe_format_version(bytes)
|
|
69
|
+
@operations.lockbox_probe_format_version(bytes)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Creates an in memory Lockbox protected by a 32 byte content key.
|
|
73
|
+
def lockbox_create(key)
|
|
74
|
+
Lockbox.new(@operations, @operations.lockbox_create(key))
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Creates a lockbox with explicit cache capacity, workload, worker policy, and job count.
|
|
78
|
+
def lockbox_create_with_options(key, cache_mode, cache_bytes, workload, worker, jobs)
|
|
79
|
+
Lockbox.new(@operations, @operations.lockbox_create_with_options(key, cache_mode, cache_bytes, workload, worker, jobs))
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Creates an in memory Lockbox protected by the supplied password.
|
|
83
|
+
def lockbox_create_password(password)
|
|
84
|
+
Lockbox.new(@operations, @operations.lockbox_create_password(password))
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Creates a password protected Lockbox with its profile signing key.
|
|
88
|
+
def lockbox_create_password_with_signing_key(password, signing_key)
|
|
89
|
+
Lockbox.new(@operations, @operations.lockbox_create_password_with_signing_key(password, signing_key.native_handle))
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Creates an in memory Lockbox that the supplied contact can open.
|
|
93
|
+
def lockbox_create_contact(contact)
|
|
94
|
+
Lockbox.new(@operations, @operations.lockbox_create_contact(contact.native_handle))
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Creates a contact protected Lockbox with its profile signing key.
|
|
98
|
+
def lockbox_create_contact_with_signing_key(contact, signing_key)
|
|
99
|
+
Lockbox.new(@operations, @operations.lockbox_create_contact_with_signing_key(contact.native_handle, signing_key.native_handle))
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Creates an in memory Lockbox and assigns its profile signing key.
|
|
103
|
+
def lockbox_create_with_signing_key(content_key, signing_key)
|
|
104
|
+
Lockbox.new(@operations, @operations.lockbox_create_with_signing_key(content_key, signing_key.native_handle))
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Opens serialized Lockbox bytes with a 32 byte content key.
|
|
108
|
+
def lockbox_open(archive, key)
|
|
109
|
+
Lockbox.new(@operations, @operations.lockbox_open(archive, key))
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Opens a lockbox with explicit cache capacity, workload, worker policy, and job count.
|
|
113
|
+
def lockbox_open_with_options(archive, key, cache_mode, cache_bytes, workload, worker, jobs)
|
|
114
|
+
Lockbox.new(@operations, @operations.lockbox_open_with_options(archive, key, cache_mode, cache_bytes, workload, worker, jobs))
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Opens serialized Lockbox bytes with the supplied password.
|
|
118
|
+
def lockbox_open_password_with_budget(archive, password, max_derivations)
|
|
119
|
+
Lockbox.new(@operations, @operations.lockbox_open_password_with_budget(archive, password, max_derivations))
|
|
120
|
+
end
|
|
121
|
+
# Opens with the default per-credential password work budget.
|
|
122
|
+
def lockbox_open_password(archive, password)
|
|
123
|
+
Lockbox.new(@operations, @operations.lockbox_open_password(archive, password))
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# Opens serialized Lockbox bytes with the supplied contact private key.
|
|
127
|
+
def lockbox_open_contact(archive, contact)
|
|
128
|
+
Lockbox.new(@operations, @operations.lockbox_open_contact(archive, contact.native_handle))
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Reads public header, signature, and access slot metadata from a Lockbox file.
|
|
132
|
+
def lockbox_inspect_file(path)
|
|
133
|
+
@operations.lockbox_inspect_file(path)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Scans a damaged Lockbox file with its 32 byte content key.
|
|
137
|
+
def lockbox_recovery_scan_path(path, key)
|
|
138
|
+
@operations.lockbox_recovery_scan_path(path, key)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Scans damaged serialized Lockbox bytes with their 32 byte content key.
|
|
142
|
+
def lockbox_recovery_scan(bytes, key)
|
|
143
|
+
@operations.lockbox_recovery_scan(bytes, key)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Builds a new Lockbox from recoverable records without changing the source.
|
|
147
|
+
def lockbox_recovery_salvage(bytes, key, signing_key)
|
|
148
|
+
Lockbox.new(@operations, @operations.lockbox_recovery_salvage(bytes, key, signing_key.native_handle))
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Generates a contact encryption key pair using secure random data.
|
|
152
|
+
def key_contact_generate()
|
|
153
|
+
ContactKeyPair.new(@operations, @operations.key_contact_generate())
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Imports a contact key pair from its private binary record.
|
|
157
|
+
def key_contact_from_private(bytes)
|
|
158
|
+
ContactKeyPair.new(@operations, @operations.key_contact_from_private(bytes))
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# Imports a contact public key from its binary representation.
|
|
162
|
+
def key_contact_public_from_bytes(bytes)
|
|
163
|
+
ContactPublicKey.new(@operations, @operations.key_contact_public_from_bytes(bytes))
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# Generates a signing identity owned by a Vault Profile.
|
|
167
|
+
def generate_profile_signing_key_pair()
|
|
168
|
+
ProfileSigningKeyPair.new(@operations, @operations.key_signing_generate())
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# Imports a Vault Profile signing identity from its private record.
|
|
172
|
+
def profile_signing_key_pair_from_private(bytes)
|
|
173
|
+
ProfileSigningKeyPair.new(@operations, @operations.key_signing_from_private(bytes))
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# Imports the public half of a Vault Profile signing identity.
|
|
177
|
+
def profile_signing_public_key_from_bytes(bytes)
|
|
178
|
+
ProfileSigningPublicKey.new(@operations, @operations.key_signing_public_from_bytes(bytes))
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# Exports a private key in the requested key format.
|
|
182
|
+
def vault_key_export_private(key, format)
|
|
183
|
+
@operations.vault_key_export_private(key.native_handle, format)
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
# Exports a public key in the requested key format.
|
|
187
|
+
def vault_key_export_public(key, format)
|
|
188
|
+
@operations.vault_key_export_public(key.native_handle, format)
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# Imports a private contact key from a detected supported encoding.
|
|
192
|
+
def vault_key_import_private(bytes)
|
|
193
|
+
ContactKeyPair.new(@operations, @operations.vault_key_import_private(bytes))
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# Imports a public contact key from a detected supported encoding.
|
|
197
|
+
def vault_key_import_public(bytes)
|
|
198
|
+
ContactPublicKey.new(@operations, @operations.vault_key_import_public(bytes))
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
# Returns the stable fingerprint used to verify a public key.
|
|
202
|
+
def vault_key_fingerprint(key)
|
|
203
|
+
@operations.vault_key_fingerprint(key.native_handle)
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
# Encodes key bytes as hexadecimal text.
|
|
207
|
+
def vault_key_format_hex(bytes)
|
|
208
|
+
@operations.vault_key_format_hex(bytes)
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# Decodes hexadecimal key text and rejects malformed input.
|
|
212
|
+
def vault_key_decode_hex(text)
|
|
213
|
+
@operations.vault_key_decode_hex(text)
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
# Encodes key bytes using Crockford Base32.
|
|
217
|
+
def vault_key_format_crockford(bytes)
|
|
218
|
+
@operations.vault_key_format_crockford(bytes)
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
# Groups a Crockford code for easier reading and transcription.
|
|
222
|
+
def vault_key_format_crockford_reading(code)
|
|
223
|
+
@operations.vault_key_format_crockford_reading(code)
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
# Decodes Crockford Base32 key text and rejects malformed input.
|
|
227
|
+
def vault_key_decode_crockford(code)
|
|
228
|
+
@operations.vault_key_decode_crockford(code)
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
# Encodes arbitrary bytes as hexadecimal text.
|
|
232
|
+
def vault_key_hex_encode(bytes)
|
|
233
|
+
@operations.vault_key_hex_encode(bytes)
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
# Decodes arbitrary hexadecimal text and rejects malformed input.
|
|
237
|
+
def vault_key_hex_decode(text)
|
|
238
|
+
@operations.vault_key_hex_decode(text)
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
# Opens an existing Vault directory with its passphrase.
|
|
242
|
+
def vault_directory_open(root, password)
|
|
243
|
+
Vault.new(@operations, @operations.vault_directory_open(root, password))
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
# Returns the newest Vault structure version supported by this engine.
|
|
247
|
+
def vault_structure_version_current()
|
|
248
|
+
@operations.vault_structure_version_current()
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
# Reads an existing Vault structure version without changing it.
|
|
252
|
+
def vault_directory_probe_structure_version(root, password)
|
|
253
|
+
@operations.vault_directory_probe_structure_version(root, password)
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
# Opens or creates the default Vault without replacing existing state.
|
|
257
|
+
def vault_directory_open_or_create_default(password)
|
|
258
|
+
Vault.new(@operations, @operations.vault_directory_open_or_create_default(password))
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
# Replaces the default Vault and all persistent data it contains.
|
|
262
|
+
def vault_directory_replace_default(password)
|
|
263
|
+
Vault.new(@operations, @operations.vault_directory_replace_default(password))
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
# Changes the passphrase for an existing Vault.
|
|
267
|
+
def vault_directory_change_password(root, old_password, new_password)
|
|
268
|
+
@operations.vault_directory_change_password(root, old_password, new_password)
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
# Changes the passphrase for the default Vault.
|
|
272
|
+
def vault_directory_change_default_password(old_password, new_password)
|
|
273
|
+
@operations.vault_directory_change_default_password(old_password, new_password)
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
# Replaces the selected Vault and all persistent data it contains.
|
|
277
|
+
def vault_directory_replace(root, password)
|
|
278
|
+
Vault.new(@operations, @operations.vault_directory_replace(root, password))
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
# Opens the selected Vault, creating it only when absent.
|
|
282
|
+
def vault_directory_open_or_create(root, password)
|
|
283
|
+
Vault.new(@operations, @operations.vault_directory_open_or_create(root, password))
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
# Writes a backup of the default Vault to the selected path.
|
|
287
|
+
def vault_backup_default(path, overwrite)
|
|
288
|
+
@operations.vault_backup_default(path, overwrite)
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
# Restores the default Vault from the selected backup.
|
|
292
|
+
def vault_restore_default(path, overwrite)
|
|
293
|
+
@operations.vault_restore_default(path, overwrite)
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
# Opens an existing Vault metadata view that cannot load private keys.
|
|
297
|
+
def vault_read_only_open(root, password)
|
|
298
|
+
ReadOnlyVault.new(@operations, @operations.vault_read_only_open(root, password))
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
# Opens the default Vault metadata view without loading private keys.
|
|
302
|
+
def vault_read_only_open_default(password)
|
|
303
|
+
ReadOnlyVault.new(@operations, @operations.vault_read_only_open_default(password))
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
# Returns the platform default Vault directory.
|
|
307
|
+
def vault_default_directory()
|
|
308
|
+
@operations.vault_default_directory()
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
# Returns the path of the default Vault file.
|
|
312
|
+
def vault_default_path()
|
|
313
|
+
@operations.vault_default_path()
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
# Returns the session agent log path.
|
|
317
|
+
def vault_agent_log_path()
|
|
318
|
+
@operations.vault_agent_log_path()
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
# Returns the configured session agent log destination.
|
|
322
|
+
def vault_agent_log_destination()
|
|
323
|
+
@operations.vault_agent_log_destination()
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
# An open encrypted archive containing files, variables, secrets, and forms.
|
|
329
|
+
class Lockbox < OwnedHandle
|
|
330
|
+
# Creates an in-memory archive protected by exactly one credential.
|
|
331
|
+
def self.create_in_memory(password: nil, content_key: nil, contact: nil, signing_key: nil, options: nil)
|
|
332
|
+
credentials = [password, content_key, contact].compact
|
|
333
|
+
raise ArgumentError, 'supply exactly one of password, content_key, or contact' unless credentials.length == 1
|
|
334
|
+
runtime = Revault.runtime
|
|
335
|
+
box = if password
|
|
336
|
+
signing_key ? runtime.lockbox_create_password_with_signing_key(password.to_str, signing_key) : runtime.lockbox_create_password(password.to_str)
|
|
337
|
+
elsif contact
|
|
338
|
+
signing_key ? runtime.lockbox_create_contact_with_signing_key(contact, signing_key) : runtime.lockbox_create_contact(contact)
|
|
339
|
+
elsif options
|
|
340
|
+
runtime.lockbox_create_with_options(content_key, options[:cache_mode], options[:cache_bytes] || 0, options[:workload], options[:worker], options[:jobs] || 0)
|
|
341
|
+
else
|
|
342
|
+
runtime.lockbox_create(content_key)
|
|
343
|
+
end
|
|
344
|
+
box.set_owner_signing_key(signing_key) if signing_key && !password && !contact
|
|
345
|
+
box
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
# Opens serialized archive bytes without consulting the Session Agent.
|
|
349
|
+
def self.open_bytes(archive, password: nil, content_key: nil, contact: nil, options: nil)
|
|
350
|
+
credentials = [password, content_key, contact].compact
|
|
351
|
+
raise ArgumentError, 'supply exactly one of password, content_key, or contact' unless credentials.length == 1
|
|
352
|
+
runtime = Revault.runtime
|
|
353
|
+
return runtime.lockbox_open_password(archive, password.to_str) if password
|
|
354
|
+
return runtime.lockbox_open_contact(archive, contact) if contact
|
|
355
|
+
options ? runtime.lockbox_open_with_options(archive, content_key, options[:cache_mode], options[:cache_bytes] || 0, options[:workload], options[:worker], options[:jobs] || 0) : runtime.lockbox_open(archive, content_key)
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
# Creates an archive file and returns its process-local handle.
|
|
359
|
+
def self.create(path, overwrite: false, **options)
|
|
360
|
+
file_handle(path, overwrite ? 'replace' : 'create', **options)
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
# Opens with a shared lock; supply signing_key for exclusive write access.
|
|
364
|
+
# Close readers before opening a writer. Example:
|
|
365
|
+
# box = Lockbox.open(path, password: password); begin; puts box.get_file('/hello'); ensure; box.close; end
|
|
366
|
+
def self.open(path, writable: nil, **options)
|
|
367
|
+
file_handle(path, writable.nil? ? 'open' : (writable ? 'write' : 'read'), **options)
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
# Opens an existing plaintext archive with a shared read-only file lock.
|
|
371
|
+
# No credential or Session Agent is used. Close the handle to release the lock;
|
|
372
|
+
# encrypted archives raise an error. Example:
|
|
373
|
+
# box = Lockbox.open_unencrypted(path); begin; puts box.get_file('/index.html'); ensure; box.close; end
|
|
374
|
+
def self.open_unencrypted(path, options: nil)
|
|
375
|
+
Revault.ensure_native
|
|
376
|
+
operations = BindingOperations.new
|
|
377
|
+
tuning = options || {}
|
|
378
|
+
cache = tuning[:cache_mode] || 'bytes'
|
|
379
|
+
cache = 'auto' if cache == 'automatic'
|
|
380
|
+
new(operations, operations.lockbox_file(path, 'read', 'unencrypted', '', nil, nil,
|
|
381
|
+
cache, tuning[:cache_bytes] || (64 << 20), tuning[:workload] || 'interactive',
|
|
382
|
+
tuning[:worker] || 'auto', tuning[:jobs] || 0))
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
# Internal credential and option marshalling for native file operations.
|
|
386
|
+
def self.file_handle(path, mode, password: nil, content_key: nil, contact: nil, signing_key: nil, options: nil)
|
|
387
|
+
raise ArgumentError, 'supply exactly one credential' unless [password, content_key, contact].compact.length == 1
|
|
388
|
+
Revault.ensure_native
|
|
389
|
+
operations = BindingOperations.new
|
|
390
|
+
tuning = options || {}
|
|
391
|
+
cache = tuning[:cache_mode] || 'bytes'
|
|
392
|
+
cache = 'auto' if cache == 'automatic'
|
|
393
|
+
credential = password ? 'password' : contact ? 'contact' : 'content-key'
|
|
394
|
+
new(operations, operations.lockbox_file(path, mode, credential, (password || content_key || '').to_str, contact&.native_handle, signing_key&.native_handle, cache, tuning[:cache_bytes] || (64 << 20), tuning[:workload] || 'interactive', tuning[:worker] || 'auto', tuning[:jobs] || 0))
|
|
395
|
+
end
|
|
396
|
+
private_class_method :file_handle
|
|
397
|
+
|
|
398
|
+
# Stages a file at the Lockbox path; replace controls an existing entry.
|
|
399
|
+
def add_file(path, data, replace)
|
|
400
|
+
@operations.lockbox_add_file(@native_handle, path, data, replace)
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
# Stages a file and its portable Unix permission bits.
|
|
404
|
+
def add_file_with_permissions(path, data, permissions, replace)
|
|
405
|
+
@operations.lockbox_add_file_with_permissions(@native_handle, path, data, permissions, replace)
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
# Reads the complete file stored at the Lockbox path.
|
|
409
|
+
def get_file(path)
|
|
410
|
+
@operations.lockbox_get_file(@native_handle, path)
|
|
411
|
+
end
|
|
412
|
+
|
|
413
|
+
# Writes one Lockbox file to the host filesystem.
|
|
414
|
+
def extract_file_with_policy(source, destination, max_file_bytes, max_total_bytes, max_files, restore_permissions, overwrite)
|
|
415
|
+
@operations.lockbox_extract_file_with_policy(@native_handle, source, destination, max_file_bytes, max_total_bytes, max_files, restore_permissions, overwrite)
|
|
416
|
+
end
|
|
417
|
+
# Extracts one file using the legacy permission policy.
|
|
418
|
+
def extract_file(source, destination, replace)
|
|
419
|
+
@operations.lockbox_extract_file(@native_handle, source, destination, replace)
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
# Extracts the Lockbox with explicit size, count, link, and permission limits.
|
|
423
|
+
def extract_directory(destination, max_file_bytes, max_total_bytes, max_files, restore_symlinks, restore_permissions, overwrite)
|
|
424
|
+
@operations.lockbox_extract_directory(@native_handle, destination, max_file_bytes, max_total_bytes, max_files, restore_symlinks, restore_permissions, overwrite)
|
|
425
|
+
end
|
|
426
|
+
|
|
427
|
+
# Lists logical or physical content chunks for streaming diagnostics.
|
|
428
|
+
def stream_content(physical)
|
|
429
|
+
@operations.lockbox_stream_content(@native_handle, physical)
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
# Returns cache statistics for this lockbox.
|
|
433
|
+
def cache_stats()
|
|
434
|
+
@operations.lockbox_cache_stats(@native_handle)
|
|
435
|
+
end
|
|
436
|
+
|
|
437
|
+
# Returns import statistics for this lockbox.
|
|
438
|
+
def import_stats()
|
|
439
|
+
@operations.lockbox_import_stats(@native_handle)
|
|
440
|
+
end
|
|
441
|
+
|
|
442
|
+
# Updates import stats.
|
|
443
|
+
def reset_import_stats()
|
|
444
|
+
@operations.lockbox_reset_import_stats(@native_handle)
|
|
445
|
+
end
|
|
446
|
+
|
|
447
|
+
# Returns page metadata for diagnostics without exposing plaintext secrets.
|
|
448
|
+
def page_inspection()
|
|
449
|
+
@operations.lockbox_page_inspection(@native_handle)
|
|
450
|
+
end
|
|
451
|
+
|
|
452
|
+
# Scans the open archive and returns its structured recovery report.
|
|
453
|
+
def recovery_report()
|
|
454
|
+
@operations.lockbox_recovery_report(@native_handle)
|
|
455
|
+
end
|
|
456
|
+
|
|
457
|
+
# Renders the recovery report for a person, capped at maxEntries.
|
|
458
|
+
def recovery_report_render(verbose, max_entries)
|
|
459
|
+
@operations.lockbox_recovery_report_render(@native_handle, verbose, max_entries)
|
|
460
|
+
end
|
|
461
|
+
|
|
462
|
+
# Returns the current serialized archive size in bytes.
|
|
463
|
+
def storage_len()
|
|
464
|
+
@operations.lockbox_storage_len(@native_handle)
|
|
465
|
+
end
|
|
466
|
+
|
|
467
|
+
# Sets workload profile.
|
|
468
|
+
def set_workload_profile(profile)
|
|
469
|
+
@operations.lockbox_set_workload_profile(@native_handle, profile)
|
|
470
|
+
end
|
|
471
|
+
|
|
472
|
+
# Sets a soft cache limit (auto, disabled, or bytes).
|
|
473
|
+
def set_cache_limit(mode, bytes)
|
|
474
|
+
@operations.lockbox_set_cache_limit(@native_handle, mode, bytes)
|
|
475
|
+
end
|
|
476
|
+
|
|
477
|
+
# Sets worker policy.
|
|
478
|
+
def set_worker_policy(mode, jobs)
|
|
479
|
+
@operations.lockbox_set_worker_policy(@native_handle, mode, jobs)
|
|
480
|
+
end
|
|
481
|
+
|
|
482
|
+
# Returns the cache, workload, and worker settings used by this Lockbox.
|
|
483
|
+
def runtime_options()
|
|
484
|
+
@operations.lockbox_runtime_options(@native_handle)
|
|
485
|
+
end
|
|
486
|
+
|
|
487
|
+
# Authenticates and publishes the staged changes.
|
|
488
|
+
def commit()
|
|
489
|
+
@operations.lockbox_commit(@native_handle)
|
|
490
|
+
end
|
|
491
|
+
|
|
492
|
+
# Stages a directory entry and optionally creates missing parents.
|
|
493
|
+
def create_dir(path, create_parents)
|
|
494
|
+
@operations.lockbox_create_dir(@native_handle, path, create_parents)
|
|
495
|
+
end
|
|
496
|
+
|
|
497
|
+
# Stages removal of a file, link, or empty directory at path.
|
|
498
|
+
def delete(path)
|
|
499
|
+
@operations.lockbox_delete(@native_handle, path)
|
|
500
|
+
end
|
|
501
|
+
|
|
502
|
+
# Stages removal of a directory, optionally including its descendants.
|
|
503
|
+
def remove_dir(path, recursive)
|
|
504
|
+
@operations.lockbox_remove_dir(@native_handle, path, recursive)
|
|
505
|
+
end
|
|
506
|
+
|
|
507
|
+
# Stages every missing parent directory for path.
|
|
508
|
+
def create_parent_dirs(path)
|
|
509
|
+
@operations.lockbox_create_parent_dirs(@native_handle, path)
|
|
510
|
+
end
|
|
511
|
+
|
|
512
|
+
# Stages an atomic move from one Lockbox path to another.
|
|
513
|
+
def rename(from, to)
|
|
514
|
+
@operations.lockbox_rename(@native_handle, from, to)
|
|
515
|
+
end
|
|
516
|
+
|
|
517
|
+
# Lists entries below path, optionally including descendants.
|
|
518
|
+
def list(path, recursive)
|
|
519
|
+
@operations.lockbox_list(@native_handle, path, recursive)
|
|
520
|
+
end
|
|
521
|
+
|
|
522
|
+
# Lists entries using glob, type, recursion, and result limit filters.
|
|
523
|
+
def list_with_options(path, glob, recursive, include_files, include_symlinks, include_directories, limit)
|
|
524
|
+
@operations.lockbox_list_with_options(@native_handle, path, glob, recursive, include_files, include_symlinks, include_directories, limit)
|
|
525
|
+
end
|
|
526
|
+
|
|
527
|
+
# Returns metadata for the selected lockbox entry.
|
|
528
|
+
def stat(path)
|
|
529
|
+
@operations.lockbox_stat(@native_handle, path)
|
|
530
|
+
end
|
|
531
|
+
|
|
532
|
+
# Stages a plain text variable; commit to publish the change.
|
|
533
|
+
def set_variable(name, value)
|
|
534
|
+
@operations.lockbox_set_variable(@native_handle, name, value)
|
|
535
|
+
end
|
|
536
|
+
|
|
537
|
+
# Stores a secret variable from mutable bytes.
|
|
538
|
+
def set_secret_variable(name, value)
|
|
539
|
+
@operations.lockbox_set_secret_variable(@native_handle, name, value)
|
|
540
|
+
end
|
|
541
|
+
|
|
542
|
+
# Get/set/clear encrypted guidance; commit Lockbox writes. Fields belong to definitions.
|
|
543
|
+
def item_description(target, reference, operation: 'get', field: nil, text: nil)
|
|
544
|
+
require 'json'
|
|
545
|
+
@operations.lockbox_item_description(@native_handle, JSON.generate({ target: target, reference: reference, operation: operation, field: field, text: text }))
|
|
546
|
+
end
|
|
547
|
+
|
|
548
|
+
# Returns a plain variable when it is present.
|
|
549
|
+
def get_variable(name)
|
|
550
|
+
@operations.lockbox_get_variable(@native_handle, name)
|
|
551
|
+
end
|
|
552
|
+
|
|
553
|
+
# Returns the encrypted lockbox description, or nil when unset.
|
|
554
|
+
# Example: set it, commit, then `puts box.description`.
|
|
555
|
+
def description
|
|
556
|
+
get_variable('/.revault/description')
|
|
557
|
+
end
|
|
558
|
+
|
|
559
|
+
# Stages encrypted description text; call commit to publish it.
|
|
560
|
+
# Example: `box.set_description('Production credentials'); box.commit`.
|
|
561
|
+
def set_description(description)
|
|
562
|
+
set_variable('/.revault/description', description)
|
|
563
|
+
end
|
|
564
|
+
|
|
565
|
+
# Stages removal of the encrypted description; call commit.
|
|
566
|
+
# Example: `box.clear_description; box.commit`.
|
|
567
|
+
def clear_description
|
|
568
|
+
delete_variable('/.revault/description')
|
|
569
|
+
end
|
|
570
|
+
|
|
571
|
+
# Yields temporary secret bytes and wipes the native transfer afterwards.
|
|
572
|
+
def with_secret_variable(name, &callback)
|
|
573
|
+
@operations.lockbox_get_secret_variable(@native_handle, name, &callback)
|
|
574
|
+
end
|
|
575
|
+
|
|
576
|
+
# Stages removal of a variable.
|
|
577
|
+
def delete_variable(name)
|
|
578
|
+
@operations.lockbox_delete_variable(@native_handle, name)
|
|
579
|
+
end
|
|
580
|
+
|
|
581
|
+
# Atomically renames variables using source and destination path pairs.
|
|
582
|
+
def move_variables(moves)
|
|
583
|
+
@operations.lockbox_move_variables(@native_handle, Internal::DomainCodec.encode_path_moves(moves))
|
|
584
|
+
end
|
|
585
|
+
|
|
586
|
+
# Lists variable names and metadata without exposing secret values.
|
|
587
|
+
def list_variables()
|
|
588
|
+
@operations.lockbox_list_variables(@native_handle)
|
|
589
|
+
end
|
|
590
|
+
|
|
591
|
+
# Returns whether a variable is plain or secret.
|
|
592
|
+
def variable_sensitivity(name)
|
|
593
|
+
@operations.lockbox_variable_sensitivity(@native_handle, name)
|
|
594
|
+
end
|
|
595
|
+
|
|
596
|
+
# Stages a symbolic link with its stored target text.
|
|
597
|
+
def add_symlink(path, target, replace)
|
|
598
|
+
@operations.lockbox_add_symlink(@native_handle, path, target, replace)
|
|
599
|
+
end
|
|
600
|
+
|
|
601
|
+
# Returns the target text stored for a symbolic link.
|
|
602
|
+
def get_symlink_target(path)
|
|
603
|
+
@operations.lockbox_get_symlink_target(@native_handle, path)
|
|
604
|
+
end
|
|
605
|
+
|
|
606
|
+
# Returns the stable public identifier stored in the Lockbox header.
|
|
607
|
+
def id()
|
|
608
|
+
@operations.lockbox_id(@native_handle)
|
|
609
|
+
end
|
|
610
|
+
|
|
611
|
+
# Reports whether an entry exists at path.
|
|
612
|
+
def exists(path)
|
|
613
|
+
@operations.lockbox_exists(@native_handle, path)
|
|
614
|
+
end
|
|
615
|
+
|
|
616
|
+
# Reports whether path names a directory entry.
|
|
617
|
+
def is_dir(path)
|
|
618
|
+
@operations.lockbox_is_dir(@native_handle, path)
|
|
619
|
+
end
|
|
620
|
+
|
|
621
|
+
# Returns the portable Unix permission bits stored for path.
|
|
622
|
+
def permissions(path)
|
|
623
|
+
@operations.lockbox_permissions(@native_handle, path)
|
|
624
|
+
end
|
|
625
|
+
|
|
626
|
+
# Stages portable Unix permission bits for path.
|
|
627
|
+
def set_permissions(path, permissions)
|
|
628
|
+
@operations.lockbox_set_permissions(@native_handle, path, permissions)
|
|
629
|
+
end
|
|
630
|
+
|
|
631
|
+
# Reads the requested byte range from a stored file.
|
|
632
|
+
def read_range(path, offset, len)
|
|
633
|
+
@operations.lockbox_read_range(@native_handle, path, offset, len)
|
|
634
|
+
end
|
|
635
|
+
|
|
636
|
+
# Adds a password access slot and returns its slot identifier.
|
|
637
|
+
def add_password(password)
|
|
638
|
+
@operations.lockbox_add_password(@native_handle, password)
|
|
639
|
+
end
|
|
640
|
+
|
|
641
|
+
# Grants a named contact access and returns the new slot identifier.
|
|
642
|
+
def add_contact(contact, name)
|
|
643
|
+
@operations.lockbox_add_contact(@native_handle, contact.native_handle, name)
|
|
644
|
+
end
|
|
645
|
+
|
|
646
|
+
# Removes an access slot; at least one usable slot must remain.
|
|
647
|
+
def delete_key(id)
|
|
648
|
+
@operations.lockbox_delete_key(@native_handle, id)
|
|
649
|
+
end
|
|
650
|
+
|
|
651
|
+
# Lists public access slot metadata without returning credentials.
|
|
652
|
+
def list_key_slots()
|
|
653
|
+
@operations.lockbox_list_key_slots(@native_handle)
|
|
654
|
+
end
|
|
655
|
+
|
|
656
|
+
# Assigns a profile signing key to the Lockbox owner role.
|
|
657
|
+
def set_owner_signing_key(key)
|
|
658
|
+
@operations.lockbox_set_owner_signing_key(@native_handle, key.native_handle)
|
|
659
|
+
end
|
|
660
|
+
|
|
661
|
+
# Returns public signing and ownership metadata for the current revision.
|
|
662
|
+
def owner_inspection()
|
|
663
|
+
@operations.lockbox_owner_inspection(@native_handle)
|
|
664
|
+
end
|
|
665
|
+
|
|
666
|
+
# Defines a reusable, versioned form from the supplied field definitions.
|
|
667
|
+
def define_form(alias_name, name, description, fields)
|
|
668
|
+
@operations.lockbox_define_form(@native_handle, alias_name, name, description, Internal::DomainCodec.encode_form_fields(fields))
|
|
669
|
+
end
|
|
670
|
+
|
|
671
|
+
# Lists the form definitions stored in this Lockbox.
|
|
672
|
+
def list_form_definitions()
|
|
673
|
+
@operations.lockbox_list_form_definitions(@native_handle)
|
|
674
|
+
end
|
|
675
|
+
|
|
676
|
+
# Resolves a form alias, type identifier, or revision.
|
|
677
|
+
def resolve_form(reference)
|
|
678
|
+
@operations.lockbox_resolve_form(@native_handle, reference)
|
|
679
|
+
end
|
|
680
|
+
|
|
681
|
+
# Lists every stored revision for a form type identifier.
|
|
682
|
+
def list_form_revisions(type_id)
|
|
683
|
+
@operations.lockbox_list_form_revisions(@native_handle, type_id)
|
|
684
|
+
end
|
|
685
|
+
|
|
686
|
+
# Stages a form record at path using the referenced definition.
|
|
687
|
+
def create_form_record(path, type_reference, name)
|
|
688
|
+
@operations.lockbox_create_form_record(@native_handle, path, type_reference, name)
|
|
689
|
+
end
|
|
690
|
+
|
|
691
|
+
# Stages a plain field value in a form record.
|
|
692
|
+
def set_form_field(path, field, value)
|
|
693
|
+
@operations.lockbox_set_form_field(@native_handle, path, field, value)
|
|
694
|
+
end
|
|
695
|
+
|
|
696
|
+
# Stores a secret form field from mutable bytes.
|
|
697
|
+
def set_secret_form_field(path, field, value)
|
|
698
|
+
@operations.lockbox_set_secret_form_field(@native_handle, path, field, value)
|
|
699
|
+
end
|
|
700
|
+
|
|
701
|
+
# Lists form records without exposing secret field values.
|
|
702
|
+
def list_form_records()
|
|
703
|
+
@operations.lockbox_list_form_records(@native_handle)
|
|
704
|
+
end
|
|
705
|
+
|
|
706
|
+
# Returns the form record at path when present.
|
|
707
|
+
def get_form_record(path)
|
|
708
|
+
@operations.lockbox_get_form_record(@native_handle, path)
|
|
709
|
+
end
|
|
710
|
+
|
|
711
|
+
# Stages removal of a form record.
|
|
712
|
+
def delete_form_record(path)
|
|
713
|
+
@operations.lockbox_delete_form_record(@native_handle, path)
|
|
714
|
+
end
|
|
715
|
+
|
|
716
|
+
# Atomically renames form records using source and destination path pairs.
|
|
717
|
+
def move_form_records(moves)
|
|
718
|
+
@operations.lockbox_move_form_records(@native_handle, Internal::DomainCodec.encode_path_moves(moves))
|
|
719
|
+
end
|
|
720
|
+
|
|
721
|
+
# Returns a plain form field when it exists.
|
|
722
|
+
def get_form_field(path, field)
|
|
723
|
+
@operations.lockbox_get_form_field(@native_handle, path, field)
|
|
724
|
+
end
|
|
725
|
+
|
|
726
|
+
# Yields temporary field bytes and wipes the native transfer afterwards.
|
|
727
|
+
def with_secret_form_field(path, field, &callback)
|
|
728
|
+
@operations.lockbox_get_secret_form_field(@native_handle, path, field, &callback)
|
|
729
|
+
end
|
|
730
|
+
|
|
731
|
+
# Serializes the current Lockbox, including committed changes.
|
|
732
|
+
def to_bytes()
|
|
733
|
+
@operations.lockbox_to_bytes(@native_handle)
|
|
734
|
+
end
|
|
735
|
+
|
|
736
|
+
# Releases the native resources held by this object.
|
|
737
|
+
def free()
|
|
738
|
+
@operations.lockbox_free(@native_handle)
|
|
739
|
+
@native_handle = nil
|
|
740
|
+
end
|
|
741
|
+
|
|
742
|
+
end
|
|
743
|
+
|
|
744
|
+
# A profile's contact-encryption identity used to decrypt keys addressed to it.
|
|
745
|
+
class ContactKeyPair < OwnedHandle
|
|
746
|
+
# Returns the canonical public bytes paired with this identity.
|
|
747
|
+
def public_bytes()
|
|
748
|
+
@operations.key_contact_public(@native_handle)
|
|
749
|
+
end
|
|
750
|
+
|
|
751
|
+
# Returns the private signing-key record for secure binary backup.
|
|
752
|
+
def private_record()
|
|
753
|
+
@operations.key_contact_private(@native_handle)
|
|
754
|
+
end
|
|
755
|
+
|
|
756
|
+
# Releases the native resources held by this object.
|
|
757
|
+
def free()
|
|
758
|
+
@operations.key_contact_free(@native_handle)
|
|
759
|
+
@native_handle = nil
|
|
760
|
+
end
|
|
761
|
+
|
|
762
|
+
# Decrypts a wrapped content key for this contact.
|
|
763
|
+
def decrypt(wrapped)
|
|
764
|
+
@operations.key_contact_decrypt(@native_handle, wrapped.native_handle)
|
|
765
|
+
end
|
|
766
|
+
|
|
767
|
+
end
|
|
768
|
+
|
|
769
|
+
# A recipient's shareable encryption identity used when granting access.
|
|
770
|
+
class ContactPublicKey < OwnedHandle
|
|
771
|
+
# Releases this public contact key.
|
|
772
|
+
def public_free()
|
|
773
|
+
@operations.key_contact_public_free(@native_handle)
|
|
774
|
+
@native_handle = nil
|
|
775
|
+
end
|
|
776
|
+
|
|
777
|
+
# Encrypts a content key for the selected contact.
|
|
778
|
+
def encrypt(content_key)
|
|
779
|
+
WrappedContactKey.new(@operations, @operations.key_contact_encrypt(@native_handle, content_key))
|
|
780
|
+
end
|
|
781
|
+
|
|
782
|
+
end
|
|
783
|
+
|
|
784
|
+
# A content key encrypted for one contact and recoverable by its matching key pair.
|
|
785
|
+
class WrappedContactKey < OwnedHandle
|
|
786
|
+
# Returns the public.
|
|
787
|
+
def public()
|
|
788
|
+
@operations.key_contact_wrapped_public(@native_handle)
|
|
789
|
+
end
|
|
790
|
+
|
|
791
|
+
# Returns the encrypted content key bytes.
|
|
792
|
+
def ciphertext()
|
|
793
|
+
@operations.key_contact_wrapped_ciphertext(@native_handle)
|
|
794
|
+
end
|
|
795
|
+
|
|
796
|
+
# Returns the complete wrapped key record for storage or transport.
|
|
797
|
+
def encrypted()
|
|
798
|
+
@operations.key_contact_wrapped_encrypted(@native_handle)
|
|
799
|
+
end
|
|
800
|
+
|
|
801
|
+
# Releases the native resources held by this object.
|
|
802
|
+
def free()
|
|
803
|
+
@operations.key_contact_wrapped_free(@native_handle)
|
|
804
|
+
@native_handle = nil
|
|
805
|
+
end
|
|
806
|
+
|
|
807
|
+
end
|
|
808
|
+
|
|
809
|
+
# A Vault Profile signing identity used to authorize mutable Lockbox revisions.
|
|
810
|
+
class ProfileSigningKeyPair < OwnedHandle
|
|
811
|
+
# Returns the public.
|
|
812
|
+
def public()
|
|
813
|
+
@operations.key_signing_public(@native_handle)
|
|
814
|
+
end
|
|
815
|
+
|
|
816
|
+
# Returns the private.
|
|
817
|
+
def private()
|
|
818
|
+
@operations.key_signing_private(@native_handle)
|
|
819
|
+
end
|
|
820
|
+
|
|
821
|
+
# Creates an independently owned public verification-key handle.
|
|
822
|
+
def public_key()
|
|
823
|
+
ProfileSigningPublicKey.new(
|
|
824
|
+
@operations,
|
|
825
|
+
@operations.key_signing_public_from_bytes(public)
|
|
826
|
+
)
|
|
827
|
+
end
|
|
828
|
+
|
|
829
|
+
# Releases the native resources held by this object.
|
|
830
|
+
def free()
|
|
831
|
+
@operations.key_signing_free(@native_handle)
|
|
832
|
+
@native_handle = nil
|
|
833
|
+
end
|
|
834
|
+
|
|
835
|
+
end
|
|
836
|
+
|
|
837
|
+
# The public half of a Vault Profile signing identity.
|
|
838
|
+
class ProfileSigningPublicKey < OwnedHandle
|
|
839
|
+
# Releases the native resources held by this object.
|
|
840
|
+
def free()
|
|
841
|
+
@operations.key_signing_public_free(@native_handle)
|
|
842
|
+
@native_handle = nil
|
|
843
|
+
end
|
|
844
|
+
|
|
845
|
+
end
|
|
846
|
+
|
|
847
|
+
# Password-protected storage for Profile keys, contacts, forms, backups, and lockbox paths.
|
|
848
|
+
class VaultStore < OwnedHandle
|
|
849
|
+
# Returns the canonical root directory of this Vault.
|
|
850
|
+
def root()
|
|
851
|
+
@operations.vault_directory_root(@native_handle)
|
|
852
|
+
end
|
|
853
|
+
|
|
854
|
+
# Returns the persistent structure version of this Vault.
|
|
855
|
+
def structure_version()
|
|
856
|
+
@operations.vault_directory_structure_version(@native_handle)
|
|
857
|
+
end
|
|
858
|
+
|
|
859
|
+
# Lists private keys.
|
|
860
|
+
def list_private_keys()
|
|
861
|
+
@operations.vault_directory_list_private_keys(@native_handle)
|
|
862
|
+
end
|
|
863
|
+
|
|
864
|
+
# Lists private key names.
|
|
865
|
+
def list_private_key_names()
|
|
866
|
+
@operations.vault_directory_list_private_key_names(@native_handle)
|
|
867
|
+
end
|
|
868
|
+
|
|
869
|
+
# Lists contact names.
|
|
870
|
+
def list_contact_names()
|
|
871
|
+
@operations.vault_directory_list_contact_names(@native_handle)
|
|
872
|
+
end
|
|
873
|
+
|
|
874
|
+
# Lists form aliases.
|
|
875
|
+
def list_form_aliases()
|
|
876
|
+
@operations.vault_directory_list_form_aliases(@native_handle)
|
|
877
|
+
end
|
|
878
|
+
|
|
879
|
+
# Reports whether the named profile private key exists.
|
|
880
|
+
def private_key_exists(name)
|
|
881
|
+
@operations.vault_directory_private_key_exists(@native_handle, name)
|
|
882
|
+
end
|
|
883
|
+
|
|
884
|
+
# Removes private key.
|
|
885
|
+
def delete_private_key(name)
|
|
886
|
+
@operations.vault_directory_delete_private_key(@native_handle, name)
|
|
887
|
+
end
|
|
888
|
+
|
|
889
|
+
# Stores private key.
|
|
890
|
+
def store_private_key(name, key)
|
|
891
|
+
@operations.vault_directory_store_private_key(@native_handle, name, key.native_handle)
|
|
892
|
+
end
|
|
893
|
+
|
|
894
|
+
# Loads private key.
|
|
895
|
+
def load_private_key(name)
|
|
896
|
+
ContactKeyPair.new(@operations, @operations.vault_directory_load_private_key(@native_handle, name))
|
|
897
|
+
end
|
|
898
|
+
|
|
899
|
+
# Loads private key generation.
|
|
900
|
+
def load_private_key_generation(name, index)
|
|
901
|
+
ContactKeyPair.new(@operations, @operations.vault_directory_load_private_key_generation(@native_handle, name, index))
|
|
902
|
+
end
|
|
903
|
+
|
|
904
|
+
# Stores contact.
|
|
905
|
+
def store_contact(name, key)
|
|
906
|
+
@operations.vault_directory_store_contact(@native_handle, name, key.native_handle)
|
|
907
|
+
end
|
|
908
|
+
|
|
909
|
+
# Loads contact.
|
|
910
|
+
def load_contact(name)
|
|
911
|
+
ContactPublicKey.new(@operations, @operations.vault_directory_load_contact(@native_handle, name))
|
|
912
|
+
end
|
|
913
|
+
|
|
914
|
+
# Reports whether the named contact exists.
|
|
915
|
+
def contact_exists(name)
|
|
916
|
+
@operations.vault_directory_contact_exists(@native_handle, name)
|
|
917
|
+
end
|
|
918
|
+
|
|
919
|
+
# Removes contact.
|
|
920
|
+
def delete_contact(name)
|
|
921
|
+
@operations.vault_directory_delete_contact(@native_handle, name)
|
|
922
|
+
end
|
|
923
|
+
|
|
924
|
+
# Lists contacts.
|
|
925
|
+
def list_contacts()
|
|
926
|
+
@operations.vault_directory_list_contacts(@native_handle)
|
|
927
|
+
end
|
|
928
|
+
|
|
929
|
+
# Stores profile email.
|
|
930
|
+
def store_profile_email(name, email)
|
|
931
|
+
@operations.vault_directory_store_profile_email(@native_handle, name, email)
|
|
932
|
+
end
|
|
933
|
+
|
|
934
|
+
# Get/set/clear encrypted guidance; commit Lockbox writes. Fields belong to definitions.
|
|
935
|
+
def item_description(target, reference, operation: 'get', field: nil, text: nil)
|
|
936
|
+
require 'json'
|
|
937
|
+
@operations.vault_directory_item_description(@native_handle, JSON.generate({ target: target, reference: reference, operation: operation, field: field, text: text }))
|
|
938
|
+
end
|
|
939
|
+
|
|
940
|
+
# Returns the email recorded for a profile, when present.
|
|
941
|
+
def profile_email(name)
|
|
942
|
+
@operations.vault_directory_profile_email(@native_handle, name)
|
|
943
|
+
end
|
|
944
|
+
|
|
945
|
+
# Stores backup.
|
|
946
|
+
def store_backup(id, bytes)
|
|
947
|
+
@operations.vault_directory_store_backup(@native_handle, id, bytes)
|
|
948
|
+
end
|
|
949
|
+
|
|
950
|
+
# Loads backup.
|
|
951
|
+
def load_backup(id)
|
|
952
|
+
@operations.vault_directory_load_backup(@native_handle, id)
|
|
953
|
+
end
|
|
954
|
+
|
|
955
|
+
# Returns the number of stored key recovery backups.
|
|
956
|
+
def backup_count()
|
|
957
|
+
@operations.vault_directory_backup_count(@native_handle)
|
|
958
|
+
end
|
|
959
|
+
|
|
960
|
+
# Restores a profile private key and signing key from recovery material.
|
|
961
|
+
def restore_private_key(name, key, signing_key, overwrite)
|
|
962
|
+
@operations.vault_directory_restore_private_key(@native_handle, name, key.native_handle, signing_key.native_handle, overwrite)
|
|
963
|
+
end
|
|
964
|
+
|
|
965
|
+
# Loads the current signing identity for a Vault Profile.
|
|
966
|
+
def load_profile_signing_key(name)
|
|
967
|
+
ProfileSigningKeyPair.new(@operations, @operations.vault_directory_load_owner_signing_key(@native_handle, name))
|
|
968
|
+
end
|
|
969
|
+
|
|
970
|
+
# Loads one historical signing identity for a Vault Profile.
|
|
971
|
+
def load_profile_signing_key_generation(name, index)
|
|
972
|
+
ProfileSigningKeyPair.new(@operations, @operations.vault_directory_load_owner_signing_key_generation(@native_handle, name, index))
|
|
973
|
+
end
|
|
974
|
+
|
|
975
|
+
# Stores contact signing key.
|
|
976
|
+
def store_contact_signing_key(name, key)
|
|
977
|
+
@operations.vault_directory_store_contact_signing_key(@native_handle, name, key.native_handle)
|
|
978
|
+
end
|
|
979
|
+
|
|
980
|
+
# Loads contact signing key.
|
|
981
|
+
def load_contact_signing_key(name)
|
|
982
|
+
ProfileSigningPublicKey.new(@operations, @operations.vault_directory_load_contact_signing_key(@native_handle, name))
|
|
983
|
+
end
|
|
984
|
+
|
|
985
|
+
# Lists profile generations.
|
|
986
|
+
def list_profile_generations(name)
|
|
987
|
+
@operations.vault_directory_list_profile_generations(@native_handle, name)
|
|
988
|
+
end
|
|
989
|
+
|
|
990
|
+
# Updates private key.
|
|
991
|
+
def rotate_private_key(name)
|
|
992
|
+
@operations.vault_directory_rotate_private_key(@native_handle, name)
|
|
993
|
+
end
|
|
994
|
+
|
|
995
|
+
# Stores lockbox.
|
|
996
|
+
def remember_lockbox(id, path)
|
|
997
|
+
@operations.vault_directory_remember_lockbox(@native_handle, id, path)
|
|
998
|
+
end
|
|
999
|
+
|
|
1000
|
+
# Lists known lockboxes.
|
|
1001
|
+
def list_known_lockboxes()
|
|
1002
|
+
@operations.vault_directory_list_known_lockboxes(@native_handle)
|
|
1003
|
+
end
|
|
1004
|
+
|
|
1005
|
+
# Resolve metadata; verify target identity when opening its path.
|
|
1006
|
+
def resolve_lockbox_alias(name)
|
|
1007
|
+
@operations.vault_directory_resolve_lockbox_alias(@native_handle, name)
|
|
1008
|
+
end
|
|
1009
|
+
|
|
1010
|
+
# Remove only an alias, preserving its target.
|
|
1011
|
+
def remove_lockbox_alias(name)
|
|
1012
|
+
@operations.vault_directory_remove_lockbox_alias(@native_handle, name)
|
|
1013
|
+
end
|
|
1014
|
+
|
|
1015
|
+
# List persisted aliases without opening target archives.
|
|
1016
|
+
def list_lockbox_aliases()
|
|
1017
|
+
@operations.vault_directory_list_lockbox_aliases(@native_handle)
|
|
1018
|
+
end
|
|
1019
|
+
|
|
1020
|
+
# Set or replace a normalized alias for a remembered identity.
|
|
1021
|
+
def set_lockbox_alias(name, id)
|
|
1022
|
+
@operations.vault_directory_set_lockbox_alias(@native_handle, name, id)
|
|
1023
|
+
end
|
|
1024
|
+
|
|
1025
|
+
# Removes lockbox.
|
|
1026
|
+
def forget_lockbox(path)
|
|
1027
|
+
@operations.vault_directory_forget_lockbox(@native_handle, path)
|
|
1028
|
+
end
|
|
1029
|
+
|
|
1030
|
+
# Stores access slot label.
|
|
1031
|
+
def remember_access_slot_label(id, slot_id, name)
|
|
1032
|
+
@operations.vault_directory_remember_access_slot_label(@native_handle, id, slot_id, name)
|
|
1033
|
+
end
|
|
1034
|
+
|
|
1035
|
+
# Lists access slot labels.
|
|
1036
|
+
def list_access_slot_labels(id)
|
|
1037
|
+
@operations.vault_directory_list_access_slot_labels(@native_handle, id)
|
|
1038
|
+
end
|
|
1039
|
+
|
|
1040
|
+
# Finds access slot labels with the supplied name for one Lockbox.
|
|
1041
|
+
def find_access_slot_labels(id, name)
|
|
1042
|
+
@operations.vault_directory_find_access_slot_labels(@native_handle, id, name)
|
|
1043
|
+
end
|
|
1044
|
+
|
|
1045
|
+
# Removes access slot label.
|
|
1046
|
+
def forget_access_slot_label(id, slot_id)
|
|
1047
|
+
@operations.vault_directory_forget_access_slot_label(@native_handle, id, slot_id)
|
|
1048
|
+
end
|
|
1049
|
+
|
|
1050
|
+
# Defines a reusable, versioned form in the local vault.
|
|
1051
|
+
def define_form(alias_name, name, description, fields)
|
|
1052
|
+
@operations.vault_directory_define_form(@native_handle, alias_name, name, description, Internal::DomainCodec.encode_form_fields(fields))
|
|
1053
|
+
end
|
|
1054
|
+
|
|
1055
|
+
# Resolves a form alias, type identifier, or revision.
|
|
1056
|
+
def resolve_form(reference)
|
|
1057
|
+
@operations.vault_directory_resolve_form(@native_handle, reference)
|
|
1058
|
+
end
|
|
1059
|
+
|
|
1060
|
+
# Lists forms.
|
|
1061
|
+
def list_forms()
|
|
1062
|
+
@operations.vault_directory_list_forms(@native_handle)
|
|
1063
|
+
end
|
|
1064
|
+
|
|
1065
|
+
# Lists every stored revision for a form type identifier.
|
|
1066
|
+
def list_form_revisions(type_id)
|
|
1067
|
+
@operations.vault_directory_list_form_revisions(@native_handle, type_id)
|
|
1068
|
+
end
|
|
1069
|
+
|
|
1070
|
+
# Adds missing standard form definitions and returns the number added.
|
|
1071
|
+
def seed_forms()
|
|
1072
|
+
@operations.vault_directory_seed_forms(@native_handle)
|
|
1073
|
+
end
|
|
1074
|
+
|
|
1075
|
+
# Stores password.
|
|
1076
|
+
def remember_password(id, password)
|
|
1077
|
+
@operations.vault_directory_remember_password(@native_handle, id, password)
|
|
1078
|
+
end
|
|
1079
|
+
|
|
1080
|
+
# Returns the Lockbox password encrypted inside this Vault.
|
|
1081
|
+
def remembered_password(id)
|
|
1082
|
+
@operations.vault_directory_remembered_password(@native_handle, id)
|
|
1083
|
+
end
|
|
1084
|
+
|
|
1085
|
+
# Releases the native resources held by this object.
|
|
1086
|
+
def free()
|
|
1087
|
+
@operations.vault_directory_free(@native_handle)
|
|
1088
|
+
@native_handle = nil
|
|
1089
|
+
end
|
|
1090
|
+
|
|
1091
|
+
end
|
|
1092
|
+
|
|
1093
|
+
# A read-only Vault view that can retrieve stored credentials.
|
|
1094
|
+
class ReadOnlyVault < OwnedHandle
|
|
1095
|
+
# Load an existing private key; close it after use.
|
|
1096
|
+
def load_private_key(name)
|
|
1097
|
+
ContactKeyPair.new(@operations, @operations.vault_read_only_load_private_key(@native_handle, name))
|
|
1098
|
+
end
|
|
1099
|
+
|
|
1100
|
+
# Load a stored historical private-key generation.
|
|
1101
|
+
def load_private_key_generation(name, index)
|
|
1102
|
+
ContactKeyPair.new(@operations, @operations.vault_read_only_load_private_key_generation(@native_handle, name, index))
|
|
1103
|
+
end
|
|
1104
|
+
|
|
1105
|
+
# Load an existing Profile signing key without creating one.
|
|
1106
|
+
def load_profile_signing_key(name)
|
|
1107
|
+
ProfileSigningKeyPair.new(@operations, @operations.vault_read_only_load_owner_signing_key(@native_handle, name))
|
|
1108
|
+
end
|
|
1109
|
+
|
|
1110
|
+
# Load a stored historical Profile signing-key generation.
|
|
1111
|
+
def load_profile_signing_key_generation(name, index)
|
|
1112
|
+
ProfileSigningKeyPair.new(@operations, @operations.vault_read_only_load_owner_signing_key_generation(@native_handle, name, index))
|
|
1113
|
+
end
|
|
1114
|
+
|
|
1115
|
+
# Use temporary password-profile bytes during the block, then wipe them.
|
|
1116
|
+
def with_profile_password(name, &callback)
|
|
1117
|
+
@operations.vault_read_only_load_profile_password(@native_handle, name, &callback)
|
|
1118
|
+
end
|
|
1119
|
+
|
|
1120
|
+
# Use remembered Lockbox password bytes, or return nil when absent.
|
|
1121
|
+
def with_remembered_password(lockbox_id, &callback)
|
|
1122
|
+
@operations.vault_read_only_remembered_password(@native_handle, lockbox_id, &callback)
|
|
1123
|
+
end
|
|
1124
|
+
# Lists profile names.
|
|
1125
|
+
def list_profile_names()
|
|
1126
|
+
@operations.vault_read_only_list_profile_names(@native_handle)
|
|
1127
|
+
end
|
|
1128
|
+
|
|
1129
|
+
# Lists contact names.
|
|
1130
|
+
def list_contact_names()
|
|
1131
|
+
@operations.vault_read_only_list_contact_names(@native_handle)
|
|
1132
|
+
end
|
|
1133
|
+
|
|
1134
|
+
# Lists form aliases.
|
|
1135
|
+
def list_form_aliases()
|
|
1136
|
+
@operations.vault_read_only_list_form_aliases(@native_handle)
|
|
1137
|
+
end
|
|
1138
|
+
|
|
1139
|
+
# Lists known lockboxes.
|
|
1140
|
+
def list_known_lockboxes()
|
|
1141
|
+
@operations.vault_read_only_list_known_lockboxes(@native_handle)
|
|
1142
|
+
end
|
|
1143
|
+
|
|
1144
|
+
# Resolve metadata; verify target identity when opening its path.
|
|
1145
|
+
def resolve_lockbox_alias(name)
|
|
1146
|
+
@operations.vault_read_only_resolve_lockbox_alias(@native_handle, name)
|
|
1147
|
+
end
|
|
1148
|
+
|
|
1149
|
+
# List persisted aliases without opening target archives.
|
|
1150
|
+
def list_lockbox_aliases()
|
|
1151
|
+
@operations.vault_read_only_list_lockbox_aliases(@native_handle)
|
|
1152
|
+
end
|
|
1153
|
+
|
|
1154
|
+
# Releases the native resources held by this object.
|
|
1155
|
+
def free()
|
|
1156
|
+
@operations.vault_read_only_free(@native_handle)
|
|
1157
|
+
end
|
|
1158
|
+
|
|
1159
|
+
end
|
|
1160
|
+
|
|
1161
|
+
# Client for the session service that temporarily caches unlock and signing keys.
|
|
1162
|
+
class Agent
|
|
1163
|
+
# Returns the initialize.
|
|
1164
|
+
def initialize(operations)
|
|
1165
|
+
@operations = operations
|
|
1166
|
+
end
|
|
1167
|
+
|
|
1168
|
+
# Reports whether running.
|
|
1169
|
+
def is_running()
|
|
1170
|
+
@operations.vault_is_running()
|
|
1171
|
+
end
|
|
1172
|
+
|
|
1173
|
+
# Removes all.
|
|
1174
|
+
def forget_all()
|
|
1175
|
+
@operations.vault_forget_all()
|
|
1176
|
+
end
|
|
1177
|
+
|
|
1178
|
+
# Runs the session agent server until it is stopped.
|
|
1179
|
+
def serve()
|
|
1180
|
+
@operations.vault_agent_serve()
|
|
1181
|
+
end
|
|
1182
|
+
|
|
1183
|
+
# Verifies transport.
|
|
1184
|
+
def verify_transport()
|
|
1185
|
+
@operations.vault_agent_verify_transport()
|
|
1186
|
+
end
|
|
1187
|
+
|
|
1188
|
+
# Returns get.
|
|
1189
|
+
def get(id)
|
|
1190
|
+
@operations.vault_agent_get(id)
|
|
1191
|
+
end
|
|
1192
|
+
|
|
1193
|
+
# Stores put.
|
|
1194
|
+
def put(id, key)
|
|
1195
|
+
@operations.vault_agent_put(id, key)
|
|
1196
|
+
end
|
|
1197
|
+
|
|
1198
|
+
# Removes forget.
|
|
1199
|
+
def forget(id)
|
|
1200
|
+
@operations.vault_agent_forget(id)
|
|
1201
|
+
end
|
|
1202
|
+
|
|
1203
|
+
# Stops stop.
|
|
1204
|
+
def stop()
|
|
1205
|
+
@operations.vault_agent_stop()
|
|
1206
|
+
end
|
|
1207
|
+
|
|
1208
|
+
# Starts start.
|
|
1209
|
+
def start()
|
|
1210
|
+
@operations.vault_agent_start()
|
|
1211
|
+
end
|
|
1212
|
+
|
|
1213
|
+
# Lists entries below path, optionally including descendants.
|
|
1214
|
+
def list()
|
|
1215
|
+
@operations.vault_agent_list()
|
|
1216
|
+
end
|
|
1217
|
+
|
|
1218
|
+
# Reports how the platform handles agent expiry during system sleep.
|
|
1219
|
+
def sleep_support()
|
|
1220
|
+
@operations.vault_agent_sleep_support()
|
|
1221
|
+
end
|
|
1222
|
+
|
|
1223
|
+
# Returns vault unlock key.
|
|
1224
|
+
def get_vault_unlock_key(vault_id)
|
|
1225
|
+
@operations.vault_agent_get_vault_unlock_key(vault_id)
|
|
1226
|
+
end
|
|
1227
|
+
|
|
1228
|
+
# Stores vault unlock key.
|
|
1229
|
+
def put_vault_unlock_key(vault_id, key, ttl_seconds)
|
|
1230
|
+
@operations.vault_agent_put_vault_unlock_key(vault_id, key, ttl_seconds)
|
|
1231
|
+
end
|
|
1232
|
+
|
|
1233
|
+
# Removes vault unlock key.
|
|
1234
|
+
def forget_vault_unlock_key(vault_id)
|
|
1235
|
+
@operations.vault_agent_forget_vault_unlock_key(vault_id)
|
|
1236
|
+
end
|
|
1237
|
+
|
|
1238
|
+
# Returns the cached signing identity for a Vault Profile.
|
|
1239
|
+
def profile_signing_key(vault_id, profile)
|
|
1240
|
+
ProfileSigningKeyPair.new(@operations, @operations.vault_agent_get_owner_signing_key(vault_id, profile))
|
|
1241
|
+
end
|
|
1242
|
+
|
|
1243
|
+
# Caches a signing identity for a Vault Profile.
|
|
1244
|
+
def cache_profile_signing_key(vault_id, profile, key, ttl_seconds)
|
|
1245
|
+
@operations.vault_agent_put_owner_signing_key(vault_id, profile, key.native_handle, ttl_seconds)
|
|
1246
|
+
end
|
|
1247
|
+
|
|
1248
|
+
# Removes a cached signing identity for a Vault Profile.
|
|
1249
|
+
def forget_profile_signing_key(vault_id, profile)
|
|
1250
|
+
@operations.vault_agent_forget_owner_signing_key(vault_id, profile)
|
|
1251
|
+
end
|
|
1252
|
+
|
|
1253
|
+
# Starts activity.
|
|
1254
|
+
def begin_activity(kind)
|
|
1255
|
+
AgentActivity.new(@operations, @operations.vault_agent_begin_activity(kind))
|
|
1256
|
+
end
|
|
1257
|
+
|
|
1258
|
+
# Stops activity.
|
|
1259
|
+
def end_activity(handle)
|
|
1260
|
+
@operations.vault_agent_end_activity(handle.native_handle)
|
|
1261
|
+
end
|
|
1262
|
+
|
|
1263
|
+
end
|
|
1264
|
+
|
|
1265
|
+
# A token kept alive while an operation needs secrets cached by the agent.
|
|
1266
|
+
class AgentActivity < OwnedHandle
|
|
1267
|
+
end
|
|
1268
|
+
|
|
1269
|
+
# Access to the platform credential store for a scoped Vault passphrase.
|
|
1270
|
+
class Platform
|
|
1271
|
+
# Returns the initialize.
|
|
1272
|
+
def initialize(operations)
|
|
1273
|
+
@operations = operations
|
|
1274
|
+
end
|
|
1275
|
+
|
|
1276
|
+
# Returns availability and user presence guarantees for platform storage.
|
|
1277
|
+
def status()
|
|
1278
|
+
@operations.vault_platform_status()
|
|
1279
|
+
end
|
|
1280
|
+
|
|
1281
|
+
# Sets scope.
|
|
1282
|
+
def set_scope(scope)
|
|
1283
|
+
@operations.vault_platform_set_scope(scope)
|
|
1284
|
+
end
|
|
1285
|
+
|
|
1286
|
+
# Removes password.
|
|
1287
|
+
def forget_password()
|
|
1288
|
+
@operations.vault_platform_forget_password()
|
|
1289
|
+
end
|
|
1290
|
+
|
|
1291
|
+
# Stores password.
|
|
1292
|
+
def put_password(password)
|
|
1293
|
+
@operations.vault_platform_put_password(password)
|
|
1294
|
+
end
|
|
1295
|
+
|
|
1296
|
+
# Enables storage of the Vault passphrase in platform credentials.
|
|
1297
|
+
def enable()
|
|
1298
|
+
@operations.vault_platform_enable()
|
|
1299
|
+
end
|
|
1300
|
+
|
|
1301
|
+
# Disables platform credential use without deleting the stored value.
|
|
1302
|
+
def disable()
|
|
1303
|
+
@operations.vault_platform_disable()
|
|
1304
|
+
end
|
|
1305
|
+
|
|
1306
|
+
# Reports whether platform credential use is disabled.
|
|
1307
|
+
def disabled()
|
|
1308
|
+
@operations.vault_platform_disabled()
|
|
1309
|
+
end
|
|
1310
|
+
|
|
1311
|
+
# Returns password.
|
|
1312
|
+
def get_password()
|
|
1313
|
+
@operations.vault_platform_get_password()
|
|
1314
|
+
end
|
|
1315
|
+
|
|
1316
|
+
end
|
|
1317
|
+
|
|
1318
|
+
# A session that opens lockboxes by host path, caches passwords, and closes local files.
|
|
1319
|
+
class LocalSession < OwnedHandle
|
|
1320
|
+
# Creates Lockbox password.
|
|
1321
|
+
def create_lockbox_password(path, password)
|
|
1322
|
+
Lockbox.new(@operations, @operations.vault_create_lockbox_password(@native_handle, path, password))
|
|
1323
|
+
end
|
|
1324
|
+
|
|
1325
|
+
# Opens Lockbox password.
|
|
1326
|
+
def open_lockbox_password(path, password)
|
|
1327
|
+
Lockbox.new(@operations, @operations.vault_open_lockbox_password(@native_handle, path, password))
|
|
1328
|
+
end
|
|
1329
|
+
|
|
1330
|
+
# Creates lockbox content key.
|
|
1331
|
+
def create_lockbox_content_key(path, content_key, signing_key)
|
|
1332
|
+
Lockbox.new(@operations, @operations.vault_create_lockbox_content_key(@native_handle, path, content_key, signing_key.native_handle))
|
|
1333
|
+
end
|
|
1334
|
+
|
|
1335
|
+
# Creates lockbox contact.
|
|
1336
|
+
def create_lockbox_contact(path, contact, name, signing_key)
|
|
1337
|
+
Lockbox.new(@operations, @operations.vault_create_lockbox_contact(@native_handle, path, contact.native_handle, name, signing_key.native_handle))
|
|
1338
|
+
end
|
|
1339
|
+
|
|
1340
|
+
# Opens lockbox content key.
|
|
1341
|
+
def open_lockbox_content_key(path, content_key, signing_key)
|
|
1342
|
+
Lockbox.new(@operations, @operations.vault_open_lockbox_content_key(@native_handle, path, content_key, signing_key.native_handle))
|
|
1343
|
+
end
|
|
1344
|
+
|
|
1345
|
+
# Stores Lockbox password.
|
|
1346
|
+
def cache_lockbox_password(path, password, ttl_seconds)
|
|
1347
|
+
@operations.vault_cache_lockbox_password(@native_handle, path, password, ttl_seconds)
|
|
1348
|
+
end
|
|
1349
|
+
|
|
1350
|
+
# Releases the native resources held by lockbox.
|
|
1351
|
+
def close_lockbox(path)
|
|
1352
|
+
@operations.vault_close_lockbox(@native_handle, path)
|
|
1353
|
+
end
|
|
1354
|
+
|
|
1355
|
+
# Releases the native resources held by all.
|
|
1356
|
+
def close_all()
|
|
1357
|
+
@operations.vault_close_all(@native_handle)
|
|
1358
|
+
end
|
|
1359
|
+
|
|
1360
|
+
# Releases the native resources held by this object.
|
|
1361
|
+
def free()
|
|
1362
|
+
@operations.vault_free(@native_handle)
|
|
1363
|
+
@native_handle = nil
|
|
1364
|
+
end
|
|
1365
|
+
|
|
1366
|
+
end
|
|
1367
|
+
|
|
1368
|
+
end
|
|
1369
|
+
|
|
1370
|
+
# Reviewed 0.3 facade names. The Fiddle transport classes above remain
|
|
1371
|
+
# implementation details of these domain objects.
|
|
1372
|
+
module Revault
|
|
1373
|
+
module LockboxCacheMode
|
|
1374
|
+
BYTES = 'bytes'; DISABLED = 'disabled'; AUTOMATIC = 'automatic'
|
|
1375
|
+
end
|
|
1376
|
+
module LockboxWorkload
|
|
1377
|
+
INTERACTIVE = 'interactive'; BULK_IMPORT = 'bulk-import'; READ_MOSTLY = 'read-mostly'
|
|
1378
|
+
end
|
|
1379
|
+
module LockboxWorker
|
|
1380
|
+
AUTO = 'auto'; SINGLE = 'single'; THREADS = 'threads'
|
|
1381
|
+
end
|
|
1382
|
+
module AgentActivityKind
|
|
1383
|
+
OPEN = 'open'; CLOSE = 'close'; VARIABLES = 'variables'; FORM = 'form'; RECOVERY = 'recovery'; VAULT = 'vault'
|
|
1384
|
+
end
|
|
1385
|
+
module KeyExportFormat
|
|
1386
|
+
LOCKBOX_PEM = 'lockbox-pem'; JWK = 'jwk'; JWKS = 'jwks'; RAW_HEX = 'raw-hex'
|
|
1387
|
+
end
|
|
1388
|
+
|
|
1389
|
+
# Mutable byte secret that can be wiped after a native operation.
|
|
1390
|
+
class SecretBytes
|
|
1391
|
+
# Copy bytes into owned mutable storage.
|
|
1392
|
+
def initialize(value = ''.b) @bytes = value.to_s.b.dup end
|
|
1393
|
+
# Return a copy of the secret bytes.
|
|
1394
|
+
def to_str = @bytes
|
|
1395
|
+
# Return a defensive copy of the secret bytes.
|
|
1396
|
+
def bytes = @bytes.dup
|
|
1397
|
+
# Wipe the secret in place.
|
|
1398
|
+
def close = @bytes.replace("\0" * @bytes.bytesize)
|
|
1399
|
+
alias dispose close
|
|
1400
|
+
end
|
|
1401
|
+
# UTF-8 secret passphrase with the same wipe semantics as SecretBytes.
|
|
1402
|
+
class SecretString < SecretBytes
|
|
1403
|
+
# Copy a passphrase into mutable UTF-8 storage.
|
|
1404
|
+
def initialize(value = '') super(value.to_s.encode(Encoding::UTF_8)) end
|
|
1405
|
+
end
|
|
1406
|
+
|
|
1407
|
+
# Persistent encrypted local store for profiles, keys, contacts and metadata.
|
|
1408
|
+
class Vault < VaultStore
|
|
1409
|
+
# Constructors for the persistent store lifecycle.
|
|
1410
|
+
class << self
|
|
1411
|
+
# Open an existing store without creating or replacing it.
|
|
1412
|
+
def open(root, vault_passphrase) = Runtime.new.vault_directory_open(root, vault_passphrase.to_str)
|
|
1413
|
+
# Open or create a store at root.
|
|
1414
|
+
def open_or_create(root, vault_passphrase) = Runtime.new.vault_directory_open_or_create(root, vault_passphrase.to_str)
|
|
1415
|
+
# Create a new store at root.
|
|
1416
|
+
def create(root, vault_passphrase) = Runtime.new.vault_directory_replace(root, vault_passphrase.to_str)
|
|
1417
|
+
# Replace an existing store explicitly.
|
|
1418
|
+
def replace(root, vault_passphrase) = Runtime.new.vault_directory_replace(root, vault_passphrase.to_str)
|
|
1419
|
+
end
|
|
1420
|
+
end
|
|
1421
|
+
|
|
1422
|
+
# Shared close convenience for all owned facade handles.
|
|
1423
|
+
class OwnedHandle
|
|
1424
|
+
# Release the native resource.
|
|
1425
|
+
def close = free
|
|
1426
|
+
end
|
|
1427
|
+
# Lockbox-specific close convenience.
|
|
1428
|
+
class Lockbox
|
|
1429
|
+
# Release the lockbox handle.
|
|
1430
|
+
alias close free
|
|
1431
|
+
end
|
|
1432
|
+
# Persistent store close convenience.
|
|
1433
|
+
class VaultStore
|
|
1434
|
+
# Release the store handle.
|
|
1435
|
+
alias close free
|
|
1436
|
+
end
|
|
1437
|
+
# Read-only store close convenience.
|
|
1438
|
+
class ReadOnlyVault
|
|
1439
|
+
# Release the read-only store handle.
|
|
1440
|
+
alias close free
|
|
1441
|
+
end
|
|
1442
|
+
# Local session close convenience.
|
|
1443
|
+
class LocalSession
|
|
1444
|
+
# Release the local session handle.
|
|
1445
|
+
alias close free
|
|
1446
|
+
end
|
|
1447
|
+
# Explicit Session Agent controller with process-local lockbox operations.
|
|
1448
|
+
class AgentSession < Agent
|
|
1449
|
+
# Return the process-wide explicit session controller.
|
|
1450
|
+
def self.instance
|
|
1451
|
+
Revault.ensure_native
|
|
1452
|
+
@instance ||= new(BindingOperations.new)
|
|
1453
|
+
end
|
|
1454
|
+
# Attach this controller to the local native session handle.
|
|
1455
|
+
def initialize(operations)
|
|
1456
|
+
super
|
|
1457
|
+
@local = operations.vault_local
|
|
1458
|
+
end
|
|
1459
|
+
# Remove one cached lockbox key.
|
|
1460
|
+
def close_lockbox(path) = @operations.vault_close_lockbox(@local, path)
|
|
1461
|
+
# Remove all cached lockbox keys.
|
|
1462
|
+
def close_all = @operations.vault_close_all(@local)
|
|
1463
|
+
# Create a password-protected lockbox file.
|
|
1464
|
+
def create_lockbox_password(path, password) = Lockbox.new(@operations, @operations.vault_create_lockbox_password(@local, path, password.to_str))
|
|
1465
|
+
# Open a password-protected lockbox file.
|
|
1466
|
+
def open_lockbox_password(path, password) = Lockbox.new(@operations, @operations.vault_open_lockbox_password(@local, path, password.to_str))
|
|
1467
|
+
# Create a content-key lockbox file.
|
|
1468
|
+
def create_lockbox_content_key(path, content_key, signing_key) = Lockbox.new(@operations, @operations.vault_create_lockbox_content_key(@local, path, content_key, signing_key.native_handle))
|
|
1469
|
+
# Create a contact-addressed lockbox file.
|
|
1470
|
+
def create_lockbox_contact(path, contact, name, signing_key) = Lockbox.new(@operations, @operations.vault_create_lockbox_contact(@local, path, contact.native_handle, name, signing_key.native_handle))
|
|
1471
|
+
# Open a content-key lockbox file.
|
|
1472
|
+
def open_lockbox_content_key(path, content_key, signing_key) = Lockbox.new(@operations, @operations.vault_open_lockbox_content_key(@local, path, content_key, signing_key.native_handle))
|
|
1473
|
+
# Cache a password-derived key for the requested TTL.
|
|
1474
|
+
def cache_lockbox_password(path, password, ttl_seconds) = @operations.vault_cache_lockbox_password(@local, path, password.to_str, ttl_seconds)
|
|
1475
|
+
# Release the local session handle.
|
|
1476
|
+
def close
|
|
1477
|
+
@operations.vault_free(@local) if @local
|
|
1478
|
+
@local = nil
|
|
1479
|
+
end
|
|
1480
|
+
alias free close
|
|
1481
|
+
end
|
|
1482
|
+
end
|
|
1483
|
+
|
|
1484
|
+
# Native handles and operation routing are implementation details of the
|
|
1485
|
+
# application-facing facade above.
|
|
1486
|
+
Revault.private_constant(:OwnedHandle, :Runtime, :VaultStore, :LocalSession)
|
|
1487
|
+
require 'json'
|
|
1488
|
+
module Revault
|
|
1489
|
+
# Full reciprocal exchange. Compare identities and fingerprint independently between calls.
|
|
1490
|
+
module ComparisonChannel
|
|
1491
|
+
IN_PERSON = 'in-person'.freeze
|
|
1492
|
+
KNOWN_PHONE_CALL = 'known-phone-call'.freeze
|
|
1493
|
+
TRUSTED_MESSENGER = 'trusted-messenger'.freeze
|
|
1494
|
+
end
|
|
1495
|
+
# Reciprocal exchange session retaining protected credentials but no Vault lock.
|
|
1496
|
+
class ContactExchange < OwnedHandle
|
|
1497
|
+
# Marshal an internal request to the shared Rust workflow.
|
|
1498
|
+
def execute(request)
|
|
1499
|
+
raise 'Exchange session is closed' unless @native_handle
|
|
1500
|
+
@operations.contact_exchange_execute(@native_handle, JSON.generate(request))
|
|
1501
|
+
end
|
|
1502
|
+
private :execute
|
|
1503
|
+
# Create an invitation; retry pending delivery using list.
|
|
1504
|
+
# Example:
|
|
1505
|
+
# ```
|
|
1506
|
+
# session = api.contact_exchange(vault_path, vault_passphrase)
|
|
1507
|
+
# begin
|
|
1508
|
+
# offer = session.invite(relay_origin, 'default', 'bob@example.com')
|
|
1509
|
+
# session.list if offer.relay_pending
|
|
1510
|
+
# ensure
|
|
1511
|
+
# session.close
|
|
1512
|
+
# end
|
|
1513
|
+
# ```
|
|
1514
|
+
def invite(relay, profile, recipient_email, ttl_hours=24) = execute(operation: 'invite', relay: relay, profile: profile, recipient_email: recipient_email, ttl_hours: ttl_hours).first
|
|
1515
|
+
# Accept with one selected profile's complete public identity.
|
|
1516
|
+
# Example:
|
|
1517
|
+
# ```
|
|
1518
|
+
# session = api.contact_exchange(vault_path, vault_passphrase)
|
|
1519
|
+
# begin
|
|
1520
|
+
# reply = session.accept(invitation_url, 'default')
|
|
1521
|
+
# puts reply.fingerprint
|
|
1522
|
+
# ensure
|
|
1523
|
+
# session.close
|
|
1524
|
+
# end
|
|
1525
|
+
# ```
|
|
1526
|
+
def accept(invitation, profile) = execute(operation: 'accept', invitation: invitation, profile: profile).first
|
|
1527
|
+
# List public status, optionally without relay requests.
|
|
1528
|
+
# Example:
|
|
1529
|
+
# ```
|
|
1530
|
+
# session = api.contact_exchange(vault_path, vault_passphrase)
|
|
1531
|
+
# begin
|
|
1532
|
+
# session.list(true).each { |status| puts status.id }
|
|
1533
|
+
# ensure
|
|
1534
|
+
# session.close
|
|
1535
|
+
# end
|
|
1536
|
+
# ```
|
|
1537
|
+
def list(offline=false) = execute(operation: 'list', offline: offline)
|
|
1538
|
+
# Save both keys after independent full-fingerprint comparison.
|
|
1539
|
+
# Example:
|
|
1540
|
+
# ```
|
|
1541
|
+
# session = api.contact_exchange(vault_path, vault_passphrase)
|
|
1542
|
+
# begin
|
|
1543
|
+
# # After independent identity and full-fingerprint comparison:
|
|
1544
|
+
# session.verify(exchange_id, 'bob', compared_fingerprint, Revault::ComparisonChannel::IN_PERSON)
|
|
1545
|
+
# ensure
|
|
1546
|
+
# session.close
|
|
1547
|
+
# end
|
|
1548
|
+
# ```
|
|
1549
|
+
def verify(id, contact_name, fingerprint, channel) = execute(operation: 'verify', id: id, contact_name: contact_name, fingerprint: fingerprint, channel: channel).first
|
|
1550
|
+
# Authenticate an archive against the saved contact signing key.
|
|
1551
|
+
# Example:
|
|
1552
|
+
# ```
|
|
1553
|
+
# session = api.contact_exchange(vault_path, vault_passphrase)
|
|
1554
|
+
# begin
|
|
1555
|
+
# session.verify_author('alice', received_archive_path)
|
|
1556
|
+
# ensure
|
|
1557
|
+
# session.close
|
|
1558
|
+
# end
|
|
1559
|
+
# ```
|
|
1560
|
+
def verify_author(contact_name, lockbox_path) = execute(operation: 'verify_author', contact_name: contact_name, lockbox_path: lockbox_path)
|
|
1561
|
+
|
|
1562
|
+
# Cancel an owned, unaccepted invitation.
|
|
1563
|
+
# Example:
|
|
1564
|
+
# ```
|
|
1565
|
+
# session = api.contact_exchange(vault_path, vault_passphrase)
|
|
1566
|
+
# begin
|
|
1567
|
+
# session.cancel(exchange_id)
|
|
1568
|
+
# ensure
|
|
1569
|
+
# session.close
|
|
1570
|
+
# end
|
|
1571
|
+
# ```
|
|
1572
|
+
def cancel(id) = execute(operation: 'cancel', id: id)
|
|
1573
|
+
# Forget local state, preserving contacts.
|
|
1574
|
+
# Example:
|
|
1575
|
+
# ```
|
|
1576
|
+
# session = api.contact_exchange(vault_path, vault_passphrase)
|
|
1577
|
+
# begin
|
|
1578
|
+
# session.forget(exchange_id)
|
|
1579
|
+
# ensure
|
|
1580
|
+
# session.close
|
|
1581
|
+
# end
|
|
1582
|
+
# ```
|
|
1583
|
+
def forget(id) = execute(operation: 'forget', id: id)
|
|
1584
|
+
# Release protected credentials without cancelling invitations.
|
|
1585
|
+
# Example:
|
|
1586
|
+
# ```
|
|
1587
|
+
# session = api.contact_exchange(vault_path, vault_passphrase)
|
|
1588
|
+
# begin
|
|
1589
|
+
# puts session.list(true).length
|
|
1590
|
+
# ensure
|
|
1591
|
+
# session.close
|
|
1592
|
+
# end
|
|
1593
|
+
# ```
|
|
1594
|
+
def free
|
|
1595
|
+
@operations.contact_exchange_free(@native_handle) if @native_handle
|
|
1596
|
+
@native_handle=nil
|
|
1597
|
+
end
|
|
1598
|
+
alias close free
|
|
1599
|
+
end
|
|
1600
|
+
end
|