From 75f6972a2677e40b35f91fe227008f20a0ee2a6d Mon Sep 17 00:00:00 2001 From: Codex Agent Date: Sat, 3 Oct 2026 05:45:46 -0500 Subject: [PATCH] ci: Remove Python BIP32 dependency Legacy master advertises Python 3.13, but bip32 pins coincurve below 21 and fails during dependency installation. The only production use of bip32 is deriving an implicit identifier in from_seed. Require an explicit four-character identifier in that API instead, remove bip32 from package and test requirements, and test the advertised Python 3.10 through 3.15 range in both workflows. Setup-python v5 enables the 3.15 prerelease job. --- .github/workflows/pylint.yml | 6 ++++-- .github/workflows/python-package.yml | 5 +++-- README.md | 8 +++++--- pyproject.toml | 6 ++++-- requirements.txt | 1 - src/codex32/bip93.py | 14 +++++--------- tests/test_bip93.py | 12 ++++++++++++ 7 files changed, 33 insertions(+), 19 deletions(-) diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml index 96b1dda..5840f5f 100644 --- a/.github/workflows/pylint.yml +++ b/.github/workflows/pylint.yml @@ -9,14 +9,16 @@ jobs: build: runs-on: ubuntu-latest strategy: + fail-fast: false matrix: - python-version: ["3.10", "3.11", "3.12", "3.13"] + python-version: ["3.10", "3.11", "3.12", "3.13", "3.14", "3.15"] steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v3 + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version: ${{ matrix.python-version }} + allow-prereleases: ${{ matrix.python-version == '3.15' }} - name: Install dependencies run: | python -m pip install --upgrade pip diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 8ed5f06..95fa4f0 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -19,14 +19,15 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.10", "3.11", "3.12", "3.13"] + python-version: ["3.10", "3.11", "3.12", "3.13", "3.14", "3.15"] steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v3 + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version: ${{ matrix.python-version }} + allow-prereleases: ${{ matrix.python-version == '3.15' }} - name: Install dependencies run: | python -m pip install --upgrade pip diff --git a/README.md b/README.md index 3507281..1aae028 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Shamir secret sharing scheme (SSSS) interpolation helpers and helpers to build c - Regular checksum (13 chars) and long checksum (15 chars) support. - Construct codex32 strings from raw seed bytes via `from_seed`. - CRC-based default padding scheme for `from_seed`. -- Default `from_seed` identifier is the bech32-encoded BIP32 fingerprint. +- `from_seed` requires an explicit four-character identifier in its prefix. - Interpolate/recover shares via `interpolate_at`. - Parse codex32 strings and access parts via properties. - Mutate codex32 strings by reassigning `is_upper`, `hrp`, `k`, `ident`, `share_idx`, `data`, and `pad_val`. @@ -21,9 +21,11 @@ Shamir secret sharing scheme (SSSS) interpolation helpers and helpers to build c ## Security Caution: This is reference code. Verify carefully before using with real funds. +For wallet backups, obtain the identifier from a trusted wallet record; +`from_seed` does not derive or verify a BIP32 fingerprint. ## Installation -**Compatibility:** Python 3.10–3.14 +**Compatibility:** Python 3.10–3.15 **Recommended:** use a virtual environment ### Linux / macOS @@ -47,7 +49,7 @@ from codex32 import Codex32String # Create from seed bytes s = Codex32String.from_seed( bytes.fromhex('ffeeddccbbaa99887766554433221100'), - "ms13cashs", # prefix string, (HRP + '1' + header) + "ms13cashs", # prefix string with explicit identifier (HRP + '1' + header) 0 # padding value (default "CRC", otherwise integer) ) print(s.s) # codex32 string diff --git a/pyproject.toml b/pyproject.toml index a2c4094..6ad3372 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,7 @@ authors = [ ] description = "Python reference implementation for codex32 (BIP93) and codex32-encoded master seeds." readme = "README.md" -requires-python = ">=3.10" +requires-python = ">=3.10,<3.16" license = "MIT" license-files = ["LICENSE*"] maintainers = [ @@ -26,11 +26,13 @@ classifiers = [ "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", + "Programming Language :: Python :: 3.15", "Operating System :: OS Independent", "Topic :: Security :: Cryptography", "Topic :: Software Development :: Libraries", ] -dependencies = ["bip32>=5.0.0"] +dependencies = [] [project.optional-dependencies] dev = ["pytest", "flake8", "mypy", "black", "isort", "ruff"] diff --git a/requirements.txt b/requirements.txt index 68fb66e..e079f8a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1 @@ -bip32 pytest diff --git a/src/codex32/bip93.py b/src/codex32/bip93.py index d36b42e..4fb4234 100644 --- a/src/codex32/bip93.py +++ b/src/codex32/bip93.py @@ -27,8 +27,6 @@ """Reference implementation for codex32/Long codex32 and codex32-encoded master seeds.""" -from bip32 import BIP32 - from codex32.bech32 import ( CHARSET, chars_to_u5, @@ -263,16 +261,14 @@ def interpolate_at( @classmethod def from_seed( - cls, data: bytes, prefix: str = "ms10", pad_val: int | str = "CRC" + cls, data: bytes, prefix: str, pad_val: int | str = "CRC" ) -> "Codex32String": - """Create Codex32String given prefix and bare seed data.""" + """Create Codex32String from seed bytes and a prefix with an identifier.""" hrp, data_part = u5_parse(prefix) header = u5_to_chars(data_part) k = "0" if not header else header[:1] - if not (ident := header[1 : max(5, len(header) - 1)]): - bip32_fingerprint = BIP32.from_seed(data).get_fingerprint() - ident = u5_to_chars(convertbits(bip32_fingerprint, 8, 5)[:4]) - elif len(ident) != 4: - raise IdNotLength4(f"identifier had wrong length {len(ident)}") + ident = header[1 : max(5, len(header) - 1)] + if len(ident) != 4: + raise IdNotLength4(f"prefix must contain a four-character identifier, got {len(ident)}") share_idx = "s" if not header[5:] else header[5:6] return cls(encode(hrp, k + ident + share_idx, data, pad_val)) diff --git a/tests/test_bip93.py b/tests/test_bip93.py index 8a5d15f..f466fec 100644 --- a/tests/test_bip93.py +++ b/tests/test_bip93.py @@ -1,5 +1,7 @@ # tests/test_bip93.py """Tests for BIP-93 codex32 implementation.""" +import inspect + import pytest from data.bip93_vectors import ( VECTOR_1, @@ -24,6 +26,7 @@ ) from codex32.bip93 import ( Codex32String, + IdNotLength4, InvalidSeedLength, MismatchedHrp, MismatchedLength, @@ -59,6 +62,15 @@ def test_parts(): assert s.data.hex() == VECTOR_1["secret_hex"] +def test_from_seed_requires_explicit_identifier(): + """Seed encoding must not infer an identifier or import BIP32.""" + seed = bytes.fromhex(VECTOR_3["secret_hex"]) + prefix = inspect.signature(Codex32String.from_seed).parameters["prefix"] + assert prefix.default is inspect.Parameter.empty + with pytest.raises(IdNotLength4, match="four-character identifier"): + Codex32String.from_seed(seed, "ms10") + + def test_derive_and_recover(): """Test Vector 2: derive new share and recover the secret""" a = Codex32String(VECTOR_2["share_A"])