Marco Ricci commited on 2025-08-14 22:47:35
Zeige 2 geänderte Dateien mit 3 Einfügungen und 83 Löschungen.
Sadly, there exist configurations and pairs of master passphrases (and presumably, pairs of services names as well) that lead to the same derived passphrase. (These are typically short-length derived passphrases with strongly restricted character sets.) Once `hypothesis` has found such a set of inputs, its example database will cause it to keep rediscovering that example. Ideally, we want to express that given enough entropy through the configuration, the chance of deriving the same passphrases with two different master passphrases or two different service names becomes very small. However, this is a statement about the function's state space, and I do not know how to sensibly express this statement in a unit-test or `hypothesis`-test-compatible way, short of perhaps enumerating the whole state space (which is computationally infeasible). So, we remove these tests of config dependence; they are clearly non-functional and misleading.
... | ... |
@@ -788,7 +788,7 @@ class TestPhraseBasic: |
788 | 788 |
|
789 | 789 |
@pytest.mark.xfail( |
790 | 790 |
True, |
791 |
- reason='not implemented yet', |
|
791 |
+ reason="not implemented yet", |
|
792 | 792 |
raises=NotImplementedError, |
793 | 793 |
strict=True, |
794 | 794 |
) |
... | ... |
@@ -800,7 +800,7 @@ class TestPhraseBasic: |
800 | 800 |
|
801 | 801 |
@pytest.mark.xfail( |
802 | 802 |
True, |
803 |
- reason='not implemented yet', |
|
803 |
+ reason="not implemented yet", |
|
804 | 804 |
raises=NotImplementedError, |
805 | 805 |
strict=True, |
806 | 806 |
) |
... | ... |
@@ -1544,7 +1544,7 @@ class TestExportConfigValid: |
1544 | 1544 |
|
1545 | 1545 |
@pytest.mark.xfail( |
1546 | 1546 |
True, |
1547 |
- reason='not implemented yet', |
|
1547 |
+ reason="not implemented yet", |
|
1548 | 1548 |
raises=NotImplementedError, |
1549 | 1549 |
strict=True, |
1550 | 1550 |
) |
... | ... |
@@ -262,46 +262,6 @@ class TestPhraseDependence: |
262 | 262 |
phrase=phrases[0], service=service |
263 | 263 |
) != vault.Vault.create_hash(phrase=phrases[1], service=service) |
264 | 264 |
|
265 |
- @hypothesis.given( |
|
266 |
- phrases=strategies.lists( |
|
267 |
- strategies.text( |
|
268 |
- strategies.characters(min_codepoint=32, max_codepoint=126), |
|
269 |
- min_size=1, |
|
270 |
- max_size=32, |
|
271 |
- ), |
|
272 |
- min_size=2, |
|
273 |
- max_size=2, |
|
274 |
- unique=True, |
|
275 |
- ), |
|
276 |
- config=hypothesis_machinery.vault_full_service_config(), |
|
277 |
- service=strategies.text( |
|
278 |
- strategies.characters(min_codepoint=32, max_codepoint=126), |
|
279 |
- min_size=1, |
|
280 |
- max_size=32, |
|
281 |
- ), |
|
282 |
- ) |
|
283 |
- def test_phrase_dependence_with_config( |
|
284 |
- self, |
|
285 |
- phrases: list[str], |
|
286 |
- config: dict[str, int], |
|
287 |
- service: bytes, |
|
288 |
- ) -> None: |
|
289 |
- """The derived passphrase is dependent on the master passphrase.""" |
|
290 |
- try: |
|
291 |
- assert vault.Vault(phrase=phrases[0], **config).generate( |
|
292 |
- service |
|
293 |
- ) != vault.Vault(phrase=phrases[1], **config).generate(service) |
|
294 |
- except ValueError as exc: # pragma: no cover |
|
295 |
- # The service configuration strategy attempts to only |
|
296 |
- # generate satisfiable configurations. It is possible, |
|
297 |
- # though rare, that this fails, and that unsatisfiability is |
|
298 |
- # only recognized when actually deriving a passphrase. In |
|
299 |
- # that case, reject the generated configuration. |
|
300 |
- hypothesis.assume("no allowed characters left" not in exc.args) |
|
301 |
- # Otherwise it's a genuine bug in the test case or the |
|
302 |
- # implementation, and should be raised. |
|
303 |
- raise |
|
304 |
- |
|
305 | 265 |
|
306 | 266 |
class TestServiceNameDependence: |
307 | 267 |
"""Test the dependence of the internal hash on the service name.""" |
... | ... |
@@ -329,46 +289,6 @@ class TestServiceNameDependence: |
329 | 289 |
phrase=phrase, service=services[0] |
330 | 290 |
) != vault.Vault.create_hash(phrase=phrase, service=services[1]) |
331 | 291 |
|
332 |
- @hypothesis.given( |
|
333 |
- phrase=strategies.text( |
|
334 |
- strategies.characters(min_codepoint=32, max_codepoint=126), |
|
335 |
- min_size=1, |
|
336 |
- max_size=32, |
|
337 |
- ), |
|
338 |
- config=hypothesis_machinery.vault_full_service_config(), |
|
339 |
- services=strategies.lists( |
|
340 |
- strategies.text( |
|
341 |
- strategies.characters(min_codepoint=32, max_codepoint=126), |
|
342 |
- min_size=1, |
|
343 |
- max_size=32, |
|
344 |
- ), |
|
345 |
- min_size=2, |
|
346 |
- max_size=2, |
|
347 |
- unique=True, |
|
348 |
- ), |
|
349 |
- ) |
|
350 |
- def test_service_name_dependence_with_config( |
|
351 |
- self, |
|
352 |
- phrase: str, |
|
353 |
- config: dict[str, int], |
|
354 |
- services: list[bytes], |
|
355 |
- ) -> None: |
|
356 |
- """The derived passphrase is dependent on the service name.""" |
|
357 |
- try: |
|
358 |
- assert vault.Vault(phrase=phrase, **config).generate( |
|
359 |
- services[0] |
|
360 |
- ) != vault.Vault(phrase=phrase, **config).generate(services[1]) |
|
361 |
- except ValueError as exc: # pragma: no cover |
|
362 |
- # The service configuration strategy attempts to only |
|
363 |
- # generate satisfiable configurations. It is possible, |
|
364 |
- # though rare, that this fails, and that unsatisfiability is |
|
365 |
- # only recognized when actually deriving a passphrase. In |
|
366 |
- # that case, reject the generated configuration. |
|
367 |
- hypothesis.assume("no allowed characters left" not in exc.args) |
|
368 |
- # Otherwise it's a genuine bug in the test case or the |
|
369 |
- # implementation, and should be raised. |
|
370 |
- raise |
|
371 |
- |
|
372 | 292 |
|
373 | 293 |
class TestInterchangablePhrases: |
374 | 294 |
"""Test the interchangability of certain master passphrases.""" |
375 | 295 |