DGSP is an efficient scalable post-quantum fully-dynamic group signature scheme implemented purely in Rust. It leverages the SPHINCS+ signature scheme to provide a secure, scalable, and efficient group signature mechanism that is future-proof against quantum adversaries.
DGSP supports:
- A vast user base of up to 260 users.
- Compact keypair and signature sizes.
- Efficient signing and verification processes.
- Proven security guarantees for correctness, unforgeability, anonymity, and traceability.
- Manager behavior can be judged.
This implementation is optimized for performance and modularity, making it a powerful choice for cryptographic research and real-world applications requiring long-term security.
To obtain more info about DGSP group signature scheme, please refer to the paper at: https://eprint.iacr.org/2025/760
The full API documentation is hosted at: https://seyyedarashazimi.github.io/dgsp/
It is automatically built from the source code.
To build and open the documentation locally from source:
cargo doc --no-deps --open- Key Generation: Manager and user key generation.
- Joining: Secure user onboarding via cryptographically generated identifiers.
- Signing: Message signing using efficient hash-based cryptographic primitives and pre-computed certificates.
- Verification: Signature verification for authenticity and validity.
- Opening: Ability to trace signatures to specific users by the manager without compromising anonymity for other parties.
- Revocation: Efficient revocation mechanism, including revoking a user, their corresponding signatures, and previously-generated certificates.
- Judge Manager: Evaluate manager behavior and make sure manager opens a given signature to an ID correctly.
- Efficient Hash-Based Cryptographic Operations:
- Takes benefit from SHA-2 and SHAKE-based variants as per SPHINCS+ parameters.
- SPHINCS+ Wrapper:
- Simplifies the use of SPHINCS+ by providing utilities for key generation, signing, and verification.
- WOTS+ (Winternitz One-Time Signature Plus):
- Serves as a base signing primitive for DGSP.
- Supports unique address derivation to ensure resistance against multi-target attacks.
- AES:
- Plays the role of a strong pseudorandom permutation for traceability.
- Is built on SPHINCS+, a stateless hash-based signature scheme.
- Is resistant to quantum adversaries.
- Provides user anonymity, unforgeability, traceability, and correctness.
- Ensures sensitive cryptographic material is securely wiped from memory when no longer needed by zeroizing them.
- Manager's behavior can be audited and judged.
- Handles up to 260 users.
- Addition and revocation of new users are seamless and efficient.
- Provides in-memory and in-disk storage backends.
- Parallelized operations using rayon crate for improved performance.
- No setup and initialization time needed.
-
PLMInterface (Private List Manager Interface):
- Stores user-related data such as usernames, activity status, and certificate counters.
- Provides functionality for adding new users, deactivating users, managing counters for issued certificates, and retrieving user information by ID or username.
- Supports in-memory and in-disk storage backends for flexibility.
- Decouples the DGSP from the storage implementation, enabling integration with other database systems.
-
RevokedListInterface:
- Manages the list of revoked certificates and ensures that revoked signatures are invalidated.
- Supports efficient insertion and checking of revoked certificates using optimized data structures.
- Designed to work seamlessly with both in-memory and in-disk storage systems.
- Allows the DGSP to operate independently of the storage implementation, supporting integration with various database systems.
The library itself provides in-memory and in-disk implementations for the above interfaces. However, one can implement these 2 interfaces, corresponding to their own database and needs.
We ran our tests on a computer with Ubuntu 24.04 using Rust 1.84.0 (stable) in release mode. The tests were done on an Intel® Core™ i7-4702MQ CPU at 2.20 GHz with 16 GiB of memory. To keep the results steady, we used just one processor core and turned off hyper-threading and turbo-boost. The results are the average of 100 test runs. Note that in reality, the most time-consuming operations like Gen Cert will be run in parallel as the code supports multi-threading.
All benchmark times are in milliseconds (ms).
| DB feature | in-memory | in-disk |
| GROUP SIZE | 2^10 | 2^25 | 2^10 | 2^25 |
| BATCH SIZE | 1 | 8 | 1 | 8 | 1 | 8 | 1 | 8 |
|----------------|--------|--------|--------|--------|--------|--------|--------|--------|
| Manager KeyGen | 3.0521 | 3.0534 | 3.0536 | 3.0545 | 3.0585 | 3.0594 | 3.0546 | 3.0558 |
| Join | 0.0030 | 0.0030 | 0.0029 | 0.0029 | 0.0259 | 0.0255 | 0.0278 | 0.0287 |
| CSR | 1.3630 | 10.985 | 1.3626 | 10.988 | 1.3982 | 11.045 | 1.4025 | 11.051 |
| Gen Cert | 61.451 | 491.53 | 61.552 | 491.55 | 61.910 | 495.04 | 61.596 | 492.15 |
| Sign | 1.3875 | 1.3870 | 1.3886 | 1.3869 | 1.4196 | 1.4202 | 1.4186 | 1.4192 |
| Verify | 2.7706 | 2.7678 | 2.7808 | 2.7725 | 2.7626 | 2.7670 | 2.7667 | 2.7778 |
| Open | 0.6798 | 0.6948 | 0.6987 | 0.7043 | 0.6957 | 0.6978 | 0.6831 | 0.6954 |
| Judge | 0.6917 | 0.6912 | 0.7014 | 0.6937 | 0.6914 | 0.6728 | 0.6878 | 0.6877 |
| Revoke | 0.0007 | 0.0027 | 0.0008 | 0.0027 | 0.0305 | 0.0754 | 0.0259 | 0.0909 || DB feature | in-memory | in-disk |
| GROUP SIZE | 2^10 | 2^25 | 2^10 | 2^25 |
| BATCH SIZE | 1 | 8 | 1 | 8 | 1 | 8 | 1 | 8 |
|----------------|--------|--------|--------|--------|--------|--------|--------|--------|
| Manager KeyGen | 48.888 | 48.892 | 48.921 | 48.886 | 48.802 | 48.923 | 48.882 | 48.919 |
| Join | 0.0030 | 0.0030 | 0.0029 | 0.0029 | 0.0250 | 0.0263 | 0.0272 | 0.0280 |
| CSR | 1.3552 | 10.894 | 1.3556 | 10.905 | 1.4094 | 11.126 | 1.4117 | 11.137 |
| Gen Cert | 582.70 | 4661.6 | 583.53 | 4661.6 | 582.93 | 4664.1 | 583.52 | 4667.4 |
| Sign | 1.3847 | 1.3835 | 1.3863 | 1.3848 | 1.4418 | 1.4396 | 1.4874 | 1.4813 |
| Verify | 1.8255 | 1.8248 | 1.8207 | 1.8180 | 1.8515 | 1.8561 | 1.8571 | 1.8529 |
| Open | 0.6991 | 0.6881 | 0.6901 | 0.6911 | 0.7137 | 0.7060 | 0.7197 | 0.7237 |
| Judge | 0.6806 | 0.6813 | 0.6744 | 0.6798 | 0.7016 | 0.6987 | 0.7016 | 0.6986 |
| Revoke | 0.0012 | 0.0044 | 0.0012 | 0.0046 | 0.1773 | 0.0733 | 0.0322 | 0.0797 |All sizes are in Bytes.
| SPHINCS+ feature | Public Key | Secret Key | Signature |
|--------------------|------------|------------|-----------|
| sphincs_sha2_128f | 32 | 96 | 17696 |
| sphincs_sha2_128s | 32 | 96 | 8464 |
| sphincs_sha2_192f | 48 | 144 | 36952 |
| sphincs_sha2_192s | 48 | 144 | 17512 |
| sphincs_sha2_256f | 64 | 192 | 52080 |
| sphincs_sha2_256s | 64 | 192 | 32016 |
| sphincs_shake_128f | 32 | 96 | 17696 |
| sphincs_shake_128s | 32 | 96 | 8464 |
| sphincs_shake_192f | 48 | 144 | 36952 |
| sphincs_shake_192s | 48 | 144 | 17512 |
| sphincs_shake_256f | 64 | 192 | 52080 |
| sphincs_shake_256s | 64 | 192 | 32016 |DGSP is fully implemented in Rust. Install Rust via rustup.
- Minimum Supported Rust Version (MSRV): 1.63.0
- Rust version used for the benchmarks in this README: 1.84.0 (stable)
- Platform used for benchmarks: Ubuntu 24.04, Intel® Core™ i7-4702MQ @ 2.20 GHz, 16 GiB RAM
The following table lists all direct dependencies and the versions used:
| Dependency | Version | Notes |
|---|---|---|
aes |
0.8.4 | AES block cipher for traceability |
pqcrypto-sphincsplus |
0.7.0 | SPHINCS+ signature scheme |
pqcrypto-traits |
0.3.5 | Traits for pqcrypto crates |
rand |
0.8.5 | Random number generation |
rayon |
1.10.0 | Data parallelism |
thiserror |
2.0.11 | Error type derivation |
zeroize |
1.8.1 | Secure memory wiping |
bincode |
1.3.3 | Binary serialization (optional, in-disk) |
serde |
1.0 | Serialization framework (optional, serialization) |
serde_json |
1.0 | JSON serialization (optional, in-disk) |
serde-big-array |
0.5.1 | Serde support for large arrays (optional, serialization) |
sha2 |
0.10.8 | SHA-2 hash functions (optional, sphincs_sha2_*) |
sha3 |
0.10.8 | SHA-3/SHAKE hash functions (optional, sphincs_shake_*) |
sled |
0.34.7 | Embedded database (optional, in-disk) |
Dev dependencies: criterion 0.5 (benchmarking), tempfile 3.15 (tests), tracing-test 0.2.5 (tests).
A pre-built Docker image is available on Docker Hub and provides a fully self-contained environment with Rust 1.84.0 and all dependencies already compiled:
docker pull arashazimi/dgspRun the end-to-end example:
docker run --rm arashazimi/dgsp cargo run --example simple --releaseRun the full test suite:
docker run --rm arashazimi/dgsp cargo test --releaseRun a benchmark (e.g. in-memory, sphincs_shake_256f):
docker run --rm arashazimi/dgsp \
cargo bench --bench dgsp_full_in_memory \
--no-default-features --features "in-memory benchmarking sphincs_shake_256f"Reproduce all paper benchmark configurations and retrieve logs to the host, starting from the project root directory:
docker run --name dgsp_bench arashazimi/dgsp bash -c "cd benches && bash all_benchmarks.sh"
docker cp dgsp_bench:"$(docker exec dgsp_bench sh -c 'ls -d /dgsp/benches/log_*')" benches/
docker rm -f dgsp_benchThen parse the results on the host (Python 3, no extra dependencies):
python3 benches/parse_benchmarks.pyTo build the image locally from source:
docker build -t arashazimi/dgsp .To use DGSP as a library, add it to your Cargo.toml:
[dependencies]
dgsp = "0.1.2"To enable specific features during installation, use as an example:
[dependencies]
dgsp = { version = "0.1.2", default-features = false, features = ["in-disk", "sphincs_shake_256f"] }Generate manager keys, and open private list of the manager and public revoked list databases:
use dgsp::*;
// generate manager keypairs:
let (pkm, skm) = DGSP::keygen_manager().unwrap();
// generate plm and revoked_list using in-memory feature
let plm = InMemoryPLM::open("").unwrap();
let revoked_list = InMemoryRevokedList::open("").unwrap();
// or generate plm and revoked_list using in-disk feature
use std::path::PathBuf;
let path = PathBuf::new();
let plm = InDiskPLM::open(&path).unwrap();
let revoked_list = InDiskRevokedList::open(&path).unwrap();A user joins the system and obtains their unique ID and cryptographic identifier:
let username = "alice";
let (id, cid_star) = DGSP::join(&skm.msk.hash_secret, username, &plm).unwrap();The user also generate a private seed:
let seed_u = DGSP::keygen_user();Create a batch of certificate signing request:
let batch_size = 8;
let (wots_pks, mut wots_seeds) = DGSP::csr(&seed_u, batch_size);Manager generates the corresponding certificates:
let mut certs = DGSP::gen_cert(&skm, id, &cid_star, &wots_pks, &plm).unwrap();User signs a message:
let message = b"Hello, DGSP!";
let signature = DGSP::sign(message, &seed_u, id, &cid_star, wots_seeds.pop().unwrap(), certs.pop().unwrap());Verify the signature:
DGSP::verify(message, &signature, &revoked_list, &pkm).unwrap();Manager can open a signature to find out who has signed it:
let (signer_id, signer_username, proof) = DGSP::open(&skm.msk, &plm, &signature, message).unwrap();Judge manager to make sure the given signature and message are correctly opened to the user id:
DGSP::judge(&signature, message, id, &proof).unwrap();Revoke a user and their associated certificates:
DGSP::revoke(&skm.msk.aes_key, &plm, &[id], &revoked_list).unwrap();To learn more, refer to the examples/simple.rs for additional information. One can run the simple.rs example for a specific sphincs feature via:
cargo run --example simple --no-default-features --features "in-disk in-memory sphincs_shake_256f" --releaseRun tests using:
cargo testTo test specific configurations, enable the required feature flags:
cargo test --no-default-features --features "in-disk sphincs_shake_256f"To test all combination of configurations in Unix-like operating systems, run the provided script:
bash ./tests/all_features_full_test.shNote that the full test will take some time to complete.
Run benchmarks using:
cargo bench --bench dgsp_full_in_disk
cargo bench --bench dgsp_full_in_memoryNote that the above will run the benchmarks for the default features selected in Cargo.toml. To choose a specific SPHINCS+ feature, run:
cargo bench --bench dgsp_full_in_disk --no-default-features --features "in-disk benchmarking sphincs_shake_256s"
cargo bench --bench dgsp_full_in_memory --no-default-features --features "in-memory benchmarking sphincs_shake_256s"To reproduce the full set of timing benchmarks reported in the paper, use the provided script that iterates over all configurations (two selected SPHINCS+ variants × both storage backends × group sizes 2^10 and 2^25 × batch sizes 1 and 8):
cd benches
bash all_benchmarks.shThe script modifies the benchmark constants, runs Criterion for each configuration, and saves raw logs under
benches/log_<timestamp>/in_memory/ and benches/log_<timestamp>/in_disk/.
Note: Each benchmark run first populates a database for the configured group size before measuring algorithm performance. For 2^25 users on our reference hardware, this one-time database setup — not algorithm performance — requires approximately 20 minutes and 8 GB per SPHINCS+ variant on our Samsung 860 EVO (SATA SSD) (in-disk), or ~1 minute (in-memory); subsequent runs reuse the cached database in ~20 seconds. Running on native Linux is recommended — Docker on macOS adds overhead due to its Linux VM layer. Remove
25fromGROUP_SIZES_LOGinall_benchmarks.shto skip these configurations.
Parsing the output: After the script finishes, use parse_benchmarks.py (Python 3, no extra dependencies) to parse the log files and print the formatted timing tables:
python3 benches/parse_benchmarks.pyThe script automatically picks the most recent log_* directory. You can also pass a path explicitly:
python3 benches/parse_benchmarks.py benches/log_<timestamp>Each Criterion log block looks like:
DGSP_in_memory_using_sphincs_shake_256f_with_1024_users_and_1_batch/keygen_manager
time: [3.0519 ms 3.0521 ms 3.0524 ms]
The three values are the lower confidence bound, mean, and upper confidence bound over 100 samples. The paper reports the mean (middle value). The parser extracts the mean and converts all values to milliseconds.
Note: The benchmarks were obtained on a specific machine (Ubuntu 24.04, Intel® Core™ i7-4702MQ @ 2.20 GHz, single core, hyper-threading and turbo-boost disabled). Results on other hardware will differ in absolute values, but the relative ordering and scaling behavior should support the main claims of the paper.
The library supports several feature flags for customization:
in-disk: Enables in-disk storage usingsledcrate.in-memory: Enables in-memory storage.serialization: Enables serialization of cryptographic keys and structures.- SPHINCS+ Variants: Choose from SHA-2-based or SHAKE-based configurations with varying security levels and performance/size goals:
sphincs_sha2_128fsphincs_sha2_128ssphincs_sha2_192fsphincs_sha2_192ssphincs_sha2_256fsphincs_sha2_256ssphincs_shake_128fsphincs_shake_128ssphincs_shake_192fsphincs_shake_192ssphincs_shake_256fsphincs_shake_256s
benchmarking: Used for benchmarking purposes.
src/
├── lib.rs # Crate root; re-exports the public API
├── scheme.rs # Core protocol: all DGSP algorithms (keygen_manager, keygen_user,
│ # join, csr, gen_cert, sign, verify, open, judge, revoke) and all
│ # key / signature / certificate type definitions
├── params.rs # Compile-time constants derived from the chosen SPHINCS+ variant
│ # (security level λ, byte sizes for keys, signatures, etc.)
├── db.rs # PLMInterface and RevokedListInterface trait definitions
├── db/
│ ├── in_memory.rs # In-memory PLM and RevokedList (feature: in-memory)
│ └── in_disk.rs # Persistent sled-backed PLM and RevokedList (feature: in-disk)
├── cipher.rs # AES wrapper used for user-ID encryption in traceability
├── hash.rs # Hash function dispatcher (SHA-2 or SHAKE, selected at compile time)
├── hash/ # Per-parameter-set SHA-2 and SHAKE implementations
├── sphincs_plus.rs # SPHINCS+ wrapper: key generation, signing, verification
├── sphincs_plus/ # Per-parameter-set SPHINCS+ constants and ADRS byte-offset tables
├── wots_plus.rs # WOTS+ (Winternitz One-Time Signature Plus) core implementation
├── wots_plus/ # ADRS (address) type used by WOTS+ operations
├── error.rs # Error and Result types (Error, VerificationError)
└── utils.rs # Internal byte-conversion helpers (u32/u64 ↔ big-endian bytes)
benches/
├── dgsp_full_in_memory.rs # Criterion benchmark suite for the in-memory backend
├── dgsp_full_in_disk.rs # Criterion benchmark suite for the in-disk backend
├── bench_utils.rs # Shared helpers (SPHINCS+ feature detection, duration formatting)
├── all_benchmarks.sh # Runs all benchmark configurations and collects log files
└── parse_benchmarks.py # Parses Criterion log files and prints timing tables (run on host)
examples/
└── simple.rs # End-to-end example covering all DGSP operations
tests/
└── all_features_full_test.sh # Iterates over all SPHINCS+ × storage feature combinations
# and runs the full test suite for each
Contributions are welcome! To contribute:
- Fork the repository.
- Create a new branch for your feature or bug fix.
- Submit a pull request with a detailed description of your changes.
This repository is licensed under the MIT License.