diff --git a/src/codex32/generation.py b/src/codex32/generation.py index 551ef61..db1843f 100644 --- a/src/codex32/generation.py +++ b/src/codex32/generation.py @@ -21,6 +21,7 @@ ) from codex32.errors import ( CeremonyStateError, + CodexError, HeaderCollision, InvalidIdentifier, InvalidLength, @@ -155,6 +156,8 @@ def generate_master_seed( """Generate or encode one unshared ``ms`` secret.""" supplied, length = _seed_input(seed_bytes, byte_length) if supplied is not None: + if not _valid_root(supplied): + raise CodexError("master seed does not form a valid BIP32 root") identifier = _random_identifier() if identifier is None else _identifier(identifier) return MasterSeed.from_seed(supplied, identifier=identifier) if identifier is None and fingerprint is None: diff --git a/tests/test_generation.py b/tests/test_generation.py index 44cd688..289662b 100644 --- a/tests/test_generation.py +++ b/tests/test_generation.py @@ -25,6 +25,7 @@ from codex32.bech32 import _u5_to_chars from codex32.errors import ( CeremonyStateError, + CodexError, HeaderCollision, InvalidIdentifier, InvalidLength, @@ -139,6 +140,12 @@ def test_raw_bytes_accept_random_or_explicit_identifiers() -> None: assert secret.header.identifier == "test" +def test_supplied_seed_must_form_a_valid_bip32_root(monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.setattr(generation_module, "_valid_root", lambda _seed: False) + with pytest.raises(CodexError, match="master seed does not form a valid BIP32 root"): + generate_master_seed(bytes(16), identifier="test") + + def test_explicit_and_random_output_order_contracts() -> None: source = generate_master_seed(bytes(range(16)), identifier="test") _secret, shares = _complete(