This repository contains an implementation of the hash-based signature scheme SPHINCS, in Python. The goal for this implementation was not to be fast, secure, or in any other way useable in a production environment. This cannot be stressed enough. DO NOT USE THIS for any signature that you or others rely on in any way.
Instead, this code was written to aid in understanding of the SPHINCS scheme, and to make it easier to experiment with the individual parts. The main aim was for the code to be flexible and (to some extent) readable.
This project relies on the Python implementation of BLAKE, by Larry Bugbee. This code was optimised for speed, but it is considered out of the scope of this repository to re-implement BLAKE from scratch.
In order to be able to run the code, make sure the requirements listed in requirements.txt
are satisfied. This can be achieved by calling pip install -r requirements.txt
The SPHINCS.py
can be called as an executable, according to the commandline interface specified below. Note again that this implementation is not optimised for speed - it takes some time to produce a signature using the default SPHINCS-256 parameters.
Usage:
SPHINCS.py keygen [--secret-key FILE] [--public-key FILE]
SPHINCS.py sign [-m FILE|--message FILE] [--secret-key FILE] [-s FILE|--signature FILE]
SPHINCS.py verify [-m FILE|--message FILE] [-s FILE|--signature FILE] [--public-key FILE]
SPHINCS.py (-h|--help)
Options:
-s FILE, --signature FILE Specify a signature file.
-m FILE, --message FILE Specify a message file.
--secret-key FILE Specify a secret-key file.
--public-key FILE Specify a public-key file.
This project includes several extensive unit tests. They are comptabile with nose2
, so calling nose2
from the project root directory is the easiest way to execute these.
This implementation was constructed based on the descriptions in the paper that introduces SPHINCS [1], rather than using the provided reference implementation. This leads to a few noteworthy design choices.
- In the paper, the addresses are specifies as 3-tuples of integers. In order to use these for seed generation, they need to be converted to byte sequences. The convention chosen in the reference implementation (i.e. leaf-first concatenation and litte-endian conversion) is adhered to here as well.
- The paper specifies that, with R = (R1, R2) ∈ {0, 1}n x {0, 1}n, the leaf index i is defined as i = Chop(R2, h). The reference implementation uses the rightmost chunk of 64 bits to compute i, however, and starts selecting bits for R1 from the third chunk. This implementation explicitly uses the bits from R from start to end instead, effectively interpreting R = F(M, SK2) as big-endian. See
SPHINCS.py:110
and onwards. In order to make the C code behave identically, one needs to explicitly use a big-endian byte order to initialise i as well. - When computing the digest, the reference implementation currently includes the public key, i.e. D = H(R1 | Q | PK1 | M). This implementation uses D = H(R1 | M), as is specified in the paper.
All of the above implies that the test vectors for WOTS+ and HORST (and ChaCha and BLAKE) provided in the tests/
directory match the results from the reference implementation, but the example signature for SPHINCS-256 does not. To get these to match, one needs to account for the above differences.
[1] Daniel J. Bernstein, Daira Hopwood, Andreas Hülsing, Tanja Lange, Ruben Niederhagen, Louiza Papachristodoulou, Peter Schwabe, and Zooko Wilcox- O’Hearn. SPHINCS: practical stateless hash-based signatures. In Marc Fischlin and Elisabeth Oswald, editors, Advances in Cryptology – EUROCRYPT 2015, volume 9056 of LNCS, pages 368–397. Springer, 2015. Document ID: 5c2820cfddf4e259cc7ea1eda384c9f9, http://cryptojedi.org/papers/#sphincs.