Note
This code relates to version 1.x of QRL, the world's first open-source PQ blockchain, which has been securing digital assets since December 2016. The next generation of QRL, version 2.0, is in development and has its own repositories. See this discussion page.
This project is under active development. Interfaces and serialized formats may change without backward compatibility.
qrllib contains the cryptographic code used by QRL and interfaces for C++, Python, Rust, Go, and JavaScript/WebAssembly. It includes:
- QRL's stateful XMSS and
XmssFastimplementations using WOTS+; - SHA2-256, SHAKE128, and SHAKE256 hashing;
- seed, address, mnemonic, and hash-chain helpers;
- historical Dilithium and Kyber implementations retained for QRL compatibility.
The XMSS parameters selected for QRL predate NIST's standardization in SP 800-208. The formats and parameter choices implemented here are QRL-specific.
The Kyber and Dilithium APIs use pre-standard candidate-era implementations retained for QRL v1 compatibility. They do not implement ML-KEM (FIPS 203) or ML-DSA (FIPS 204).
The XMSS references below explain state-management risks. Citing them does not imply standards compliance.
An XMSS OTS index must never be reused with the same private key. Reusing a private-key state removes the scheme's security guarantee and can make signature forgery possible. See RFC 8391 section 1.1.
Best practice for qrllib is to keep signing under application and user control, backed by an independent, durable ledger of every OTS index reserved or consumed by a signing attempt. When a signature is returned, associate the signature or its stable identifier with that index in the ledger. The calling application must coordinate the ledger across processes, signer objects, backups, and restored seeds. This enforces the application-level single-use requirement in RFC 8391 section 4.1.12. Do not rely only on a signer object's in-memory index.
Reserve and persist an index before asking qrllib to sign. Treat the index as consumed even if signing fails or the process stops before returning a signature. This ordering is consistent with RFC 8391 section 4.1.9, which updates the private-key state before releasing a signature. A durable high-water mark is sufficient only if every lower reserved or skipped index is permanently burned.
Copying an XmssFast object duplicates its private key and current index. Each
copy then advances independently. Moving a C++ object is the safer way to
transfer signing ownership. Reconstructing a signer from the same seed also
reconstructs the same key, so it must use the same external OTS record.
Methods such as getSK, getSeed, getHexSeed, and getMnemonic explicitly
export private key material. The returned vector or string belongs to the
caller and may be copied again by the language runtime. qrllib explicitly
erases selected secret-bearing storage that it owns, but this is defense in
depth rather than a guarantee that no compiler, runtime, allocator, operating
system, or third-party copy remains. It cannot erase an exported value after
return. Export secrets only when needed, keep them out of logs, and overwrite
mutable caller-owned buffers before releasing them.
The Go bindings do not attach finalizers to SWIG vector proxies. After copying
a top-level returned UcharVector into Go-owned memory, call the matching
module's DeleteUcharVector function; use
goqrllib.DeleteX_string_list_list for the nested result from
GetHashChainSeed. Element vectors obtained through that nested proxy's Get
method borrow the outer object and must not be deleted separately. These
deletion functions wipe the C++ vector storage before freeing it. Go-owned byte
slices and strings remain under the caller's control and must be overwritten
separately when appropriate.
Coverage differs by backend. The native historical Dilithium and Kyber
compatibility path erases wrapper state and its directly owned sensitive work
buffers. The pinned pqcrypto dependencies used by the Rust wrappers do not
erase every internal stack and Keccak workspace, so their coverage remains
partial.
CircleCI tests core C++ on Linux x64, macOS arm64, and Windows x64; historical Dilithium and Kyber on Linux x64 and macOS arm64; and Python, Rust, Go, and JavaScript/WebAssembly on Linux x64. GitHub Actions builds releases.
The Python package is published as pyqrllib:
python3 -m pip install pyqrllibThe Python package is tested on Linux x64.
Clone the submodules and install CMake 3.20 or later plus a C++17 compiler for
the commands below. Python and Go bindings also require SWIG. The JavaScript
build requires the Emscripten version checked by
.circleci/build_emscripten.sh.
git clone --recurse-submodules https://github.com/theQRL/qrllib.git
cd qrllib
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build --parallel
ctest --test-dir build --output-on-failureThe Rust implementation requires Rust 1.78 or later. Run its tests with:
cargo testFor the Python extension and tests:
python3 -m pip install --upgrade pip setuptools wheel
python3 -m pip install -e '.[testing]'
python3 -m pytest tests/pythonWith the pinned Emscripten toolchain active:
./.circleci/build_emscripten.sh
cd tests/js
npm ci
npm test
npm run test:browserThe generated files are copied to tests/js/tmp and build. The npm package
publishes the declared bundles from build; it does not currently define a
root main or exports entry point.
qrllib is distributed under the MIT license. Parts of the XMSS code derive from the public-domain reference implementation by Andreas Huelsing and Joost Rijneveld. Several third-party components are included as Git submodules.