Move exporter command-line...
Marco Ricci authored 2 weeks ago
|
1) # SPDX-FileCopyrightText: 2024 Marco Ricci <m@the13thletter.info>
2) #
3) # SPDX-License-Identifier: MIT
4)
5) from __future__ import annotations
6)
|
Rename vault v0.2/v0.3 clas...
Marco Ricci authored 2 weeks ago
|
7) import base64
|
Move exporter command-line...
Marco Ricci authored 2 weeks ago
|
8) import json
|
Test exporter data loading...
Marco Ricci authored 2 weeks ago
|
9) from typing import TYPE_CHECKING
|
Move exporter command-line...
Marco Ricci authored 2 weeks ago
|
10)
11) import click.testing
12) import pytest
13)
14) import tests
|
Rename vault v0.2/v0.3 clas...
Marco Ricci authored 2 weeks ago
|
15) from derivepassphrase.exporter import cli, storeroom, vault_v03_and_below
|
Move exporter command-line...
Marco Ricci authored 2 weeks ago
|
16)
17) cryptography = pytest.importorskip('cryptography', minversion='38.0')
18)
|
Test exporter data loading...
Marco Ricci authored 2 weeks ago
|
19) if TYPE_CHECKING:
|
Rename vault v0.2/v0.3 clas...
Marco Ricci authored 2 weeks ago
|
20) from collections.abc import Callable
|
Test exporter data loading...
Marco Ricci authored 2 weeks ago
|
21) from typing import Any
22)
|
Move exporter command-line...
Marco Ricci authored 2 weeks ago
|
23)
24) class TestCLI:
25) def test_200_path_parameter(self, monkeypatch: pytest.MonkeyPatch) -> None:
26) runner = click.testing.CliRunner(mix_stderr=False)
27) with tests.isolated_vault_exporter_config(
28) monkeypatch=monkeypatch,
29) runner=runner,
30) vault_config=tests.VAULT_V03_CONFIG,
31) vault_key=tests.VAULT_MASTER_KEY,
32) ):
33) monkeypatch.setenv('VAULT_KEY', tests.VAULT_MASTER_KEY)
34) result = runner.invoke(
35) cli.derivepassphrase_export,
36) ['VAULT_PATH'],
37) )
38) assert not result.exception
39) assert (result.exit_code, result.stderr_bytes) == (0, b'')
40) assert json.loads(result.stdout) == tests.VAULT_V03_CONFIG_DATA
41)
42) def test_201_key_parameter(self, monkeypatch: pytest.MonkeyPatch) -> None:
43) runner = click.testing.CliRunner(mix_stderr=False)
44) with tests.isolated_vault_exporter_config(
45) monkeypatch=monkeypatch,
46) runner=runner,
47) vault_config=tests.VAULT_V03_CONFIG,
48) ):
49) result = runner.invoke(
50) cli.derivepassphrase_export,
51) ['-k', tests.VAULT_MASTER_KEY, '.vault'],
52) )
53) assert not result.exception
54) assert (result.exit_code, result.stderr_bytes) == (0, b'')
55) assert json.loads(result.stdout) == tests.VAULT_V03_CONFIG_DATA
56)
57) @pytest.mark.parametrize(
58) ['format', 'config', 'config_data'],
59) [
60) pytest.param(
61) 'v0.2',
62) tests.VAULT_V02_CONFIG,
63) tests.VAULT_V02_CONFIG_DATA,
64) id='0.2',
65) ),
66) pytest.param(
67) 'v0.3',
68) tests.VAULT_V03_CONFIG,
69) tests.VAULT_V03_CONFIG_DATA,
70) id='0.3',
71) ),
72) pytest.param(
73) 'storeroom',
74) tests.VAULT_STOREROOM_CONFIG_ZIPPED,
75) tests.VAULT_STOREROOM_CONFIG_DATA,
76) id='storeroom',
77) ),
78) ],
79) )
80) def test_210_load_vault_v02_v03_storeroom(
81) self,
82) monkeypatch: pytest.MonkeyPatch,
83) format: str,
84) config: str | bytes,
85) config_data: dict[str, Any],
86) ) -> None:
87) runner = click.testing.CliRunner(mix_stderr=False)
88) with tests.isolated_vault_exporter_config(
89) monkeypatch=monkeypatch,
90) runner=runner,
91) vault_config=config,
92) ):
93) result = runner.invoke(
94) cli.derivepassphrase_export,
95) ['-f', format, '-k', tests.VAULT_MASTER_KEY, 'VAULT_PATH'],
96) )
97) assert not result.exception
98) assert (result.exit_code, result.stderr_bytes) == (0, b'')
99) assert json.loads(result.stdout) == config_data
100)
101) # test_300_invalid_format is found in
102) # tests.test_derivepassphrase_export::Test002CLI
103)
104) def test_301_vault_config_not_found(
105) self,
106) monkeypatch: pytest.MonkeyPatch,
107) ) -> None:
108) runner = click.testing.CliRunner(mix_stderr=False)
109) with tests.isolated_vault_exporter_config(
110) monkeypatch=monkeypatch,
111) runner=runner,
112) vault_config=tests.VAULT_V03_CONFIG,
113) vault_key=tests.VAULT_MASTER_KEY,
114) ):
115) result = runner.invoke(
116) cli.derivepassphrase_export,
117) ['does-not-exist.txt'],
118) )
119) assert isinstance(result.exception, SystemExit)
120) assert result.exit_code
121) assert result.stderr_bytes
122) assert (
123) b"Cannot parse 'does-not-exist.txt' as a valid config"
124) in result.stderr_bytes
125) )
126) assert tests.CANNOT_LOAD_CRYPTOGRAPHY not in result.stderr_bytes
127)
128) def test_302_vault_config_invalid(
129) self,
130) monkeypatch: pytest.MonkeyPatch,
131) ) -> None:
132) runner = click.testing.CliRunner(mix_stderr=False)
133) with tests.isolated_vault_exporter_config(
134) monkeypatch=monkeypatch,
135) runner=runner,
136) vault_config='',
137) vault_key=tests.VAULT_MASTER_KEY,
138) ):
139) result = runner.invoke(
140) cli.derivepassphrase_export,
141) ['.vault'],
142) )
143) assert isinstance(result.exception, SystemExit)
144) assert result.exit_code
145) assert result.stderr_bytes
146) assert (
147) b"Cannot parse '.vault' as a valid config." in result.stderr_bytes
148) )
149) assert tests.CANNOT_LOAD_CRYPTOGRAPHY not in result.stderr_bytes
|
Test exporter data loading...
Marco Ricci authored 2 weeks ago
|
150)
151) def test_403_invalid_vault_config_bad_signature(
152) self,
153) monkeypatch: pytest.MonkeyPatch,
154) ) -> None:
155) runner = click.testing.CliRunner(mix_stderr=False)
156) with tests.isolated_vault_exporter_config(
157) monkeypatch=monkeypatch,
158) runner=runner,
159) vault_config=tests.VAULT_V02_CONFIG,
160) vault_key=tests.VAULT_MASTER_KEY,
161) ):
162) result = runner.invoke(
163) cli.derivepassphrase_export,
164) ['-f', 'v0.3', '.vault'],
165) )
166) assert isinstance(result.exception, SystemExit)
167) assert result.exit_code
168) assert result.stderr_bytes
169) assert (
170) b"Cannot parse '.vault' as a valid config." in result.stderr_bytes
171) )
172) assert tests.CANNOT_LOAD_CRYPTOGRAPHY not in result.stderr_bytes
173)
174) def test_500_vault_config_invalid_internal(
175) self,
176) monkeypatch: pytest.MonkeyPatch,
177) ) -> None:
178) runner = click.testing.CliRunner(mix_stderr=False)
179) with tests.isolated_vault_exporter_config(
180) monkeypatch=monkeypatch,
181) runner=runner,
182) vault_config=tests.VAULT_V03_CONFIG,
183) vault_key=tests.VAULT_MASTER_KEY,
184) ):
185)
186) def _load_data(*_args: Any, **_kwargs: Any) -> None:
187) return None
188)
189) monkeypatch.setattr(cli, '_load_data', _load_data)
190) result = runner.invoke(
191) cli.derivepassphrase_export,
192) ['.vault'],
193) )
194) assert isinstance(result.exception, SystemExit)
195) assert result.exit_code
196) assert result.stderr_bytes
197) assert b'Invalid vault config: ' in result.stderr_bytes
198) assert tests.CANNOT_LOAD_CRYPTOGRAPHY not in result.stderr_bytes
|
Add more tests of the store...
Marco Ricci authored 2 weeks ago
|
199)
200)
201) class TestStoreroom:
202) @pytest.mark.parametrize(
203) ['path', 'key'],
204) [
205) ('.vault', tests.VAULT_MASTER_KEY),
206) ('.vault', None),
207) (None, tests.VAULT_MASTER_KEY),
208) (None, None),
209) ],
210) )
211) def test_200_export_data_path_and_keys_type(
212) self,
213) monkeypatch: pytest.MonkeyPatch,
214) path: str | None,
215) key: str | None,
216) ) -> None:
217) runner = click.testing.CliRunner(mix_stderr=False)
218) with tests.isolated_vault_exporter_config(
219) monkeypatch=monkeypatch,
220) runner=runner,
221) vault_config=tests.VAULT_STOREROOM_CONFIG_ZIPPED,
222) vault_key=tests.VAULT_MASTER_KEY,
223) ):
224) assert (
225) storeroom.export_storeroom_data(path, key)
226) == tests.VAULT_STOREROOM_CONFIG_DATA
227) )
228)
229) def test_400_decrypt_bucket_item_unknown_version(self) -> None:
230) bucket_item = (
231) b'\xff' + bytes(storeroom.ENCRYPTED_KEYPAIR_SIZE) + bytes(3)
232) )
233) master_keys: storeroom.MasterKeys = {
234) 'encryption_key': bytes(storeroom.KEY_SIZE),
235) 'signing_key': bytes(storeroom.KEY_SIZE),
236) 'hashing_key': bytes(storeroom.KEY_SIZE),
237) }
238) with pytest.raises(RuntimeError, match='Cannot handle version 255'):
239) storeroom.decrypt_bucket_item(bucket_item, master_keys)
240)
241) @pytest.mark.parametrize('config', ['xxx', 'null', '{"version": 255}'])
242) def test_401_decrypt_bucket_file_bad_json_or_version(
243) self,
244) monkeypatch: pytest.MonkeyPatch,
245) config: str,
246) ) -> None:
247) runner = click.testing.CliRunner(mix_stderr=False)
248) master_keys: storeroom.MasterKeys = {
249) 'encryption_key': bytes(storeroom.KEY_SIZE),
250) 'signing_key': bytes(storeroom.KEY_SIZE),
251) 'hashing_key': bytes(storeroom.KEY_SIZE),
252) }
253) with (
254) tests.isolated_vault_exporter_config(
255) monkeypatch=monkeypatch,
256) runner=runner,
257) vault_config=tests.VAULT_STOREROOM_CONFIG_ZIPPED,
258) ),
259) ):
260) with open('.vault/20', 'w', encoding='UTF-8') as outfile:
261) print(config, file=outfile)
262) with pytest.raises(RuntimeError, match='Invalid bucket file: '):
263) list(storeroom.decrypt_bucket_file('.vault/20', master_keys))
264)
265) @pytest.mark.parametrize(
266) ['data', 'err_msg'],
267) [
268) ('{"version": 255}', 'bad or unsupported keys version header'),
269) ('{"version": 1}\nAAAA\nAAAA', 'trailing data; cannot make sense'),
270) ('{"version": 1}\nAAAA', 'cannot handle version 0 encrypted keys'),
271) ],
272) )
273) def test_402_export_storeroom_data_bad_master_keys_file(
274) self,
275) monkeypatch: pytest.MonkeyPatch,
276) data: str,
277) err_msg: str,
278) ) -> None:
279) runner = click.testing.CliRunner(mix_stderr=False)
280) with (
281) tests.isolated_vault_exporter_config(
282) monkeypatch=monkeypatch,
283) runner=runner,
284) vault_config=tests.VAULT_STOREROOM_CONFIG_ZIPPED,
285) vault_key=tests.VAULT_MASTER_KEY,
286) ),
287) ):
288) with open('.vault/.keys', 'w', encoding='UTF-8') as outfile:
289) print(data, file=outfile)
290) with pytest.raises(RuntimeError, match=err_msg):
291) storeroom.export_storeroom_data()
292)
293) def test_403_export_storeroom_data_bad_directory_listing(
294) self,
295) monkeypatch: pytest.MonkeyPatch,
296) ) -> None:
297) runner = click.testing.CliRunner(mix_stderr=False)
298) with (
299) tests.isolated_vault_exporter_config(
300) monkeypatch=monkeypatch,
301) runner=runner,
302) vault_config=tests.VAULT_STOREROOM_BROKEN_DIR_CONFIG_ZIPPED,
303) vault_key=tests.VAULT_MASTER_KEY,
304) ),
305) pytest.raises(RuntimeError, match='Object key mismatch'),
306) ):
|
Fix formatting and linting...
Marco Ricci authored 2 weeks ago
|
307) storeroom.export_storeroom_data()
|
Rename vault v0.2/v0.3 clas...
Marco Ricci authored 2 weeks ago
|
308)
309)
310) class TestVaultNativeConfig:
311) @pytest.mark.parametrize(
312) ['iterations', 'result'],
313) [
314) (100, b'6ede361e81e9c061efcdd68aeb768b80'),
315) (200, b'bcc7d01e075b9ffb69e702bf701187c1'),
316) ],
317) )
318) def test_200_pbkdf2_manually(self, iterations: int, result: bytes) -> None:
|
Fix formatting and linting...
Marco Ricci authored 2 weeks ago
|
319) assert (
320) vault_v03_and_below.VaultNativeConfigParser.pbkdf2(
321) tests.VAULT_MASTER_KEY.encode('utf-8'), 32, iterations
322) )
323) == result
324) )
|
Rename vault v0.2/v0.3 clas...
Marco Ricci authored 2 weeks ago
|
325)
326) @pytest.mark.parametrize(
327) ['parser_class', 'config', 'result'],
328) [
329) pytest.param(
330) vault_v03_and_below.VaultNativeV02ConfigParser,
331) tests.VAULT_V02_CONFIG,
332) tests.VAULT_V02_CONFIG_DATA,
333) id='0.2',
334) ),
335) pytest.param(
336) vault_v03_and_below.VaultNativeV03ConfigParser,
337) tests.VAULT_V03_CONFIG,
338) tests.VAULT_V03_CONFIG_DATA,
339) id='0.3',
340) ),
341) ],
342) )
343) def test_300_result_caching(
344) self,
345) monkeypatch: pytest.MonkeyPatch,
346) parser_class: type[vault_v03_and_below.VaultNativeConfigParser],
347) config: str,
348) result: dict[str, Any],
349) ) -> None:
350) def null_func(name: str) -> Callable[..., None]:
351) def func(*_args: Any, **_kwargs: Any) -> None: # pragma: no cover
352) msg = f'disallowed and stubbed out function {name} called'
353) raise AssertionError(msg)
|
Fix formatting and linting...
Marco Ricci authored 2 weeks ago
|
354)
|
Rename vault v0.2/v0.3 clas...
Marco Ricci authored 2 weeks ago
|
355) return func
356)
357) runner = click.testing.CliRunner(mix_stderr=False)
358) with tests.isolated_vault_exporter_config(
359) monkeypatch=monkeypatch,
360) runner=runner,
361) vault_config=config,
362) ):
|
Fix formatting and linting...
Marco Ricci authored 2 weeks ago
|
363) parser = parser_class(
364) base64.b64decode(config), tests.VAULT_MASTER_KEY
365) )
|
Rename vault v0.2/v0.3 clas...
Marco Ricci authored 2 weeks ago
|
366) assert parser() == result
367) # Now stub out all functions used to calculate the above result.
|
Fix formatting and linting...
Marco Ricci authored 2 weeks ago
|
368) monkeypatch.setattr(
369) parser, '_parse_contents', null_func('_parse_contents')
370) )
371) monkeypatch.setattr(
372) parser, '_derive_keys', null_func('_derive_keys')
373) )
374) monkeypatch.setattr(
375) parser, '_check_signature', null_func('_check_signature')
376) )
377) monkeypatch.setattr(
378) parser, '_decrypt_payload', null_func('_decrypt_payload')
379) )
|
Rename vault v0.2/v0.3 clas...
Marco Ricci authored 2 weeks ago
|
380) assert parser() == result
|
Fix formatting and linting...
Marco Ricci authored 2 weeks ago
|
381) super_call = vault_v03_and_below.VaultNativeConfigParser.__call__
382) assert super_call(parser) == result
|