WOTS+ hash-based signatures in Python with optional Rust acceleration
Project description
hashsigs (Python)
Python package for WOTS+ with optional Rust acceleration from hashsigs-rs.
Installation
pip install hashsigs
For best performance, ensure you have Rust installed (the package will automatically build the Rust extension if available):
# Install Rust toolchain (optional, for better performance)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Then install hashsigs
pip install hashsigs
Quick Usage
import hashsigs
# Create a WOTS+ instance
wots = hashsigs.WOTSPlus()
# Generate a key pair
private_key, public_key = wots.generate_key_pair()
# Sign a message
message = b"Hello, world!"
signature = wots.sign(private_key, message)
# Verify the signature
is_valid = wots.verify(public_key, message, signature)
print(f"Signature valid: {is_valid}") # True
# Check if Rust acceleration is available
try:
import hashsigs._rust
print("Using Rust acceleration")
except ImportError:
print("Using pure Python implementation")
Development Setup
For contributors and advanced users who want to build from source:
# from the repo root
python3 -m venv .hashsigs
source .hashsigs/bin/activate
python -m pip install -U pip pytest pytest-cov
# Ensure Rust toolchain (required to build the optional extension)
# macOS: also ensure Xcode CLT: xcode-select --install
rustup --version || brew install rust
cargo --version
# Install dev deps and try to build rust extension (quote extras in zsh)
pip install -v -e '.[rust,dev]'
# Install keccak provider (required for vectors when falling back to Python)
pip install pycryptodome
# or: pip install pysha3
# Quick check the extension is present (optional)
python -c "import hashsigs._rust as m; print('rust ext ok:', m)"
# Lint + type + tests with coverage; this is the full suite
pytest -q --cov=hashsigs --cov-report=term-missing
If the Rust toolchain is not available, the above will fail on the rust-backed vector tests. See the “Test options” section for alternatives.
Test options
You can choose between three categories:
-
All Tests (vectors + rust-backed + lint + type + coverage) — fails if keccak or rust ext are missing
- pip install -e '.[rust,dev]' && pip install pycryptodome
- pytest -q
-
Basic (pure Python functionality only; requires keccak provider, but no Rust extension)
- pip install -e '.[dev]' pycryptodome
- HASHSIGS_BUILD_RUST=0 pytest -q -m "not requires_rust"
-
Basic (no keccak) — internal consistency tests only using hashlib.sha3_256; no vectors
- pip install -e '.[dev]'
- pytest -q -m "not vectors"
Troubleshooting
-
pysha3 build fails on Python 3.13 (macOS) with missing pystrhex.h
- Symptom: fatal error: 'pystrhex.h' file not found when building _pysha3
- Fix: install pycryptodome instead (preferred). Example: pip install pycryptodome
- Alternative: use Python 3.11/3.12 where pysha3 wheels may exist, or wait for pysha3 to add 3.13 support
-
Rust extension won’t build/import
- Ensure Rust toolchain is installed: curl https://sh.rustup.rs -sSf | sh (or brew install rust)
- On macOS, ensure Xcode Command Line Tools are installed: xcode-select --install
- Clean and rebuild in your venv:
- pip uninstall -y hashsigs; pip install -e .[rust,dev]
- python -c "import hashsigs._rust as m; print('rust ext ok', m)"
- If it still fails, you can run Basic tests while you investigate: HASHSIGS_BUILD_RUST=0 pytest -q -m "not requires_rust"
Usage example
from hashsigs import WOTSPlus
# Prefer the Rust backend if available; falls back to Python keccak provider if not
wots = WOTSPlus.keccak256(prefer_rust=True)
# Derive a keypair from a 32-byte seed
seed = bytes([1]) * 32
pk, sk = wots.generate_key_pair(seed)
# Sign and verify a 32-byte message
msg = bytes([2]) * 32
sig = wots.sign(sk, msg)
assert wots.verify(pk, msg, sig)
print("Signature verifies!")
Quick self-check (from shell)
Run a one-liner to confirm the package imports, the Rust extension is available (optional), and basic operations work:
python - <<'PY'
from hashsigs import WOTSPlus
try:
import hashsigs._rust as _
print('Rust extension: available')
except Exception:
print('Rust extension: not available (falling back to Python)')
wots = WOTSPlus.keccak256(prefer_rust=True)
seed = bytes([1]) * 32
pk, sk = wots.generate_key_pair(seed)
msg = bytes([2]) * 32
sig = wots.sign(sk, msg)
print('verify:', wots.verify(pk, msg, sig))
PY
Rust backend
We now depend on the public crate and repository:
- Crate: hashsigs-rs = "0.0.2"
- Repo: https://github.com/QuipNetwork/hashsigs-rs
The Python bindings (PyO3) will attempt to build against the published crate during installation. If the build fails, the package still installs and falls back to pure Python.
License
AGPL-3.0-or-later; see COPYING
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file hashsigs-0.0.2-py3-none-any.whl.
File metadata
- Download URL: hashsigs-0.0.2-py3-none-any.whl
- Upload date:
- Size: 31.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a3f3373f79c2d49a7632461609ee9a49b3e3335eff89e99a4e6e53ae2383321d
|
|
| MD5 |
ce4093ed13dce829ca70c462f93061e9
|
|
| BLAKE2b-256 |
a61d20d8d41a5244d80fd30e6b2734a4e2ec5c2bbc66818b03e4b6802a689995
|
Provenance
The following attestation bundles were made for hashsigs-0.0.2-py3-none-any.whl:
Publisher:
publish.yml on QuipNetwork/hashsigs-py
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
hashsigs-0.0.2-py3-none-any.whl -
Subject digest:
a3f3373f79c2d49a7632461609ee9a49b3e3335eff89e99a4e6e53ae2383321d - Sigstore transparency entry: 440236896
- Sigstore integration time:
-
Permalink:
QuipNetwork/hashsigs-py@645ae0387ce5d8025ea82affd6b2c8b668a65ef4 -
Branch / Tag:
refs/tags/v0.0.2 - Owner: https://github.com/QuipNetwork
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@645ae0387ce5d8025ea82affd6b2c8b668a65ef4 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file hashsigs-0.0.2-cp311-cp311-win_amd64.whl.
File metadata
- Download URL: hashsigs-0.0.2-cp311-cp311-win_amd64.whl
- Upload date:
- Size: 158.1 kB
- Tags: CPython 3.11, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2e6ee3bec429cac8bdbbe03692c71b0dc9a2908caed100ae6eff28395235914c
|
|
| MD5 |
b19af35faa3cfa6a9b1f57da36fa2ed6
|
|
| BLAKE2b-256 |
9d8ab538954e4b61734f43669ce8beb9b16db1686a78355c3e55a5eee08c8439
|
Provenance
The following attestation bundles were made for hashsigs-0.0.2-cp311-cp311-win_amd64.whl:
Publisher:
publish.yml on QuipNetwork/hashsigs-py
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
hashsigs-0.0.2-cp311-cp311-win_amd64.whl -
Subject digest:
2e6ee3bec429cac8bdbbe03692c71b0dc9a2908caed100ae6eff28395235914c - Sigstore transparency entry: 440236907
- Sigstore integration time:
-
Permalink:
QuipNetwork/hashsigs-py@645ae0387ce5d8025ea82affd6b2c8b668a65ef4 -
Branch / Tag:
refs/tags/v0.0.2 - Owner: https://github.com/QuipNetwork
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@645ae0387ce5d8025ea82affd6b2c8b668a65ef4 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file hashsigs-0.0.2-cp311-cp311-macosx_11_0_universal2.whl.
File metadata
- Download URL: hashsigs-0.0.2-cp311-cp311-macosx_11_0_universal2.whl
- Upload date:
- Size: 261.8 kB
- Tags: CPython 3.11, macOS 11.0+ universal2 (ARM64, x86-64)
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9ec2657feffbfe106e2aca8f819993a0738bd2ff82ca74a58cde50ef40c5e2e9
|
|
| MD5 |
ee6cbe417b0c2b03b278a398e1c7d035
|
|
| BLAKE2b-256 |
e7bc905918e8974573efb49ed0dcc7de9cbfcb228b51dce5657118856a0bc15f
|
Provenance
The following attestation bundles were made for hashsigs-0.0.2-cp311-cp311-macosx_11_0_universal2.whl:
Publisher:
publish.yml on QuipNetwork/hashsigs-py
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
hashsigs-0.0.2-cp311-cp311-macosx_11_0_universal2.whl -
Subject digest:
9ec2657feffbfe106e2aca8f819993a0738bd2ff82ca74a58cde50ef40c5e2e9 - Sigstore transparency entry: 440236887
- Sigstore integration time:
-
Permalink:
QuipNetwork/hashsigs-py@645ae0387ce5d8025ea82affd6b2c8b668a65ef4 -
Branch / Tag:
refs/tags/v0.0.2 - Owner: https://github.com/QuipNetwork
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@645ae0387ce5d8025ea82affd6b2c8b668a65ef4 -
Trigger Event:
workflow_dispatch
-
Statement type: