test_seed_zeroing.py
python
sha256:2eaa5d95f9d9383498e76947410a26e5a3ba23d182f339910c424cf88fad412b
fix: try fetch/presign before fetch/mpack to avoid Cloudfla…
Sonnet 4.6
patch
7 days ago
| 1 | """Tests for mnemonic_to_seed memory zeroing. |
| 2 | |
| 3 | mnemonic_to_seed previously returned immutable ``bytes`` — the 64-byte root |
| 4 | seed lingered in the Python heap indefinitely with no way for callers to wipe |
| 5 | it. Because the seed is the master secret from which *every* derived key |
| 6 | flows, this is a critical residual-memory exposure. |
| 7 | |
| 8 | Fix: |
| 9 | - mnemonic_to_seed now returns bytearray so callers can zero it after use. |
| 10 | - All call sites in auth.py zero the seed bytearray immediately after the |
| 11 | last use (operator_seed, agent_sub_seed, seed). |
| 12 | |
| 13 | Coverage |
| 14 | -------- |
| 15 | I Return type is bytearray |
| 16 | I1 mnemonic_to_seed returns bytearray, not bytes |
| 17 | |
| 18 | II Callers can zero the returned value |
| 19 | II1 returned bytearray can be overwritten in-place |
| 20 | |
| 21 | III Correctness unaffected |
| 22 | III1 same inputs → same 64-byte output (determinism preserved) |
| 23 | III2 output length is always 64 bytes |
| 24 | III3 passphrase variant produces different seed (BIP39 spec) |
| 25 | """ |
| 26 | |
| 27 | from __future__ import annotations |
| 28 | |
| 29 | import pytest |
| 30 | |
| 31 | from muse.core.bip39 import mnemonic_to_seed |
| 32 | |
| 33 | _MNEMONIC = ( |
| 34 | "abandon abandon abandon abandon abandon abandon abandon abandon " |
| 35 | "abandon abandon abandon about" |
| 36 | ) |
| 37 | |
| 38 | |
| 39 | # --------------------------------------------------------------------------- |
| 40 | # I Return type is bytearray |
| 41 | # --------------------------------------------------------------------------- |
| 42 | |
| 43 | class TestReturnType: |
| 44 | def test_I1_returns_bytearray(self) -> None: |
| 45 | """I1: mnemonic_to_seed must return bytearray so callers can zero it.""" |
| 46 | result = mnemonic_to_seed(_MNEMONIC) |
| 47 | assert isinstance(result, bytearray), ( |
| 48 | f"Expected bytearray, got {type(result).__name__}" |
| 49 | ) |
| 50 | |
| 51 | |
| 52 | # --------------------------------------------------------------------------- |
| 53 | # II Callers can zero the returned value |
| 54 | # --------------------------------------------------------------------------- |
| 55 | |
| 56 | class TestCallerCanZero: |
| 57 | def test_II1_returned_bytearray_is_mutable(self) -> None: |
| 58 | """II1: the returned bytearray can be overwritten in place.""" |
| 59 | result = mnemonic_to_seed(_MNEMONIC) |
| 60 | assert any(b != 0 for b in result), "pre-condition: seed must not be all-zero" |
| 61 | result[:] = b"\x00" * len(result) |
| 62 | assert result == bytearray(64), "caller must be able to zero the returned bytearray" |
| 63 | |
| 64 | |
| 65 | # --------------------------------------------------------------------------- |
| 66 | # III Correctness unaffected |
| 67 | # --------------------------------------------------------------------------- |
| 68 | |
| 69 | class TestCorrectness: |
| 70 | def test_III1_deterministic(self) -> None: |
| 71 | """III1: same inputs always produce the same 64 bytes.""" |
| 72 | r1 = mnemonic_to_seed(_MNEMONIC) |
| 73 | r2 = mnemonic_to_seed(_MNEMONIC) |
| 74 | assert bytes(r1) == bytes(r2), "mnemonic_to_seed must be deterministic" |
| 75 | |
| 76 | def test_III2_length_64(self) -> None: |
| 77 | """III2: output is always exactly 64 bytes.""" |
| 78 | result = mnemonic_to_seed(_MNEMONIC) |
| 79 | assert len(result) == 64 |
| 80 | |
| 81 | def test_III3_passphrase_produces_different_seed(self) -> None: |
| 82 | """III3: a non-empty passphrase produces a completely different seed (BIP39 spec).""" |
| 83 | r1 = mnemonic_to_seed(_MNEMONIC) |
| 84 | r2 = mnemonic_to_seed(_MNEMONIC, passphrase="TREZOR") |
| 85 | assert bytes(r1) != bytes(r2), ( |
| 86 | "BIP39: different passphrases must produce different seeds" |
| 87 | ) |
File History
1 commit
sha256:2eaa5d95f9d9383498e76947410a26e5a3ba23d182f339910c424cf88fad412b
fix: try fetch/presign before fetch/mpack to avoid Cloudfla…
Sonnet 4.6
patch
7 days ago