Marco Ricci commited on 2025-01-27 19:46:57
Zeige 1 geänderte Dateien mit 79 Einfügungen und 88 Löschungen.
Reformat the "vault" tests. This includes retiring the `Vault` type alias and using the proper Google-style `vault.Vault` instead, because the API documentation does not correctly resolve this type alias otherwise.
| ... | ... |
@@ -13,16 +13,14 @@ from typing import TYPE_CHECKING |
| 13 | 13 |
import hypothesis |
| 14 | 14 |
import pytest |
| 15 | 15 |
from hypothesis import strategies |
| 16 |
-from typing_extensions import TypeAlias, TypeVar |
|
| 16 |
+from typing_extensions import TypeVar |
|
| 17 | 17 |
|
| 18 |
-import derivepassphrase |
|
| 19 | 18 |
import tests |
| 19 |
+from derivepassphrase import vault |
|
| 20 | 20 |
|
| 21 | 21 |
if TYPE_CHECKING: |
| 22 | 22 |
from collections.abc import Iterator |
| 23 | 23 |
|
| 24 |
-Vault: TypeAlias = derivepassphrase.vault.Vault |
|
| 25 |
- |
|
| 26 | 24 |
BLOCK_SIZE = hashlib.sha1().block_size |
| 27 | 25 |
DIGEST_SIZE = hashlib.sha1().digest_size |
| 28 | 26 |
|
| ... | ... |
@@ -32,7 +30,7 @@ def phrases_are_interchangable( |
| 32 | 30 |
phrase2: bytes | bytearray | str, |
| 33 | 31 |
/, |
| 34 | 32 |
) -> bool: |
| 35 |
- """Work-alike of [`Vault.phrases_are_interchangable`][]. |
|
| 33 |
+ """Work-alike of [`vault.Vault.phrases_are_interchangable`][]. |
|
| 36 | 34 |
|
| 37 | 35 |
This version is not resistant to timing attacks, but faster, and |
| 38 | 36 |
supports strings directly. |
| ... | ... |
@@ -44,7 +42,7 @@ def phrases_are_interchangable( |
| 44 | 42 |
A passphrase to compare. |
| 45 | 43 |
|
| 46 | 44 |
Returns: |
| 47 |
- True if the phrases behave identically under [`Vault`][], |
|
| 45 |
+ True if the phrases behave identically under [`vault.Vault`][], |
|
| 48 | 46 |
false otherwise. |
| 49 | 47 |
|
| 50 | 48 |
""" |
| ... | ... |
@@ -56,8 +54,8 @@ def phrases_are_interchangable( |
| 56 | 54 |
else bs.rstrip(b'\x00') |
| 57 | 55 |
) |
| 58 | 56 |
|
| 59 |
- phrase1 = canon(Vault._get_binary_string(phrase1)) |
|
| 60 |
- phrase2 = canon(Vault._get_binary_string(phrase2)) |
|
| 57 |
+ phrase1 = canon(vault.Vault._get_binary_string(phrase1)) |
|
| 58 |
+ phrase2 = canon(vault.Vault._get_binary_string(phrase2)) |
|
| 61 | 59 |
return phrase1 == phrase2 |
| 62 | 60 |
|
| 63 | 61 |
|
| ... | ... |
@@ -83,9 +81,7 @@ class TestVault: |
| 83 | 81 |
min_size=2, |
| 84 | 82 |
max_size=2, |
| 85 | 83 |
unique=True, |
| 86 |
- ).filter( |
|
| 87 |
- lambda tup: not phrases_are_interchangable(*tup) |
|
| 88 |
- ), |
|
| 84 |
+ ).filter(lambda tup: not phrases_are_interchangable(*tup)), |
|
| 89 | 85 |
service=strategies.text( |
| 90 | 86 |
strategies.characters(min_codepoint=32, max_codepoint=126), |
| 91 | 87 |
min_size=1, |
| ... | ... |
@@ -102,9 +98,9 @@ class TestVault: |
| 102 | 98 |
We filter out interchangable passphrases during generation. |
| 103 | 99 |
|
| 104 | 100 |
""" |
| 105 |
- assert Vault.create_hash( |
|
| 101 |
+ assert vault.Vault.create_hash( |
|
| 106 | 102 |
phrase=phrases[0], service=service |
| 107 |
- ) != Vault.create_hash(phrase=phrases[1], service=service) |
|
| 103 |
+ ) != vault.Vault.create_hash(phrase=phrases[1], service=service) |
|
| 108 | 104 |
|
| 109 | 105 |
@hypothesis.given( |
| 110 | 106 |
phrases=strategies.lists( |
| ... | ... |
@@ -112,9 +108,7 @@ class TestVault: |
| 112 | 108 |
min_size=2, |
| 113 | 109 |
max_size=2, |
| 114 | 110 |
unique=True, |
| 115 |
- ).filter( |
|
| 116 |
- lambda tup: not phrases_are_interchangable(*tup) |
|
| 117 |
- ), |
|
| 111 |
+ ).filter(lambda tup: not phrases_are_interchangable(*tup)), |
|
| 118 | 112 |
service=strategies.text( |
| 119 | 113 |
strategies.characters(min_codepoint=32, max_codepoint=126), |
| 120 | 114 |
min_size=1, |
| ... | ... |
@@ -131,9 +125,9 @@ class TestVault: |
| 131 | 125 |
We filter out interchangable passphrases during generation. |
| 132 | 126 |
|
| 133 | 127 |
""" |
| 134 |
- assert Vault.create_hash( |
|
| 128 |
+ assert vault.Vault.create_hash( |
|
| 135 | 129 |
phrase=phrases[0], service=service |
| 136 |
- ) != Vault.create_hash(phrase=phrases[1], service=service) |
|
| 130 |
+ ) != vault.Vault.create_hash(phrase=phrases[1], service=service) |
|
| 137 | 131 |
|
| 138 | 132 |
@hypothesis.given( |
| 139 | 133 |
phrases=strategies.lists( |
| ... | ... |
@@ -143,9 +137,7 @@ class TestVault: |
| 143 | 137 |
min_size=2, |
| 144 | 138 |
max_size=2, |
| 145 | 139 |
unique=True, |
| 146 |
- ).filter( |
|
| 147 |
- lambda tup: not phrases_are_interchangable(*tup) |
|
| 148 |
- ), |
|
| 140 |
+ ).filter(lambda tup: not phrases_are_interchangable(*tup)), |
|
| 149 | 141 |
service=strategies.text( |
| 150 | 142 |
strategies.characters(min_codepoint=32, max_codepoint=126), |
| 151 | 143 |
min_size=1, |
| ... | ... |
@@ -162,9 +154,9 @@ class TestVault: |
| 162 | 154 |
We filter out interchangable passphrases during generation. |
| 163 | 155 |
|
| 164 | 156 |
""" |
| 165 |
- assert Vault.create_hash( |
|
| 157 |
+ assert vault.Vault.create_hash( |
|
| 166 | 158 |
phrase=phrases[0], service=service |
| 167 |
- ) != Vault.create_hash(phrase=phrases[1], service=service) |
|
| 159 |
+ ) != vault.Vault.create_hash(phrase=phrases[1], service=service) |
|
| 168 | 160 |
|
| 169 | 161 |
@hypothesis.given( |
| 170 | 162 |
phrases=strategies.lists( |
| ... | ... |
@@ -178,9 +170,7 @@ class TestVault: |
| 178 | 170 |
min_size=2, |
| 179 | 171 |
max_size=2, |
| 180 | 172 |
unique=True, |
| 181 |
- ).filter( |
|
| 182 |
- lambda tup: not phrases_are_interchangable(*tup) |
|
| 183 |
- ), |
|
| 173 |
+ ).filter(lambda tup: not phrases_are_interchangable(*tup)), |
|
| 184 | 174 |
service=strategies.text( |
| 185 | 175 |
strategies.characters(min_codepoint=32, max_codepoint=126), |
| 186 | 176 |
min_size=1, |
| ... | ... |
@@ -197,9 +187,9 @@ class TestVault: |
| 197 | 187 |
We filter out interchangable passphrases during generation. |
| 198 | 188 |
|
| 199 | 189 |
""" |
| 200 |
- assert Vault.create_hash( |
|
| 190 |
+ assert vault.Vault.create_hash( |
|
| 201 | 191 |
phrase=phrases[0], service=service |
| 202 |
- ) != Vault.create_hash(phrase=phrases[1], service=service) |
|
| 192 |
+ ) != vault.Vault.create_hash(phrase=phrases[1], service=service) |
|
| 203 | 193 |
|
| 204 | 194 |
@hypothesis.given( |
| 205 | 195 |
phrase=strategies.text( |
| ... | ... |
@@ -220,9 +210,9 @@ class TestVault: |
| 220 | 210 |
services: list[bytes], |
| 221 | 211 |
) -> None: |
| 222 | 212 |
"""The internal hash is dependent on the service name.""" |
| 223 |
- assert Vault.create_hash( |
|
| 213 |
+ assert vault.Vault.create_hash( |
|
| 224 | 214 |
phrase=phrase, service=services[0] |
| 225 |
- ) != Vault.create_hash(phrase=phrase, service=services[1]) |
|
| 215 |
+ ) != vault.Vault.create_hash(phrase=phrase, service=services[1]) |
|
| 226 | 216 |
|
| 227 | 217 |
@tests.hypothesis_settings_coverage_compatible |
| 228 | 218 |
@hypothesis.given( |
| ... | ... |
@@ -232,7 +222,7 @@ class TestVault: |
| 232 | 222 |
strategies.integers( |
| 233 | 223 |
min_value=1, |
| 234 | 224 |
max_value=BLOCK_SIZE - len(bs), |
| 235 |
- ).map(lambda num: bs + b'\x00' * num) |
|
| 225 |
+ ).map(lambda num: bs + b'\x00' * num), |
|
| 236 | 226 |
) |
| 237 | 227 |
), |
| 238 | 228 |
service=strategies.text( |
| ... | ... |
@@ -247,10 +237,10 @@ class TestVault: |
| 247 | 237 |
service: str, |
| 248 | 238 |
) -> None: |
| 249 | 239 |
"""Claimed interchangable passphrases are actually interchangable.""" |
| 250 |
- assert Vault.phrases_are_interchangable(*phrases) |
|
| 251 |
- assert Vault.create_hash( |
|
| 240 |
+ assert vault.Vault.phrases_are_interchangable(*phrases) |
|
| 241 |
+ assert vault.Vault.create_hash( |
|
| 252 | 242 |
phrase=phrases[0], service=service |
| 253 |
- ) == Vault.create_hash(phrase=phrases[1], service=service) |
|
| 243 |
+ ) == vault.Vault.create_hash(phrase=phrases[1], service=service) |
|
| 254 | 244 |
|
| 255 | 245 |
@tests.hypothesis_settings_coverage_compatible |
| 256 | 246 |
@hypothesis.given( |
| ... | ... |
@@ -279,10 +269,10 @@ class TestVault: |
| 279 | 269 |
service: str, |
| 280 | 270 |
) -> None: |
| 281 | 271 |
"""Claimed interchangable passphrases are actually interchangable.""" |
| 282 |
- assert Vault.phrases_are_interchangable(*phrases) |
|
| 283 |
- assert Vault.create_hash( |
|
| 272 |
+ assert vault.Vault.phrases_are_interchangable(*phrases) |
|
| 273 |
+ assert vault.Vault.create_hash( |
|
| 284 | 274 |
phrase=phrases[0], service=service |
| 285 |
- ) == Vault.create_hash(phrase=phrases[1], service=service) |
|
| 275 |
+ ) == vault.Vault.create_hash(phrase=phrases[1], service=service) |
|
| 286 | 276 |
|
| 287 | 277 |
@pytest.mark.parametrize( |
| 288 | 278 |
['service', 'expected'], |
| ... | ... |
@@ -295,12 +285,12 @@ class TestVault: |
| 295 | 285 |
self, service: bytes | str, expected: bytes |
| 296 | 286 |
) -> None: |
| 297 | 287 |
"""Deriving a passphrase principally works.""" |
| 298 |
- assert Vault(phrase=self.phrase).generate(service) == expected |
|
| 288 |
+ assert vault.Vault(phrase=self.phrase).generate(service) == expected |
|
| 299 | 289 |
|
| 300 | 290 |
def test_201_phrase_dependence(self) -> None: |
| 301 | 291 |
"""The derived passphrase is dependent on the master passphrase.""" |
| 302 | 292 |
assert ( |
| 303 |
- Vault(phrase=(self.phrase + b'X')).generate('google')
|
|
| 293 |
+ vault.Vault(phrase=(self.phrase + b'X')).generate('google')
|
|
| 304 | 294 |
== b'n+oIz6sL>K*lTEWYRO%7' |
| 305 | 295 |
) |
| 306 | 296 |
|
| ... | ... |
@@ -310,9 +300,7 @@ class TestVault: |
| 310 | 300 |
min_size=2, |
| 311 | 301 |
max_size=2, |
| 312 | 302 |
unique=True, |
| 313 |
- ).filter( |
|
| 314 |
- lambda tup: not phrases_are_interchangable(*tup) |
|
| 315 |
- ), |
|
| 303 |
+ ).filter(lambda tup: not phrases_are_interchangable(*tup)), |
|
| 316 | 304 |
service=strategies.text( |
| 317 | 305 |
strategies.characters(min_codepoint=32, max_codepoint=126), |
| 318 | 306 |
min_size=1, |
| ... | ... |
@@ -348,27 +336,27 @@ class TestVault: |
| 348 | 336 |
"""The derived passphrase is dependent on the master passphrase. |
| 349 | 337 |
|
| 350 | 338 |
Certain pairs of master passphrases are known to be |
| 351 |
- interchangable; see [`Vault.phrases_are_interchangable`][]. |
|
| 339 |
+ interchangable; see [`vault.Vault.phrases_are_interchangable`][]. |
|
| 352 | 340 |
These are excluded from consideration by the hypothesis |
| 353 | 341 |
strategy. |
| 354 | 342 |
|
| 355 | 343 |
""" |
| 356 | 344 |
# See test_100_create_hash_phrase_dependence for context. |
| 357 |
- assert Vault(phrase=phrases[0]).generate( |
|
| 358 |
- service |
|
| 359 |
- ) != Vault(phrase=phrases[1]).generate(service) |
|
| 345 |
+ assert vault.Vault(phrase=phrases[0]).generate(service) != vault.Vault( |
|
| 346 |
+ phrase=phrases[1] |
|
| 347 |
+ ).generate(service) |
|
| 360 | 348 |
|
| 361 | 349 |
def test_202a_reproducibility_and_bytes_service_name(self) -> None: |
| 362 | 350 |
"""Deriving a passphrase works equally for byte strings.""" |
| 363 |
- assert Vault(phrase=self.phrase).generate(b'google') == Vault( |
|
| 364 |
- phrase=self.phrase |
|
| 365 |
- ).generate('google')
|
|
| 351 |
+ assert vault.Vault(phrase=self.phrase).generate( |
|
| 352 |
+ b'google' |
|
| 353 |
+ ) == vault.Vault(phrase=self.phrase).generate('google')
|
|
| 366 | 354 |
|
| 367 | 355 |
def test_202b_reproducibility_and_bytearray_service_name(self) -> None: |
| 368 | 356 |
"""Deriving a passphrase works equally for byte arrays.""" |
| 369 |
- assert Vault(phrase=self.phrase).generate(b'google') == Vault( |
|
| 370 |
- phrase=self.phrase |
|
| 371 |
- ).generate(bytearray(b'google')) |
|
| 357 |
+ assert vault.Vault(phrase=self.phrase).generate( |
|
| 358 |
+ b'google' |
|
| 359 |
+ ) == vault.Vault(phrase=self.phrase).generate(bytearray(b'google')) |
|
| 372 | 360 |
|
| 373 | 361 |
@hypothesis.given( |
| 374 | 362 |
phrase=strategies.text( |
| ... | ... |
@@ -388,10 +376,10 @@ class TestVault: |
| 388 | 376 |
service: str, |
| 389 | 377 |
) -> None: |
| 390 | 378 |
"""Deriving a passphrase works equally for byte arrays/strings.""" |
| 391 |
- assert Vault(phrase=phrase).generate(service) == Vault( |
|
| 379 |
+ assert vault.Vault(phrase=phrase).generate(service) == vault.Vault( |
|
| 392 | 380 |
phrase=phrase |
| 393 | 381 |
).generate(service.encode('utf-8'))
|
| 394 |
- assert Vault(phrase=phrase).generate(service) == Vault( |
|
| 382 |
+ assert vault.Vault(phrase=phrase).generate(service) == vault.Vault( |
|
| 395 | 383 |
phrase=phrase |
| 396 | 384 |
).generate(bytearray(service.encode('utf-8')))
|
| 397 | 385 |
|
| ... | ... |
@@ -414,9 +402,9 @@ class TestVault: |
| 414 | 402 |
services: list[bytes], |
| 415 | 403 |
) -> None: |
| 416 | 404 |
"""The derived passphrase is dependent on the service name.""" |
| 417 |
- assert Vault(phrase=phrase).generate( |
|
| 418 |
- services[0] |
|
| 419 |
- ) != Vault(phrase=phrase).generate(services[1]) |
|
| 405 |
+ assert vault.Vault(phrase=phrase).generate(services[0]) != vault.Vault( |
|
| 406 |
+ phrase=phrase |
|
| 407 |
+ ).generate(services[1]) |
|
| 420 | 408 |
|
| 421 | 409 |
@tests.hypothesis_settings_coverage_compatible |
| 422 | 410 |
@hypothesis.given( |
| ... | ... |
@@ -441,9 +429,9 @@ class TestVault: |
| 441 | 429 |
) -> None: |
| 442 | 430 |
"""The derived passphrase is dependent on the service name.""" |
| 443 | 431 |
try: |
| 444 |
- assert Vault(phrase=phrase, **config).generate( |
|
| 432 |
+ assert vault.Vault(phrase=phrase, **config).generate( |
|
| 445 | 433 |
services[0] |
| 446 |
- ) != Vault(phrase=phrase, **config).generate(services[1]) |
|
| 434 |
+ ) != vault.Vault(phrase=phrase, **config).generate(services[1]) |
|
| 447 | 435 |
except ValueError as exc: |
| 448 | 436 |
# The service configuration strategy attempts to only |
| 449 | 437 |
# generate satisfiable configurations. It is possible, |
| ... | ... |
@@ -458,7 +446,8 @@ class TestVault: |
| 458 | 446 |
def test_210_nonstandard_length(self) -> None: |
| 459 | 447 |
"""Deriving a passphrase adheres to imposed length limits.""" |
| 460 | 448 |
assert ( |
| 461 |
- Vault(phrase=self.phrase, length=4).generate('google') == b'xDFu'
|
|
| 449 |
+ vault.Vault(phrase=self.phrase, length=4).generate('google')
|
|
| 450 |
+ == b'xDFu' |
|
| 462 | 451 |
) |
| 463 | 452 |
|
| 464 | 453 |
@tests.hypothesis_settings_coverage_compatible |
| ... | ... |
@@ -481,13 +470,13 @@ class TestVault: |
| 481 | 470 |
service: str, |
| 482 | 471 |
) -> None: |
| 483 | 472 |
"""Derived passphrases have the requested length.""" |
| 484 |
- password = Vault(phrase=phrase, length=length).generate(service) |
|
| 473 |
+ password = vault.Vault(phrase=phrase, length=length).generate(service) |
|
| 485 | 474 |
assert len(password) == length |
| 486 | 475 |
|
| 487 | 476 |
def test_211_repetition_limit(self) -> None: |
| 488 | 477 |
"""Deriving a passphrase adheres to imposed repetition limits.""" |
| 489 | 478 |
assert ( |
| 490 |
- Vault( |
|
| 479 |
+ vault.Vault( |
|
| 491 | 480 |
phrase=b'', length=24, symbol=0, number=0, repeat=1 |
| 492 | 481 |
).generate('asd')
|
| 493 | 482 |
== b'IVTDzACftqopUXqDHPkuCIhV' |
| ... | ... |
@@ -496,14 +485,14 @@ class TestVault: |
| 496 | 485 |
def test_212_without_symbols(self) -> None: |
| 497 | 486 |
"""Deriving a passphrase adheres to imposed limits on symbols.""" |
| 498 | 487 |
assert ( |
| 499 |
- Vault(phrase=self.phrase, symbol=0).generate('google')
|
|
| 488 |
+ vault.Vault(phrase=self.phrase, symbol=0).generate('google')
|
|
| 500 | 489 |
== b'XZ4wRe0bZCazbljCaMqR' |
| 501 | 490 |
) |
| 502 | 491 |
|
| 503 | 492 |
def test_213_no_numbers(self) -> None: |
| 504 | 493 |
"""Deriving a passphrase adheres to imposed limits on numbers.""" |
| 505 | 494 |
assert ( |
| 506 |
- Vault(phrase=self.phrase, number=0).generate('google')
|
|
| 495 |
+ vault.Vault(phrase=self.phrase, number=0).generate('google')
|
|
| 507 | 496 |
== b'_*$TVH.%^aZl(LUeOT?>' |
| 508 | 497 |
) |
| 509 | 498 |
|
| ... | ... |
@@ -512,28 +501,30 @@ class TestVault: |
| 512 | 501 |
Deriving a passphrase adheres to imposed limits on lowercase letters. |
| 513 | 502 |
""" |
| 514 | 503 |
assert ( |
| 515 |
- Vault(phrase=self.phrase, lower=0).generate('google')
|
|
| 504 |
+ vault.Vault(phrase=self.phrase, lower=0).generate('google')
|
|
| 516 | 505 |
== b':{?)+7~@OA:L]!0E$)(+'
|
| 517 | 506 |
) |
| 518 | 507 |
|
| 519 | 508 |
def test_215_at_least_5_digits(self) -> None: |
| 520 | 509 |
"""Deriving a passphrase adheres to imposed counts of numbers.""" |
| 521 | 510 |
assert ( |
| 522 |
- Vault(phrase=self.phrase, length=8, number=5).generate('songkick')
|
|
| 511 |
+ vault.Vault(phrase=self.phrase, length=8, number=5).generate( |
|
| 512 |
+ 'songkick' |
|
| 513 |
+ ) |
|
| 523 | 514 |
== b'i0908.7[' |
| 524 | 515 |
) |
| 525 | 516 |
|
| 526 | 517 |
def test_216_lots_of_spaces(self) -> None: |
| 527 | 518 |
"""Deriving a passphrase adheres to imposed counts of spaces.""" |
| 528 | 519 |
assert ( |
| 529 |
- Vault(phrase=self.phrase, space=12).generate('songkick')
|
|
| 520 |
+ vault.Vault(phrase=self.phrase, space=12).generate('songkick')
|
|
| 530 | 521 |
== b' c 6 Bq % 5fR ' |
| 531 | 522 |
) |
| 532 | 523 |
|
| 533 | 524 |
def test_217_all_character_classes(self) -> None: |
| 534 | 525 |
"""Deriving a passphrase adheres to imposed counts of all types.""" |
| 535 | 526 |
assert ( |
| 536 |
- Vault( |
|
| 527 |
+ vault.Vault( |
|
| 537 | 528 |
phrase=self.phrase, |
| 538 | 529 |
lower=2, |
| 539 | 530 |
upper=2, |
| ... | ... |
@@ -603,7 +594,7 @@ class TestVault: |
| 603 | 594 |
) -> None: |
| 604 | 595 |
"""Derived passphrases obey character and occurrence restraints.""" |
| 605 | 596 |
try: |
| 606 |
- password = Vault(phrase=phrase, **config).generate(service) |
|
| 597 |
+ password = vault.Vault(phrase=phrase, **config).generate(service) |
|
| 607 | 598 |
except ValueError as exc: |
| 608 | 599 |
# The service configuration strategy attempts to only |
| 609 | 600 |
# generate satisfiable configurations. It is possible, |
| ... | ... |
@@ -619,7 +610,7 @@ class TestVault: |
| 619 | 610 |
for key in ('lower', 'upper', 'number', 'space', 'dash', 'symbol'):
|
| 620 | 611 |
if config[key] > 0: |
| 621 | 612 |
assert ( |
| 622 |
- sum(c in Vault._CHARSETS[key] for c in password) |
|
| 613 |
+ sum(c in vault.Vault._CHARSETS[key] for c in password) |
|
| 623 | 614 |
>= config[key] |
| 624 | 615 |
), ( |
| 625 | 616 |
'Password does not satisfy ' |
| ... | ... |
@@ -630,9 +621,9 @@ class TestVault: |
| 630 | 621 |
# appear via the other character class. |
| 631 | 622 |
assert True |
| 632 | 623 |
else: |
| 633 |
- assert sum(c in Vault._CHARSETS[key] for c in password) == 0, ( |
|
| 634 |
- 'Password does not satisfy character ban constraints.' |
|
| 635 |
- ) |
|
| 624 |
+ assert ( |
|
| 625 |
+ sum(c in vault.Vault._CHARSETS[key] for c in password) == 0 |
|
| 626 |
+ ), 'Password does not satisfy character ban constraints.' |
|
| 636 | 627 |
|
| 637 | 628 |
T = TypeVar('T', str, bytes)
|
| 638 | 629 |
|
| ... | ... |
@@ -653,7 +644,7 @@ class TestVault: |
| 653 | 644 |
This example is checked explicitly against forbidden substrings. |
| 654 | 645 |
|
| 655 | 646 |
""" |
| 656 |
- generated = Vault( |
|
| 647 |
+ generated = vault.Vault( |
|
| 657 | 648 |
phrase=b'', |
| 658 | 649 |
length=40, |
| 659 | 650 |
lower=0, |
| ... | ... |
@@ -702,22 +693,22 @@ class TestVault: |
| 702 | 693 |
service: str, |
| 703 | 694 |
) -> None: |
| 704 | 695 |
"""Derived passphrases obey the given occurrence constraint.""" |
| 705 |
- password = Vault(phrase=phrase, length=length, repeat=repeat).generate( |
|
| 706 |
- service |
|
| 707 |
- ) |
|
| 696 |
+ password = vault.Vault( |
|
| 697 |
+ phrase=phrase, length=length, repeat=repeat |
|
| 698 |
+ ).generate(service) |
|
| 708 | 699 |
for i in range((length + 1) - (repeat + 1)): |
| 709 | 700 |
assert len(set(password[i : i + repeat + 1])) > 1 |
| 710 | 701 |
|
| 711 | 702 |
def test_219_very_limited_character_set(self) -> None: |
| 712 | 703 |
"""Deriving a passphrase works even with limited character sets.""" |
| 713 |
- generated = Vault( |
|
| 704 |
+ generated = vault.Vault( |
|
| 714 | 705 |
phrase=b'', length=24, lower=0, upper=0, space=0, symbol=0 |
| 715 | 706 |
).generate('testing')
|
| 716 | 707 |
assert generated == b'763252593304946694588866' |
| 717 | 708 |
|
| 718 | 709 |
def test_220_character_set_subtraction(self) -> None: |
| 719 | 710 |
"""Removing allowed characters internally works.""" |
| 720 |
- assert Vault._subtract(b'be', b'abcdef') == bytearray(b'acdf') |
|
| 711 |
+ assert vault.Vault._subtract(b'be', b'abcdef') == bytearray(b'acdf') |
|
| 721 | 712 |
|
| 722 | 713 |
@pytest.mark.parametrize( |
| 723 | 714 |
['length', 'settings', 'entropy'], |
| ... | ... |
@@ -742,7 +733,7 @@ class TestVault: |
| 742 | 733 |
self, length: int, settings: dict[str, int], entropy: int |
| 743 | 734 |
) -> None: |
| 744 | 735 |
"""Estimating the entropy and sufficient hash length works.""" |
| 745 |
- v = Vault(length=length, **settings) # type: ignore[arg-type] |
|
| 736 |
+ v = vault.Vault(length=length, **settings) # type: ignore[arg-type] |
|
| 746 | 737 |
assert math.isclose(v._entropy(), entropy) |
| 747 | 738 |
assert v._estimate_sufficient_hash_length() > 0 |
| 748 | 739 |
if math.isfinite(entropy) and entropy: |
| ... | ... |
@@ -755,7 +746,7 @@ class TestVault: |
| 755 | 746 |
""" |
| 756 | 747 |
Estimating the entropy and hash length for degenerate cases works. |
| 757 | 748 |
""" |
| 758 |
- v = Vault( |
|
| 749 |
+ v = vault.Vault( |
|
| 759 | 750 |
phrase=self.phrase, |
| 760 | 751 |
lower=0, |
| 761 | 752 |
upper=0, |
| ... | ... |
@@ -783,7 +774,7 @@ class TestVault: |
| 783 | 774 |
""" |
| 784 | 775 |
Estimating the entropy and hash length for the degenerate case works. |
| 785 | 776 |
""" |
| 786 |
- v = Vault(phrase=self.phrase) |
|
| 777 |
+ v = vault.Vault(phrase=self.phrase) |
|
| 787 | 778 |
monkeypatch.setattr( |
| 788 | 779 |
v, |
| 789 | 780 |
'_estimate_sufficient_hash_length', |
| ... | ... |
@@ -805,7 +796,7 @@ class TestVault: |
| 805 | 796 |
) |
| 806 | 797 |
def test_224_binary_strings(self, s: str | bytes | bytearray) -> None: |
| 807 | 798 |
"""Byte string conversion is idempotent.""" |
| 808 |
- binstr = Vault._get_binary_string |
|
| 799 |
+ binstr = vault.Vault._get_binary_string |
|
| 809 | 800 |
if isinstance(s, str): |
| 810 | 801 |
assert binstr(s) == s.encode('UTF-8')
|
| 811 | 802 |
assert binstr(binstr(s)) == s.encode('UTF-8')
|
| ... | ... |
@@ -818,12 +809,12 @@ class TestVault: |
| 818 | 809 |
with pytest.raises( |
| 819 | 810 |
ValueError, match='requested passphrase length too short' |
| 820 | 811 |
): |
| 821 |
- Vault(phrase=self.phrase, symbol=100) |
|
| 812 |
+ vault.Vault(phrase=self.phrase, symbol=100) |
|
| 822 | 813 |
|
| 823 | 814 |
def test_311_no_viable_characters(self) -> None: |
| 824 | 815 |
"""Deriving passphrases without allowed characters fails.""" |
| 825 | 816 |
with pytest.raises(ValueError, match='no allowed characters left'): |
| 826 |
- Vault( |
|
| 817 |
+ vault.Vault( |
|
| 827 | 818 |
phrase=self.phrase, |
| 828 | 819 |
lower=0, |
| 829 | 820 |
upper=0, |
| ... | ... |
@@ -836,13 +827,13 @@ class TestVault: |
| 836 | 827 |
def test_320_character_set_subtraction_duplicate(self) -> None: |
| 837 | 828 |
"""Character sets do not contain duplicate characters.""" |
| 838 | 829 |
with pytest.raises(ValueError, match='duplicate characters'): |
| 839 |
- Vault._subtract(b'abcdef', b'aabbccddeeff') |
|
| 830 |
+ vault.Vault._subtract(b'abcdef', b'aabbccddeeff') |
|
| 840 | 831 |
with pytest.raises(ValueError, match='duplicate characters'): |
| 841 |
- Vault._subtract(b'aabbccddeeff', b'abcdef') |
|
| 832 |
+ vault.Vault._subtract(b'aabbccddeeff', b'abcdef') |
|
| 842 | 833 |
|
| 843 | 834 |
def test_322_hash_length_estimation(self) -> None: |
| 844 | 835 |
"""Hash length estimation rejects invalid safety factors.""" |
| 845 |
- v = Vault(phrase=self.phrase) |
|
| 836 |
+ v = vault.Vault(phrase=self.phrase) |
|
| 846 | 837 |
with pytest.raises(ValueError, match='invalid safety factor'): |
| 847 | 838 |
assert v._estimate_sufficient_hash_length(-1.0) |
| 848 | 839 |
with pytest.raises( |
| 849 | 840 |