revault-api 0.1.0__py3-none-win_amd64.whl
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.
- revault_api/__init__.py +409 -0
- revault_api/_native/windows-x86_64-msvc/revault_api.dll +0 -0
- revault_api/facade.py +1136 -0
- revault_api/revault_bindings_pb2.py +122 -0
- revault_api/revault_native.py +425 -0
- revault_api-0.1.0.dist-info/METADATA +20 -0
- revault_api-0.1.0.dist-info/RECORD +10 -0
- revault_api-0.1.0.dist-info/WHEEL +5 -0
- revault_api-0.1.0.dist-info/licenses/LICENSE +87 -0
- revault_api-0.1.0.dist-info/top_level.txt +1 -0
revault_api/__init__.py
ADDED
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
"""Typed ctypes wrapper for the reVault native binding library."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import ctypes
|
|
5
|
+
import os
|
|
6
|
+
import platform
|
|
7
|
+
import sys
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from enum import Enum
|
|
11
|
+
from typing import Optional
|
|
12
|
+
|
|
13
|
+
from . import revault_bindings_pb2 as messages
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class _Buffer(ctypes.Structure):
|
|
17
|
+
_fields_ = [("ptr", ctypes.POINTER(ctypes.c_uint8)), ("len", ctypes.c_size_t)]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class LockboxEntryKind(str, Enum):
|
|
21
|
+
FILE = "file"
|
|
22
|
+
SYMLINK = "symlink"
|
|
23
|
+
DIRECTORY = "directory"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class ProfileGenerationStatus(str, Enum):
|
|
27
|
+
ACTIVE = "active"
|
|
28
|
+
RETIRED = "retired"
|
|
29
|
+
COMPROMISED = "compromised"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass(frozen=True)
|
|
33
|
+
class ProfileGeneration:
|
|
34
|
+
index: int
|
|
35
|
+
status: ProfileGenerationStatus
|
|
36
|
+
contact_fingerprint: bytes
|
|
37
|
+
created_at_unix_ms: int
|
|
38
|
+
retired_at_unix_ms: int | None
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dataclass(frozen=True)
|
|
42
|
+
class ProfileHistory:
|
|
43
|
+
name: str
|
|
44
|
+
active_generation: int
|
|
45
|
+
generations: tuple[ProfileGeneration, ...]
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass(frozen=True)
|
|
49
|
+
class LockboxEntry:
|
|
50
|
+
path: str
|
|
51
|
+
kind: LockboxEntryKind
|
|
52
|
+
length: int
|
|
53
|
+
permissions: int
|
|
54
|
+
|
|
55
|
+
@classmethod
|
|
56
|
+
def from_protobuf(cls, value: bytes) -> "LockboxEntry":
|
|
57
|
+
message = messages.LockboxEntry.FromString(value)
|
|
58
|
+
return cls(
|
|
59
|
+
path=message.path,
|
|
60
|
+
kind=LockboxEntryKind({1: "file", 2: "symlink", 3: "directory"}[message.kind]),
|
|
61
|
+
length=message.length,
|
|
62
|
+
permissions=message.permissions,
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class Lockbox:
|
|
67
|
+
def __init__(self, library: ctypes.CDLL, handle: int):
|
|
68
|
+
self._lib, self._handle = library, ctypes.c_void_p(handle)
|
|
69
|
+
|
|
70
|
+
def __del__(self) -> None:
|
|
71
|
+
if getattr(self, "_handle", None):
|
|
72
|
+
self._lib.lockbox_free(self._handle)
|
|
73
|
+
self._handle = None
|
|
74
|
+
|
|
75
|
+
def add_file(self, path: str, data: bytes, replace: bool = False) -> None:
|
|
76
|
+
encoded = path.encode()
|
|
77
|
+
if not self._lib.lockbox_add_file(self._handle, encoded, len(encoded), data, len(data), replace):
|
|
78
|
+
raise RuntimeError(_error(self._lib))
|
|
79
|
+
|
|
80
|
+
def commit(self) -> None:
|
|
81
|
+
if not self._lib.lockbox_commit(self._handle):
|
|
82
|
+
raise RuntimeError(_error(self._lib))
|
|
83
|
+
|
|
84
|
+
def _bytes(self, result: _Buffer) -> bytes:
|
|
85
|
+
if not result.ptr:
|
|
86
|
+
raise RuntimeError(_error(self._lib))
|
|
87
|
+
try:
|
|
88
|
+
return ctypes.string_at(result.ptr, result.len)
|
|
89
|
+
finally:
|
|
90
|
+
self._lib.buffer_free(result)
|
|
91
|
+
|
|
92
|
+
def add_file_with_permissions(self, path: str, data: bytes, permissions: int, replace: bool = False) -> None:
|
|
93
|
+
encoded = path.encode()
|
|
94
|
+
if not self._lib.lockbox_add_file_with_permissions(self._handle, encoded, len(encoded), data, len(data), permissions, replace):
|
|
95
|
+
raise RuntimeError(_error(self._lib))
|
|
96
|
+
|
|
97
|
+
def create_dir(self, path: str, create_parents: bool = True) -> None:
|
|
98
|
+
encoded = path.encode()
|
|
99
|
+
if not self._lib.lockbox_create_dir(self._handle, encoded, len(encoded), create_parents):
|
|
100
|
+
raise RuntimeError(_error(self._lib))
|
|
101
|
+
|
|
102
|
+
def remove_dir(self, path: str, recursive: bool = False) -> None:
|
|
103
|
+
encoded = path.encode()
|
|
104
|
+
if not self._lib.lockbox_remove_dir(self._handle, encoded, len(encoded), recursive):
|
|
105
|
+
raise RuntimeError(_error(self._lib))
|
|
106
|
+
|
|
107
|
+
def delete(self, path: str) -> None:
|
|
108
|
+
encoded = path.encode()
|
|
109
|
+
if not self._lib.lockbox_delete(self._handle, encoded, len(encoded)):
|
|
110
|
+
raise RuntimeError(_error(self._lib))
|
|
111
|
+
|
|
112
|
+
def rename(self, source: str, destination: str) -> None:
|
|
113
|
+
source_bytes, destination_bytes = source.encode(), destination.encode()
|
|
114
|
+
if not self._lib.lockbox_rename(self._handle, source_bytes, len(source_bytes), destination_bytes, len(destination_bytes)):
|
|
115
|
+
raise RuntimeError(_error(self._lib))
|
|
116
|
+
|
|
117
|
+
def read_range(self, path: str, offset: int, length: int) -> bytes:
|
|
118
|
+
encoded = path.encode()
|
|
119
|
+
return self._bytes(self._lib.lockbox_read_range(self._handle, encoded, len(encoded), offset, length))
|
|
120
|
+
|
|
121
|
+
def list(self, path: str = "/", recursive: bool = False) -> list[LockboxEntry]:
|
|
122
|
+
encoded = path.encode()
|
|
123
|
+
payload = _wire_payload(self._bytes(self._lib.lockbox_list(self._handle, encoded, len(encoded), recursive)))
|
|
124
|
+
result = messages.LockboxEntryList.FromString(payload)
|
|
125
|
+
return [LockboxEntry.from_protobuf(value.SerializeToString()) for value in result.entries]
|
|
126
|
+
|
|
127
|
+
def stat(self, path: str) -> LockboxEntry | None:
|
|
128
|
+
encoded = path.encode()
|
|
129
|
+
result = self._lib.lockbox_stat(self._handle, encoded, len(encoded))
|
|
130
|
+
if not result.ptr:
|
|
131
|
+
return None
|
|
132
|
+
payload = _wire_payload(self._bytes(result))
|
|
133
|
+
entry = messages.OptionalLockboxEntry.FromString(payload)
|
|
134
|
+
return LockboxEntry.from_protobuf(entry.value.SerializeToString()) if entry.HasField("value") else None
|
|
135
|
+
|
|
136
|
+
def set_variable(self, name: str, value: str, secret: bool = False) -> None:
|
|
137
|
+
name_bytes, value_bytes = name.encode(), value.encode()
|
|
138
|
+
if not self._lib.lockbox_set_variable(self._handle, name_bytes, len(name_bytes), value_bytes, len(value_bytes), secret):
|
|
139
|
+
raise RuntimeError(_error(self._lib))
|
|
140
|
+
|
|
141
|
+
def get_variable(self, name: str) -> str:
|
|
142
|
+
encoded = name.encode()
|
|
143
|
+
return self._bytes(self._lib.lockbox_get_variable(self._handle, encoded, len(encoded))).decode()
|
|
144
|
+
|
|
145
|
+
def get_file(self, path: str) -> bytes:
|
|
146
|
+
encoded = path.encode()
|
|
147
|
+
return self._bytes(self._lib.lockbox_get_file(self._handle, encoded, len(encoded)))
|
|
148
|
+
|
|
149
|
+
def move_variables(self, moves: messages.PathMoveList) -> None:
|
|
150
|
+
value = moves.SerializeToString()
|
|
151
|
+
if not self._lib.lockbox_move_variables(self._handle, value, len(value)):
|
|
152
|
+
raise RuntimeError(_error(self._lib))
|
|
153
|
+
|
|
154
|
+
def move_form_records(self, moves: messages.PathMoveList) -> None:
|
|
155
|
+
value = moves.SerializeToString()
|
|
156
|
+
if not self._lib.lockbox_move_form_records(self._handle, value, len(value)):
|
|
157
|
+
raise RuntimeError(_error(self._lib))
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
class ContactKey:
|
|
161
|
+
def __init__(self, library: ctypes.CDLL, handle: int):
|
|
162
|
+
self._lib, self._handle = library, ctypes.c_void_p(handle)
|
|
163
|
+
|
|
164
|
+
def __del__(self) -> None:
|
|
165
|
+
if getattr(self, "_handle", None):
|
|
166
|
+
self._lib.key_contact_free(self._handle)
|
|
167
|
+
self._handle = None
|
|
168
|
+
|
|
169
|
+
def public_bytes(self) -> bytes:
|
|
170
|
+
return Lockbox._bytes(self, self._lib.key_contact_public(self._handle))
|
|
171
|
+
|
|
172
|
+
def private_record(self) -> bytes:
|
|
173
|
+
return Lockbox._bytes(self, self._lib.key_contact_private(self._handle))
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
class VaultDirectory:
|
|
177
|
+
def __init__(self, library: ctypes.CDLL, handle: int):
|
|
178
|
+
self._lib, self._handle = library, ctypes.c_void_p(handle)
|
|
179
|
+
|
|
180
|
+
def __del__(self) -> None:
|
|
181
|
+
if getattr(self, "_handle", None):
|
|
182
|
+
self._lib.vault_directory_free(self._handle)
|
|
183
|
+
self._handle = None
|
|
184
|
+
|
|
185
|
+
def list_profile_generations(self, name: str) -> ProfileHistory:
|
|
186
|
+
encoded = name.encode()
|
|
187
|
+
frame = self._lib.vault_directory_list_profile_generations(self._handle, encoded, len(encoded))
|
|
188
|
+
payload = _wire_payload(Lockbox._bytes(self, frame))
|
|
189
|
+
value = messages.ProfileHistory.FromString(payload)
|
|
190
|
+
generations = tuple(
|
|
191
|
+
ProfileGeneration(
|
|
192
|
+
index=generation.index,
|
|
193
|
+
status=ProfileGenerationStatus(generation.status),
|
|
194
|
+
contact_fingerprint=generation.contact_fingerprint,
|
|
195
|
+
created_at_unix_ms=generation.created_at_unix_ms,
|
|
196
|
+
retired_at_unix_ms=generation.retired_at_unix_ms if generation.has_retired_at else None,
|
|
197
|
+
)
|
|
198
|
+
for generation in value.generations
|
|
199
|
+
)
|
|
200
|
+
return ProfileHistory(
|
|
201
|
+
name=value.name,
|
|
202
|
+
active_generation=value.active_generation,
|
|
203
|
+
generations=generations,
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
def list_form_revisions(self, type_id: str) -> messages.FormDefinitionList:
|
|
207
|
+
encoded = type_id.encode()
|
|
208
|
+
return _message(self, self._lib.vault_directory_list_form_revisions(
|
|
209
|
+
self._handle, encoded, len(encoded)), messages.FormDefinitionList)
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
class ReadOnlyVaultDirectory:
|
|
213
|
+
def __init__(self, library: ctypes.CDLL, handle: int):
|
|
214
|
+
self._lib, self._handle = library, ctypes.c_void_p(handle)
|
|
215
|
+
|
|
216
|
+
def close(self) -> None:
|
|
217
|
+
if self._handle:
|
|
218
|
+
self._lib.vault_read_only_free(self._handle)
|
|
219
|
+
self._handle = None
|
|
220
|
+
|
|
221
|
+
def __del__(self) -> None:
|
|
222
|
+
self.close()
|
|
223
|
+
|
|
224
|
+
def list_profile_names(self) -> messages.StringList:
|
|
225
|
+
return _message(self, self._lib.vault_read_only_list_profile_names(self._handle), messages.StringList)
|
|
226
|
+
|
|
227
|
+
def list_contact_names(self) -> messages.StringList:
|
|
228
|
+
return _message(self, self._lib.vault_read_only_list_contact_names(self._handle), messages.StringList)
|
|
229
|
+
|
|
230
|
+
def list_form_aliases(self) -> messages.StringList:
|
|
231
|
+
return _message(self, self._lib.vault_read_only_list_form_aliases(self._handle), messages.StringList)
|
|
232
|
+
|
|
233
|
+
def list_known_lockboxes(self) -> messages.KnownLockboxList:
|
|
234
|
+
return _message(self, self._lib.vault_read_only_list_known_lockboxes(self._handle), messages.KnownLockboxList)
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
class Revault:
|
|
238
|
+
"""Owned entry point for the native lockbox and vault APIs."""
|
|
239
|
+
def __init__(self, path: Optional[str | Path] = None):
|
|
240
|
+
self.library = load(path)
|
|
241
|
+
self._lib = self.library
|
|
242
|
+
|
|
243
|
+
@property
|
|
244
|
+
def lockbox_format_version(self) -> int:
|
|
245
|
+
return self.library.lockbox_format_version()
|
|
246
|
+
|
|
247
|
+
def probe_lockbox_format_version(self, value: bytes) -> int:
|
|
248
|
+
return self.library.lockbox_probe_format_version(value, len(value))
|
|
249
|
+
|
|
250
|
+
@property
|
|
251
|
+
def current_vault_structure_version(self) -> int:
|
|
252
|
+
return self.library.vault_structure_version_current()
|
|
253
|
+
|
|
254
|
+
def probe_vault_structure_version(self, root: str, password: bytes) -> int:
|
|
255
|
+
encoded = root.encode()
|
|
256
|
+
return self.library.vault_directory_probe_structure_version(encoded, len(encoded), password, len(password))
|
|
257
|
+
|
|
258
|
+
def last_error_details(self) -> messages.ErrorDetails:
|
|
259
|
+
return _message(self, self.library.buffer_last_error_details(), messages.ErrorDetails)
|
|
260
|
+
|
|
261
|
+
def open_read_only_vault(self, root: str, password: bytes) -> ReadOnlyVaultDirectory:
|
|
262
|
+
encoded = root.encode()
|
|
263
|
+
handle = self.library.vault_read_only_open(encoded, len(encoded), password, len(password))
|
|
264
|
+
if not handle: raise RuntimeError(_error(self.library))
|
|
265
|
+
return ReadOnlyVaultDirectory(self.library, handle)
|
|
266
|
+
|
|
267
|
+
def open_default_read_only_vault(self, password: bytes) -> ReadOnlyVaultDirectory:
|
|
268
|
+
handle = self.library.vault_read_only_open_default(password, len(password))
|
|
269
|
+
if not handle: raise RuntimeError(_error(self.library))
|
|
270
|
+
return ReadOnlyVaultDirectory(self.library, handle)
|
|
271
|
+
|
|
272
|
+
def start_agent(self) -> None:
|
|
273
|
+
if not self.library.vault_agent_start(): raise RuntimeError(_error(self.library))
|
|
274
|
+
|
|
275
|
+
def put_vault_unlock_key(self, vault_id: str, key: bytes, ttl_seconds: int) -> None:
|
|
276
|
+
value = vault_id.encode()
|
|
277
|
+
if not self.library.vault_agent_put_vault_unlock_key(value, len(value), key, len(key), ttl_seconds):
|
|
278
|
+
raise RuntimeError(_error(self.library))
|
|
279
|
+
|
|
280
|
+
def get_vault_unlock_key(self, vault_id: str) -> bytes:
|
|
281
|
+
value = vault_id.encode()
|
|
282
|
+
return Lockbox._bytes(self, self.library.vault_agent_get_vault_unlock_key(value, len(value)))
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
def _error(lib: ctypes.CDLL) -> str:
|
|
286
|
+
return lib.buffer_last_error().decode()
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
def _wire_payload(frame: bytes) -> bytes:
|
|
290
|
+
if frame[:4] != b"LBWF" or len(frame) < 12:
|
|
291
|
+
raise RuntimeError("invalid binding frame")
|
|
292
|
+
length = int.from_bytes(frame[8:12], "big")
|
|
293
|
+
if len(frame) != 12 + length:
|
|
294
|
+
raise RuntimeError("invalid binding frame length")
|
|
295
|
+
return frame[12:]
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
def _message(owner: object, frame: _Buffer, message_type: type):
|
|
299
|
+
return message_type.FromString(_wire_payload(Lockbox._bytes(owner, frame)))
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def _native_library_path() -> str:
|
|
303
|
+
override = os.environ.get("REVAULT_LIBRARY")
|
|
304
|
+
if override:
|
|
305
|
+
return override
|
|
306
|
+
machine = platform.machine().lower()
|
|
307
|
+
arch = {"amd64": "x86_64", "x86_64": "x86_64", "arm64": "aarch64", "aarch64": "aarch64"}.get(machine)
|
|
308
|
+
os_name = "macos" if sys.platform == "darwin" else "windows" if sys.platform == "win32" else "linux" if sys.platform.startswith("linux") else None
|
|
309
|
+
if arch is None or os_name is None:
|
|
310
|
+
raise RuntimeError(f"reVault does not publish a native library for {sys.platform}/{machine}")
|
|
311
|
+
target = f"{os_name}-{arch}" + ("-gnu" if os_name == "linux" else "-msvc" if os_name == "windows" else "")
|
|
312
|
+
filename = "revault_api.dll" if os_name == "windows" else "librevault_api.dylib" if os_name == "macos" else "librevault_api.so"
|
|
313
|
+
bundled = Path(__file__).resolve().parent / "_native" / target / filename
|
|
314
|
+
if not bundled.is_file():
|
|
315
|
+
raise RuntimeError(
|
|
316
|
+
f"revault-api native carrier is missing for {target}; "
|
|
317
|
+
"set REVAULT_LIBRARY for development"
|
|
318
|
+
)
|
|
319
|
+
return str(bundled)
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
def load(path: Optional[str | Path] = None) -> ctypes.CDLL:
|
|
323
|
+
"""Load the native library and configure its ABI signatures."""
|
|
324
|
+
lib = ctypes.CDLL(str(path or _native_library_path()))
|
|
325
|
+
lib.api_abi_version.argtypes = []
|
|
326
|
+
lib.api_abi_version.restype = ctypes.c_uint32
|
|
327
|
+
if lib.api_abi_version() != 1:
|
|
328
|
+
raise RuntimeError("revault-api native ABI mismatch; expected 1")
|
|
329
|
+
try:
|
|
330
|
+
from .revault_native import configure_native
|
|
331
|
+
except ImportError:
|
|
332
|
+
from revault_native import configure_native
|
|
333
|
+
configure_native(lib, _Buffer)
|
|
334
|
+
lib.lockbox_create.argtypes = [ctypes.c_void_p, ctypes.c_size_t]
|
|
335
|
+
lib.lockbox_create.restype = ctypes.c_void_p
|
|
336
|
+
_configure(lib, "lockbox_add_file", [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_size_t, ctypes.c_void_p, ctypes.c_size_t, ctypes.c_bool], ctypes.c_bool)
|
|
337
|
+
_configure(lib, "lockbox_add_file_with_permissions", [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_size_t, ctypes.c_void_p, ctypes.c_size_t, ctypes.c_uint32, ctypes.c_bool], ctypes.c_bool)
|
|
338
|
+
_configure(lib, "lockbox_commit", [ctypes.c_void_p], ctypes.c_bool)
|
|
339
|
+
_configure(lib, "lockbox_create_dir", [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_size_t, ctypes.c_bool], ctypes.c_bool)
|
|
340
|
+
_configure(lib, "lockbox_remove_dir", [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_size_t, ctypes.c_bool], ctypes.c_bool)
|
|
341
|
+
_configure(lib, "lockbox_delete", [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_size_t], ctypes.c_bool)
|
|
342
|
+
_configure(lib, "lockbox_rename", [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_size_t, ctypes.c_char_p, ctypes.c_size_t], ctypes.c_bool)
|
|
343
|
+
_configure(lib, "lockbox_get_file", [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_size_t], _Buffer)
|
|
344
|
+
_configure(lib, "lockbox_read_range", [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_size_t, ctypes.c_uint64, ctypes.c_uint64], _Buffer)
|
|
345
|
+
_configure(lib, "lockbox_list", [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_size_t, ctypes.c_bool], _Buffer)
|
|
346
|
+
_configure(lib, "lockbox_stat", [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_size_t], _Buffer)
|
|
347
|
+
_configure(lib, "lockbox_set_variable", [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_size_t, ctypes.c_char_p, ctypes.c_size_t, ctypes.c_bool], ctypes.c_bool)
|
|
348
|
+
_configure(lib, "lockbox_get_variable", [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_size_t], _Buffer)
|
|
349
|
+
_configure(lib, "key_contact_generate", [], ctypes.c_void_p)
|
|
350
|
+
_configure(lib, "key_contact_public", [ctypes.c_void_p], _Buffer)
|
|
351
|
+
_configure(lib, "key_contact_private", [ctypes.c_void_p], _Buffer)
|
|
352
|
+
_configure(lib, "key_contact_free", [ctypes.c_void_p], None)
|
|
353
|
+
lib.lockbox_free.argtypes = [ctypes.c_void_p]
|
|
354
|
+
_configure(lib, "vault_directory_list_profile_generations", [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_size_t], _Buffer)
|
|
355
|
+
_configure(lib, "vault_directory_open_or_create", [ctypes.c_char_p, ctypes.c_size_t, ctypes.c_void_p, ctypes.c_size_t], ctypes.c_void_p)
|
|
356
|
+
_configure(lib, "vault_directory_free", [ctypes.c_void_p], None)
|
|
357
|
+
lib.buffer_free.argtypes = [_Buffer]
|
|
358
|
+
lib.buffer_last_error.restype = ctypes.c_char_p
|
|
359
|
+
return lib
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
def _configure(lib: ctypes.CDLL, name: str, args: list[object], result: object) -> None:
|
|
363
|
+
function = getattr(lib, name)
|
|
364
|
+
function.argtypes, function.restype = args, result
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
# The generated facade is imported after the low-level loader and frame helpers
|
|
368
|
+
# are defined so every public native operation has an owned, class-oriented route.
|
|
369
|
+
from .facade import ( # noqa: E402,F401
|
|
370
|
+
Agent,
|
|
371
|
+
AgentActivity,
|
|
372
|
+
ContactKeyPair,
|
|
373
|
+
ContactPublicKey,
|
|
374
|
+
LocalVault,
|
|
375
|
+
Lockbox,
|
|
376
|
+
Platform,
|
|
377
|
+
ReadOnlyVaultDirectory,
|
|
378
|
+
Revault,
|
|
379
|
+
SigningKeyPair,
|
|
380
|
+
SigningPublicKey,
|
|
381
|
+
Vault,
|
|
382
|
+
VaultDirectory,
|
|
383
|
+
WrappedContactKey,
|
|
384
|
+
)
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
def generate_contact_key(library: Optional[ctypes.CDLL] = None) -> ContactKey:
|
|
388
|
+
lib = library or load()
|
|
389
|
+
handle = lib.key_contact_generate()
|
|
390
|
+
if not handle:
|
|
391
|
+
raise RuntimeError(_error(lib))
|
|
392
|
+
return ContactKey(lib, handle)
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
def create(key: bytes, library: Optional[ctypes.CDLL] = None) -> Lockbox:
|
|
396
|
+
lib = library or load()
|
|
397
|
+
handle = lib.lockbox_create(key, len(key))
|
|
398
|
+
if not handle:
|
|
399
|
+
raise RuntimeError(_error(lib))
|
|
400
|
+
return Lockbox(lib, handle)
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
def open_vault_directory(root: str, password: bytes, library: Optional[ctypes.CDLL] = None) -> VaultDirectory:
|
|
404
|
+
lib = library or load()
|
|
405
|
+
encoded = root.encode()
|
|
406
|
+
handle = lib.vault_directory_open_or_create(encoded, len(encoded), password, len(password))
|
|
407
|
+
if not handle:
|
|
408
|
+
raise RuntimeError(_error(lib))
|
|
409
|
+
return VaultDirectory(lib, handle)
|
|
Binary file
|