diff --git a/.circleci/config.yml b/.circleci/config.yml index 62d388ce..e3721790 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -17,7 +17,7 @@ version: 2.1 - run: name: Run the tests in a container command: | - docker run -e CI=true -e PQCLEAN_ONLY_DIFF=1 --rm -v `pwd`:`pwd` -w `pwd` "pqclean/ci-container:$ARCH" /bin/bash -c " + docker run -e CI=true -e PQCLEAN_ONLY_DIFF=1 -e PQCLEAN_SKIP_SCHEMES=sphincs-haraka-128f-robust,sphincs-haraka-192s-robust,sphincs-sha256-128f-robust,sphincs-sha256-192s-robust,sphincs-shake256-128f-robust,sphincs-shake256-192s-robust,sphincs-haraka-128f-simple,sphincs-haraka-192s-simple,sphincs-sha256-128f-simple,sphincs-sha256-192s-simple,sphincs-shake256-128f-simple,sphincs-shake256-192s-simple,sphincs-haraka-128s-robust,sphincs-haraka-256f-robust,sphincs-sha256-128s-robust,sphincs-sha256-256f-robust,sphincs-shake256-128s-robust,sphincs-shake256-256f-robust,sphincs-haraka-128s-simple,sphincs-haraka-256f-simple,sphincs-sha256-128s-simple,sphincs-sha256-256f-simple,sphincs-shake256-128s-simple,sphincs-shake256-256f-simple,sphincs-haraka-192f-robust,sphincs-haraka-256s-robust,sphincs-sha256-192f-robust,sphincs-sha256-256s-robust,sphincs-shake256-192f-robust,sphincs-shake256-256s-robust,sphincs-haraka-192f-simple,sphincs-haraka-256s-simple,sphincs-sha256-192f-simple,sphincs-sha256-256s-simple,sphincs-shake256-192f-simple,sphincs-shake256-256s-simple --rm -v `pwd`:`pwd` -w `pwd` "pqclean/ci-container:$ARCH" /bin/bash -c " uname -a && export CC=${CC} && pip3 install -r requirements.txt && @@ -38,6 +38,7 @@ version: 2.1 command: | export CC=${CC} export PQCLEAN_ONLY_DIFF=1 + export PQCLEAN_SKIP_SCHEMES=sphincs-haraka-128f-robust,sphincs-haraka-192s-robust,sphincs-sha256-128f-robust,sphincs-sha256-192s-robust,sphincs-shake256-128f-robust,sphincs-shake256-192s-robust,sphincs-haraka-128f-simple,sphincs-haraka-192s-simple,sphincs-sha256-128f-simple,sphincs-sha256-192s-simple,sphincs-shake256-128f-simple,sphincs-shake256-192s-simple,sphincs-haraka-128s-robust,sphincs-haraka-256f-robust,sphincs-sha256-128s-robust,sphincs-sha256-256f-robust,sphincs-shake256-128s-robust,sphincs-shake256-256f-robust,sphincs-haraka-128s-simple,sphincs-haraka-256f-simple,sphincs-sha256-128s-simple,sphincs-sha256-256f-simple,sphincs-shake256-128s-simple,sphincs-shake256-256f-simple,sphincs-haraka-192f-robust,sphincs-haraka-256s-robust,sphincs-sha256-192f-robust,sphincs-sha256-256s-robust,sphincs-shake256-192f-robust,sphincs-shake256-256s-robust,sphincs-haraka-192f-simple,sphincs-haraka-256s-simple,sphincs-sha256-192f-simple,sphincs-sha256-256s-simple,sphincs-shake256-192f-simple,sphincs-shake256-256s-simple pip3 install -r requirements.txt mkdir test-results cd test diff --git a/.travis.yml b/.travis.yml index 8336d199..278ce904 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,7 @@ matrix: - git reset --hard $TRAVIS_COMMIT script: # Use travis-wait to allow slower tests to run - - "cd test && travis_wait 30 python3 -m nose --rednose --verbose" + - "cd test && travis_wait 60 python3 -m nose --rednose --verbose" env: PQCLEAN_ONLY_DIFF: 1 addons: @@ -43,7 +43,7 @@ matrix: - gcc --version script: # Use travis-wait to allow slower tests to run - - "cd test && travis_wait 30 python3 -m nose --rednose --verbose" + - "cd test && travis_wait 60 python3 -m nose --rednose --verbose" cache: pip diff --git a/appveyor.yml b/appveyor.yml index c021142b..0b3da3fb 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -9,6 +9,7 @@ shallow_clone: false environment: PQCLEAN_ONLY_DIFF: 1 + PQCLEAN_SKIP_SCHEMES: sphincs-haraka-128f-robust,sphincs-haraka-192s-robust,sphincs-sha256-128f-robust,sphincs-sha256-192s-robust,sphincs-shake256-128f-robust,sphincs-shake256-192s-robust,sphincs-haraka-128f-simple,sphincs-haraka-192s-simple,sphincs-sha256-128f-simple,sphincs-sha256-192s-simple,sphincs-shake256-128f-simple,sphincs-shake256-192s-simple,sphincs-haraka-128s-robust,sphincs-haraka-256f-robust,sphincs-sha256-128s-robust,sphincs-sha256-256f-robust,sphincs-shake256-128s-robust,sphincs-shake256-256f-robust,sphincs-haraka-128s-simple,sphincs-haraka-256f-simple,sphincs-sha256-128s-simple,sphincs-sha256-256f-simple,sphincs-shake256-128s-simple,sphincs-shake256-256f-simple,sphincs-haraka-192f-robust,sphincs-haraka-256s-robust,sphincs-sha256-192f-robust,sphincs-sha256-256s-robust,sphincs-shake256-192f-robust,sphincs-shake256-256s-robust,sphincs-haraka-192f-simple,sphincs-haraka-256s-simple,sphincs-sha256-192f-simple,sphincs-sha256-256s-simple,sphincs-shake256-192f-simple,sphincs-shake256-256s-simple matrix: - BITS: 64 - BITS: 32 diff --git a/crypto_sign/sphincs-haraka-128f-robust/META.yml b/crypto_sign/sphincs-haraka-128f-robust/META.yml new file mode 100644 index 00000000..d79ca253 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128f-robust/META.yml @@ -0,0 +1,27 @@ +name: SPHINCS+ +type: signature +claimed-nist-level: 1 +length-public-key: 32 +length-secret-key: 64 +length-signature: 16976 +testvectors-sha256: f0f84722cf529a108006d84b52966cbebd92146ee33cacdd7d1bba2cdc1944fd +principal-submitter: Andreas Hülsing +auxiliary-submitters: + - Jean-Philippe Aumasson + - Daniel J. Bernstein, + - Christoph Dobraunig + - Maria Eichlseder + - Scott Fluhrer + - Stefan-Lukas Gazdag + - Panos Kampanakis + - Stefan Kölbl + - Tanja Lange + - Martin M. Lauridsen + - Florian Mendel + - Ruben Niederhagen + - Christian Rechberger + - Joost Rijneveld + - Peter Schwabe +implementations: + - name: clean + version: https://github.com/sphincs/sphincsplus/commit/77755c94d0bc744478044d6efbb888dc13156441 diff --git a/crypto_sign/sphincs-haraka-128f-robust/clean/LICENSE b/crypto_sign/sphincs-haraka-128f-robust/clean/LICENSE new file mode 100644 index 00000000..670154e3 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128f-robust/clean/LICENSE @@ -0,0 +1,116 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + diff --git a/crypto_sign/sphincs-haraka-128f-robust/clean/Makefile b/crypto_sign/sphincs-haraka-128f-robust/clean/Makefile new file mode 100644 index 00000000..8bf94024 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128f-robust/clean/Makefile @@ -0,0 +1,20 @@ +# This Makefile can be used with GNU Make or BSD Make + +LIB=libsphincs-haraka-128f-robust_clean.a + +HEADERS = params.h address.h wots.h utils.h fors.h api.h hash.h thash.h haraka.h +OBJECTS = address.o wots.o utils.o fors.o sign.o hash_haraka.o thash_haraka_robust.o haraka.o + +CFLAGS=-O3 -Wall -Wconversion -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) + +all: $(LIB) + +%.o: %.c $(HEADERS) + $(CC) $(CFLAGS) -c -o $@ $< + +$(LIB): $(OBJECTS) + $(AR) -r $@ $(OBJECTS) + +clean: + $(RM) $(OBJECTS) + $(RM) $(LIB) diff --git a/crypto_sign/sphincs-haraka-128f-robust/clean/Makefile.Microsoft_nmake b/crypto_sign/sphincs-haraka-128f-robust/clean/Makefile.Microsoft_nmake new file mode 100644 index 00000000..ff0a5821 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128f-robust/clean/Makefile.Microsoft_nmake @@ -0,0 +1,19 @@ +# This Makefile can be used with Microsoft Visual Studio's nmake using the command: +# nmake /f Makefile.Microsoft_nmake + +LIBRARY=libsphincs-haraka-128f-robust_clean.lib +OBJECTS=address.obj wots.obj utils.obj fors.obj sign.obj hash_haraka.obj thash_haraka_robust.obj haraka.obj + +CFLAGS=/nologo /I ..\..\..\common /W4 /WX + +all: $(LIBRARY) + +# Make sure objects are recompiled if headers change. +$(OBJECTS): *.h + +$(LIBRARY): $(OBJECTS) + LIB.EXE /NOLOGO /WX /OUT:$@ $** + +clean: + -DEL $(OBJECTS) + -DEL $(LIBRARY) diff --git a/crypto_sign/sphincs-haraka-128f-robust/clean/address.c b/crypto_sign/sphincs-haraka-128f-robust/clean/address.c new file mode 100644 index 00000000..76591a03 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128f-robust/clean/address.c @@ -0,0 +1,78 @@ +#include + +#include "address.h" +#include "params.h" +#include "utils.h" + +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]) { + int i; + + for (i = 0; i < 8; i++) { + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_ull_to_bytes( + bytes + i * 4, 4, addr[i]); + } +} + +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer) { + addr[0] = layer; +} + +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree) { + addr[1] = 0; + addr[2] = (uint32_t) (tree >> 32); + addr[3] = (uint32_t) tree; +} + +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_type( + uint32_t addr[8], uint32_t type) { + addr[4] = type; +} + +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; +} + +/* These functions are used for OTS addresses. */ + +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair) { + addr[5] = keypair; +} + +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; + out[5] = in[5]; +} + +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain) { + addr[6] = chain; +} + +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash) { + addr[7] = hash; +} + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height) { + addr[6] = tree_height; +} + +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index) { + addr[7] = tree_index; +} diff --git a/crypto_sign/sphincs-haraka-128f-robust/clean/address.h b/crypto_sign/sphincs-haraka-128f-robust/clean/address.h new file mode 100644 index 00000000..0695c893 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128f-robust/clean/address.h @@ -0,0 +1,50 @@ +#ifndef SPX_ADDRESS_H +#define SPX_ADDRESS_H + +#include + +#define SPX_ADDR_TYPE_WOTS 0 +#define SPX_ADDR_TYPE_WOTSPK 1 +#define SPX_ADDR_TYPE_HASHTREE 2 +#define SPX_ADDR_TYPE_FORSTREE 3 +#define SPX_ADDR_TYPE_FORSPK 4 + +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer); + +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree); + +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_type( + uint32_t addr[8], uint32_t type); + +/* Copies the layer and tree part of one address into the other */ +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for WOTS and FORS addresses. */ + +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair); + +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain); + +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash); + +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height); + +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index); + +#endif diff --git a/crypto_sign/sphincs-haraka-128f-robust/clean/api.h b/crypto_sign/sphincs-haraka-128f-robust/clean/api.h new file mode 100644 index 00000000..7b723bd4 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128f-robust/clean/api.h @@ -0,0 +1,78 @@ +#ifndef PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_API_H +#define PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_API_H + +#include +#include + +#define PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_CRYPTO_ALGNAME "SPHINCS+" + +#define PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_CRYPTO_SECRETKEYBYTES 64 +#define PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES 32 +#define PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_CRYPTO_BYTES 16976 +#define PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_CRYPTO_SEEDBYTES 48 + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_crypto_sign_secretkeybytes(void); + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_crypto_sign_publickeybytes(void); + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_crypto_sign_bytes(void); + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_crypto_sign_seedbytes(void); + +/* + * Generates a SPHINCS+ key pair given a seed. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed); + +/* + * Generates a SPHINCS+ key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk); + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk); + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk); + +#endif diff --git a/crypto_sign/sphincs-haraka-128f-robust/clean/fors.c b/crypto_sign/sphincs-haraka-128f-robust/clean/fors.c new file mode 100644 index 00000000..f1d08c51 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128f-robust/clean/fors.c @@ -0,0 +1,164 @@ +#include +#include +#include + +#include "address.h" +#include "fors.h" +#include "hash.h" +#include "thash.h" +#include "utils.h" + +static void fors_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_prf_addr( + sk, sk_seed, fors_leaf_addr); +} + +static void fors_sk_to_leaf(unsigned char *leaf, const unsigned char *sk, + const unsigned char *pub_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_thash_1( + leaf, sk, pub_seed, fors_leaf_addr); +} + +static void fors_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t fors_tree_addr[8]) { + uint32_t fors_leaf_addr[8] = {0}; + + /* Only copy the parts that must be kept in fors_leaf_addr. */ + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_copy_keypair_addr( + fors_leaf_addr, fors_tree_addr); + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_type( + fors_leaf_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_tree_index( + fors_leaf_addr, addr_idx); + + fors_gen_sk(leaf, sk_seed, fors_leaf_addr); + fors_sk_to_leaf(leaf, leaf, pub_seed, fors_leaf_addr); +} + +/** + * Interprets m as SPX_FORS_HEIGHT-bit unsigned integers. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + * Assumes indices has space for SPX_FORS_TREES integers. + */ +static void message_to_indices(uint32_t *indices, const unsigned char *m) { + unsigned int i, j; + unsigned int offset = 0; + + for (i = 0; i < SPX_FORS_TREES; i++) { + indices[i] = 0; + for (j = 0; j < SPX_FORS_HEIGHT; j++) { + indices[i] ^= (((uint32_t)m[offset >> 3] >> (offset & 0x7)) & 0x1) << j; + offset++; + } + } +} + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Include the secret key part that produces the selected leaf node. */ + fors_gen_sk(sig, sk_seed, fors_tree_addr); + sig += SPX_N; + + /* Compute the authentication path for this leaf node. */ + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_treehash_FORS_HEIGHT( + roots + i * SPX_N, sig, sk_seed, pub_seed, + indices[i], idx_offset, fors_gen_leaf, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + unsigned char leaf[SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Derive the leaf from the included secret key part. */ + fors_sk_to_leaf(leaf, sig, pub_seed, fors_tree_addr); + sig += SPX_N; + + /* Derive the corresponding root node of this tree. */ + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_compute_root( + roots + i * SPX_N, leaf, indices[i], idx_offset, sig, + SPX_FORS_HEIGHT, pub_seed, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} diff --git a/crypto_sign/sphincs-haraka-128f-robust/clean/fors.h b/crypto_sign/sphincs-haraka-128f-robust/clean/fors.h new file mode 100644 index 00000000..8eadb7bb --- /dev/null +++ b/crypto_sign/sphincs-haraka-128f-robust/clean/fors.h @@ -0,0 +1,30 @@ +#ifndef SPX_FORS_H +#define SPX_FORS_H + +#include + +#include "params.h" + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]); + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-haraka-128f-robust/clean/haraka.c b/crypto_sign/sphincs-haraka-128f-robust/clean/haraka.c new file mode 100644 index 00000000..4387402c --- /dev/null +++ b/crypto_sign/sphincs-haraka-128f-robust/clean/haraka.c @@ -0,0 +1,965 @@ +/* + * Constant time implementation of the Haraka hash function. + * + * The bit-sliced implementation of the AES round functions are + * based on the AES implementation in BearSSL written + * by Thomas Pornin + */ + +#include +#include +#include + +#include "haraka.h" + +#define HARAKAS_RATE 32 + +static const uint64_t haraka512_rc64[10][8] = { + {0x24cf0ab9086f628b, 0xbdd6eeecc83b8382, 0xd96fb0306cdad0a7, 0xaace082ac8f95f89, 0x449d8e8870d7041f, 0x49bb2f80b2b3e2f8, 0x0569ae98d93bb258, 0x23dc9691e7d6a4b1}, + {0xd8ba10ede0fe5b6e, 0x7ecf7dbe424c7b8e, 0x6ea9949c6df62a31, 0xbf3f3c97ec9c313e, 0x241d03a196a1861e, 0xead3a51116e5a2ea, 0x77d479fcad9574e3, 0x18657a1af894b7a0}, + {0x10671e1a7f595522, 0xd9a00ff675d28c7b, 0x2f1edf0d2b9ba661, 0xb8ff58b8e3de45f9, 0xee29261da9865c02, 0xd1532aa4b50bdf43, 0x8bf858159b231bb1, 0xdf17439d22d4f599}, + {0xdd4b2f0870b918c0, 0x757a81f3b39b1bb6, 0x7a5c556898952e3f, 0x7dd70a16d915d87a, 0x3ae61971982b8301, 0xc3ab319e030412be, 0x17c0033ac094a8cb, 0x5a0630fc1a8dc4ef}, + {0x17708988c1632f73, 0xf92ddae090b44f4f, 0x11ac0285c43aa314, 0x509059941936b8ba, 0xd03e152fa2ce9b69, 0x3fbcbcb63a32998b, 0x6204696d692254f7, 0x915542ed93ec59b4}, + {0xf4ed94aa8879236e, 0xff6cb41cd38e03c0, 0x069b38602368aeab, 0x669495b820f0ddba, 0xf42013b1b8bf9e3d, 0xcf935efe6439734d, 0xbc1dcf42ca29e3f8, 0x7e6d3ed29f78ad67}, + {0xf3b0f6837ffcddaa, 0x3a76faef934ddf41, 0xcec7ae583a9c8e35, 0xe4dd18c68f0260af, 0x2c0e5df1ad398eaa, 0x478df5236ae22e8c, 0xfb944c46fe865f39, 0xaa48f82f028132ba}, + {0x231b9ae2b76aca77, 0x292a76a712db0b40, 0x5850625dc8134491, 0x73137dd469810fb5, 0x8a12a6a202a474fd, 0xd36fd9daa78bdb80, 0xb34c5e733505706f, 0xbaf1cdca818d9d96}, + {0x2e99781335e8c641, 0xbddfe5cce47d560e, 0xf74e9bf32e5e040c, 0x1d7a709d65996be9, 0x670df36a9cf66cdd, 0xd05ef84a176a2875, 0x0f888e828cb1c44e, 0x1a79e9c9727b052c}, + {0x83497348628d84de, 0x2e9387d51f22a754, 0xb000068da2f852d6, 0x378c9e1190fd6fe5, 0x870027c316de7293, 0xe51a9d4462e047bb, 0x90ecf7f8c6251195, 0x655953bfbed90a9c}, +}; + +static uint64_t tweaked512_rc64[10][8]; +static uint32_t tweaked256_rc32[10][8]; +static uint32_t tweaked256_rc32_sseed[10][8]; + +static inline uint32_t br_dec32le(const unsigned char *src) { + return (uint32_t)src[0] + | ((uint32_t)src[1] << 8) + | ((uint32_t)src[2] << 16) + | ((uint32_t)src[3] << 24); +} + +static void br_range_dec32le(uint32_t *v, size_t num, const unsigned char *src) { + while (num-- > 0) { + *v ++ = br_dec32le(src); + src += 4; + } +} + +static inline void br_enc32le(unsigned char *dst, uint32_t x) { + dst[0] = (unsigned char)x; + dst[1] = (unsigned char)(x >> 8); + dst[2] = (unsigned char)(x >> 16); + dst[3] = (unsigned char)(x >> 24); +} + + +static void br_range_enc32le(unsigned char *dst, const uint32_t *v, size_t num) { + while (num-- > 0) { + br_enc32le(dst, *v ++); + dst += 4; + } +} + +static void br_aes_ct64_bitslice_Sbox(uint64_t *q) { + /* + * This S-box implementation is a straightforward translation of + * the circuit described by Boyar and Peralta in "A new + * combinational logic minimization technique with applications + * to cryptology" (https://eprint.iacr.org/2009/191.pdf). + * + * Note that variables x* (input) and s* (output) are numbered + * in "reverse" order (x0 is the high bit, x7 is the low bit). + */ + + uint64_t x0, x1, x2, x3, x4, x5, x6, x7; + uint64_t y1, y2, y3, y4, y5, y6, y7, y8, y9; + uint64_t y10, y11, y12, y13, y14, y15, y16, y17, y18, y19; + uint64_t y20, y21; + uint64_t z0, z1, z2, z3, z4, z5, z6, z7, z8, z9; + uint64_t z10, z11, z12, z13, z14, z15, z16, z17; + uint64_t t0, t1, t2, t3, t4, t5, t6, t7, t8, t9; + uint64_t t10, t11, t12, t13, t14, t15, t16, t17, t18, t19; + uint64_t t20, t21, t22, t23, t24, t25, t26, t27, t28, t29; + uint64_t t30, t31, t32, t33, t34, t35, t36, t37, t38, t39; + uint64_t t40, t41, t42, t43, t44, t45, t46, t47, t48, t49; + uint64_t t50, t51, t52, t53, t54, t55, t56, t57, t58, t59; + uint64_t t60, t61, t62, t63, t64, t65, t66, t67; + uint64_t s0, s1, s2, s3, s4, s5, s6, s7; + + x0 = q[7]; + x1 = q[6]; + x2 = q[5]; + x3 = q[4]; + x4 = q[3]; + x5 = q[2]; + x6 = q[1]; + x7 = q[0]; + + /* + * Top linear transformation. + */ + y14 = x3 ^ x5; + y13 = x0 ^ x6; + y9 = x0 ^ x3; + y8 = x0 ^ x5; + t0 = x1 ^ x2; + y1 = t0 ^ x7; + y4 = y1 ^ x3; + y12 = y13 ^ y14; + y2 = y1 ^ x0; + y5 = y1 ^ x6; + y3 = y5 ^ y8; + t1 = x4 ^ y12; + y15 = t1 ^ x5; + y20 = t1 ^ x1; + y6 = y15 ^ x7; + y10 = y15 ^ t0; + y11 = y20 ^ y9; + y7 = x7 ^ y11; + y17 = y10 ^ y11; + y19 = y10 ^ y8; + y16 = t0 ^ y11; + y21 = y13 ^ y16; + y18 = x0 ^ y16; + + /* + * Non-linear section. + */ + t2 = y12 & y15; + t3 = y3 & y6; + t4 = t3 ^ t2; + t5 = y4 & x7; + t6 = t5 ^ t2; + t7 = y13 & y16; + t8 = y5 & y1; + t9 = t8 ^ t7; + t10 = y2 & y7; + t11 = t10 ^ t7; + t12 = y9 & y11; + t13 = y14 & y17; + t14 = t13 ^ t12; + t15 = y8 & y10; + t16 = t15 ^ t12; + t17 = t4 ^ t14; + t18 = t6 ^ t16; + t19 = t9 ^ t14; + t20 = t11 ^ t16; + t21 = t17 ^ y20; + t22 = t18 ^ y19; + t23 = t19 ^ y21; + t24 = t20 ^ y18; + + t25 = t21 ^ t22; + t26 = t21 & t23; + t27 = t24 ^ t26; + t28 = t25 & t27; + t29 = t28 ^ t22; + t30 = t23 ^ t24; + t31 = t22 ^ t26; + t32 = t31 & t30; + t33 = t32 ^ t24; + t34 = t23 ^ t33; + t35 = t27 ^ t33; + t36 = t24 & t35; + t37 = t36 ^ t34; + t38 = t27 ^ t36; + t39 = t29 & t38; + t40 = t25 ^ t39; + + t41 = t40 ^ t37; + t42 = t29 ^ t33; + t43 = t29 ^ t40; + t44 = t33 ^ t37; + t45 = t42 ^ t41; + z0 = t44 & y15; + z1 = t37 & y6; + z2 = t33 & x7; + z3 = t43 & y16; + z4 = t40 & y1; + z5 = t29 & y7; + z6 = t42 & y11; + z7 = t45 & y17; + z8 = t41 & y10; + z9 = t44 & y12; + z10 = t37 & y3; + z11 = t33 & y4; + z12 = t43 & y13; + z13 = t40 & y5; + z14 = t29 & y2; + z15 = t42 & y9; + z16 = t45 & y14; + z17 = t41 & y8; + + /* + * Bottom linear transformation. + */ + t46 = z15 ^ z16; + t47 = z10 ^ z11; + t48 = z5 ^ z13; + t49 = z9 ^ z10; + t50 = z2 ^ z12; + t51 = z2 ^ z5; + t52 = z7 ^ z8; + t53 = z0 ^ z3; + t54 = z6 ^ z7; + t55 = z16 ^ z17; + t56 = z12 ^ t48; + t57 = t50 ^ t53; + t58 = z4 ^ t46; + t59 = z3 ^ t54; + t60 = t46 ^ t57; + t61 = z14 ^ t57; + t62 = t52 ^ t58; + t63 = t49 ^ t58; + t64 = z4 ^ t59; + t65 = t61 ^ t62; + t66 = z1 ^ t63; + s0 = t59 ^ t63; + s6 = t56 ^ ~t62; + s7 = t48 ^ ~t60; + t67 = t64 ^ t65; + s3 = t53 ^ t66; + s4 = t51 ^ t66; + s5 = t47 ^ t65; + s1 = t64 ^ ~s3; + s2 = t55 ^ ~t67; + + q[7] = s0; + q[6] = s1; + q[5] = s2; + q[4] = s3; + q[3] = s4; + q[2] = s5; + q[1] = s6; + q[0] = s7; +} + +static void br_aes_ct_bitslice_Sbox(uint32_t *q) { + /* + * This S-box implementation is a straightforward translation of + * the circuit described by Boyar and Peralta in "A new + * combinational logic minimization technique with applications + * to cryptology" (https://eprint.iacr.org/2009/191.pdf). + * + * Note that variables x* (input) and s* (output) are numbered + * in "reverse" order (x0 is the high bit, x7 is the low bit). + */ + + uint32_t x0, x1, x2, x3, x4, x5, x6, x7; + uint32_t y1, y2, y3, y4, y5, y6, y7, y8, y9; + uint32_t y10, y11, y12, y13, y14, y15, y16, y17, y18, y19; + uint32_t y20, y21; + uint32_t z0, z1, z2, z3, z4, z5, z6, z7, z8, z9; + uint32_t z10, z11, z12, z13, z14, z15, z16, z17; + uint32_t t0, t1, t2, t3, t4, t5, t6, t7, t8, t9; + uint32_t t10, t11, t12, t13, t14, t15, t16, t17, t18, t19; + uint32_t t20, t21, t22, t23, t24, t25, t26, t27, t28, t29; + uint32_t t30, t31, t32, t33, t34, t35, t36, t37, t38, t39; + uint32_t t40, t41, t42, t43, t44, t45, t46, t47, t48, t49; + uint32_t t50, t51, t52, t53, t54, t55, t56, t57, t58, t59; + uint32_t t60, t61, t62, t63, t64, t65, t66, t67; + uint32_t s0, s1, s2, s3, s4, s5, s6, s7; + + x0 = q[7]; + x1 = q[6]; + x2 = q[5]; + x3 = q[4]; + x4 = q[3]; + x5 = q[2]; + x6 = q[1]; + x7 = q[0]; + + /* + * Top linear transformation. + */ + y14 = x3 ^ x5; + y13 = x0 ^ x6; + y9 = x0 ^ x3; + y8 = x0 ^ x5; + t0 = x1 ^ x2; + y1 = t0 ^ x7; + y4 = y1 ^ x3; + y12 = y13 ^ y14; + y2 = y1 ^ x0; + y5 = y1 ^ x6; + y3 = y5 ^ y8; + t1 = x4 ^ y12; + y15 = t1 ^ x5; + y20 = t1 ^ x1; + y6 = y15 ^ x7; + y10 = y15 ^ t0; + y11 = y20 ^ y9; + y7 = x7 ^ y11; + y17 = y10 ^ y11; + y19 = y10 ^ y8; + y16 = t0 ^ y11; + y21 = y13 ^ y16; + y18 = x0 ^ y16; + + /* + * Non-linear section. + */ + t2 = y12 & y15; + t3 = y3 & y6; + t4 = t3 ^ t2; + t5 = y4 & x7; + t6 = t5 ^ t2; + t7 = y13 & y16; + t8 = y5 & y1; + t9 = t8 ^ t7; + t10 = y2 & y7; + t11 = t10 ^ t7; + t12 = y9 & y11; + t13 = y14 & y17; + t14 = t13 ^ t12; + t15 = y8 & y10; + t16 = t15 ^ t12; + t17 = t4 ^ t14; + t18 = t6 ^ t16; + t19 = t9 ^ t14; + t20 = t11 ^ t16; + t21 = t17 ^ y20; + t22 = t18 ^ y19; + t23 = t19 ^ y21; + t24 = t20 ^ y18; + + t25 = t21 ^ t22; + t26 = t21 & t23; + t27 = t24 ^ t26; + t28 = t25 & t27; + t29 = t28 ^ t22; + t30 = t23 ^ t24; + t31 = t22 ^ t26; + t32 = t31 & t30; + t33 = t32 ^ t24; + t34 = t23 ^ t33; + t35 = t27 ^ t33; + t36 = t24 & t35; + t37 = t36 ^ t34; + t38 = t27 ^ t36; + t39 = t29 & t38; + t40 = t25 ^ t39; + + t41 = t40 ^ t37; + t42 = t29 ^ t33; + t43 = t29 ^ t40; + t44 = t33 ^ t37; + t45 = t42 ^ t41; + z0 = t44 & y15; + z1 = t37 & y6; + z2 = t33 & x7; + z3 = t43 & y16; + z4 = t40 & y1; + z5 = t29 & y7; + z6 = t42 & y11; + z7 = t45 & y17; + z8 = t41 & y10; + z9 = t44 & y12; + z10 = t37 & y3; + z11 = t33 & y4; + z12 = t43 & y13; + z13 = t40 & y5; + z14 = t29 & y2; + z15 = t42 & y9; + z16 = t45 & y14; + z17 = t41 & y8; + + /* + * Bottom linear transformation. + */ + t46 = z15 ^ z16; + t47 = z10 ^ z11; + t48 = z5 ^ z13; + t49 = z9 ^ z10; + t50 = z2 ^ z12; + t51 = z2 ^ z5; + t52 = z7 ^ z8; + t53 = z0 ^ z3; + t54 = z6 ^ z7; + t55 = z16 ^ z17; + t56 = z12 ^ t48; + t57 = t50 ^ t53; + t58 = z4 ^ t46; + t59 = z3 ^ t54; + t60 = t46 ^ t57; + t61 = z14 ^ t57; + t62 = t52 ^ t58; + t63 = t49 ^ t58; + t64 = z4 ^ t59; + t65 = t61 ^ t62; + t66 = z1 ^ t63; + s0 = t59 ^ t63; + s6 = t56 ^ ~t62; + s7 = t48 ^ ~t60; + t67 = t64 ^ t65; + s3 = t53 ^ t66; + s4 = t51 ^ t66; + s5 = t47 ^ t65; + s1 = t64 ^ ~s3; + s2 = t55 ^ ~t67; + + q[7] = s0; + q[6] = s1; + q[5] = s2; + q[4] = s3; + q[3] = s4; + q[2] = s5; + q[1] = s6; + q[0] = s7; +} + +static void br_aes_ct_ortho(uint32_t *q) { +#define SWAPN_32(cl, ch, s, x, y) do { \ + uint32_t a, b; \ + a = (x); \ + b = (y); \ + (x) = (a & (uint32_t)(cl)) | ((b & (uint32_t)(cl)) << (s)); \ + (y) = ((a & (uint32_t)(ch)) >> (s)) | (b & (uint32_t)(ch)); \ + } while (0) + +#define SWAP2_32(x, y) SWAPN_32(0x55555555, 0xAAAAAAAA, 1, x, y) +#define SWAP4_32(x, y) SWAPN_32(0x33333333, 0xCCCCCCCC, 2, x, y) +#define SWAP8_32(x, y) SWAPN_32(0x0F0F0F0F, 0xF0F0F0F0, 4, x, y) + + SWAP2_32(q[0], q[1]); + SWAP2_32(q[2], q[3]); + SWAP2_32(q[4], q[5]); + SWAP2_32(q[6], q[7]); + + SWAP4_32(q[0], q[2]); + SWAP4_32(q[1], q[3]); + SWAP4_32(q[4], q[6]); + SWAP4_32(q[5], q[7]); + + SWAP8_32(q[0], q[4]); + SWAP8_32(q[1], q[5]); + SWAP8_32(q[2], q[6]); + SWAP8_32(q[3], q[7]); +} + +static inline void add_round_key32(uint32_t *q, const uint32_t *sk) { + q[0] ^= sk[0]; + q[1] ^= sk[1]; + q[2] ^= sk[2]; + q[3] ^= sk[3]; + q[4] ^= sk[4]; + q[5] ^= sk[5]; + q[6] ^= sk[6]; + q[7] ^= sk[7]; +} + +static inline void shift_rows32(uint32_t *q) { + int i; + + for (i = 0; i < 8; i++) { + uint32_t x; + + x = q[i]; + q[i] = (x & 0x000000FF) + | ((x & 0x0000FC00) >> 2) | ((x & 0x00000300) << 6) + | ((x & 0x00F00000) >> 4) | ((x & 0x000F0000) << 4) + | ((x & 0xC0000000) >> 6) | ((x & 0x3F000000) << 2); + } +} + +static inline uint32_t rotr16(uint32_t x) { + return (x << 16) | (x >> 16); +} + +static inline void mix_columns32(uint32_t *q) { + uint32_t q0, q1, q2, q3, q4, q5, q6, q7; + uint32_t r0, r1, r2, r3, r4, r5, r6, r7; + + q0 = q[0]; + q1 = q[1]; + q2 = q[2]; + q3 = q[3]; + q4 = q[4]; + q5 = q[5]; + q6 = q[6]; + q7 = q[7]; + r0 = (q0 >> 8) | (q0 << 24); + r1 = (q1 >> 8) | (q1 << 24); + r2 = (q2 >> 8) | (q2 << 24); + r3 = (q3 >> 8) | (q3 << 24); + r4 = (q4 >> 8) | (q4 << 24); + r5 = (q5 >> 8) | (q5 << 24); + r6 = (q6 >> 8) | (q6 << 24); + r7 = (q7 >> 8) | (q7 << 24); + + q[0] = q7 ^ r7 ^ r0 ^ rotr16(q0 ^ r0); + q[1] = q0 ^ r0 ^ q7 ^ r7 ^ r1 ^ rotr16(q1 ^ r1); + q[2] = q1 ^ r1 ^ r2 ^ rotr16(q2 ^ r2); + q[3] = q2 ^ r2 ^ q7 ^ r7 ^ r3 ^ rotr16(q3 ^ r3); + q[4] = q3 ^ r3 ^ q7 ^ r7 ^ r4 ^ rotr16(q4 ^ r4); + q[5] = q4 ^ r4 ^ r5 ^ rotr16(q5 ^ r5); + q[6] = q5 ^ r5 ^ r6 ^ rotr16(q6 ^ r6); + q[7] = q6 ^ r6 ^ r7 ^ rotr16(q7 ^ r7); +} + +static void br_aes_ct64_ortho(uint64_t *q) { +#define SWAPN(cl, ch, s, x, y) do { \ + uint64_t a, b; \ + a = (x); \ + b = (y); \ + (x) = (a & (uint64_t)(cl)) | ((b & (uint64_t)(cl)) << (s)); \ + (y) = ((a & (uint64_t)(ch)) >> (s)) | (b & (uint64_t)(ch)); \ + } while (0) + +#define SWAP2(x, y) SWAPN(0x5555555555555555, 0xAAAAAAAAAAAAAAAA, 1, x, y) +#define SWAP4(x, y) SWAPN(0x3333333333333333, 0xCCCCCCCCCCCCCCCC, 2, x, y) +#define SWAP8(x, y) SWAPN(0x0F0F0F0F0F0F0F0F, 0xF0F0F0F0F0F0F0F0, 4, x, y) + + SWAP2(q[0], q[1]); + SWAP2(q[2], q[3]); + SWAP2(q[4], q[5]); + SWAP2(q[6], q[7]); + + SWAP4(q[0], q[2]); + SWAP4(q[1], q[3]); + SWAP4(q[4], q[6]); + SWAP4(q[5], q[7]); + + SWAP8(q[0], q[4]); + SWAP8(q[1], q[5]); + SWAP8(q[2], q[6]); + SWAP8(q[3], q[7]); +} + + +static void br_aes_ct64_interleave_in(uint64_t *q0, uint64_t *q1, const uint32_t *w) { + uint64_t x0, x1, x2, x3; + + x0 = w[0]; + x1 = w[1]; + x2 = w[2]; + x3 = w[3]; + x0 |= (x0 << 16); + x1 |= (x1 << 16); + x2 |= (x2 << 16); + x3 |= (x3 << 16); + x0 &= (uint64_t)0x0000FFFF0000FFFF; + x1 &= (uint64_t)0x0000FFFF0000FFFF; + x2 &= (uint64_t)0x0000FFFF0000FFFF; + x3 &= (uint64_t)0x0000FFFF0000FFFF; + x0 |= (x0 << 8); + x1 |= (x1 << 8); + x2 |= (x2 << 8); + x3 |= (x3 << 8); + x0 &= (uint64_t)0x00FF00FF00FF00FF; + x1 &= (uint64_t)0x00FF00FF00FF00FF; + x2 &= (uint64_t)0x00FF00FF00FF00FF; + x3 &= (uint64_t)0x00FF00FF00FF00FF; + *q0 = x0 | (x2 << 8); + *q1 = x1 | (x3 << 8); +} + + +static void br_aes_ct64_interleave_out(uint32_t *w, uint64_t q0, uint64_t q1) { + uint64_t x0, x1, x2, x3; + + x0 = q0 & (uint64_t)0x00FF00FF00FF00FF; + x1 = q1 & (uint64_t)0x00FF00FF00FF00FF; + x2 = (q0 >> 8) & (uint64_t)0x00FF00FF00FF00FF; + x3 = (q1 >> 8) & (uint64_t)0x00FF00FF00FF00FF; + x0 |= (x0 >> 8); + x1 |= (x1 >> 8); + x2 |= (x2 >> 8); + x3 |= (x3 >> 8); + x0 &= (uint64_t)0x0000FFFF0000FFFF; + x1 &= (uint64_t)0x0000FFFF0000FFFF; + x2 &= (uint64_t)0x0000FFFF0000FFFF; + x3 &= (uint64_t)0x0000FFFF0000FFFF; + w[0] = (uint32_t)x0 | (uint32_t)(x0 >> 16); + w[1] = (uint32_t)x1 | (uint32_t)(x1 >> 16); + w[2] = (uint32_t)x2 | (uint32_t)(x2 >> 16); + w[3] = (uint32_t)x3 | (uint32_t)(x3 >> 16); +} + +static inline void add_round_key(uint64_t *q, const uint64_t *sk) { + q[0] ^= sk[0]; + q[1] ^= sk[1]; + q[2] ^= sk[2]; + q[3] ^= sk[3]; + q[4] ^= sk[4]; + q[5] ^= sk[5]; + q[6] ^= sk[6]; + q[7] ^= sk[7]; +} + +static inline void shift_rows(uint64_t *q) { + int i; + + for (i = 0; i < 8; i++) { + uint64_t x; + + x = q[i]; + q[i] = (x & (uint64_t)0x000000000000FFFF) + | ((x & (uint64_t)0x00000000FFF00000) >> 4) + | ((x & (uint64_t)0x00000000000F0000) << 12) + | ((x & (uint64_t)0x0000FF0000000000) >> 8) + | ((x & (uint64_t)0x000000FF00000000) << 8) + | ((x & (uint64_t)0xF000000000000000) >> 12) + | ((x & (uint64_t)0x0FFF000000000000) << 4); + } +} + +static inline uint64_t rotr32(uint64_t x) { + return (x << 32) | (x >> 32); +} + +static inline void mix_columns(uint64_t *q) { + uint64_t q0, q1, q2, q3, q4, q5, q6, q7; + uint64_t r0, r1, r2, r3, r4, r5, r6, r7; + + q0 = q[0]; + q1 = q[1]; + q2 = q[2]; + q3 = q[3]; + q4 = q[4]; + q5 = q[5]; + q6 = q[6]; + q7 = q[7]; + r0 = (q0 >> 16) | (q0 << 48); + r1 = (q1 >> 16) | (q1 << 48); + r2 = (q2 >> 16) | (q2 << 48); + r3 = (q3 >> 16) | (q3 << 48); + r4 = (q4 >> 16) | (q4 << 48); + r5 = (q5 >> 16) | (q5 << 48); + r6 = (q6 >> 16) | (q6 << 48); + r7 = (q7 >> 16) | (q7 << 48); + + q[0] = q7 ^ r7 ^ r0 ^ rotr32(q0 ^ r0); + q[1] = q0 ^ r0 ^ q7 ^ r7 ^ r1 ^ rotr32(q1 ^ r1); + q[2] = q1 ^ r1 ^ r2 ^ rotr32(q2 ^ r2); + q[3] = q2 ^ r2 ^ q7 ^ r7 ^ r3 ^ rotr32(q3 ^ r3); + q[4] = q3 ^ r3 ^ q7 ^ r7 ^ r4 ^ rotr32(q4 ^ r4); + q[5] = q4 ^ r4 ^ r5 ^ rotr32(q5 ^ r5); + q[6] = q5 ^ r5 ^ r6 ^ rotr32(q6 ^ r6); + q[7] = q6 ^ r6 ^ r7 ^ rotr32(q7 ^ r7); +} + +static void interleave_constant(uint64_t *out, const unsigned char *in) { + uint32_t tmp_32_constant[16]; + int i; + + br_range_dec32le(tmp_32_constant, 16, in); + for (i = 0; i < 4; i++) { + br_aes_ct64_interleave_in(&out[i], &out[i + 4], tmp_32_constant + (i << 2)); + } + br_aes_ct64_ortho(out); +} + +static void interleave_constant32(uint32_t *out, const unsigned char *in) { + int i; + for (i = 0; i < 4; i++) { + out[2 * i] = br_dec32le(in + 4 * i); + out[2 * i + 1] = br_dec32le(in + 4 * i + 16); + } + br_aes_ct_ortho(out); +} + +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_tweak_constants( + const unsigned char *pk_seed, const unsigned char *sk_seed, + unsigned long long seed_length) { + unsigned char buf[40 * 16]; + int i; + + /* Use the standard constants to generate tweaked ones. */ + memcpy((uint8_t *)tweaked512_rc64, (uint8_t *)haraka512_rc64, 40 * 16); + + /* Constants for sk.seed */ + if (sk_seed != NULL) { + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka_S( + buf, 40 * 16, sk_seed, seed_length); + + /* Interleave constants */ + for (i = 0; i < 10; i++) { + interleave_constant32(tweaked256_rc32_sseed[i], buf + 32 * i); + } + } + + /* Constants for pk.seed */ + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka_S( + buf, 40 * 16, pk_seed, seed_length); + for (i = 0; i < 10; i++) { + interleave_constant32(tweaked256_rc32[i], buf + 32 * i); + interleave_constant(tweaked512_rc64[i], buf + 64 * i); + } +} + +static void haraka_S_absorb(unsigned char *s, + const unsigned char *m, unsigned long long mlen, + unsigned char p) { + unsigned long long i; + unsigned char t[HARAKAS_RATE]; + + while (mlen >= HARAKAS_RATE) { + /* XOR block to state */ + for (i = 0; i < HARAKAS_RATE; ++i) { + s[i] ^= m[i]; + } + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka512_perm(s, s); + mlen -= HARAKAS_RATE; + m += HARAKAS_RATE; + } + + for (i = 0; i < HARAKAS_RATE; ++i) { + t[i] = 0; + } + for (i = 0; i < mlen; ++i) { + t[i] = m[i]; + } + t[i] = p; + t[HARAKAS_RATE - 1] |= 128; + for (i = 0; i < HARAKAS_RATE; ++i) { + s[i] ^= t[i]; + } +} + +static void haraka_S_squeezeblocks(unsigned char *h, unsigned long long nblocks, + unsigned char *s) { + while (nblocks > 0) { + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka512_perm(s, s); + memcpy(h, s, HARAKAS_RATE); + h += HARAKAS_RATE; + nblocks--; + } +} + +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka_S_inc_init(uint8_t *s_inc) { + size_t i; + + for (i = 0; i < 64; i++) { + s_inc[i] = 0; + } + s_inc[64] = 0; +} + +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka_S_inc_absorb(uint8_t *s_inc, const uint8_t *m, size_t mlen) { + size_t i; + + /* Recall that s_inc[64] is the non-absorbed bytes xored into the state */ + while (mlen + s_inc[64] >= HARAKAS_RATE) { + for (i = 0; i < (size_t)(HARAKAS_RATE - s_inc[64]); i++) { + /* Take the i'th byte from message + xor with the s_inc[64] + i'th byte of the state */ + s_inc[s_inc[64] + i] ^= m[i]; + } + mlen -= (size_t)(HARAKAS_RATE - s_inc[64]); + m += HARAKAS_RATE - s_inc[64]; + s_inc[64] = 0; + + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka512_perm(s_inc, s_inc); + } + + for (i = 0; i < mlen; i++) { + s_inc[s_inc[64] + i] ^= m[i]; + } + s_inc[64] = (uint8_t)(mlen + s_inc[64]); +} + +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka_S_inc_finalize(uint8_t *s_inc) { + /* After haraka_S_inc_absorb, we are guaranteed that s_inc[64] < HARAKAS_RATE, + so we can always use one more byte for p in the current state. */ + s_inc[s_inc[64]] ^= 0x1F; + s_inc[HARAKAS_RATE - 1] ^= 128; + s_inc[64] = 0; +} + +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka_S_inc_squeeze(uint8_t *out, size_t outlen, uint8_t *s_inc) { + uint8_t i; + + /* First consume any bytes we still have sitting around */ + for (i = 0; i < outlen && i < s_inc[64]; i++) { + /* There are s_inc[64] bytes left, so r - s_inc[64] is the first + available byte. We consume from there, i.e., up to r. */ + out[i] = s_inc[(HARAKAS_RATE - s_inc[64] + i)]; + } + out += i; + outlen -= i; + s_inc[64] = (uint8_t)(s_inc[64] - i); + + /* Then squeeze the remaining necessary blocks */ + while (outlen > 0) { + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka512_perm(s_inc, s_inc); + + for (i = 0; i < outlen && i < HARAKAS_RATE; i++) { + out[i] = s_inc[i]; + } + out += i; + outlen -= i; + s_inc[64] = (uint8_t)(HARAKAS_RATE - i); + } +} + +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka_S(unsigned char *out, unsigned long long outlen, const unsigned char *in, unsigned long long inlen) { + unsigned long long i; + unsigned char s[64]; + unsigned char d[32]; + + for (i = 0; i < 64; i++) { + s[i] = 0; + } + haraka_S_absorb(s, in, inlen, 0x1F); + + haraka_S_squeezeblocks(out, outlen / 32, s); + out += (outlen / 32) * 32; + + if (outlen % 32) { + haraka_S_squeezeblocks(d, 1, s); + for (i = 0; i < outlen % 32; i++) { + out[i] = d[i]; + } + } +} + +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka512_perm(unsigned char *out, const unsigned char *in) { + uint32_t w[16]; + uint64_t q[8], tmp_q; + unsigned int i, j; + + br_range_dec32le(w, 16, in); + for (i = 0; i < 4; i++) { + br_aes_ct64_interleave_in(&q[i], &q[i + 4], w + (i << 2)); + } + br_aes_ct64_ortho(q); + + /* AES rounds */ + for (i = 0; i < 5; i++) { + for (j = 0; j < 2; j++) { + br_aes_ct64_bitslice_Sbox(q); + shift_rows(q); + mix_columns(q); + add_round_key(q, tweaked512_rc64[2 * i + j]); + } + /* Mix states */ + for (j = 0; j < 8; j++) { + tmp_q = q[j]; + q[j] = (tmp_q & 0x0001000100010001) << 5 | + (tmp_q & 0x0002000200020002) << 12 | + (tmp_q & 0x0004000400040004) >> 1 | + (tmp_q & 0x0008000800080008) << 6 | + (tmp_q & 0x0020002000200020) << 9 | + (tmp_q & 0x0040004000400040) >> 4 | + (tmp_q & 0x0080008000800080) << 3 | + (tmp_q & 0x2100210021002100) >> 5 | + (tmp_q & 0x0210021002100210) << 2 | + (tmp_q & 0x0800080008000800) << 4 | + (tmp_q & 0x1000100010001000) >> 12 | + (tmp_q & 0x4000400040004000) >> 10 | + (tmp_q & 0x8400840084008400) >> 3; + } + } + + br_aes_ct64_ortho(q); + for (i = 0; i < 4; i ++) { + br_aes_ct64_interleave_out(w + (i << 2), q[i], q[i + 4]); + } + br_range_enc32le(out, w, 16); +} + +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka512(unsigned char *out, const unsigned char *in) { + int i; + + unsigned char buf[64]; + + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka512_perm(buf, in); + /* Feed-forward */ + for (i = 0; i < 64; i++) { + buf[i] = buf[i] ^ in[i]; + } + + /* Truncated */ + memcpy(out, buf + 8, 8); + memcpy(out + 8, buf + 24, 8); + memcpy(out + 16, buf + 32, 8); + memcpy(out + 24, buf + 48, 8); +} + + +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka256(unsigned char *out, const unsigned char *in) { + uint32_t q[8], tmp_q; + int i, j; + + for (i = 0; i < 4; i++) { + q[2 * i] = br_dec32le(in + 4 * i); + q[2 * i + 1] = br_dec32le(in + 4 * i + 16); + } + br_aes_ct_ortho(q); + + /* AES rounds */ + for (i = 0; i < 5; i++) { + for (j = 0; j < 2; j++) { + br_aes_ct_bitslice_Sbox(q); + shift_rows32(q); + mix_columns32(q); + add_round_key32(q, tweaked256_rc32[2 * i + j]); + } + + /* Mix states */ + for (j = 0; j < 8; j++) { + tmp_q = q[j]; + q[j] = (tmp_q & 0x81818181) | + (tmp_q & 0x02020202) << 1 | + (tmp_q & 0x04040404) << 2 | + (tmp_q & 0x08080808) << 3 | + (tmp_q & 0x10101010) >> 3 | + (tmp_q & 0x20202020) >> 2 | + (tmp_q & 0x40404040) >> 1; + } + } + + br_aes_ct_ortho(q); + for (i = 0; i < 4; i++) { + br_enc32le(out + 4 * i, q[2 * i]); + br_enc32le(out + 4 * i + 16, q[2 * i + 1]); + } + + for (i = 0; i < 32; i++) { + out[i] ^= in[i]; + } +} + +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka256_sk(unsigned char *out, const unsigned char *in) { + uint32_t q[8], tmp_q; + int i, j; + + for (i = 0; i < 4; i++) { + q[2 * i] = br_dec32le(in + 4 * i); + q[2 * i + 1] = br_dec32le(in + 4 * i + 16); + } + br_aes_ct_ortho(q); + + /* AES rounds */ + for (i = 0; i < 5; i++) { + for (j = 0; j < 2; j++) { + br_aes_ct_bitslice_Sbox(q); + shift_rows32(q); + mix_columns32(q); + add_round_key32(q, tweaked256_rc32_sseed[2 * i + j]); + } + + /* Mix states */ + for (j = 0; j < 8; j++) { + tmp_q = q[j]; + q[j] = (tmp_q & 0x81818181) | + (tmp_q & 0x02020202) << 1 | + (tmp_q & 0x04040404) << 2 | + (tmp_q & 0x08080808) << 3 | + (tmp_q & 0x10101010) >> 3 | + (tmp_q & 0x20202020) >> 2 | + (tmp_q & 0x40404040) >> 1; + } + } + + br_aes_ct_ortho(q); + for (i = 0; i < 4; i++) { + br_enc32le(out + 4 * i, q[2 * i]); + br_enc32le(out + 4 * i + 16, q[2 * i + 1]); + } + + for (i = 0; i < 32; i++) { + out[i] ^= in[i]; + } +} diff --git a/crypto_sign/sphincs-haraka-128f-robust/clean/haraka.h b/crypto_sign/sphincs-haraka-128f-robust/clean/haraka.h new file mode 100644 index 00000000..b4b880df --- /dev/null +++ b/crypto_sign/sphincs-haraka-128f-robust/clean/haraka.h @@ -0,0 +1,30 @@ +#ifndef SPX_HARAKA_H +#define SPX_HARAKA_H + +/* Tweak constants with seed */ +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_tweak_constants( + const unsigned char *pk_seed, const unsigned char *sk_seed, + unsigned long long seed_length); + +/* Haraka Sponge */ +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka_S_inc_init(uint8_t *s_inc); +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka_S_inc_absorb(uint8_t *s_inc, const uint8_t *m, size_t mlen); +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka_S_inc_finalize(uint8_t *s_inc); +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka_S_inc_squeeze(uint8_t *out, size_t outlen, uint8_t *s_inc); +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka_S( + unsigned char *out, unsigned long long outlen, + const unsigned char *in, unsigned long long inlen); + +/* Applies the 512-bit Haraka permutation to in. */ +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka512_perm(unsigned char *out, const unsigned char *in); + +/* Implementation of Haraka-512 */ +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka512(unsigned char *out, const unsigned char *in); + +/* Implementation of Haraka-256 */ +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka256(unsigned char *out, const unsigned char *in); + +/* Implementation of Haraka-256 using sk.seed constants */ +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka256_sk(unsigned char *out, const unsigned char *in); + +#endif diff --git a/crypto_sign/sphincs-haraka-128f-robust/clean/hash.h b/crypto_sign/sphincs-haraka-128f-robust/clean/hash.h new file mode 100644 index 00000000..c2449fb0 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128f-robust/clean/hash.h @@ -0,0 +1,22 @@ +#ifndef SPX_HASH_H +#define SPX_HASH_H + +#include + +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed); + +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen); + +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen); + +#endif diff --git a/crypto_sign/sphincs-haraka-128f-robust/clean/hash_haraka.c b/crypto_sign/sphincs-haraka-128f-robust/clean/hash_haraka.c new file mode 100644 index 00000000..cec634c2 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128f-robust/clean/hash_haraka.c @@ -0,0 +1,86 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "utils.h" + +#include "haraka.h" + +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed) { + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_tweak_constants(pub_seed, sk_seed, SPX_N); +} + +/* + * Computes PRF(key, addr), given a secret key of SPX_N bytes and an address + */ +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]) { + unsigned char buf[SPX_ADDR_BYTES]; + /* Since SPX_N may be smaller than 32, we need a temporary buffer. */ + unsigned char outbuf[32]; + + (void)key; /* Suppress an 'unused parameter' warning. */ + + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_addr_to_bytes(buf, addr); + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka256_sk(outbuf, buf); + memcpy(out, outbuf, SPX_N); +} + +/** + * Computes the message-dependent randomness R, using a secret seed and an + * optional randomization value as well as the message. + */ +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen) { + uint8_t s_inc[65]; + + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka_S_inc_init(s_inc); + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka_S_inc_absorb(s_inc, sk_prf, SPX_N); + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka_S_inc_absorb(s_inc, optrand, SPX_N); + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka_S_inc_absorb(s_inc, m, mlen); + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka_S_inc_finalize(s_inc); + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka_S_inc_squeeze(R, SPX_N, s_inc); +} + +/** + * Computes the message hash using R, the public key, and the message. + * Outputs the message digest and the index of the leaf. The index is split in + * the tree index and the leaf index, for convenient copying to an address. + */ +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen) { +#define SPX_TREE_BITS (SPX_TREE_HEIGHT * (SPX_D - 1)) +#define SPX_TREE_BYTES ((SPX_TREE_BITS + 7) / 8) +#define SPX_LEAF_BITS SPX_TREE_HEIGHT +#define SPX_LEAF_BYTES ((SPX_LEAF_BITS + 7) / 8) +#define SPX_DGST_BYTES (SPX_FORS_MSG_BYTES + SPX_TREE_BYTES + SPX_LEAF_BYTES) + + unsigned char buf[SPX_DGST_BYTES]; + unsigned char *bufp = buf; + uint8_t s_inc[65]; + + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka_S_inc_init(s_inc); + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka_S_inc_absorb(s_inc, R, SPX_N); + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka_S_inc_absorb(s_inc, pk, SPX_PK_BYTES); + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka_S_inc_absorb(s_inc, m, mlen); + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka_S_inc_finalize(s_inc); + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka_S_inc_squeeze(buf, SPX_DGST_BYTES, s_inc); + + memcpy(digest, bufp, SPX_FORS_MSG_BYTES); + bufp += SPX_FORS_MSG_BYTES; + + *tree = PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_bytes_to_ull(bufp, SPX_TREE_BYTES); + *tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS); + bufp += SPX_TREE_BYTES; + + *leaf_idx = (uint32_t)PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_bytes_to_ull( + bufp, SPX_LEAF_BYTES); + *leaf_idx &= (~(uint32_t)0) >> (32 - SPX_LEAF_BITS); +} diff --git a/crypto_sign/sphincs-haraka-128f-robust/clean/params.h b/crypto_sign/sphincs-haraka-128f-robust/clean/params.h new file mode 100644 index 00000000..367ef88d --- /dev/null +++ b/crypto_sign/sphincs-haraka-128f-robust/clean/params.h @@ -0,0 +1,53 @@ +#ifndef SPX_PARAMS_H +#define SPX_PARAMS_H + +/* Hash output length in bytes. */ +#define SPX_N 16 +/* Height of the hypertree. */ +#define SPX_FULL_HEIGHT 60 +/* Number of subtree layer. */ +#define SPX_D 20 +/* FORS tree dimensions. */ +#define SPX_FORS_HEIGHT 9 +#define SPX_FORS_TREES 30 +/* Winternitz parameter, */ +#define SPX_WOTS_W 16 + +/* The hash function is defined by linking a different hash.c file, as opposed + to setting a #define constant. */ + +/* For clarity */ +#define SPX_ADDR_BYTES 32 + +/* WOTS parameters. */ +#define SPX_WOTS_LOGW 4 + +#define SPX_WOTS_LEN1 (8 * SPX_N / SPX_WOTS_LOGW) + +/* SPX_WOTS_LEN2 is floor(log(len_1 * (w - 1)) / log(w)) + 1; we precompute */ +#define SPX_WOTS_LEN2 3 + +#define SPX_WOTS_LEN (SPX_WOTS_LEN1 + SPX_WOTS_LEN2) +#define SPX_WOTS_BYTES (SPX_WOTS_LEN * SPX_N) +#define SPX_WOTS_PK_BYTES SPX_WOTS_BYTES + +/* Subtree size. */ +#define SPX_TREE_HEIGHT (SPX_FULL_HEIGHT / SPX_D) + +/* FORS parameters. */ +#define SPX_FORS_MSG_BYTES ((SPX_FORS_HEIGHT * SPX_FORS_TREES + 7) / 8) +#define SPX_FORS_BYTES ((SPX_FORS_HEIGHT + 1) * SPX_FORS_TREES * SPX_N) +#define SPX_FORS_PK_BYTES SPX_N + +/* Resulting SPX sizes. */ +#define SPX_BYTES (SPX_N + SPX_FORS_BYTES + SPX_D * SPX_WOTS_BYTES +\ + SPX_FULL_HEIGHT * SPX_N) +#define SPX_PK_BYTES (2 * SPX_N) +#define SPX_SK_BYTES (2 * SPX_N + SPX_PK_BYTES) + +/* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ +#define SPX_OPTRAND_BYTES 32 + +#endif diff --git a/crypto_sign/sphincs-haraka-128f-robust/clean/sign.c b/crypto_sign/sphincs-haraka-128f-robust/clean/sign.c new file mode 100644 index 00000000..977d01d2 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128f-robust/clean/sign.c @@ -0,0 +1,344 @@ +#include +#include +#include + +#include "address.h" +#include "api.h" +#include "fors.h" +#include "hash.h" +#include "params.h" +#include "randombytes.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +/** + * Computes the leaf at a given address. First generates the WOTS key pair, + * then computes leaf by hashing horizontally. + */ +static void wots_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t tree_addr[8]) { + unsigned char pk[SPX_WOTS_BYTES]; + uint32_t wots_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_keypair_addr( + wots_addr, addr_idx); + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_wots_gen_pk( + pk, sk_seed, pub_seed, wots_addr); + + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_thash_WOTS_LEN( + leaf, pk, pub_seed, wots_pk_addr); +} + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_crypto_sign_secretkeybytes(void) { + return PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_CRYPTO_SECRETKEYBYTES; +} + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_crypto_sign_publickeybytes(void) { + return PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES; +} + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_crypto_sign_bytes(void) { + return PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_CRYPTO_BYTES; +} + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_crypto_sign_seedbytes(void) { + return PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_CRYPTO_SEEDBYTES; +} + +/* + * Generates an SPX key pair given a seed of length + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed) { + /* We do not need the auth path in key generation, but it simplifies the + code to have just one treehash routine that computes both root and path + in one function. */ + unsigned char auth_path[SPX_TREE_HEIGHT * SPX_N]; + uint32_t top_tree_addr[8] = {0}; + + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_layer_addr( + top_tree_addr, SPX_D - 1); + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_type( + top_tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */ + memcpy(sk, seed, PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_CRYPTO_SEEDBYTES); + + memcpy(pk, sk + 2 * SPX_N, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_initialize_hash_function(pk, sk); + + /* Compute root node of the top-most subtree. */ + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_treehash_TREE_HEIGHT( + sk + 3 * SPX_N, auth_path, sk, sk + 2 * SPX_N, 0, 0, + wots_gen_leaf, top_tree_addr); + + memcpy(pk + SPX_N, sk + 3 * SPX_N, SPX_N); + + return 0; +} + +/* + * Generates an SPX key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk) { + unsigned char seed[PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_CRYPTO_SEEDBYTES]; + randombytes(seed, PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_CRYPTO_SEEDBYTES); + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_crypto_sign_seed_keypair( + pk, sk, seed); + + return 0; +} + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + const unsigned char *sk_seed = sk; + const unsigned char *sk_prf = sk + SPX_N; + const unsigned char *pk = sk + 2 * SPX_N; + const unsigned char *pub_seed = pk; + + unsigned char optrand[SPX_N]; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char root[SPX_N]; + uint32_t i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_initialize_hash_function( + pub_seed, sk_seed); + + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ + randombytes(optrand, SPX_N); + /* Compute the digest randomization value. */ + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_gen_message_random( + sig, sk_prf, optrand, m, mlen); + + /* Derive the message digest and leaf index from R, PK and M. */ + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Sign the message hash using FORS. */ + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_fors_sign( + sig, root, mhash, sk_seed, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Compute a WOTS signature. */ + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_wots_sign( + sig, root, sk_seed, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the authentication path for the used WOTS leaf. */ + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_treehash_TREE_HEIGHT( + root, sig, sk_seed, pub_seed, idx_leaf, 0, + wots_gen_leaf, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + *siglen = SPX_BYTES; + + return 0; +} + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk) { + const unsigned char *pub_seed = pk; + const unsigned char *pub_root = pk + SPX_N; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char wots_pk[SPX_WOTS_BYTES]; + unsigned char root[SPX_N]; + unsigned char leaf[SPX_N]; + unsigned int i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + if (siglen != SPX_BYTES) { + return -1; + } + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_initialize_hash_function( + pub_seed, NULL); + + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + /* Derive the message digest and leaf index from R || PK || M. */ + /* The additional SPX_N is a result of the hash domain separator. */ + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + /* Layer correctly defaults to 0, so no need to set_layer_addr */ + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_fors_pk_from_sig( + root, sig, mhash, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + /* For each subtree.. */ + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + + /* The WOTS public key is only correct if the signature was correct. */ + /* Initially, root is the FORS pk, but on subsequent iterations it is + the root of the subtree below the currently processed subtree. */ + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_wots_pk_from_sig( + wots_pk, sig, root, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the leaf node using the WOTS public key. */ + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_thash_WOTS_LEN( + leaf, wots_pk, pub_seed, wots_pk_addr); + + /* Compute the root node of this subtree. */ + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_compute_root( + root, leaf, idx_leaf, 0, sig, SPX_TREE_HEIGHT, + pub_seed, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + /* Check if the root node equals the root node in the public key. */ + if (memcmp(root, pub_root, SPX_N) != 0) { + return -1; + } + + return 0; +} + + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + size_t siglen; + + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_crypto_sign_signature( + sm, &siglen, m, mlen, sk); + + memmove(sm + SPX_BYTES, m, mlen); + *smlen = siglen + mlen; + + return 0; +} + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk) { + /* The API caller does not necessarily know what size a signature should be + but SPHINCS+ signatures are always exactly SPX_BYTES. */ + if (smlen < SPX_BYTES) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + *mlen = smlen - SPX_BYTES; + + if (PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_crypto_sign_verify( + sm, SPX_BYTES, sm + SPX_BYTES, *mlen, pk)) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + /* If verification was successful, move the message to the right place. */ + memmove(m, sm + SPX_BYTES, *mlen); + + return 0; +} diff --git a/crypto_sign/sphincs-haraka-128f-robust/clean/thash.h b/crypto_sign/sphincs-haraka-128f-robust/clean/thash.h new file mode 100644 index 00000000..355d680f --- /dev/null +++ b/crypto_sign/sphincs-haraka-128f-robust/clean/thash.h @@ -0,0 +1,22 @@ +#ifndef SPX_THASH_H +#define SPX_THASH_H + +#include + +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-haraka-128f-robust/clean/thash_haraka_robust.c b/crypto_sign/sphincs-haraka-128f-robust/clean/thash_haraka_robust.c new file mode 100644 index 00000000..aa86229d --- /dev/null +++ b/crypto_sign/sphincs-haraka-128f-robust/clean/thash_haraka_robust.c @@ -0,0 +1,88 @@ +#include +#include + +#include "address.h" +#include "params.h" +#include "thash.h" + +#include "haraka.h" + +/** + * Takes an array of inblocks concatenated arrays of SPX_N bytes. + */ +static void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_thash( + unsigned char *out, unsigned char *buf, + const unsigned char *in, unsigned int inblocks, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char *bitmask = buf + SPX_ADDR_BYTES; + unsigned char outbuf[32]; + unsigned char buf_tmp[64]; + unsigned int i; + + (void)pub_seed; /* Suppress an 'unused parameter' warning. */ + + if (inblocks == 1) { + /* F function */ + /* Since SPX_N may be smaller than 32, we need a temporary buffer. */ + memset(buf_tmp, 0, 64); + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_addr_to_bytes(buf_tmp, addr); + + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka256(outbuf, buf_tmp); + for (i = 0; i < inblocks * SPX_N; i++) { + buf_tmp[SPX_ADDR_BYTES + i] = in[i] ^ outbuf[i]; + } + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka512(outbuf, buf_tmp); + memcpy(out, outbuf, SPX_N); + } else { + /* All other tweakable hashes*/ + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_addr_to_bytes(buf, addr); + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka_S( + bitmask, inblocks * SPX_N, buf, SPX_ADDR_BYTES); + + for (i = 0; i < inblocks * SPX_N; i++) { + buf[SPX_ADDR_BYTES + i] = in[i] ^ bitmask[i]; + } + + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka_S( + out, SPX_N, buf, SPX_ADDR_BYTES + inblocks * SPX_N); + } +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_ADDR_BYTES + 1 * SPX_N]; + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_thash( + out, buf, in, 1, pub_seed, addr); +} + +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_ADDR_BYTES + 2 * SPX_N]; + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_thash( + out, buf, in, 2, pub_seed, addr); +} + +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_ADDR_BYTES + SPX_WOTS_LEN * SPX_N]; + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_thash( + out, buf, in, SPX_WOTS_LEN, pub_seed, addr); +} + +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_ADDR_BYTES + SPX_FORS_TREES * SPX_N]; + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_thash( + out, buf, in, SPX_FORS_TREES, pub_seed, addr); +} diff --git a/crypto_sign/sphincs-haraka-128f-robust/clean/utils.c b/crypto_sign/sphincs-haraka-128f-robust/clean/utils.c new file mode 100644 index 00000000..7fe7d983 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128f-robust/clean/utils.c @@ -0,0 +1,192 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in) { + + /* Iterate over out in decreasing order, for big-endianness. */ + for (size_t i = outlen; i > 0; i--) { + out[i - 1] = in & 0xff; + in = in >> 8; + } +} + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen) { + unsigned long long retval = 0; + + for (size_t i = 0; i < inlen; i++) { + retval |= ((unsigned long long)in[i]) << (8 * (inlen - 1 - i)); + } + return retval; +} + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + unsigned char buffer[2 * SPX_N]; + + /* If leaf_idx is odd (last bit = 1), current path element is a right child + and auth_path has to go left. Otherwise it is the other way around. */ + if (leaf_idx & 1) { + memcpy(buffer + SPX_N, leaf, SPX_N); + memcpy(buffer, auth_path, SPX_N); + } else { + memcpy(buffer, leaf, SPX_N); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + + for (i = 0; i < tree_height - 1; i++) { + leaf_idx >>= 1; + idx_offset >>= 1; + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_tree_height(addr, i + 1); + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + + /* Pick the right or left neighbor, depending on parity of the node. */ + if (leaf_idx & 1) { + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_thash_2( + buffer + SPX_N, buffer, pub_seed, addr); + memcpy(buffer, auth_path, SPX_N); + } else { + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_thash_2( + buffer, buffer, pub_seed, addr); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + } + + /* The last iteration is exceptional; we do not copy an auth_path node. */ + leaf_idx >>= 1; + idx_offset >>= 1; + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_tree_height(addr, tree_height); + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_thash_2( + root, buffer, pub_seed, addr); +} + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +static void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_treehash( + unsigned char *root, unsigned char *auth_path, + unsigned char *stack, unsigned int *heights, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned int offset = 0; + uint32_t idx; + uint32_t tree_idx; + + for (idx = 0; idx < (uint32_t)(1 << tree_height); idx++) { + /* Add the next leaf node to the stack. */ + gen_leaf(stack + offset * SPX_N, + sk_seed, pub_seed, idx + idx_offset, tree_addr); + offset++; + heights[offset - 1] = 0; + + /* If this is a node we need for the auth path.. */ + if ((leaf_idx ^ 0x1) == idx) { + memcpy(auth_path, stack + (offset - 1)*SPX_N, SPX_N); + } + + /* While the top-most nodes are of equal height.. */ + while (offset >= 2 && heights[offset - 1] == heights[offset - 2]) { + /* Compute index of the new node, in the next layer. */ + tree_idx = (idx >> (heights[offset - 1] + 1)); + + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_tree_height( + tree_addr, heights[offset - 1] + 1); + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_tree_index( + tree_addr, tree_idx + (idx_offset >> (heights[offset - 1] + 1))); + /* Hash the top-most nodes from the stack together. */ + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_thash_2( + stack + (offset - 2)*SPX_N, stack + (offset - 2)*SPX_N, + pub_seed, tree_addr); + offset--; + /* Note that the top-most node is now one layer higher. */ + heights[offset - 1]++; + + /* If this is a node we need for the auth path.. */ + if (((leaf_idx >> heights[offset - 1]) ^ 0x1) == tree_idx) { + memcpy(auth_path + heights[offset - 1]*SPX_N, + stack + (offset - 1)*SPX_N, SPX_N); + } + } + } + memcpy(root, stack, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_FORS_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_FORS_HEIGHT + 1]; + + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_FORS_HEIGHT, gen_leaf, tree_addr); +} + +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_TREE_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_TREE_HEIGHT + 1]; + + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_TREE_HEIGHT, gen_leaf, tree_addr); +} diff --git a/crypto_sign/sphincs-haraka-128f-robust/clean/utils.h b/crypto_sign/sphincs-haraka-128f-robust/clean/utils.h new file mode 100644 index 00000000..50fa131a --- /dev/null +++ b/crypto_sign/sphincs-haraka-128f-robust/clean/utils.h @@ -0,0 +1,60 @@ +#ifndef SPX_UTILS_H +#define SPX_UTILS_H + +#include "params.h" +#include +#include + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in); + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen); + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-haraka-128f-robust/clean/wots.c b/crypto_sign/sphincs-haraka-128f-robust/clean/wots.c new file mode 100644 index 00000000..8a806365 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128f-robust/clean/wots.c @@ -0,0 +1,161 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +// TODO clarify address expectations, and make them more uniform. +// TODO i.e. do we expect types to be set already? +// TODO and do we expect modifications or copies? + +/** + * Computes the starting value for a chain, i.e. the secret key. + * Expects the address to be complete up to the chain address. + */ +static void wots_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t wots_addr[8]) { + /* Make sure that the hash address is actually zeroed. */ + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_hash_addr(wots_addr, 0); + + /* Generate sk element. */ + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_prf_addr(sk, sk_seed, wots_addr); +} + +/** + * Computes the chaining function. + * out and in have to be n-byte arrays. + * + * Interprets in as start-th value of the chain. + * addr has to contain the address of the chain. + */ +static void gen_chain(unsigned char *out, const unsigned char *in, + unsigned int start, unsigned int steps, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + /* Initialize out with the value at position 'start'. */ + memcpy(out, in, SPX_N); + + /* Iterate 'steps' calls to the hash function. */ + for (i = start; i < (start + steps) && i < SPX_WOTS_W; i++) { + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_hash_addr(addr, i); + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_thash_1( + out, out, pub_seed, addr); + } +} + +/** + * base_w algorithm as described in draft. + * Interprets an array of bytes as integers in base w. + * This only works when log_w is a divisor of 8. + */ +static void base_w(unsigned int *output, const size_t out_len, + const unsigned char *input) { + size_t in = 0; + size_t out = 0; + unsigned char total = 0; + unsigned int bits = 0; + size_t consumed; + + for (consumed = 0; consumed < out_len; consumed++) { + if (bits == 0) { + total = input[in]; + in++; + bits += 8; + } + bits -= SPX_WOTS_LOGW; + output[out] = (unsigned int)((total >> bits) & (SPX_WOTS_W - 1)); + out++; + } +} + +/* Computes the WOTS+ checksum over a message (in base_w). */ +static void wots_checksum(unsigned int *csum_base_w, + const unsigned int *msg_base_w) { + unsigned int csum = 0; + unsigned char csum_bytes[(SPX_WOTS_LEN2 * SPX_WOTS_LOGW + 7) / 8]; + unsigned int i; + + /* Compute checksum. */ + for (i = 0; i < SPX_WOTS_LEN1; i++) { + csum += SPX_WOTS_W - 1 - msg_base_w[i]; + } + + /* Convert checksum to base_w. */ + /* Make sure expected empty zero bits are the least significant bits. */ + csum = csum << (8 - ((SPX_WOTS_LEN2 * SPX_WOTS_LOGW) % 8)); + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_ull_to_bytes( + csum_bytes, sizeof(csum_bytes), csum); + base_w(csum_base_w, SPX_WOTS_LEN2, csum_bytes); +} + +/* Takes a message and derives the matching chain lengths. */ +static void chain_lengths(unsigned int *lengths, const unsigned char *msg) { + base_w(lengths, SPX_WOTS_LEN1, msg); + wots_checksum(lengths + SPX_WOTS_LEN1, lengths); +} + +/** + * WOTS key generation. Takes a 32 byte sk_seed, expands it to WOTS private key + * elements and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(pk + i * SPX_N, sk_seed, addr); + gen_chain(pk + i * SPX_N, pk + i * SPX_N, + 0, SPX_WOTS_W - 1, pub_seed, addr); + } +} + +/** + * Takes a n-byte message and the 32-byte sk_see to compute a signature 'sig'. + */ +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(sig + i * SPX_N, sk_seed, addr); + gen_chain(sig + i * SPX_N, sig + i * SPX_N, 0, lengths[i], pub_seed, addr); + } +} + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_set_chain_addr(addr, i); + gen_chain(pk + i * SPX_N, sig + i * SPX_N, + lengths[i], SPX_WOTS_W - 1 - lengths[i], pub_seed, addr); + } +} diff --git a/crypto_sign/sphincs-haraka-128f-robust/clean/wots.h b/crypto_sign/sphincs-haraka-128f-robust/clean/wots.h new file mode 100644 index 00000000..c0393e26 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128f-robust/clean/wots.h @@ -0,0 +1,38 @@ +#ifndef SPX_WOTS_H +#define SPX_WOTS_H + +#include "params.h" +#include + +/** + * WOTS key generation. Takes a 32 byte seed for the private key, expands it to + * a full WOTS private key and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * Takes a n-byte message and the 32-byte seed for the private key to compute a + * signature that is placed at 'sig'. + */ +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]); + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-haraka-128f-simple/META.yml b/crypto_sign/sphincs-haraka-128f-simple/META.yml new file mode 100644 index 00000000..f7783474 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128f-simple/META.yml @@ -0,0 +1,27 @@ +name: SPHINCS+ +type: signature +claimed-nist-level: 1 +length-public-key: 32 +length-secret-key: 64 +length-signature: 16976 +testvectors-sha256: b9ea5703411a79c215a2643862bf4924ff62eeec08a0d1e328e39f47417fec8f +principal-submitter: Andreas Hülsing +auxiliary-submitters: + - Jean-Philippe Aumasson + - Daniel J. Bernstein, + - Christoph Dobraunig + - Maria Eichlseder + - Scott Fluhrer + - Stefan-Lukas Gazdag + - Panos Kampanakis + - Stefan Kölbl + - Tanja Lange + - Martin M. Lauridsen + - Florian Mendel + - Ruben Niederhagen + - Christian Rechberger + - Joost Rijneveld + - Peter Schwabe +implementations: + - name: clean + version: https://github.com/sphincs/sphincsplus/commit/77755c94d0bc744478044d6efbb888dc13156441 diff --git a/crypto_sign/sphincs-haraka-128f-simple/clean/LICENSE b/crypto_sign/sphincs-haraka-128f-simple/clean/LICENSE new file mode 100644 index 00000000..670154e3 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128f-simple/clean/LICENSE @@ -0,0 +1,116 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + diff --git a/crypto_sign/sphincs-haraka-128f-simple/clean/Makefile b/crypto_sign/sphincs-haraka-128f-simple/clean/Makefile new file mode 100644 index 00000000..b99f46b6 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128f-simple/clean/Makefile @@ -0,0 +1,20 @@ +# This Makefile can be used with GNU Make or BSD Make + +LIB=libsphincs-haraka-128f-simple_clean.a + +HEADERS = params.h address.h wots.h utils.h fors.h api.h hash.h thash.h haraka.h +OBJECTS = address.o wots.o utils.o fors.o sign.o hash_haraka.o thash_haraka_simple.o haraka.o + +CFLAGS=-O3 -Wall -Wconversion -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) + +all: $(LIB) + +%.o: %.c $(HEADERS) + $(CC) $(CFLAGS) -c -o $@ $< + +$(LIB): $(OBJECTS) + $(AR) -r $@ $(OBJECTS) + +clean: + $(RM) $(OBJECTS) + $(RM) $(LIB) diff --git a/crypto_sign/sphincs-haraka-128f-simple/clean/Makefile.Microsoft_nmake b/crypto_sign/sphincs-haraka-128f-simple/clean/Makefile.Microsoft_nmake new file mode 100644 index 00000000..a7cc9c83 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128f-simple/clean/Makefile.Microsoft_nmake @@ -0,0 +1,19 @@ +# This Makefile can be used with Microsoft Visual Studio's nmake using the command: +# nmake /f Makefile.Microsoft_nmake + +LIBRARY=libsphincs-haraka-128f-simple_clean.lib +OBJECTS=address.obj wots.obj utils.obj fors.obj sign.obj hash_haraka.obj thash_haraka_simple.obj haraka.obj + +CFLAGS=/nologo /I ..\..\..\common /W4 /WX + +all: $(LIBRARY) + +# Make sure objects are recompiled if headers change. +$(OBJECTS): *.h + +$(LIBRARY): $(OBJECTS) + LIB.EXE /NOLOGO /WX /OUT:$@ $** + +clean: + -DEL $(OBJECTS) + -DEL $(LIBRARY) diff --git a/crypto_sign/sphincs-haraka-128f-simple/clean/address.c b/crypto_sign/sphincs-haraka-128f-simple/clean/address.c new file mode 100644 index 00000000..ce8106a5 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128f-simple/clean/address.c @@ -0,0 +1,78 @@ +#include + +#include "address.h" +#include "params.h" +#include "utils.h" + +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]) { + int i; + + for (i = 0; i < 8; i++) { + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_ull_to_bytes( + bytes + i * 4, 4, addr[i]); + } +} + +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer) { + addr[0] = layer; +} + +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree) { + addr[1] = 0; + addr[2] = (uint32_t) (tree >> 32); + addr[3] = (uint32_t) tree; +} + +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_type( + uint32_t addr[8], uint32_t type) { + addr[4] = type; +} + +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; +} + +/* These functions are used for OTS addresses. */ + +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair) { + addr[5] = keypair; +} + +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; + out[5] = in[5]; +} + +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain) { + addr[6] = chain; +} + +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash) { + addr[7] = hash; +} + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height) { + addr[6] = tree_height; +} + +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index) { + addr[7] = tree_index; +} diff --git a/crypto_sign/sphincs-haraka-128f-simple/clean/address.h b/crypto_sign/sphincs-haraka-128f-simple/clean/address.h new file mode 100644 index 00000000..c3de4306 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128f-simple/clean/address.h @@ -0,0 +1,50 @@ +#ifndef SPX_ADDRESS_H +#define SPX_ADDRESS_H + +#include + +#define SPX_ADDR_TYPE_WOTS 0 +#define SPX_ADDR_TYPE_WOTSPK 1 +#define SPX_ADDR_TYPE_HASHTREE 2 +#define SPX_ADDR_TYPE_FORSTREE 3 +#define SPX_ADDR_TYPE_FORSPK 4 + +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer); + +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree); + +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_type( + uint32_t addr[8], uint32_t type); + +/* Copies the layer and tree part of one address into the other */ +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for WOTS and FORS addresses. */ + +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair); + +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain); + +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash); + +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height); + +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index); + +#endif diff --git a/crypto_sign/sphincs-haraka-128f-simple/clean/api.h b/crypto_sign/sphincs-haraka-128f-simple/clean/api.h new file mode 100644 index 00000000..04bedc68 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128f-simple/clean/api.h @@ -0,0 +1,78 @@ +#ifndef PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_API_H +#define PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_API_H + +#include +#include + +#define PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_CRYPTO_ALGNAME "SPHINCS+" + +#define PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_CRYPTO_SECRETKEYBYTES 64 +#define PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES 32 +#define PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_CRYPTO_BYTES 16976 +#define PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_CRYPTO_SEEDBYTES 48 + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_crypto_sign_secretkeybytes(void); + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_crypto_sign_publickeybytes(void); + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_crypto_sign_bytes(void); + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_crypto_sign_seedbytes(void); + +/* + * Generates a SPHINCS+ key pair given a seed. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed); + +/* + * Generates a SPHINCS+ key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk); + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk); + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk); + +#endif diff --git a/crypto_sign/sphincs-haraka-128f-simple/clean/fors.c b/crypto_sign/sphincs-haraka-128f-simple/clean/fors.c new file mode 100644 index 00000000..5a84fe7b --- /dev/null +++ b/crypto_sign/sphincs-haraka-128f-simple/clean/fors.c @@ -0,0 +1,164 @@ +#include +#include +#include + +#include "address.h" +#include "fors.h" +#include "hash.h" +#include "thash.h" +#include "utils.h" + +static void fors_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_prf_addr( + sk, sk_seed, fors_leaf_addr); +} + +static void fors_sk_to_leaf(unsigned char *leaf, const unsigned char *sk, + const unsigned char *pub_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_thash_1( + leaf, sk, pub_seed, fors_leaf_addr); +} + +static void fors_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t fors_tree_addr[8]) { + uint32_t fors_leaf_addr[8] = {0}; + + /* Only copy the parts that must be kept in fors_leaf_addr. */ + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_copy_keypair_addr( + fors_leaf_addr, fors_tree_addr); + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_type( + fors_leaf_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_tree_index( + fors_leaf_addr, addr_idx); + + fors_gen_sk(leaf, sk_seed, fors_leaf_addr); + fors_sk_to_leaf(leaf, leaf, pub_seed, fors_leaf_addr); +} + +/** + * Interprets m as SPX_FORS_HEIGHT-bit unsigned integers. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + * Assumes indices has space for SPX_FORS_TREES integers. + */ +static void message_to_indices(uint32_t *indices, const unsigned char *m) { + unsigned int i, j; + unsigned int offset = 0; + + for (i = 0; i < SPX_FORS_TREES; i++) { + indices[i] = 0; + for (j = 0; j < SPX_FORS_HEIGHT; j++) { + indices[i] ^= (((uint32_t)m[offset >> 3] >> (offset & 0x7)) & 0x1) << j; + offset++; + } + } +} + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Include the secret key part that produces the selected leaf node. */ + fors_gen_sk(sig, sk_seed, fors_tree_addr); + sig += SPX_N; + + /* Compute the authentication path for this leaf node. */ + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_treehash_FORS_HEIGHT( + roots + i * SPX_N, sig, sk_seed, pub_seed, + indices[i], idx_offset, fors_gen_leaf, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + unsigned char leaf[SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Derive the leaf from the included secret key part. */ + fors_sk_to_leaf(leaf, sig, pub_seed, fors_tree_addr); + sig += SPX_N; + + /* Derive the corresponding root node of this tree. */ + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_compute_root( + roots + i * SPX_N, leaf, indices[i], idx_offset, sig, + SPX_FORS_HEIGHT, pub_seed, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} diff --git a/crypto_sign/sphincs-haraka-128f-simple/clean/fors.h b/crypto_sign/sphincs-haraka-128f-simple/clean/fors.h new file mode 100644 index 00000000..3fb1be7d --- /dev/null +++ b/crypto_sign/sphincs-haraka-128f-simple/clean/fors.h @@ -0,0 +1,30 @@ +#ifndef SPX_FORS_H +#define SPX_FORS_H + +#include + +#include "params.h" + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]); + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-haraka-128f-simple/clean/haraka.c b/crypto_sign/sphincs-haraka-128f-simple/clean/haraka.c new file mode 100644 index 00000000..0ce00c98 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128f-simple/clean/haraka.c @@ -0,0 +1,965 @@ +/* + * Constant time implementation of the Haraka hash function. + * + * The bit-sliced implementation of the AES round functions are + * based on the AES implementation in BearSSL written + * by Thomas Pornin + */ + +#include +#include +#include + +#include "haraka.h" + +#define HARAKAS_RATE 32 + +static const uint64_t haraka512_rc64[10][8] = { + {0x24cf0ab9086f628b, 0xbdd6eeecc83b8382, 0xd96fb0306cdad0a7, 0xaace082ac8f95f89, 0x449d8e8870d7041f, 0x49bb2f80b2b3e2f8, 0x0569ae98d93bb258, 0x23dc9691e7d6a4b1}, + {0xd8ba10ede0fe5b6e, 0x7ecf7dbe424c7b8e, 0x6ea9949c6df62a31, 0xbf3f3c97ec9c313e, 0x241d03a196a1861e, 0xead3a51116e5a2ea, 0x77d479fcad9574e3, 0x18657a1af894b7a0}, + {0x10671e1a7f595522, 0xd9a00ff675d28c7b, 0x2f1edf0d2b9ba661, 0xb8ff58b8e3de45f9, 0xee29261da9865c02, 0xd1532aa4b50bdf43, 0x8bf858159b231bb1, 0xdf17439d22d4f599}, + {0xdd4b2f0870b918c0, 0x757a81f3b39b1bb6, 0x7a5c556898952e3f, 0x7dd70a16d915d87a, 0x3ae61971982b8301, 0xc3ab319e030412be, 0x17c0033ac094a8cb, 0x5a0630fc1a8dc4ef}, + {0x17708988c1632f73, 0xf92ddae090b44f4f, 0x11ac0285c43aa314, 0x509059941936b8ba, 0xd03e152fa2ce9b69, 0x3fbcbcb63a32998b, 0x6204696d692254f7, 0x915542ed93ec59b4}, + {0xf4ed94aa8879236e, 0xff6cb41cd38e03c0, 0x069b38602368aeab, 0x669495b820f0ddba, 0xf42013b1b8bf9e3d, 0xcf935efe6439734d, 0xbc1dcf42ca29e3f8, 0x7e6d3ed29f78ad67}, + {0xf3b0f6837ffcddaa, 0x3a76faef934ddf41, 0xcec7ae583a9c8e35, 0xe4dd18c68f0260af, 0x2c0e5df1ad398eaa, 0x478df5236ae22e8c, 0xfb944c46fe865f39, 0xaa48f82f028132ba}, + {0x231b9ae2b76aca77, 0x292a76a712db0b40, 0x5850625dc8134491, 0x73137dd469810fb5, 0x8a12a6a202a474fd, 0xd36fd9daa78bdb80, 0xb34c5e733505706f, 0xbaf1cdca818d9d96}, + {0x2e99781335e8c641, 0xbddfe5cce47d560e, 0xf74e9bf32e5e040c, 0x1d7a709d65996be9, 0x670df36a9cf66cdd, 0xd05ef84a176a2875, 0x0f888e828cb1c44e, 0x1a79e9c9727b052c}, + {0x83497348628d84de, 0x2e9387d51f22a754, 0xb000068da2f852d6, 0x378c9e1190fd6fe5, 0x870027c316de7293, 0xe51a9d4462e047bb, 0x90ecf7f8c6251195, 0x655953bfbed90a9c}, +}; + +static uint64_t tweaked512_rc64[10][8]; +static uint32_t tweaked256_rc32[10][8]; +static uint32_t tweaked256_rc32_sseed[10][8]; + +static inline uint32_t br_dec32le(const unsigned char *src) { + return (uint32_t)src[0] + | ((uint32_t)src[1] << 8) + | ((uint32_t)src[2] << 16) + | ((uint32_t)src[3] << 24); +} + +static void br_range_dec32le(uint32_t *v, size_t num, const unsigned char *src) { + while (num-- > 0) { + *v ++ = br_dec32le(src); + src += 4; + } +} + +static inline void br_enc32le(unsigned char *dst, uint32_t x) { + dst[0] = (unsigned char)x; + dst[1] = (unsigned char)(x >> 8); + dst[2] = (unsigned char)(x >> 16); + dst[3] = (unsigned char)(x >> 24); +} + + +static void br_range_enc32le(unsigned char *dst, const uint32_t *v, size_t num) { + while (num-- > 0) { + br_enc32le(dst, *v ++); + dst += 4; + } +} + +static void br_aes_ct64_bitslice_Sbox(uint64_t *q) { + /* + * This S-box implementation is a straightforward translation of + * the circuit described by Boyar and Peralta in "A new + * combinational logic minimization technique with applications + * to cryptology" (https://eprint.iacr.org/2009/191.pdf). + * + * Note that variables x* (input) and s* (output) are numbered + * in "reverse" order (x0 is the high bit, x7 is the low bit). + */ + + uint64_t x0, x1, x2, x3, x4, x5, x6, x7; + uint64_t y1, y2, y3, y4, y5, y6, y7, y8, y9; + uint64_t y10, y11, y12, y13, y14, y15, y16, y17, y18, y19; + uint64_t y20, y21; + uint64_t z0, z1, z2, z3, z4, z5, z6, z7, z8, z9; + uint64_t z10, z11, z12, z13, z14, z15, z16, z17; + uint64_t t0, t1, t2, t3, t4, t5, t6, t7, t8, t9; + uint64_t t10, t11, t12, t13, t14, t15, t16, t17, t18, t19; + uint64_t t20, t21, t22, t23, t24, t25, t26, t27, t28, t29; + uint64_t t30, t31, t32, t33, t34, t35, t36, t37, t38, t39; + uint64_t t40, t41, t42, t43, t44, t45, t46, t47, t48, t49; + uint64_t t50, t51, t52, t53, t54, t55, t56, t57, t58, t59; + uint64_t t60, t61, t62, t63, t64, t65, t66, t67; + uint64_t s0, s1, s2, s3, s4, s5, s6, s7; + + x0 = q[7]; + x1 = q[6]; + x2 = q[5]; + x3 = q[4]; + x4 = q[3]; + x5 = q[2]; + x6 = q[1]; + x7 = q[0]; + + /* + * Top linear transformation. + */ + y14 = x3 ^ x5; + y13 = x0 ^ x6; + y9 = x0 ^ x3; + y8 = x0 ^ x5; + t0 = x1 ^ x2; + y1 = t0 ^ x7; + y4 = y1 ^ x3; + y12 = y13 ^ y14; + y2 = y1 ^ x0; + y5 = y1 ^ x6; + y3 = y5 ^ y8; + t1 = x4 ^ y12; + y15 = t1 ^ x5; + y20 = t1 ^ x1; + y6 = y15 ^ x7; + y10 = y15 ^ t0; + y11 = y20 ^ y9; + y7 = x7 ^ y11; + y17 = y10 ^ y11; + y19 = y10 ^ y8; + y16 = t0 ^ y11; + y21 = y13 ^ y16; + y18 = x0 ^ y16; + + /* + * Non-linear section. + */ + t2 = y12 & y15; + t3 = y3 & y6; + t4 = t3 ^ t2; + t5 = y4 & x7; + t6 = t5 ^ t2; + t7 = y13 & y16; + t8 = y5 & y1; + t9 = t8 ^ t7; + t10 = y2 & y7; + t11 = t10 ^ t7; + t12 = y9 & y11; + t13 = y14 & y17; + t14 = t13 ^ t12; + t15 = y8 & y10; + t16 = t15 ^ t12; + t17 = t4 ^ t14; + t18 = t6 ^ t16; + t19 = t9 ^ t14; + t20 = t11 ^ t16; + t21 = t17 ^ y20; + t22 = t18 ^ y19; + t23 = t19 ^ y21; + t24 = t20 ^ y18; + + t25 = t21 ^ t22; + t26 = t21 & t23; + t27 = t24 ^ t26; + t28 = t25 & t27; + t29 = t28 ^ t22; + t30 = t23 ^ t24; + t31 = t22 ^ t26; + t32 = t31 & t30; + t33 = t32 ^ t24; + t34 = t23 ^ t33; + t35 = t27 ^ t33; + t36 = t24 & t35; + t37 = t36 ^ t34; + t38 = t27 ^ t36; + t39 = t29 & t38; + t40 = t25 ^ t39; + + t41 = t40 ^ t37; + t42 = t29 ^ t33; + t43 = t29 ^ t40; + t44 = t33 ^ t37; + t45 = t42 ^ t41; + z0 = t44 & y15; + z1 = t37 & y6; + z2 = t33 & x7; + z3 = t43 & y16; + z4 = t40 & y1; + z5 = t29 & y7; + z6 = t42 & y11; + z7 = t45 & y17; + z8 = t41 & y10; + z9 = t44 & y12; + z10 = t37 & y3; + z11 = t33 & y4; + z12 = t43 & y13; + z13 = t40 & y5; + z14 = t29 & y2; + z15 = t42 & y9; + z16 = t45 & y14; + z17 = t41 & y8; + + /* + * Bottom linear transformation. + */ + t46 = z15 ^ z16; + t47 = z10 ^ z11; + t48 = z5 ^ z13; + t49 = z9 ^ z10; + t50 = z2 ^ z12; + t51 = z2 ^ z5; + t52 = z7 ^ z8; + t53 = z0 ^ z3; + t54 = z6 ^ z7; + t55 = z16 ^ z17; + t56 = z12 ^ t48; + t57 = t50 ^ t53; + t58 = z4 ^ t46; + t59 = z3 ^ t54; + t60 = t46 ^ t57; + t61 = z14 ^ t57; + t62 = t52 ^ t58; + t63 = t49 ^ t58; + t64 = z4 ^ t59; + t65 = t61 ^ t62; + t66 = z1 ^ t63; + s0 = t59 ^ t63; + s6 = t56 ^ ~t62; + s7 = t48 ^ ~t60; + t67 = t64 ^ t65; + s3 = t53 ^ t66; + s4 = t51 ^ t66; + s5 = t47 ^ t65; + s1 = t64 ^ ~s3; + s2 = t55 ^ ~t67; + + q[7] = s0; + q[6] = s1; + q[5] = s2; + q[4] = s3; + q[3] = s4; + q[2] = s5; + q[1] = s6; + q[0] = s7; +} + +static void br_aes_ct_bitslice_Sbox(uint32_t *q) { + /* + * This S-box implementation is a straightforward translation of + * the circuit described by Boyar and Peralta in "A new + * combinational logic minimization technique with applications + * to cryptology" (https://eprint.iacr.org/2009/191.pdf). + * + * Note that variables x* (input) and s* (output) are numbered + * in "reverse" order (x0 is the high bit, x7 is the low bit). + */ + + uint32_t x0, x1, x2, x3, x4, x5, x6, x7; + uint32_t y1, y2, y3, y4, y5, y6, y7, y8, y9; + uint32_t y10, y11, y12, y13, y14, y15, y16, y17, y18, y19; + uint32_t y20, y21; + uint32_t z0, z1, z2, z3, z4, z5, z6, z7, z8, z9; + uint32_t z10, z11, z12, z13, z14, z15, z16, z17; + uint32_t t0, t1, t2, t3, t4, t5, t6, t7, t8, t9; + uint32_t t10, t11, t12, t13, t14, t15, t16, t17, t18, t19; + uint32_t t20, t21, t22, t23, t24, t25, t26, t27, t28, t29; + uint32_t t30, t31, t32, t33, t34, t35, t36, t37, t38, t39; + uint32_t t40, t41, t42, t43, t44, t45, t46, t47, t48, t49; + uint32_t t50, t51, t52, t53, t54, t55, t56, t57, t58, t59; + uint32_t t60, t61, t62, t63, t64, t65, t66, t67; + uint32_t s0, s1, s2, s3, s4, s5, s6, s7; + + x0 = q[7]; + x1 = q[6]; + x2 = q[5]; + x3 = q[4]; + x4 = q[3]; + x5 = q[2]; + x6 = q[1]; + x7 = q[0]; + + /* + * Top linear transformation. + */ + y14 = x3 ^ x5; + y13 = x0 ^ x6; + y9 = x0 ^ x3; + y8 = x0 ^ x5; + t0 = x1 ^ x2; + y1 = t0 ^ x7; + y4 = y1 ^ x3; + y12 = y13 ^ y14; + y2 = y1 ^ x0; + y5 = y1 ^ x6; + y3 = y5 ^ y8; + t1 = x4 ^ y12; + y15 = t1 ^ x5; + y20 = t1 ^ x1; + y6 = y15 ^ x7; + y10 = y15 ^ t0; + y11 = y20 ^ y9; + y7 = x7 ^ y11; + y17 = y10 ^ y11; + y19 = y10 ^ y8; + y16 = t0 ^ y11; + y21 = y13 ^ y16; + y18 = x0 ^ y16; + + /* + * Non-linear section. + */ + t2 = y12 & y15; + t3 = y3 & y6; + t4 = t3 ^ t2; + t5 = y4 & x7; + t6 = t5 ^ t2; + t7 = y13 & y16; + t8 = y5 & y1; + t9 = t8 ^ t7; + t10 = y2 & y7; + t11 = t10 ^ t7; + t12 = y9 & y11; + t13 = y14 & y17; + t14 = t13 ^ t12; + t15 = y8 & y10; + t16 = t15 ^ t12; + t17 = t4 ^ t14; + t18 = t6 ^ t16; + t19 = t9 ^ t14; + t20 = t11 ^ t16; + t21 = t17 ^ y20; + t22 = t18 ^ y19; + t23 = t19 ^ y21; + t24 = t20 ^ y18; + + t25 = t21 ^ t22; + t26 = t21 & t23; + t27 = t24 ^ t26; + t28 = t25 & t27; + t29 = t28 ^ t22; + t30 = t23 ^ t24; + t31 = t22 ^ t26; + t32 = t31 & t30; + t33 = t32 ^ t24; + t34 = t23 ^ t33; + t35 = t27 ^ t33; + t36 = t24 & t35; + t37 = t36 ^ t34; + t38 = t27 ^ t36; + t39 = t29 & t38; + t40 = t25 ^ t39; + + t41 = t40 ^ t37; + t42 = t29 ^ t33; + t43 = t29 ^ t40; + t44 = t33 ^ t37; + t45 = t42 ^ t41; + z0 = t44 & y15; + z1 = t37 & y6; + z2 = t33 & x7; + z3 = t43 & y16; + z4 = t40 & y1; + z5 = t29 & y7; + z6 = t42 & y11; + z7 = t45 & y17; + z8 = t41 & y10; + z9 = t44 & y12; + z10 = t37 & y3; + z11 = t33 & y4; + z12 = t43 & y13; + z13 = t40 & y5; + z14 = t29 & y2; + z15 = t42 & y9; + z16 = t45 & y14; + z17 = t41 & y8; + + /* + * Bottom linear transformation. + */ + t46 = z15 ^ z16; + t47 = z10 ^ z11; + t48 = z5 ^ z13; + t49 = z9 ^ z10; + t50 = z2 ^ z12; + t51 = z2 ^ z5; + t52 = z7 ^ z8; + t53 = z0 ^ z3; + t54 = z6 ^ z7; + t55 = z16 ^ z17; + t56 = z12 ^ t48; + t57 = t50 ^ t53; + t58 = z4 ^ t46; + t59 = z3 ^ t54; + t60 = t46 ^ t57; + t61 = z14 ^ t57; + t62 = t52 ^ t58; + t63 = t49 ^ t58; + t64 = z4 ^ t59; + t65 = t61 ^ t62; + t66 = z1 ^ t63; + s0 = t59 ^ t63; + s6 = t56 ^ ~t62; + s7 = t48 ^ ~t60; + t67 = t64 ^ t65; + s3 = t53 ^ t66; + s4 = t51 ^ t66; + s5 = t47 ^ t65; + s1 = t64 ^ ~s3; + s2 = t55 ^ ~t67; + + q[7] = s0; + q[6] = s1; + q[5] = s2; + q[4] = s3; + q[3] = s4; + q[2] = s5; + q[1] = s6; + q[0] = s7; +} + +static void br_aes_ct_ortho(uint32_t *q) { +#define SWAPN_32(cl, ch, s, x, y) do { \ + uint32_t a, b; \ + a = (x); \ + b = (y); \ + (x) = (a & (uint32_t)(cl)) | ((b & (uint32_t)(cl)) << (s)); \ + (y) = ((a & (uint32_t)(ch)) >> (s)) | (b & (uint32_t)(ch)); \ + } while (0) + +#define SWAP2_32(x, y) SWAPN_32(0x55555555, 0xAAAAAAAA, 1, x, y) +#define SWAP4_32(x, y) SWAPN_32(0x33333333, 0xCCCCCCCC, 2, x, y) +#define SWAP8_32(x, y) SWAPN_32(0x0F0F0F0F, 0xF0F0F0F0, 4, x, y) + + SWAP2_32(q[0], q[1]); + SWAP2_32(q[2], q[3]); + SWAP2_32(q[4], q[5]); + SWAP2_32(q[6], q[7]); + + SWAP4_32(q[0], q[2]); + SWAP4_32(q[1], q[3]); + SWAP4_32(q[4], q[6]); + SWAP4_32(q[5], q[7]); + + SWAP8_32(q[0], q[4]); + SWAP8_32(q[1], q[5]); + SWAP8_32(q[2], q[6]); + SWAP8_32(q[3], q[7]); +} + +static inline void add_round_key32(uint32_t *q, const uint32_t *sk) { + q[0] ^= sk[0]; + q[1] ^= sk[1]; + q[2] ^= sk[2]; + q[3] ^= sk[3]; + q[4] ^= sk[4]; + q[5] ^= sk[5]; + q[6] ^= sk[6]; + q[7] ^= sk[7]; +} + +static inline void shift_rows32(uint32_t *q) { + int i; + + for (i = 0; i < 8; i++) { + uint32_t x; + + x = q[i]; + q[i] = (x & 0x000000FF) + | ((x & 0x0000FC00) >> 2) | ((x & 0x00000300) << 6) + | ((x & 0x00F00000) >> 4) | ((x & 0x000F0000) << 4) + | ((x & 0xC0000000) >> 6) | ((x & 0x3F000000) << 2); + } +} + +static inline uint32_t rotr16(uint32_t x) { + return (x << 16) | (x >> 16); +} + +static inline void mix_columns32(uint32_t *q) { + uint32_t q0, q1, q2, q3, q4, q5, q6, q7; + uint32_t r0, r1, r2, r3, r4, r5, r6, r7; + + q0 = q[0]; + q1 = q[1]; + q2 = q[2]; + q3 = q[3]; + q4 = q[4]; + q5 = q[5]; + q6 = q[6]; + q7 = q[7]; + r0 = (q0 >> 8) | (q0 << 24); + r1 = (q1 >> 8) | (q1 << 24); + r2 = (q2 >> 8) | (q2 << 24); + r3 = (q3 >> 8) | (q3 << 24); + r4 = (q4 >> 8) | (q4 << 24); + r5 = (q5 >> 8) | (q5 << 24); + r6 = (q6 >> 8) | (q6 << 24); + r7 = (q7 >> 8) | (q7 << 24); + + q[0] = q7 ^ r7 ^ r0 ^ rotr16(q0 ^ r0); + q[1] = q0 ^ r0 ^ q7 ^ r7 ^ r1 ^ rotr16(q1 ^ r1); + q[2] = q1 ^ r1 ^ r2 ^ rotr16(q2 ^ r2); + q[3] = q2 ^ r2 ^ q7 ^ r7 ^ r3 ^ rotr16(q3 ^ r3); + q[4] = q3 ^ r3 ^ q7 ^ r7 ^ r4 ^ rotr16(q4 ^ r4); + q[5] = q4 ^ r4 ^ r5 ^ rotr16(q5 ^ r5); + q[6] = q5 ^ r5 ^ r6 ^ rotr16(q6 ^ r6); + q[7] = q6 ^ r6 ^ r7 ^ rotr16(q7 ^ r7); +} + +static void br_aes_ct64_ortho(uint64_t *q) { +#define SWAPN(cl, ch, s, x, y) do { \ + uint64_t a, b; \ + a = (x); \ + b = (y); \ + (x) = (a & (uint64_t)(cl)) | ((b & (uint64_t)(cl)) << (s)); \ + (y) = ((a & (uint64_t)(ch)) >> (s)) | (b & (uint64_t)(ch)); \ + } while (0) + +#define SWAP2(x, y) SWAPN(0x5555555555555555, 0xAAAAAAAAAAAAAAAA, 1, x, y) +#define SWAP4(x, y) SWAPN(0x3333333333333333, 0xCCCCCCCCCCCCCCCC, 2, x, y) +#define SWAP8(x, y) SWAPN(0x0F0F0F0F0F0F0F0F, 0xF0F0F0F0F0F0F0F0, 4, x, y) + + SWAP2(q[0], q[1]); + SWAP2(q[2], q[3]); + SWAP2(q[4], q[5]); + SWAP2(q[6], q[7]); + + SWAP4(q[0], q[2]); + SWAP4(q[1], q[3]); + SWAP4(q[4], q[6]); + SWAP4(q[5], q[7]); + + SWAP8(q[0], q[4]); + SWAP8(q[1], q[5]); + SWAP8(q[2], q[6]); + SWAP8(q[3], q[7]); +} + + +static void br_aes_ct64_interleave_in(uint64_t *q0, uint64_t *q1, const uint32_t *w) { + uint64_t x0, x1, x2, x3; + + x0 = w[0]; + x1 = w[1]; + x2 = w[2]; + x3 = w[3]; + x0 |= (x0 << 16); + x1 |= (x1 << 16); + x2 |= (x2 << 16); + x3 |= (x3 << 16); + x0 &= (uint64_t)0x0000FFFF0000FFFF; + x1 &= (uint64_t)0x0000FFFF0000FFFF; + x2 &= (uint64_t)0x0000FFFF0000FFFF; + x3 &= (uint64_t)0x0000FFFF0000FFFF; + x0 |= (x0 << 8); + x1 |= (x1 << 8); + x2 |= (x2 << 8); + x3 |= (x3 << 8); + x0 &= (uint64_t)0x00FF00FF00FF00FF; + x1 &= (uint64_t)0x00FF00FF00FF00FF; + x2 &= (uint64_t)0x00FF00FF00FF00FF; + x3 &= (uint64_t)0x00FF00FF00FF00FF; + *q0 = x0 | (x2 << 8); + *q1 = x1 | (x3 << 8); +} + + +static void br_aes_ct64_interleave_out(uint32_t *w, uint64_t q0, uint64_t q1) { + uint64_t x0, x1, x2, x3; + + x0 = q0 & (uint64_t)0x00FF00FF00FF00FF; + x1 = q1 & (uint64_t)0x00FF00FF00FF00FF; + x2 = (q0 >> 8) & (uint64_t)0x00FF00FF00FF00FF; + x3 = (q1 >> 8) & (uint64_t)0x00FF00FF00FF00FF; + x0 |= (x0 >> 8); + x1 |= (x1 >> 8); + x2 |= (x2 >> 8); + x3 |= (x3 >> 8); + x0 &= (uint64_t)0x0000FFFF0000FFFF; + x1 &= (uint64_t)0x0000FFFF0000FFFF; + x2 &= (uint64_t)0x0000FFFF0000FFFF; + x3 &= (uint64_t)0x0000FFFF0000FFFF; + w[0] = (uint32_t)x0 | (uint32_t)(x0 >> 16); + w[1] = (uint32_t)x1 | (uint32_t)(x1 >> 16); + w[2] = (uint32_t)x2 | (uint32_t)(x2 >> 16); + w[3] = (uint32_t)x3 | (uint32_t)(x3 >> 16); +} + +static inline void add_round_key(uint64_t *q, const uint64_t *sk) { + q[0] ^= sk[0]; + q[1] ^= sk[1]; + q[2] ^= sk[2]; + q[3] ^= sk[3]; + q[4] ^= sk[4]; + q[5] ^= sk[5]; + q[6] ^= sk[6]; + q[7] ^= sk[7]; +} + +static inline void shift_rows(uint64_t *q) { + int i; + + for (i = 0; i < 8; i++) { + uint64_t x; + + x = q[i]; + q[i] = (x & (uint64_t)0x000000000000FFFF) + | ((x & (uint64_t)0x00000000FFF00000) >> 4) + | ((x & (uint64_t)0x00000000000F0000) << 12) + | ((x & (uint64_t)0x0000FF0000000000) >> 8) + | ((x & (uint64_t)0x000000FF00000000) << 8) + | ((x & (uint64_t)0xF000000000000000) >> 12) + | ((x & (uint64_t)0x0FFF000000000000) << 4); + } +} + +static inline uint64_t rotr32(uint64_t x) { + return (x << 32) | (x >> 32); +} + +static inline void mix_columns(uint64_t *q) { + uint64_t q0, q1, q2, q3, q4, q5, q6, q7; + uint64_t r0, r1, r2, r3, r4, r5, r6, r7; + + q0 = q[0]; + q1 = q[1]; + q2 = q[2]; + q3 = q[3]; + q4 = q[4]; + q5 = q[5]; + q6 = q[6]; + q7 = q[7]; + r0 = (q0 >> 16) | (q0 << 48); + r1 = (q1 >> 16) | (q1 << 48); + r2 = (q2 >> 16) | (q2 << 48); + r3 = (q3 >> 16) | (q3 << 48); + r4 = (q4 >> 16) | (q4 << 48); + r5 = (q5 >> 16) | (q5 << 48); + r6 = (q6 >> 16) | (q6 << 48); + r7 = (q7 >> 16) | (q7 << 48); + + q[0] = q7 ^ r7 ^ r0 ^ rotr32(q0 ^ r0); + q[1] = q0 ^ r0 ^ q7 ^ r7 ^ r1 ^ rotr32(q1 ^ r1); + q[2] = q1 ^ r1 ^ r2 ^ rotr32(q2 ^ r2); + q[3] = q2 ^ r2 ^ q7 ^ r7 ^ r3 ^ rotr32(q3 ^ r3); + q[4] = q3 ^ r3 ^ q7 ^ r7 ^ r4 ^ rotr32(q4 ^ r4); + q[5] = q4 ^ r4 ^ r5 ^ rotr32(q5 ^ r5); + q[6] = q5 ^ r5 ^ r6 ^ rotr32(q6 ^ r6); + q[7] = q6 ^ r6 ^ r7 ^ rotr32(q7 ^ r7); +} + +static void interleave_constant(uint64_t *out, const unsigned char *in) { + uint32_t tmp_32_constant[16]; + int i; + + br_range_dec32le(tmp_32_constant, 16, in); + for (i = 0; i < 4; i++) { + br_aes_ct64_interleave_in(&out[i], &out[i + 4], tmp_32_constant + (i << 2)); + } + br_aes_ct64_ortho(out); +} + +static void interleave_constant32(uint32_t *out, const unsigned char *in) { + int i; + for (i = 0; i < 4; i++) { + out[2 * i] = br_dec32le(in + 4 * i); + out[2 * i + 1] = br_dec32le(in + 4 * i + 16); + } + br_aes_ct_ortho(out); +} + +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_tweak_constants( + const unsigned char *pk_seed, const unsigned char *sk_seed, + unsigned long long seed_length) { + unsigned char buf[40 * 16]; + int i; + + /* Use the standard constants to generate tweaked ones. */ + memcpy((uint8_t *)tweaked512_rc64, (uint8_t *)haraka512_rc64, 40 * 16); + + /* Constants for sk.seed */ + if (sk_seed != NULL) { + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka_S( + buf, 40 * 16, sk_seed, seed_length); + + /* Interleave constants */ + for (i = 0; i < 10; i++) { + interleave_constant32(tweaked256_rc32_sseed[i], buf + 32 * i); + } + } + + /* Constants for pk.seed */ + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka_S( + buf, 40 * 16, pk_seed, seed_length); + for (i = 0; i < 10; i++) { + interleave_constant32(tweaked256_rc32[i], buf + 32 * i); + interleave_constant(tweaked512_rc64[i], buf + 64 * i); + } +} + +static void haraka_S_absorb(unsigned char *s, + const unsigned char *m, unsigned long long mlen, + unsigned char p) { + unsigned long long i; + unsigned char t[HARAKAS_RATE]; + + while (mlen >= HARAKAS_RATE) { + /* XOR block to state */ + for (i = 0; i < HARAKAS_RATE; ++i) { + s[i] ^= m[i]; + } + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka512_perm(s, s); + mlen -= HARAKAS_RATE; + m += HARAKAS_RATE; + } + + for (i = 0; i < HARAKAS_RATE; ++i) { + t[i] = 0; + } + for (i = 0; i < mlen; ++i) { + t[i] = m[i]; + } + t[i] = p; + t[HARAKAS_RATE - 1] |= 128; + for (i = 0; i < HARAKAS_RATE; ++i) { + s[i] ^= t[i]; + } +} + +static void haraka_S_squeezeblocks(unsigned char *h, unsigned long long nblocks, + unsigned char *s) { + while (nblocks > 0) { + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka512_perm(s, s); + memcpy(h, s, HARAKAS_RATE); + h += HARAKAS_RATE; + nblocks--; + } +} + +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka_S_inc_init(uint8_t *s_inc) { + size_t i; + + for (i = 0; i < 64; i++) { + s_inc[i] = 0; + } + s_inc[64] = 0; +} + +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka_S_inc_absorb(uint8_t *s_inc, const uint8_t *m, size_t mlen) { + size_t i; + + /* Recall that s_inc[64] is the non-absorbed bytes xored into the state */ + while (mlen + s_inc[64] >= HARAKAS_RATE) { + for (i = 0; i < (size_t)(HARAKAS_RATE - s_inc[64]); i++) { + /* Take the i'th byte from message + xor with the s_inc[64] + i'th byte of the state */ + s_inc[s_inc[64] + i] ^= m[i]; + } + mlen -= (size_t)(HARAKAS_RATE - s_inc[64]); + m += HARAKAS_RATE - s_inc[64]; + s_inc[64] = 0; + + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka512_perm(s_inc, s_inc); + } + + for (i = 0; i < mlen; i++) { + s_inc[s_inc[64] + i] ^= m[i]; + } + s_inc[64] = (uint8_t)(mlen + s_inc[64]); +} + +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka_S_inc_finalize(uint8_t *s_inc) { + /* After haraka_S_inc_absorb, we are guaranteed that s_inc[64] < HARAKAS_RATE, + so we can always use one more byte for p in the current state. */ + s_inc[s_inc[64]] ^= 0x1F; + s_inc[HARAKAS_RATE - 1] ^= 128; + s_inc[64] = 0; +} + +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka_S_inc_squeeze(uint8_t *out, size_t outlen, uint8_t *s_inc) { + uint8_t i; + + /* First consume any bytes we still have sitting around */ + for (i = 0; i < outlen && i < s_inc[64]; i++) { + /* There are s_inc[64] bytes left, so r - s_inc[64] is the first + available byte. We consume from there, i.e., up to r. */ + out[i] = s_inc[(HARAKAS_RATE - s_inc[64] + i)]; + } + out += i; + outlen -= i; + s_inc[64] = (uint8_t)(s_inc[64] - i); + + /* Then squeeze the remaining necessary blocks */ + while (outlen > 0) { + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka512_perm(s_inc, s_inc); + + for (i = 0; i < outlen && i < HARAKAS_RATE; i++) { + out[i] = s_inc[i]; + } + out += i; + outlen -= i; + s_inc[64] = (uint8_t)(HARAKAS_RATE - i); + } +} + +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka_S(unsigned char *out, unsigned long long outlen, const unsigned char *in, unsigned long long inlen) { + unsigned long long i; + unsigned char s[64]; + unsigned char d[32]; + + for (i = 0; i < 64; i++) { + s[i] = 0; + } + haraka_S_absorb(s, in, inlen, 0x1F); + + haraka_S_squeezeblocks(out, outlen / 32, s); + out += (outlen / 32) * 32; + + if (outlen % 32) { + haraka_S_squeezeblocks(d, 1, s); + for (i = 0; i < outlen % 32; i++) { + out[i] = d[i]; + } + } +} + +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka512_perm(unsigned char *out, const unsigned char *in) { + uint32_t w[16]; + uint64_t q[8], tmp_q; + unsigned int i, j; + + br_range_dec32le(w, 16, in); + for (i = 0; i < 4; i++) { + br_aes_ct64_interleave_in(&q[i], &q[i + 4], w + (i << 2)); + } + br_aes_ct64_ortho(q); + + /* AES rounds */ + for (i = 0; i < 5; i++) { + for (j = 0; j < 2; j++) { + br_aes_ct64_bitslice_Sbox(q); + shift_rows(q); + mix_columns(q); + add_round_key(q, tweaked512_rc64[2 * i + j]); + } + /* Mix states */ + for (j = 0; j < 8; j++) { + tmp_q = q[j]; + q[j] = (tmp_q & 0x0001000100010001) << 5 | + (tmp_q & 0x0002000200020002) << 12 | + (tmp_q & 0x0004000400040004) >> 1 | + (tmp_q & 0x0008000800080008) << 6 | + (tmp_q & 0x0020002000200020) << 9 | + (tmp_q & 0x0040004000400040) >> 4 | + (tmp_q & 0x0080008000800080) << 3 | + (tmp_q & 0x2100210021002100) >> 5 | + (tmp_q & 0x0210021002100210) << 2 | + (tmp_q & 0x0800080008000800) << 4 | + (tmp_q & 0x1000100010001000) >> 12 | + (tmp_q & 0x4000400040004000) >> 10 | + (tmp_q & 0x8400840084008400) >> 3; + } + } + + br_aes_ct64_ortho(q); + for (i = 0; i < 4; i ++) { + br_aes_ct64_interleave_out(w + (i << 2), q[i], q[i + 4]); + } + br_range_enc32le(out, w, 16); +} + +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka512(unsigned char *out, const unsigned char *in) { + int i; + + unsigned char buf[64]; + + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka512_perm(buf, in); + /* Feed-forward */ + for (i = 0; i < 64; i++) { + buf[i] = buf[i] ^ in[i]; + } + + /* Truncated */ + memcpy(out, buf + 8, 8); + memcpy(out + 8, buf + 24, 8); + memcpy(out + 16, buf + 32, 8); + memcpy(out + 24, buf + 48, 8); +} + + +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka256(unsigned char *out, const unsigned char *in) { + uint32_t q[8], tmp_q; + int i, j; + + for (i = 0; i < 4; i++) { + q[2 * i] = br_dec32le(in + 4 * i); + q[2 * i + 1] = br_dec32le(in + 4 * i + 16); + } + br_aes_ct_ortho(q); + + /* AES rounds */ + for (i = 0; i < 5; i++) { + for (j = 0; j < 2; j++) { + br_aes_ct_bitslice_Sbox(q); + shift_rows32(q); + mix_columns32(q); + add_round_key32(q, tweaked256_rc32[2 * i + j]); + } + + /* Mix states */ + for (j = 0; j < 8; j++) { + tmp_q = q[j]; + q[j] = (tmp_q & 0x81818181) | + (tmp_q & 0x02020202) << 1 | + (tmp_q & 0x04040404) << 2 | + (tmp_q & 0x08080808) << 3 | + (tmp_q & 0x10101010) >> 3 | + (tmp_q & 0x20202020) >> 2 | + (tmp_q & 0x40404040) >> 1; + } + } + + br_aes_ct_ortho(q); + for (i = 0; i < 4; i++) { + br_enc32le(out + 4 * i, q[2 * i]); + br_enc32le(out + 4 * i + 16, q[2 * i + 1]); + } + + for (i = 0; i < 32; i++) { + out[i] ^= in[i]; + } +} + +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka256_sk(unsigned char *out, const unsigned char *in) { + uint32_t q[8], tmp_q; + int i, j; + + for (i = 0; i < 4; i++) { + q[2 * i] = br_dec32le(in + 4 * i); + q[2 * i + 1] = br_dec32le(in + 4 * i + 16); + } + br_aes_ct_ortho(q); + + /* AES rounds */ + for (i = 0; i < 5; i++) { + for (j = 0; j < 2; j++) { + br_aes_ct_bitslice_Sbox(q); + shift_rows32(q); + mix_columns32(q); + add_round_key32(q, tweaked256_rc32_sseed[2 * i + j]); + } + + /* Mix states */ + for (j = 0; j < 8; j++) { + tmp_q = q[j]; + q[j] = (tmp_q & 0x81818181) | + (tmp_q & 0x02020202) << 1 | + (tmp_q & 0x04040404) << 2 | + (tmp_q & 0x08080808) << 3 | + (tmp_q & 0x10101010) >> 3 | + (tmp_q & 0x20202020) >> 2 | + (tmp_q & 0x40404040) >> 1; + } + } + + br_aes_ct_ortho(q); + for (i = 0; i < 4; i++) { + br_enc32le(out + 4 * i, q[2 * i]); + br_enc32le(out + 4 * i + 16, q[2 * i + 1]); + } + + for (i = 0; i < 32; i++) { + out[i] ^= in[i]; + } +} diff --git a/crypto_sign/sphincs-haraka-128f-simple/clean/haraka.h b/crypto_sign/sphincs-haraka-128f-simple/clean/haraka.h new file mode 100644 index 00000000..cc5ae81d --- /dev/null +++ b/crypto_sign/sphincs-haraka-128f-simple/clean/haraka.h @@ -0,0 +1,30 @@ +#ifndef SPX_HARAKA_H +#define SPX_HARAKA_H + +/* Tweak constants with seed */ +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_tweak_constants( + const unsigned char *pk_seed, const unsigned char *sk_seed, + unsigned long long seed_length); + +/* Haraka Sponge */ +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka_S_inc_init(uint8_t *s_inc); +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka_S_inc_absorb(uint8_t *s_inc, const uint8_t *m, size_t mlen); +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka_S_inc_finalize(uint8_t *s_inc); +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka_S_inc_squeeze(uint8_t *out, size_t outlen, uint8_t *s_inc); +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka_S( + unsigned char *out, unsigned long long outlen, + const unsigned char *in, unsigned long long inlen); + +/* Applies the 512-bit Haraka permutation to in. */ +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka512_perm(unsigned char *out, const unsigned char *in); + +/* Implementation of Haraka-512 */ +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka512(unsigned char *out, const unsigned char *in); + +/* Implementation of Haraka-256 */ +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka256(unsigned char *out, const unsigned char *in); + +/* Implementation of Haraka-256 using sk.seed constants */ +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka256_sk(unsigned char *out, const unsigned char *in); + +#endif diff --git a/crypto_sign/sphincs-haraka-128f-simple/clean/hash.h b/crypto_sign/sphincs-haraka-128f-simple/clean/hash.h new file mode 100644 index 00000000..b7e666be --- /dev/null +++ b/crypto_sign/sphincs-haraka-128f-simple/clean/hash.h @@ -0,0 +1,22 @@ +#ifndef SPX_HASH_H +#define SPX_HASH_H + +#include + +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed); + +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen); + +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen); + +#endif diff --git a/crypto_sign/sphincs-haraka-128f-simple/clean/hash_haraka.c b/crypto_sign/sphincs-haraka-128f-simple/clean/hash_haraka.c new file mode 100644 index 00000000..2fd368a1 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128f-simple/clean/hash_haraka.c @@ -0,0 +1,86 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "utils.h" + +#include "haraka.h" + +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed) { + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_tweak_constants(pub_seed, sk_seed, SPX_N); +} + +/* + * Computes PRF(key, addr), given a secret key of SPX_N bytes and an address + */ +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]) { + unsigned char buf[SPX_ADDR_BYTES]; + /* Since SPX_N may be smaller than 32, we need a temporary buffer. */ + unsigned char outbuf[32]; + + (void)key; /* Suppress an 'unused parameter' warning. */ + + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_addr_to_bytes(buf, addr); + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka256_sk(outbuf, buf); + memcpy(out, outbuf, SPX_N); +} + +/** + * Computes the message-dependent randomness R, using a secret seed and an + * optional randomization value as well as the message. + */ +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen) { + uint8_t s_inc[65]; + + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka_S_inc_init(s_inc); + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka_S_inc_absorb(s_inc, sk_prf, SPX_N); + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka_S_inc_absorb(s_inc, optrand, SPX_N); + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka_S_inc_absorb(s_inc, m, mlen); + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka_S_inc_finalize(s_inc); + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka_S_inc_squeeze(R, SPX_N, s_inc); +} + +/** + * Computes the message hash using R, the public key, and the message. + * Outputs the message digest and the index of the leaf. The index is split in + * the tree index and the leaf index, for convenient copying to an address. + */ +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen) { +#define SPX_TREE_BITS (SPX_TREE_HEIGHT * (SPX_D - 1)) +#define SPX_TREE_BYTES ((SPX_TREE_BITS + 7) / 8) +#define SPX_LEAF_BITS SPX_TREE_HEIGHT +#define SPX_LEAF_BYTES ((SPX_LEAF_BITS + 7) / 8) +#define SPX_DGST_BYTES (SPX_FORS_MSG_BYTES + SPX_TREE_BYTES + SPX_LEAF_BYTES) + + unsigned char buf[SPX_DGST_BYTES]; + unsigned char *bufp = buf; + uint8_t s_inc[65]; + + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka_S_inc_init(s_inc); + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka_S_inc_absorb(s_inc, R, SPX_N); + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka_S_inc_absorb(s_inc, pk, SPX_PK_BYTES); + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka_S_inc_absorb(s_inc, m, mlen); + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka_S_inc_finalize(s_inc); + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka_S_inc_squeeze(buf, SPX_DGST_BYTES, s_inc); + + memcpy(digest, bufp, SPX_FORS_MSG_BYTES); + bufp += SPX_FORS_MSG_BYTES; + + *tree = PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_bytes_to_ull(bufp, SPX_TREE_BYTES); + *tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS); + bufp += SPX_TREE_BYTES; + + *leaf_idx = (uint32_t)PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_bytes_to_ull( + bufp, SPX_LEAF_BYTES); + *leaf_idx &= (~(uint32_t)0) >> (32 - SPX_LEAF_BITS); +} diff --git a/crypto_sign/sphincs-haraka-128f-simple/clean/params.h b/crypto_sign/sphincs-haraka-128f-simple/clean/params.h new file mode 100644 index 00000000..367ef88d --- /dev/null +++ b/crypto_sign/sphincs-haraka-128f-simple/clean/params.h @@ -0,0 +1,53 @@ +#ifndef SPX_PARAMS_H +#define SPX_PARAMS_H + +/* Hash output length in bytes. */ +#define SPX_N 16 +/* Height of the hypertree. */ +#define SPX_FULL_HEIGHT 60 +/* Number of subtree layer. */ +#define SPX_D 20 +/* FORS tree dimensions. */ +#define SPX_FORS_HEIGHT 9 +#define SPX_FORS_TREES 30 +/* Winternitz parameter, */ +#define SPX_WOTS_W 16 + +/* The hash function is defined by linking a different hash.c file, as opposed + to setting a #define constant. */ + +/* For clarity */ +#define SPX_ADDR_BYTES 32 + +/* WOTS parameters. */ +#define SPX_WOTS_LOGW 4 + +#define SPX_WOTS_LEN1 (8 * SPX_N / SPX_WOTS_LOGW) + +/* SPX_WOTS_LEN2 is floor(log(len_1 * (w - 1)) / log(w)) + 1; we precompute */ +#define SPX_WOTS_LEN2 3 + +#define SPX_WOTS_LEN (SPX_WOTS_LEN1 + SPX_WOTS_LEN2) +#define SPX_WOTS_BYTES (SPX_WOTS_LEN * SPX_N) +#define SPX_WOTS_PK_BYTES SPX_WOTS_BYTES + +/* Subtree size. */ +#define SPX_TREE_HEIGHT (SPX_FULL_HEIGHT / SPX_D) + +/* FORS parameters. */ +#define SPX_FORS_MSG_BYTES ((SPX_FORS_HEIGHT * SPX_FORS_TREES + 7) / 8) +#define SPX_FORS_BYTES ((SPX_FORS_HEIGHT + 1) * SPX_FORS_TREES * SPX_N) +#define SPX_FORS_PK_BYTES SPX_N + +/* Resulting SPX sizes. */ +#define SPX_BYTES (SPX_N + SPX_FORS_BYTES + SPX_D * SPX_WOTS_BYTES +\ + SPX_FULL_HEIGHT * SPX_N) +#define SPX_PK_BYTES (2 * SPX_N) +#define SPX_SK_BYTES (2 * SPX_N + SPX_PK_BYTES) + +/* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ +#define SPX_OPTRAND_BYTES 32 + +#endif diff --git a/crypto_sign/sphincs-haraka-128f-simple/clean/sign.c b/crypto_sign/sphincs-haraka-128f-simple/clean/sign.c new file mode 100644 index 00000000..3742fed4 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128f-simple/clean/sign.c @@ -0,0 +1,344 @@ +#include +#include +#include + +#include "address.h" +#include "api.h" +#include "fors.h" +#include "hash.h" +#include "params.h" +#include "randombytes.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +/** + * Computes the leaf at a given address. First generates the WOTS key pair, + * then computes leaf by hashing horizontally. + */ +static void wots_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t tree_addr[8]) { + unsigned char pk[SPX_WOTS_BYTES]; + uint32_t wots_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_keypair_addr( + wots_addr, addr_idx); + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_wots_gen_pk( + pk, sk_seed, pub_seed, wots_addr); + + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_thash_WOTS_LEN( + leaf, pk, pub_seed, wots_pk_addr); +} + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_crypto_sign_secretkeybytes(void) { + return PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_CRYPTO_SECRETKEYBYTES; +} + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_crypto_sign_publickeybytes(void) { + return PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES; +} + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_crypto_sign_bytes(void) { + return PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_CRYPTO_BYTES; +} + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_crypto_sign_seedbytes(void) { + return PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_CRYPTO_SEEDBYTES; +} + +/* + * Generates an SPX key pair given a seed of length + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed) { + /* We do not need the auth path in key generation, but it simplifies the + code to have just one treehash routine that computes both root and path + in one function. */ + unsigned char auth_path[SPX_TREE_HEIGHT * SPX_N]; + uint32_t top_tree_addr[8] = {0}; + + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_layer_addr( + top_tree_addr, SPX_D - 1); + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_type( + top_tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */ + memcpy(sk, seed, PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_CRYPTO_SEEDBYTES); + + memcpy(pk, sk + 2 * SPX_N, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_initialize_hash_function(pk, sk); + + /* Compute root node of the top-most subtree. */ + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_treehash_TREE_HEIGHT( + sk + 3 * SPX_N, auth_path, sk, sk + 2 * SPX_N, 0, 0, + wots_gen_leaf, top_tree_addr); + + memcpy(pk + SPX_N, sk + 3 * SPX_N, SPX_N); + + return 0; +} + +/* + * Generates an SPX key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk) { + unsigned char seed[PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_CRYPTO_SEEDBYTES]; + randombytes(seed, PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_CRYPTO_SEEDBYTES); + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_crypto_sign_seed_keypair( + pk, sk, seed); + + return 0; +} + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + const unsigned char *sk_seed = sk; + const unsigned char *sk_prf = sk + SPX_N; + const unsigned char *pk = sk + 2 * SPX_N; + const unsigned char *pub_seed = pk; + + unsigned char optrand[SPX_N]; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char root[SPX_N]; + uint32_t i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_initialize_hash_function( + pub_seed, sk_seed); + + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ + randombytes(optrand, SPX_N); + /* Compute the digest randomization value. */ + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_gen_message_random( + sig, sk_prf, optrand, m, mlen); + + /* Derive the message digest and leaf index from R, PK and M. */ + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Sign the message hash using FORS. */ + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_fors_sign( + sig, root, mhash, sk_seed, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Compute a WOTS signature. */ + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_wots_sign( + sig, root, sk_seed, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the authentication path for the used WOTS leaf. */ + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_treehash_TREE_HEIGHT( + root, sig, sk_seed, pub_seed, idx_leaf, 0, + wots_gen_leaf, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + *siglen = SPX_BYTES; + + return 0; +} + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk) { + const unsigned char *pub_seed = pk; + const unsigned char *pub_root = pk + SPX_N; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char wots_pk[SPX_WOTS_BYTES]; + unsigned char root[SPX_N]; + unsigned char leaf[SPX_N]; + unsigned int i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + if (siglen != SPX_BYTES) { + return -1; + } + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_initialize_hash_function( + pub_seed, NULL); + + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + /* Derive the message digest and leaf index from R || PK || M. */ + /* The additional SPX_N is a result of the hash domain separator. */ + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + /* Layer correctly defaults to 0, so no need to set_layer_addr */ + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_fors_pk_from_sig( + root, sig, mhash, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + /* For each subtree.. */ + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + + /* The WOTS public key is only correct if the signature was correct. */ + /* Initially, root is the FORS pk, but on subsequent iterations it is + the root of the subtree below the currently processed subtree. */ + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_wots_pk_from_sig( + wots_pk, sig, root, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the leaf node using the WOTS public key. */ + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_thash_WOTS_LEN( + leaf, wots_pk, pub_seed, wots_pk_addr); + + /* Compute the root node of this subtree. */ + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_compute_root( + root, leaf, idx_leaf, 0, sig, SPX_TREE_HEIGHT, + pub_seed, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + /* Check if the root node equals the root node in the public key. */ + if (memcmp(root, pub_root, SPX_N) != 0) { + return -1; + } + + return 0; +} + + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + size_t siglen; + + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_crypto_sign_signature( + sm, &siglen, m, mlen, sk); + + memmove(sm + SPX_BYTES, m, mlen); + *smlen = siglen + mlen; + + return 0; +} + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk) { + /* The API caller does not necessarily know what size a signature should be + but SPHINCS+ signatures are always exactly SPX_BYTES. */ + if (smlen < SPX_BYTES) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + *mlen = smlen - SPX_BYTES; + + if (PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_crypto_sign_verify( + sm, SPX_BYTES, sm + SPX_BYTES, *mlen, pk)) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + /* If verification was successful, move the message to the right place. */ + memmove(m, sm + SPX_BYTES, *mlen); + + return 0; +} diff --git a/crypto_sign/sphincs-haraka-128f-simple/clean/thash.h b/crypto_sign/sphincs-haraka-128f-simple/clean/thash.h new file mode 100644 index 00000000..6d387539 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128f-simple/clean/thash.h @@ -0,0 +1,22 @@ +#ifndef SPX_THASH_H +#define SPX_THASH_H + +#include + +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-haraka-128f-simple/clean/thash_haraka_simple.c b/crypto_sign/sphincs-haraka-128f-simple/clean/thash_haraka_simple.c new file mode 100644 index 00000000..d4580ee8 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128f-simple/clean/thash_haraka_simple.c @@ -0,0 +1,78 @@ +#include +#include + +#include "address.h" +#include "params.h" +#include "thash.h" + +#include "haraka.h" + +/** + * Takes an array of inblocks concatenated arrays of SPX_N bytes. + */ +static void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_thash( + unsigned char *out, unsigned char *buf, + const unsigned char *in, unsigned int inblocks, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char outbuf[32]; + unsigned char buf_tmp[64]; + + (void)pub_seed; /* Suppress an 'unused parameter' warning. */ + + if (inblocks == 1) { + /* F function */ + /* Since SPX_N may be smaller than 32, we need a temporary buffer. */ + memset(buf_tmp, 0, 64); + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_addr_to_bytes(buf_tmp, addr); + memcpy(buf_tmp + SPX_ADDR_BYTES, in, SPX_N); + + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka512(outbuf, buf_tmp); + memcpy(out, outbuf, SPX_N); + } else { + /* All other tweakable hashes*/ + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_addr_to_bytes(buf, addr); + memcpy(buf + SPX_ADDR_BYTES, in, inblocks * SPX_N); + + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka_S( + out, SPX_N, buf, SPX_ADDR_BYTES + inblocks * SPX_N); + } +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_ADDR_BYTES + 1 * SPX_N]; + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_thash( + out, buf, in, 1, pub_seed, addr); +} + +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_ADDR_BYTES + 2 * SPX_N]; + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_thash( + out, buf, in, 2, pub_seed, addr); +} + +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_ADDR_BYTES + SPX_WOTS_LEN * SPX_N]; + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_thash( + out, buf, in, SPX_WOTS_LEN, pub_seed, addr); +} + +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_ADDR_BYTES + SPX_FORS_TREES * SPX_N]; + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_thash( + out, buf, in, SPX_FORS_TREES, pub_seed, addr); +} diff --git a/crypto_sign/sphincs-haraka-128f-simple/clean/utils.c b/crypto_sign/sphincs-haraka-128f-simple/clean/utils.c new file mode 100644 index 00000000..f6002aa4 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128f-simple/clean/utils.c @@ -0,0 +1,192 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in) { + + /* Iterate over out in decreasing order, for big-endianness. */ + for (size_t i = outlen; i > 0; i--) { + out[i - 1] = in & 0xff; + in = in >> 8; + } +} + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen) { + unsigned long long retval = 0; + + for (size_t i = 0; i < inlen; i++) { + retval |= ((unsigned long long)in[i]) << (8 * (inlen - 1 - i)); + } + return retval; +} + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + unsigned char buffer[2 * SPX_N]; + + /* If leaf_idx is odd (last bit = 1), current path element is a right child + and auth_path has to go left. Otherwise it is the other way around. */ + if (leaf_idx & 1) { + memcpy(buffer + SPX_N, leaf, SPX_N); + memcpy(buffer, auth_path, SPX_N); + } else { + memcpy(buffer, leaf, SPX_N); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + + for (i = 0; i < tree_height - 1; i++) { + leaf_idx >>= 1; + idx_offset >>= 1; + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_tree_height(addr, i + 1); + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + + /* Pick the right or left neighbor, depending on parity of the node. */ + if (leaf_idx & 1) { + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_thash_2( + buffer + SPX_N, buffer, pub_seed, addr); + memcpy(buffer, auth_path, SPX_N); + } else { + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_thash_2( + buffer, buffer, pub_seed, addr); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + } + + /* The last iteration is exceptional; we do not copy an auth_path node. */ + leaf_idx >>= 1; + idx_offset >>= 1; + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_tree_height(addr, tree_height); + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_thash_2( + root, buffer, pub_seed, addr); +} + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +static void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_treehash( + unsigned char *root, unsigned char *auth_path, + unsigned char *stack, unsigned int *heights, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned int offset = 0; + uint32_t idx; + uint32_t tree_idx; + + for (idx = 0; idx < (uint32_t)(1 << tree_height); idx++) { + /* Add the next leaf node to the stack. */ + gen_leaf(stack + offset * SPX_N, + sk_seed, pub_seed, idx + idx_offset, tree_addr); + offset++; + heights[offset - 1] = 0; + + /* If this is a node we need for the auth path.. */ + if ((leaf_idx ^ 0x1) == idx) { + memcpy(auth_path, stack + (offset - 1)*SPX_N, SPX_N); + } + + /* While the top-most nodes are of equal height.. */ + while (offset >= 2 && heights[offset - 1] == heights[offset - 2]) { + /* Compute index of the new node, in the next layer. */ + tree_idx = (idx >> (heights[offset - 1] + 1)); + + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_tree_height( + tree_addr, heights[offset - 1] + 1); + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_tree_index( + tree_addr, tree_idx + (idx_offset >> (heights[offset - 1] + 1))); + /* Hash the top-most nodes from the stack together. */ + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_thash_2( + stack + (offset - 2)*SPX_N, stack + (offset - 2)*SPX_N, + pub_seed, tree_addr); + offset--; + /* Note that the top-most node is now one layer higher. */ + heights[offset - 1]++; + + /* If this is a node we need for the auth path.. */ + if (((leaf_idx >> heights[offset - 1]) ^ 0x1) == tree_idx) { + memcpy(auth_path + heights[offset - 1]*SPX_N, + stack + (offset - 1)*SPX_N, SPX_N); + } + } + } + memcpy(root, stack, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_FORS_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_FORS_HEIGHT + 1]; + + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_FORS_HEIGHT, gen_leaf, tree_addr); +} + +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_TREE_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_TREE_HEIGHT + 1]; + + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_TREE_HEIGHT, gen_leaf, tree_addr); +} diff --git a/crypto_sign/sphincs-haraka-128f-simple/clean/utils.h b/crypto_sign/sphincs-haraka-128f-simple/clean/utils.h new file mode 100644 index 00000000..799f080a --- /dev/null +++ b/crypto_sign/sphincs-haraka-128f-simple/clean/utils.h @@ -0,0 +1,60 @@ +#ifndef SPX_UTILS_H +#define SPX_UTILS_H + +#include "params.h" +#include +#include + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in); + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen); + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-haraka-128f-simple/clean/wots.c b/crypto_sign/sphincs-haraka-128f-simple/clean/wots.c new file mode 100644 index 00000000..cc7f4e52 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128f-simple/clean/wots.c @@ -0,0 +1,161 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +// TODO clarify address expectations, and make them more uniform. +// TODO i.e. do we expect types to be set already? +// TODO and do we expect modifications or copies? + +/** + * Computes the starting value for a chain, i.e. the secret key. + * Expects the address to be complete up to the chain address. + */ +static void wots_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t wots_addr[8]) { + /* Make sure that the hash address is actually zeroed. */ + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_hash_addr(wots_addr, 0); + + /* Generate sk element. */ + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_prf_addr(sk, sk_seed, wots_addr); +} + +/** + * Computes the chaining function. + * out and in have to be n-byte arrays. + * + * Interprets in as start-th value of the chain. + * addr has to contain the address of the chain. + */ +static void gen_chain(unsigned char *out, const unsigned char *in, + unsigned int start, unsigned int steps, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + /* Initialize out with the value at position 'start'. */ + memcpy(out, in, SPX_N); + + /* Iterate 'steps' calls to the hash function. */ + for (i = start; i < (start + steps) && i < SPX_WOTS_W; i++) { + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_hash_addr(addr, i); + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_thash_1( + out, out, pub_seed, addr); + } +} + +/** + * base_w algorithm as described in draft. + * Interprets an array of bytes as integers in base w. + * This only works when log_w is a divisor of 8. + */ +static void base_w(unsigned int *output, const size_t out_len, + const unsigned char *input) { + size_t in = 0; + size_t out = 0; + unsigned char total = 0; + unsigned int bits = 0; + size_t consumed; + + for (consumed = 0; consumed < out_len; consumed++) { + if (bits == 0) { + total = input[in]; + in++; + bits += 8; + } + bits -= SPX_WOTS_LOGW; + output[out] = (unsigned int)((total >> bits) & (SPX_WOTS_W - 1)); + out++; + } +} + +/* Computes the WOTS+ checksum over a message (in base_w). */ +static void wots_checksum(unsigned int *csum_base_w, + const unsigned int *msg_base_w) { + unsigned int csum = 0; + unsigned char csum_bytes[(SPX_WOTS_LEN2 * SPX_WOTS_LOGW + 7) / 8]; + unsigned int i; + + /* Compute checksum. */ + for (i = 0; i < SPX_WOTS_LEN1; i++) { + csum += SPX_WOTS_W - 1 - msg_base_w[i]; + } + + /* Convert checksum to base_w. */ + /* Make sure expected empty zero bits are the least significant bits. */ + csum = csum << (8 - ((SPX_WOTS_LEN2 * SPX_WOTS_LOGW) % 8)); + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_ull_to_bytes( + csum_bytes, sizeof(csum_bytes), csum); + base_w(csum_base_w, SPX_WOTS_LEN2, csum_bytes); +} + +/* Takes a message and derives the matching chain lengths. */ +static void chain_lengths(unsigned int *lengths, const unsigned char *msg) { + base_w(lengths, SPX_WOTS_LEN1, msg); + wots_checksum(lengths + SPX_WOTS_LEN1, lengths); +} + +/** + * WOTS key generation. Takes a 32 byte sk_seed, expands it to WOTS private key + * elements and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(pk + i * SPX_N, sk_seed, addr); + gen_chain(pk + i * SPX_N, pk + i * SPX_N, + 0, SPX_WOTS_W - 1, pub_seed, addr); + } +} + +/** + * Takes a n-byte message and the 32-byte sk_see to compute a signature 'sig'. + */ +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(sig + i * SPX_N, sk_seed, addr); + gen_chain(sig + i * SPX_N, sig + i * SPX_N, 0, lengths[i], pub_seed, addr); + } +} + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_set_chain_addr(addr, i); + gen_chain(pk + i * SPX_N, sig + i * SPX_N, + lengths[i], SPX_WOTS_W - 1 - lengths[i], pub_seed, addr); + } +} diff --git a/crypto_sign/sphincs-haraka-128f-simple/clean/wots.h b/crypto_sign/sphincs-haraka-128f-simple/clean/wots.h new file mode 100644 index 00000000..03513306 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128f-simple/clean/wots.h @@ -0,0 +1,38 @@ +#ifndef SPX_WOTS_H +#define SPX_WOTS_H + +#include "params.h" +#include + +/** + * WOTS key generation. Takes a 32 byte seed for the private key, expands it to + * a full WOTS private key and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * Takes a n-byte message and the 32-byte seed for the private key to compute a + * signature that is placed at 'sig'. + */ +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]); + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-haraka-128s-robust/META.yml b/crypto_sign/sphincs-haraka-128s-robust/META.yml new file mode 100644 index 00000000..4d28ab3b --- /dev/null +++ b/crypto_sign/sphincs-haraka-128s-robust/META.yml @@ -0,0 +1,27 @@ +name: SPHINCS+ +type: signature +claimed-nist-level: 1 +length-public-key: 32 +length-secret-key: 64 +length-signature: 8080 +testvectors-sha256: a7057ca5ce0d7f01d1c1aabe474f8449796b051becbc8b148a78c84893193fcf +principal-submitter: Andreas Hülsing +auxiliary-submitters: + - Jean-Philippe Aumasson + - Daniel J. Bernstein, + - Christoph Dobraunig + - Maria Eichlseder + - Scott Fluhrer + - Stefan-Lukas Gazdag + - Panos Kampanakis + - Stefan Kölbl + - Tanja Lange + - Martin M. Lauridsen + - Florian Mendel + - Ruben Niederhagen + - Christian Rechberger + - Joost Rijneveld + - Peter Schwabe +implementations: + - name: clean + version: https://github.com/sphincs/sphincsplus/commit/77755c94d0bc744478044d6efbb888dc13156441 diff --git a/crypto_sign/sphincs-haraka-128s-robust/clean/LICENSE b/crypto_sign/sphincs-haraka-128s-robust/clean/LICENSE new file mode 100644 index 00000000..670154e3 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128s-robust/clean/LICENSE @@ -0,0 +1,116 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + diff --git a/crypto_sign/sphincs-haraka-128s-robust/clean/Makefile b/crypto_sign/sphincs-haraka-128s-robust/clean/Makefile new file mode 100644 index 00000000..630bbb51 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128s-robust/clean/Makefile @@ -0,0 +1,20 @@ +# This Makefile can be used with GNU Make or BSD Make + +LIB=libsphincs-haraka-128s-robust_clean.a + +HEADERS = params.h address.h wots.h utils.h fors.h api.h hash.h thash.h haraka.h +OBJECTS = address.o wots.o utils.o fors.o sign.o hash_haraka.o thash_haraka_robust.o haraka.o + +CFLAGS=-O3 -Wall -Wconversion -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) + +all: $(LIB) + +%.o: %.c $(HEADERS) + $(CC) $(CFLAGS) -c -o $@ $< + +$(LIB): $(OBJECTS) + $(AR) -r $@ $(OBJECTS) + +clean: + $(RM) $(OBJECTS) + $(RM) $(LIB) diff --git a/crypto_sign/sphincs-haraka-128s-robust/clean/Makefile.Microsoft_nmake b/crypto_sign/sphincs-haraka-128s-robust/clean/Makefile.Microsoft_nmake new file mode 100644 index 00000000..26dc2df1 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128s-robust/clean/Makefile.Microsoft_nmake @@ -0,0 +1,19 @@ +# This Makefile can be used with Microsoft Visual Studio's nmake using the command: +# nmake /f Makefile.Microsoft_nmake + +LIBRARY=libsphincs-haraka-128s-robust_clean.lib +OBJECTS=address.obj wots.obj utils.obj fors.obj sign.obj hash_haraka.obj thash_haraka_robust.obj haraka.obj + +CFLAGS=/nologo /I ..\..\..\common /W4 /WX + +all: $(LIBRARY) + +# Make sure objects are recompiled if headers change. +$(OBJECTS): *.h + +$(LIBRARY): $(OBJECTS) + LIB.EXE /NOLOGO /WX /OUT:$@ $** + +clean: + -DEL $(OBJECTS) + -DEL $(LIBRARY) diff --git a/crypto_sign/sphincs-haraka-128s-robust/clean/address.c b/crypto_sign/sphincs-haraka-128s-robust/clean/address.c new file mode 100644 index 00000000..495d9451 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128s-robust/clean/address.c @@ -0,0 +1,78 @@ +#include + +#include "address.h" +#include "params.h" +#include "utils.h" + +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]) { + int i; + + for (i = 0; i < 8; i++) { + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_ull_to_bytes( + bytes + i * 4, 4, addr[i]); + } +} + +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer) { + addr[0] = layer; +} + +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree) { + addr[1] = 0; + addr[2] = (uint32_t) (tree >> 32); + addr[3] = (uint32_t) tree; +} + +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_type( + uint32_t addr[8], uint32_t type) { + addr[4] = type; +} + +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; +} + +/* These functions are used for OTS addresses. */ + +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair) { + addr[5] = keypair; +} + +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; + out[5] = in[5]; +} + +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain) { + addr[6] = chain; +} + +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash) { + addr[7] = hash; +} + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height) { + addr[6] = tree_height; +} + +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index) { + addr[7] = tree_index; +} diff --git a/crypto_sign/sphincs-haraka-128s-robust/clean/address.h b/crypto_sign/sphincs-haraka-128s-robust/clean/address.h new file mode 100644 index 00000000..bb97668a --- /dev/null +++ b/crypto_sign/sphincs-haraka-128s-robust/clean/address.h @@ -0,0 +1,50 @@ +#ifndef SPX_ADDRESS_H +#define SPX_ADDRESS_H + +#include + +#define SPX_ADDR_TYPE_WOTS 0 +#define SPX_ADDR_TYPE_WOTSPK 1 +#define SPX_ADDR_TYPE_HASHTREE 2 +#define SPX_ADDR_TYPE_FORSTREE 3 +#define SPX_ADDR_TYPE_FORSPK 4 + +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer); + +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree); + +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_type( + uint32_t addr[8], uint32_t type); + +/* Copies the layer and tree part of one address into the other */ +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for WOTS and FORS addresses. */ + +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair); + +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain); + +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash); + +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height); + +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index); + +#endif diff --git a/crypto_sign/sphincs-haraka-128s-robust/clean/api.h b/crypto_sign/sphincs-haraka-128s-robust/clean/api.h new file mode 100644 index 00000000..58ef3f29 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128s-robust/clean/api.h @@ -0,0 +1,78 @@ +#ifndef PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_API_H +#define PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_API_H + +#include +#include + +#define PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_CRYPTO_ALGNAME "SPHINCS+" + +#define PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_CRYPTO_SECRETKEYBYTES 64 +#define PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES 32 +#define PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_CRYPTO_BYTES 8080 +#define PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_CRYPTO_SEEDBYTES 48 + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_crypto_sign_secretkeybytes(void); + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_crypto_sign_publickeybytes(void); + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_crypto_sign_bytes(void); + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_crypto_sign_seedbytes(void); + +/* + * Generates a SPHINCS+ key pair given a seed. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed); + +/* + * Generates a SPHINCS+ key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk); + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk); + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk); + +#endif diff --git a/crypto_sign/sphincs-haraka-128s-robust/clean/fors.c b/crypto_sign/sphincs-haraka-128s-robust/clean/fors.c new file mode 100644 index 00000000..c797af4f --- /dev/null +++ b/crypto_sign/sphincs-haraka-128s-robust/clean/fors.c @@ -0,0 +1,164 @@ +#include +#include +#include + +#include "address.h" +#include "fors.h" +#include "hash.h" +#include "thash.h" +#include "utils.h" + +static void fors_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_prf_addr( + sk, sk_seed, fors_leaf_addr); +} + +static void fors_sk_to_leaf(unsigned char *leaf, const unsigned char *sk, + const unsigned char *pub_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_thash_1( + leaf, sk, pub_seed, fors_leaf_addr); +} + +static void fors_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t fors_tree_addr[8]) { + uint32_t fors_leaf_addr[8] = {0}; + + /* Only copy the parts that must be kept in fors_leaf_addr. */ + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_copy_keypair_addr( + fors_leaf_addr, fors_tree_addr); + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_type( + fors_leaf_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_tree_index( + fors_leaf_addr, addr_idx); + + fors_gen_sk(leaf, sk_seed, fors_leaf_addr); + fors_sk_to_leaf(leaf, leaf, pub_seed, fors_leaf_addr); +} + +/** + * Interprets m as SPX_FORS_HEIGHT-bit unsigned integers. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + * Assumes indices has space for SPX_FORS_TREES integers. + */ +static void message_to_indices(uint32_t *indices, const unsigned char *m) { + unsigned int i, j; + unsigned int offset = 0; + + for (i = 0; i < SPX_FORS_TREES; i++) { + indices[i] = 0; + for (j = 0; j < SPX_FORS_HEIGHT; j++) { + indices[i] ^= (((uint32_t)m[offset >> 3] >> (offset & 0x7)) & 0x1) << j; + offset++; + } + } +} + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Include the secret key part that produces the selected leaf node. */ + fors_gen_sk(sig, sk_seed, fors_tree_addr); + sig += SPX_N; + + /* Compute the authentication path for this leaf node. */ + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_treehash_FORS_HEIGHT( + roots + i * SPX_N, sig, sk_seed, pub_seed, + indices[i], idx_offset, fors_gen_leaf, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + unsigned char leaf[SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Derive the leaf from the included secret key part. */ + fors_sk_to_leaf(leaf, sig, pub_seed, fors_tree_addr); + sig += SPX_N; + + /* Derive the corresponding root node of this tree. */ + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_compute_root( + roots + i * SPX_N, leaf, indices[i], idx_offset, sig, + SPX_FORS_HEIGHT, pub_seed, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} diff --git a/crypto_sign/sphincs-haraka-128s-robust/clean/fors.h b/crypto_sign/sphincs-haraka-128s-robust/clean/fors.h new file mode 100644 index 00000000..135be456 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128s-robust/clean/fors.h @@ -0,0 +1,30 @@ +#ifndef SPX_FORS_H +#define SPX_FORS_H + +#include + +#include "params.h" + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]); + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-haraka-128s-robust/clean/haraka.c b/crypto_sign/sphincs-haraka-128s-robust/clean/haraka.c new file mode 100644 index 00000000..72ac40ef --- /dev/null +++ b/crypto_sign/sphincs-haraka-128s-robust/clean/haraka.c @@ -0,0 +1,965 @@ +/* + * Constant time implementation of the Haraka hash function. + * + * The bit-sliced implementation of the AES round functions are + * based on the AES implementation in BearSSL written + * by Thomas Pornin + */ + +#include +#include +#include + +#include "haraka.h" + +#define HARAKAS_RATE 32 + +static const uint64_t haraka512_rc64[10][8] = { + {0x24cf0ab9086f628b, 0xbdd6eeecc83b8382, 0xd96fb0306cdad0a7, 0xaace082ac8f95f89, 0x449d8e8870d7041f, 0x49bb2f80b2b3e2f8, 0x0569ae98d93bb258, 0x23dc9691e7d6a4b1}, + {0xd8ba10ede0fe5b6e, 0x7ecf7dbe424c7b8e, 0x6ea9949c6df62a31, 0xbf3f3c97ec9c313e, 0x241d03a196a1861e, 0xead3a51116e5a2ea, 0x77d479fcad9574e3, 0x18657a1af894b7a0}, + {0x10671e1a7f595522, 0xd9a00ff675d28c7b, 0x2f1edf0d2b9ba661, 0xb8ff58b8e3de45f9, 0xee29261da9865c02, 0xd1532aa4b50bdf43, 0x8bf858159b231bb1, 0xdf17439d22d4f599}, + {0xdd4b2f0870b918c0, 0x757a81f3b39b1bb6, 0x7a5c556898952e3f, 0x7dd70a16d915d87a, 0x3ae61971982b8301, 0xc3ab319e030412be, 0x17c0033ac094a8cb, 0x5a0630fc1a8dc4ef}, + {0x17708988c1632f73, 0xf92ddae090b44f4f, 0x11ac0285c43aa314, 0x509059941936b8ba, 0xd03e152fa2ce9b69, 0x3fbcbcb63a32998b, 0x6204696d692254f7, 0x915542ed93ec59b4}, + {0xf4ed94aa8879236e, 0xff6cb41cd38e03c0, 0x069b38602368aeab, 0x669495b820f0ddba, 0xf42013b1b8bf9e3d, 0xcf935efe6439734d, 0xbc1dcf42ca29e3f8, 0x7e6d3ed29f78ad67}, + {0xf3b0f6837ffcddaa, 0x3a76faef934ddf41, 0xcec7ae583a9c8e35, 0xe4dd18c68f0260af, 0x2c0e5df1ad398eaa, 0x478df5236ae22e8c, 0xfb944c46fe865f39, 0xaa48f82f028132ba}, + {0x231b9ae2b76aca77, 0x292a76a712db0b40, 0x5850625dc8134491, 0x73137dd469810fb5, 0x8a12a6a202a474fd, 0xd36fd9daa78bdb80, 0xb34c5e733505706f, 0xbaf1cdca818d9d96}, + {0x2e99781335e8c641, 0xbddfe5cce47d560e, 0xf74e9bf32e5e040c, 0x1d7a709d65996be9, 0x670df36a9cf66cdd, 0xd05ef84a176a2875, 0x0f888e828cb1c44e, 0x1a79e9c9727b052c}, + {0x83497348628d84de, 0x2e9387d51f22a754, 0xb000068da2f852d6, 0x378c9e1190fd6fe5, 0x870027c316de7293, 0xe51a9d4462e047bb, 0x90ecf7f8c6251195, 0x655953bfbed90a9c}, +}; + +static uint64_t tweaked512_rc64[10][8]; +static uint32_t tweaked256_rc32[10][8]; +static uint32_t tweaked256_rc32_sseed[10][8]; + +static inline uint32_t br_dec32le(const unsigned char *src) { + return (uint32_t)src[0] + | ((uint32_t)src[1] << 8) + | ((uint32_t)src[2] << 16) + | ((uint32_t)src[3] << 24); +} + +static void br_range_dec32le(uint32_t *v, size_t num, const unsigned char *src) { + while (num-- > 0) { + *v ++ = br_dec32le(src); + src += 4; + } +} + +static inline void br_enc32le(unsigned char *dst, uint32_t x) { + dst[0] = (unsigned char)x; + dst[1] = (unsigned char)(x >> 8); + dst[2] = (unsigned char)(x >> 16); + dst[3] = (unsigned char)(x >> 24); +} + + +static void br_range_enc32le(unsigned char *dst, const uint32_t *v, size_t num) { + while (num-- > 0) { + br_enc32le(dst, *v ++); + dst += 4; + } +} + +static void br_aes_ct64_bitslice_Sbox(uint64_t *q) { + /* + * This S-box implementation is a straightforward translation of + * the circuit described by Boyar and Peralta in "A new + * combinational logic minimization technique with applications + * to cryptology" (https://eprint.iacr.org/2009/191.pdf). + * + * Note that variables x* (input) and s* (output) are numbered + * in "reverse" order (x0 is the high bit, x7 is the low bit). + */ + + uint64_t x0, x1, x2, x3, x4, x5, x6, x7; + uint64_t y1, y2, y3, y4, y5, y6, y7, y8, y9; + uint64_t y10, y11, y12, y13, y14, y15, y16, y17, y18, y19; + uint64_t y20, y21; + uint64_t z0, z1, z2, z3, z4, z5, z6, z7, z8, z9; + uint64_t z10, z11, z12, z13, z14, z15, z16, z17; + uint64_t t0, t1, t2, t3, t4, t5, t6, t7, t8, t9; + uint64_t t10, t11, t12, t13, t14, t15, t16, t17, t18, t19; + uint64_t t20, t21, t22, t23, t24, t25, t26, t27, t28, t29; + uint64_t t30, t31, t32, t33, t34, t35, t36, t37, t38, t39; + uint64_t t40, t41, t42, t43, t44, t45, t46, t47, t48, t49; + uint64_t t50, t51, t52, t53, t54, t55, t56, t57, t58, t59; + uint64_t t60, t61, t62, t63, t64, t65, t66, t67; + uint64_t s0, s1, s2, s3, s4, s5, s6, s7; + + x0 = q[7]; + x1 = q[6]; + x2 = q[5]; + x3 = q[4]; + x4 = q[3]; + x5 = q[2]; + x6 = q[1]; + x7 = q[0]; + + /* + * Top linear transformation. + */ + y14 = x3 ^ x5; + y13 = x0 ^ x6; + y9 = x0 ^ x3; + y8 = x0 ^ x5; + t0 = x1 ^ x2; + y1 = t0 ^ x7; + y4 = y1 ^ x3; + y12 = y13 ^ y14; + y2 = y1 ^ x0; + y5 = y1 ^ x6; + y3 = y5 ^ y8; + t1 = x4 ^ y12; + y15 = t1 ^ x5; + y20 = t1 ^ x1; + y6 = y15 ^ x7; + y10 = y15 ^ t0; + y11 = y20 ^ y9; + y7 = x7 ^ y11; + y17 = y10 ^ y11; + y19 = y10 ^ y8; + y16 = t0 ^ y11; + y21 = y13 ^ y16; + y18 = x0 ^ y16; + + /* + * Non-linear section. + */ + t2 = y12 & y15; + t3 = y3 & y6; + t4 = t3 ^ t2; + t5 = y4 & x7; + t6 = t5 ^ t2; + t7 = y13 & y16; + t8 = y5 & y1; + t9 = t8 ^ t7; + t10 = y2 & y7; + t11 = t10 ^ t7; + t12 = y9 & y11; + t13 = y14 & y17; + t14 = t13 ^ t12; + t15 = y8 & y10; + t16 = t15 ^ t12; + t17 = t4 ^ t14; + t18 = t6 ^ t16; + t19 = t9 ^ t14; + t20 = t11 ^ t16; + t21 = t17 ^ y20; + t22 = t18 ^ y19; + t23 = t19 ^ y21; + t24 = t20 ^ y18; + + t25 = t21 ^ t22; + t26 = t21 & t23; + t27 = t24 ^ t26; + t28 = t25 & t27; + t29 = t28 ^ t22; + t30 = t23 ^ t24; + t31 = t22 ^ t26; + t32 = t31 & t30; + t33 = t32 ^ t24; + t34 = t23 ^ t33; + t35 = t27 ^ t33; + t36 = t24 & t35; + t37 = t36 ^ t34; + t38 = t27 ^ t36; + t39 = t29 & t38; + t40 = t25 ^ t39; + + t41 = t40 ^ t37; + t42 = t29 ^ t33; + t43 = t29 ^ t40; + t44 = t33 ^ t37; + t45 = t42 ^ t41; + z0 = t44 & y15; + z1 = t37 & y6; + z2 = t33 & x7; + z3 = t43 & y16; + z4 = t40 & y1; + z5 = t29 & y7; + z6 = t42 & y11; + z7 = t45 & y17; + z8 = t41 & y10; + z9 = t44 & y12; + z10 = t37 & y3; + z11 = t33 & y4; + z12 = t43 & y13; + z13 = t40 & y5; + z14 = t29 & y2; + z15 = t42 & y9; + z16 = t45 & y14; + z17 = t41 & y8; + + /* + * Bottom linear transformation. + */ + t46 = z15 ^ z16; + t47 = z10 ^ z11; + t48 = z5 ^ z13; + t49 = z9 ^ z10; + t50 = z2 ^ z12; + t51 = z2 ^ z5; + t52 = z7 ^ z8; + t53 = z0 ^ z3; + t54 = z6 ^ z7; + t55 = z16 ^ z17; + t56 = z12 ^ t48; + t57 = t50 ^ t53; + t58 = z4 ^ t46; + t59 = z3 ^ t54; + t60 = t46 ^ t57; + t61 = z14 ^ t57; + t62 = t52 ^ t58; + t63 = t49 ^ t58; + t64 = z4 ^ t59; + t65 = t61 ^ t62; + t66 = z1 ^ t63; + s0 = t59 ^ t63; + s6 = t56 ^ ~t62; + s7 = t48 ^ ~t60; + t67 = t64 ^ t65; + s3 = t53 ^ t66; + s4 = t51 ^ t66; + s5 = t47 ^ t65; + s1 = t64 ^ ~s3; + s2 = t55 ^ ~t67; + + q[7] = s0; + q[6] = s1; + q[5] = s2; + q[4] = s3; + q[3] = s4; + q[2] = s5; + q[1] = s6; + q[0] = s7; +} + +static void br_aes_ct_bitslice_Sbox(uint32_t *q) { + /* + * This S-box implementation is a straightforward translation of + * the circuit described by Boyar and Peralta in "A new + * combinational logic minimization technique with applications + * to cryptology" (https://eprint.iacr.org/2009/191.pdf). + * + * Note that variables x* (input) and s* (output) are numbered + * in "reverse" order (x0 is the high bit, x7 is the low bit). + */ + + uint32_t x0, x1, x2, x3, x4, x5, x6, x7; + uint32_t y1, y2, y3, y4, y5, y6, y7, y8, y9; + uint32_t y10, y11, y12, y13, y14, y15, y16, y17, y18, y19; + uint32_t y20, y21; + uint32_t z0, z1, z2, z3, z4, z5, z6, z7, z8, z9; + uint32_t z10, z11, z12, z13, z14, z15, z16, z17; + uint32_t t0, t1, t2, t3, t4, t5, t6, t7, t8, t9; + uint32_t t10, t11, t12, t13, t14, t15, t16, t17, t18, t19; + uint32_t t20, t21, t22, t23, t24, t25, t26, t27, t28, t29; + uint32_t t30, t31, t32, t33, t34, t35, t36, t37, t38, t39; + uint32_t t40, t41, t42, t43, t44, t45, t46, t47, t48, t49; + uint32_t t50, t51, t52, t53, t54, t55, t56, t57, t58, t59; + uint32_t t60, t61, t62, t63, t64, t65, t66, t67; + uint32_t s0, s1, s2, s3, s4, s5, s6, s7; + + x0 = q[7]; + x1 = q[6]; + x2 = q[5]; + x3 = q[4]; + x4 = q[3]; + x5 = q[2]; + x6 = q[1]; + x7 = q[0]; + + /* + * Top linear transformation. + */ + y14 = x3 ^ x5; + y13 = x0 ^ x6; + y9 = x0 ^ x3; + y8 = x0 ^ x5; + t0 = x1 ^ x2; + y1 = t0 ^ x7; + y4 = y1 ^ x3; + y12 = y13 ^ y14; + y2 = y1 ^ x0; + y5 = y1 ^ x6; + y3 = y5 ^ y8; + t1 = x4 ^ y12; + y15 = t1 ^ x5; + y20 = t1 ^ x1; + y6 = y15 ^ x7; + y10 = y15 ^ t0; + y11 = y20 ^ y9; + y7 = x7 ^ y11; + y17 = y10 ^ y11; + y19 = y10 ^ y8; + y16 = t0 ^ y11; + y21 = y13 ^ y16; + y18 = x0 ^ y16; + + /* + * Non-linear section. + */ + t2 = y12 & y15; + t3 = y3 & y6; + t4 = t3 ^ t2; + t5 = y4 & x7; + t6 = t5 ^ t2; + t7 = y13 & y16; + t8 = y5 & y1; + t9 = t8 ^ t7; + t10 = y2 & y7; + t11 = t10 ^ t7; + t12 = y9 & y11; + t13 = y14 & y17; + t14 = t13 ^ t12; + t15 = y8 & y10; + t16 = t15 ^ t12; + t17 = t4 ^ t14; + t18 = t6 ^ t16; + t19 = t9 ^ t14; + t20 = t11 ^ t16; + t21 = t17 ^ y20; + t22 = t18 ^ y19; + t23 = t19 ^ y21; + t24 = t20 ^ y18; + + t25 = t21 ^ t22; + t26 = t21 & t23; + t27 = t24 ^ t26; + t28 = t25 & t27; + t29 = t28 ^ t22; + t30 = t23 ^ t24; + t31 = t22 ^ t26; + t32 = t31 & t30; + t33 = t32 ^ t24; + t34 = t23 ^ t33; + t35 = t27 ^ t33; + t36 = t24 & t35; + t37 = t36 ^ t34; + t38 = t27 ^ t36; + t39 = t29 & t38; + t40 = t25 ^ t39; + + t41 = t40 ^ t37; + t42 = t29 ^ t33; + t43 = t29 ^ t40; + t44 = t33 ^ t37; + t45 = t42 ^ t41; + z0 = t44 & y15; + z1 = t37 & y6; + z2 = t33 & x7; + z3 = t43 & y16; + z4 = t40 & y1; + z5 = t29 & y7; + z6 = t42 & y11; + z7 = t45 & y17; + z8 = t41 & y10; + z9 = t44 & y12; + z10 = t37 & y3; + z11 = t33 & y4; + z12 = t43 & y13; + z13 = t40 & y5; + z14 = t29 & y2; + z15 = t42 & y9; + z16 = t45 & y14; + z17 = t41 & y8; + + /* + * Bottom linear transformation. + */ + t46 = z15 ^ z16; + t47 = z10 ^ z11; + t48 = z5 ^ z13; + t49 = z9 ^ z10; + t50 = z2 ^ z12; + t51 = z2 ^ z5; + t52 = z7 ^ z8; + t53 = z0 ^ z3; + t54 = z6 ^ z7; + t55 = z16 ^ z17; + t56 = z12 ^ t48; + t57 = t50 ^ t53; + t58 = z4 ^ t46; + t59 = z3 ^ t54; + t60 = t46 ^ t57; + t61 = z14 ^ t57; + t62 = t52 ^ t58; + t63 = t49 ^ t58; + t64 = z4 ^ t59; + t65 = t61 ^ t62; + t66 = z1 ^ t63; + s0 = t59 ^ t63; + s6 = t56 ^ ~t62; + s7 = t48 ^ ~t60; + t67 = t64 ^ t65; + s3 = t53 ^ t66; + s4 = t51 ^ t66; + s5 = t47 ^ t65; + s1 = t64 ^ ~s3; + s2 = t55 ^ ~t67; + + q[7] = s0; + q[6] = s1; + q[5] = s2; + q[4] = s3; + q[3] = s4; + q[2] = s5; + q[1] = s6; + q[0] = s7; +} + +static void br_aes_ct_ortho(uint32_t *q) { +#define SWAPN_32(cl, ch, s, x, y) do { \ + uint32_t a, b; \ + a = (x); \ + b = (y); \ + (x) = (a & (uint32_t)(cl)) | ((b & (uint32_t)(cl)) << (s)); \ + (y) = ((a & (uint32_t)(ch)) >> (s)) | (b & (uint32_t)(ch)); \ + } while (0) + +#define SWAP2_32(x, y) SWAPN_32(0x55555555, 0xAAAAAAAA, 1, x, y) +#define SWAP4_32(x, y) SWAPN_32(0x33333333, 0xCCCCCCCC, 2, x, y) +#define SWAP8_32(x, y) SWAPN_32(0x0F0F0F0F, 0xF0F0F0F0, 4, x, y) + + SWAP2_32(q[0], q[1]); + SWAP2_32(q[2], q[3]); + SWAP2_32(q[4], q[5]); + SWAP2_32(q[6], q[7]); + + SWAP4_32(q[0], q[2]); + SWAP4_32(q[1], q[3]); + SWAP4_32(q[4], q[6]); + SWAP4_32(q[5], q[7]); + + SWAP8_32(q[0], q[4]); + SWAP8_32(q[1], q[5]); + SWAP8_32(q[2], q[6]); + SWAP8_32(q[3], q[7]); +} + +static inline void add_round_key32(uint32_t *q, const uint32_t *sk) { + q[0] ^= sk[0]; + q[1] ^= sk[1]; + q[2] ^= sk[2]; + q[3] ^= sk[3]; + q[4] ^= sk[4]; + q[5] ^= sk[5]; + q[6] ^= sk[6]; + q[7] ^= sk[7]; +} + +static inline void shift_rows32(uint32_t *q) { + int i; + + for (i = 0; i < 8; i++) { + uint32_t x; + + x = q[i]; + q[i] = (x & 0x000000FF) + | ((x & 0x0000FC00) >> 2) | ((x & 0x00000300) << 6) + | ((x & 0x00F00000) >> 4) | ((x & 0x000F0000) << 4) + | ((x & 0xC0000000) >> 6) | ((x & 0x3F000000) << 2); + } +} + +static inline uint32_t rotr16(uint32_t x) { + return (x << 16) | (x >> 16); +} + +static inline void mix_columns32(uint32_t *q) { + uint32_t q0, q1, q2, q3, q4, q5, q6, q7; + uint32_t r0, r1, r2, r3, r4, r5, r6, r7; + + q0 = q[0]; + q1 = q[1]; + q2 = q[2]; + q3 = q[3]; + q4 = q[4]; + q5 = q[5]; + q6 = q[6]; + q7 = q[7]; + r0 = (q0 >> 8) | (q0 << 24); + r1 = (q1 >> 8) | (q1 << 24); + r2 = (q2 >> 8) | (q2 << 24); + r3 = (q3 >> 8) | (q3 << 24); + r4 = (q4 >> 8) | (q4 << 24); + r5 = (q5 >> 8) | (q5 << 24); + r6 = (q6 >> 8) | (q6 << 24); + r7 = (q7 >> 8) | (q7 << 24); + + q[0] = q7 ^ r7 ^ r0 ^ rotr16(q0 ^ r0); + q[1] = q0 ^ r0 ^ q7 ^ r7 ^ r1 ^ rotr16(q1 ^ r1); + q[2] = q1 ^ r1 ^ r2 ^ rotr16(q2 ^ r2); + q[3] = q2 ^ r2 ^ q7 ^ r7 ^ r3 ^ rotr16(q3 ^ r3); + q[4] = q3 ^ r3 ^ q7 ^ r7 ^ r4 ^ rotr16(q4 ^ r4); + q[5] = q4 ^ r4 ^ r5 ^ rotr16(q5 ^ r5); + q[6] = q5 ^ r5 ^ r6 ^ rotr16(q6 ^ r6); + q[7] = q6 ^ r6 ^ r7 ^ rotr16(q7 ^ r7); +} + +static void br_aes_ct64_ortho(uint64_t *q) { +#define SWAPN(cl, ch, s, x, y) do { \ + uint64_t a, b; \ + a = (x); \ + b = (y); \ + (x) = (a & (uint64_t)(cl)) | ((b & (uint64_t)(cl)) << (s)); \ + (y) = ((a & (uint64_t)(ch)) >> (s)) | (b & (uint64_t)(ch)); \ + } while (0) + +#define SWAP2(x, y) SWAPN(0x5555555555555555, 0xAAAAAAAAAAAAAAAA, 1, x, y) +#define SWAP4(x, y) SWAPN(0x3333333333333333, 0xCCCCCCCCCCCCCCCC, 2, x, y) +#define SWAP8(x, y) SWAPN(0x0F0F0F0F0F0F0F0F, 0xF0F0F0F0F0F0F0F0, 4, x, y) + + SWAP2(q[0], q[1]); + SWAP2(q[2], q[3]); + SWAP2(q[4], q[5]); + SWAP2(q[6], q[7]); + + SWAP4(q[0], q[2]); + SWAP4(q[1], q[3]); + SWAP4(q[4], q[6]); + SWAP4(q[5], q[7]); + + SWAP8(q[0], q[4]); + SWAP8(q[1], q[5]); + SWAP8(q[2], q[6]); + SWAP8(q[3], q[7]); +} + + +static void br_aes_ct64_interleave_in(uint64_t *q0, uint64_t *q1, const uint32_t *w) { + uint64_t x0, x1, x2, x3; + + x0 = w[0]; + x1 = w[1]; + x2 = w[2]; + x3 = w[3]; + x0 |= (x0 << 16); + x1 |= (x1 << 16); + x2 |= (x2 << 16); + x3 |= (x3 << 16); + x0 &= (uint64_t)0x0000FFFF0000FFFF; + x1 &= (uint64_t)0x0000FFFF0000FFFF; + x2 &= (uint64_t)0x0000FFFF0000FFFF; + x3 &= (uint64_t)0x0000FFFF0000FFFF; + x0 |= (x0 << 8); + x1 |= (x1 << 8); + x2 |= (x2 << 8); + x3 |= (x3 << 8); + x0 &= (uint64_t)0x00FF00FF00FF00FF; + x1 &= (uint64_t)0x00FF00FF00FF00FF; + x2 &= (uint64_t)0x00FF00FF00FF00FF; + x3 &= (uint64_t)0x00FF00FF00FF00FF; + *q0 = x0 | (x2 << 8); + *q1 = x1 | (x3 << 8); +} + + +static void br_aes_ct64_interleave_out(uint32_t *w, uint64_t q0, uint64_t q1) { + uint64_t x0, x1, x2, x3; + + x0 = q0 & (uint64_t)0x00FF00FF00FF00FF; + x1 = q1 & (uint64_t)0x00FF00FF00FF00FF; + x2 = (q0 >> 8) & (uint64_t)0x00FF00FF00FF00FF; + x3 = (q1 >> 8) & (uint64_t)0x00FF00FF00FF00FF; + x0 |= (x0 >> 8); + x1 |= (x1 >> 8); + x2 |= (x2 >> 8); + x3 |= (x3 >> 8); + x0 &= (uint64_t)0x0000FFFF0000FFFF; + x1 &= (uint64_t)0x0000FFFF0000FFFF; + x2 &= (uint64_t)0x0000FFFF0000FFFF; + x3 &= (uint64_t)0x0000FFFF0000FFFF; + w[0] = (uint32_t)x0 | (uint32_t)(x0 >> 16); + w[1] = (uint32_t)x1 | (uint32_t)(x1 >> 16); + w[2] = (uint32_t)x2 | (uint32_t)(x2 >> 16); + w[3] = (uint32_t)x3 | (uint32_t)(x3 >> 16); +} + +static inline void add_round_key(uint64_t *q, const uint64_t *sk) { + q[0] ^= sk[0]; + q[1] ^= sk[1]; + q[2] ^= sk[2]; + q[3] ^= sk[3]; + q[4] ^= sk[4]; + q[5] ^= sk[5]; + q[6] ^= sk[6]; + q[7] ^= sk[7]; +} + +static inline void shift_rows(uint64_t *q) { + int i; + + for (i = 0; i < 8; i++) { + uint64_t x; + + x = q[i]; + q[i] = (x & (uint64_t)0x000000000000FFFF) + | ((x & (uint64_t)0x00000000FFF00000) >> 4) + | ((x & (uint64_t)0x00000000000F0000) << 12) + | ((x & (uint64_t)0x0000FF0000000000) >> 8) + | ((x & (uint64_t)0x000000FF00000000) << 8) + | ((x & (uint64_t)0xF000000000000000) >> 12) + | ((x & (uint64_t)0x0FFF000000000000) << 4); + } +} + +static inline uint64_t rotr32(uint64_t x) { + return (x << 32) | (x >> 32); +} + +static inline void mix_columns(uint64_t *q) { + uint64_t q0, q1, q2, q3, q4, q5, q6, q7; + uint64_t r0, r1, r2, r3, r4, r5, r6, r7; + + q0 = q[0]; + q1 = q[1]; + q2 = q[2]; + q3 = q[3]; + q4 = q[4]; + q5 = q[5]; + q6 = q[6]; + q7 = q[7]; + r0 = (q0 >> 16) | (q0 << 48); + r1 = (q1 >> 16) | (q1 << 48); + r2 = (q2 >> 16) | (q2 << 48); + r3 = (q3 >> 16) | (q3 << 48); + r4 = (q4 >> 16) | (q4 << 48); + r5 = (q5 >> 16) | (q5 << 48); + r6 = (q6 >> 16) | (q6 << 48); + r7 = (q7 >> 16) | (q7 << 48); + + q[0] = q7 ^ r7 ^ r0 ^ rotr32(q0 ^ r0); + q[1] = q0 ^ r0 ^ q7 ^ r7 ^ r1 ^ rotr32(q1 ^ r1); + q[2] = q1 ^ r1 ^ r2 ^ rotr32(q2 ^ r2); + q[3] = q2 ^ r2 ^ q7 ^ r7 ^ r3 ^ rotr32(q3 ^ r3); + q[4] = q3 ^ r3 ^ q7 ^ r7 ^ r4 ^ rotr32(q4 ^ r4); + q[5] = q4 ^ r4 ^ r5 ^ rotr32(q5 ^ r5); + q[6] = q5 ^ r5 ^ r6 ^ rotr32(q6 ^ r6); + q[7] = q6 ^ r6 ^ r7 ^ rotr32(q7 ^ r7); +} + +static void interleave_constant(uint64_t *out, const unsigned char *in) { + uint32_t tmp_32_constant[16]; + int i; + + br_range_dec32le(tmp_32_constant, 16, in); + for (i = 0; i < 4; i++) { + br_aes_ct64_interleave_in(&out[i], &out[i + 4], tmp_32_constant + (i << 2)); + } + br_aes_ct64_ortho(out); +} + +static void interleave_constant32(uint32_t *out, const unsigned char *in) { + int i; + for (i = 0; i < 4; i++) { + out[2 * i] = br_dec32le(in + 4 * i); + out[2 * i + 1] = br_dec32le(in + 4 * i + 16); + } + br_aes_ct_ortho(out); +} + +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_tweak_constants( + const unsigned char *pk_seed, const unsigned char *sk_seed, + unsigned long long seed_length) { + unsigned char buf[40 * 16]; + int i; + + /* Use the standard constants to generate tweaked ones. */ + memcpy((uint8_t *)tweaked512_rc64, (uint8_t *)haraka512_rc64, 40 * 16); + + /* Constants for sk.seed */ + if (sk_seed != NULL) { + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka_S( + buf, 40 * 16, sk_seed, seed_length); + + /* Interleave constants */ + for (i = 0; i < 10; i++) { + interleave_constant32(tweaked256_rc32_sseed[i], buf + 32 * i); + } + } + + /* Constants for pk.seed */ + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka_S( + buf, 40 * 16, pk_seed, seed_length); + for (i = 0; i < 10; i++) { + interleave_constant32(tweaked256_rc32[i], buf + 32 * i); + interleave_constant(tweaked512_rc64[i], buf + 64 * i); + } +} + +static void haraka_S_absorb(unsigned char *s, + const unsigned char *m, unsigned long long mlen, + unsigned char p) { + unsigned long long i; + unsigned char t[HARAKAS_RATE]; + + while (mlen >= HARAKAS_RATE) { + /* XOR block to state */ + for (i = 0; i < HARAKAS_RATE; ++i) { + s[i] ^= m[i]; + } + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka512_perm(s, s); + mlen -= HARAKAS_RATE; + m += HARAKAS_RATE; + } + + for (i = 0; i < HARAKAS_RATE; ++i) { + t[i] = 0; + } + for (i = 0; i < mlen; ++i) { + t[i] = m[i]; + } + t[i] = p; + t[HARAKAS_RATE - 1] |= 128; + for (i = 0; i < HARAKAS_RATE; ++i) { + s[i] ^= t[i]; + } +} + +static void haraka_S_squeezeblocks(unsigned char *h, unsigned long long nblocks, + unsigned char *s) { + while (nblocks > 0) { + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka512_perm(s, s); + memcpy(h, s, HARAKAS_RATE); + h += HARAKAS_RATE; + nblocks--; + } +} + +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka_S_inc_init(uint8_t *s_inc) { + size_t i; + + for (i = 0; i < 64; i++) { + s_inc[i] = 0; + } + s_inc[64] = 0; +} + +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka_S_inc_absorb(uint8_t *s_inc, const uint8_t *m, size_t mlen) { + size_t i; + + /* Recall that s_inc[64] is the non-absorbed bytes xored into the state */ + while (mlen + s_inc[64] >= HARAKAS_RATE) { + for (i = 0; i < (size_t)(HARAKAS_RATE - s_inc[64]); i++) { + /* Take the i'th byte from message + xor with the s_inc[64] + i'th byte of the state */ + s_inc[s_inc[64] + i] ^= m[i]; + } + mlen -= (size_t)(HARAKAS_RATE - s_inc[64]); + m += HARAKAS_RATE - s_inc[64]; + s_inc[64] = 0; + + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka512_perm(s_inc, s_inc); + } + + for (i = 0; i < mlen; i++) { + s_inc[s_inc[64] + i] ^= m[i]; + } + s_inc[64] = (uint8_t)(mlen + s_inc[64]); +} + +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka_S_inc_finalize(uint8_t *s_inc) { + /* After haraka_S_inc_absorb, we are guaranteed that s_inc[64] < HARAKAS_RATE, + so we can always use one more byte for p in the current state. */ + s_inc[s_inc[64]] ^= 0x1F; + s_inc[HARAKAS_RATE - 1] ^= 128; + s_inc[64] = 0; +} + +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka_S_inc_squeeze(uint8_t *out, size_t outlen, uint8_t *s_inc) { + uint8_t i; + + /* First consume any bytes we still have sitting around */ + for (i = 0; i < outlen && i < s_inc[64]; i++) { + /* There are s_inc[64] bytes left, so r - s_inc[64] is the first + available byte. We consume from there, i.e., up to r. */ + out[i] = s_inc[(HARAKAS_RATE - s_inc[64] + i)]; + } + out += i; + outlen -= i; + s_inc[64] = (uint8_t)(s_inc[64] - i); + + /* Then squeeze the remaining necessary blocks */ + while (outlen > 0) { + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka512_perm(s_inc, s_inc); + + for (i = 0; i < outlen && i < HARAKAS_RATE; i++) { + out[i] = s_inc[i]; + } + out += i; + outlen -= i; + s_inc[64] = (uint8_t)(HARAKAS_RATE - i); + } +} + +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka_S(unsigned char *out, unsigned long long outlen, const unsigned char *in, unsigned long long inlen) { + unsigned long long i; + unsigned char s[64]; + unsigned char d[32]; + + for (i = 0; i < 64; i++) { + s[i] = 0; + } + haraka_S_absorb(s, in, inlen, 0x1F); + + haraka_S_squeezeblocks(out, outlen / 32, s); + out += (outlen / 32) * 32; + + if (outlen % 32) { + haraka_S_squeezeblocks(d, 1, s); + for (i = 0; i < outlen % 32; i++) { + out[i] = d[i]; + } + } +} + +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka512_perm(unsigned char *out, const unsigned char *in) { + uint32_t w[16]; + uint64_t q[8], tmp_q; + unsigned int i, j; + + br_range_dec32le(w, 16, in); + for (i = 0; i < 4; i++) { + br_aes_ct64_interleave_in(&q[i], &q[i + 4], w + (i << 2)); + } + br_aes_ct64_ortho(q); + + /* AES rounds */ + for (i = 0; i < 5; i++) { + for (j = 0; j < 2; j++) { + br_aes_ct64_bitslice_Sbox(q); + shift_rows(q); + mix_columns(q); + add_round_key(q, tweaked512_rc64[2 * i + j]); + } + /* Mix states */ + for (j = 0; j < 8; j++) { + tmp_q = q[j]; + q[j] = (tmp_q & 0x0001000100010001) << 5 | + (tmp_q & 0x0002000200020002) << 12 | + (tmp_q & 0x0004000400040004) >> 1 | + (tmp_q & 0x0008000800080008) << 6 | + (tmp_q & 0x0020002000200020) << 9 | + (tmp_q & 0x0040004000400040) >> 4 | + (tmp_q & 0x0080008000800080) << 3 | + (tmp_q & 0x2100210021002100) >> 5 | + (tmp_q & 0x0210021002100210) << 2 | + (tmp_q & 0x0800080008000800) << 4 | + (tmp_q & 0x1000100010001000) >> 12 | + (tmp_q & 0x4000400040004000) >> 10 | + (tmp_q & 0x8400840084008400) >> 3; + } + } + + br_aes_ct64_ortho(q); + for (i = 0; i < 4; i ++) { + br_aes_ct64_interleave_out(w + (i << 2), q[i], q[i + 4]); + } + br_range_enc32le(out, w, 16); +} + +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka512(unsigned char *out, const unsigned char *in) { + int i; + + unsigned char buf[64]; + + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka512_perm(buf, in); + /* Feed-forward */ + for (i = 0; i < 64; i++) { + buf[i] = buf[i] ^ in[i]; + } + + /* Truncated */ + memcpy(out, buf + 8, 8); + memcpy(out + 8, buf + 24, 8); + memcpy(out + 16, buf + 32, 8); + memcpy(out + 24, buf + 48, 8); +} + + +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka256(unsigned char *out, const unsigned char *in) { + uint32_t q[8], tmp_q; + int i, j; + + for (i = 0; i < 4; i++) { + q[2 * i] = br_dec32le(in + 4 * i); + q[2 * i + 1] = br_dec32le(in + 4 * i + 16); + } + br_aes_ct_ortho(q); + + /* AES rounds */ + for (i = 0; i < 5; i++) { + for (j = 0; j < 2; j++) { + br_aes_ct_bitslice_Sbox(q); + shift_rows32(q); + mix_columns32(q); + add_round_key32(q, tweaked256_rc32[2 * i + j]); + } + + /* Mix states */ + for (j = 0; j < 8; j++) { + tmp_q = q[j]; + q[j] = (tmp_q & 0x81818181) | + (tmp_q & 0x02020202) << 1 | + (tmp_q & 0x04040404) << 2 | + (tmp_q & 0x08080808) << 3 | + (tmp_q & 0x10101010) >> 3 | + (tmp_q & 0x20202020) >> 2 | + (tmp_q & 0x40404040) >> 1; + } + } + + br_aes_ct_ortho(q); + for (i = 0; i < 4; i++) { + br_enc32le(out + 4 * i, q[2 * i]); + br_enc32le(out + 4 * i + 16, q[2 * i + 1]); + } + + for (i = 0; i < 32; i++) { + out[i] ^= in[i]; + } +} + +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka256_sk(unsigned char *out, const unsigned char *in) { + uint32_t q[8], tmp_q; + int i, j; + + for (i = 0; i < 4; i++) { + q[2 * i] = br_dec32le(in + 4 * i); + q[2 * i + 1] = br_dec32le(in + 4 * i + 16); + } + br_aes_ct_ortho(q); + + /* AES rounds */ + for (i = 0; i < 5; i++) { + for (j = 0; j < 2; j++) { + br_aes_ct_bitslice_Sbox(q); + shift_rows32(q); + mix_columns32(q); + add_round_key32(q, tweaked256_rc32_sseed[2 * i + j]); + } + + /* Mix states */ + for (j = 0; j < 8; j++) { + tmp_q = q[j]; + q[j] = (tmp_q & 0x81818181) | + (tmp_q & 0x02020202) << 1 | + (tmp_q & 0x04040404) << 2 | + (tmp_q & 0x08080808) << 3 | + (tmp_q & 0x10101010) >> 3 | + (tmp_q & 0x20202020) >> 2 | + (tmp_q & 0x40404040) >> 1; + } + } + + br_aes_ct_ortho(q); + for (i = 0; i < 4; i++) { + br_enc32le(out + 4 * i, q[2 * i]); + br_enc32le(out + 4 * i + 16, q[2 * i + 1]); + } + + for (i = 0; i < 32; i++) { + out[i] ^= in[i]; + } +} diff --git a/crypto_sign/sphincs-haraka-128s-robust/clean/haraka.h b/crypto_sign/sphincs-haraka-128s-robust/clean/haraka.h new file mode 100644 index 00000000..d60ad356 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128s-robust/clean/haraka.h @@ -0,0 +1,30 @@ +#ifndef SPX_HARAKA_H +#define SPX_HARAKA_H + +/* Tweak constants with seed */ +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_tweak_constants( + const unsigned char *pk_seed, const unsigned char *sk_seed, + unsigned long long seed_length); + +/* Haraka Sponge */ +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka_S_inc_init(uint8_t *s_inc); +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka_S_inc_absorb(uint8_t *s_inc, const uint8_t *m, size_t mlen); +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka_S_inc_finalize(uint8_t *s_inc); +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka_S_inc_squeeze(uint8_t *out, size_t outlen, uint8_t *s_inc); +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka_S( + unsigned char *out, unsigned long long outlen, + const unsigned char *in, unsigned long long inlen); + +/* Applies the 512-bit Haraka permutation to in. */ +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka512_perm(unsigned char *out, const unsigned char *in); + +/* Implementation of Haraka-512 */ +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka512(unsigned char *out, const unsigned char *in); + +/* Implementation of Haraka-256 */ +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka256(unsigned char *out, const unsigned char *in); + +/* Implementation of Haraka-256 using sk.seed constants */ +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka256_sk(unsigned char *out, const unsigned char *in); + +#endif diff --git a/crypto_sign/sphincs-haraka-128s-robust/clean/hash.h b/crypto_sign/sphincs-haraka-128s-robust/clean/hash.h new file mode 100644 index 00000000..c03147f5 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128s-robust/clean/hash.h @@ -0,0 +1,22 @@ +#ifndef SPX_HASH_H +#define SPX_HASH_H + +#include + +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed); + +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen); + +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen); + +#endif diff --git a/crypto_sign/sphincs-haraka-128s-robust/clean/hash_haraka.c b/crypto_sign/sphincs-haraka-128s-robust/clean/hash_haraka.c new file mode 100644 index 00000000..3e401c33 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128s-robust/clean/hash_haraka.c @@ -0,0 +1,86 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "utils.h" + +#include "haraka.h" + +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed) { + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_tweak_constants(pub_seed, sk_seed, SPX_N); +} + +/* + * Computes PRF(key, addr), given a secret key of SPX_N bytes and an address + */ +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]) { + unsigned char buf[SPX_ADDR_BYTES]; + /* Since SPX_N may be smaller than 32, we need a temporary buffer. */ + unsigned char outbuf[32]; + + (void)key; /* Suppress an 'unused parameter' warning. */ + + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_addr_to_bytes(buf, addr); + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka256_sk(outbuf, buf); + memcpy(out, outbuf, SPX_N); +} + +/** + * Computes the message-dependent randomness R, using a secret seed and an + * optional randomization value as well as the message. + */ +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen) { + uint8_t s_inc[65]; + + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka_S_inc_init(s_inc); + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka_S_inc_absorb(s_inc, sk_prf, SPX_N); + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka_S_inc_absorb(s_inc, optrand, SPX_N); + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka_S_inc_absorb(s_inc, m, mlen); + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka_S_inc_finalize(s_inc); + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka_S_inc_squeeze(R, SPX_N, s_inc); +} + +/** + * Computes the message hash using R, the public key, and the message. + * Outputs the message digest and the index of the leaf. The index is split in + * the tree index and the leaf index, for convenient copying to an address. + */ +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen) { +#define SPX_TREE_BITS (SPX_TREE_HEIGHT * (SPX_D - 1)) +#define SPX_TREE_BYTES ((SPX_TREE_BITS + 7) / 8) +#define SPX_LEAF_BITS SPX_TREE_HEIGHT +#define SPX_LEAF_BYTES ((SPX_LEAF_BITS + 7) / 8) +#define SPX_DGST_BYTES (SPX_FORS_MSG_BYTES + SPX_TREE_BYTES + SPX_LEAF_BYTES) + + unsigned char buf[SPX_DGST_BYTES]; + unsigned char *bufp = buf; + uint8_t s_inc[65]; + + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka_S_inc_init(s_inc); + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka_S_inc_absorb(s_inc, R, SPX_N); + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka_S_inc_absorb(s_inc, pk, SPX_PK_BYTES); + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka_S_inc_absorb(s_inc, m, mlen); + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka_S_inc_finalize(s_inc); + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka_S_inc_squeeze(buf, SPX_DGST_BYTES, s_inc); + + memcpy(digest, bufp, SPX_FORS_MSG_BYTES); + bufp += SPX_FORS_MSG_BYTES; + + *tree = PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_bytes_to_ull(bufp, SPX_TREE_BYTES); + *tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS); + bufp += SPX_TREE_BYTES; + + *leaf_idx = (uint32_t)PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_bytes_to_ull( + bufp, SPX_LEAF_BYTES); + *leaf_idx &= (~(uint32_t)0) >> (32 - SPX_LEAF_BITS); +} diff --git a/crypto_sign/sphincs-haraka-128s-robust/clean/params.h b/crypto_sign/sphincs-haraka-128s-robust/clean/params.h new file mode 100644 index 00000000..ac30fdea --- /dev/null +++ b/crypto_sign/sphincs-haraka-128s-robust/clean/params.h @@ -0,0 +1,53 @@ +#ifndef SPX_PARAMS_H +#define SPX_PARAMS_H + +/* Hash output length in bytes. */ +#define SPX_N 16 +/* Height of the hypertree. */ +#define SPX_FULL_HEIGHT 64 +/* Number of subtree layer. */ +#define SPX_D 8 +/* FORS tree dimensions. */ +#define SPX_FORS_HEIGHT 15 +#define SPX_FORS_TREES 10 +/* Winternitz parameter, */ +#define SPX_WOTS_W 16 + +/* The hash function is defined by linking a different hash.c file, as opposed + to setting a #define constant. */ + +/* For clarity */ +#define SPX_ADDR_BYTES 32 + +/* WOTS parameters. */ +#define SPX_WOTS_LOGW 4 + +#define SPX_WOTS_LEN1 (8 * SPX_N / SPX_WOTS_LOGW) + +/* SPX_WOTS_LEN2 is floor(log(len_1 * (w - 1)) / log(w)) + 1; we precompute */ +#define SPX_WOTS_LEN2 3 + +#define SPX_WOTS_LEN (SPX_WOTS_LEN1 + SPX_WOTS_LEN2) +#define SPX_WOTS_BYTES (SPX_WOTS_LEN * SPX_N) +#define SPX_WOTS_PK_BYTES SPX_WOTS_BYTES + +/* Subtree size. */ +#define SPX_TREE_HEIGHT (SPX_FULL_HEIGHT / SPX_D) + +/* FORS parameters. */ +#define SPX_FORS_MSG_BYTES ((SPX_FORS_HEIGHT * SPX_FORS_TREES + 7) / 8) +#define SPX_FORS_BYTES ((SPX_FORS_HEIGHT + 1) * SPX_FORS_TREES * SPX_N) +#define SPX_FORS_PK_BYTES SPX_N + +/* Resulting SPX sizes. */ +#define SPX_BYTES (SPX_N + SPX_FORS_BYTES + SPX_D * SPX_WOTS_BYTES +\ + SPX_FULL_HEIGHT * SPX_N) +#define SPX_PK_BYTES (2 * SPX_N) +#define SPX_SK_BYTES (2 * SPX_N + SPX_PK_BYTES) + +/* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ +#define SPX_OPTRAND_BYTES 32 + +#endif diff --git a/crypto_sign/sphincs-haraka-128s-robust/clean/sign.c b/crypto_sign/sphincs-haraka-128s-robust/clean/sign.c new file mode 100644 index 00000000..5f859e47 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128s-robust/clean/sign.c @@ -0,0 +1,344 @@ +#include +#include +#include + +#include "address.h" +#include "api.h" +#include "fors.h" +#include "hash.h" +#include "params.h" +#include "randombytes.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +/** + * Computes the leaf at a given address. First generates the WOTS key pair, + * then computes leaf by hashing horizontally. + */ +static void wots_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t tree_addr[8]) { + unsigned char pk[SPX_WOTS_BYTES]; + uint32_t wots_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_keypair_addr( + wots_addr, addr_idx); + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_wots_gen_pk( + pk, sk_seed, pub_seed, wots_addr); + + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_thash_WOTS_LEN( + leaf, pk, pub_seed, wots_pk_addr); +} + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_crypto_sign_secretkeybytes(void) { + return PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_CRYPTO_SECRETKEYBYTES; +} + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_crypto_sign_publickeybytes(void) { + return PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES; +} + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_crypto_sign_bytes(void) { + return PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_CRYPTO_BYTES; +} + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_crypto_sign_seedbytes(void) { + return PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_CRYPTO_SEEDBYTES; +} + +/* + * Generates an SPX key pair given a seed of length + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed) { + /* We do not need the auth path in key generation, but it simplifies the + code to have just one treehash routine that computes both root and path + in one function. */ + unsigned char auth_path[SPX_TREE_HEIGHT * SPX_N]; + uint32_t top_tree_addr[8] = {0}; + + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_layer_addr( + top_tree_addr, SPX_D - 1); + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_type( + top_tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */ + memcpy(sk, seed, PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_CRYPTO_SEEDBYTES); + + memcpy(pk, sk + 2 * SPX_N, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_initialize_hash_function(pk, sk); + + /* Compute root node of the top-most subtree. */ + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_treehash_TREE_HEIGHT( + sk + 3 * SPX_N, auth_path, sk, sk + 2 * SPX_N, 0, 0, + wots_gen_leaf, top_tree_addr); + + memcpy(pk + SPX_N, sk + 3 * SPX_N, SPX_N); + + return 0; +} + +/* + * Generates an SPX key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk) { + unsigned char seed[PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_CRYPTO_SEEDBYTES]; + randombytes(seed, PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_CRYPTO_SEEDBYTES); + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_crypto_sign_seed_keypair( + pk, sk, seed); + + return 0; +} + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + const unsigned char *sk_seed = sk; + const unsigned char *sk_prf = sk + SPX_N; + const unsigned char *pk = sk + 2 * SPX_N; + const unsigned char *pub_seed = pk; + + unsigned char optrand[SPX_N]; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char root[SPX_N]; + uint32_t i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_initialize_hash_function( + pub_seed, sk_seed); + + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ + randombytes(optrand, SPX_N); + /* Compute the digest randomization value. */ + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_gen_message_random( + sig, sk_prf, optrand, m, mlen); + + /* Derive the message digest and leaf index from R, PK and M. */ + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Sign the message hash using FORS. */ + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_fors_sign( + sig, root, mhash, sk_seed, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Compute a WOTS signature. */ + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_wots_sign( + sig, root, sk_seed, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the authentication path for the used WOTS leaf. */ + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_treehash_TREE_HEIGHT( + root, sig, sk_seed, pub_seed, idx_leaf, 0, + wots_gen_leaf, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + *siglen = SPX_BYTES; + + return 0; +} + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk) { + const unsigned char *pub_seed = pk; + const unsigned char *pub_root = pk + SPX_N; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char wots_pk[SPX_WOTS_BYTES]; + unsigned char root[SPX_N]; + unsigned char leaf[SPX_N]; + unsigned int i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + if (siglen != SPX_BYTES) { + return -1; + } + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_initialize_hash_function( + pub_seed, NULL); + + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + /* Derive the message digest and leaf index from R || PK || M. */ + /* The additional SPX_N is a result of the hash domain separator. */ + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + /* Layer correctly defaults to 0, so no need to set_layer_addr */ + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_fors_pk_from_sig( + root, sig, mhash, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + /* For each subtree.. */ + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + + /* The WOTS public key is only correct if the signature was correct. */ + /* Initially, root is the FORS pk, but on subsequent iterations it is + the root of the subtree below the currently processed subtree. */ + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_wots_pk_from_sig( + wots_pk, sig, root, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the leaf node using the WOTS public key. */ + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_thash_WOTS_LEN( + leaf, wots_pk, pub_seed, wots_pk_addr); + + /* Compute the root node of this subtree. */ + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_compute_root( + root, leaf, idx_leaf, 0, sig, SPX_TREE_HEIGHT, + pub_seed, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + /* Check if the root node equals the root node in the public key. */ + if (memcmp(root, pub_root, SPX_N) != 0) { + return -1; + } + + return 0; +} + + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + size_t siglen; + + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_crypto_sign_signature( + sm, &siglen, m, mlen, sk); + + memmove(sm + SPX_BYTES, m, mlen); + *smlen = siglen + mlen; + + return 0; +} + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk) { + /* The API caller does not necessarily know what size a signature should be + but SPHINCS+ signatures are always exactly SPX_BYTES. */ + if (smlen < SPX_BYTES) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + *mlen = smlen - SPX_BYTES; + + if (PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_crypto_sign_verify( + sm, SPX_BYTES, sm + SPX_BYTES, *mlen, pk)) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + /* If verification was successful, move the message to the right place. */ + memmove(m, sm + SPX_BYTES, *mlen); + + return 0; +} diff --git a/crypto_sign/sphincs-haraka-128s-robust/clean/thash.h b/crypto_sign/sphincs-haraka-128s-robust/clean/thash.h new file mode 100644 index 00000000..23f18b6a --- /dev/null +++ b/crypto_sign/sphincs-haraka-128s-robust/clean/thash.h @@ -0,0 +1,22 @@ +#ifndef SPX_THASH_H +#define SPX_THASH_H + +#include + +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-haraka-128s-robust/clean/thash_haraka_robust.c b/crypto_sign/sphincs-haraka-128s-robust/clean/thash_haraka_robust.c new file mode 100644 index 00000000..58c02163 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128s-robust/clean/thash_haraka_robust.c @@ -0,0 +1,88 @@ +#include +#include + +#include "address.h" +#include "params.h" +#include "thash.h" + +#include "haraka.h" + +/** + * Takes an array of inblocks concatenated arrays of SPX_N bytes. + */ +static void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_thash( + unsigned char *out, unsigned char *buf, + const unsigned char *in, unsigned int inblocks, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char *bitmask = buf + SPX_ADDR_BYTES; + unsigned char outbuf[32]; + unsigned char buf_tmp[64]; + unsigned int i; + + (void)pub_seed; /* Suppress an 'unused parameter' warning. */ + + if (inblocks == 1) { + /* F function */ + /* Since SPX_N may be smaller than 32, we need a temporary buffer. */ + memset(buf_tmp, 0, 64); + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_addr_to_bytes(buf_tmp, addr); + + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka256(outbuf, buf_tmp); + for (i = 0; i < inblocks * SPX_N; i++) { + buf_tmp[SPX_ADDR_BYTES + i] = in[i] ^ outbuf[i]; + } + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka512(outbuf, buf_tmp); + memcpy(out, outbuf, SPX_N); + } else { + /* All other tweakable hashes*/ + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_addr_to_bytes(buf, addr); + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka_S( + bitmask, inblocks * SPX_N, buf, SPX_ADDR_BYTES); + + for (i = 0; i < inblocks * SPX_N; i++) { + buf[SPX_ADDR_BYTES + i] = in[i] ^ bitmask[i]; + } + + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka_S( + out, SPX_N, buf, SPX_ADDR_BYTES + inblocks * SPX_N); + } +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_ADDR_BYTES + 1 * SPX_N]; + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_thash( + out, buf, in, 1, pub_seed, addr); +} + +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_ADDR_BYTES + 2 * SPX_N]; + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_thash( + out, buf, in, 2, pub_seed, addr); +} + +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_ADDR_BYTES + SPX_WOTS_LEN * SPX_N]; + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_thash( + out, buf, in, SPX_WOTS_LEN, pub_seed, addr); +} + +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_ADDR_BYTES + SPX_FORS_TREES * SPX_N]; + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_thash( + out, buf, in, SPX_FORS_TREES, pub_seed, addr); +} diff --git a/crypto_sign/sphincs-haraka-128s-robust/clean/utils.c b/crypto_sign/sphincs-haraka-128s-robust/clean/utils.c new file mode 100644 index 00000000..d00b223c --- /dev/null +++ b/crypto_sign/sphincs-haraka-128s-robust/clean/utils.c @@ -0,0 +1,192 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in) { + + /* Iterate over out in decreasing order, for big-endianness. */ + for (size_t i = outlen; i > 0; i--) { + out[i - 1] = in & 0xff; + in = in >> 8; + } +} + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen) { + unsigned long long retval = 0; + + for (size_t i = 0; i < inlen; i++) { + retval |= ((unsigned long long)in[i]) << (8 * (inlen - 1 - i)); + } + return retval; +} + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + unsigned char buffer[2 * SPX_N]; + + /* If leaf_idx is odd (last bit = 1), current path element is a right child + and auth_path has to go left. Otherwise it is the other way around. */ + if (leaf_idx & 1) { + memcpy(buffer + SPX_N, leaf, SPX_N); + memcpy(buffer, auth_path, SPX_N); + } else { + memcpy(buffer, leaf, SPX_N); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + + for (i = 0; i < tree_height - 1; i++) { + leaf_idx >>= 1; + idx_offset >>= 1; + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_tree_height(addr, i + 1); + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + + /* Pick the right or left neighbor, depending on parity of the node. */ + if (leaf_idx & 1) { + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_thash_2( + buffer + SPX_N, buffer, pub_seed, addr); + memcpy(buffer, auth_path, SPX_N); + } else { + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_thash_2( + buffer, buffer, pub_seed, addr); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + } + + /* The last iteration is exceptional; we do not copy an auth_path node. */ + leaf_idx >>= 1; + idx_offset >>= 1; + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_tree_height(addr, tree_height); + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_thash_2( + root, buffer, pub_seed, addr); +} + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +static void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_treehash( + unsigned char *root, unsigned char *auth_path, + unsigned char *stack, unsigned int *heights, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned int offset = 0; + uint32_t idx; + uint32_t tree_idx; + + for (idx = 0; idx < (uint32_t)(1 << tree_height); idx++) { + /* Add the next leaf node to the stack. */ + gen_leaf(stack + offset * SPX_N, + sk_seed, pub_seed, idx + idx_offset, tree_addr); + offset++; + heights[offset - 1] = 0; + + /* If this is a node we need for the auth path.. */ + if ((leaf_idx ^ 0x1) == idx) { + memcpy(auth_path, stack + (offset - 1)*SPX_N, SPX_N); + } + + /* While the top-most nodes are of equal height.. */ + while (offset >= 2 && heights[offset - 1] == heights[offset - 2]) { + /* Compute index of the new node, in the next layer. */ + tree_idx = (idx >> (heights[offset - 1] + 1)); + + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_tree_height( + tree_addr, heights[offset - 1] + 1); + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_tree_index( + tree_addr, tree_idx + (idx_offset >> (heights[offset - 1] + 1))); + /* Hash the top-most nodes from the stack together. */ + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_thash_2( + stack + (offset - 2)*SPX_N, stack + (offset - 2)*SPX_N, + pub_seed, tree_addr); + offset--; + /* Note that the top-most node is now one layer higher. */ + heights[offset - 1]++; + + /* If this is a node we need for the auth path.. */ + if (((leaf_idx >> heights[offset - 1]) ^ 0x1) == tree_idx) { + memcpy(auth_path + heights[offset - 1]*SPX_N, + stack + (offset - 1)*SPX_N, SPX_N); + } + } + } + memcpy(root, stack, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_FORS_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_FORS_HEIGHT + 1]; + + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_FORS_HEIGHT, gen_leaf, tree_addr); +} + +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_TREE_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_TREE_HEIGHT + 1]; + + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_TREE_HEIGHT, gen_leaf, tree_addr); +} diff --git a/crypto_sign/sphincs-haraka-128s-robust/clean/utils.h b/crypto_sign/sphincs-haraka-128s-robust/clean/utils.h new file mode 100644 index 00000000..de929b27 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128s-robust/clean/utils.h @@ -0,0 +1,60 @@ +#ifndef SPX_UTILS_H +#define SPX_UTILS_H + +#include "params.h" +#include +#include + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in); + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen); + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-haraka-128s-robust/clean/wots.c b/crypto_sign/sphincs-haraka-128s-robust/clean/wots.c new file mode 100644 index 00000000..74fdc431 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128s-robust/clean/wots.c @@ -0,0 +1,161 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +// TODO clarify address expectations, and make them more uniform. +// TODO i.e. do we expect types to be set already? +// TODO and do we expect modifications or copies? + +/** + * Computes the starting value for a chain, i.e. the secret key. + * Expects the address to be complete up to the chain address. + */ +static void wots_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t wots_addr[8]) { + /* Make sure that the hash address is actually zeroed. */ + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_hash_addr(wots_addr, 0); + + /* Generate sk element. */ + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_prf_addr(sk, sk_seed, wots_addr); +} + +/** + * Computes the chaining function. + * out and in have to be n-byte arrays. + * + * Interprets in as start-th value of the chain. + * addr has to contain the address of the chain. + */ +static void gen_chain(unsigned char *out, const unsigned char *in, + unsigned int start, unsigned int steps, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + /* Initialize out with the value at position 'start'. */ + memcpy(out, in, SPX_N); + + /* Iterate 'steps' calls to the hash function. */ + for (i = start; i < (start + steps) && i < SPX_WOTS_W; i++) { + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_hash_addr(addr, i); + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_thash_1( + out, out, pub_seed, addr); + } +} + +/** + * base_w algorithm as described in draft. + * Interprets an array of bytes as integers in base w. + * This only works when log_w is a divisor of 8. + */ +static void base_w(unsigned int *output, const size_t out_len, + const unsigned char *input) { + size_t in = 0; + size_t out = 0; + unsigned char total = 0; + unsigned int bits = 0; + size_t consumed; + + for (consumed = 0; consumed < out_len; consumed++) { + if (bits == 0) { + total = input[in]; + in++; + bits += 8; + } + bits -= SPX_WOTS_LOGW; + output[out] = (unsigned int)((total >> bits) & (SPX_WOTS_W - 1)); + out++; + } +} + +/* Computes the WOTS+ checksum over a message (in base_w). */ +static void wots_checksum(unsigned int *csum_base_w, + const unsigned int *msg_base_w) { + unsigned int csum = 0; + unsigned char csum_bytes[(SPX_WOTS_LEN2 * SPX_WOTS_LOGW + 7) / 8]; + unsigned int i; + + /* Compute checksum. */ + for (i = 0; i < SPX_WOTS_LEN1; i++) { + csum += SPX_WOTS_W - 1 - msg_base_w[i]; + } + + /* Convert checksum to base_w. */ + /* Make sure expected empty zero bits are the least significant bits. */ + csum = csum << (8 - ((SPX_WOTS_LEN2 * SPX_WOTS_LOGW) % 8)); + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_ull_to_bytes( + csum_bytes, sizeof(csum_bytes), csum); + base_w(csum_base_w, SPX_WOTS_LEN2, csum_bytes); +} + +/* Takes a message and derives the matching chain lengths. */ +static void chain_lengths(unsigned int *lengths, const unsigned char *msg) { + base_w(lengths, SPX_WOTS_LEN1, msg); + wots_checksum(lengths + SPX_WOTS_LEN1, lengths); +} + +/** + * WOTS key generation. Takes a 32 byte sk_seed, expands it to WOTS private key + * elements and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(pk + i * SPX_N, sk_seed, addr); + gen_chain(pk + i * SPX_N, pk + i * SPX_N, + 0, SPX_WOTS_W - 1, pub_seed, addr); + } +} + +/** + * Takes a n-byte message and the 32-byte sk_see to compute a signature 'sig'. + */ +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(sig + i * SPX_N, sk_seed, addr); + gen_chain(sig + i * SPX_N, sig + i * SPX_N, 0, lengths[i], pub_seed, addr); + } +} + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_set_chain_addr(addr, i); + gen_chain(pk + i * SPX_N, sig + i * SPX_N, + lengths[i], SPX_WOTS_W - 1 - lengths[i], pub_seed, addr); + } +} diff --git a/crypto_sign/sphincs-haraka-128s-robust/clean/wots.h b/crypto_sign/sphincs-haraka-128s-robust/clean/wots.h new file mode 100644 index 00000000..0aa00577 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128s-robust/clean/wots.h @@ -0,0 +1,38 @@ +#ifndef SPX_WOTS_H +#define SPX_WOTS_H + +#include "params.h" +#include + +/** + * WOTS key generation. Takes a 32 byte seed for the private key, expands it to + * a full WOTS private key and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * Takes a n-byte message and the 32-byte seed for the private key to compute a + * signature that is placed at 'sig'. + */ +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]); + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-haraka-128s-simple/META.yml b/crypto_sign/sphincs-haraka-128s-simple/META.yml new file mode 100644 index 00000000..d9907683 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128s-simple/META.yml @@ -0,0 +1,27 @@ +name: SPHINCS+ +type: signature +claimed-nist-level: 1 +length-public-key: 32 +length-secret-key: 64 +length-signature: 8080 +testvectors-sha256: fcc816e14d200e212b4b955d3011f5a6b61240c7c0003e17acb1bf396ca5d4ad +principal-submitter: Andreas Hülsing +auxiliary-submitters: + - Jean-Philippe Aumasson + - Daniel J. Bernstein, + - Christoph Dobraunig + - Maria Eichlseder + - Scott Fluhrer + - Stefan-Lukas Gazdag + - Panos Kampanakis + - Stefan Kölbl + - Tanja Lange + - Martin M. Lauridsen + - Florian Mendel + - Ruben Niederhagen + - Christian Rechberger + - Joost Rijneveld + - Peter Schwabe +implementations: + - name: clean + version: https://github.com/sphincs/sphincsplus/commit/77755c94d0bc744478044d6efbb888dc13156441 diff --git a/crypto_sign/sphincs-haraka-128s-simple/clean/LICENSE b/crypto_sign/sphincs-haraka-128s-simple/clean/LICENSE new file mode 100644 index 00000000..670154e3 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128s-simple/clean/LICENSE @@ -0,0 +1,116 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + diff --git a/crypto_sign/sphincs-haraka-128s-simple/clean/Makefile b/crypto_sign/sphincs-haraka-128s-simple/clean/Makefile new file mode 100644 index 00000000..7f5c5278 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128s-simple/clean/Makefile @@ -0,0 +1,20 @@ +# This Makefile can be used with GNU Make or BSD Make + +LIB=libsphincs-haraka-128s-simple_clean.a + +HEADERS = params.h address.h wots.h utils.h fors.h api.h hash.h thash.h haraka.h +OBJECTS = address.o wots.o utils.o fors.o sign.o hash_haraka.o thash_haraka_simple.o haraka.o + +CFLAGS=-O3 -Wall -Wconversion -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) + +all: $(LIB) + +%.o: %.c $(HEADERS) + $(CC) $(CFLAGS) -c -o $@ $< + +$(LIB): $(OBJECTS) + $(AR) -r $@ $(OBJECTS) + +clean: + $(RM) $(OBJECTS) + $(RM) $(LIB) diff --git a/crypto_sign/sphincs-haraka-128s-simple/clean/Makefile.Microsoft_nmake b/crypto_sign/sphincs-haraka-128s-simple/clean/Makefile.Microsoft_nmake new file mode 100644 index 00000000..11d2bb0b --- /dev/null +++ b/crypto_sign/sphincs-haraka-128s-simple/clean/Makefile.Microsoft_nmake @@ -0,0 +1,19 @@ +# This Makefile can be used with Microsoft Visual Studio's nmake using the command: +# nmake /f Makefile.Microsoft_nmake + +LIBRARY=libsphincs-haraka-128s-simple_clean.lib +OBJECTS=address.obj wots.obj utils.obj fors.obj sign.obj hash_haraka.obj thash_haraka_simple.obj haraka.obj + +CFLAGS=/nologo /I ..\..\..\common /W4 /WX + +all: $(LIBRARY) + +# Make sure objects are recompiled if headers change. +$(OBJECTS): *.h + +$(LIBRARY): $(OBJECTS) + LIB.EXE /NOLOGO /WX /OUT:$@ $** + +clean: + -DEL $(OBJECTS) + -DEL $(LIBRARY) diff --git a/crypto_sign/sphincs-haraka-128s-simple/clean/address.c b/crypto_sign/sphincs-haraka-128s-simple/clean/address.c new file mode 100644 index 00000000..7c4de6f4 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128s-simple/clean/address.c @@ -0,0 +1,78 @@ +#include + +#include "address.h" +#include "params.h" +#include "utils.h" + +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]) { + int i; + + for (i = 0; i < 8; i++) { + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_ull_to_bytes( + bytes + i * 4, 4, addr[i]); + } +} + +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer) { + addr[0] = layer; +} + +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree) { + addr[1] = 0; + addr[2] = (uint32_t) (tree >> 32); + addr[3] = (uint32_t) tree; +} + +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_type( + uint32_t addr[8], uint32_t type) { + addr[4] = type; +} + +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; +} + +/* These functions are used for OTS addresses. */ + +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair) { + addr[5] = keypair; +} + +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; + out[5] = in[5]; +} + +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain) { + addr[6] = chain; +} + +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash) { + addr[7] = hash; +} + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height) { + addr[6] = tree_height; +} + +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index) { + addr[7] = tree_index; +} diff --git a/crypto_sign/sphincs-haraka-128s-simple/clean/address.h b/crypto_sign/sphincs-haraka-128s-simple/clean/address.h new file mode 100644 index 00000000..179a3761 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128s-simple/clean/address.h @@ -0,0 +1,50 @@ +#ifndef SPX_ADDRESS_H +#define SPX_ADDRESS_H + +#include + +#define SPX_ADDR_TYPE_WOTS 0 +#define SPX_ADDR_TYPE_WOTSPK 1 +#define SPX_ADDR_TYPE_HASHTREE 2 +#define SPX_ADDR_TYPE_FORSTREE 3 +#define SPX_ADDR_TYPE_FORSPK 4 + +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer); + +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree); + +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_type( + uint32_t addr[8], uint32_t type); + +/* Copies the layer and tree part of one address into the other */ +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for WOTS and FORS addresses. */ + +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair); + +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain); + +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash); + +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height); + +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index); + +#endif diff --git a/crypto_sign/sphincs-haraka-128s-simple/clean/api.h b/crypto_sign/sphincs-haraka-128s-simple/clean/api.h new file mode 100644 index 00000000..631d2716 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128s-simple/clean/api.h @@ -0,0 +1,78 @@ +#ifndef PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_API_H +#define PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_API_H + +#include +#include + +#define PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_CRYPTO_ALGNAME "SPHINCS+" + +#define PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_CRYPTO_SECRETKEYBYTES 64 +#define PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES 32 +#define PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_CRYPTO_BYTES 8080 +#define PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_CRYPTO_SEEDBYTES 48 + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_crypto_sign_secretkeybytes(void); + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_crypto_sign_publickeybytes(void); + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_crypto_sign_bytes(void); + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_crypto_sign_seedbytes(void); + +/* + * Generates a SPHINCS+ key pair given a seed. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed); + +/* + * Generates a SPHINCS+ key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk); + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk); + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk); + +#endif diff --git a/crypto_sign/sphincs-haraka-128s-simple/clean/fors.c b/crypto_sign/sphincs-haraka-128s-simple/clean/fors.c new file mode 100644 index 00000000..296a451a --- /dev/null +++ b/crypto_sign/sphincs-haraka-128s-simple/clean/fors.c @@ -0,0 +1,164 @@ +#include +#include +#include + +#include "address.h" +#include "fors.h" +#include "hash.h" +#include "thash.h" +#include "utils.h" + +static void fors_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_prf_addr( + sk, sk_seed, fors_leaf_addr); +} + +static void fors_sk_to_leaf(unsigned char *leaf, const unsigned char *sk, + const unsigned char *pub_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_thash_1( + leaf, sk, pub_seed, fors_leaf_addr); +} + +static void fors_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t fors_tree_addr[8]) { + uint32_t fors_leaf_addr[8] = {0}; + + /* Only copy the parts that must be kept in fors_leaf_addr. */ + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_copy_keypair_addr( + fors_leaf_addr, fors_tree_addr); + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_type( + fors_leaf_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_tree_index( + fors_leaf_addr, addr_idx); + + fors_gen_sk(leaf, sk_seed, fors_leaf_addr); + fors_sk_to_leaf(leaf, leaf, pub_seed, fors_leaf_addr); +} + +/** + * Interprets m as SPX_FORS_HEIGHT-bit unsigned integers. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + * Assumes indices has space for SPX_FORS_TREES integers. + */ +static void message_to_indices(uint32_t *indices, const unsigned char *m) { + unsigned int i, j; + unsigned int offset = 0; + + for (i = 0; i < SPX_FORS_TREES; i++) { + indices[i] = 0; + for (j = 0; j < SPX_FORS_HEIGHT; j++) { + indices[i] ^= (((uint32_t)m[offset >> 3] >> (offset & 0x7)) & 0x1) << j; + offset++; + } + } +} + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Include the secret key part that produces the selected leaf node. */ + fors_gen_sk(sig, sk_seed, fors_tree_addr); + sig += SPX_N; + + /* Compute the authentication path for this leaf node. */ + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_treehash_FORS_HEIGHT( + roots + i * SPX_N, sig, sk_seed, pub_seed, + indices[i], idx_offset, fors_gen_leaf, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + unsigned char leaf[SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Derive the leaf from the included secret key part. */ + fors_sk_to_leaf(leaf, sig, pub_seed, fors_tree_addr); + sig += SPX_N; + + /* Derive the corresponding root node of this tree. */ + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_compute_root( + roots + i * SPX_N, leaf, indices[i], idx_offset, sig, + SPX_FORS_HEIGHT, pub_seed, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} diff --git a/crypto_sign/sphincs-haraka-128s-simple/clean/fors.h b/crypto_sign/sphincs-haraka-128s-simple/clean/fors.h new file mode 100644 index 00000000..0907a358 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128s-simple/clean/fors.h @@ -0,0 +1,30 @@ +#ifndef SPX_FORS_H +#define SPX_FORS_H + +#include + +#include "params.h" + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]); + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-haraka-128s-simple/clean/haraka.c b/crypto_sign/sphincs-haraka-128s-simple/clean/haraka.c new file mode 100644 index 00000000..908cd36d --- /dev/null +++ b/crypto_sign/sphincs-haraka-128s-simple/clean/haraka.c @@ -0,0 +1,965 @@ +/* + * Constant time implementation of the Haraka hash function. + * + * The bit-sliced implementation of the AES round functions are + * based on the AES implementation in BearSSL written + * by Thomas Pornin + */ + +#include +#include +#include + +#include "haraka.h" + +#define HARAKAS_RATE 32 + +static const uint64_t haraka512_rc64[10][8] = { + {0x24cf0ab9086f628b, 0xbdd6eeecc83b8382, 0xd96fb0306cdad0a7, 0xaace082ac8f95f89, 0x449d8e8870d7041f, 0x49bb2f80b2b3e2f8, 0x0569ae98d93bb258, 0x23dc9691e7d6a4b1}, + {0xd8ba10ede0fe5b6e, 0x7ecf7dbe424c7b8e, 0x6ea9949c6df62a31, 0xbf3f3c97ec9c313e, 0x241d03a196a1861e, 0xead3a51116e5a2ea, 0x77d479fcad9574e3, 0x18657a1af894b7a0}, + {0x10671e1a7f595522, 0xd9a00ff675d28c7b, 0x2f1edf0d2b9ba661, 0xb8ff58b8e3de45f9, 0xee29261da9865c02, 0xd1532aa4b50bdf43, 0x8bf858159b231bb1, 0xdf17439d22d4f599}, + {0xdd4b2f0870b918c0, 0x757a81f3b39b1bb6, 0x7a5c556898952e3f, 0x7dd70a16d915d87a, 0x3ae61971982b8301, 0xc3ab319e030412be, 0x17c0033ac094a8cb, 0x5a0630fc1a8dc4ef}, + {0x17708988c1632f73, 0xf92ddae090b44f4f, 0x11ac0285c43aa314, 0x509059941936b8ba, 0xd03e152fa2ce9b69, 0x3fbcbcb63a32998b, 0x6204696d692254f7, 0x915542ed93ec59b4}, + {0xf4ed94aa8879236e, 0xff6cb41cd38e03c0, 0x069b38602368aeab, 0x669495b820f0ddba, 0xf42013b1b8bf9e3d, 0xcf935efe6439734d, 0xbc1dcf42ca29e3f8, 0x7e6d3ed29f78ad67}, + {0xf3b0f6837ffcddaa, 0x3a76faef934ddf41, 0xcec7ae583a9c8e35, 0xe4dd18c68f0260af, 0x2c0e5df1ad398eaa, 0x478df5236ae22e8c, 0xfb944c46fe865f39, 0xaa48f82f028132ba}, + {0x231b9ae2b76aca77, 0x292a76a712db0b40, 0x5850625dc8134491, 0x73137dd469810fb5, 0x8a12a6a202a474fd, 0xd36fd9daa78bdb80, 0xb34c5e733505706f, 0xbaf1cdca818d9d96}, + {0x2e99781335e8c641, 0xbddfe5cce47d560e, 0xf74e9bf32e5e040c, 0x1d7a709d65996be9, 0x670df36a9cf66cdd, 0xd05ef84a176a2875, 0x0f888e828cb1c44e, 0x1a79e9c9727b052c}, + {0x83497348628d84de, 0x2e9387d51f22a754, 0xb000068da2f852d6, 0x378c9e1190fd6fe5, 0x870027c316de7293, 0xe51a9d4462e047bb, 0x90ecf7f8c6251195, 0x655953bfbed90a9c}, +}; + +static uint64_t tweaked512_rc64[10][8]; +static uint32_t tweaked256_rc32[10][8]; +static uint32_t tweaked256_rc32_sseed[10][8]; + +static inline uint32_t br_dec32le(const unsigned char *src) { + return (uint32_t)src[0] + | ((uint32_t)src[1] << 8) + | ((uint32_t)src[2] << 16) + | ((uint32_t)src[3] << 24); +} + +static void br_range_dec32le(uint32_t *v, size_t num, const unsigned char *src) { + while (num-- > 0) { + *v ++ = br_dec32le(src); + src += 4; + } +} + +static inline void br_enc32le(unsigned char *dst, uint32_t x) { + dst[0] = (unsigned char)x; + dst[1] = (unsigned char)(x >> 8); + dst[2] = (unsigned char)(x >> 16); + dst[3] = (unsigned char)(x >> 24); +} + + +static void br_range_enc32le(unsigned char *dst, const uint32_t *v, size_t num) { + while (num-- > 0) { + br_enc32le(dst, *v ++); + dst += 4; + } +} + +static void br_aes_ct64_bitslice_Sbox(uint64_t *q) { + /* + * This S-box implementation is a straightforward translation of + * the circuit described by Boyar and Peralta in "A new + * combinational logic minimization technique with applications + * to cryptology" (https://eprint.iacr.org/2009/191.pdf). + * + * Note that variables x* (input) and s* (output) are numbered + * in "reverse" order (x0 is the high bit, x7 is the low bit). + */ + + uint64_t x0, x1, x2, x3, x4, x5, x6, x7; + uint64_t y1, y2, y3, y4, y5, y6, y7, y8, y9; + uint64_t y10, y11, y12, y13, y14, y15, y16, y17, y18, y19; + uint64_t y20, y21; + uint64_t z0, z1, z2, z3, z4, z5, z6, z7, z8, z9; + uint64_t z10, z11, z12, z13, z14, z15, z16, z17; + uint64_t t0, t1, t2, t3, t4, t5, t6, t7, t8, t9; + uint64_t t10, t11, t12, t13, t14, t15, t16, t17, t18, t19; + uint64_t t20, t21, t22, t23, t24, t25, t26, t27, t28, t29; + uint64_t t30, t31, t32, t33, t34, t35, t36, t37, t38, t39; + uint64_t t40, t41, t42, t43, t44, t45, t46, t47, t48, t49; + uint64_t t50, t51, t52, t53, t54, t55, t56, t57, t58, t59; + uint64_t t60, t61, t62, t63, t64, t65, t66, t67; + uint64_t s0, s1, s2, s3, s4, s5, s6, s7; + + x0 = q[7]; + x1 = q[6]; + x2 = q[5]; + x3 = q[4]; + x4 = q[3]; + x5 = q[2]; + x6 = q[1]; + x7 = q[0]; + + /* + * Top linear transformation. + */ + y14 = x3 ^ x5; + y13 = x0 ^ x6; + y9 = x0 ^ x3; + y8 = x0 ^ x5; + t0 = x1 ^ x2; + y1 = t0 ^ x7; + y4 = y1 ^ x3; + y12 = y13 ^ y14; + y2 = y1 ^ x0; + y5 = y1 ^ x6; + y3 = y5 ^ y8; + t1 = x4 ^ y12; + y15 = t1 ^ x5; + y20 = t1 ^ x1; + y6 = y15 ^ x7; + y10 = y15 ^ t0; + y11 = y20 ^ y9; + y7 = x7 ^ y11; + y17 = y10 ^ y11; + y19 = y10 ^ y8; + y16 = t0 ^ y11; + y21 = y13 ^ y16; + y18 = x0 ^ y16; + + /* + * Non-linear section. + */ + t2 = y12 & y15; + t3 = y3 & y6; + t4 = t3 ^ t2; + t5 = y4 & x7; + t6 = t5 ^ t2; + t7 = y13 & y16; + t8 = y5 & y1; + t9 = t8 ^ t7; + t10 = y2 & y7; + t11 = t10 ^ t7; + t12 = y9 & y11; + t13 = y14 & y17; + t14 = t13 ^ t12; + t15 = y8 & y10; + t16 = t15 ^ t12; + t17 = t4 ^ t14; + t18 = t6 ^ t16; + t19 = t9 ^ t14; + t20 = t11 ^ t16; + t21 = t17 ^ y20; + t22 = t18 ^ y19; + t23 = t19 ^ y21; + t24 = t20 ^ y18; + + t25 = t21 ^ t22; + t26 = t21 & t23; + t27 = t24 ^ t26; + t28 = t25 & t27; + t29 = t28 ^ t22; + t30 = t23 ^ t24; + t31 = t22 ^ t26; + t32 = t31 & t30; + t33 = t32 ^ t24; + t34 = t23 ^ t33; + t35 = t27 ^ t33; + t36 = t24 & t35; + t37 = t36 ^ t34; + t38 = t27 ^ t36; + t39 = t29 & t38; + t40 = t25 ^ t39; + + t41 = t40 ^ t37; + t42 = t29 ^ t33; + t43 = t29 ^ t40; + t44 = t33 ^ t37; + t45 = t42 ^ t41; + z0 = t44 & y15; + z1 = t37 & y6; + z2 = t33 & x7; + z3 = t43 & y16; + z4 = t40 & y1; + z5 = t29 & y7; + z6 = t42 & y11; + z7 = t45 & y17; + z8 = t41 & y10; + z9 = t44 & y12; + z10 = t37 & y3; + z11 = t33 & y4; + z12 = t43 & y13; + z13 = t40 & y5; + z14 = t29 & y2; + z15 = t42 & y9; + z16 = t45 & y14; + z17 = t41 & y8; + + /* + * Bottom linear transformation. + */ + t46 = z15 ^ z16; + t47 = z10 ^ z11; + t48 = z5 ^ z13; + t49 = z9 ^ z10; + t50 = z2 ^ z12; + t51 = z2 ^ z5; + t52 = z7 ^ z8; + t53 = z0 ^ z3; + t54 = z6 ^ z7; + t55 = z16 ^ z17; + t56 = z12 ^ t48; + t57 = t50 ^ t53; + t58 = z4 ^ t46; + t59 = z3 ^ t54; + t60 = t46 ^ t57; + t61 = z14 ^ t57; + t62 = t52 ^ t58; + t63 = t49 ^ t58; + t64 = z4 ^ t59; + t65 = t61 ^ t62; + t66 = z1 ^ t63; + s0 = t59 ^ t63; + s6 = t56 ^ ~t62; + s7 = t48 ^ ~t60; + t67 = t64 ^ t65; + s3 = t53 ^ t66; + s4 = t51 ^ t66; + s5 = t47 ^ t65; + s1 = t64 ^ ~s3; + s2 = t55 ^ ~t67; + + q[7] = s0; + q[6] = s1; + q[5] = s2; + q[4] = s3; + q[3] = s4; + q[2] = s5; + q[1] = s6; + q[0] = s7; +} + +static void br_aes_ct_bitslice_Sbox(uint32_t *q) { + /* + * This S-box implementation is a straightforward translation of + * the circuit described by Boyar and Peralta in "A new + * combinational logic minimization technique with applications + * to cryptology" (https://eprint.iacr.org/2009/191.pdf). + * + * Note that variables x* (input) and s* (output) are numbered + * in "reverse" order (x0 is the high bit, x7 is the low bit). + */ + + uint32_t x0, x1, x2, x3, x4, x5, x6, x7; + uint32_t y1, y2, y3, y4, y5, y6, y7, y8, y9; + uint32_t y10, y11, y12, y13, y14, y15, y16, y17, y18, y19; + uint32_t y20, y21; + uint32_t z0, z1, z2, z3, z4, z5, z6, z7, z8, z9; + uint32_t z10, z11, z12, z13, z14, z15, z16, z17; + uint32_t t0, t1, t2, t3, t4, t5, t6, t7, t8, t9; + uint32_t t10, t11, t12, t13, t14, t15, t16, t17, t18, t19; + uint32_t t20, t21, t22, t23, t24, t25, t26, t27, t28, t29; + uint32_t t30, t31, t32, t33, t34, t35, t36, t37, t38, t39; + uint32_t t40, t41, t42, t43, t44, t45, t46, t47, t48, t49; + uint32_t t50, t51, t52, t53, t54, t55, t56, t57, t58, t59; + uint32_t t60, t61, t62, t63, t64, t65, t66, t67; + uint32_t s0, s1, s2, s3, s4, s5, s6, s7; + + x0 = q[7]; + x1 = q[6]; + x2 = q[5]; + x3 = q[4]; + x4 = q[3]; + x5 = q[2]; + x6 = q[1]; + x7 = q[0]; + + /* + * Top linear transformation. + */ + y14 = x3 ^ x5; + y13 = x0 ^ x6; + y9 = x0 ^ x3; + y8 = x0 ^ x5; + t0 = x1 ^ x2; + y1 = t0 ^ x7; + y4 = y1 ^ x3; + y12 = y13 ^ y14; + y2 = y1 ^ x0; + y5 = y1 ^ x6; + y3 = y5 ^ y8; + t1 = x4 ^ y12; + y15 = t1 ^ x5; + y20 = t1 ^ x1; + y6 = y15 ^ x7; + y10 = y15 ^ t0; + y11 = y20 ^ y9; + y7 = x7 ^ y11; + y17 = y10 ^ y11; + y19 = y10 ^ y8; + y16 = t0 ^ y11; + y21 = y13 ^ y16; + y18 = x0 ^ y16; + + /* + * Non-linear section. + */ + t2 = y12 & y15; + t3 = y3 & y6; + t4 = t3 ^ t2; + t5 = y4 & x7; + t6 = t5 ^ t2; + t7 = y13 & y16; + t8 = y5 & y1; + t9 = t8 ^ t7; + t10 = y2 & y7; + t11 = t10 ^ t7; + t12 = y9 & y11; + t13 = y14 & y17; + t14 = t13 ^ t12; + t15 = y8 & y10; + t16 = t15 ^ t12; + t17 = t4 ^ t14; + t18 = t6 ^ t16; + t19 = t9 ^ t14; + t20 = t11 ^ t16; + t21 = t17 ^ y20; + t22 = t18 ^ y19; + t23 = t19 ^ y21; + t24 = t20 ^ y18; + + t25 = t21 ^ t22; + t26 = t21 & t23; + t27 = t24 ^ t26; + t28 = t25 & t27; + t29 = t28 ^ t22; + t30 = t23 ^ t24; + t31 = t22 ^ t26; + t32 = t31 & t30; + t33 = t32 ^ t24; + t34 = t23 ^ t33; + t35 = t27 ^ t33; + t36 = t24 & t35; + t37 = t36 ^ t34; + t38 = t27 ^ t36; + t39 = t29 & t38; + t40 = t25 ^ t39; + + t41 = t40 ^ t37; + t42 = t29 ^ t33; + t43 = t29 ^ t40; + t44 = t33 ^ t37; + t45 = t42 ^ t41; + z0 = t44 & y15; + z1 = t37 & y6; + z2 = t33 & x7; + z3 = t43 & y16; + z4 = t40 & y1; + z5 = t29 & y7; + z6 = t42 & y11; + z7 = t45 & y17; + z8 = t41 & y10; + z9 = t44 & y12; + z10 = t37 & y3; + z11 = t33 & y4; + z12 = t43 & y13; + z13 = t40 & y5; + z14 = t29 & y2; + z15 = t42 & y9; + z16 = t45 & y14; + z17 = t41 & y8; + + /* + * Bottom linear transformation. + */ + t46 = z15 ^ z16; + t47 = z10 ^ z11; + t48 = z5 ^ z13; + t49 = z9 ^ z10; + t50 = z2 ^ z12; + t51 = z2 ^ z5; + t52 = z7 ^ z8; + t53 = z0 ^ z3; + t54 = z6 ^ z7; + t55 = z16 ^ z17; + t56 = z12 ^ t48; + t57 = t50 ^ t53; + t58 = z4 ^ t46; + t59 = z3 ^ t54; + t60 = t46 ^ t57; + t61 = z14 ^ t57; + t62 = t52 ^ t58; + t63 = t49 ^ t58; + t64 = z4 ^ t59; + t65 = t61 ^ t62; + t66 = z1 ^ t63; + s0 = t59 ^ t63; + s6 = t56 ^ ~t62; + s7 = t48 ^ ~t60; + t67 = t64 ^ t65; + s3 = t53 ^ t66; + s4 = t51 ^ t66; + s5 = t47 ^ t65; + s1 = t64 ^ ~s3; + s2 = t55 ^ ~t67; + + q[7] = s0; + q[6] = s1; + q[5] = s2; + q[4] = s3; + q[3] = s4; + q[2] = s5; + q[1] = s6; + q[0] = s7; +} + +static void br_aes_ct_ortho(uint32_t *q) { +#define SWAPN_32(cl, ch, s, x, y) do { \ + uint32_t a, b; \ + a = (x); \ + b = (y); \ + (x) = (a & (uint32_t)(cl)) | ((b & (uint32_t)(cl)) << (s)); \ + (y) = ((a & (uint32_t)(ch)) >> (s)) | (b & (uint32_t)(ch)); \ + } while (0) + +#define SWAP2_32(x, y) SWAPN_32(0x55555555, 0xAAAAAAAA, 1, x, y) +#define SWAP4_32(x, y) SWAPN_32(0x33333333, 0xCCCCCCCC, 2, x, y) +#define SWAP8_32(x, y) SWAPN_32(0x0F0F0F0F, 0xF0F0F0F0, 4, x, y) + + SWAP2_32(q[0], q[1]); + SWAP2_32(q[2], q[3]); + SWAP2_32(q[4], q[5]); + SWAP2_32(q[6], q[7]); + + SWAP4_32(q[0], q[2]); + SWAP4_32(q[1], q[3]); + SWAP4_32(q[4], q[6]); + SWAP4_32(q[5], q[7]); + + SWAP8_32(q[0], q[4]); + SWAP8_32(q[1], q[5]); + SWAP8_32(q[2], q[6]); + SWAP8_32(q[3], q[7]); +} + +static inline void add_round_key32(uint32_t *q, const uint32_t *sk) { + q[0] ^= sk[0]; + q[1] ^= sk[1]; + q[2] ^= sk[2]; + q[3] ^= sk[3]; + q[4] ^= sk[4]; + q[5] ^= sk[5]; + q[6] ^= sk[6]; + q[7] ^= sk[7]; +} + +static inline void shift_rows32(uint32_t *q) { + int i; + + for (i = 0; i < 8; i++) { + uint32_t x; + + x = q[i]; + q[i] = (x & 0x000000FF) + | ((x & 0x0000FC00) >> 2) | ((x & 0x00000300) << 6) + | ((x & 0x00F00000) >> 4) | ((x & 0x000F0000) << 4) + | ((x & 0xC0000000) >> 6) | ((x & 0x3F000000) << 2); + } +} + +static inline uint32_t rotr16(uint32_t x) { + return (x << 16) | (x >> 16); +} + +static inline void mix_columns32(uint32_t *q) { + uint32_t q0, q1, q2, q3, q4, q5, q6, q7; + uint32_t r0, r1, r2, r3, r4, r5, r6, r7; + + q0 = q[0]; + q1 = q[1]; + q2 = q[2]; + q3 = q[3]; + q4 = q[4]; + q5 = q[5]; + q6 = q[6]; + q7 = q[7]; + r0 = (q0 >> 8) | (q0 << 24); + r1 = (q1 >> 8) | (q1 << 24); + r2 = (q2 >> 8) | (q2 << 24); + r3 = (q3 >> 8) | (q3 << 24); + r4 = (q4 >> 8) | (q4 << 24); + r5 = (q5 >> 8) | (q5 << 24); + r6 = (q6 >> 8) | (q6 << 24); + r7 = (q7 >> 8) | (q7 << 24); + + q[0] = q7 ^ r7 ^ r0 ^ rotr16(q0 ^ r0); + q[1] = q0 ^ r0 ^ q7 ^ r7 ^ r1 ^ rotr16(q1 ^ r1); + q[2] = q1 ^ r1 ^ r2 ^ rotr16(q2 ^ r2); + q[3] = q2 ^ r2 ^ q7 ^ r7 ^ r3 ^ rotr16(q3 ^ r3); + q[4] = q3 ^ r3 ^ q7 ^ r7 ^ r4 ^ rotr16(q4 ^ r4); + q[5] = q4 ^ r4 ^ r5 ^ rotr16(q5 ^ r5); + q[6] = q5 ^ r5 ^ r6 ^ rotr16(q6 ^ r6); + q[7] = q6 ^ r6 ^ r7 ^ rotr16(q7 ^ r7); +} + +static void br_aes_ct64_ortho(uint64_t *q) { +#define SWAPN(cl, ch, s, x, y) do { \ + uint64_t a, b; \ + a = (x); \ + b = (y); \ + (x) = (a & (uint64_t)(cl)) | ((b & (uint64_t)(cl)) << (s)); \ + (y) = ((a & (uint64_t)(ch)) >> (s)) | (b & (uint64_t)(ch)); \ + } while (0) + +#define SWAP2(x, y) SWAPN(0x5555555555555555, 0xAAAAAAAAAAAAAAAA, 1, x, y) +#define SWAP4(x, y) SWAPN(0x3333333333333333, 0xCCCCCCCCCCCCCCCC, 2, x, y) +#define SWAP8(x, y) SWAPN(0x0F0F0F0F0F0F0F0F, 0xF0F0F0F0F0F0F0F0, 4, x, y) + + SWAP2(q[0], q[1]); + SWAP2(q[2], q[3]); + SWAP2(q[4], q[5]); + SWAP2(q[6], q[7]); + + SWAP4(q[0], q[2]); + SWAP4(q[1], q[3]); + SWAP4(q[4], q[6]); + SWAP4(q[5], q[7]); + + SWAP8(q[0], q[4]); + SWAP8(q[1], q[5]); + SWAP8(q[2], q[6]); + SWAP8(q[3], q[7]); +} + + +static void br_aes_ct64_interleave_in(uint64_t *q0, uint64_t *q1, const uint32_t *w) { + uint64_t x0, x1, x2, x3; + + x0 = w[0]; + x1 = w[1]; + x2 = w[2]; + x3 = w[3]; + x0 |= (x0 << 16); + x1 |= (x1 << 16); + x2 |= (x2 << 16); + x3 |= (x3 << 16); + x0 &= (uint64_t)0x0000FFFF0000FFFF; + x1 &= (uint64_t)0x0000FFFF0000FFFF; + x2 &= (uint64_t)0x0000FFFF0000FFFF; + x3 &= (uint64_t)0x0000FFFF0000FFFF; + x0 |= (x0 << 8); + x1 |= (x1 << 8); + x2 |= (x2 << 8); + x3 |= (x3 << 8); + x0 &= (uint64_t)0x00FF00FF00FF00FF; + x1 &= (uint64_t)0x00FF00FF00FF00FF; + x2 &= (uint64_t)0x00FF00FF00FF00FF; + x3 &= (uint64_t)0x00FF00FF00FF00FF; + *q0 = x0 | (x2 << 8); + *q1 = x1 | (x3 << 8); +} + + +static void br_aes_ct64_interleave_out(uint32_t *w, uint64_t q0, uint64_t q1) { + uint64_t x0, x1, x2, x3; + + x0 = q0 & (uint64_t)0x00FF00FF00FF00FF; + x1 = q1 & (uint64_t)0x00FF00FF00FF00FF; + x2 = (q0 >> 8) & (uint64_t)0x00FF00FF00FF00FF; + x3 = (q1 >> 8) & (uint64_t)0x00FF00FF00FF00FF; + x0 |= (x0 >> 8); + x1 |= (x1 >> 8); + x2 |= (x2 >> 8); + x3 |= (x3 >> 8); + x0 &= (uint64_t)0x0000FFFF0000FFFF; + x1 &= (uint64_t)0x0000FFFF0000FFFF; + x2 &= (uint64_t)0x0000FFFF0000FFFF; + x3 &= (uint64_t)0x0000FFFF0000FFFF; + w[0] = (uint32_t)x0 | (uint32_t)(x0 >> 16); + w[1] = (uint32_t)x1 | (uint32_t)(x1 >> 16); + w[2] = (uint32_t)x2 | (uint32_t)(x2 >> 16); + w[3] = (uint32_t)x3 | (uint32_t)(x3 >> 16); +} + +static inline void add_round_key(uint64_t *q, const uint64_t *sk) { + q[0] ^= sk[0]; + q[1] ^= sk[1]; + q[2] ^= sk[2]; + q[3] ^= sk[3]; + q[4] ^= sk[4]; + q[5] ^= sk[5]; + q[6] ^= sk[6]; + q[7] ^= sk[7]; +} + +static inline void shift_rows(uint64_t *q) { + int i; + + for (i = 0; i < 8; i++) { + uint64_t x; + + x = q[i]; + q[i] = (x & (uint64_t)0x000000000000FFFF) + | ((x & (uint64_t)0x00000000FFF00000) >> 4) + | ((x & (uint64_t)0x00000000000F0000) << 12) + | ((x & (uint64_t)0x0000FF0000000000) >> 8) + | ((x & (uint64_t)0x000000FF00000000) << 8) + | ((x & (uint64_t)0xF000000000000000) >> 12) + | ((x & (uint64_t)0x0FFF000000000000) << 4); + } +} + +static inline uint64_t rotr32(uint64_t x) { + return (x << 32) | (x >> 32); +} + +static inline void mix_columns(uint64_t *q) { + uint64_t q0, q1, q2, q3, q4, q5, q6, q7; + uint64_t r0, r1, r2, r3, r4, r5, r6, r7; + + q0 = q[0]; + q1 = q[1]; + q2 = q[2]; + q3 = q[3]; + q4 = q[4]; + q5 = q[5]; + q6 = q[6]; + q7 = q[7]; + r0 = (q0 >> 16) | (q0 << 48); + r1 = (q1 >> 16) | (q1 << 48); + r2 = (q2 >> 16) | (q2 << 48); + r3 = (q3 >> 16) | (q3 << 48); + r4 = (q4 >> 16) | (q4 << 48); + r5 = (q5 >> 16) | (q5 << 48); + r6 = (q6 >> 16) | (q6 << 48); + r7 = (q7 >> 16) | (q7 << 48); + + q[0] = q7 ^ r7 ^ r0 ^ rotr32(q0 ^ r0); + q[1] = q0 ^ r0 ^ q7 ^ r7 ^ r1 ^ rotr32(q1 ^ r1); + q[2] = q1 ^ r1 ^ r2 ^ rotr32(q2 ^ r2); + q[3] = q2 ^ r2 ^ q7 ^ r7 ^ r3 ^ rotr32(q3 ^ r3); + q[4] = q3 ^ r3 ^ q7 ^ r7 ^ r4 ^ rotr32(q4 ^ r4); + q[5] = q4 ^ r4 ^ r5 ^ rotr32(q5 ^ r5); + q[6] = q5 ^ r5 ^ r6 ^ rotr32(q6 ^ r6); + q[7] = q6 ^ r6 ^ r7 ^ rotr32(q7 ^ r7); +} + +static void interleave_constant(uint64_t *out, const unsigned char *in) { + uint32_t tmp_32_constant[16]; + int i; + + br_range_dec32le(tmp_32_constant, 16, in); + for (i = 0; i < 4; i++) { + br_aes_ct64_interleave_in(&out[i], &out[i + 4], tmp_32_constant + (i << 2)); + } + br_aes_ct64_ortho(out); +} + +static void interleave_constant32(uint32_t *out, const unsigned char *in) { + int i; + for (i = 0; i < 4; i++) { + out[2 * i] = br_dec32le(in + 4 * i); + out[2 * i + 1] = br_dec32le(in + 4 * i + 16); + } + br_aes_ct_ortho(out); +} + +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_tweak_constants( + const unsigned char *pk_seed, const unsigned char *sk_seed, + unsigned long long seed_length) { + unsigned char buf[40 * 16]; + int i; + + /* Use the standard constants to generate tweaked ones. */ + memcpy((uint8_t *)tweaked512_rc64, (uint8_t *)haraka512_rc64, 40 * 16); + + /* Constants for sk.seed */ + if (sk_seed != NULL) { + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka_S( + buf, 40 * 16, sk_seed, seed_length); + + /* Interleave constants */ + for (i = 0; i < 10; i++) { + interleave_constant32(tweaked256_rc32_sseed[i], buf + 32 * i); + } + } + + /* Constants for pk.seed */ + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka_S( + buf, 40 * 16, pk_seed, seed_length); + for (i = 0; i < 10; i++) { + interleave_constant32(tweaked256_rc32[i], buf + 32 * i); + interleave_constant(tweaked512_rc64[i], buf + 64 * i); + } +} + +static void haraka_S_absorb(unsigned char *s, + const unsigned char *m, unsigned long long mlen, + unsigned char p) { + unsigned long long i; + unsigned char t[HARAKAS_RATE]; + + while (mlen >= HARAKAS_RATE) { + /* XOR block to state */ + for (i = 0; i < HARAKAS_RATE; ++i) { + s[i] ^= m[i]; + } + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka512_perm(s, s); + mlen -= HARAKAS_RATE; + m += HARAKAS_RATE; + } + + for (i = 0; i < HARAKAS_RATE; ++i) { + t[i] = 0; + } + for (i = 0; i < mlen; ++i) { + t[i] = m[i]; + } + t[i] = p; + t[HARAKAS_RATE - 1] |= 128; + for (i = 0; i < HARAKAS_RATE; ++i) { + s[i] ^= t[i]; + } +} + +static void haraka_S_squeezeblocks(unsigned char *h, unsigned long long nblocks, + unsigned char *s) { + while (nblocks > 0) { + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka512_perm(s, s); + memcpy(h, s, HARAKAS_RATE); + h += HARAKAS_RATE; + nblocks--; + } +} + +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka_S_inc_init(uint8_t *s_inc) { + size_t i; + + for (i = 0; i < 64; i++) { + s_inc[i] = 0; + } + s_inc[64] = 0; +} + +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka_S_inc_absorb(uint8_t *s_inc, const uint8_t *m, size_t mlen) { + size_t i; + + /* Recall that s_inc[64] is the non-absorbed bytes xored into the state */ + while (mlen + s_inc[64] >= HARAKAS_RATE) { + for (i = 0; i < (size_t)(HARAKAS_RATE - s_inc[64]); i++) { + /* Take the i'th byte from message + xor with the s_inc[64] + i'th byte of the state */ + s_inc[s_inc[64] + i] ^= m[i]; + } + mlen -= (size_t)(HARAKAS_RATE - s_inc[64]); + m += HARAKAS_RATE - s_inc[64]; + s_inc[64] = 0; + + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka512_perm(s_inc, s_inc); + } + + for (i = 0; i < mlen; i++) { + s_inc[s_inc[64] + i] ^= m[i]; + } + s_inc[64] = (uint8_t)(mlen + s_inc[64]); +} + +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka_S_inc_finalize(uint8_t *s_inc) { + /* After haraka_S_inc_absorb, we are guaranteed that s_inc[64] < HARAKAS_RATE, + so we can always use one more byte for p in the current state. */ + s_inc[s_inc[64]] ^= 0x1F; + s_inc[HARAKAS_RATE - 1] ^= 128; + s_inc[64] = 0; +} + +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka_S_inc_squeeze(uint8_t *out, size_t outlen, uint8_t *s_inc) { + uint8_t i; + + /* First consume any bytes we still have sitting around */ + for (i = 0; i < outlen && i < s_inc[64]; i++) { + /* There are s_inc[64] bytes left, so r - s_inc[64] is the first + available byte. We consume from there, i.e., up to r. */ + out[i] = s_inc[(HARAKAS_RATE - s_inc[64] + i)]; + } + out += i; + outlen -= i; + s_inc[64] = (uint8_t)(s_inc[64] - i); + + /* Then squeeze the remaining necessary blocks */ + while (outlen > 0) { + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka512_perm(s_inc, s_inc); + + for (i = 0; i < outlen && i < HARAKAS_RATE; i++) { + out[i] = s_inc[i]; + } + out += i; + outlen -= i; + s_inc[64] = (uint8_t)(HARAKAS_RATE - i); + } +} + +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka_S(unsigned char *out, unsigned long long outlen, const unsigned char *in, unsigned long long inlen) { + unsigned long long i; + unsigned char s[64]; + unsigned char d[32]; + + for (i = 0; i < 64; i++) { + s[i] = 0; + } + haraka_S_absorb(s, in, inlen, 0x1F); + + haraka_S_squeezeblocks(out, outlen / 32, s); + out += (outlen / 32) * 32; + + if (outlen % 32) { + haraka_S_squeezeblocks(d, 1, s); + for (i = 0; i < outlen % 32; i++) { + out[i] = d[i]; + } + } +} + +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka512_perm(unsigned char *out, const unsigned char *in) { + uint32_t w[16]; + uint64_t q[8], tmp_q; + unsigned int i, j; + + br_range_dec32le(w, 16, in); + for (i = 0; i < 4; i++) { + br_aes_ct64_interleave_in(&q[i], &q[i + 4], w + (i << 2)); + } + br_aes_ct64_ortho(q); + + /* AES rounds */ + for (i = 0; i < 5; i++) { + for (j = 0; j < 2; j++) { + br_aes_ct64_bitslice_Sbox(q); + shift_rows(q); + mix_columns(q); + add_round_key(q, tweaked512_rc64[2 * i + j]); + } + /* Mix states */ + for (j = 0; j < 8; j++) { + tmp_q = q[j]; + q[j] = (tmp_q & 0x0001000100010001) << 5 | + (tmp_q & 0x0002000200020002) << 12 | + (tmp_q & 0x0004000400040004) >> 1 | + (tmp_q & 0x0008000800080008) << 6 | + (tmp_q & 0x0020002000200020) << 9 | + (tmp_q & 0x0040004000400040) >> 4 | + (tmp_q & 0x0080008000800080) << 3 | + (tmp_q & 0x2100210021002100) >> 5 | + (tmp_q & 0x0210021002100210) << 2 | + (tmp_q & 0x0800080008000800) << 4 | + (tmp_q & 0x1000100010001000) >> 12 | + (tmp_q & 0x4000400040004000) >> 10 | + (tmp_q & 0x8400840084008400) >> 3; + } + } + + br_aes_ct64_ortho(q); + for (i = 0; i < 4; i ++) { + br_aes_ct64_interleave_out(w + (i << 2), q[i], q[i + 4]); + } + br_range_enc32le(out, w, 16); +} + +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka512(unsigned char *out, const unsigned char *in) { + int i; + + unsigned char buf[64]; + + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka512_perm(buf, in); + /* Feed-forward */ + for (i = 0; i < 64; i++) { + buf[i] = buf[i] ^ in[i]; + } + + /* Truncated */ + memcpy(out, buf + 8, 8); + memcpy(out + 8, buf + 24, 8); + memcpy(out + 16, buf + 32, 8); + memcpy(out + 24, buf + 48, 8); +} + + +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka256(unsigned char *out, const unsigned char *in) { + uint32_t q[8], tmp_q; + int i, j; + + for (i = 0; i < 4; i++) { + q[2 * i] = br_dec32le(in + 4 * i); + q[2 * i + 1] = br_dec32le(in + 4 * i + 16); + } + br_aes_ct_ortho(q); + + /* AES rounds */ + for (i = 0; i < 5; i++) { + for (j = 0; j < 2; j++) { + br_aes_ct_bitslice_Sbox(q); + shift_rows32(q); + mix_columns32(q); + add_round_key32(q, tweaked256_rc32[2 * i + j]); + } + + /* Mix states */ + for (j = 0; j < 8; j++) { + tmp_q = q[j]; + q[j] = (tmp_q & 0x81818181) | + (tmp_q & 0x02020202) << 1 | + (tmp_q & 0x04040404) << 2 | + (tmp_q & 0x08080808) << 3 | + (tmp_q & 0x10101010) >> 3 | + (tmp_q & 0x20202020) >> 2 | + (tmp_q & 0x40404040) >> 1; + } + } + + br_aes_ct_ortho(q); + for (i = 0; i < 4; i++) { + br_enc32le(out + 4 * i, q[2 * i]); + br_enc32le(out + 4 * i + 16, q[2 * i + 1]); + } + + for (i = 0; i < 32; i++) { + out[i] ^= in[i]; + } +} + +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka256_sk(unsigned char *out, const unsigned char *in) { + uint32_t q[8], tmp_q; + int i, j; + + for (i = 0; i < 4; i++) { + q[2 * i] = br_dec32le(in + 4 * i); + q[2 * i + 1] = br_dec32le(in + 4 * i + 16); + } + br_aes_ct_ortho(q); + + /* AES rounds */ + for (i = 0; i < 5; i++) { + for (j = 0; j < 2; j++) { + br_aes_ct_bitslice_Sbox(q); + shift_rows32(q); + mix_columns32(q); + add_round_key32(q, tweaked256_rc32_sseed[2 * i + j]); + } + + /* Mix states */ + for (j = 0; j < 8; j++) { + tmp_q = q[j]; + q[j] = (tmp_q & 0x81818181) | + (tmp_q & 0x02020202) << 1 | + (tmp_q & 0x04040404) << 2 | + (tmp_q & 0x08080808) << 3 | + (tmp_q & 0x10101010) >> 3 | + (tmp_q & 0x20202020) >> 2 | + (tmp_q & 0x40404040) >> 1; + } + } + + br_aes_ct_ortho(q); + for (i = 0; i < 4; i++) { + br_enc32le(out + 4 * i, q[2 * i]); + br_enc32le(out + 4 * i + 16, q[2 * i + 1]); + } + + for (i = 0; i < 32; i++) { + out[i] ^= in[i]; + } +} diff --git a/crypto_sign/sphincs-haraka-128s-simple/clean/haraka.h b/crypto_sign/sphincs-haraka-128s-simple/clean/haraka.h new file mode 100644 index 00000000..f09e5abf --- /dev/null +++ b/crypto_sign/sphincs-haraka-128s-simple/clean/haraka.h @@ -0,0 +1,30 @@ +#ifndef SPX_HARAKA_H +#define SPX_HARAKA_H + +/* Tweak constants with seed */ +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_tweak_constants( + const unsigned char *pk_seed, const unsigned char *sk_seed, + unsigned long long seed_length); + +/* Haraka Sponge */ +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka_S_inc_init(uint8_t *s_inc); +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka_S_inc_absorb(uint8_t *s_inc, const uint8_t *m, size_t mlen); +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka_S_inc_finalize(uint8_t *s_inc); +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka_S_inc_squeeze(uint8_t *out, size_t outlen, uint8_t *s_inc); +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka_S( + unsigned char *out, unsigned long long outlen, + const unsigned char *in, unsigned long long inlen); + +/* Applies the 512-bit Haraka permutation to in. */ +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka512_perm(unsigned char *out, const unsigned char *in); + +/* Implementation of Haraka-512 */ +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka512(unsigned char *out, const unsigned char *in); + +/* Implementation of Haraka-256 */ +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka256(unsigned char *out, const unsigned char *in); + +/* Implementation of Haraka-256 using sk.seed constants */ +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka256_sk(unsigned char *out, const unsigned char *in); + +#endif diff --git a/crypto_sign/sphincs-haraka-128s-simple/clean/hash.h b/crypto_sign/sphincs-haraka-128s-simple/clean/hash.h new file mode 100644 index 00000000..17afdf61 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128s-simple/clean/hash.h @@ -0,0 +1,22 @@ +#ifndef SPX_HASH_H +#define SPX_HASH_H + +#include + +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed); + +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen); + +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen); + +#endif diff --git a/crypto_sign/sphincs-haraka-128s-simple/clean/hash_haraka.c b/crypto_sign/sphincs-haraka-128s-simple/clean/hash_haraka.c new file mode 100644 index 00000000..b926eb3b --- /dev/null +++ b/crypto_sign/sphincs-haraka-128s-simple/clean/hash_haraka.c @@ -0,0 +1,86 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "utils.h" + +#include "haraka.h" + +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed) { + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_tweak_constants(pub_seed, sk_seed, SPX_N); +} + +/* + * Computes PRF(key, addr), given a secret key of SPX_N bytes and an address + */ +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]) { + unsigned char buf[SPX_ADDR_BYTES]; + /* Since SPX_N may be smaller than 32, we need a temporary buffer. */ + unsigned char outbuf[32]; + + (void)key; /* Suppress an 'unused parameter' warning. */ + + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_addr_to_bytes(buf, addr); + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka256_sk(outbuf, buf); + memcpy(out, outbuf, SPX_N); +} + +/** + * Computes the message-dependent randomness R, using a secret seed and an + * optional randomization value as well as the message. + */ +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen) { + uint8_t s_inc[65]; + + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka_S_inc_init(s_inc); + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka_S_inc_absorb(s_inc, sk_prf, SPX_N); + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka_S_inc_absorb(s_inc, optrand, SPX_N); + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka_S_inc_absorb(s_inc, m, mlen); + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka_S_inc_finalize(s_inc); + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka_S_inc_squeeze(R, SPX_N, s_inc); +} + +/** + * Computes the message hash using R, the public key, and the message. + * Outputs the message digest and the index of the leaf. The index is split in + * the tree index and the leaf index, for convenient copying to an address. + */ +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen) { +#define SPX_TREE_BITS (SPX_TREE_HEIGHT * (SPX_D - 1)) +#define SPX_TREE_BYTES ((SPX_TREE_BITS + 7) / 8) +#define SPX_LEAF_BITS SPX_TREE_HEIGHT +#define SPX_LEAF_BYTES ((SPX_LEAF_BITS + 7) / 8) +#define SPX_DGST_BYTES (SPX_FORS_MSG_BYTES + SPX_TREE_BYTES + SPX_LEAF_BYTES) + + unsigned char buf[SPX_DGST_BYTES]; + unsigned char *bufp = buf; + uint8_t s_inc[65]; + + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka_S_inc_init(s_inc); + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka_S_inc_absorb(s_inc, R, SPX_N); + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka_S_inc_absorb(s_inc, pk, SPX_PK_BYTES); + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka_S_inc_absorb(s_inc, m, mlen); + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka_S_inc_finalize(s_inc); + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka_S_inc_squeeze(buf, SPX_DGST_BYTES, s_inc); + + memcpy(digest, bufp, SPX_FORS_MSG_BYTES); + bufp += SPX_FORS_MSG_BYTES; + + *tree = PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_bytes_to_ull(bufp, SPX_TREE_BYTES); + *tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS); + bufp += SPX_TREE_BYTES; + + *leaf_idx = (uint32_t)PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_bytes_to_ull( + bufp, SPX_LEAF_BYTES); + *leaf_idx &= (~(uint32_t)0) >> (32 - SPX_LEAF_BITS); +} diff --git a/crypto_sign/sphincs-haraka-128s-simple/clean/params.h b/crypto_sign/sphincs-haraka-128s-simple/clean/params.h new file mode 100644 index 00000000..ac30fdea --- /dev/null +++ b/crypto_sign/sphincs-haraka-128s-simple/clean/params.h @@ -0,0 +1,53 @@ +#ifndef SPX_PARAMS_H +#define SPX_PARAMS_H + +/* Hash output length in bytes. */ +#define SPX_N 16 +/* Height of the hypertree. */ +#define SPX_FULL_HEIGHT 64 +/* Number of subtree layer. */ +#define SPX_D 8 +/* FORS tree dimensions. */ +#define SPX_FORS_HEIGHT 15 +#define SPX_FORS_TREES 10 +/* Winternitz parameter, */ +#define SPX_WOTS_W 16 + +/* The hash function is defined by linking a different hash.c file, as opposed + to setting a #define constant. */ + +/* For clarity */ +#define SPX_ADDR_BYTES 32 + +/* WOTS parameters. */ +#define SPX_WOTS_LOGW 4 + +#define SPX_WOTS_LEN1 (8 * SPX_N / SPX_WOTS_LOGW) + +/* SPX_WOTS_LEN2 is floor(log(len_1 * (w - 1)) / log(w)) + 1; we precompute */ +#define SPX_WOTS_LEN2 3 + +#define SPX_WOTS_LEN (SPX_WOTS_LEN1 + SPX_WOTS_LEN2) +#define SPX_WOTS_BYTES (SPX_WOTS_LEN * SPX_N) +#define SPX_WOTS_PK_BYTES SPX_WOTS_BYTES + +/* Subtree size. */ +#define SPX_TREE_HEIGHT (SPX_FULL_HEIGHT / SPX_D) + +/* FORS parameters. */ +#define SPX_FORS_MSG_BYTES ((SPX_FORS_HEIGHT * SPX_FORS_TREES + 7) / 8) +#define SPX_FORS_BYTES ((SPX_FORS_HEIGHT + 1) * SPX_FORS_TREES * SPX_N) +#define SPX_FORS_PK_BYTES SPX_N + +/* Resulting SPX sizes. */ +#define SPX_BYTES (SPX_N + SPX_FORS_BYTES + SPX_D * SPX_WOTS_BYTES +\ + SPX_FULL_HEIGHT * SPX_N) +#define SPX_PK_BYTES (2 * SPX_N) +#define SPX_SK_BYTES (2 * SPX_N + SPX_PK_BYTES) + +/* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ +#define SPX_OPTRAND_BYTES 32 + +#endif diff --git a/crypto_sign/sphincs-haraka-128s-simple/clean/sign.c b/crypto_sign/sphincs-haraka-128s-simple/clean/sign.c new file mode 100644 index 00000000..5a230816 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128s-simple/clean/sign.c @@ -0,0 +1,344 @@ +#include +#include +#include + +#include "address.h" +#include "api.h" +#include "fors.h" +#include "hash.h" +#include "params.h" +#include "randombytes.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +/** + * Computes the leaf at a given address. First generates the WOTS key pair, + * then computes leaf by hashing horizontally. + */ +static void wots_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t tree_addr[8]) { + unsigned char pk[SPX_WOTS_BYTES]; + uint32_t wots_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, addr_idx); + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_wots_gen_pk( + pk, sk_seed, pub_seed, wots_addr); + + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_thash_WOTS_LEN( + leaf, pk, pub_seed, wots_pk_addr); +} + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_crypto_sign_secretkeybytes(void) { + return PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_CRYPTO_SECRETKEYBYTES; +} + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_crypto_sign_publickeybytes(void) { + return PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES; +} + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_crypto_sign_bytes(void) { + return PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_CRYPTO_BYTES; +} + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_crypto_sign_seedbytes(void) { + return PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_CRYPTO_SEEDBYTES; +} + +/* + * Generates an SPX key pair given a seed of length + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed) { + /* We do not need the auth path in key generation, but it simplifies the + code to have just one treehash routine that computes both root and path + in one function. */ + unsigned char auth_path[SPX_TREE_HEIGHT * SPX_N]; + uint32_t top_tree_addr[8] = {0}; + + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_layer_addr( + top_tree_addr, SPX_D - 1); + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_type( + top_tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */ + memcpy(sk, seed, PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_CRYPTO_SEEDBYTES); + + memcpy(pk, sk + 2 * SPX_N, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_initialize_hash_function(pk, sk); + + /* Compute root node of the top-most subtree. */ + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_treehash_TREE_HEIGHT( + sk + 3 * SPX_N, auth_path, sk, sk + 2 * SPX_N, 0, 0, + wots_gen_leaf, top_tree_addr); + + memcpy(pk + SPX_N, sk + 3 * SPX_N, SPX_N); + + return 0; +} + +/* + * Generates an SPX key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk) { + unsigned char seed[PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_CRYPTO_SEEDBYTES]; + randombytes(seed, PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_CRYPTO_SEEDBYTES); + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_crypto_sign_seed_keypair( + pk, sk, seed); + + return 0; +} + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + const unsigned char *sk_seed = sk; + const unsigned char *sk_prf = sk + SPX_N; + const unsigned char *pk = sk + 2 * SPX_N; + const unsigned char *pub_seed = pk; + + unsigned char optrand[SPX_N]; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char root[SPX_N]; + uint32_t i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_initialize_hash_function( + pub_seed, sk_seed); + + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ + randombytes(optrand, SPX_N); + /* Compute the digest randomization value. */ + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_gen_message_random( + sig, sk_prf, optrand, m, mlen); + + /* Derive the message digest and leaf index from R, PK and M. */ + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Sign the message hash using FORS. */ + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_fors_sign( + sig, root, mhash, sk_seed, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Compute a WOTS signature. */ + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_wots_sign( + sig, root, sk_seed, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the authentication path for the used WOTS leaf. */ + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_treehash_TREE_HEIGHT( + root, sig, sk_seed, pub_seed, idx_leaf, 0, + wots_gen_leaf, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + *siglen = SPX_BYTES; + + return 0; +} + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk) { + const unsigned char *pub_seed = pk; + const unsigned char *pub_root = pk + SPX_N; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char wots_pk[SPX_WOTS_BYTES]; + unsigned char root[SPX_N]; + unsigned char leaf[SPX_N]; + unsigned int i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + if (siglen != SPX_BYTES) { + return -1; + } + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_initialize_hash_function( + pub_seed, NULL); + + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + /* Derive the message digest and leaf index from R || PK || M. */ + /* The additional SPX_N is a result of the hash domain separator. */ + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + /* Layer correctly defaults to 0, so no need to set_layer_addr */ + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_fors_pk_from_sig( + root, sig, mhash, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + /* For each subtree.. */ + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + + /* The WOTS public key is only correct if the signature was correct. */ + /* Initially, root is the FORS pk, but on subsequent iterations it is + the root of the subtree below the currently processed subtree. */ + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_wots_pk_from_sig( + wots_pk, sig, root, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the leaf node using the WOTS public key. */ + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_thash_WOTS_LEN( + leaf, wots_pk, pub_seed, wots_pk_addr); + + /* Compute the root node of this subtree. */ + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_compute_root( + root, leaf, idx_leaf, 0, sig, SPX_TREE_HEIGHT, + pub_seed, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + /* Check if the root node equals the root node in the public key. */ + if (memcmp(root, pub_root, SPX_N) != 0) { + return -1; + } + + return 0; +} + + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + size_t siglen; + + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_crypto_sign_signature( + sm, &siglen, m, mlen, sk); + + memmove(sm + SPX_BYTES, m, mlen); + *smlen = siglen + mlen; + + return 0; +} + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk) { + /* The API caller does not necessarily know what size a signature should be + but SPHINCS+ signatures are always exactly SPX_BYTES. */ + if (smlen < SPX_BYTES) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + *mlen = smlen - SPX_BYTES; + + if (PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_crypto_sign_verify( + sm, SPX_BYTES, sm + SPX_BYTES, *mlen, pk)) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + /* If verification was successful, move the message to the right place. */ + memmove(m, sm + SPX_BYTES, *mlen); + + return 0; +} diff --git a/crypto_sign/sphincs-haraka-128s-simple/clean/thash.h b/crypto_sign/sphincs-haraka-128s-simple/clean/thash.h new file mode 100644 index 00000000..6ee7086b --- /dev/null +++ b/crypto_sign/sphincs-haraka-128s-simple/clean/thash.h @@ -0,0 +1,22 @@ +#ifndef SPX_THASH_H +#define SPX_THASH_H + +#include + +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-haraka-128s-simple/clean/thash_haraka_simple.c b/crypto_sign/sphincs-haraka-128s-simple/clean/thash_haraka_simple.c new file mode 100644 index 00000000..a9ae35e2 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128s-simple/clean/thash_haraka_simple.c @@ -0,0 +1,78 @@ +#include +#include + +#include "address.h" +#include "params.h" +#include "thash.h" + +#include "haraka.h" + +/** + * Takes an array of inblocks concatenated arrays of SPX_N bytes. + */ +static void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_thash( + unsigned char *out, unsigned char *buf, + const unsigned char *in, unsigned int inblocks, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char outbuf[32]; + unsigned char buf_tmp[64]; + + (void)pub_seed; /* Suppress an 'unused parameter' warning. */ + + if (inblocks == 1) { + /* F function */ + /* Since SPX_N may be smaller than 32, we need a temporary buffer. */ + memset(buf_tmp, 0, 64); + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_addr_to_bytes(buf_tmp, addr); + memcpy(buf_tmp + SPX_ADDR_BYTES, in, SPX_N); + + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka512(outbuf, buf_tmp); + memcpy(out, outbuf, SPX_N); + } else { + /* All other tweakable hashes*/ + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_addr_to_bytes(buf, addr); + memcpy(buf + SPX_ADDR_BYTES, in, inblocks * SPX_N); + + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka_S( + out, SPX_N, buf, SPX_ADDR_BYTES + inblocks * SPX_N); + } +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_ADDR_BYTES + 1 * SPX_N]; + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_thash( + out, buf, in, 1, pub_seed, addr); +} + +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_ADDR_BYTES + 2 * SPX_N]; + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_thash( + out, buf, in, 2, pub_seed, addr); +} + +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_ADDR_BYTES + SPX_WOTS_LEN * SPX_N]; + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_thash( + out, buf, in, SPX_WOTS_LEN, pub_seed, addr); +} + +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_ADDR_BYTES + SPX_FORS_TREES * SPX_N]; + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_thash( + out, buf, in, SPX_FORS_TREES, pub_seed, addr); +} diff --git a/crypto_sign/sphincs-haraka-128s-simple/clean/utils.c b/crypto_sign/sphincs-haraka-128s-simple/clean/utils.c new file mode 100644 index 00000000..e65d91c3 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128s-simple/clean/utils.c @@ -0,0 +1,192 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in) { + + /* Iterate over out in decreasing order, for big-endianness. */ + for (size_t i = outlen; i > 0; i--) { + out[i - 1] = in & 0xff; + in = in >> 8; + } +} + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen) { + unsigned long long retval = 0; + + for (size_t i = 0; i < inlen; i++) { + retval |= ((unsigned long long)in[i]) << (8 * (inlen - 1 - i)); + } + return retval; +} + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + unsigned char buffer[2 * SPX_N]; + + /* If leaf_idx is odd (last bit = 1), current path element is a right child + and auth_path has to go left. Otherwise it is the other way around. */ + if (leaf_idx & 1) { + memcpy(buffer + SPX_N, leaf, SPX_N); + memcpy(buffer, auth_path, SPX_N); + } else { + memcpy(buffer, leaf, SPX_N); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + + for (i = 0; i < tree_height - 1; i++) { + leaf_idx >>= 1; + idx_offset >>= 1; + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_tree_height(addr, i + 1); + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + + /* Pick the right or left neighbor, depending on parity of the node. */ + if (leaf_idx & 1) { + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_thash_2( + buffer + SPX_N, buffer, pub_seed, addr); + memcpy(buffer, auth_path, SPX_N); + } else { + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_thash_2( + buffer, buffer, pub_seed, addr); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + } + + /* The last iteration is exceptional; we do not copy an auth_path node. */ + leaf_idx >>= 1; + idx_offset >>= 1; + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_tree_height(addr, tree_height); + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_thash_2( + root, buffer, pub_seed, addr); +} + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +static void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_treehash( + unsigned char *root, unsigned char *auth_path, + unsigned char *stack, unsigned int *heights, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned int offset = 0; + uint32_t idx; + uint32_t tree_idx; + + for (idx = 0; idx < (uint32_t)(1 << tree_height); idx++) { + /* Add the next leaf node to the stack. */ + gen_leaf(stack + offset * SPX_N, + sk_seed, pub_seed, idx + idx_offset, tree_addr); + offset++; + heights[offset - 1] = 0; + + /* If this is a node we need for the auth path.. */ + if ((leaf_idx ^ 0x1) == idx) { + memcpy(auth_path, stack + (offset - 1)*SPX_N, SPX_N); + } + + /* While the top-most nodes are of equal height.. */ + while (offset >= 2 && heights[offset - 1] == heights[offset - 2]) { + /* Compute index of the new node, in the next layer. */ + tree_idx = (idx >> (heights[offset - 1] + 1)); + + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_tree_height( + tree_addr, heights[offset - 1] + 1); + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_tree_index( + tree_addr, tree_idx + (idx_offset >> (heights[offset - 1] + 1))); + /* Hash the top-most nodes from the stack together. */ + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_thash_2( + stack + (offset - 2)*SPX_N, stack + (offset - 2)*SPX_N, + pub_seed, tree_addr); + offset--; + /* Note that the top-most node is now one layer higher. */ + heights[offset - 1]++; + + /* If this is a node we need for the auth path.. */ + if (((leaf_idx >> heights[offset - 1]) ^ 0x1) == tree_idx) { + memcpy(auth_path + heights[offset - 1]*SPX_N, + stack + (offset - 1)*SPX_N, SPX_N); + } + } + } + memcpy(root, stack, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_FORS_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_FORS_HEIGHT + 1]; + + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_FORS_HEIGHT, gen_leaf, tree_addr); +} + +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_TREE_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_TREE_HEIGHT + 1]; + + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_TREE_HEIGHT, gen_leaf, tree_addr); +} diff --git a/crypto_sign/sphincs-haraka-128s-simple/clean/utils.h b/crypto_sign/sphincs-haraka-128s-simple/clean/utils.h new file mode 100644 index 00000000..583c889e --- /dev/null +++ b/crypto_sign/sphincs-haraka-128s-simple/clean/utils.h @@ -0,0 +1,60 @@ +#ifndef SPX_UTILS_H +#define SPX_UTILS_H + +#include "params.h" +#include +#include + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in); + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen); + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-haraka-128s-simple/clean/wots.c b/crypto_sign/sphincs-haraka-128s-simple/clean/wots.c new file mode 100644 index 00000000..291e5e0c --- /dev/null +++ b/crypto_sign/sphincs-haraka-128s-simple/clean/wots.c @@ -0,0 +1,161 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +// TODO clarify address expectations, and make them more uniform. +// TODO i.e. do we expect types to be set already? +// TODO and do we expect modifications or copies? + +/** + * Computes the starting value for a chain, i.e. the secret key. + * Expects the address to be complete up to the chain address. + */ +static void wots_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t wots_addr[8]) { + /* Make sure that the hash address is actually zeroed. */ + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_hash_addr(wots_addr, 0); + + /* Generate sk element. */ + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_prf_addr(sk, sk_seed, wots_addr); +} + +/** + * Computes the chaining function. + * out and in have to be n-byte arrays. + * + * Interprets in as start-th value of the chain. + * addr has to contain the address of the chain. + */ +static void gen_chain(unsigned char *out, const unsigned char *in, + unsigned int start, unsigned int steps, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + /* Initialize out with the value at position 'start'. */ + memcpy(out, in, SPX_N); + + /* Iterate 'steps' calls to the hash function. */ + for (i = start; i < (start + steps) && i < SPX_WOTS_W; i++) { + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_hash_addr(addr, i); + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_thash_1( + out, out, pub_seed, addr); + } +} + +/** + * base_w algorithm as described in draft. + * Interprets an array of bytes as integers in base w. + * This only works when log_w is a divisor of 8. + */ +static void base_w(unsigned int *output, const size_t out_len, + const unsigned char *input) { + size_t in = 0; + size_t out = 0; + unsigned char total = 0; + unsigned int bits = 0; + size_t consumed; + + for (consumed = 0; consumed < out_len; consumed++) { + if (bits == 0) { + total = input[in]; + in++; + bits += 8; + } + bits -= SPX_WOTS_LOGW; + output[out] = (unsigned int)((total >> bits) & (SPX_WOTS_W - 1)); + out++; + } +} + +/* Computes the WOTS+ checksum over a message (in base_w). */ +static void wots_checksum(unsigned int *csum_base_w, + const unsigned int *msg_base_w) { + unsigned int csum = 0; + unsigned char csum_bytes[(SPX_WOTS_LEN2 * SPX_WOTS_LOGW + 7) / 8]; + unsigned int i; + + /* Compute checksum. */ + for (i = 0; i < SPX_WOTS_LEN1; i++) { + csum += SPX_WOTS_W - 1 - msg_base_w[i]; + } + + /* Convert checksum to base_w. */ + /* Make sure expected empty zero bits are the least significant bits. */ + csum = csum << (8 - ((SPX_WOTS_LEN2 * SPX_WOTS_LOGW) % 8)); + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_ull_to_bytes( + csum_bytes, sizeof(csum_bytes), csum); + base_w(csum_base_w, SPX_WOTS_LEN2, csum_bytes); +} + +/* Takes a message and derives the matching chain lengths. */ +static void chain_lengths(unsigned int *lengths, const unsigned char *msg) { + base_w(lengths, SPX_WOTS_LEN1, msg); + wots_checksum(lengths + SPX_WOTS_LEN1, lengths); +} + +/** + * WOTS key generation. Takes a 32 byte sk_seed, expands it to WOTS private key + * elements and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(pk + i * SPX_N, sk_seed, addr); + gen_chain(pk + i * SPX_N, pk + i * SPX_N, + 0, SPX_WOTS_W - 1, pub_seed, addr); + } +} + +/** + * Takes a n-byte message and the 32-byte sk_see to compute a signature 'sig'. + */ +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(sig + i * SPX_N, sk_seed, addr); + gen_chain(sig + i * SPX_N, sig + i * SPX_N, 0, lengths[i], pub_seed, addr); + } +} + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_set_chain_addr(addr, i); + gen_chain(pk + i * SPX_N, sig + i * SPX_N, + lengths[i], SPX_WOTS_W - 1 - lengths[i], pub_seed, addr); + } +} diff --git a/crypto_sign/sphincs-haraka-128s-simple/clean/wots.h b/crypto_sign/sphincs-haraka-128s-simple/clean/wots.h new file mode 100644 index 00000000..414cf1e2 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128s-simple/clean/wots.h @@ -0,0 +1,38 @@ +#ifndef SPX_WOTS_H +#define SPX_WOTS_H + +#include "params.h" +#include + +/** + * WOTS key generation. Takes a 32 byte seed for the private key, expands it to + * a full WOTS private key and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * Takes a n-byte message and the 32-byte seed for the private key to compute a + * signature that is placed at 'sig'. + */ +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]); + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-haraka-192f-robust/META.yml b/crypto_sign/sphincs-haraka-192f-robust/META.yml new file mode 100644 index 00000000..a893346f --- /dev/null +++ b/crypto_sign/sphincs-haraka-192f-robust/META.yml @@ -0,0 +1,27 @@ +name: SPHINCS+ +type: signature +claimed-nist-level: 3 +length-public-key: 48 +length-secret-key: 96 +length-signature: 35664 +testvectors-sha256: a88d3adbeb5c1805a90e506c93f5000b266d1227f1621c0f77adf75bdbe4ba02 +principal-submitter: Andreas Hülsing +auxiliary-submitters: + - Jean-Philippe Aumasson + - Daniel J. Bernstein, + - Christoph Dobraunig + - Maria Eichlseder + - Scott Fluhrer + - Stefan-Lukas Gazdag + - Panos Kampanakis + - Stefan Kölbl + - Tanja Lange + - Martin M. Lauridsen + - Florian Mendel + - Ruben Niederhagen + - Christian Rechberger + - Joost Rijneveld + - Peter Schwabe +implementations: + - name: clean + version: https://github.com/sphincs/sphincsplus/commit/77755c94d0bc744478044d6efbb888dc13156441 diff --git a/crypto_sign/sphincs-haraka-192f-robust/clean/LICENSE b/crypto_sign/sphincs-haraka-192f-robust/clean/LICENSE new file mode 100644 index 00000000..670154e3 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192f-robust/clean/LICENSE @@ -0,0 +1,116 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + diff --git a/crypto_sign/sphincs-haraka-192f-robust/clean/Makefile b/crypto_sign/sphincs-haraka-192f-robust/clean/Makefile new file mode 100644 index 00000000..aa67c3fa --- /dev/null +++ b/crypto_sign/sphincs-haraka-192f-robust/clean/Makefile @@ -0,0 +1,20 @@ +# This Makefile can be used with GNU Make or BSD Make + +LIB=libsphincs-haraka-192f-robust_clean.a + +HEADERS = params.h address.h wots.h utils.h fors.h api.h hash.h thash.h haraka.h +OBJECTS = address.o wots.o utils.o fors.o sign.o hash_haraka.o thash_haraka_robust.o haraka.o + +CFLAGS=-O3 -Wall -Wconversion -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) + +all: $(LIB) + +%.o: %.c $(HEADERS) + $(CC) $(CFLAGS) -c -o $@ $< + +$(LIB): $(OBJECTS) + $(AR) -r $@ $(OBJECTS) + +clean: + $(RM) $(OBJECTS) + $(RM) $(LIB) diff --git a/crypto_sign/sphincs-haraka-192f-robust/clean/Makefile.Microsoft_nmake b/crypto_sign/sphincs-haraka-192f-robust/clean/Makefile.Microsoft_nmake new file mode 100644 index 00000000..c2d2d985 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192f-robust/clean/Makefile.Microsoft_nmake @@ -0,0 +1,19 @@ +# This Makefile can be used with Microsoft Visual Studio's nmake using the command: +# nmake /f Makefile.Microsoft_nmake + +LIBRARY=libsphincs-haraka-192f-robust_clean.lib +OBJECTS=address.obj wots.obj utils.obj fors.obj sign.obj hash_haraka.obj thash_haraka_robust.obj haraka.obj + +CFLAGS=/nologo /I ..\..\..\common /W4 /WX + +all: $(LIBRARY) + +# Make sure objects are recompiled if headers change. +$(OBJECTS): *.h + +$(LIBRARY): $(OBJECTS) + LIB.EXE /NOLOGO /WX /OUT:$@ $** + +clean: + -DEL $(OBJECTS) + -DEL $(LIBRARY) diff --git a/crypto_sign/sphincs-haraka-192f-robust/clean/address.c b/crypto_sign/sphincs-haraka-192f-robust/clean/address.c new file mode 100644 index 00000000..7bd831e8 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192f-robust/clean/address.c @@ -0,0 +1,78 @@ +#include + +#include "address.h" +#include "params.h" +#include "utils.h" + +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]) { + int i; + + for (i = 0; i < 8; i++) { + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_ull_to_bytes( + bytes + i * 4, 4, addr[i]); + } +} + +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer) { + addr[0] = layer; +} + +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree) { + addr[1] = 0; + addr[2] = (uint32_t) (tree >> 32); + addr[3] = (uint32_t) tree; +} + +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_type( + uint32_t addr[8], uint32_t type) { + addr[4] = type; +} + +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; +} + +/* These functions are used for OTS addresses. */ + +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair) { + addr[5] = keypair; +} + +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; + out[5] = in[5]; +} + +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain) { + addr[6] = chain; +} + +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash) { + addr[7] = hash; +} + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height) { + addr[6] = tree_height; +} + +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index) { + addr[7] = tree_index; +} diff --git a/crypto_sign/sphincs-haraka-192f-robust/clean/address.h b/crypto_sign/sphincs-haraka-192f-robust/clean/address.h new file mode 100644 index 00000000..9f679132 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192f-robust/clean/address.h @@ -0,0 +1,50 @@ +#ifndef SPX_ADDRESS_H +#define SPX_ADDRESS_H + +#include + +#define SPX_ADDR_TYPE_WOTS 0 +#define SPX_ADDR_TYPE_WOTSPK 1 +#define SPX_ADDR_TYPE_HASHTREE 2 +#define SPX_ADDR_TYPE_FORSTREE 3 +#define SPX_ADDR_TYPE_FORSPK 4 + +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer); + +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree); + +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_type( + uint32_t addr[8], uint32_t type); + +/* Copies the layer and tree part of one address into the other */ +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for WOTS and FORS addresses. */ + +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair); + +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain); + +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash); + +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height); + +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index); + +#endif diff --git a/crypto_sign/sphincs-haraka-192f-robust/clean/api.h b/crypto_sign/sphincs-haraka-192f-robust/clean/api.h new file mode 100644 index 00000000..18800dd9 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192f-robust/clean/api.h @@ -0,0 +1,78 @@ +#ifndef PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_API_H +#define PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_API_H + +#include +#include + +#define PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_CRYPTO_ALGNAME "SPHINCS+" + +#define PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_CRYPTO_SECRETKEYBYTES 96 +#define PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES 48 +#define PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_CRYPTO_BYTES 35664 +#define PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_CRYPTO_SEEDBYTES 72 + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_crypto_sign_secretkeybytes(void); + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_crypto_sign_publickeybytes(void); + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_crypto_sign_bytes(void); + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_crypto_sign_seedbytes(void); + +/* + * Generates a SPHINCS+ key pair given a seed. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed); + +/* + * Generates a SPHINCS+ key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk); + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk); + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk); + +#endif diff --git a/crypto_sign/sphincs-haraka-192f-robust/clean/fors.c b/crypto_sign/sphincs-haraka-192f-robust/clean/fors.c new file mode 100644 index 00000000..6293338f --- /dev/null +++ b/crypto_sign/sphincs-haraka-192f-robust/clean/fors.c @@ -0,0 +1,164 @@ +#include +#include +#include + +#include "address.h" +#include "fors.h" +#include "hash.h" +#include "thash.h" +#include "utils.h" + +static void fors_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_prf_addr( + sk, sk_seed, fors_leaf_addr); +} + +static void fors_sk_to_leaf(unsigned char *leaf, const unsigned char *sk, + const unsigned char *pub_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_thash_1( + leaf, sk, pub_seed, fors_leaf_addr); +} + +static void fors_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t fors_tree_addr[8]) { + uint32_t fors_leaf_addr[8] = {0}; + + /* Only copy the parts that must be kept in fors_leaf_addr. */ + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_copy_keypair_addr( + fors_leaf_addr, fors_tree_addr); + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_type( + fors_leaf_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_tree_index( + fors_leaf_addr, addr_idx); + + fors_gen_sk(leaf, sk_seed, fors_leaf_addr); + fors_sk_to_leaf(leaf, leaf, pub_seed, fors_leaf_addr); +} + +/** + * Interprets m as SPX_FORS_HEIGHT-bit unsigned integers. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + * Assumes indices has space for SPX_FORS_TREES integers. + */ +static void message_to_indices(uint32_t *indices, const unsigned char *m) { + unsigned int i, j; + unsigned int offset = 0; + + for (i = 0; i < SPX_FORS_TREES; i++) { + indices[i] = 0; + for (j = 0; j < SPX_FORS_HEIGHT; j++) { + indices[i] ^= (((uint32_t)m[offset >> 3] >> (offset & 0x7)) & 0x1) << j; + offset++; + } + } +} + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Include the secret key part that produces the selected leaf node. */ + fors_gen_sk(sig, sk_seed, fors_tree_addr); + sig += SPX_N; + + /* Compute the authentication path for this leaf node. */ + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_treehash_FORS_HEIGHT( + roots + i * SPX_N, sig, sk_seed, pub_seed, + indices[i], idx_offset, fors_gen_leaf, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + unsigned char leaf[SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Derive the leaf from the included secret key part. */ + fors_sk_to_leaf(leaf, sig, pub_seed, fors_tree_addr); + sig += SPX_N; + + /* Derive the corresponding root node of this tree. */ + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_compute_root( + roots + i * SPX_N, leaf, indices[i], idx_offset, sig, + SPX_FORS_HEIGHT, pub_seed, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} diff --git a/crypto_sign/sphincs-haraka-192f-robust/clean/fors.h b/crypto_sign/sphincs-haraka-192f-robust/clean/fors.h new file mode 100644 index 00000000..74b26ae6 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192f-robust/clean/fors.h @@ -0,0 +1,30 @@ +#ifndef SPX_FORS_H +#define SPX_FORS_H + +#include + +#include "params.h" + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]); + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-haraka-192f-robust/clean/haraka.c b/crypto_sign/sphincs-haraka-192f-robust/clean/haraka.c new file mode 100644 index 00000000..25e3bbe0 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192f-robust/clean/haraka.c @@ -0,0 +1,965 @@ +/* + * Constant time implementation of the Haraka hash function. + * + * The bit-sliced implementation of the AES round functions are + * based on the AES implementation in BearSSL written + * by Thomas Pornin + */ + +#include +#include +#include + +#include "haraka.h" + +#define HARAKAS_RATE 32 + +static const uint64_t haraka512_rc64[10][8] = { + {0x24cf0ab9086f628b, 0xbdd6eeecc83b8382, 0xd96fb0306cdad0a7, 0xaace082ac8f95f89, 0x449d8e8870d7041f, 0x49bb2f80b2b3e2f8, 0x0569ae98d93bb258, 0x23dc9691e7d6a4b1}, + {0xd8ba10ede0fe5b6e, 0x7ecf7dbe424c7b8e, 0x6ea9949c6df62a31, 0xbf3f3c97ec9c313e, 0x241d03a196a1861e, 0xead3a51116e5a2ea, 0x77d479fcad9574e3, 0x18657a1af894b7a0}, + {0x10671e1a7f595522, 0xd9a00ff675d28c7b, 0x2f1edf0d2b9ba661, 0xb8ff58b8e3de45f9, 0xee29261da9865c02, 0xd1532aa4b50bdf43, 0x8bf858159b231bb1, 0xdf17439d22d4f599}, + {0xdd4b2f0870b918c0, 0x757a81f3b39b1bb6, 0x7a5c556898952e3f, 0x7dd70a16d915d87a, 0x3ae61971982b8301, 0xc3ab319e030412be, 0x17c0033ac094a8cb, 0x5a0630fc1a8dc4ef}, + {0x17708988c1632f73, 0xf92ddae090b44f4f, 0x11ac0285c43aa314, 0x509059941936b8ba, 0xd03e152fa2ce9b69, 0x3fbcbcb63a32998b, 0x6204696d692254f7, 0x915542ed93ec59b4}, + {0xf4ed94aa8879236e, 0xff6cb41cd38e03c0, 0x069b38602368aeab, 0x669495b820f0ddba, 0xf42013b1b8bf9e3d, 0xcf935efe6439734d, 0xbc1dcf42ca29e3f8, 0x7e6d3ed29f78ad67}, + {0xf3b0f6837ffcddaa, 0x3a76faef934ddf41, 0xcec7ae583a9c8e35, 0xe4dd18c68f0260af, 0x2c0e5df1ad398eaa, 0x478df5236ae22e8c, 0xfb944c46fe865f39, 0xaa48f82f028132ba}, + {0x231b9ae2b76aca77, 0x292a76a712db0b40, 0x5850625dc8134491, 0x73137dd469810fb5, 0x8a12a6a202a474fd, 0xd36fd9daa78bdb80, 0xb34c5e733505706f, 0xbaf1cdca818d9d96}, + {0x2e99781335e8c641, 0xbddfe5cce47d560e, 0xf74e9bf32e5e040c, 0x1d7a709d65996be9, 0x670df36a9cf66cdd, 0xd05ef84a176a2875, 0x0f888e828cb1c44e, 0x1a79e9c9727b052c}, + {0x83497348628d84de, 0x2e9387d51f22a754, 0xb000068da2f852d6, 0x378c9e1190fd6fe5, 0x870027c316de7293, 0xe51a9d4462e047bb, 0x90ecf7f8c6251195, 0x655953bfbed90a9c}, +}; + +static uint64_t tweaked512_rc64[10][8]; +static uint32_t tweaked256_rc32[10][8]; +static uint32_t tweaked256_rc32_sseed[10][8]; + +static inline uint32_t br_dec32le(const unsigned char *src) { + return (uint32_t)src[0] + | ((uint32_t)src[1] << 8) + | ((uint32_t)src[2] << 16) + | ((uint32_t)src[3] << 24); +} + +static void br_range_dec32le(uint32_t *v, size_t num, const unsigned char *src) { + while (num-- > 0) { + *v ++ = br_dec32le(src); + src += 4; + } +} + +static inline void br_enc32le(unsigned char *dst, uint32_t x) { + dst[0] = (unsigned char)x; + dst[1] = (unsigned char)(x >> 8); + dst[2] = (unsigned char)(x >> 16); + dst[3] = (unsigned char)(x >> 24); +} + + +static void br_range_enc32le(unsigned char *dst, const uint32_t *v, size_t num) { + while (num-- > 0) { + br_enc32le(dst, *v ++); + dst += 4; + } +} + +static void br_aes_ct64_bitslice_Sbox(uint64_t *q) { + /* + * This S-box implementation is a straightforward translation of + * the circuit described by Boyar and Peralta in "A new + * combinational logic minimization technique with applications + * to cryptology" (https://eprint.iacr.org/2009/191.pdf). + * + * Note that variables x* (input) and s* (output) are numbered + * in "reverse" order (x0 is the high bit, x7 is the low bit). + */ + + uint64_t x0, x1, x2, x3, x4, x5, x6, x7; + uint64_t y1, y2, y3, y4, y5, y6, y7, y8, y9; + uint64_t y10, y11, y12, y13, y14, y15, y16, y17, y18, y19; + uint64_t y20, y21; + uint64_t z0, z1, z2, z3, z4, z5, z6, z7, z8, z9; + uint64_t z10, z11, z12, z13, z14, z15, z16, z17; + uint64_t t0, t1, t2, t3, t4, t5, t6, t7, t8, t9; + uint64_t t10, t11, t12, t13, t14, t15, t16, t17, t18, t19; + uint64_t t20, t21, t22, t23, t24, t25, t26, t27, t28, t29; + uint64_t t30, t31, t32, t33, t34, t35, t36, t37, t38, t39; + uint64_t t40, t41, t42, t43, t44, t45, t46, t47, t48, t49; + uint64_t t50, t51, t52, t53, t54, t55, t56, t57, t58, t59; + uint64_t t60, t61, t62, t63, t64, t65, t66, t67; + uint64_t s0, s1, s2, s3, s4, s5, s6, s7; + + x0 = q[7]; + x1 = q[6]; + x2 = q[5]; + x3 = q[4]; + x4 = q[3]; + x5 = q[2]; + x6 = q[1]; + x7 = q[0]; + + /* + * Top linear transformation. + */ + y14 = x3 ^ x5; + y13 = x0 ^ x6; + y9 = x0 ^ x3; + y8 = x0 ^ x5; + t0 = x1 ^ x2; + y1 = t0 ^ x7; + y4 = y1 ^ x3; + y12 = y13 ^ y14; + y2 = y1 ^ x0; + y5 = y1 ^ x6; + y3 = y5 ^ y8; + t1 = x4 ^ y12; + y15 = t1 ^ x5; + y20 = t1 ^ x1; + y6 = y15 ^ x7; + y10 = y15 ^ t0; + y11 = y20 ^ y9; + y7 = x7 ^ y11; + y17 = y10 ^ y11; + y19 = y10 ^ y8; + y16 = t0 ^ y11; + y21 = y13 ^ y16; + y18 = x0 ^ y16; + + /* + * Non-linear section. + */ + t2 = y12 & y15; + t3 = y3 & y6; + t4 = t3 ^ t2; + t5 = y4 & x7; + t6 = t5 ^ t2; + t7 = y13 & y16; + t8 = y5 & y1; + t9 = t8 ^ t7; + t10 = y2 & y7; + t11 = t10 ^ t7; + t12 = y9 & y11; + t13 = y14 & y17; + t14 = t13 ^ t12; + t15 = y8 & y10; + t16 = t15 ^ t12; + t17 = t4 ^ t14; + t18 = t6 ^ t16; + t19 = t9 ^ t14; + t20 = t11 ^ t16; + t21 = t17 ^ y20; + t22 = t18 ^ y19; + t23 = t19 ^ y21; + t24 = t20 ^ y18; + + t25 = t21 ^ t22; + t26 = t21 & t23; + t27 = t24 ^ t26; + t28 = t25 & t27; + t29 = t28 ^ t22; + t30 = t23 ^ t24; + t31 = t22 ^ t26; + t32 = t31 & t30; + t33 = t32 ^ t24; + t34 = t23 ^ t33; + t35 = t27 ^ t33; + t36 = t24 & t35; + t37 = t36 ^ t34; + t38 = t27 ^ t36; + t39 = t29 & t38; + t40 = t25 ^ t39; + + t41 = t40 ^ t37; + t42 = t29 ^ t33; + t43 = t29 ^ t40; + t44 = t33 ^ t37; + t45 = t42 ^ t41; + z0 = t44 & y15; + z1 = t37 & y6; + z2 = t33 & x7; + z3 = t43 & y16; + z4 = t40 & y1; + z5 = t29 & y7; + z6 = t42 & y11; + z7 = t45 & y17; + z8 = t41 & y10; + z9 = t44 & y12; + z10 = t37 & y3; + z11 = t33 & y4; + z12 = t43 & y13; + z13 = t40 & y5; + z14 = t29 & y2; + z15 = t42 & y9; + z16 = t45 & y14; + z17 = t41 & y8; + + /* + * Bottom linear transformation. + */ + t46 = z15 ^ z16; + t47 = z10 ^ z11; + t48 = z5 ^ z13; + t49 = z9 ^ z10; + t50 = z2 ^ z12; + t51 = z2 ^ z5; + t52 = z7 ^ z8; + t53 = z0 ^ z3; + t54 = z6 ^ z7; + t55 = z16 ^ z17; + t56 = z12 ^ t48; + t57 = t50 ^ t53; + t58 = z4 ^ t46; + t59 = z3 ^ t54; + t60 = t46 ^ t57; + t61 = z14 ^ t57; + t62 = t52 ^ t58; + t63 = t49 ^ t58; + t64 = z4 ^ t59; + t65 = t61 ^ t62; + t66 = z1 ^ t63; + s0 = t59 ^ t63; + s6 = t56 ^ ~t62; + s7 = t48 ^ ~t60; + t67 = t64 ^ t65; + s3 = t53 ^ t66; + s4 = t51 ^ t66; + s5 = t47 ^ t65; + s1 = t64 ^ ~s3; + s2 = t55 ^ ~t67; + + q[7] = s0; + q[6] = s1; + q[5] = s2; + q[4] = s3; + q[3] = s4; + q[2] = s5; + q[1] = s6; + q[0] = s7; +} + +static void br_aes_ct_bitslice_Sbox(uint32_t *q) { + /* + * This S-box implementation is a straightforward translation of + * the circuit described by Boyar and Peralta in "A new + * combinational logic minimization technique with applications + * to cryptology" (https://eprint.iacr.org/2009/191.pdf). + * + * Note that variables x* (input) and s* (output) are numbered + * in "reverse" order (x0 is the high bit, x7 is the low bit). + */ + + uint32_t x0, x1, x2, x3, x4, x5, x6, x7; + uint32_t y1, y2, y3, y4, y5, y6, y7, y8, y9; + uint32_t y10, y11, y12, y13, y14, y15, y16, y17, y18, y19; + uint32_t y20, y21; + uint32_t z0, z1, z2, z3, z4, z5, z6, z7, z8, z9; + uint32_t z10, z11, z12, z13, z14, z15, z16, z17; + uint32_t t0, t1, t2, t3, t4, t5, t6, t7, t8, t9; + uint32_t t10, t11, t12, t13, t14, t15, t16, t17, t18, t19; + uint32_t t20, t21, t22, t23, t24, t25, t26, t27, t28, t29; + uint32_t t30, t31, t32, t33, t34, t35, t36, t37, t38, t39; + uint32_t t40, t41, t42, t43, t44, t45, t46, t47, t48, t49; + uint32_t t50, t51, t52, t53, t54, t55, t56, t57, t58, t59; + uint32_t t60, t61, t62, t63, t64, t65, t66, t67; + uint32_t s0, s1, s2, s3, s4, s5, s6, s7; + + x0 = q[7]; + x1 = q[6]; + x2 = q[5]; + x3 = q[4]; + x4 = q[3]; + x5 = q[2]; + x6 = q[1]; + x7 = q[0]; + + /* + * Top linear transformation. + */ + y14 = x3 ^ x5; + y13 = x0 ^ x6; + y9 = x0 ^ x3; + y8 = x0 ^ x5; + t0 = x1 ^ x2; + y1 = t0 ^ x7; + y4 = y1 ^ x3; + y12 = y13 ^ y14; + y2 = y1 ^ x0; + y5 = y1 ^ x6; + y3 = y5 ^ y8; + t1 = x4 ^ y12; + y15 = t1 ^ x5; + y20 = t1 ^ x1; + y6 = y15 ^ x7; + y10 = y15 ^ t0; + y11 = y20 ^ y9; + y7 = x7 ^ y11; + y17 = y10 ^ y11; + y19 = y10 ^ y8; + y16 = t0 ^ y11; + y21 = y13 ^ y16; + y18 = x0 ^ y16; + + /* + * Non-linear section. + */ + t2 = y12 & y15; + t3 = y3 & y6; + t4 = t3 ^ t2; + t5 = y4 & x7; + t6 = t5 ^ t2; + t7 = y13 & y16; + t8 = y5 & y1; + t9 = t8 ^ t7; + t10 = y2 & y7; + t11 = t10 ^ t7; + t12 = y9 & y11; + t13 = y14 & y17; + t14 = t13 ^ t12; + t15 = y8 & y10; + t16 = t15 ^ t12; + t17 = t4 ^ t14; + t18 = t6 ^ t16; + t19 = t9 ^ t14; + t20 = t11 ^ t16; + t21 = t17 ^ y20; + t22 = t18 ^ y19; + t23 = t19 ^ y21; + t24 = t20 ^ y18; + + t25 = t21 ^ t22; + t26 = t21 & t23; + t27 = t24 ^ t26; + t28 = t25 & t27; + t29 = t28 ^ t22; + t30 = t23 ^ t24; + t31 = t22 ^ t26; + t32 = t31 & t30; + t33 = t32 ^ t24; + t34 = t23 ^ t33; + t35 = t27 ^ t33; + t36 = t24 & t35; + t37 = t36 ^ t34; + t38 = t27 ^ t36; + t39 = t29 & t38; + t40 = t25 ^ t39; + + t41 = t40 ^ t37; + t42 = t29 ^ t33; + t43 = t29 ^ t40; + t44 = t33 ^ t37; + t45 = t42 ^ t41; + z0 = t44 & y15; + z1 = t37 & y6; + z2 = t33 & x7; + z3 = t43 & y16; + z4 = t40 & y1; + z5 = t29 & y7; + z6 = t42 & y11; + z7 = t45 & y17; + z8 = t41 & y10; + z9 = t44 & y12; + z10 = t37 & y3; + z11 = t33 & y4; + z12 = t43 & y13; + z13 = t40 & y5; + z14 = t29 & y2; + z15 = t42 & y9; + z16 = t45 & y14; + z17 = t41 & y8; + + /* + * Bottom linear transformation. + */ + t46 = z15 ^ z16; + t47 = z10 ^ z11; + t48 = z5 ^ z13; + t49 = z9 ^ z10; + t50 = z2 ^ z12; + t51 = z2 ^ z5; + t52 = z7 ^ z8; + t53 = z0 ^ z3; + t54 = z6 ^ z7; + t55 = z16 ^ z17; + t56 = z12 ^ t48; + t57 = t50 ^ t53; + t58 = z4 ^ t46; + t59 = z3 ^ t54; + t60 = t46 ^ t57; + t61 = z14 ^ t57; + t62 = t52 ^ t58; + t63 = t49 ^ t58; + t64 = z4 ^ t59; + t65 = t61 ^ t62; + t66 = z1 ^ t63; + s0 = t59 ^ t63; + s6 = t56 ^ ~t62; + s7 = t48 ^ ~t60; + t67 = t64 ^ t65; + s3 = t53 ^ t66; + s4 = t51 ^ t66; + s5 = t47 ^ t65; + s1 = t64 ^ ~s3; + s2 = t55 ^ ~t67; + + q[7] = s0; + q[6] = s1; + q[5] = s2; + q[4] = s3; + q[3] = s4; + q[2] = s5; + q[1] = s6; + q[0] = s7; +} + +static void br_aes_ct_ortho(uint32_t *q) { +#define SWAPN_32(cl, ch, s, x, y) do { \ + uint32_t a, b; \ + a = (x); \ + b = (y); \ + (x) = (a & (uint32_t)(cl)) | ((b & (uint32_t)(cl)) << (s)); \ + (y) = ((a & (uint32_t)(ch)) >> (s)) | (b & (uint32_t)(ch)); \ + } while (0) + +#define SWAP2_32(x, y) SWAPN_32(0x55555555, 0xAAAAAAAA, 1, x, y) +#define SWAP4_32(x, y) SWAPN_32(0x33333333, 0xCCCCCCCC, 2, x, y) +#define SWAP8_32(x, y) SWAPN_32(0x0F0F0F0F, 0xF0F0F0F0, 4, x, y) + + SWAP2_32(q[0], q[1]); + SWAP2_32(q[2], q[3]); + SWAP2_32(q[4], q[5]); + SWAP2_32(q[6], q[7]); + + SWAP4_32(q[0], q[2]); + SWAP4_32(q[1], q[3]); + SWAP4_32(q[4], q[6]); + SWAP4_32(q[5], q[7]); + + SWAP8_32(q[0], q[4]); + SWAP8_32(q[1], q[5]); + SWAP8_32(q[2], q[6]); + SWAP8_32(q[3], q[7]); +} + +static inline void add_round_key32(uint32_t *q, const uint32_t *sk) { + q[0] ^= sk[0]; + q[1] ^= sk[1]; + q[2] ^= sk[2]; + q[3] ^= sk[3]; + q[4] ^= sk[4]; + q[5] ^= sk[5]; + q[6] ^= sk[6]; + q[7] ^= sk[7]; +} + +static inline void shift_rows32(uint32_t *q) { + int i; + + for (i = 0; i < 8; i++) { + uint32_t x; + + x = q[i]; + q[i] = (x & 0x000000FF) + | ((x & 0x0000FC00) >> 2) | ((x & 0x00000300) << 6) + | ((x & 0x00F00000) >> 4) | ((x & 0x000F0000) << 4) + | ((x & 0xC0000000) >> 6) | ((x & 0x3F000000) << 2); + } +} + +static inline uint32_t rotr16(uint32_t x) { + return (x << 16) | (x >> 16); +} + +static inline void mix_columns32(uint32_t *q) { + uint32_t q0, q1, q2, q3, q4, q5, q6, q7; + uint32_t r0, r1, r2, r3, r4, r5, r6, r7; + + q0 = q[0]; + q1 = q[1]; + q2 = q[2]; + q3 = q[3]; + q4 = q[4]; + q5 = q[5]; + q6 = q[6]; + q7 = q[7]; + r0 = (q0 >> 8) | (q0 << 24); + r1 = (q1 >> 8) | (q1 << 24); + r2 = (q2 >> 8) | (q2 << 24); + r3 = (q3 >> 8) | (q3 << 24); + r4 = (q4 >> 8) | (q4 << 24); + r5 = (q5 >> 8) | (q5 << 24); + r6 = (q6 >> 8) | (q6 << 24); + r7 = (q7 >> 8) | (q7 << 24); + + q[0] = q7 ^ r7 ^ r0 ^ rotr16(q0 ^ r0); + q[1] = q0 ^ r0 ^ q7 ^ r7 ^ r1 ^ rotr16(q1 ^ r1); + q[2] = q1 ^ r1 ^ r2 ^ rotr16(q2 ^ r2); + q[3] = q2 ^ r2 ^ q7 ^ r7 ^ r3 ^ rotr16(q3 ^ r3); + q[4] = q3 ^ r3 ^ q7 ^ r7 ^ r4 ^ rotr16(q4 ^ r4); + q[5] = q4 ^ r4 ^ r5 ^ rotr16(q5 ^ r5); + q[6] = q5 ^ r5 ^ r6 ^ rotr16(q6 ^ r6); + q[7] = q6 ^ r6 ^ r7 ^ rotr16(q7 ^ r7); +} + +static void br_aes_ct64_ortho(uint64_t *q) { +#define SWAPN(cl, ch, s, x, y) do { \ + uint64_t a, b; \ + a = (x); \ + b = (y); \ + (x) = (a & (uint64_t)(cl)) | ((b & (uint64_t)(cl)) << (s)); \ + (y) = ((a & (uint64_t)(ch)) >> (s)) | (b & (uint64_t)(ch)); \ + } while (0) + +#define SWAP2(x, y) SWAPN(0x5555555555555555, 0xAAAAAAAAAAAAAAAA, 1, x, y) +#define SWAP4(x, y) SWAPN(0x3333333333333333, 0xCCCCCCCCCCCCCCCC, 2, x, y) +#define SWAP8(x, y) SWAPN(0x0F0F0F0F0F0F0F0F, 0xF0F0F0F0F0F0F0F0, 4, x, y) + + SWAP2(q[0], q[1]); + SWAP2(q[2], q[3]); + SWAP2(q[4], q[5]); + SWAP2(q[6], q[7]); + + SWAP4(q[0], q[2]); + SWAP4(q[1], q[3]); + SWAP4(q[4], q[6]); + SWAP4(q[5], q[7]); + + SWAP8(q[0], q[4]); + SWAP8(q[1], q[5]); + SWAP8(q[2], q[6]); + SWAP8(q[3], q[7]); +} + + +static void br_aes_ct64_interleave_in(uint64_t *q0, uint64_t *q1, const uint32_t *w) { + uint64_t x0, x1, x2, x3; + + x0 = w[0]; + x1 = w[1]; + x2 = w[2]; + x3 = w[3]; + x0 |= (x0 << 16); + x1 |= (x1 << 16); + x2 |= (x2 << 16); + x3 |= (x3 << 16); + x0 &= (uint64_t)0x0000FFFF0000FFFF; + x1 &= (uint64_t)0x0000FFFF0000FFFF; + x2 &= (uint64_t)0x0000FFFF0000FFFF; + x3 &= (uint64_t)0x0000FFFF0000FFFF; + x0 |= (x0 << 8); + x1 |= (x1 << 8); + x2 |= (x2 << 8); + x3 |= (x3 << 8); + x0 &= (uint64_t)0x00FF00FF00FF00FF; + x1 &= (uint64_t)0x00FF00FF00FF00FF; + x2 &= (uint64_t)0x00FF00FF00FF00FF; + x3 &= (uint64_t)0x00FF00FF00FF00FF; + *q0 = x0 | (x2 << 8); + *q1 = x1 | (x3 << 8); +} + + +static void br_aes_ct64_interleave_out(uint32_t *w, uint64_t q0, uint64_t q1) { + uint64_t x0, x1, x2, x3; + + x0 = q0 & (uint64_t)0x00FF00FF00FF00FF; + x1 = q1 & (uint64_t)0x00FF00FF00FF00FF; + x2 = (q0 >> 8) & (uint64_t)0x00FF00FF00FF00FF; + x3 = (q1 >> 8) & (uint64_t)0x00FF00FF00FF00FF; + x0 |= (x0 >> 8); + x1 |= (x1 >> 8); + x2 |= (x2 >> 8); + x3 |= (x3 >> 8); + x0 &= (uint64_t)0x0000FFFF0000FFFF; + x1 &= (uint64_t)0x0000FFFF0000FFFF; + x2 &= (uint64_t)0x0000FFFF0000FFFF; + x3 &= (uint64_t)0x0000FFFF0000FFFF; + w[0] = (uint32_t)x0 | (uint32_t)(x0 >> 16); + w[1] = (uint32_t)x1 | (uint32_t)(x1 >> 16); + w[2] = (uint32_t)x2 | (uint32_t)(x2 >> 16); + w[3] = (uint32_t)x3 | (uint32_t)(x3 >> 16); +} + +static inline void add_round_key(uint64_t *q, const uint64_t *sk) { + q[0] ^= sk[0]; + q[1] ^= sk[1]; + q[2] ^= sk[2]; + q[3] ^= sk[3]; + q[4] ^= sk[4]; + q[5] ^= sk[5]; + q[6] ^= sk[6]; + q[7] ^= sk[7]; +} + +static inline void shift_rows(uint64_t *q) { + int i; + + for (i = 0; i < 8; i++) { + uint64_t x; + + x = q[i]; + q[i] = (x & (uint64_t)0x000000000000FFFF) + | ((x & (uint64_t)0x00000000FFF00000) >> 4) + | ((x & (uint64_t)0x00000000000F0000) << 12) + | ((x & (uint64_t)0x0000FF0000000000) >> 8) + | ((x & (uint64_t)0x000000FF00000000) << 8) + | ((x & (uint64_t)0xF000000000000000) >> 12) + | ((x & (uint64_t)0x0FFF000000000000) << 4); + } +} + +static inline uint64_t rotr32(uint64_t x) { + return (x << 32) | (x >> 32); +} + +static inline void mix_columns(uint64_t *q) { + uint64_t q0, q1, q2, q3, q4, q5, q6, q7; + uint64_t r0, r1, r2, r3, r4, r5, r6, r7; + + q0 = q[0]; + q1 = q[1]; + q2 = q[2]; + q3 = q[3]; + q4 = q[4]; + q5 = q[5]; + q6 = q[6]; + q7 = q[7]; + r0 = (q0 >> 16) | (q0 << 48); + r1 = (q1 >> 16) | (q1 << 48); + r2 = (q2 >> 16) | (q2 << 48); + r3 = (q3 >> 16) | (q3 << 48); + r4 = (q4 >> 16) | (q4 << 48); + r5 = (q5 >> 16) | (q5 << 48); + r6 = (q6 >> 16) | (q6 << 48); + r7 = (q7 >> 16) | (q7 << 48); + + q[0] = q7 ^ r7 ^ r0 ^ rotr32(q0 ^ r0); + q[1] = q0 ^ r0 ^ q7 ^ r7 ^ r1 ^ rotr32(q1 ^ r1); + q[2] = q1 ^ r1 ^ r2 ^ rotr32(q2 ^ r2); + q[3] = q2 ^ r2 ^ q7 ^ r7 ^ r3 ^ rotr32(q3 ^ r3); + q[4] = q3 ^ r3 ^ q7 ^ r7 ^ r4 ^ rotr32(q4 ^ r4); + q[5] = q4 ^ r4 ^ r5 ^ rotr32(q5 ^ r5); + q[6] = q5 ^ r5 ^ r6 ^ rotr32(q6 ^ r6); + q[7] = q6 ^ r6 ^ r7 ^ rotr32(q7 ^ r7); +} + +static void interleave_constant(uint64_t *out, const unsigned char *in) { + uint32_t tmp_32_constant[16]; + int i; + + br_range_dec32le(tmp_32_constant, 16, in); + for (i = 0; i < 4; i++) { + br_aes_ct64_interleave_in(&out[i], &out[i + 4], tmp_32_constant + (i << 2)); + } + br_aes_ct64_ortho(out); +} + +static void interleave_constant32(uint32_t *out, const unsigned char *in) { + int i; + for (i = 0; i < 4; i++) { + out[2 * i] = br_dec32le(in + 4 * i); + out[2 * i + 1] = br_dec32le(in + 4 * i + 16); + } + br_aes_ct_ortho(out); +} + +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_tweak_constants( + const unsigned char *pk_seed, const unsigned char *sk_seed, + unsigned long long seed_length) { + unsigned char buf[40 * 16]; + int i; + + /* Use the standard constants to generate tweaked ones. */ + memcpy((uint8_t *)tweaked512_rc64, (uint8_t *)haraka512_rc64, 40 * 16); + + /* Constants for sk.seed */ + if (sk_seed != NULL) { + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka_S( + buf, 40 * 16, sk_seed, seed_length); + + /* Interleave constants */ + for (i = 0; i < 10; i++) { + interleave_constant32(tweaked256_rc32_sseed[i], buf + 32 * i); + } + } + + /* Constants for pk.seed */ + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka_S( + buf, 40 * 16, pk_seed, seed_length); + for (i = 0; i < 10; i++) { + interleave_constant32(tweaked256_rc32[i], buf + 32 * i); + interleave_constant(tweaked512_rc64[i], buf + 64 * i); + } +} + +static void haraka_S_absorb(unsigned char *s, + const unsigned char *m, unsigned long long mlen, + unsigned char p) { + unsigned long long i; + unsigned char t[HARAKAS_RATE]; + + while (mlen >= HARAKAS_RATE) { + /* XOR block to state */ + for (i = 0; i < HARAKAS_RATE; ++i) { + s[i] ^= m[i]; + } + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka512_perm(s, s); + mlen -= HARAKAS_RATE; + m += HARAKAS_RATE; + } + + for (i = 0; i < HARAKAS_RATE; ++i) { + t[i] = 0; + } + for (i = 0; i < mlen; ++i) { + t[i] = m[i]; + } + t[i] = p; + t[HARAKAS_RATE - 1] |= 128; + for (i = 0; i < HARAKAS_RATE; ++i) { + s[i] ^= t[i]; + } +} + +static void haraka_S_squeezeblocks(unsigned char *h, unsigned long long nblocks, + unsigned char *s) { + while (nblocks > 0) { + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka512_perm(s, s); + memcpy(h, s, HARAKAS_RATE); + h += HARAKAS_RATE; + nblocks--; + } +} + +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka_S_inc_init(uint8_t *s_inc) { + size_t i; + + for (i = 0; i < 64; i++) { + s_inc[i] = 0; + } + s_inc[64] = 0; +} + +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka_S_inc_absorb(uint8_t *s_inc, const uint8_t *m, size_t mlen) { + size_t i; + + /* Recall that s_inc[64] is the non-absorbed bytes xored into the state */ + while (mlen + s_inc[64] >= HARAKAS_RATE) { + for (i = 0; i < (size_t)(HARAKAS_RATE - s_inc[64]); i++) { + /* Take the i'th byte from message + xor with the s_inc[64] + i'th byte of the state */ + s_inc[s_inc[64] + i] ^= m[i]; + } + mlen -= (size_t)(HARAKAS_RATE - s_inc[64]); + m += HARAKAS_RATE - s_inc[64]; + s_inc[64] = 0; + + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka512_perm(s_inc, s_inc); + } + + for (i = 0; i < mlen; i++) { + s_inc[s_inc[64] + i] ^= m[i]; + } + s_inc[64] = (uint8_t)(mlen + s_inc[64]); +} + +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka_S_inc_finalize(uint8_t *s_inc) { + /* After haraka_S_inc_absorb, we are guaranteed that s_inc[64] < HARAKAS_RATE, + so we can always use one more byte for p in the current state. */ + s_inc[s_inc[64]] ^= 0x1F; + s_inc[HARAKAS_RATE - 1] ^= 128; + s_inc[64] = 0; +} + +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka_S_inc_squeeze(uint8_t *out, size_t outlen, uint8_t *s_inc) { + uint8_t i; + + /* First consume any bytes we still have sitting around */ + for (i = 0; i < outlen && i < s_inc[64]; i++) { + /* There are s_inc[64] bytes left, so r - s_inc[64] is the first + available byte. We consume from there, i.e., up to r. */ + out[i] = s_inc[(HARAKAS_RATE - s_inc[64] + i)]; + } + out += i; + outlen -= i; + s_inc[64] = (uint8_t)(s_inc[64] - i); + + /* Then squeeze the remaining necessary blocks */ + while (outlen > 0) { + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka512_perm(s_inc, s_inc); + + for (i = 0; i < outlen && i < HARAKAS_RATE; i++) { + out[i] = s_inc[i]; + } + out += i; + outlen -= i; + s_inc[64] = (uint8_t)(HARAKAS_RATE - i); + } +} + +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka_S(unsigned char *out, unsigned long long outlen, const unsigned char *in, unsigned long long inlen) { + unsigned long long i; + unsigned char s[64]; + unsigned char d[32]; + + for (i = 0; i < 64; i++) { + s[i] = 0; + } + haraka_S_absorb(s, in, inlen, 0x1F); + + haraka_S_squeezeblocks(out, outlen / 32, s); + out += (outlen / 32) * 32; + + if (outlen % 32) { + haraka_S_squeezeblocks(d, 1, s); + for (i = 0; i < outlen % 32; i++) { + out[i] = d[i]; + } + } +} + +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka512_perm(unsigned char *out, const unsigned char *in) { + uint32_t w[16]; + uint64_t q[8], tmp_q; + unsigned int i, j; + + br_range_dec32le(w, 16, in); + for (i = 0; i < 4; i++) { + br_aes_ct64_interleave_in(&q[i], &q[i + 4], w + (i << 2)); + } + br_aes_ct64_ortho(q); + + /* AES rounds */ + for (i = 0; i < 5; i++) { + for (j = 0; j < 2; j++) { + br_aes_ct64_bitslice_Sbox(q); + shift_rows(q); + mix_columns(q); + add_round_key(q, tweaked512_rc64[2 * i + j]); + } + /* Mix states */ + for (j = 0; j < 8; j++) { + tmp_q = q[j]; + q[j] = (tmp_q & 0x0001000100010001) << 5 | + (tmp_q & 0x0002000200020002) << 12 | + (tmp_q & 0x0004000400040004) >> 1 | + (tmp_q & 0x0008000800080008) << 6 | + (tmp_q & 0x0020002000200020) << 9 | + (tmp_q & 0x0040004000400040) >> 4 | + (tmp_q & 0x0080008000800080) << 3 | + (tmp_q & 0x2100210021002100) >> 5 | + (tmp_q & 0x0210021002100210) << 2 | + (tmp_q & 0x0800080008000800) << 4 | + (tmp_q & 0x1000100010001000) >> 12 | + (tmp_q & 0x4000400040004000) >> 10 | + (tmp_q & 0x8400840084008400) >> 3; + } + } + + br_aes_ct64_ortho(q); + for (i = 0; i < 4; i ++) { + br_aes_ct64_interleave_out(w + (i << 2), q[i], q[i + 4]); + } + br_range_enc32le(out, w, 16); +} + +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka512(unsigned char *out, const unsigned char *in) { + int i; + + unsigned char buf[64]; + + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka512_perm(buf, in); + /* Feed-forward */ + for (i = 0; i < 64; i++) { + buf[i] = buf[i] ^ in[i]; + } + + /* Truncated */ + memcpy(out, buf + 8, 8); + memcpy(out + 8, buf + 24, 8); + memcpy(out + 16, buf + 32, 8); + memcpy(out + 24, buf + 48, 8); +} + + +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka256(unsigned char *out, const unsigned char *in) { + uint32_t q[8], tmp_q; + int i, j; + + for (i = 0; i < 4; i++) { + q[2 * i] = br_dec32le(in + 4 * i); + q[2 * i + 1] = br_dec32le(in + 4 * i + 16); + } + br_aes_ct_ortho(q); + + /* AES rounds */ + for (i = 0; i < 5; i++) { + for (j = 0; j < 2; j++) { + br_aes_ct_bitslice_Sbox(q); + shift_rows32(q); + mix_columns32(q); + add_round_key32(q, tweaked256_rc32[2 * i + j]); + } + + /* Mix states */ + for (j = 0; j < 8; j++) { + tmp_q = q[j]; + q[j] = (tmp_q & 0x81818181) | + (tmp_q & 0x02020202) << 1 | + (tmp_q & 0x04040404) << 2 | + (tmp_q & 0x08080808) << 3 | + (tmp_q & 0x10101010) >> 3 | + (tmp_q & 0x20202020) >> 2 | + (tmp_q & 0x40404040) >> 1; + } + } + + br_aes_ct_ortho(q); + for (i = 0; i < 4; i++) { + br_enc32le(out + 4 * i, q[2 * i]); + br_enc32le(out + 4 * i + 16, q[2 * i + 1]); + } + + for (i = 0; i < 32; i++) { + out[i] ^= in[i]; + } +} + +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka256_sk(unsigned char *out, const unsigned char *in) { + uint32_t q[8], tmp_q; + int i, j; + + for (i = 0; i < 4; i++) { + q[2 * i] = br_dec32le(in + 4 * i); + q[2 * i + 1] = br_dec32le(in + 4 * i + 16); + } + br_aes_ct_ortho(q); + + /* AES rounds */ + for (i = 0; i < 5; i++) { + for (j = 0; j < 2; j++) { + br_aes_ct_bitslice_Sbox(q); + shift_rows32(q); + mix_columns32(q); + add_round_key32(q, tweaked256_rc32_sseed[2 * i + j]); + } + + /* Mix states */ + for (j = 0; j < 8; j++) { + tmp_q = q[j]; + q[j] = (tmp_q & 0x81818181) | + (tmp_q & 0x02020202) << 1 | + (tmp_q & 0x04040404) << 2 | + (tmp_q & 0x08080808) << 3 | + (tmp_q & 0x10101010) >> 3 | + (tmp_q & 0x20202020) >> 2 | + (tmp_q & 0x40404040) >> 1; + } + } + + br_aes_ct_ortho(q); + for (i = 0; i < 4; i++) { + br_enc32le(out + 4 * i, q[2 * i]); + br_enc32le(out + 4 * i + 16, q[2 * i + 1]); + } + + for (i = 0; i < 32; i++) { + out[i] ^= in[i]; + } +} diff --git a/crypto_sign/sphincs-haraka-192f-robust/clean/haraka.h b/crypto_sign/sphincs-haraka-192f-robust/clean/haraka.h new file mode 100644 index 00000000..768856b5 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192f-robust/clean/haraka.h @@ -0,0 +1,30 @@ +#ifndef SPX_HARAKA_H +#define SPX_HARAKA_H + +/* Tweak constants with seed */ +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_tweak_constants( + const unsigned char *pk_seed, const unsigned char *sk_seed, + unsigned long long seed_length); + +/* Haraka Sponge */ +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka_S_inc_init(uint8_t *s_inc); +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka_S_inc_absorb(uint8_t *s_inc, const uint8_t *m, size_t mlen); +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka_S_inc_finalize(uint8_t *s_inc); +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka_S_inc_squeeze(uint8_t *out, size_t outlen, uint8_t *s_inc); +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka_S( + unsigned char *out, unsigned long long outlen, + const unsigned char *in, unsigned long long inlen); + +/* Applies the 512-bit Haraka permutation to in. */ +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka512_perm(unsigned char *out, const unsigned char *in); + +/* Implementation of Haraka-512 */ +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka512(unsigned char *out, const unsigned char *in); + +/* Implementation of Haraka-256 */ +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka256(unsigned char *out, const unsigned char *in); + +/* Implementation of Haraka-256 using sk.seed constants */ +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka256_sk(unsigned char *out, const unsigned char *in); + +#endif diff --git a/crypto_sign/sphincs-haraka-192f-robust/clean/hash.h b/crypto_sign/sphincs-haraka-192f-robust/clean/hash.h new file mode 100644 index 00000000..97023993 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192f-robust/clean/hash.h @@ -0,0 +1,22 @@ +#ifndef SPX_HASH_H +#define SPX_HASH_H + +#include + +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed); + +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen); + +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen); + +#endif diff --git a/crypto_sign/sphincs-haraka-192f-robust/clean/hash_haraka.c b/crypto_sign/sphincs-haraka-192f-robust/clean/hash_haraka.c new file mode 100644 index 00000000..ff28b376 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192f-robust/clean/hash_haraka.c @@ -0,0 +1,86 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "utils.h" + +#include "haraka.h" + +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed) { + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_tweak_constants(pub_seed, sk_seed, SPX_N); +} + +/* + * Computes PRF(key, addr), given a secret key of SPX_N bytes and an address + */ +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]) { + unsigned char buf[SPX_ADDR_BYTES]; + /* Since SPX_N may be smaller than 32, we need a temporary buffer. */ + unsigned char outbuf[32]; + + (void)key; /* Suppress an 'unused parameter' warning. */ + + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_addr_to_bytes(buf, addr); + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka256_sk(outbuf, buf); + memcpy(out, outbuf, SPX_N); +} + +/** + * Computes the message-dependent randomness R, using a secret seed and an + * optional randomization value as well as the message. + */ +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen) { + uint8_t s_inc[65]; + + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka_S_inc_init(s_inc); + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka_S_inc_absorb(s_inc, sk_prf, SPX_N); + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka_S_inc_absorb(s_inc, optrand, SPX_N); + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka_S_inc_absorb(s_inc, m, mlen); + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka_S_inc_finalize(s_inc); + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka_S_inc_squeeze(R, SPX_N, s_inc); +} + +/** + * Computes the message hash using R, the public key, and the message. + * Outputs the message digest and the index of the leaf. The index is split in + * the tree index and the leaf index, for convenient copying to an address. + */ +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen) { +#define SPX_TREE_BITS (SPX_TREE_HEIGHT * (SPX_D - 1)) +#define SPX_TREE_BYTES ((SPX_TREE_BITS + 7) / 8) +#define SPX_LEAF_BITS SPX_TREE_HEIGHT +#define SPX_LEAF_BYTES ((SPX_LEAF_BITS + 7) / 8) +#define SPX_DGST_BYTES (SPX_FORS_MSG_BYTES + SPX_TREE_BYTES + SPX_LEAF_BYTES) + + unsigned char buf[SPX_DGST_BYTES]; + unsigned char *bufp = buf; + uint8_t s_inc[65]; + + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka_S_inc_init(s_inc); + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka_S_inc_absorb(s_inc, R, SPX_N); + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka_S_inc_absorb(s_inc, pk, SPX_PK_BYTES); + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka_S_inc_absorb(s_inc, m, mlen); + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka_S_inc_finalize(s_inc); + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka_S_inc_squeeze(buf, SPX_DGST_BYTES, s_inc); + + memcpy(digest, bufp, SPX_FORS_MSG_BYTES); + bufp += SPX_FORS_MSG_BYTES; + + *tree = PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_bytes_to_ull(bufp, SPX_TREE_BYTES); + *tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS); + bufp += SPX_TREE_BYTES; + + *leaf_idx = (uint32_t)PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_bytes_to_ull( + bufp, SPX_LEAF_BYTES); + *leaf_idx &= (~(uint32_t)0) >> (32 - SPX_LEAF_BITS); +} diff --git a/crypto_sign/sphincs-haraka-192f-robust/clean/params.h b/crypto_sign/sphincs-haraka-192f-robust/clean/params.h new file mode 100644 index 00000000..898f177f --- /dev/null +++ b/crypto_sign/sphincs-haraka-192f-robust/clean/params.h @@ -0,0 +1,53 @@ +#ifndef SPX_PARAMS_H +#define SPX_PARAMS_H + +/* Hash output length in bytes. */ +#define SPX_N 24 +/* Height of the hypertree. */ +#define SPX_FULL_HEIGHT 66 +/* Number of subtree layer. */ +#define SPX_D 22 +/* FORS tree dimensions. */ +#define SPX_FORS_HEIGHT 8 +#define SPX_FORS_TREES 33 +/* Winternitz parameter, */ +#define SPX_WOTS_W 16 + +/* The hash function is defined by linking a different hash.c file, as opposed + to setting a #define constant. */ + +/* For clarity */ +#define SPX_ADDR_BYTES 32 + +/* WOTS parameters. */ +#define SPX_WOTS_LOGW 4 + +#define SPX_WOTS_LEN1 (8 * SPX_N / SPX_WOTS_LOGW) + +/* SPX_WOTS_LEN2 is floor(log(len_1 * (w - 1)) / log(w)) + 1; we precompute */ +#define SPX_WOTS_LEN2 3 + +#define SPX_WOTS_LEN (SPX_WOTS_LEN1 + SPX_WOTS_LEN2) +#define SPX_WOTS_BYTES (SPX_WOTS_LEN * SPX_N) +#define SPX_WOTS_PK_BYTES SPX_WOTS_BYTES + +/* Subtree size. */ +#define SPX_TREE_HEIGHT (SPX_FULL_HEIGHT / SPX_D) + +/* FORS parameters. */ +#define SPX_FORS_MSG_BYTES ((SPX_FORS_HEIGHT * SPX_FORS_TREES + 7) / 8) +#define SPX_FORS_BYTES ((SPX_FORS_HEIGHT + 1) * SPX_FORS_TREES * SPX_N) +#define SPX_FORS_PK_BYTES SPX_N + +/* Resulting SPX sizes. */ +#define SPX_BYTES (SPX_N + SPX_FORS_BYTES + SPX_D * SPX_WOTS_BYTES +\ + SPX_FULL_HEIGHT * SPX_N) +#define SPX_PK_BYTES (2 * SPX_N) +#define SPX_SK_BYTES (2 * SPX_N + SPX_PK_BYTES) + +/* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ +#define SPX_OPTRAND_BYTES 32 + +#endif diff --git a/crypto_sign/sphincs-haraka-192f-robust/clean/sign.c b/crypto_sign/sphincs-haraka-192f-robust/clean/sign.c new file mode 100644 index 00000000..f9f19227 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192f-robust/clean/sign.c @@ -0,0 +1,344 @@ +#include +#include +#include + +#include "address.h" +#include "api.h" +#include "fors.h" +#include "hash.h" +#include "params.h" +#include "randombytes.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +/** + * Computes the leaf at a given address. First generates the WOTS key pair, + * then computes leaf by hashing horizontally. + */ +static void wots_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t tree_addr[8]) { + unsigned char pk[SPX_WOTS_BYTES]; + uint32_t wots_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_keypair_addr( + wots_addr, addr_idx); + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_wots_gen_pk( + pk, sk_seed, pub_seed, wots_addr); + + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_thash_WOTS_LEN( + leaf, pk, pub_seed, wots_pk_addr); +} + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_crypto_sign_secretkeybytes(void) { + return PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_CRYPTO_SECRETKEYBYTES; +} + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_crypto_sign_publickeybytes(void) { + return PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES; +} + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_crypto_sign_bytes(void) { + return PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_CRYPTO_BYTES; +} + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_crypto_sign_seedbytes(void) { + return PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_CRYPTO_SEEDBYTES; +} + +/* + * Generates an SPX key pair given a seed of length + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed) { + /* We do not need the auth path in key generation, but it simplifies the + code to have just one treehash routine that computes both root and path + in one function. */ + unsigned char auth_path[SPX_TREE_HEIGHT * SPX_N]; + uint32_t top_tree_addr[8] = {0}; + + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_layer_addr( + top_tree_addr, SPX_D - 1); + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_type( + top_tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */ + memcpy(sk, seed, PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_CRYPTO_SEEDBYTES); + + memcpy(pk, sk + 2 * SPX_N, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_initialize_hash_function(pk, sk); + + /* Compute root node of the top-most subtree. */ + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_treehash_TREE_HEIGHT( + sk + 3 * SPX_N, auth_path, sk, sk + 2 * SPX_N, 0, 0, + wots_gen_leaf, top_tree_addr); + + memcpy(pk + SPX_N, sk + 3 * SPX_N, SPX_N); + + return 0; +} + +/* + * Generates an SPX key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk) { + unsigned char seed[PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_CRYPTO_SEEDBYTES]; + randombytes(seed, PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_CRYPTO_SEEDBYTES); + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_crypto_sign_seed_keypair( + pk, sk, seed); + + return 0; +} + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + const unsigned char *sk_seed = sk; + const unsigned char *sk_prf = sk + SPX_N; + const unsigned char *pk = sk + 2 * SPX_N; + const unsigned char *pub_seed = pk; + + unsigned char optrand[SPX_N]; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char root[SPX_N]; + uint32_t i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_initialize_hash_function( + pub_seed, sk_seed); + + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ + randombytes(optrand, SPX_N); + /* Compute the digest randomization value. */ + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_gen_message_random( + sig, sk_prf, optrand, m, mlen); + + /* Derive the message digest and leaf index from R, PK and M. */ + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Sign the message hash using FORS. */ + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_fors_sign( + sig, root, mhash, sk_seed, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Compute a WOTS signature. */ + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_wots_sign( + sig, root, sk_seed, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the authentication path for the used WOTS leaf. */ + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_treehash_TREE_HEIGHT( + root, sig, sk_seed, pub_seed, idx_leaf, 0, + wots_gen_leaf, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + *siglen = SPX_BYTES; + + return 0; +} + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk) { + const unsigned char *pub_seed = pk; + const unsigned char *pub_root = pk + SPX_N; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char wots_pk[SPX_WOTS_BYTES]; + unsigned char root[SPX_N]; + unsigned char leaf[SPX_N]; + unsigned int i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + if (siglen != SPX_BYTES) { + return -1; + } + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_initialize_hash_function( + pub_seed, NULL); + + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + /* Derive the message digest and leaf index from R || PK || M. */ + /* The additional SPX_N is a result of the hash domain separator. */ + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + /* Layer correctly defaults to 0, so no need to set_layer_addr */ + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_fors_pk_from_sig( + root, sig, mhash, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + /* For each subtree.. */ + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + + /* The WOTS public key is only correct if the signature was correct. */ + /* Initially, root is the FORS pk, but on subsequent iterations it is + the root of the subtree below the currently processed subtree. */ + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_wots_pk_from_sig( + wots_pk, sig, root, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the leaf node using the WOTS public key. */ + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_thash_WOTS_LEN( + leaf, wots_pk, pub_seed, wots_pk_addr); + + /* Compute the root node of this subtree. */ + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_compute_root( + root, leaf, idx_leaf, 0, sig, SPX_TREE_HEIGHT, + pub_seed, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + /* Check if the root node equals the root node in the public key. */ + if (memcmp(root, pub_root, SPX_N) != 0) { + return -1; + } + + return 0; +} + + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + size_t siglen; + + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_crypto_sign_signature( + sm, &siglen, m, mlen, sk); + + memmove(sm + SPX_BYTES, m, mlen); + *smlen = siglen + mlen; + + return 0; +} + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk) { + /* The API caller does not necessarily know what size a signature should be + but SPHINCS+ signatures are always exactly SPX_BYTES. */ + if (smlen < SPX_BYTES) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + *mlen = smlen - SPX_BYTES; + + if (PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_crypto_sign_verify( + sm, SPX_BYTES, sm + SPX_BYTES, *mlen, pk)) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + /* If verification was successful, move the message to the right place. */ + memmove(m, sm + SPX_BYTES, *mlen); + + return 0; +} diff --git a/crypto_sign/sphincs-haraka-192f-robust/clean/thash.h b/crypto_sign/sphincs-haraka-192f-robust/clean/thash.h new file mode 100644 index 00000000..6765ced5 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192f-robust/clean/thash.h @@ -0,0 +1,22 @@ +#ifndef SPX_THASH_H +#define SPX_THASH_H + +#include + +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-haraka-192f-robust/clean/thash_haraka_robust.c b/crypto_sign/sphincs-haraka-192f-robust/clean/thash_haraka_robust.c new file mode 100644 index 00000000..85c78083 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192f-robust/clean/thash_haraka_robust.c @@ -0,0 +1,88 @@ +#include +#include + +#include "address.h" +#include "params.h" +#include "thash.h" + +#include "haraka.h" + +/** + * Takes an array of inblocks concatenated arrays of SPX_N bytes. + */ +static void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_thash( + unsigned char *out, unsigned char *buf, + const unsigned char *in, unsigned int inblocks, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char *bitmask = buf + SPX_ADDR_BYTES; + unsigned char outbuf[32]; + unsigned char buf_tmp[64]; + unsigned int i; + + (void)pub_seed; /* Suppress an 'unused parameter' warning. */ + + if (inblocks == 1) { + /* F function */ + /* Since SPX_N may be smaller than 32, we need a temporary buffer. */ + memset(buf_tmp, 0, 64); + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_addr_to_bytes(buf_tmp, addr); + + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka256(outbuf, buf_tmp); + for (i = 0; i < inblocks * SPX_N; i++) { + buf_tmp[SPX_ADDR_BYTES + i] = in[i] ^ outbuf[i]; + } + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka512(outbuf, buf_tmp); + memcpy(out, outbuf, SPX_N); + } else { + /* All other tweakable hashes*/ + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_addr_to_bytes(buf, addr); + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka_S( + bitmask, inblocks * SPX_N, buf, SPX_ADDR_BYTES); + + for (i = 0; i < inblocks * SPX_N; i++) { + buf[SPX_ADDR_BYTES + i] = in[i] ^ bitmask[i]; + } + + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka_S( + out, SPX_N, buf, SPX_ADDR_BYTES + inblocks * SPX_N); + } +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_ADDR_BYTES + 1 * SPX_N]; + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_thash( + out, buf, in, 1, pub_seed, addr); +} + +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_ADDR_BYTES + 2 * SPX_N]; + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_thash( + out, buf, in, 2, pub_seed, addr); +} + +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_ADDR_BYTES + SPX_WOTS_LEN * SPX_N]; + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_thash( + out, buf, in, SPX_WOTS_LEN, pub_seed, addr); +} + +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_ADDR_BYTES + SPX_FORS_TREES * SPX_N]; + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_thash( + out, buf, in, SPX_FORS_TREES, pub_seed, addr); +} diff --git a/crypto_sign/sphincs-haraka-192f-robust/clean/utils.c b/crypto_sign/sphincs-haraka-192f-robust/clean/utils.c new file mode 100644 index 00000000..10e2e6a9 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192f-robust/clean/utils.c @@ -0,0 +1,192 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in) { + + /* Iterate over out in decreasing order, for big-endianness. */ + for (size_t i = outlen; i > 0; i--) { + out[i - 1] = in & 0xff; + in = in >> 8; + } +} + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen) { + unsigned long long retval = 0; + + for (size_t i = 0; i < inlen; i++) { + retval |= ((unsigned long long)in[i]) << (8 * (inlen - 1 - i)); + } + return retval; +} + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + unsigned char buffer[2 * SPX_N]; + + /* If leaf_idx is odd (last bit = 1), current path element is a right child + and auth_path has to go left. Otherwise it is the other way around. */ + if (leaf_idx & 1) { + memcpy(buffer + SPX_N, leaf, SPX_N); + memcpy(buffer, auth_path, SPX_N); + } else { + memcpy(buffer, leaf, SPX_N); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + + for (i = 0; i < tree_height - 1; i++) { + leaf_idx >>= 1; + idx_offset >>= 1; + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_tree_height(addr, i + 1); + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + + /* Pick the right or left neighbor, depending on parity of the node. */ + if (leaf_idx & 1) { + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_thash_2( + buffer + SPX_N, buffer, pub_seed, addr); + memcpy(buffer, auth_path, SPX_N); + } else { + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_thash_2( + buffer, buffer, pub_seed, addr); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + } + + /* The last iteration is exceptional; we do not copy an auth_path node. */ + leaf_idx >>= 1; + idx_offset >>= 1; + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_tree_height(addr, tree_height); + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_thash_2( + root, buffer, pub_seed, addr); +} + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +static void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_treehash( + unsigned char *root, unsigned char *auth_path, + unsigned char *stack, unsigned int *heights, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned int offset = 0; + uint32_t idx; + uint32_t tree_idx; + + for (idx = 0; idx < (uint32_t)(1 << tree_height); idx++) { + /* Add the next leaf node to the stack. */ + gen_leaf(stack + offset * SPX_N, + sk_seed, pub_seed, idx + idx_offset, tree_addr); + offset++; + heights[offset - 1] = 0; + + /* If this is a node we need for the auth path.. */ + if ((leaf_idx ^ 0x1) == idx) { + memcpy(auth_path, stack + (offset - 1)*SPX_N, SPX_N); + } + + /* While the top-most nodes are of equal height.. */ + while (offset >= 2 && heights[offset - 1] == heights[offset - 2]) { + /* Compute index of the new node, in the next layer. */ + tree_idx = (idx >> (heights[offset - 1] + 1)); + + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_tree_height( + tree_addr, heights[offset - 1] + 1); + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_tree_index( + tree_addr, tree_idx + (idx_offset >> (heights[offset - 1] + 1))); + /* Hash the top-most nodes from the stack together. */ + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_thash_2( + stack + (offset - 2)*SPX_N, stack + (offset - 2)*SPX_N, + pub_seed, tree_addr); + offset--; + /* Note that the top-most node is now one layer higher. */ + heights[offset - 1]++; + + /* If this is a node we need for the auth path.. */ + if (((leaf_idx >> heights[offset - 1]) ^ 0x1) == tree_idx) { + memcpy(auth_path + heights[offset - 1]*SPX_N, + stack + (offset - 1)*SPX_N, SPX_N); + } + } + } + memcpy(root, stack, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_FORS_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_FORS_HEIGHT + 1]; + + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_FORS_HEIGHT, gen_leaf, tree_addr); +} + +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_TREE_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_TREE_HEIGHT + 1]; + + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_TREE_HEIGHT, gen_leaf, tree_addr); +} diff --git a/crypto_sign/sphincs-haraka-192f-robust/clean/utils.h b/crypto_sign/sphincs-haraka-192f-robust/clean/utils.h new file mode 100644 index 00000000..004ae681 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192f-robust/clean/utils.h @@ -0,0 +1,60 @@ +#ifndef SPX_UTILS_H +#define SPX_UTILS_H + +#include "params.h" +#include +#include + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in); + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen); + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-haraka-192f-robust/clean/wots.c b/crypto_sign/sphincs-haraka-192f-robust/clean/wots.c new file mode 100644 index 00000000..3910eda0 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192f-robust/clean/wots.c @@ -0,0 +1,161 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +// TODO clarify address expectations, and make them more uniform. +// TODO i.e. do we expect types to be set already? +// TODO and do we expect modifications or copies? + +/** + * Computes the starting value for a chain, i.e. the secret key. + * Expects the address to be complete up to the chain address. + */ +static void wots_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t wots_addr[8]) { + /* Make sure that the hash address is actually zeroed. */ + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_hash_addr(wots_addr, 0); + + /* Generate sk element. */ + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_prf_addr(sk, sk_seed, wots_addr); +} + +/** + * Computes the chaining function. + * out and in have to be n-byte arrays. + * + * Interprets in as start-th value of the chain. + * addr has to contain the address of the chain. + */ +static void gen_chain(unsigned char *out, const unsigned char *in, + unsigned int start, unsigned int steps, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + /* Initialize out with the value at position 'start'. */ + memcpy(out, in, SPX_N); + + /* Iterate 'steps' calls to the hash function. */ + for (i = start; i < (start + steps) && i < SPX_WOTS_W; i++) { + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_hash_addr(addr, i); + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_thash_1( + out, out, pub_seed, addr); + } +} + +/** + * base_w algorithm as described in draft. + * Interprets an array of bytes as integers in base w. + * This only works when log_w is a divisor of 8. + */ +static void base_w(unsigned int *output, const size_t out_len, + const unsigned char *input) { + size_t in = 0; + size_t out = 0; + unsigned char total = 0; + unsigned int bits = 0; + size_t consumed; + + for (consumed = 0; consumed < out_len; consumed++) { + if (bits == 0) { + total = input[in]; + in++; + bits += 8; + } + bits -= SPX_WOTS_LOGW; + output[out] = (unsigned int)((total >> bits) & (SPX_WOTS_W - 1)); + out++; + } +} + +/* Computes the WOTS+ checksum over a message (in base_w). */ +static void wots_checksum(unsigned int *csum_base_w, + const unsigned int *msg_base_w) { + unsigned int csum = 0; + unsigned char csum_bytes[(SPX_WOTS_LEN2 * SPX_WOTS_LOGW + 7) / 8]; + unsigned int i; + + /* Compute checksum. */ + for (i = 0; i < SPX_WOTS_LEN1; i++) { + csum += SPX_WOTS_W - 1 - msg_base_w[i]; + } + + /* Convert checksum to base_w. */ + /* Make sure expected empty zero bits are the least significant bits. */ + csum = csum << (8 - ((SPX_WOTS_LEN2 * SPX_WOTS_LOGW) % 8)); + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_ull_to_bytes( + csum_bytes, sizeof(csum_bytes), csum); + base_w(csum_base_w, SPX_WOTS_LEN2, csum_bytes); +} + +/* Takes a message and derives the matching chain lengths. */ +static void chain_lengths(unsigned int *lengths, const unsigned char *msg) { + base_w(lengths, SPX_WOTS_LEN1, msg); + wots_checksum(lengths + SPX_WOTS_LEN1, lengths); +} + +/** + * WOTS key generation. Takes a 32 byte sk_seed, expands it to WOTS private key + * elements and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(pk + i * SPX_N, sk_seed, addr); + gen_chain(pk + i * SPX_N, pk + i * SPX_N, + 0, SPX_WOTS_W - 1, pub_seed, addr); + } +} + +/** + * Takes a n-byte message and the 32-byte sk_see to compute a signature 'sig'. + */ +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(sig + i * SPX_N, sk_seed, addr); + gen_chain(sig + i * SPX_N, sig + i * SPX_N, 0, lengths[i], pub_seed, addr); + } +} + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_set_chain_addr(addr, i); + gen_chain(pk + i * SPX_N, sig + i * SPX_N, + lengths[i], SPX_WOTS_W - 1 - lengths[i], pub_seed, addr); + } +} diff --git a/crypto_sign/sphincs-haraka-192f-robust/clean/wots.h b/crypto_sign/sphincs-haraka-192f-robust/clean/wots.h new file mode 100644 index 00000000..6a5e27f4 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192f-robust/clean/wots.h @@ -0,0 +1,38 @@ +#ifndef SPX_WOTS_H +#define SPX_WOTS_H + +#include "params.h" +#include + +/** + * WOTS key generation. Takes a 32 byte seed for the private key, expands it to + * a full WOTS private key and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * Takes a n-byte message and the 32-byte seed for the private key to compute a + * signature that is placed at 'sig'. + */ +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]); + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-haraka-192f-simple/META.yml b/crypto_sign/sphincs-haraka-192f-simple/META.yml new file mode 100644 index 00000000..c0473ea7 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192f-simple/META.yml @@ -0,0 +1,27 @@ +name: SPHINCS+ +type: signature +claimed-nist-level: 3 +length-public-key: 48 +length-secret-key: 96 +length-signature: 35664 +testvectors-sha256: d054d5394d578057e8264c5ef8a33627fcf194a25270a1dc6c2d7de86408876d +principal-submitter: Andreas Hülsing +auxiliary-submitters: + - Jean-Philippe Aumasson + - Daniel J. Bernstein, + - Christoph Dobraunig + - Maria Eichlseder + - Scott Fluhrer + - Stefan-Lukas Gazdag + - Panos Kampanakis + - Stefan Kölbl + - Tanja Lange + - Martin M. Lauridsen + - Florian Mendel + - Ruben Niederhagen + - Christian Rechberger + - Joost Rijneveld + - Peter Schwabe +implementations: + - name: clean + version: https://github.com/sphincs/sphincsplus/commit/77755c94d0bc744478044d6efbb888dc13156441 diff --git a/crypto_sign/sphincs-haraka-192f-simple/clean/LICENSE b/crypto_sign/sphincs-haraka-192f-simple/clean/LICENSE new file mode 100644 index 00000000..670154e3 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192f-simple/clean/LICENSE @@ -0,0 +1,116 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + diff --git a/crypto_sign/sphincs-haraka-192f-simple/clean/Makefile b/crypto_sign/sphincs-haraka-192f-simple/clean/Makefile new file mode 100644 index 00000000..b6c0e316 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192f-simple/clean/Makefile @@ -0,0 +1,20 @@ +# This Makefile can be used with GNU Make or BSD Make + +LIB=libsphincs-haraka-192f-simple_clean.a + +HEADERS = params.h address.h wots.h utils.h fors.h api.h hash.h thash.h haraka.h +OBJECTS = address.o wots.o utils.o fors.o sign.o hash_haraka.o thash_haraka_simple.o haraka.o + +CFLAGS=-O3 -Wall -Wconversion -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) + +all: $(LIB) + +%.o: %.c $(HEADERS) + $(CC) $(CFLAGS) -c -o $@ $< + +$(LIB): $(OBJECTS) + $(AR) -r $@ $(OBJECTS) + +clean: + $(RM) $(OBJECTS) + $(RM) $(LIB) diff --git a/crypto_sign/sphincs-haraka-192f-simple/clean/Makefile.Microsoft_nmake b/crypto_sign/sphincs-haraka-192f-simple/clean/Makefile.Microsoft_nmake new file mode 100644 index 00000000..41441823 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192f-simple/clean/Makefile.Microsoft_nmake @@ -0,0 +1,19 @@ +# This Makefile can be used with Microsoft Visual Studio's nmake using the command: +# nmake /f Makefile.Microsoft_nmake + +LIBRARY=libsphincs-haraka-192f-simple_clean.lib +OBJECTS=address.obj wots.obj utils.obj fors.obj sign.obj hash_haraka.obj thash_haraka_simple.obj haraka.obj + +CFLAGS=/nologo /I ..\..\..\common /W4 /WX + +all: $(LIBRARY) + +# Make sure objects are recompiled if headers change. +$(OBJECTS): *.h + +$(LIBRARY): $(OBJECTS) + LIB.EXE /NOLOGO /WX /OUT:$@ $** + +clean: + -DEL $(OBJECTS) + -DEL $(LIBRARY) diff --git a/crypto_sign/sphincs-haraka-192f-simple/clean/address.c b/crypto_sign/sphincs-haraka-192f-simple/clean/address.c new file mode 100644 index 00000000..cdee97b6 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192f-simple/clean/address.c @@ -0,0 +1,78 @@ +#include + +#include "address.h" +#include "params.h" +#include "utils.h" + +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]) { + int i; + + for (i = 0; i < 8; i++) { + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_ull_to_bytes( + bytes + i * 4, 4, addr[i]); + } +} + +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer) { + addr[0] = layer; +} + +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree) { + addr[1] = 0; + addr[2] = (uint32_t) (tree >> 32); + addr[3] = (uint32_t) tree; +} + +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_type( + uint32_t addr[8], uint32_t type) { + addr[4] = type; +} + +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; +} + +/* These functions are used for OTS addresses. */ + +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair) { + addr[5] = keypair; +} + +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; + out[5] = in[5]; +} + +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain) { + addr[6] = chain; +} + +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash) { + addr[7] = hash; +} + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height) { + addr[6] = tree_height; +} + +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index) { + addr[7] = tree_index; +} diff --git a/crypto_sign/sphincs-haraka-192f-simple/clean/address.h b/crypto_sign/sphincs-haraka-192f-simple/clean/address.h new file mode 100644 index 00000000..c9122690 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192f-simple/clean/address.h @@ -0,0 +1,50 @@ +#ifndef SPX_ADDRESS_H +#define SPX_ADDRESS_H + +#include + +#define SPX_ADDR_TYPE_WOTS 0 +#define SPX_ADDR_TYPE_WOTSPK 1 +#define SPX_ADDR_TYPE_HASHTREE 2 +#define SPX_ADDR_TYPE_FORSTREE 3 +#define SPX_ADDR_TYPE_FORSPK 4 + +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer); + +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree); + +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_type( + uint32_t addr[8], uint32_t type); + +/* Copies the layer and tree part of one address into the other */ +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for WOTS and FORS addresses. */ + +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair); + +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain); + +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash); + +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height); + +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index); + +#endif diff --git a/crypto_sign/sphincs-haraka-192f-simple/clean/api.h b/crypto_sign/sphincs-haraka-192f-simple/clean/api.h new file mode 100644 index 00000000..5cafbdfd --- /dev/null +++ b/crypto_sign/sphincs-haraka-192f-simple/clean/api.h @@ -0,0 +1,78 @@ +#ifndef PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_API_H +#define PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_API_H + +#include +#include + +#define PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_CRYPTO_ALGNAME "SPHINCS+" + +#define PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_CRYPTO_SECRETKEYBYTES 96 +#define PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES 48 +#define PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_CRYPTO_BYTES 35664 +#define PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_CRYPTO_SEEDBYTES 72 + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_crypto_sign_secretkeybytes(void); + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_crypto_sign_publickeybytes(void); + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_crypto_sign_bytes(void); + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_crypto_sign_seedbytes(void); + +/* + * Generates a SPHINCS+ key pair given a seed. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed); + +/* + * Generates a SPHINCS+ key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk); + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk); + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk); + +#endif diff --git a/crypto_sign/sphincs-haraka-192f-simple/clean/fors.c b/crypto_sign/sphincs-haraka-192f-simple/clean/fors.c new file mode 100644 index 00000000..8ed93917 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192f-simple/clean/fors.c @@ -0,0 +1,164 @@ +#include +#include +#include + +#include "address.h" +#include "fors.h" +#include "hash.h" +#include "thash.h" +#include "utils.h" + +static void fors_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_prf_addr( + sk, sk_seed, fors_leaf_addr); +} + +static void fors_sk_to_leaf(unsigned char *leaf, const unsigned char *sk, + const unsigned char *pub_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_thash_1( + leaf, sk, pub_seed, fors_leaf_addr); +} + +static void fors_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t fors_tree_addr[8]) { + uint32_t fors_leaf_addr[8] = {0}; + + /* Only copy the parts that must be kept in fors_leaf_addr. */ + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_copy_keypair_addr( + fors_leaf_addr, fors_tree_addr); + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_type( + fors_leaf_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_tree_index( + fors_leaf_addr, addr_idx); + + fors_gen_sk(leaf, sk_seed, fors_leaf_addr); + fors_sk_to_leaf(leaf, leaf, pub_seed, fors_leaf_addr); +} + +/** + * Interprets m as SPX_FORS_HEIGHT-bit unsigned integers. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + * Assumes indices has space for SPX_FORS_TREES integers. + */ +static void message_to_indices(uint32_t *indices, const unsigned char *m) { + unsigned int i, j; + unsigned int offset = 0; + + for (i = 0; i < SPX_FORS_TREES; i++) { + indices[i] = 0; + for (j = 0; j < SPX_FORS_HEIGHT; j++) { + indices[i] ^= (((uint32_t)m[offset >> 3] >> (offset & 0x7)) & 0x1) << j; + offset++; + } + } +} + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Include the secret key part that produces the selected leaf node. */ + fors_gen_sk(sig, sk_seed, fors_tree_addr); + sig += SPX_N; + + /* Compute the authentication path for this leaf node. */ + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_treehash_FORS_HEIGHT( + roots + i * SPX_N, sig, sk_seed, pub_seed, + indices[i], idx_offset, fors_gen_leaf, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + unsigned char leaf[SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Derive the leaf from the included secret key part. */ + fors_sk_to_leaf(leaf, sig, pub_seed, fors_tree_addr); + sig += SPX_N; + + /* Derive the corresponding root node of this tree. */ + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_compute_root( + roots + i * SPX_N, leaf, indices[i], idx_offset, sig, + SPX_FORS_HEIGHT, pub_seed, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} diff --git a/crypto_sign/sphincs-haraka-192f-simple/clean/fors.h b/crypto_sign/sphincs-haraka-192f-simple/clean/fors.h new file mode 100644 index 00000000..3270a129 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192f-simple/clean/fors.h @@ -0,0 +1,30 @@ +#ifndef SPX_FORS_H +#define SPX_FORS_H + +#include + +#include "params.h" + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]); + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-haraka-192f-simple/clean/haraka.c b/crypto_sign/sphincs-haraka-192f-simple/clean/haraka.c new file mode 100644 index 00000000..c792f83e --- /dev/null +++ b/crypto_sign/sphincs-haraka-192f-simple/clean/haraka.c @@ -0,0 +1,965 @@ +/* + * Constant time implementation of the Haraka hash function. + * + * The bit-sliced implementation of the AES round functions are + * based on the AES implementation in BearSSL written + * by Thomas Pornin + */ + +#include +#include +#include + +#include "haraka.h" + +#define HARAKAS_RATE 32 + +static const uint64_t haraka512_rc64[10][8] = { + {0x24cf0ab9086f628b, 0xbdd6eeecc83b8382, 0xd96fb0306cdad0a7, 0xaace082ac8f95f89, 0x449d8e8870d7041f, 0x49bb2f80b2b3e2f8, 0x0569ae98d93bb258, 0x23dc9691e7d6a4b1}, + {0xd8ba10ede0fe5b6e, 0x7ecf7dbe424c7b8e, 0x6ea9949c6df62a31, 0xbf3f3c97ec9c313e, 0x241d03a196a1861e, 0xead3a51116e5a2ea, 0x77d479fcad9574e3, 0x18657a1af894b7a0}, + {0x10671e1a7f595522, 0xd9a00ff675d28c7b, 0x2f1edf0d2b9ba661, 0xb8ff58b8e3de45f9, 0xee29261da9865c02, 0xd1532aa4b50bdf43, 0x8bf858159b231bb1, 0xdf17439d22d4f599}, + {0xdd4b2f0870b918c0, 0x757a81f3b39b1bb6, 0x7a5c556898952e3f, 0x7dd70a16d915d87a, 0x3ae61971982b8301, 0xc3ab319e030412be, 0x17c0033ac094a8cb, 0x5a0630fc1a8dc4ef}, + {0x17708988c1632f73, 0xf92ddae090b44f4f, 0x11ac0285c43aa314, 0x509059941936b8ba, 0xd03e152fa2ce9b69, 0x3fbcbcb63a32998b, 0x6204696d692254f7, 0x915542ed93ec59b4}, + {0xf4ed94aa8879236e, 0xff6cb41cd38e03c0, 0x069b38602368aeab, 0x669495b820f0ddba, 0xf42013b1b8bf9e3d, 0xcf935efe6439734d, 0xbc1dcf42ca29e3f8, 0x7e6d3ed29f78ad67}, + {0xf3b0f6837ffcddaa, 0x3a76faef934ddf41, 0xcec7ae583a9c8e35, 0xe4dd18c68f0260af, 0x2c0e5df1ad398eaa, 0x478df5236ae22e8c, 0xfb944c46fe865f39, 0xaa48f82f028132ba}, + {0x231b9ae2b76aca77, 0x292a76a712db0b40, 0x5850625dc8134491, 0x73137dd469810fb5, 0x8a12a6a202a474fd, 0xd36fd9daa78bdb80, 0xb34c5e733505706f, 0xbaf1cdca818d9d96}, + {0x2e99781335e8c641, 0xbddfe5cce47d560e, 0xf74e9bf32e5e040c, 0x1d7a709d65996be9, 0x670df36a9cf66cdd, 0xd05ef84a176a2875, 0x0f888e828cb1c44e, 0x1a79e9c9727b052c}, + {0x83497348628d84de, 0x2e9387d51f22a754, 0xb000068da2f852d6, 0x378c9e1190fd6fe5, 0x870027c316de7293, 0xe51a9d4462e047bb, 0x90ecf7f8c6251195, 0x655953bfbed90a9c}, +}; + +static uint64_t tweaked512_rc64[10][8]; +static uint32_t tweaked256_rc32[10][8]; +static uint32_t tweaked256_rc32_sseed[10][8]; + +static inline uint32_t br_dec32le(const unsigned char *src) { + return (uint32_t)src[0] + | ((uint32_t)src[1] << 8) + | ((uint32_t)src[2] << 16) + | ((uint32_t)src[3] << 24); +} + +static void br_range_dec32le(uint32_t *v, size_t num, const unsigned char *src) { + while (num-- > 0) { + *v ++ = br_dec32le(src); + src += 4; + } +} + +static inline void br_enc32le(unsigned char *dst, uint32_t x) { + dst[0] = (unsigned char)x; + dst[1] = (unsigned char)(x >> 8); + dst[2] = (unsigned char)(x >> 16); + dst[3] = (unsigned char)(x >> 24); +} + + +static void br_range_enc32le(unsigned char *dst, const uint32_t *v, size_t num) { + while (num-- > 0) { + br_enc32le(dst, *v ++); + dst += 4; + } +} + +static void br_aes_ct64_bitslice_Sbox(uint64_t *q) { + /* + * This S-box implementation is a straightforward translation of + * the circuit described by Boyar and Peralta in "A new + * combinational logic minimization technique with applications + * to cryptology" (https://eprint.iacr.org/2009/191.pdf). + * + * Note that variables x* (input) and s* (output) are numbered + * in "reverse" order (x0 is the high bit, x7 is the low bit). + */ + + uint64_t x0, x1, x2, x3, x4, x5, x6, x7; + uint64_t y1, y2, y3, y4, y5, y6, y7, y8, y9; + uint64_t y10, y11, y12, y13, y14, y15, y16, y17, y18, y19; + uint64_t y20, y21; + uint64_t z0, z1, z2, z3, z4, z5, z6, z7, z8, z9; + uint64_t z10, z11, z12, z13, z14, z15, z16, z17; + uint64_t t0, t1, t2, t3, t4, t5, t6, t7, t8, t9; + uint64_t t10, t11, t12, t13, t14, t15, t16, t17, t18, t19; + uint64_t t20, t21, t22, t23, t24, t25, t26, t27, t28, t29; + uint64_t t30, t31, t32, t33, t34, t35, t36, t37, t38, t39; + uint64_t t40, t41, t42, t43, t44, t45, t46, t47, t48, t49; + uint64_t t50, t51, t52, t53, t54, t55, t56, t57, t58, t59; + uint64_t t60, t61, t62, t63, t64, t65, t66, t67; + uint64_t s0, s1, s2, s3, s4, s5, s6, s7; + + x0 = q[7]; + x1 = q[6]; + x2 = q[5]; + x3 = q[4]; + x4 = q[3]; + x5 = q[2]; + x6 = q[1]; + x7 = q[0]; + + /* + * Top linear transformation. + */ + y14 = x3 ^ x5; + y13 = x0 ^ x6; + y9 = x0 ^ x3; + y8 = x0 ^ x5; + t0 = x1 ^ x2; + y1 = t0 ^ x7; + y4 = y1 ^ x3; + y12 = y13 ^ y14; + y2 = y1 ^ x0; + y5 = y1 ^ x6; + y3 = y5 ^ y8; + t1 = x4 ^ y12; + y15 = t1 ^ x5; + y20 = t1 ^ x1; + y6 = y15 ^ x7; + y10 = y15 ^ t0; + y11 = y20 ^ y9; + y7 = x7 ^ y11; + y17 = y10 ^ y11; + y19 = y10 ^ y8; + y16 = t0 ^ y11; + y21 = y13 ^ y16; + y18 = x0 ^ y16; + + /* + * Non-linear section. + */ + t2 = y12 & y15; + t3 = y3 & y6; + t4 = t3 ^ t2; + t5 = y4 & x7; + t6 = t5 ^ t2; + t7 = y13 & y16; + t8 = y5 & y1; + t9 = t8 ^ t7; + t10 = y2 & y7; + t11 = t10 ^ t7; + t12 = y9 & y11; + t13 = y14 & y17; + t14 = t13 ^ t12; + t15 = y8 & y10; + t16 = t15 ^ t12; + t17 = t4 ^ t14; + t18 = t6 ^ t16; + t19 = t9 ^ t14; + t20 = t11 ^ t16; + t21 = t17 ^ y20; + t22 = t18 ^ y19; + t23 = t19 ^ y21; + t24 = t20 ^ y18; + + t25 = t21 ^ t22; + t26 = t21 & t23; + t27 = t24 ^ t26; + t28 = t25 & t27; + t29 = t28 ^ t22; + t30 = t23 ^ t24; + t31 = t22 ^ t26; + t32 = t31 & t30; + t33 = t32 ^ t24; + t34 = t23 ^ t33; + t35 = t27 ^ t33; + t36 = t24 & t35; + t37 = t36 ^ t34; + t38 = t27 ^ t36; + t39 = t29 & t38; + t40 = t25 ^ t39; + + t41 = t40 ^ t37; + t42 = t29 ^ t33; + t43 = t29 ^ t40; + t44 = t33 ^ t37; + t45 = t42 ^ t41; + z0 = t44 & y15; + z1 = t37 & y6; + z2 = t33 & x7; + z3 = t43 & y16; + z4 = t40 & y1; + z5 = t29 & y7; + z6 = t42 & y11; + z7 = t45 & y17; + z8 = t41 & y10; + z9 = t44 & y12; + z10 = t37 & y3; + z11 = t33 & y4; + z12 = t43 & y13; + z13 = t40 & y5; + z14 = t29 & y2; + z15 = t42 & y9; + z16 = t45 & y14; + z17 = t41 & y8; + + /* + * Bottom linear transformation. + */ + t46 = z15 ^ z16; + t47 = z10 ^ z11; + t48 = z5 ^ z13; + t49 = z9 ^ z10; + t50 = z2 ^ z12; + t51 = z2 ^ z5; + t52 = z7 ^ z8; + t53 = z0 ^ z3; + t54 = z6 ^ z7; + t55 = z16 ^ z17; + t56 = z12 ^ t48; + t57 = t50 ^ t53; + t58 = z4 ^ t46; + t59 = z3 ^ t54; + t60 = t46 ^ t57; + t61 = z14 ^ t57; + t62 = t52 ^ t58; + t63 = t49 ^ t58; + t64 = z4 ^ t59; + t65 = t61 ^ t62; + t66 = z1 ^ t63; + s0 = t59 ^ t63; + s6 = t56 ^ ~t62; + s7 = t48 ^ ~t60; + t67 = t64 ^ t65; + s3 = t53 ^ t66; + s4 = t51 ^ t66; + s5 = t47 ^ t65; + s1 = t64 ^ ~s3; + s2 = t55 ^ ~t67; + + q[7] = s0; + q[6] = s1; + q[5] = s2; + q[4] = s3; + q[3] = s4; + q[2] = s5; + q[1] = s6; + q[0] = s7; +} + +static void br_aes_ct_bitslice_Sbox(uint32_t *q) { + /* + * This S-box implementation is a straightforward translation of + * the circuit described by Boyar and Peralta in "A new + * combinational logic minimization technique with applications + * to cryptology" (https://eprint.iacr.org/2009/191.pdf). + * + * Note that variables x* (input) and s* (output) are numbered + * in "reverse" order (x0 is the high bit, x7 is the low bit). + */ + + uint32_t x0, x1, x2, x3, x4, x5, x6, x7; + uint32_t y1, y2, y3, y4, y5, y6, y7, y8, y9; + uint32_t y10, y11, y12, y13, y14, y15, y16, y17, y18, y19; + uint32_t y20, y21; + uint32_t z0, z1, z2, z3, z4, z5, z6, z7, z8, z9; + uint32_t z10, z11, z12, z13, z14, z15, z16, z17; + uint32_t t0, t1, t2, t3, t4, t5, t6, t7, t8, t9; + uint32_t t10, t11, t12, t13, t14, t15, t16, t17, t18, t19; + uint32_t t20, t21, t22, t23, t24, t25, t26, t27, t28, t29; + uint32_t t30, t31, t32, t33, t34, t35, t36, t37, t38, t39; + uint32_t t40, t41, t42, t43, t44, t45, t46, t47, t48, t49; + uint32_t t50, t51, t52, t53, t54, t55, t56, t57, t58, t59; + uint32_t t60, t61, t62, t63, t64, t65, t66, t67; + uint32_t s0, s1, s2, s3, s4, s5, s6, s7; + + x0 = q[7]; + x1 = q[6]; + x2 = q[5]; + x3 = q[4]; + x4 = q[3]; + x5 = q[2]; + x6 = q[1]; + x7 = q[0]; + + /* + * Top linear transformation. + */ + y14 = x3 ^ x5; + y13 = x0 ^ x6; + y9 = x0 ^ x3; + y8 = x0 ^ x5; + t0 = x1 ^ x2; + y1 = t0 ^ x7; + y4 = y1 ^ x3; + y12 = y13 ^ y14; + y2 = y1 ^ x0; + y5 = y1 ^ x6; + y3 = y5 ^ y8; + t1 = x4 ^ y12; + y15 = t1 ^ x5; + y20 = t1 ^ x1; + y6 = y15 ^ x7; + y10 = y15 ^ t0; + y11 = y20 ^ y9; + y7 = x7 ^ y11; + y17 = y10 ^ y11; + y19 = y10 ^ y8; + y16 = t0 ^ y11; + y21 = y13 ^ y16; + y18 = x0 ^ y16; + + /* + * Non-linear section. + */ + t2 = y12 & y15; + t3 = y3 & y6; + t4 = t3 ^ t2; + t5 = y4 & x7; + t6 = t5 ^ t2; + t7 = y13 & y16; + t8 = y5 & y1; + t9 = t8 ^ t7; + t10 = y2 & y7; + t11 = t10 ^ t7; + t12 = y9 & y11; + t13 = y14 & y17; + t14 = t13 ^ t12; + t15 = y8 & y10; + t16 = t15 ^ t12; + t17 = t4 ^ t14; + t18 = t6 ^ t16; + t19 = t9 ^ t14; + t20 = t11 ^ t16; + t21 = t17 ^ y20; + t22 = t18 ^ y19; + t23 = t19 ^ y21; + t24 = t20 ^ y18; + + t25 = t21 ^ t22; + t26 = t21 & t23; + t27 = t24 ^ t26; + t28 = t25 & t27; + t29 = t28 ^ t22; + t30 = t23 ^ t24; + t31 = t22 ^ t26; + t32 = t31 & t30; + t33 = t32 ^ t24; + t34 = t23 ^ t33; + t35 = t27 ^ t33; + t36 = t24 & t35; + t37 = t36 ^ t34; + t38 = t27 ^ t36; + t39 = t29 & t38; + t40 = t25 ^ t39; + + t41 = t40 ^ t37; + t42 = t29 ^ t33; + t43 = t29 ^ t40; + t44 = t33 ^ t37; + t45 = t42 ^ t41; + z0 = t44 & y15; + z1 = t37 & y6; + z2 = t33 & x7; + z3 = t43 & y16; + z4 = t40 & y1; + z5 = t29 & y7; + z6 = t42 & y11; + z7 = t45 & y17; + z8 = t41 & y10; + z9 = t44 & y12; + z10 = t37 & y3; + z11 = t33 & y4; + z12 = t43 & y13; + z13 = t40 & y5; + z14 = t29 & y2; + z15 = t42 & y9; + z16 = t45 & y14; + z17 = t41 & y8; + + /* + * Bottom linear transformation. + */ + t46 = z15 ^ z16; + t47 = z10 ^ z11; + t48 = z5 ^ z13; + t49 = z9 ^ z10; + t50 = z2 ^ z12; + t51 = z2 ^ z5; + t52 = z7 ^ z8; + t53 = z0 ^ z3; + t54 = z6 ^ z7; + t55 = z16 ^ z17; + t56 = z12 ^ t48; + t57 = t50 ^ t53; + t58 = z4 ^ t46; + t59 = z3 ^ t54; + t60 = t46 ^ t57; + t61 = z14 ^ t57; + t62 = t52 ^ t58; + t63 = t49 ^ t58; + t64 = z4 ^ t59; + t65 = t61 ^ t62; + t66 = z1 ^ t63; + s0 = t59 ^ t63; + s6 = t56 ^ ~t62; + s7 = t48 ^ ~t60; + t67 = t64 ^ t65; + s3 = t53 ^ t66; + s4 = t51 ^ t66; + s5 = t47 ^ t65; + s1 = t64 ^ ~s3; + s2 = t55 ^ ~t67; + + q[7] = s0; + q[6] = s1; + q[5] = s2; + q[4] = s3; + q[3] = s4; + q[2] = s5; + q[1] = s6; + q[0] = s7; +} + +static void br_aes_ct_ortho(uint32_t *q) { +#define SWAPN_32(cl, ch, s, x, y) do { \ + uint32_t a, b; \ + a = (x); \ + b = (y); \ + (x) = (a & (uint32_t)(cl)) | ((b & (uint32_t)(cl)) << (s)); \ + (y) = ((a & (uint32_t)(ch)) >> (s)) | (b & (uint32_t)(ch)); \ + } while (0) + +#define SWAP2_32(x, y) SWAPN_32(0x55555555, 0xAAAAAAAA, 1, x, y) +#define SWAP4_32(x, y) SWAPN_32(0x33333333, 0xCCCCCCCC, 2, x, y) +#define SWAP8_32(x, y) SWAPN_32(0x0F0F0F0F, 0xF0F0F0F0, 4, x, y) + + SWAP2_32(q[0], q[1]); + SWAP2_32(q[2], q[3]); + SWAP2_32(q[4], q[5]); + SWAP2_32(q[6], q[7]); + + SWAP4_32(q[0], q[2]); + SWAP4_32(q[1], q[3]); + SWAP4_32(q[4], q[6]); + SWAP4_32(q[5], q[7]); + + SWAP8_32(q[0], q[4]); + SWAP8_32(q[1], q[5]); + SWAP8_32(q[2], q[6]); + SWAP8_32(q[3], q[7]); +} + +static inline void add_round_key32(uint32_t *q, const uint32_t *sk) { + q[0] ^= sk[0]; + q[1] ^= sk[1]; + q[2] ^= sk[2]; + q[3] ^= sk[3]; + q[4] ^= sk[4]; + q[5] ^= sk[5]; + q[6] ^= sk[6]; + q[7] ^= sk[7]; +} + +static inline void shift_rows32(uint32_t *q) { + int i; + + for (i = 0; i < 8; i++) { + uint32_t x; + + x = q[i]; + q[i] = (x & 0x000000FF) + | ((x & 0x0000FC00) >> 2) | ((x & 0x00000300) << 6) + | ((x & 0x00F00000) >> 4) | ((x & 0x000F0000) << 4) + | ((x & 0xC0000000) >> 6) | ((x & 0x3F000000) << 2); + } +} + +static inline uint32_t rotr16(uint32_t x) { + return (x << 16) | (x >> 16); +} + +static inline void mix_columns32(uint32_t *q) { + uint32_t q0, q1, q2, q3, q4, q5, q6, q7; + uint32_t r0, r1, r2, r3, r4, r5, r6, r7; + + q0 = q[0]; + q1 = q[1]; + q2 = q[2]; + q3 = q[3]; + q4 = q[4]; + q5 = q[5]; + q6 = q[6]; + q7 = q[7]; + r0 = (q0 >> 8) | (q0 << 24); + r1 = (q1 >> 8) | (q1 << 24); + r2 = (q2 >> 8) | (q2 << 24); + r3 = (q3 >> 8) | (q3 << 24); + r4 = (q4 >> 8) | (q4 << 24); + r5 = (q5 >> 8) | (q5 << 24); + r6 = (q6 >> 8) | (q6 << 24); + r7 = (q7 >> 8) | (q7 << 24); + + q[0] = q7 ^ r7 ^ r0 ^ rotr16(q0 ^ r0); + q[1] = q0 ^ r0 ^ q7 ^ r7 ^ r1 ^ rotr16(q1 ^ r1); + q[2] = q1 ^ r1 ^ r2 ^ rotr16(q2 ^ r2); + q[3] = q2 ^ r2 ^ q7 ^ r7 ^ r3 ^ rotr16(q3 ^ r3); + q[4] = q3 ^ r3 ^ q7 ^ r7 ^ r4 ^ rotr16(q4 ^ r4); + q[5] = q4 ^ r4 ^ r5 ^ rotr16(q5 ^ r5); + q[6] = q5 ^ r5 ^ r6 ^ rotr16(q6 ^ r6); + q[7] = q6 ^ r6 ^ r7 ^ rotr16(q7 ^ r7); +} + +static void br_aes_ct64_ortho(uint64_t *q) { +#define SWAPN(cl, ch, s, x, y) do { \ + uint64_t a, b; \ + a = (x); \ + b = (y); \ + (x) = (a & (uint64_t)(cl)) | ((b & (uint64_t)(cl)) << (s)); \ + (y) = ((a & (uint64_t)(ch)) >> (s)) | (b & (uint64_t)(ch)); \ + } while (0) + +#define SWAP2(x, y) SWAPN(0x5555555555555555, 0xAAAAAAAAAAAAAAAA, 1, x, y) +#define SWAP4(x, y) SWAPN(0x3333333333333333, 0xCCCCCCCCCCCCCCCC, 2, x, y) +#define SWAP8(x, y) SWAPN(0x0F0F0F0F0F0F0F0F, 0xF0F0F0F0F0F0F0F0, 4, x, y) + + SWAP2(q[0], q[1]); + SWAP2(q[2], q[3]); + SWAP2(q[4], q[5]); + SWAP2(q[6], q[7]); + + SWAP4(q[0], q[2]); + SWAP4(q[1], q[3]); + SWAP4(q[4], q[6]); + SWAP4(q[5], q[7]); + + SWAP8(q[0], q[4]); + SWAP8(q[1], q[5]); + SWAP8(q[2], q[6]); + SWAP8(q[3], q[7]); +} + + +static void br_aes_ct64_interleave_in(uint64_t *q0, uint64_t *q1, const uint32_t *w) { + uint64_t x0, x1, x2, x3; + + x0 = w[0]; + x1 = w[1]; + x2 = w[2]; + x3 = w[3]; + x0 |= (x0 << 16); + x1 |= (x1 << 16); + x2 |= (x2 << 16); + x3 |= (x3 << 16); + x0 &= (uint64_t)0x0000FFFF0000FFFF; + x1 &= (uint64_t)0x0000FFFF0000FFFF; + x2 &= (uint64_t)0x0000FFFF0000FFFF; + x3 &= (uint64_t)0x0000FFFF0000FFFF; + x0 |= (x0 << 8); + x1 |= (x1 << 8); + x2 |= (x2 << 8); + x3 |= (x3 << 8); + x0 &= (uint64_t)0x00FF00FF00FF00FF; + x1 &= (uint64_t)0x00FF00FF00FF00FF; + x2 &= (uint64_t)0x00FF00FF00FF00FF; + x3 &= (uint64_t)0x00FF00FF00FF00FF; + *q0 = x0 | (x2 << 8); + *q1 = x1 | (x3 << 8); +} + + +static void br_aes_ct64_interleave_out(uint32_t *w, uint64_t q0, uint64_t q1) { + uint64_t x0, x1, x2, x3; + + x0 = q0 & (uint64_t)0x00FF00FF00FF00FF; + x1 = q1 & (uint64_t)0x00FF00FF00FF00FF; + x2 = (q0 >> 8) & (uint64_t)0x00FF00FF00FF00FF; + x3 = (q1 >> 8) & (uint64_t)0x00FF00FF00FF00FF; + x0 |= (x0 >> 8); + x1 |= (x1 >> 8); + x2 |= (x2 >> 8); + x3 |= (x3 >> 8); + x0 &= (uint64_t)0x0000FFFF0000FFFF; + x1 &= (uint64_t)0x0000FFFF0000FFFF; + x2 &= (uint64_t)0x0000FFFF0000FFFF; + x3 &= (uint64_t)0x0000FFFF0000FFFF; + w[0] = (uint32_t)x0 | (uint32_t)(x0 >> 16); + w[1] = (uint32_t)x1 | (uint32_t)(x1 >> 16); + w[2] = (uint32_t)x2 | (uint32_t)(x2 >> 16); + w[3] = (uint32_t)x3 | (uint32_t)(x3 >> 16); +} + +static inline void add_round_key(uint64_t *q, const uint64_t *sk) { + q[0] ^= sk[0]; + q[1] ^= sk[1]; + q[2] ^= sk[2]; + q[3] ^= sk[3]; + q[4] ^= sk[4]; + q[5] ^= sk[5]; + q[6] ^= sk[6]; + q[7] ^= sk[7]; +} + +static inline void shift_rows(uint64_t *q) { + int i; + + for (i = 0; i < 8; i++) { + uint64_t x; + + x = q[i]; + q[i] = (x & (uint64_t)0x000000000000FFFF) + | ((x & (uint64_t)0x00000000FFF00000) >> 4) + | ((x & (uint64_t)0x00000000000F0000) << 12) + | ((x & (uint64_t)0x0000FF0000000000) >> 8) + | ((x & (uint64_t)0x000000FF00000000) << 8) + | ((x & (uint64_t)0xF000000000000000) >> 12) + | ((x & (uint64_t)0x0FFF000000000000) << 4); + } +} + +static inline uint64_t rotr32(uint64_t x) { + return (x << 32) | (x >> 32); +} + +static inline void mix_columns(uint64_t *q) { + uint64_t q0, q1, q2, q3, q4, q5, q6, q7; + uint64_t r0, r1, r2, r3, r4, r5, r6, r7; + + q0 = q[0]; + q1 = q[1]; + q2 = q[2]; + q3 = q[3]; + q4 = q[4]; + q5 = q[5]; + q6 = q[6]; + q7 = q[7]; + r0 = (q0 >> 16) | (q0 << 48); + r1 = (q1 >> 16) | (q1 << 48); + r2 = (q2 >> 16) | (q2 << 48); + r3 = (q3 >> 16) | (q3 << 48); + r4 = (q4 >> 16) | (q4 << 48); + r5 = (q5 >> 16) | (q5 << 48); + r6 = (q6 >> 16) | (q6 << 48); + r7 = (q7 >> 16) | (q7 << 48); + + q[0] = q7 ^ r7 ^ r0 ^ rotr32(q0 ^ r0); + q[1] = q0 ^ r0 ^ q7 ^ r7 ^ r1 ^ rotr32(q1 ^ r1); + q[2] = q1 ^ r1 ^ r2 ^ rotr32(q2 ^ r2); + q[3] = q2 ^ r2 ^ q7 ^ r7 ^ r3 ^ rotr32(q3 ^ r3); + q[4] = q3 ^ r3 ^ q7 ^ r7 ^ r4 ^ rotr32(q4 ^ r4); + q[5] = q4 ^ r4 ^ r5 ^ rotr32(q5 ^ r5); + q[6] = q5 ^ r5 ^ r6 ^ rotr32(q6 ^ r6); + q[7] = q6 ^ r6 ^ r7 ^ rotr32(q7 ^ r7); +} + +static void interleave_constant(uint64_t *out, const unsigned char *in) { + uint32_t tmp_32_constant[16]; + int i; + + br_range_dec32le(tmp_32_constant, 16, in); + for (i = 0; i < 4; i++) { + br_aes_ct64_interleave_in(&out[i], &out[i + 4], tmp_32_constant + (i << 2)); + } + br_aes_ct64_ortho(out); +} + +static void interleave_constant32(uint32_t *out, const unsigned char *in) { + int i; + for (i = 0; i < 4; i++) { + out[2 * i] = br_dec32le(in + 4 * i); + out[2 * i + 1] = br_dec32le(in + 4 * i + 16); + } + br_aes_ct_ortho(out); +} + +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_tweak_constants( + const unsigned char *pk_seed, const unsigned char *sk_seed, + unsigned long long seed_length) { + unsigned char buf[40 * 16]; + int i; + + /* Use the standard constants to generate tweaked ones. */ + memcpy((uint8_t *)tweaked512_rc64, (uint8_t *)haraka512_rc64, 40 * 16); + + /* Constants for sk.seed */ + if (sk_seed != NULL) { + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_haraka_S( + buf, 40 * 16, sk_seed, seed_length); + + /* Interleave constants */ + for (i = 0; i < 10; i++) { + interleave_constant32(tweaked256_rc32_sseed[i], buf + 32 * i); + } + } + + /* Constants for pk.seed */ + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_haraka_S( + buf, 40 * 16, pk_seed, seed_length); + for (i = 0; i < 10; i++) { + interleave_constant32(tweaked256_rc32[i], buf + 32 * i); + interleave_constant(tweaked512_rc64[i], buf + 64 * i); + } +} + +static void haraka_S_absorb(unsigned char *s, + const unsigned char *m, unsigned long long mlen, + unsigned char p) { + unsigned long long i; + unsigned char t[HARAKAS_RATE]; + + while (mlen >= HARAKAS_RATE) { + /* XOR block to state */ + for (i = 0; i < HARAKAS_RATE; ++i) { + s[i] ^= m[i]; + } + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_haraka512_perm(s, s); + mlen -= HARAKAS_RATE; + m += HARAKAS_RATE; + } + + for (i = 0; i < HARAKAS_RATE; ++i) { + t[i] = 0; + } + for (i = 0; i < mlen; ++i) { + t[i] = m[i]; + } + t[i] = p; + t[HARAKAS_RATE - 1] |= 128; + for (i = 0; i < HARAKAS_RATE; ++i) { + s[i] ^= t[i]; + } +} + +static void haraka_S_squeezeblocks(unsigned char *h, unsigned long long nblocks, + unsigned char *s) { + while (nblocks > 0) { + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_haraka512_perm(s, s); + memcpy(h, s, HARAKAS_RATE); + h += HARAKAS_RATE; + nblocks--; + } +} + +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_haraka_S_inc_init(uint8_t *s_inc) { + size_t i; + + for (i = 0; i < 64; i++) { + s_inc[i] = 0; + } + s_inc[64] = 0; +} + +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_haraka_S_inc_absorb(uint8_t *s_inc, const uint8_t *m, size_t mlen) { + size_t i; + + /* Recall that s_inc[64] is the non-absorbed bytes xored into the state */ + while (mlen + s_inc[64] >= HARAKAS_RATE) { + for (i = 0; i < (size_t)(HARAKAS_RATE - s_inc[64]); i++) { + /* Take the i'th byte from message + xor with the s_inc[64] + i'th byte of the state */ + s_inc[s_inc[64] + i] ^= m[i]; + } + mlen -= (size_t)(HARAKAS_RATE - s_inc[64]); + m += HARAKAS_RATE - s_inc[64]; + s_inc[64] = 0; + + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_haraka512_perm(s_inc, s_inc); + } + + for (i = 0; i < mlen; i++) { + s_inc[s_inc[64] + i] ^= m[i]; + } + s_inc[64] = (uint8_t)(mlen + s_inc[64]); +} + +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_haraka_S_inc_finalize(uint8_t *s_inc) { + /* After haraka_S_inc_absorb, we are guaranteed that s_inc[64] < HARAKAS_RATE, + so we can always use one more byte for p in the current state. */ + s_inc[s_inc[64]] ^= 0x1F; + s_inc[HARAKAS_RATE - 1] ^= 128; + s_inc[64] = 0; +} + +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_haraka_S_inc_squeeze(uint8_t *out, size_t outlen, uint8_t *s_inc) { + uint8_t i; + + /* First consume any bytes we still have sitting around */ + for (i = 0; i < outlen && i < s_inc[64]; i++) { + /* There are s_inc[64] bytes left, so r - s_inc[64] is the first + available byte. We consume from there, i.e., up to r. */ + out[i] = s_inc[(HARAKAS_RATE - s_inc[64] + i)]; + } + out += i; + outlen -= i; + s_inc[64] = (uint8_t)(s_inc[64] - i); + + /* Then squeeze the remaining necessary blocks */ + while (outlen > 0) { + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_haraka512_perm(s_inc, s_inc); + + for (i = 0; i < outlen && i < HARAKAS_RATE; i++) { + out[i] = s_inc[i]; + } + out += i; + outlen -= i; + s_inc[64] = (uint8_t)(HARAKAS_RATE - i); + } +} + +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_haraka_S(unsigned char *out, unsigned long long outlen, const unsigned char *in, unsigned long long inlen) { + unsigned long long i; + unsigned char s[64]; + unsigned char d[32]; + + for (i = 0; i < 64; i++) { + s[i] = 0; + } + haraka_S_absorb(s, in, inlen, 0x1F); + + haraka_S_squeezeblocks(out, outlen / 32, s); + out += (outlen / 32) * 32; + + if (outlen % 32) { + haraka_S_squeezeblocks(d, 1, s); + for (i = 0; i < outlen % 32; i++) { + out[i] = d[i]; + } + } +} + +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_haraka512_perm(unsigned char *out, const unsigned char *in) { + uint32_t w[16]; + uint64_t q[8], tmp_q; + unsigned int i, j; + + br_range_dec32le(w, 16, in); + for (i = 0; i < 4; i++) { + br_aes_ct64_interleave_in(&q[i], &q[i + 4], w + (i << 2)); + } + br_aes_ct64_ortho(q); + + /* AES rounds */ + for (i = 0; i < 5; i++) { + for (j = 0; j < 2; j++) { + br_aes_ct64_bitslice_Sbox(q); + shift_rows(q); + mix_columns(q); + add_round_key(q, tweaked512_rc64[2 * i + j]); + } + /* Mix states */ + for (j = 0; j < 8; j++) { + tmp_q = q[j]; + q[j] = (tmp_q & 0x0001000100010001) << 5 | + (tmp_q & 0x0002000200020002) << 12 | + (tmp_q & 0x0004000400040004) >> 1 | + (tmp_q & 0x0008000800080008) << 6 | + (tmp_q & 0x0020002000200020) << 9 | + (tmp_q & 0x0040004000400040) >> 4 | + (tmp_q & 0x0080008000800080) << 3 | + (tmp_q & 0x2100210021002100) >> 5 | + (tmp_q & 0x0210021002100210) << 2 | + (tmp_q & 0x0800080008000800) << 4 | + (tmp_q & 0x1000100010001000) >> 12 | + (tmp_q & 0x4000400040004000) >> 10 | + (tmp_q & 0x8400840084008400) >> 3; + } + } + + br_aes_ct64_ortho(q); + for (i = 0; i < 4; i ++) { + br_aes_ct64_interleave_out(w + (i << 2), q[i], q[i + 4]); + } + br_range_enc32le(out, w, 16); +} + +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_haraka512(unsigned char *out, const unsigned char *in) { + int i; + + unsigned char buf[64]; + + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_haraka512_perm(buf, in); + /* Feed-forward */ + for (i = 0; i < 64; i++) { + buf[i] = buf[i] ^ in[i]; + } + + /* Truncated */ + memcpy(out, buf + 8, 8); + memcpy(out + 8, buf + 24, 8); + memcpy(out + 16, buf + 32, 8); + memcpy(out + 24, buf + 48, 8); +} + + +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_haraka256(unsigned char *out, const unsigned char *in) { + uint32_t q[8], tmp_q; + int i, j; + + for (i = 0; i < 4; i++) { + q[2 * i] = br_dec32le(in + 4 * i); + q[2 * i + 1] = br_dec32le(in + 4 * i + 16); + } + br_aes_ct_ortho(q); + + /* AES rounds */ + for (i = 0; i < 5; i++) { + for (j = 0; j < 2; j++) { + br_aes_ct_bitslice_Sbox(q); + shift_rows32(q); + mix_columns32(q); + add_round_key32(q, tweaked256_rc32[2 * i + j]); + } + + /* Mix states */ + for (j = 0; j < 8; j++) { + tmp_q = q[j]; + q[j] = (tmp_q & 0x81818181) | + (tmp_q & 0x02020202) << 1 | + (tmp_q & 0x04040404) << 2 | + (tmp_q & 0x08080808) << 3 | + (tmp_q & 0x10101010) >> 3 | + (tmp_q & 0x20202020) >> 2 | + (tmp_q & 0x40404040) >> 1; + } + } + + br_aes_ct_ortho(q); + for (i = 0; i < 4; i++) { + br_enc32le(out + 4 * i, q[2 * i]); + br_enc32le(out + 4 * i + 16, q[2 * i + 1]); + } + + for (i = 0; i < 32; i++) { + out[i] ^= in[i]; + } +} + +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_haraka256_sk(unsigned char *out, const unsigned char *in) { + uint32_t q[8], tmp_q; + int i, j; + + for (i = 0; i < 4; i++) { + q[2 * i] = br_dec32le(in + 4 * i); + q[2 * i + 1] = br_dec32le(in + 4 * i + 16); + } + br_aes_ct_ortho(q); + + /* AES rounds */ + for (i = 0; i < 5; i++) { + for (j = 0; j < 2; j++) { + br_aes_ct_bitslice_Sbox(q); + shift_rows32(q); + mix_columns32(q); + add_round_key32(q, tweaked256_rc32_sseed[2 * i + j]); + } + + /* Mix states */ + for (j = 0; j < 8; j++) { + tmp_q = q[j]; + q[j] = (tmp_q & 0x81818181) | + (tmp_q & 0x02020202) << 1 | + (tmp_q & 0x04040404) << 2 | + (tmp_q & 0x08080808) << 3 | + (tmp_q & 0x10101010) >> 3 | + (tmp_q & 0x20202020) >> 2 | + (tmp_q & 0x40404040) >> 1; + } + } + + br_aes_ct_ortho(q); + for (i = 0; i < 4; i++) { + br_enc32le(out + 4 * i, q[2 * i]); + br_enc32le(out + 4 * i + 16, q[2 * i + 1]); + } + + for (i = 0; i < 32; i++) { + out[i] ^= in[i]; + } +} diff --git a/crypto_sign/sphincs-haraka-192f-simple/clean/haraka.h b/crypto_sign/sphincs-haraka-192f-simple/clean/haraka.h new file mode 100644 index 00000000..ff547019 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192f-simple/clean/haraka.h @@ -0,0 +1,30 @@ +#ifndef SPX_HARAKA_H +#define SPX_HARAKA_H + +/* Tweak constants with seed */ +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_tweak_constants( + const unsigned char *pk_seed, const unsigned char *sk_seed, + unsigned long long seed_length); + +/* Haraka Sponge */ +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_haraka_S_inc_init(uint8_t *s_inc); +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_haraka_S_inc_absorb(uint8_t *s_inc, const uint8_t *m, size_t mlen); +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_haraka_S_inc_finalize(uint8_t *s_inc); +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_haraka_S_inc_squeeze(uint8_t *out, size_t outlen, uint8_t *s_inc); +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_haraka_S( + unsigned char *out, unsigned long long outlen, + const unsigned char *in, unsigned long long inlen); + +/* Applies the 512-bit Haraka permutation to in. */ +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_haraka512_perm(unsigned char *out, const unsigned char *in); + +/* Implementation of Haraka-512 */ +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_haraka512(unsigned char *out, const unsigned char *in); + +/* Implementation of Haraka-256 */ +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_haraka256(unsigned char *out, const unsigned char *in); + +/* Implementation of Haraka-256 using sk.seed constants */ +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_haraka256_sk(unsigned char *out, const unsigned char *in); + +#endif diff --git a/crypto_sign/sphincs-haraka-192f-simple/clean/hash.h b/crypto_sign/sphincs-haraka-192f-simple/clean/hash.h new file mode 100644 index 00000000..f1d4a167 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192f-simple/clean/hash.h @@ -0,0 +1,22 @@ +#ifndef SPX_HASH_H +#define SPX_HASH_H + +#include + +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed); + +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen); + +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen); + +#endif diff --git a/crypto_sign/sphincs-haraka-192f-simple/clean/hash_haraka.c b/crypto_sign/sphincs-haraka-192f-simple/clean/hash_haraka.c new file mode 100644 index 00000000..5f1c9cde --- /dev/null +++ b/crypto_sign/sphincs-haraka-192f-simple/clean/hash_haraka.c @@ -0,0 +1,86 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "utils.h" + +#include "haraka.h" + +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed) { + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_tweak_constants(pub_seed, sk_seed, SPX_N); +} + +/* + * Computes PRF(key, addr), given a secret key of SPX_N bytes and an address + */ +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]) { + unsigned char buf[SPX_ADDR_BYTES]; + /* Since SPX_N may be smaller than 32, we need a temporary buffer. */ + unsigned char outbuf[32]; + + (void)key; /* Suppress an 'unused parameter' warning. */ + + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_addr_to_bytes(buf, addr); + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_haraka256_sk(outbuf, buf); + memcpy(out, outbuf, SPX_N); +} + +/** + * Computes the message-dependent randomness R, using a secret seed and an + * optional randomization value as well as the message. + */ +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen) { + uint8_t s_inc[65]; + + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_haraka_S_inc_init(s_inc); + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_haraka_S_inc_absorb(s_inc, sk_prf, SPX_N); + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_haraka_S_inc_absorb(s_inc, optrand, SPX_N); + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_haraka_S_inc_absorb(s_inc, m, mlen); + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_haraka_S_inc_finalize(s_inc); + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_haraka_S_inc_squeeze(R, SPX_N, s_inc); +} + +/** + * Computes the message hash using R, the public key, and the message. + * Outputs the message digest and the index of the leaf. The index is split in + * the tree index and the leaf index, for convenient copying to an address. + */ +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen) { +#define SPX_TREE_BITS (SPX_TREE_HEIGHT * (SPX_D - 1)) +#define SPX_TREE_BYTES ((SPX_TREE_BITS + 7) / 8) +#define SPX_LEAF_BITS SPX_TREE_HEIGHT +#define SPX_LEAF_BYTES ((SPX_LEAF_BITS + 7) / 8) +#define SPX_DGST_BYTES (SPX_FORS_MSG_BYTES + SPX_TREE_BYTES + SPX_LEAF_BYTES) + + unsigned char buf[SPX_DGST_BYTES]; + unsigned char *bufp = buf; + uint8_t s_inc[65]; + + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_haraka_S_inc_init(s_inc); + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_haraka_S_inc_absorb(s_inc, R, SPX_N); + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_haraka_S_inc_absorb(s_inc, pk, SPX_PK_BYTES); + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_haraka_S_inc_absorb(s_inc, m, mlen); + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_haraka_S_inc_finalize(s_inc); + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_haraka_S_inc_squeeze(buf, SPX_DGST_BYTES, s_inc); + + memcpy(digest, bufp, SPX_FORS_MSG_BYTES); + bufp += SPX_FORS_MSG_BYTES; + + *tree = PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_bytes_to_ull(bufp, SPX_TREE_BYTES); + *tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS); + bufp += SPX_TREE_BYTES; + + *leaf_idx = (uint32_t)PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_bytes_to_ull( + bufp, SPX_LEAF_BYTES); + *leaf_idx &= (~(uint32_t)0) >> (32 - SPX_LEAF_BITS); +} diff --git a/crypto_sign/sphincs-haraka-192f-simple/clean/params.h b/crypto_sign/sphincs-haraka-192f-simple/clean/params.h new file mode 100644 index 00000000..898f177f --- /dev/null +++ b/crypto_sign/sphincs-haraka-192f-simple/clean/params.h @@ -0,0 +1,53 @@ +#ifndef SPX_PARAMS_H +#define SPX_PARAMS_H + +/* Hash output length in bytes. */ +#define SPX_N 24 +/* Height of the hypertree. */ +#define SPX_FULL_HEIGHT 66 +/* Number of subtree layer. */ +#define SPX_D 22 +/* FORS tree dimensions. */ +#define SPX_FORS_HEIGHT 8 +#define SPX_FORS_TREES 33 +/* Winternitz parameter, */ +#define SPX_WOTS_W 16 + +/* The hash function is defined by linking a different hash.c file, as opposed + to setting a #define constant. */ + +/* For clarity */ +#define SPX_ADDR_BYTES 32 + +/* WOTS parameters. */ +#define SPX_WOTS_LOGW 4 + +#define SPX_WOTS_LEN1 (8 * SPX_N / SPX_WOTS_LOGW) + +/* SPX_WOTS_LEN2 is floor(log(len_1 * (w - 1)) / log(w)) + 1; we precompute */ +#define SPX_WOTS_LEN2 3 + +#define SPX_WOTS_LEN (SPX_WOTS_LEN1 + SPX_WOTS_LEN2) +#define SPX_WOTS_BYTES (SPX_WOTS_LEN * SPX_N) +#define SPX_WOTS_PK_BYTES SPX_WOTS_BYTES + +/* Subtree size. */ +#define SPX_TREE_HEIGHT (SPX_FULL_HEIGHT / SPX_D) + +/* FORS parameters. */ +#define SPX_FORS_MSG_BYTES ((SPX_FORS_HEIGHT * SPX_FORS_TREES + 7) / 8) +#define SPX_FORS_BYTES ((SPX_FORS_HEIGHT + 1) * SPX_FORS_TREES * SPX_N) +#define SPX_FORS_PK_BYTES SPX_N + +/* Resulting SPX sizes. */ +#define SPX_BYTES (SPX_N + SPX_FORS_BYTES + SPX_D * SPX_WOTS_BYTES +\ + SPX_FULL_HEIGHT * SPX_N) +#define SPX_PK_BYTES (2 * SPX_N) +#define SPX_SK_BYTES (2 * SPX_N + SPX_PK_BYTES) + +/* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ +#define SPX_OPTRAND_BYTES 32 + +#endif diff --git a/crypto_sign/sphincs-haraka-192f-simple/clean/sign.c b/crypto_sign/sphincs-haraka-192f-simple/clean/sign.c new file mode 100644 index 00000000..8c8bfaaf --- /dev/null +++ b/crypto_sign/sphincs-haraka-192f-simple/clean/sign.c @@ -0,0 +1,344 @@ +#include +#include +#include + +#include "address.h" +#include "api.h" +#include "fors.h" +#include "hash.h" +#include "params.h" +#include "randombytes.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +/** + * Computes the leaf at a given address. First generates the WOTS key pair, + * then computes leaf by hashing horizontally. + */ +static void wots_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t tree_addr[8]) { + unsigned char pk[SPX_WOTS_BYTES]; + uint32_t wots_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_keypair_addr( + wots_addr, addr_idx); + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_wots_gen_pk( + pk, sk_seed, pub_seed, wots_addr); + + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_thash_WOTS_LEN( + leaf, pk, pub_seed, wots_pk_addr); +} + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_crypto_sign_secretkeybytes(void) { + return PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_CRYPTO_SECRETKEYBYTES; +} + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_crypto_sign_publickeybytes(void) { + return PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES; +} + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_crypto_sign_bytes(void) { + return PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_CRYPTO_BYTES; +} + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_crypto_sign_seedbytes(void) { + return PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_CRYPTO_SEEDBYTES; +} + +/* + * Generates an SPX key pair given a seed of length + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed) { + /* We do not need the auth path in key generation, but it simplifies the + code to have just one treehash routine that computes both root and path + in one function. */ + unsigned char auth_path[SPX_TREE_HEIGHT * SPX_N]; + uint32_t top_tree_addr[8] = {0}; + + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_layer_addr( + top_tree_addr, SPX_D - 1); + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_type( + top_tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */ + memcpy(sk, seed, PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_CRYPTO_SEEDBYTES); + + memcpy(pk, sk + 2 * SPX_N, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_initialize_hash_function(pk, sk); + + /* Compute root node of the top-most subtree. */ + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_treehash_TREE_HEIGHT( + sk + 3 * SPX_N, auth_path, sk, sk + 2 * SPX_N, 0, 0, + wots_gen_leaf, top_tree_addr); + + memcpy(pk + SPX_N, sk + 3 * SPX_N, SPX_N); + + return 0; +} + +/* + * Generates an SPX key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk) { + unsigned char seed[PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_CRYPTO_SEEDBYTES]; + randombytes(seed, PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_CRYPTO_SEEDBYTES); + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_crypto_sign_seed_keypair( + pk, sk, seed); + + return 0; +} + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + const unsigned char *sk_seed = sk; + const unsigned char *sk_prf = sk + SPX_N; + const unsigned char *pk = sk + 2 * SPX_N; + const unsigned char *pub_seed = pk; + + unsigned char optrand[SPX_N]; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char root[SPX_N]; + uint32_t i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_initialize_hash_function( + pub_seed, sk_seed); + + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ + randombytes(optrand, SPX_N); + /* Compute the digest randomization value. */ + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_gen_message_random( + sig, sk_prf, optrand, m, mlen); + + /* Derive the message digest and leaf index from R, PK and M. */ + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Sign the message hash using FORS. */ + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_fors_sign( + sig, root, mhash, sk_seed, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Compute a WOTS signature. */ + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_wots_sign( + sig, root, sk_seed, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the authentication path for the used WOTS leaf. */ + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_treehash_TREE_HEIGHT( + root, sig, sk_seed, pub_seed, idx_leaf, 0, + wots_gen_leaf, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + *siglen = SPX_BYTES; + + return 0; +} + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk) { + const unsigned char *pub_seed = pk; + const unsigned char *pub_root = pk + SPX_N; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char wots_pk[SPX_WOTS_BYTES]; + unsigned char root[SPX_N]; + unsigned char leaf[SPX_N]; + unsigned int i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + if (siglen != SPX_BYTES) { + return -1; + } + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_initialize_hash_function( + pub_seed, NULL); + + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + /* Derive the message digest and leaf index from R || PK || M. */ + /* The additional SPX_N is a result of the hash domain separator. */ + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + /* Layer correctly defaults to 0, so no need to set_layer_addr */ + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_fors_pk_from_sig( + root, sig, mhash, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + /* For each subtree.. */ + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + + /* The WOTS public key is only correct if the signature was correct. */ + /* Initially, root is the FORS pk, but on subsequent iterations it is + the root of the subtree below the currently processed subtree. */ + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_wots_pk_from_sig( + wots_pk, sig, root, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the leaf node using the WOTS public key. */ + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_thash_WOTS_LEN( + leaf, wots_pk, pub_seed, wots_pk_addr); + + /* Compute the root node of this subtree. */ + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_compute_root( + root, leaf, idx_leaf, 0, sig, SPX_TREE_HEIGHT, + pub_seed, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + /* Check if the root node equals the root node in the public key. */ + if (memcmp(root, pub_root, SPX_N) != 0) { + return -1; + } + + return 0; +} + + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + size_t siglen; + + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_crypto_sign_signature( + sm, &siglen, m, mlen, sk); + + memmove(sm + SPX_BYTES, m, mlen); + *smlen = siglen + mlen; + + return 0; +} + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk) { + /* The API caller does not necessarily know what size a signature should be + but SPHINCS+ signatures are always exactly SPX_BYTES. */ + if (smlen < SPX_BYTES) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + *mlen = smlen - SPX_BYTES; + + if (PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_crypto_sign_verify( + sm, SPX_BYTES, sm + SPX_BYTES, *mlen, pk)) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + /* If verification was successful, move the message to the right place. */ + memmove(m, sm + SPX_BYTES, *mlen); + + return 0; +} diff --git a/crypto_sign/sphincs-haraka-192f-simple/clean/thash.h b/crypto_sign/sphincs-haraka-192f-simple/clean/thash.h new file mode 100644 index 00000000..5ee99ebb --- /dev/null +++ b/crypto_sign/sphincs-haraka-192f-simple/clean/thash.h @@ -0,0 +1,22 @@ +#ifndef SPX_THASH_H +#define SPX_THASH_H + +#include + +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-haraka-192f-simple/clean/thash_haraka_simple.c b/crypto_sign/sphincs-haraka-192f-simple/clean/thash_haraka_simple.c new file mode 100644 index 00000000..3fc723df --- /dev/null +++ b/crypto_sign/sphincs-haraka-192f-simple/clean/thash_haraka_simple.c @@ -0,0 +1,78 @@ +#include +#include + +#include "address.h" +#include "params.h" +#include "thash.h" + +#include "haraka.h" + +/** + * Takes an array of inblocks concatenated arrays of SPX_N bytes. + */ +static void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_thash( + unsigned char *out, unsigned char *buf, + const unsigned char *in, unsigned int inblocks, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char outbuf[32]; + unsigned char buf_tmp[64]; + + (void)pub_seed; /* Suppress an 'unused parameter' warning. */ + + if (inblocks == 1) { + /* F function */ + /* Since SPX_N may be smaller than 32, we need a temporary buffer. */ + memset(buf_tmp, 0, 64); + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_addr_to_bytes(buf_tmp, addr); + memcpy(buf_tmp + SPX_ADDR_BYTES, in, SPX_N); + + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_haraka512(outbuf, buf_tmp); + memcpy(out, outbuf, SPX_N); + } else { + /* All other tweakable hashes*/ + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_addr_to_bytes(buf, addr); + memcpy(buf + SPX_ADDR_BYTES, in, inblocks * SPX_N); + + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_haraka_S( + out, SPX_N, buf, SPX_ADDR_BYTES + inblocks * SPX_N); + } +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_ADDR_BYTES + 1 * SPX_N]; + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_thash( + out, buf, in, 1, pub_seed, addr); +} + +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_ADDR_BYTES + 2 * SPX_N]; + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_thash( + out, buf, in, 2, pub_seed, addr); +} + +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_ADDR_BYTES + SPX_WOTS_LEN * SPX_N]; + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_thash( + out, buf, in, SPX_WOTS_LEN, pub_seed, addr); +} + +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_ADDR_BYTES + SPX_FORS_TREES * SPX_N]; + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_thash( + out, buf, in, SPX_FORS_TREES, pub_seed, addr); +} diff --git a/crypto_sign/sphincs-haraka-192f-simple/clean/utils.c b/crypto_sign/sphincs-haraka-192f-simple/clean/utils.c new file mode 100644 index 00000000..1b40945d --- /dev/null +++ b/crypto_sign/sphincs-haraka-192f-simple/clean/utils.c @@ -0,0 +1,192 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in) { + + /* Iterate over out in decreasing order, for big-endianness. */ + for (size_t i = outlen; i > 0; i--) { + out[i - 1] = in & 0xff; + in = in >> 8; + } +} + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen) { + unsigned long long retval = 0; + + for (size_t i = 0; i < inlen; i++) { + retval |= ((unsigned long long)in[i]) << (8 * (inlen - 1 - i)); + } + return retval; +} + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + unsigned char buffer[2 * SPX_N]; + + /* If leaf_idx is odd (last bit = 1), current path element is a right child + and auth_path has to go left. Otherwise it is the other way around. */ + if (leaf_idx & 1) { + memcpy(buffer + SPX_N, leaf, SPX_N); + memcpy(buffer, auth_path, SPX_N); + } else { + memcpy(buffer, leaf, SPX_N); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + + for (i = 0; i < tree_height - 1; i++) { + leaf_idx >>= 1; + idx_offset >>= 1; + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_tree_height(addr, i + 1); + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + + /* Pick the right or left neighbor, depending on parity of the node. */ + if (leaf_idx & 1) { + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_thash_2( + buffer + SPX_N, buffer, pub_seed, addr); + memcpy(buffer, auth_path, SPX_N); + } else { + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_thash_2( + buffer, buffer, pub_seed, addr); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + } + + /* The last iteration is exceptional; we do not copy an auth_path node. */ + leaf_idx >>= 1; + idx_offset >>= 1; + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_tree_height(addr, tree_height); + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_thash_2( + root, buffer, pub_seed, addr); +} + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +static void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_treehash( + unsigned char *root, unsigned char *auth_path, + unsigned char *stack, unsigned int *heights, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned int offset = 0; + uint32_t idx; + uint32_t tree_idx; + + for (idx = 0; idx < (uint32_t)(1 << tree_height); idx++) { + /* Add the next leaf node to the stack. */ + gen_leaf(stack + offset * SPX_N, + sk_seed, pub_seed, idx + idx_offset, tree_addr); + offset++; + heights[offset - 1] = 0; + + /* If this is a node we need for the auth path.. */ + if ((leaf_idx ^ 0x1) == idx) { + memcpy(auth_path, stack + (offset - 1)*SPX_N, SPX_N); + } + + /* While the top-most nodes are of equal height.. */ + while (offset >= 2 && heights[offset - 1] == heights[offset - 2]) { + /* Compute index of the new node, in the next layer. */ + tree_idx = (idx >> (heights[offset - 1] + 1)); + + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_tree_height( + tree_addr, heights[offset - 1] + 1); + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_tree_index( + tree_addr, tree_idx + (idx_offset >> (heights[offset - 1] + 1))); + /* Hash the top-most nodes from the stack together. */ + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_thash_2( + stack + (offset - 2)*SPX_N, stack + (offset - 2)*SPX_N, + pub_seed, tree_addr); + offset--; + /* Note that the top-most node is now one layer higher. */ + heights[offset - 1]++; + + /* If this is a node we need for the auth path.. */ + if (((leaf_idx >> heights[offset - 1]) ^ 0x1) == tree_idx) { + memcpy(auth_path + heights[offset - 1]*SPX_N, + stack + (offset - 1)*SPX_N, SPX_N); + } + } + } + memcpy(root, stack, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_FORS_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_FORS_HEIGHT + 1]; + + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_FORS_HEIGHT, gen_leaf, tree_addr); +} + +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_TREE_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_TREE_HEIGHT + 1]; + + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_TREE_HEIGHT, gen_leaf, tree_addr); +} diff --git a/crypto_sign/sphincs-haraka-192f-simple/clean/utils.h b/crypto_sign/sphincs-haraka-192f-simple/clean/utils.h new file mode 100644 index 00000000..246968cf --- /dev/null +++ b/crypto_sign/sphincs-haraka-192f-simple/clean/utils.h @@ -0,0 +1,60 @@ +#ifndef SPX_UTILS_H +#define SPX_UTILS_H + +#include "params.h" +#include +#include + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in); + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen); + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-haraka-192f-simple/clean/wots.c b/crypto_sign/sphincs-haraka-192f-simple/clean/wots.c new file mode 100644 index 00000000..269d9fb7 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192f-simple/clean/wots.c @@ -0,0 +1,161 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +// TODO clarify address expectations, and make them more uniform. +// TODO i.e. do we expect types to be set already? +// TODO and do we expect modifications or copies? + +/** + * Computes the starting value for a chain, i.e. the secret key. + * Expects the address to be complete up to the chain address. + */ +static void wots_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t wots_addr[8]) { + /* Make sure that the hash address is actually zeroed. */ + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_hash_addr(wots_addr, 0); + + /* Generate sk element. */ + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_prf_addr(sk, sk_seed, wots_addr); +} + +/** + * Computes the chaining function. + * out and in have to be n-byte arrays. + * + * Interprets in as start-th value of the chain. + * addr has to contain the address of the chain. + */ +static void gen_chain(unsigned char *out, const unsigned char *in, + unsigned int start, unsigned int steps, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + /* Initialize out with the value at position 'start'. */ + memcpy(out, in, SPX_N); + + /* Iterate 'steps' calls to the hash function. */ + for (i = start; i < (start + steps) && i < SPX_WOTS_W; i++) { + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_hash_addr(addr, i); + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_thash_1( + out, out, pub_seed, addr); + } +} + +/** + * base_w algorithm as described in draft. + * Interprets an array of bytes as integers in base w. + * This only works when log_w is a divisor of 8. + */ +static void base_w(unsigned int *output, const size_t out_len, + const unsigned char *input) { + size_t in = 0; + size_t out = 0; + unsigned char total = 0; + unsigned int bits = 0; + size_t consumed; + + for (consumed = 0; consumed < out_len; consumed++) { + if (bits == 0) { + total = input[in]; + in++; + bits += 8; + } + bits -= SPX_WOTS_LOGW; + output[out] = (unsigned int)((total >> bits) & (SPX_WOTS_W - 1)); + out++; + } +} + +/* Computes the WOTS+ checksum over a message (in base_w). */ +static void wots_checksum(unsigned int *csum_base_w, + const unsigned int *msg_base_w) { + unsigned int csum = 0; + unsigned char csum_bytes[(SPX_WOTS_LEN2 * SPX_WOTS_LOGW + 7) / 8]; + unsigned int i; + + /* Compute checksum. */ + for (i = 0; i < SPX_WOTS_LEN1; i++) { + csum += SPX_WOTS_W - 1 - msg_base_w[i]; + } + + /* Convert checksum to base_w. */ + /* Make sure expected empty zero bits are the least significant bits. */ + csum = csum << (8 - ((SPX_WOTS_LEN2 * SPX_WOTS_LOGW) % 8)); + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_ull_to_bytes( + csum_bytes, sizeof(csum_bytes), csum); + base_w(csum_base_w, SPX_WOTS_LEN2, csum_bytes); +} + +/* Takes a message and derives the matching chain lengths. */ +static void chain_lengths(unsigned int *lengths, const unsigned char *msg) { + base_w(lengths, SPX_WOTS_LEN1, msg); + wots_checksum(lengths + SPX_WOTS_LEN1, lengths); +} + +/** + * WOTS key generation. Takes a 32 byte sk_seed, expands it to WOTS private key + * elements and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(pk + i * SPX_N, sk_seed, addr); + gen_chain(pk + i * SPX_N, pk + i * SPX_N, + 0, SPX_WOTS_W - 1, pub_seed, addr); + } +} + +/** + * Takes a n-byte message and the 32-byte sk_see to compute a signature 'sig'. + */ +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(sig + i * SPX_N, sk_seed, addr); + gen_chain(sig + i * SPX_N, sig + i * SPX_N, 0, lengths[i], pub_seed, addr); + } +} + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_set_chain_addr(addr, i); + gen_chain(pk + i * SPX_N, sig + i * SPX_N, + lengths[i], SPX_WOTS_W - 1 - lengths[i], pub_seed, addr); + } +} diff --git a/crypto_sign/sphincs-haraka-192f-simple/clean/wots.h b/crypto_sign/sphincs-haraka-192f-simple/clean/wots.h new file mode 100644 index 00000000..8283078d --- /dev/null +++ b/crypto_sign/sphincs-haraka-192f-simple/clean/wots.h @@ -0,0 +1,38 @@ +#ifndef SPX_WOTS_H +#define SPX_WOTS_H + +#include "params.h" +#include + +/** + * WOTS key generation. Takes a 32 byte seed for the private key, expands it to + * a full WOTS private key and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * Takes a n-byte message and the 32-byte seed for the private key to compute a + * signature that is placed at 'sig'. + */ +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]); + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-haraka-192s-robust/META.yml b/crypto_sign/sphincs-haraka-192s-robust/META.yml new file mode 100644 index 00000000..81e2ed43 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192s-robust/META.yml @@ -0,0 +1,27 @@ +name: SPHINCS+ +type: signature +claimed-nist-level: 3 +length-public-key: 48 +length-secret-key: 96 +length-signature: 17064 +testvectors-sha256: 5dd40c8ea9a81ad93e0685843ec1cabdcb6eec9f6e64fc01d928ebaf7cf377c6 +principal-submitter: Andreas Hülsing +auxiliary-submitters: + - Jean-Philippe Aumasson + - Daniel J. Bernstein, + - Christoph Dobraunig + - Maria Eichlseder + - Scott Fluhrer + - Stefan-Lukas Gazdag + - Panos Kampanakis + - Stefan Kölbl + - Tanja Lange + - Martin M. Lauridsen + - Florian Mendel + - Ruben Niederhagen + - Christian Rechberger + - Joost Rijneveld + - Peter Schwabe +implementations: + - name: clean + version: https://github.com/sphincs/sphincsplus/commit/77755c94d0bc744478044d6efbb888dc13156441 diff --git a/crypto_sign/sphincs-haraka-192s-robust/clean/LICENSE b/crypto_sign/sphincs-haraka-192s-robust/clean/LICENSE new file mode 100644 index 00000000..670154e3 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192s-robust/clean/LICENSE @@ -0,0 +1,116 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + diff --git a/crypto_sign/sphincs-haraka-192s-robust/clean/Makefile b/crypto_sign/sphincs-haraka-192s-robust/clean/Makefile new file mode 100644 index 00000000..6e7a2986 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192s-robust/clean/Makefile @@ -0,0 +1,20 @@ +# This Makefile can be used with GNU Make or BSD Make + +LIB=libsphincs-haraka-192s-robust_clean.a + +HEADERS = params.h address.h wots.h utils.h fors.h api.h hash.h thash.h haraka.h +OBJECTS = address.o wots.o utils.o fors.o sign.o hash_haraka.o thash_haraka_robust.o haraka.o + +CFLAGS=-O3 -Wall -Wconversion -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) + +all: $(LIB) + +%.o: %.c $(HEADERS) + $(CC) $(CFLAGS) -c -o $@ $< + +$(LIB): $(OBJECTS) + $(AR) -r $@ $(OBJECTS) + +clean: + $(RM) $(OBJECTS) + $(RM) $(LIB) diff --git a/crypto_sign/sphincs-haraka-192s-robust/clean/Makefile.Microsoft_nmake b/crypto_sign/sphincs-haraka-192s-robust/clean/Makefile.Microsoft_nmake new file mode 100644 index 00000000..b74d7aec --- /dev/null +++ b/crypto_sign/sphincs-haraka-192s-robust/clean/Makefile.Microsoft_nmake @@ -0,0 +1,19 @@ +# This Makefile can be used with Microsoft Visual Studio's nmake using the command: +# nmake /f Makefile.Microsoft_nmake + +LIBRARY=libsphincs-haraka-192s-robust_clean.lib +OBJECTS=address.obj wots.obj utils.obj fors.obj sign.obj hash_haraka.obj thash_haraka_robust.obj haraka.obj + +CFLAGS=/nologo /I ..\..\..\common /W4 /WX + +all: $(LIBRARY) + +# Make sure objects are recompiled if headers change. +$(OBJECTS): *.h + +$(LIBRARY): $(OBJECTS) + LIB.EXE /NOLOGO /WX /OUT:$@ $** + +clean: + -DEL $(OBJECTS) + -DEL $(LIBRARY) diff --git a/crypto_sign/sphincs-haraka-192s-robust/clean/address.c b/crypto_sign/sphincs-haraka-192s-robust/clean/address.c new file mode 100644 index 00000000..b97822db --- /dev/null +++ b/crypto_sign/sphincs-haraka-192s-robust/clean/address.c @@ -0,0 +1,78 @@ +#include + +#include "address.h" +#include "params.h" +#include "utils.h" + +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]) { + int i; + + for (i = 0; i < 8; i++) { + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_ull_to_bytes( + bytes + i * 4, 4, addr[i]); + } +} + +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer) { + addr[0] = layer; +} + +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree) { + addr[1] = 0; + addr[2] = (uint32_t) (tree >> 32); + addr[3] = (uint32_t) tree; +} + +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_type( + uint32_t addr[8], uint32_t type) { + addr[4] = type; +} + +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; +} + +/* These functions are used for OTS addresses. */ + +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair) { + addr[5] = keypair; +} + +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; + out[5] = in[5]; +} + +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain) { + addr[6] = chain; +} + +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash) { + addr[7] = hash; +} + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height) { + addr[6] = tree_height; +} + +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index) { + addr[7] = tree_index; +} diff --git a/crypto_sign/sphincs-haraka-192s-robust/clean/address.h b/crypto_sign/sphincs-haraka-192s-robust/clean/address.h new file mode 100644 index 00000000..60932e74 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192s-robust/clean/address.h @@ -0,0 +1,50 @@ +#ifndef SPX_ADDRESS_H +#define SPX_ADDRESS_H + +#include + +#define SPX_ADDR_TYPE_WOTS 0 +#define SPX_ADDR_TYPE_WOTSPK 1 +#define SPX_ADDR_TYPE_HASHTREE 2 +#define SPX_ADDR_TYPE_FORSTREE 3 +#define SPX_ADDR_TYPE_FORSPK 4 + +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer); + +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree); + +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_type( + uint32_t addr[8], uint32_t type); + +/* Copies the layer and tree part of one address into the other */ +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for WOTS and FORS addresses. */ + +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair); + +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain); + +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash); + +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height); + +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index); + +#endif diff --git a/crypto_sign/sphincs-haraka-192s-robust/clean/api.h b/crypto_sign/sphincs-haraka-192s-robust/clean/api.h new file mode 100644 index 00000000..93ff0cbf --- /dev/null +++ b/crypto_sign/sphincs-haraka-192s-robust/clean/api.h @@ -0,0 +1,78 @@ +#ifndef PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_API_H +#define PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_API_H + +#include +#include + +#define PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_CRYPTO_ALGNAME "SPHINCS+" + +#define PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_CRYPTO_SECRETKEYBYTES 96 +#define PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES 48 +#define PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_CRYPTO_BYTES 17064 +#define PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_CRYPTO_SEEDBYTES 72 + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_crypto_sign_secretkeybytes(void); + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_crypto_sign_publickeybytes(void); + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_crypto_sign_bytes(void); + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_crypto_sign_seedbytes(void); + +/* + * Generates a SPHINCS+ key pair given a seed. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed); + +/* + * Generates a SPHINCS+ key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk); + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk); + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk); + +#endif diff --git a/crypto_sign/sphincs-haraka-192s-robust/clean/fors.c b/crypto_sign/sphincs-haraka-192s-robust/clean/fors.c new file mode 100644 index 00000000..5581843d --- /dev/null +++ b/crypto_sign/sphincs-haraka-192s-robust/clean/fors.c @@ -0,0 +1,164 @@ +#include +#include +#include + +#include "address.h" +#include "fors.h" +#include "hash.h" +#include "thash.h" +#include "utils.h" + +static void fors_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_prf_addr( + sk, sk_seed, fors_leaf_addr); +} + +static void fors_sk_to_leaf(unsigned char *leaf, const unsigned char *sk, + const unsigned char *pub_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_thash_1( + leaf, sk, pub_seed, fors_leaf_addr); +} + +static void fors_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t fors_tree_addr[8]) { + uint32_t fors_leaf_addr[8] = {0}; + + /* Only copy the parts that must be kept in fors_leaf_addr. */ + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_copy_keypair_addr( + fors_leaf_addr, fors_tree_addr); + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_type( + fors_leaf_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_tree_index( + fors_leaf_addr, addr_idx); + + fors_gen_sk(leaf, sk_seed, fors_leaf_addr); + fors_sk_to_leaf(leaf, leaf, pub_seed, fors_leaf_addr); +} + +/** + * Interprets m as SPX_FORS_HEIGHT-bit unsigned integers. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + * Assumes indices has space for SPX_FORS_TREES integers. + */ +static void message_to_indices(uint32_t *indices, const unsigned char *m) { + unsigned int i, j; + unsigned int offset = 0; + + for (i = 0; i < SPX_FORS_TREES; i++) { + indices[i] = 0; + for (j = 0; j < SPX_FORS_HEIGHT; j++) { + indices[i] ^= (((uint32_t)m[offset >> 3] >> (offset & 0x7)) & 0x1) << j; + offset++; + } + } +} + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Include the secret key part that produces the selected leaf node. */ + fors_gen_sk(sig, sk_seed, fors_tree_addr); + sig += SPX_N; + + /* Compute the authentication path for this leaf node. */ + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_treehash_FORS_HEIGHT( + roots + i * SPX_N, sig, sk_seed, pub_seed, + indices[i], idx_offset, fors_gen_leaf, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + unsigned char leaf[SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Derive the leaf from the included secret key part. */ + fors_sk_to_leaf(leaf, sig, pub_seed, fors_tree_addr); + sig += SPX_N; + + /* Derive the corresponding root node of this tree. */ + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_compute_root( + roots + i * SPX_N, leaf, indices[i], idx_offset, sig, + SPX_FORS_HEIGHT, pub_seed, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} diff --git a/crypto_sign/sphincs-haraka-192s-robust/clean/fors.h b/crypto_sign/sphincs-haraka-192s-robust/clean/fors.h new file mode 100644 index 00000000..d5a30a0d --- /dev/null +++ b/crypto_sign/sphincs-haraka-192s-robust/clean/fors.h @@ -0,0 +1,30 @@ +#ifndef SPX_FORS_H +#define SPX_FORS_H + +#include + +#include "params.h" + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]); + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-haraka-192s-robust/clean/haraka.c b/crypto_sign/sphincs-haraka-192s-robust/clean/haraka.c new file mode 100644 index 00000000..cd6045a8 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192s-robust/clean/haraka.c @@ -0,0 +1,965 @@ +/* + * Constant time implementation of the Haraka hash function. + * + * The bit-sliced implementation of the AES round functions are + * based on the AES implementation in BearSSL written + * by Thomas Pornin + */ + +#include +#include +#include + +#include "haraka.h" + +#define HARAKAS_RATE 32 + +static const uint64_t haraka512_rc64[10][8] = { + {0x24cf0ab9086f628b, 0xbdd6eeecc83b8382, 0xd96fb0306cdad0a7, 0xaace082ac8f95f89, 0x449d8e8870d7041f, 0x49bb2f80b2b3e2f8, 0x0569ae98d93bb258, 0x23dc9691e7d6a4b1}, + {0xd8ba10ede0fe5b6e, 0x7ecf7dbe424c7b8e, 0x6ea9949c6df62a31, 0xbf3f3c97ec9c313e, 0x241d03a196a1861e, 0xead3a51116e5a2ea, 0x77d479fcad9574e3, 0x18657a1af894b7a0}, + {0x10671e1a7f595522, 0xd9a00ff675d28c7b, 0x2f1edf0d2b9ba661, 0xb8ff58b8e3de45f9, 0xee29261da9865c02, 0xd1532aa4b50bdf43, 0x8bf858159b231bb1, 0xdf17439d22d4f599}, + {0xdd4b2f0870b918c0, 0x757a81f3b39b1bb6, 0x7a5c556898952e3f, 0x7dd70a16d915d87a, 0x3ae61971982b8301, 0xc3ab319e030412be, 0x17c0033ac094a8cb, 0x5a0630fc1a8dc4ef}, + {0x17708988c1632f73, 0xf92ddae090b44f4f, 0x11ac0285c43aa314, 0x509059941936b8ba, 0xd03e152fa2ce9b69, 0x3fbcbcb63a32998b, 0x6204696d692254f7, 0x915542ed93ec59b4}, + {0xf4ed94aa8879236e, 0xff6cb41cd38e03c0, 0x069b38602368aeab, 0x669495b820f0ddba, 0xf42013b1b8bf9e3d, 0xcf935efe6439734d, 0xbc1dcf42ca29e3f8, 0x7e6d3ed29f78ad67}, + {0xf3b0f6837ffcddaa, 0x3a76faef934ddf41, 0xcec7ae583a9c8e35, 0xe4dd18c68f0260af, 0x2c0e5df1ad398eaa, 0x478df5236ae22e8c, 0xfb944c46fe865f39, 0xaa48f82f028132ba}, + {0x231b9ae2b76aca77, 0x292a76a712db0b40, 0x5850625dc8134491, 0x73137dd469810fb5, 0x8a12a6a202a474fd, 0xd36fd9daa78bdb80, 0xb34c5e733505706f, 0xbaf1cdca818d9d96}, + {0x2e99781335e8c641, 0xbddfe5cce47d560e, 0xf74e9bf32e5e040c, 0x1d7a709d65996be9, 0x670df36a9cf66cdd, 0xd05ef84a176a2875, 0x0f888e828cb1c44e, 0x1a79e9c9727b052c}, + {0x83497348628d84de, 0x2e9387d51f22a754, 0xb000068da2f852d6, 0x378c9e1190fd6fe5, 0x870027c316de7293, 0xe51a9d4462e047bb, 0x90ecf7f8c6251195, 0x655953bfbed90a9c}, +}; + +static uint64_t tweaked512_rc64[10][8]; +static uint32_t tweaked256_rc32[10][8]; +static uint32_t tweaked256_rc32_sseed[10][8]; + +static inline uint32_t br_dec32le(const unsigned char *src) { + return (uint32_t)src[0] + | ((uint32_t)src[1] << 8) + | ((uint32_t)src[2] << 16) + | ((uint32_t)src[3] << 24); +} + +static void br_range_dec32le(uint32_t *v, size_t num, const unsigned char *src) { + while (num-- > 0) { + *v ++ = br_dec32le(src); + src += 4; + } +} + +static inline void br_enc32le(unsigned char *dst, uint32_t x) { + dst[0] = (unsigned char)x; + dst[1] = (unsigned char)(x >> 8); + dst[2] = (unsigned char)(x >> 16); + dst[3] = (unsigned char)(x >> 24); +} + + +static void br_range_enc32le(unsigned char *dst, const uint32_t *v, size_t num) { + while (num-- > 0) { + br_enc32le(dst, *v ++); + dst += 4; + } +} + +static void br_aes_ct64_bitslice_Sbox(uint64_t *q) { + /* + * This S-box implementation is a straightforward translation of + * the circuit described by Boyar and Peralta in "A new + * combinational logic minimization technique with applications + * to cryptology" (https://eprint.iacr.org/2009/191.pdf). + * + * Note that variables x* (input) and s* (output) are numbered + * in "reverse" order (x0 is the high bit, x7 is the low bit). + */ + + uint64_t x0, x1, x2, x3, x4, x5, x6, x7; + uint64_t y1, y2, y3, y4, y5, y6, y7, y8, y9; + uint64_t y10, y11, y12, y13, y14, y15, y16, y17, y18, y19; + uint64_t y20, y21; + uint64_t z0, z1, z2, z3, z4, z5, z6, z7, z8, z9; + uint64_t z10, z11, z12, z13, z14, z15, z16, z17; + uint64_t t0, t1, t2, t3, t4, t5, t6, t7, t8, t9; + uint64_t t10, t11, t12, t13, t14, t15, t16, t17, t18, t19; + uint64_t t20, t21, t22, t23, t24, t25, t26, t27, t28, t29; + uint64_t t30, t31, t32, t33, t34, t35, t36, t37, t38, t39; + uint64_t t40, t41, t42, t43, t44, t45, t46, t47, t48, t49; + uint64_t t50, t51, t52, t53, t54, t55, t56, t57, t58, t59; + uint64_t t60, t61, t62, t63, t64, t65, t66, t67; + uint64_t s0, s1, s2, s3, s4, s5, s6, s7; + + x0 = q[7]; + x1 = q[6]; + x2 = q[5]; + x3 = q[4]; + x4 = q[3]; + x5 = q[2]; + x6 = q[1]; + x7 = q[0]; + + /* + * Top linear transformation. + */ + y14 = x3 ^ x5; + y13 = x0 ^ x6; + y9 = x0 ^ x3; + y8 = x0 ^ x5; + t0 = x1 ^ x2; + y1 = t0 ^ x7; + y4 = y1 ^ x3; + y12 = y13 ^ y14; + y2 = y1 ^ x0; + y5 = y1 ^ x6; + y3 = y5 ^ y8; + t1 = x4 ^ y12; + y15 = t1 ^ x5; + y20 = t1 ^ x1; + y6 = y15 ^ x7; + y10 = y15 ^ t0; + y11 = y20 ^ y9; + y7 = x7 ^ y11; + y17 = y10 ^ y11; + y19 = y10 ^ y8; + y16 = t0 ^ y11; + y21 = y13 ^ y16; + y18 = x0 ^ y16; + + /* + * Non-linear section. + */ + t2 = y12 & y15; + t3 = y3 & y6; + t4 = t3 ^ t2; + t5 = y4 & x7; + t6 = t5 ^ t2; + t7 = y13 & y16; + t8 = y5 & y1; + t9 = t8 ^ t7; + t10 = y2 & y7; + t11 = t10 ^ t7; + t12 = y9 & y11; + t13 = y14 & y17; + t14 = t13 ^ t12; + t15 = y8 & y10; + t16 = t15 ^ t12; + t17 = t4 ^ t14; + t18 = t6 ^ t16; + t19 = t9 ^ t14; + t20 = t11 ^ t16; + t21 = t17 ^ y20; + t22 = t18 ^ y19; + t23 = t19 ^ y21; + t24 = t20 ^ y18; + + t25 = t21 ^ t22; + t26 = t21 & t23; + t27 = t24 ^ t26; + t28 = t25 & t27; + t29 = t28 ^ t22; + t30 = t23 ^ t24; + t31 = t22 ^ t26; + t32 = t31 & t30; + t33 = t32 ^ t24; + t34 = t23 ^ t33; + t35 = t27 ^ t33; + t36 = t24 & t35; + t37 = t36 ^ t34; + t38 = t27 ^ t36; + t39 = t29 & t38; + t40 = t25 ^ t39; + + t41 = t40 ^ t37; + t42 = t29 ^ t33; + t43 = t29 ^ t40; + t44 = t33 ^ t37; + t45 = t42 ^ t41; + z0 = t44 & y15; + z1 = t37 & y6; + z2 = t33 & x7; + z3 = t43 & y16; + z4 = t40 & y1; + z5 = t29 & y7; + z6 = t42 & y11; + z7 = t45 & y17; + z8 = t41 & y10; + z9 = t44 & y12; + z10 = t37 & y3; + z11 = t33 & y4; + z12 = t43 & y13; + z13 = t40 & y5; + z14 = t29 & y2; + z15 = t42 & y9; + z16 = t45 & y14; + z17 = t41 & y8; + + /* + * Bottom linear transformation. + */ + t46 = z15 ^ z16; + t47 = z10 ^ z11; + t48 = z5 ^ z13; + t49 = z9 ^ z10; + t50 = z2 ^ z12; + t51 = z2 ^ z5; + t52 = z7 ^ z8; + t53 = z0 ^ z3; + t54 = z6 ^ z7; + t55 = z16 ^ z17; + t56 = z12 ^ t48; + t57 = t50 ^ t53; + t58 = z4 ^ t46; + t59 = z3 ^ t54; + t60 = t46 ^ t57; + t61 = z14 ^ t57; + t62 = t52 ^ t58; + t63 = t49 ^ t58; + t64 = z4 ^ t59; + t65 = t61 ^ t62; + t66 = z1 ^ t63; + s0 = t59 ^ t63; + s6 = t56 ^ ~t62; + s7 = t48 ^ ~t60; + t67 = t64 ^ t65; + s3 = t53 ^ t66; + s4 = t51 ^ t66; + s5 = t47 ^ t65; + s1 = t64 ^ ~s3; + s2 = t55 ^ ~t67; + + q[7] = s0; + q[6] = s1; + q[5] = s2; + q[4] = s3; + q[3] = s4; + q[2] = s5; + q[1] = s6; + q[0] = s7; +} + +static void br_aes_ct_bitslice_Sbox(uint32_t *q) { + /* + * This S-box implementation is a straightforward translation of + * the circuit described by Boyar and Peralta in "A new + * combinational logic minimization technique with applications + * to cryptology" (https://eprint.iacr.org/2009/191.pdf). + * + * Note that variables x* (input) and s* (output) are numbered + * in "reverse" order (x0 is the high bit, x7 is the low bit). + */ + + uint32_t x0, x1, x2, x3, x4, x5, x6, x7; + uint32_t y1, y2, y3, y4, y5, y6, y7, y8, y9; + uint32_t y10, y11, y12, y13, y14, y15, y16, y17, y18, y19; + uint32_t y20, y21; + uint32_t z0, z1, z2, z3, z4, z5, z6, z7, z8, z9; + uint32_t z10, z11, z12, z13, z14, z15, z16, z17; + uint32_t t0, t1, t2, t3, t4, t5, t6, t7, t8, t9; + uint32_t t10, t11, t12, t13, t14, t15, t16, t17, t18, t19; + uint32_t t20, t21, t22, t23, t24, t25, t26, t27, t28, t29; + uint32_t t30, t31, t32, t33, t34, t35, t36, t37, t38, t39; + uint32_t t40, t41, t42, t43, t44, t45, t46, t47, t48, t49; + uint32_t t50, t51, t52, t53, t54, t55, t56, t57, t58, t59; + uint32_t t60, t61, t62, t63, t64, t65, t66, t67; + uint32_t s0, s1, s2, s3, s4, s5, s6, s7; + + x0 = q[7]; + x1 = q[6]; + x2 = q[5]; + x3 = q[4]; + x4 = q[3]; + x5 = q[2]; + x6 = q[1]; + x7 = q[0]; + + /* + * Top linear transformation. + */ + y14 = x3 ^ x5; + y13 = x0 ^ x6; + y9 = x0 ^ x3; + y8 = x0 ^ x5; + t0 = x1 ^ x2; + y1 = t0 ^ x7; + y4 = y1 ^ x3; + y12 = y13 ^ y14; + y2 = y1 ^ x0; + y5 = y1 ^ x6; + y3 = y5 ^ y8; + t1 = x4 ^ y12; + y15 = t1 ^ x5; + y20 = t1 ^ x1; + y6 = y15 ^ x7; + y10 = y15 ^ t0; + y11 = y20 ^ y9; + y7 = x7 ^ y11; + y17 = y10 ^ y11; + y19 = y10 ^ y8; + y16 = t0 ^ y11; + y21 = y13 ^ y16; + y18 = x0 ^ y16; + + /* + * Non-linear section. + */ + t2 = y12 & y15; + t3 = y3 & y6; + t4 = t3 ^ t2; + t5 = y4 & x7; + t6 = t5 ^ t2; + t7 = y13 & y16; + t8 = y5 & y1; + t9 = t8 ^ t7; + t10 = y2 & y7; + t11 = t10 ^ t7; + t12 = y9 & y11; + t13 = y14 & y17; + t14 = t13 ^ t12; + t15 = y8 & y10; + t16 = t15 ^ t12; + t17 = t4 ^ t14; + t18 = t6 ^ t16; + t19 = t9 ^ t14; + t20 = t11 ^ t16; + t21 = t17 ^ y20; + t22 = t18 ^ y19; + t23 = t19 ^ y21; + t24 = t20 ^ y18; + + t25 = t21 ^ t22; + t26 = t21 & t23; + t27 = t24 ^ t26; + t28 = t25 & t27; + t29 = t28 ^ t22; + t30 = t23 ^ t24; + t31 = t22 ^ t26; + t32 = t31 & t30; + t33 = t32 ^ t24; + t34 = t23 ^ t33; + t35 = t27 ^ t33; + t36 = t24 & t35; + t37 = t36 ^ t34; + t38 = t27 ^ t36; + t39 = t29 & t38; + t40 = t25 ^ t39; + + t41 = t40 ^ t37; + t42 = t29 ^ t33; + t43 = t29 ^ t40; + t44 = t33 ^ t37; + t45 = t42 ^ t41; + z0 = t44 & y15; + z1 = t37 & y6; + z2 = t33 & x7; + z3 = t43 & y16; + z4 = t40 & y1; + z5 = t29 & y7; + z6 = t42 & y11; + z7 = t45 & y17; + z8 = t41 & y10; + z9 = t44 & y12; + z10 = t37 & y3; + z11 = t33 & y4; + z12 = t43 & y13; + z13 = t40 & y5; + z14 = t29 & y2; + z15 = t42 & y9; + z16 = t45 & y14; + z17 = t41 & y8; + + /* + * Bottom linear transformation. + */ + t46 = z15 ^ z16; + t47 = z10 ^ z11; + t48 = z5 ^ z13; + t49 = z9 ^ z10; + t50 = z2 ^ z12; + t51 = z2 ^ z5; + t52 = z7 ^ z8; + t53 = z0 ^ z3; + t54 = z6 ^ z7; + t55 = z16 ^ z17; + t56 = z12 ^ t48; + t57 = t50 ^ t53; + t58 = z4 ^ t46; + t59 = z3 ^ t54; + t60 = t46 ^ t57; + t61 = z14 ^ t57; + t62 = t52 ^ t58; + t63 = t49 ^ t58; + t64 = z4 ^ t59; + t65 = t61 ^ t62; + t66 = z1 ^ t63; + s0 = t59 ^ t63; + s6 = t56 ^ ~t62; + s7 = t48 ^ ~t60; + t67 = t64 ^ t65; + s3 = t53 ^ t66; + s4 = t51 ^ t66; + s5 = t47 ^ t65; + s1 = t64 ^ ~s3; + s2 = t55 ^ ~t67; + + q[7] = s0; + q[6] = s1; + q[5] = s2; + q[4] = s3; + q[3] = s4; + q[2] = s5; + q[1] = s6; + q[0] = s7; +} + +static void br_aes_ct_ortho(uint32_t *q) { +#define SWAPN_32(cl, ch, s, x, y) do { \ + uint32_t a, b; \ + a = (x); \ + b = (y); \ + (x) = (a & (uint32_t)(cl)) | ((b & (uint32_t)(cl)) << (s)); \ + (y) = ((a & (uint32_t)(ch)) >> (s)) | (b & (uint32_t)(ch)); \ + } while (0) + +#define SWAP2_32(x, y) SWAPN_32(0x55555555, 0xAAAAAAAA, 1, x, y) +#define SWAP4_32(x, y) SWAPN_32(0x33333333, 0xCCCCCCCC, 2, x, y) +#define SWAP8_32(x, y) SWAPN_32(0x0F0F0F0F, 0xF0F0F0F0, 4, x, y) + + SWAP2_32(q[0], q[1]); + SWAP2_32(q[2], q[3]); + SWAP2_32(q[4], q[5]); + SWAP2_32(q[6], q[7]); + + SWAP4_32(q[0], q[2]); + SWAP4_32(q[1], q[3]); + SWAP4_32(q[4], q[6]); + SWAP4_32(q[5], q[7]); + + SWAP8_32(q[0], q[4]); + SWAP8_32(q[1], q[5]); + SWAP8_32(q[2], q[6]); + SWAP8_32(q[3], q[7]); +} + +static inline void add_round_key32(uint32_t *q, const uint32_t *sk) { + q[0] ^= sk[0]; + q[1] ^= sk[1]; + q[2] ^= sk[2]; + q[3] ^= sk[3]; + q[4] ^= sk[4]; + q[5] ^= sk[5]; + q[6] ^= sk[6]; + q[7] ^= sk[7]; +} + +static inline void shift_rows32(uint32_t *q) { + int i; + + for (i = 0; i < 8; i++) { + uint32_t x; + + x = q[i]; + q[i] = (x & 0x000000FF) + | ((x & 0x0000FC00) >> 2) | ((x & 0x00000300) << 6) + | ((x & 0x00F00000) >> 4) | ((x & 0x000F0000) << 4) + | ((x & 0xC0000000) >> 6) | ((x & 0x3F000000) << 2); + } +} + +static inline uint32_t rotr16(uint32_t x) { + return (x << 16) | (x >> 16); +} + +static inline void mix_columns32(uint32_t *q) { + uint32_t q0, q1, q2, q3, q4, q5, q6, q7; + uint32_t r0, r1, r2, r3, r4, r5, r6, r7; + + q0 = q[0]; + q1 = q[1]; + q2 = q[2]; + q3 = q[3]; + q4 = q[4]; + q5 = q[5]; + q6 = q[6]; + q7 = q[7]; + r0 = (q0 >> 8) | (q0 << 24); + r1 = (q1 >> 8) | (q1 << 24); + r2 = (q2 >> 8) | (q2 << 24); + r3 = (q3 >> 8) | (q3 << 24); + r4 = (q4 >> 8) | (q4 << 24); + r5 = (q5 >> 8) | (q5 << 24); + r6 = (q6 >> 8) | (q6 << 24); + r7 = (q7 >> 8) | (q7 << 24); + + q[0] = q7 ^ r7 ^ r0 ^ rotr16(q0 ^ r0); + q[1] = q0 ^ r0 ^ q7 ^ r7 ^ r1 ^ rotr16(q1 ^ r1); + q[2] = q1 ^ r1 ^ r2 ^ rotr16(q2 ^ r2); + q[3] = q2 ^ r2 ^ q7 ^ r7 ^ r3 ^ rotr16(q3 ^ r3); + q[4] = q3 ^ r3 ^ q7 ^ r7 ^ r4 ^ rotr16(q4 ^ r4); + q[5] = q4 ^ r4 ^ r5 ^ rotr16(q5 ^ r5); + q[6] = q5 ^ r5 ^ r6 ^ rotr16(q6 ^ r6); + q[7] = q6 ^ r6 ^ r7 ^ rotr16(q7 ^ r7); +} + +static void br_aes_ct64_ortho(uint64_t *q) { +#define SWAPN(cl, ch, s, x, y) do { \ + uint64_t a, b; \ + a = (x); \ + b = (y); \ + (x) = (a & (uint64_t)(cl)) | ((b & (uint64_t)(cl)) << (s)); \ + (y) = ((a & (uint64_t)(ch)) >> (s)) | (b & (uint64_t)(ch)); \ + } while (0) + +#define SWAP2(x, y) SWAPN(0x5555555555555555, 0xAAAAAAAAAAAAAAAA, 1, x, y) +#define SWAP4(x, y) SWAPN(0x3333333333333333, 0xCCCCCCCCCCCCCCCC, 2, x, y) +#define SWAP8(x, y) SWAPN(0x0F0F0F0F0F0F0F0F, 0xF0F0F0F0F0F0F0F0, 4, x, y) + + SWAP2(q[0], q[1]); + SWAP2(q[2], q[3]); + SWAP2(q[4], q[5]); + SWAP2(q[6], q[7]); + + SWAP4(q[0], q[2]); + SWAP4(q[1], q[3]); + SWAP4(q[4], q[6]); + SWAP4(q[5], q[7]); + + SWAP8(q[0], q[4]); + SWAP8(q[1], q[5]); + SWAP8(q[2], q[6]); + SWAP8(q[3], q[7]); +} + + +static void br_aes_ct64_interleave_in(uint64_t *q0, uint64_t *q1, const uint32_t *w) { + uint64_t x0, x1, x2, x3; + + x0 = w[0]; + x1 = w[1]; + x2 = w[2]; + x3 = w[3]; + x0 |= (x0 << 16); + x1 |= (x1 << 16); + x2 |= (x2 << 16); + x3 |= (x3 << 16); + x0 &= (uint64_t)0x0000FFFF0000FFFF; + x1 &= (uint64_t)0x0000FFFF0000FFFF; + x2 &= (uint64_t)0x0000FFFF0000FFFF; + x3 &= (uint64_t)0x0000FFFF0000FFFF; + x0 |= (x0 << 8); + x1 |= (x1 << 8); + x2 |= (x2 << 8); + x3 |= (x3 << 8); + x0 &= (uint64_t)0x00FF00FF00FF00FF; + x1 &= (uint64_t)0x00FF00FF00FF00FF; + x2 &= (uint64_t)0x00FF00FF00FF00FF; + x3 &= (uint64_t)0x00FF00FF00FF00FF; + *q0 = x0 | (x2 << 8); + *q1 = x1 | (x3 << 8); +} + + +static void br_aes_ct64_interleave_out(uint32_t *w, uint64_t q0, uint64_t q1) { + uint64_t x0, x1, x2, x3; + + x0 = q0 & (uint64_t)0x00FF00FF00FF00FF; + x1 = q1 & (uint64_t)0x00FF00FF00FF00FF; + x2 = (q0 >> 8) & (uint64_t)0x00FF00FF00FF00FF; + x3 = (q1 >> 8) & (uint64_t)0x00FF00FF00FF00FF; + x0 |= (x0 >> 8); + x1 |= (x1 >> 8); + x2 |= (x2 >> 8); + x3 |= (x3 >> 8); + x0 &= (uint64_t)0x0000FFFF0000FFFF; + x1 &= (uint64_t)0x0000FFFF0000FFFF; + x2 &= (uint64_t)0x0000FFFF0000FFFF; + x3 &= (uint64_t)0x0000FFFF0000FFFF; + w[0] = (uint32_t)x0 | (uint32_t)(x0 >> 16); + w[1] = (uint32_t)x1 | (uint32_t)(x1 >> 16); + w[2] = (uint32_t)x2 | (uint32_t)(x2 >> 16); + w[3] = (uint32_t)x3 | (uint32_t)(x3 >> 16); +} + +static inline void add_round_key(uint64_t *q, const uint64_t *sk) { + q[0] ^= sk[0]; + q[1] ^= sk[1]; + q[2] ^= sk[2]; + q[3] ^= sk[3]; + q[4] ^= sk[4]; + q[5] ^= sk[5]; + q[6] ^= sk[6]; + q[7] ^= sk[7]; +} + +static inline void shift_rows(uint64_t *q) { + int i; + + for (i = 0; i < 8; i++) { + uint64_t x; + + x = q[i]; + q[i] = (x & (uint64_t)0x000000000000FFFF) + | ((x & (uint64_t)0x00000000FFF00000) >> 4) + | ((x & (uint64_t)0x00000000000F0000) << 12) + | ((x & (uint64_t)0x0000FF0000000000) >> 8) + | ((x & (uint64_t)0x000000FF00000000) << 8) + | ((x & (uint64_t)0xF000000000000000) >> 12) + | ((x & (uint64_t)0x0FFF000000000000) << 4); + } +} + +static inline uint64_t rotr32(uint64_t x) { + return (x << 32) | (x >> 32); +} + +static inline void mix_columns(uint64_t *q) { + uint64_t q0, q1, q2, q3, q4, q5, q6, q7; + uint64_t r0, r1, r2, r3, r4, r5, r6, r7; + + q0 = q[0]; + q1 = q[1]; + q2 = q[2]; + q3 = q[3]; + q4 = q[4]; + q5 = q[5]; + q6 = q[6]; + q7 = q[7]; + r0 = (q0 >> 16) | (q0 << 48); + r1 = (q1 >> 16) | (q1 << 48); + r2 = (q2 >> 16) | (q2 << 48); + r3 = (q3 >> 16) | (q3 << 48); + r4 = (q4 >> 16) | (q4 << 48); + r5 = (q5 >> 16) | (q5 << 48); + r6 = (q6 >> 16) | (q6 << 48); + r7 = (q7 >> 16) | (q7 << 48); + + q[0] = q7 ^ r7 ^ r0 ^ rotr32(q0 ^ r0); + q[1] = q0 ^ r0 ^ q7 ^ r7 ^ r1 ^ rotr32(q1 ^ r1); + q[2] = q1 ^ r1 ^ r2 ^ rotr32(q2 ^ r2); + q[3] = q2 ^ r2 ^ q7 ^ r7 ^ r3 ^ rotr32(q3 ^ r3); + q[4] = q3 ^ r3 ^ q7 ^ r7 ^ r4 ^ rotr32(q4 ^ r4); + q[5] = q4 ^ r4 ^ r5 ^ rotr32(q5 ^ r5); + q[6] = q5 ^ r5 ^ r6 ^ rotr32(q6 ^ r6); + q[7] = q6 ^ r6 ^ r7 ^ rotr32(q7 ^ r7); +} + +static void interleave_constant(uint64_t *out, const unsigned char *in) { + uint32_t tmp_32_constant[16]; + int i; + + br_range_dec32le(tmp_32_constant, 16, in); + for (i = 0; i < 4; i++) { + br_aes_ct64_interleave_in(&out[i], &out[i + 4], tmp_32_constant + (i << 2)); + } + br_aes_ct64_ortho(out); +} + +static void interleave_constant32(uint32_t *out, const unsigned char *in) { + int i; + for (i = 0; i < 4; i++) { + out[2 * i] = br_dec32le(in + 4 * i); + out[2 * i + 1] = br_dec32le(in + 4 * i + 16); + } + br_aes_ct_ortho(out); +} + +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_tweak_constants( + const unsigned char *pk_seed, const unsigned char *sk_seed, + unsigned long long seed_length) { + unsigned char buf[40 * 16]; + int i; + + /* Use the standard constants to generate tweaked ones. */ + memcpy((uint8_t *)tweaked512_rc64, (uint8_t *)haraka512_rc64, 40 * 16); + + /* Constants for sk.seed */ + if (sk_seed != NULL) { + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_haraka_S( + buf, 40 * 16, sk_seed, seed_length); + + /* Interleave constants */ + for (i = 0; i < 10; i++) { + interleave_constant32(tweaked256_rc32_sseed[i], buf + 32 * i); + } + } + + /* Constants for pk.seed */ + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_haraka_S( + buf, 40 * 16, pk_seed, seed_length); + for (i = 0; i < 10; i++) { + interleave_constant32(tweaked256_rc32[i], buf + 32 * i); + interleave_constant(tweaked512_rc64[i], buf + 64 * i); + } +} + +static void haraka_S_absorb(unsigned char *s, + const unsigned char *m, unsigned long long mlen, + unsigned char p) { + unsigned long long i; + unsigned char t[HARAKAS_RATE]; + + while (mlen >= HARAKAS_RATE) { + /* XOR block to state */ + for (i = 0; i < HARAKAS_RATE; ++i) { + s[i] ^= m[i]; + } + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_haraka512_perm(s, s); + mlen -= HARAKAS_RATE; + m += HARAKAS_RATE; + } + + for (i = 0; i < HARAKAS_RATE; ++i) { + t[i] = 0; + } + for (i = 0; i < mlen; ++i) { + t[i] = m[i]; + } + t[i] = p; + t[HARAKAS_RATE - 1] |= 128; + for (i = 0; i < HARAKAS_RATE; ++i) { + s[i] ^= t[i]; + } +} + +static void haraka_S_squeezeblocks(unsigned char *h, unsigned long long nblocks, + unsigned char *s) { + while (nblocks > 0) { + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_haraka512_perm(s, s); + memcpy(h, s, HARAKAS_RATE); + h += HARAKAS_RATE; + nblocks--; + } +} + +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_haraka_S_inc_init(uint8_t *s_inc) { + size_t i; + + for (i = 0; i < 64; i++) { + s_inc[i] = 0; + } + s_inc[64] = 0; +} + +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_haraka_S_inc_absorb(uint8_t *s_inc, const uint8_t *m, size_t mlen) { + size_t i; + + /* Recall that s_inc[64] is the non-absorbed bytes xored into the state */ + while (mlen + s_inc[64] >= HARAKAS_RATE) { + for (i = 0; i < (size_t)(HARAKAS_RATE - s_inc[64]); i++) { + /* Take the i'th byte from message + xor with the s_inc[64] + i'th byte of the state */ + s_inc[s_inc[64] + i] ^= m[i]; + } + mlen -= (size_t)(HARAKAS_RATE - s_inc[64]); + m += HARAKAS_RATE - s_inc[64]; + s_inc[64] = 0; + + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_haraka512_perm(s_inc, s_inc); + } + + for (i = 0; i < mlen; i++) { + s_inc[s_inc[64] + i] ^= m[i]; + } + s_inc[64] = (uint8_t)(mlen + s_inc[64]); +} + +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_haraka_S_inc_finalize(uint8_t *s_inc) { + /* After haraka_S_inc_absorb, we are guaranteed that s_inc[64] < HARAKAS_RATE, + so we can always use one more byte for p in the current state. */ + s_inc[s_inc[64]] ^= 0x1F; + s_inc[HARAKAS_RATE - 1] ^= 128; + s_inc[64] = 0; +} + +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_haraka_S_inc_squeeze(uint8_t *out, size_t outlen, uint8_t *s_inc) { + uint8_t i; + + /* First consume any bytes we still have sitting around */ + for (i = 0; i < outlen && i < s_inc[64]; i++) { + /* There are s_inc[64] bytes left, so r - s_inc[64] is the first + available byte. We consume from there, i.e., up to r. */ + out[i] = s_inc[(HARAKAS_RATE - s_inc[64] + i)]; + } + out += i; + outlen -= i; + s_inc[64] = (uint8_t)(s_inc[64] - i); + + /* Then squeeze the remaining necessary blocks */ + while (outlen > 0) { + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_haraka512_perm(s_inc, s_inc); + + for (i = 0; i < outlen && i < HARAKAS_RATE; i++) { + out[i] = s_inc[i]; + } + out += i; + outlen -= i; + s_inc[64] = (uint8_t)(HARAKAS_RATE - i); + } +} + +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_haraka_S(unsigned char *out, unsigned long long outlen, const unsigned char *in, unsigned long long inlen) { + unsigned long long i; + unsigned char s[64]; + unsigned char d[32]; + + for (i = 0; i < 64; i++) { + s[i] = 0; + } + haraka_S_absorb(s, in, inlen, 0x1F); + + haraka_S_squeezeblocks(out, outlen / 32, s); + out += (outlen / 32) * 32; + + if (outlen % 32) { + haraka_S_squeezeblocks(d, 1, s); + for (i = 0; i < outlen % 32; i++) { + out[i] = d[i]; + } + } +} + +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_haraka512_perm(unsigned char *out, const unsigned char *in) { + uint32_t w[16]; + uint64_t q[8], tmp_q; + unsigned int i, j; + + br_range_dec32le(w, 16, in); + for (i = 0; i < 4; i++) { + br_aes_ct64_interleave_in(&q[i], &q[i + 4], w + (i << 2)); + } + br_aes_ct64_ortho(q); + + /* AES rounds */ + for (i = 0; i < 5; i++) { + for (j = 0; j < 2; j++) { + br_aes_ct64_bitslice_Sbox(q); + shift_rows(q); + mix_columns(q); + add_round_key(q, tweaked512_rc64[2 * i + j]); + } + /* Mix states */ + for (j = 0; j < 8; j++) { + tmp_q = q[j]; + q[j] = (tmp_q & 0x0001000100010001) << 5 | + (tmp_q & 0x0002000200020002) << 12 | + (tmp_q & 0x0004000400040004) >> 1 | + (tmp_q & 0x0008000800080008) << 6 | + (tmp_q & 0x0020002000200020) << 9 | + (tmp_q & 0x0040004000400040) >> 4 | + (tmp_q & 0x0080008000800080) << 3 | + (tmp_q & 0x2100210021002100) >> 5 | + (tmp_q & 0x0210021002100210) << 2 | + (tmp_q & 0x0800080008000800) << 4 | + (tmp_q & 0x1000100010001000) >> 12 | + (tmp_q & 0x4000400040004000) >> 10 | + (tmp_q & 0x8400840084008400) >> 3; + } + } + + br_aes_ct64_ortho(q); + for (i = 0; i < 4; i ++) { + br_aes_ct64_interleave_out(w + (i << 2), q[i], q[i + 4]); + } + br_range_enc32le(out, w, 16); +} + +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_haraka512(unsigned char *out, const unsigned char *in) { + int i; + + unsigned char buf[64]; + + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_haraka512_perm(buf, in); + /* Feed-forward */ + for (i = 0; i < 64; i++) { + buf[i] = buf[i] ^ in[i]; + } + + /* Truncated */ + memcpy(out, buf + 8, 8); + memcpy(out + 8, buf + 24, 8); + memcpy(out + 16, buf + 32, 8); + memcpy(out + 24, buf + 48, 8); +} + + +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_haraka256(unsigned char *out, const unsigned char *in) { + uint32_t q[8], tmp_q; + int i, j; + + for (i = 0; i < 4; i++) { + q[2 * i] = br_dec32le(in + 4 * i); + q[2 * i + 1] = br_dec32le(in + 4 * i + 16); + } + br_aes_ct_ortho(q); + + /* AES rounds */ + for (i = 0; i < 5; i++) { + for (j = 0; j < 2; j++) { + br_aes_ct_bitslice_Sbox(q); + shift_rows32(q); + mix_columns32(q); + add_round_key32(q, tweaked256_rc32[2 * i + j]); + } + + /* Mix states */ + for (j = 0; j < 8; j++) { + tmp_q = q[j]; + q[j] = (tmp_q & 0x81818181) | + (tmp_q & 0x02020202) << 1 | + (tmp_q & 0x04040404) << 2 | + (tmp_q & 0x08080808) << 3 | + (tmp_q & 0x10101010) >> 3 | + (tmp_q & 0x20202020) >> 2 | + (tmp_q & 0x40404040) >> 1; + } + } + + br_aes_ct_ortho(q); + for (i = 0; i < 4; i++) { + br_enc32le(out + 4 * i, q[2 * i]); + br_enc32le(out + 4 * i + 16, q[2 * i + 1]); + } + + for (i = 0; i < 32; i++) { + out[i] ^= in[i]; + } +} + +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_haraka256_sk(unsigned char *out, const unsigned char *in) { + uint32_t q[8], tmp_q; + int i, j; + + for (i = 0; i < 4; i++) { + q[2 * i] = br_dec32le(in + 4 * i); + q[2 * i + 1] = br_dec32le(in + 4 * i + 16); + } + br_aes_ct_ortho(q); + + /* AES rounds */ + for (i = 0; i < 5; i++) { + for (j = 0; j < 2; j++) { + br_aes_ct_bitslice_Sbox(q); + shift_rows32(q); + mix_columns32(q); + add_round_key32(q, tweaked256_rc32_sseed[2 * i + j]); + } + + /* Mix states */ + for (j = 0; j < 8; j++) { + tmp_q = q[j]; + q[j] = (tmp_q & 0x81818181) | + (tmp_q & 0x02020202) << 1 | + (tmp_q & 0x04040404) << 2 | + (tmp_q & 0x08080808) << 3 | + (tmp_q & 0x10101010) >> 3 | + (tmp_q & 0x20202020) >> 2 | + (tmp_q & 0x40404040) >> 1; + } + } + + br_aes_ct_ortho(q); + for (i = 0; i < 4; i++) { + br_enc32le(out + 4 * i, q[2 * i]); + br_enc32le(out + 4 * i + 16, q[2 * i + 1]); + } + + for (i = 0; i < 32; i++) { + out[i] ^= in[i]; + } +} diff --git a/crypto_sign/sphincs-haraka-192s-robust/clean/haraka.h b/crypto_sign/sphincs-haraka-192s-robust/clean/haraka.h new file mode 100644 index 00000000..01772b2e --- /dev/null +++ b/crypto_sign/sphincs-haraka-192s-robust/clean/haraka.h @@ -0,0 +1,30 @@ +#ifndef SPX_HARAKA_H +#define SPX_HARAKA_H + +/* Tweak constants with seed */ +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_tweak_constants( + const unsigned char *pk_seed, const unsigned char *sk_seed, + unsigned long long seed_length); + +/* Haraka Sponge */ +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_haraka_S_inc_init(uint8_t *s_inc); +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_haraka_S_inc_absorb(uint8_t *s_inc, const uint8_t *m, size_t mlen); +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_haraka_S_inc_finalize(uint8_t *s_inc); +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_haraka_S_inc_squeeze(uint8_t *out, size_t outlen, uint8_t *s_inc); +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_haraka_S( + unsigned char *out, unsigned long long outlen, + const unsigned char *in, unsigned long long inlen); + +/* Applies the 512-bit Haraka permutation to in. */ +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_haraka512_perm(unsigned char *out, const unsigned char *in); + +/* Implementation of Haraka-512 */ +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_haraka512(unsigned char *out, const unsigned char *in); + +/* Implementation of Haraka-256 */ +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_haraka256(unsigned char *out, const unsigned char *in); + +/* Implementation of Haraka-256 using sk.seed constants */ +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_haraka256_sk(unsigned char *out, const unsigned char *in); + +#endif diff --git a/crypto_sign/sphincs-haraka-192s-robust/clean/hash.h b/crypto_sign/sphincs-haraka-192s-robust/clean/hash.h new file mode 100644 index 00000000..7d8c166b --- /dev/null +++ b/crypto_sign/sphincs-haraka-192s-robust/clean/hash.h @@ -0,0 +1,22 @@ +#ifndef SPX_HASH_H +#define SPX_HASH_H + +#include + +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed); + +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen); + +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen); + +#endif diff --git a/crypto_sign/sphincs-haraka-192s-robust/clean/hash_haraka.c b/crypto_sign/sphincs-haraka-192s-robust/clean/hash_haraka.c new file mode 100644 index 00000000..ccf98f67 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192s-robust/clean/hash_haraka.c @@ -0,0 +1,86 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "utils.h" + +#include "haraka.h" + +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed) { + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_tweak_constants(pub_seed, sk_seed, SPX_N); +} + +/* + * Computes PRF(key, addr), given a secret key of SPX_N bytes and an address + */ +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]) { + unsigned char buf[SPX_ADDR_BYTES]; + /* Since SPX_N may be smaller than 32, we need a temporary buffer. */ + unsigned char outbuf[32]; + + (void)key; /* Suppress an 'unused parameter' warning. */ + + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_addr_to_bytes(buf, addr); + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_haraka256_sk(outbuf, buf); + memcpy(out, outbuf, SPX_N); +} + +/** + * Computes the message-dependent randomness R, using a secret seed and an + * optional randomization value as well as the message. + */ +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen) { + uint8_t s_inc[65]; + + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_haraka_S_inc_init(s_inc); + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_haraka_S_inc_absorb(s_inc, sk_prf, SPX_N); + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_haraka_S_inc_absorb(s_inc, optrand, SPX_N); + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_haraka_S_inc_absorb(s_inc, m, mlen); + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_haraka_S_inc_finalize(s_inc); + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_haraka_S_inc_squeeze(R, SPX_N, s_inc); +} + +/** + * Computes the message hash using R, the public key, and the message. + * Outputs the message digest and the index of the leaf. The index is split in + * the tree index and the leaf index, for convenient copying to an address. + */ +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen) { +#define SPX_TREE_BITS (SPX_TREE_HEIGHT * (SPX_D - 1)) +#define SPX_TREE_BYTES ((SPX_TREE_BITS + 7) / 8) +#define SPX_LEAF_BITS SPX_TREE_HEIGHT +#define SPX_LEAF_BYTES ((SPX_LEAF_BITS + 7) / 8) +#define SPX_DGST_BYTES (SPX_FORS_MSG_BYTES + SPX_TREE_BYTES + SPX_LEAF_BYTES) + + unsigned char buf[SPX_DGST_BYTES]; + unsigned char *bufp = buf; + uint8_t s_inc[65]; + + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_haraka_S_inc_init(s_inc); + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_haraka_S_inc_absorb(s_inc, R, SPX_N); + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_haraka_S_inc_absorb(s_inc, pk, SPX_PK_BYTES); + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_haraka_S_inc_absorb(s_inc, m, mlen); + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_haraka_S_inc_finalize(s_inc); + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_haraka_S_inc_squeeze(buf, SPX_DGST_BYTES, s_inc); + + memcpy(digest, bufp, SPX_FORS_MSG_BYTES); + bufp += SPX_FORS_MSG_BYTES; + + *tree = PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_bytes_to_ull(bufp, SPX_TREE_BYTES); + *tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS); + bufp += SPX_TREE_BYTES; + + *leaf_idx = (uint32_t)PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_bytes_to_ull( + bufp, SPX_LEAF_BYTES); + *leaf_idx &= (~(uint32_t)0) >> (32 - SPX_LEAF_BITS); +} diff --git a/crypto_sign/sphincs-haraka-192s-robust/clean/params.h b/crypto_sign/sphincs-haraka-192s-robust/clean/params.h new file mode 100644 index 00000000..483b5374 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192s-robust/clean/params.h @@ -0,0 +1,53 @@ +#ifndef SPX_PARAMS_H +#define SPX_PARAMS_H + +/* Hash output length in bytes. */ +#define SPX_N 24 +/* Height of the hypertree. */ +#define SPX_FULL_HEIGHT 64 +/* Number of subtree layer. */ +#define SPX_D 8 +/* FORS tree dimensions. */ +#define SPX_FORS_HEIGHT 16 +#define SPX_FORS_TREES 14 +/* Winternitz parameter, */ +#define SPX_WOTS_W 16 + +/* The hash function is defined by linking a different hash.c file, as opposed + to setting a #define constant. */ + +/* For clarity */ +#define SPX_ADDR_BYTES 32 + +/* WOTS parameters. */ +#define SPX_WOTS_LOGW 4 + +#define SPX_WOTS_LEN1 (8 * SPX_N / SPX_WOTS_LOGW) + +/* SPX_WOTS_LEN2 is floor(log(len_1 * (w - 1)) / log(w)) + 1; we precompute */ +#define SPX_WOTS_LEN2 3 + +#define SPX_WOTS_LEN (SPX_WOTS_LEN1 + SPX_WOTS_LEN2) +#define SPX_WOTS_BYTES (SPX_WOTS_LEN * SPX_N) +#define SPX_WOTS_PK_BYTES SPX_WOTS_BYTES + +/* Subtree size. */ +#define SPX_TREE_HEIGHT (SPX_FULL_HEIGHT / SPX_D) + +/* FORS parameters. */ +#define SPX_FORS_MSG_BYTES ((SPX_FORS_HEIGHT * SPX_FORS_TREES + 7) / 8) +#define SPX_FORS_BYTES ((SPX_FORS_HEIGHT + 1) * SPX_FORS_TREES * SPX_N) +#define SPX_FORS_PK_BYTES SPX_N + +/* Resulting SPX sizes. */ +#define SPX_BYTES (SPX_N + SPX_FORS_BYTES + SPX_D * SPX_WOTS_BYTES +\ + SPX_FULL_HEIGHT * SPX_N) +#define SPX_PK_BYTES (2 * SPX_N) +#define SPX_SK_BYTES (2 * SPX_N + SPX_PK_BYTES) + +/* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ +#define SPX_OPTRAND_BYTES 32 + +#endif diff --git a/crypto_sign/sphincs-haraka-192s-robust/clean/sign.c b/crypto_sign/sphincs-haraka-192s-robust/clean/sign.c new file mode 100644 index 00000000..359a6261 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192s-robust/clean/sign.c @@ -0,0 +1,344 @@ +#include +#include +#include + +#include "address.h" +#include "api.h" +#include "fors.h" +#include "hash.h" +#include "params.h" +#include "randombytes.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +/** + * Computes the leaf at a given address. First generates the WOTS key pair, + * then computes leaf by hashing horizontally. + */ +static void wots_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t tree_addr[8]) { + unsigned char pk[SPX_WOTS_BYTES]; + uint32_t wots_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_keypair_addr( + wots_addr, addr_idx); + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_wots_gen_pk( + pk, sk_seed, pub_seed, wots_addr); + + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_thash_WOTS_LEN( + leaf, pk, pub_seed, wots_pk_addr); +} + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_crypto_sign_secretkeybytes(void) { + return PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_CRYPTO_SECRETKEYBYTES; +} + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_crypto_sign_publickeybytes(void) { + return PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES; +} + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_crypto_sign_bytes(void) { + return PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_CRYPTO_BYTES; +} + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_crypto_sign_seedbytes(void) { + return PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_CRYPTO_SEEDBYTES; +} + +/* + * Generates an SPX key pair given a seed of length + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed) { + /* We do not need the auth path in key generation, but it simplifies the + code to have just one treehash routine that computes both root and path + in one function. */ + unsigned char auth_path[SPX_TREE_HEIGHT * SPX_N]; + uint32_t top_tree_addr[8] = {0}; + + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_layer_addr( + top_tree_addr, SPX_D - 1); + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_type( + top_tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */ + memcpy(sk, seed, PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_CRYPTO_SEEDBYTES); + + memcpy(pk, sk + 2 * SPX_N, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_initialize_hash_function(pk, sk); + + /* Compute root node of the top-most subtree. */ + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_treehash_TREE_HEIGHT( + sk + 3 * SPX_N, auth_path, sk, sk + 2 * SPX_N, 0, 0, + wots_gen_leaf, top_tree_addr); + + memcpy(pk + SPX_N, sk + 3 * SPX_N, SPX_N); + + return 0; +} + +/* + * Generates an SPX key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk) { + unsigned char seed[PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_CRYPTO_SEEDBYTES]; + randombytes(seed, PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_CRYPTO_SEEDBYTES); + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_crypto_sign_seed_keypair( + pk, sk, seed); + + return 0; +} + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + const unsigned char *sk_seed = sk; + const unsigned char *sk_prf = sk + SPX_N; + const unsigned char *pk = sk + 2 * SPX_N; + const unsigned char *pub_seed = pk; + + unsigned char optrand[SPX_N]; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char root[SPX_N]; + uint32_t i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_initialize_hash_function( + pub_seed, sk_seed); + + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ + randombytes(optrand, SPX_N); + /* Compute the digest randomization value. */ + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_gen_message_random( + sig, sk_prf, optrand, m, mlen); + + /* Derive the message digest and leaf index from R, PK and M. */ + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Sign the message hash using FORS. */ + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_fors_sign( + sig, root, mhash, sk_seed, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Compute a WOTS signature. */ + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_wots_sign( + sig, root, sk_seed, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the authentication path for the used WOTS leaf. */ + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_treehash_TREE_HEIGHT( + root, sig, sk_seed, pub_seed, idx_leaf, 0, + wots_gen_leaf, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + *siglen = SPX_BYTES; + + return 0; +} + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk) { + const unsigned char *pub_seed = pk; + const unsigned char *pub_root = pk + SPX_N; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char wots_pk[SPX_WOTS_BYTES]; + unsigned char root[SPX_N]; + unsigned char leaf[SPX_N]; + unsigned int i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + if (siglen != SPX_BYTES) { + return -1; + } + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_initialize_hash_function( + pub_seed, NULL); + + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + /* Derive the message digest and leaf index from R || PK || M. */ + /* The additional SPX_N is a result of the hash domain separator. */ + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + /* Layer correctly defaults to 0, so no need to set_layer_addr */ + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_fors_pk_from_sig( + root, sig, mhash, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + /* For each subtree.. */ + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + + /* The WOTS public key is only correct if the signature was correct. */ + /* Initially, root is the FORS pk, but on subsequent iterations it is + the root of the subtree below the currently processed subtree. */ + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_wots_pk_from_sig( + wots_pk, sig, root, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the leaf node using the WOTS public key. */ + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_thash_WOTS_LEN( + leaf, wots_pk, pub_seed, wots_pk_addr); + + /* Compute the root node of this subtree. */ + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_compute_root( + root, leaf, idx_leaf, 0, sig, SPX_TREE_HEIGHT, + pub_seed, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + /* Check if the root node equals the root node in the public key. */ + if (memcmp(root, pub_root, SPX_N) != 0) { + return -1; + } + + return 0; +} + + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + size_t siglen; + + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_crypto_sign_signature( + sm, &siglen, m, mlen, sk); + + memmove(sm + SPX_BYTES, m, mlen); + *smlen = siglen + mlen; + + return 0; +} + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk) { + /* The API caller does not necessarily know what size a signature should be + but SPHINCS+ signatures are always exactly SPX_BYTES. */ + if (smlen < SPX_BYTES) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + *mlen = smlen - SPX_BYTES; + + if (PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_crypto_sign_verify( + sm, SPX_BYTES, sm + SPX_BYTES, *mlen, pk)) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + /* If verification was successful, move the message to the right place. */ + memmove(m, sm + SPX_BYTES, *mlen); + + return 0; +} diff --git a/crypto_sign/sphincs-haraka-192s-robust/clean/thash.h b/crypto_sign/sphincs-haraka-192s-robust/clean/thash.h new file mode 100644 index 00000000..7f0ccc94 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192s-robust/clean/thash.h @@ -0,0 +1,22 @@ +#ifndef SPX_THASH_H +#define SPX_THASH_H + +#include + +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-haraka-192s-robust/clean/thash_haraka_robust.c b/crypto_sign/sphincs-haraka-192s-robust/clean/thash_haraka_robust.c new file mode 100644 index 00000000..d2541a34 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192s-robust/clean/thash_haraka_robust.c @@ -0,0 +1,88 @@ +#include +#include + +#include "address.h" +#include "params.h" +#include "thash.h" + +#include "haraka.h" + +/** + * Takes an array of inblocks concatenated arrays of SPX_N bytes. + */ +static void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_thash( + unsigned char *out, unsigned char *buf, + const unsigned char *in, unsigned int inblocks, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char *bitmask = buf + SPX_ADDR_BYTES; + unsigned char outbuf[32]; + unsigned char buf_tmp[64]; + unsigned int i; + + (void)pub_seed; /* Suppress an 'unused parameter' warning. */ + + if (inblocks == 1) { + /* F function */ + /* Since SPX_N may be smaller than 32, we need a temporary buffer. */ + memset(buf_tmp, 0, 64); + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_addr_to_bytes(buf_tmp, addr); + + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_haraka256(outbuf, buf_tmp); + for (i = 0; i < inblocks * SPX_N; i++) { + buf_tmp[SPX_ADDR_BYTES + i] = in[i] ^ outbuf[i]; + } + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_haraka512(outbuf, buf_tmp); + memcpy(out, outbuf, SPX_N); + } else { + /* All other tweakable hashes*/ + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_addr_to_bytes(buf, addr); + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_haraka_S( + bitmask, inblocks * SPX_N, buf, SPX_ADDR_BYTES); + + for (i = 0; i < inblocks * SPX_N; i++) { + buf[SPX_ADDR_BYTES + i] = in[i] ^ bitmask[i]; + } + + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_haraka_S( + out, SPX_N, buf, SPX_ADDR_BYTES + inblocks * SPX_N); + } +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_ADDR_BYTES + 1 * SPX_N]; + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_thash( + out, buf, in, 1, pub_seed, addr); +} + +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_ADDR_BYTES + 2 * SPX_N]; + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_thash( + out, buf, in, 2, pub_seed, addr); +} + +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_ADDR_BYTES + SPX_WOTS_LEN * SPX_N]; + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_thash( + out, buf, in, SPX_WOTS_LEN, pub_seed, addr); +} + +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_ADDR_BYTES + SPX_FORS_TREES * SPX_N]; + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_thash( + out, buf, in, SPX_FORS_TREES, pub_seed, addr); +} diff --git a/crypto_sign/sphincs-haraka-192s-robust/clean/utils.c b/crypto_sign/sphincs-haraka-192s-robust/clean/utils.c new file mode 100644 index 00000000..e6372855 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192s-robust/clean/utils.c @@ -0,0 +1,192 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in) { + + /* Iterate over out in decreasing order, for big-endianness. */ + for (size_t i = outlen; i > 0; i--) { + out[i - 1] = in & 0xff; + in = in >> 8; + } +} + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen) { + unsigned long long retval = 0; + + for (size_t i = 0; i < inlen; i++) { + retval |= ((unsigned long long)in[i]) << (8 * (inlen - 1 - i)); + } + return retval; +} + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + unsigned char buffer[2 * SPX_N]; + + /* If leaf_idx is odd (last bit = 1), current path element is a right child + and auth_path has to go left. Otherwise it is the other way around. */ + if (leaf_idx & 1) { + memcpy(buffer + SPX_N, leaf, SPX_N); + memcpy(buffer, auth_path, SPX_N); + } else { + memcpy(buffer, leaf, SPX_N); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + + for (i = 0; i < tree_height - 1; i++) { + leaf_idx >>= 1; + idx_offset >>= 1; + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_tree_height(addr, i + 1); + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + + /* Pick the right or left neighbor, depending on parity of the node. */ + if (leaf_idx & 1) { + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_thash_2( + buffer + SPX_N, buffer, pub_seed, addr); + memcpy(buffer, auth_path, SPX_N); + } else { + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_thash_2( + buffer, buffer, pub_seed, addr); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + } + + /* The last iteration is exceptional; we do not copy an auth_path node. */ + leaf_idx >>= 1; + idx_offset >>= 1; + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_tree_height(addr, tree_height); + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_thash_2( + root, buffer, pub_seed, addr); +} + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +static void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_treehash( + unsigned char *root, unsigned char *auth_path, + unsigned char *stack, unsigned int *heights, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned int offset = 0; + uint32_t idx; + uint32_t tree_idx; + + for (idx = 0; idx < (uint32_t)(1 << tree_height); idx++) { + /* Add the next leaf node to the stack. */ + gen_leaf(stack + offset * SPX_N, + sk_seed, pub_seed, idx + idx_offset, tree_addr); + offset++; + heights[offset - 1] = 0; + + /* If this is a node we need for the auth path.. */ + if ((leaf_idx ^ 0x1) == idx) { + memcpy(auth_path, stack + (offset - 1)*SPX_N, SPX_N); + } + + /* While the top-most nodes are of equal height.. */ + while (offset >= 2 && heights[offset - 1] == heights[offset - 2]) { + /* Compute index of the new node, in the next layer. */ + tree_idx = (idx >> (heights[offset - 1] + 1)); + + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_tree_height( + tree_addr, heights[offset - 1] + 1); + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_tree_index( + tree_addr, tree_idx + (idx_offset >> (heights[offset - 1] + 1))); + /* Hash the top-most nodes from the stack together. */ + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_thash_2( + stack + (offset - 2)*SPX_N, stack + (offset - 2)*SPX_N, + pub_seed, tree_addr); + offset--; + /* Note that the top-most node is now one layer higher. */ + heights[offset - 1]++; + + /* If this is a node we need for the auth path.. */ + if (((leaf_idx >> heights[offset - 1]) ^ 0x1) == tree_idx) { + memcpy(auth_path + heights[offset - 1]*SPX_N, + stack + (offset - 1)*SPX_N, SPX_N); + } + } + } + memcpy(root, stack, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_FORS_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_FORS_HEIGHT + 1]; + + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_FORS_HEIGHT, gen_leaf, tree_addr); +} + +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_TREE_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_TREE_HEIGHT + 1]; + + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_TREE_HEIGHT, gen_leaf, tree_addr); +} diff --git a/crypto_sign/sphincs-haraka-192s-robust/clean/utils.h b/crypto_sign/sphincs-haraka-192s-robust/clean/utils.h new file mode 100644 index 00000000..9d689867 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192s-robust/clean/utils.h @@ -0,0 +1,60 @@ +#ifndef SPX_UTILS_H +#define SPX_UTILS_H + +#include "params.h" +#include +#include + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in); + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen); + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-haraka-192s-robust/clean/wots.c b/crypto_sign/sphincs-haraka-192s-robust/clean/wots.c new file mode 100644 index 00000000..7653f500 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192s-robust/clean/wots.c @@ -0,0 +1,161 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +// TODO clarify address expectations, and make them more uniform. +// TODO i.e. do we expect types to be set already? +// TODO and do we expect modifications or copies? + +/** + * Computes the starting value for a chain, i.e. the secret key. + * Expects the address to be complete up to the chain address. + */ +static void wots_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t wots_addr[8]) { + /* Make sure that the hash address is actually zeroed. */ + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_hash_addr(wots_addr, 0); + + /* Generate sk element. */ + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_prf_addr(sk, sk_seed, wots_addr); +} + +/** + * Computes the chaining function. + * out and in have to be n-byte arrays. + * + * Interprets in as start-th value of the chain. + * addr has to contain the address of the chain. + */ +static void gen_chain(unsigned char *out, const unsigned char *in, + unsigned int start, unsigned int steps, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + /* Initialize out with the value at position 'start'. */ + memcpy(out, in, SPX_N); + + /* Iterate 'steps' calls to the hash function. */ + for (i = start; i < (start + steps) && i < SPX_WOTS_W; i++) { + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_hash_addr(addr, i); + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_thash_1( + out, out, pub_seed, addr); + } +} + +/** + * base_w algorithm as described in draft. + * Interprets an array of bytes as integers in base w. + * This only works when log_w is a divisor of 8. + */ +static void base_w(unsigned int *output, const size_t out_len, + const unsigned char *input) { + size_t in = 0; + size_t out = 0; + unsigned char total = 0; + unsigned int bits = 0; + size_t consumed; + + for (consumed = 0; consumed < out_len; consumed++) { + if (bits == 0) { + total = input[in]; + in++; + bits += 8; + } + bits -= SPX_WOTS_LOGW; + output[out] = (unsigned int)((total >> bits) & (SPX_WOTS_W - 1)); + out++; + } +} + +/* Computes the WOTS+ checksum over a message (in base_w). */ +static void wots_checksum(unsigned int *csum_base_w, + const unsigned int *msg_base_w) { + unsigned int csum = 0; + unsigned char csum_bytes[(SPX_WOTS_LEN2 * SPX_WOTS_LOGW + 7) / 8]; + unsigned int i; + + /* Compute checksum. */ + for (i = 0; i < SPX_WOTS_LEN1; i++) { + csum += SPX_WOTS_W - 1 - msg_base_w[i]; + } + + /* Convert checksum to base_w. */ + /* Make sure expected empty zero bits are the least significant bits. */ + csum = csum << (8 - ((SPX_WOTS_LEN2 * SPX_WOTS_LOGW) % 8)); + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_ull_to_bytes( + csum_bytes, sizeof(csum_bytes), csum); + base_w(csum_base_w, SPX_WOTS_LEN2, csum_bytes); +} + +/* Takes a message and derives the matching chain lengths. */ +static void chain_lengths(unsigned int *lengths, const unsigned char *msg) { + base_w(lengths, SPX_WOTS_LEN1, msg); + wots_checksum(lengths + SPX_WOTS_LEN1, lengths); +} + +/** + * WOTS key generation. Takes a 32 byte sk_seed, expands it to WOTS private key + * elements and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(pk + i * SPX_N, sk_seed, addr); + gen_chain(pk + i * SPX_N, pk + i * SPX_N, + 0, SPX_WOTS_W - 1, pub_seed, addr); + } +} + +/** + * Takes a n-byte message and the 32-byte sk_see to compute a signature 'sig'. + */ +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(sig + i * SPX_N, sk_seed, addr); + gen_chain(sig + i * SPX_N, sig + i * SPX_N, 0, lengths[i], pub_seed, addr); + } +} + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_set_chain_addr(addr, i); + gen_chain(pk + i * SPX_N, sig + i * SPX_N, + lengths[i], SPX_WOTS_W - 1 - lengths[i], pub_seed, addr); + } +} diff --git a/crypto_sign/sphincs-haraka-192s-robust/clean/wots.h b/crypto_sign/sphincs-haraka-192s-robust/clean/wots.h new file mode 100644 index 00000000..17239b06 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192s-robust/clean/wots.h @@ -0,0 +1,38 @@ +#ifndef SPX_WOTS_H +#define SPX_WOTS_H + +#include "params.h" +#include + +/** + * WOTS key generation. Takes a 32 byte seed for the private key, expands it to + * a full WOTS private key and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * Takes a n-byte message and the 32-byte seed for the private key to compute a + * signature that is placed at 'sig'. + */ +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]); + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-haraka-192s-simple/META.yml b/crypto_sign/sphincs-haraka-192s-simple/META.yml new file mode 100644 index 00000000..d13213bd --- /dev/null +++ b/crypto_sign/sphincs-haraka-192s-simple/META.yml @@ -0,0 +1,27 @@ +name: SPHINCS+ +type: signature +claimed-nist-level: 3 +length-public-key: 48 +length-secret-key: 96 +length-signature: 17064 +testvectors-sha256: 7e50b92ec85e31260326092a62e84d2f12df84213a494d0f0527125a5e6b7ed7 +principal-submitter: Andreas Hülsing +auxiliary-submitters: + - Jean-Philippe Aumasson + - Daniel J. Bernstein, + - Christoph Dobraunig + - Maria Eichlseder + - Scott Fluhrer + - Stefan-Lukas Gazdag + - Panos Kampanakis + - Stefan Kölbl + - Tanja Lange + - Martin M. Lauridsen + - Florian Mendel + - Ruben Niederhagen + - Christian Rechberger + - Joost Rijneveld + - Peter Schwabe +implementations: + - name: clean + version: https://github.com/sphincs/sphincsplus/commit/77755c94d0bc744478044d6efbb888dc13156441 diff --git a/crypto_sign/sphincs-haraka-192s-simple/clean/LICENSE b/crypto_sign/sphincs-haraka-192s-simple/clean/LICENSE new file mode 100644 index 00000000..670154e3 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192s-simple/clean/LICENSE @@ -0,0 +1,116 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + diff --git a/crypto_sign/sphincs-haraka-192s-simple/clean/Makefile b/crypto_sign/sphincs-haraka-192s-simple/clean/Makefile new file mode 100644 index 00000000..13423bf4 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192s-simple/clean/Makefile @@ -0,0 +1,20 @@ +# This Makefile can be used with GNU Make or BSD Make + +LIB=libsphincs-haraka-192s-simple_clean.a + +HEADERS = params.h address.h wots.h utils.h fors.h api.h hash.h thash.h haraka.h +OBJECTS = address.o wots.o utils.o fors.o sign.o hash_haraka.o thash_haraka_simple.o haraka.o + +CFLAGS=-O3 -Wall -Wconversion -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) + +all: $(LIB) + +%.o: %.c $(HEADERS) + $(CC) $(CFLAGS) -c -o $@ $< + +$(LIB): $(OBJECTS) + $(AR) -r $@ $(OBJECTS) + +clean: + $(RM) $(OBJECTS) + $(RM) $(LIB) diff --git a/crypto_sign/sphincs-haraka-192s-simple/clean/Makefile.Microsoft_nmake b/crypto_sign/sphincs-haraka-192s-simple/clean/Makefile.Microsoft_nmake new file mode 100644 index 00000000..dd8be4cd --- /dev/null +++ b/crypto_sign/sphincs-haraka-192s-simple/clean/Makefile.Microsoft_nmake @@ -0,0 +1,19 @@ +# This Makefile can be used with Microsoft Visual Studio's nmake using the command: +# nmake /f Makefile.Microsoft_nmake + +LIBRARY=libsphincs-haraka-192s-simple_clean.lib +OBJECTS=address.obj wots.obj utils.obj fors.obj sign.obj hash_haraka.obj thash_haraka_simple.obj haraka.obj + +CFLAGS=/nologo /I ..\..\..\common /W4 /WX + +all: $(LIBRARY) + +# Make sure objects are recompiled if headers change. +$(OBJECTS): *.h + +$(LIBRARY): $(OBJECTS) + LIB.EXE /NOLOGO /WX /OUT:$@ $** + +clean: + -DEL $(OBJECTS) + -DEL $(LIBRARY) diff --git a/crypto_sign/sphincs-haraka-192s-simple/clean/address.c b/crypto_sign/sphincs-haraka-192s-simple/clean/address.c new file mode 100644 index 00000000..cacdfdcb --- /dev/null +++ b/crypto_sign/sphincs-haraka-192s-simple/clean/address.c @@ -0,0 +1,78 @@ +#include + +#include "address.h" +#include "params.h" +#include "utils.h" + +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]) { + int i; + + for (i = 0; i < 8; i++) { + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_ull_to_bytes( + bytes + i * 4, 4, addr[i]); + } +} + +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer) { + addr[0] = layer; +} + +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree) { + addr[1] = 0; + addr[2] = (uint32_t) (tree >> 32); + addr[3] = (uint32_t) tree; +} + +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_type( + uint32_t addr[8], uint32_t type) { + addr[4] = type; +} + +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; +} + +/* These functions are used for OTS addresses. */ + +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair) { + addr[5] = keypair; +} + +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; + out[5] = in[5]; +} + +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain) { + addr[6] = chain; +} + +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash) { + addr[7] = hash; +} + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height) { + addr[6] = tree_height; +} + +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index) { + addr[7] = tree_index; +} diff --git a/crypto_sign/sphincs-haraka-192s-simple/clean/address.h b/crypto_sign/sphincs-haraka-192s-simple/clean/address.h new file mode 100644 index 00000000..f205b3de --- /dev/null +++ b/crypto_sign/sphincs-haraka-192s-simple/clean/address.h @@ -0,0 +1,50 @@ +#ifndef SPX_ADDRESS_H +#define SPX_ADDRESS_H + +#include + +#define SPX_ADDR_TYPE_WOTS 0 +#define SPX_ADDR_TYPE_WOTSPK 1 +#define SPX_ADDR_TYPE_HASHTREE 2 +#define SPX_ADDR_TYPE_FORSTREE 3 +#define SPX_ADDR_TYPE_FORSPK 4 + +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer); + +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree); + +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_type( + uint32_t addr[8], uint32_t type); + +/* Copies the layer and tree part of one address into the other */ +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for WOTS and FORS addresses. */ + +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair); + +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain); + +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash); + +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height); + +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index); + +#endif diff --git a/crypto_sign/sphincs-haraka-192s-simple/clean/api.h b/crypto_sign/sphincs-haraka-192s-simple/clean/api.h new file mode 100644 index 00000000..72dd6768 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192s-simple/clean/api.h @@ -0,0 +1,78 @@ +#ifndef PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_API_H +#define PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_API_H + +#include +#include + +#define PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_CRYPTO_ALGNAME "SPHINCS+" + +#define PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_CRYPTO_SECRETKEYBYTES 96 +#define PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES 48 +#define PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_CRYPTO_BYTES 17064 +#define PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_CRYPTO_SEEDBYTES 72 + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_crypto_sign_secretkeybytes(void); + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_crypto_sign_publickeybytes(void); + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_crypto_sign_bytes(void); + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_crypto_sign_seedbytes(void); + +/* + * Generates a SPHINCS+ key pair given a seed. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed); + +/* + * Generates a SPHINCS+ key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk); + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk); + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk); + +#endif diff --git a/crypto_sign/sphincs-haraka-192s-simple/clean/fors.c b/crypto_sign/sphincs-haraka-192s-simple/clean/fors.c new file mode 100644 index 00000000..3e4b0e9a --- /dev/null +++ b/crypto_sign/sphincs-haraka-192s-simple/clean/fors.c @@ -0,0 +1,164 @@ +#include +#include +#include + +#include "address.h" +#include "fors.h" +#include "hash.h" +#include "thash.h" +#include "utils.h" + +static void fors_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_prf_addr( + sk, sk_seed, fors_leaf_addr); +} + +static void fors_sk_to_leaf(unsigned char *leaf, const unsigned char *sk, + const unsigned char *pub_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_thash_1( + leaf, sk, pub_seed, fors_leaf_addr); +} + +static void fors_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t fors_tree_addr[8]) { + uint32_t fors_leaf_addr[8] = {0}; + + /* Only copy the parts that must be kept in fors_leaf_addr. */ + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_copy_keypair_addr( + fors_leaf_addr, fors_tree_addr); + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_type( + fors_leaf_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_tree_index( + fors_leaf_addr, addr_idx); + + fors_gen_sk(leaf, sk_seed, fors_leaf_addr); + fors_sk_to_leaf(leaf, leaf, pub_seed, fors_leaf_addr); +} + +/** + * Interprets m as SPX_FORS_HEIGHT-bit unsigned integers. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + * Assumes indices has space for SPX_FORS_TREES integers. + */ +static void message_to_indices(uint32_t *indices, const unsigned char *m) { + unsigned int i, j; + unsigned int offset = 0; + + for (i = 0; i < SPX_FORS_TREES; i++) { + indices[i] = 0; + for (j = 0; j < SPX_FORS_HEIGHT; j++) { + indices[i] ^= (((uint32_t)m[offset >> 3] >> (offset & 0x7)) & 0x1) << j; + offset++; + } + } +} + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Include the secret key part that produces the selected leaf node. */ + fors_gen_sk(sig, sk_seed, fors_tree_addr); + sig += SPX_N; + + /* Compute the authentication path for this leaf node. */ + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_treehash_FORS_HEIGHT( + roots + i * SPX_N, sig, sk_seed, pub_seed, + indices[i], idx_offset, fors_gen_leaf, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + unsigned char leaf[SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Derive the leaf from the included secret key part. */ + fors_sk_to_leaf(leaf, sig, pub_seed, fors_tree_addr); + sig += SPX_N; + + /* Derive the corresponding root node of this tree. */ + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_compute_root( + roots + i * SPX_N, leaf, indices[i], idx_offset, sig, + SPX_FORS_HEIGHT, pub_seed, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} diff --git a/crypto_sign/sphincs-haraka-192s-simple/clean/fors.h b/crypto_sign/sphincs-haraka-192s-simple/clean/fors.h new file mode 100644 index 00000000..3656d0f6 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192s-simple/clean/fors.h @@ -0,0 +1,30 @@ +#ifndef SPX_FORS_H +#define SPX_FORS_H + +#include + +#include "params.h" + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]); + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-haraka-192s-simple/clean/haraka.c b/crypto_sign/sphincs-haraka-192s-simple/clean/haraka.c new file mode 100644 index 00000000..22febc91 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192s-simple/clean/haraka.c @@ -0,0 +1,965 @@ +/* + * Constant time implementation of the Haraka hash function. + * + * The bit-sliced implementation of the AES round functions are + * based on the AES implementation in BearSSL written + * by Thomas Pornin + */ + +#include +#include +#include + +#include "haraka.h" + +#define HARAKAS_RATE 32 + +static const uint64_t haraka512_rc64[10][8] = { + {0x24cf0ab9086f628b, 0xbdd6eeecc83b8382, 0xd96fb0306cdad0a7, 0xaace082ac8f95f89, 0x449d8e8870d7041f, 0x49bb2f80b2b3e2f8, 0x0569ae98d93bb258, 0x23dc9691e7d6a4b1}, + {0xd8ba10ede0fe5b6e, 0x7ecf7dbe424c7b8e, 0x6ea9949c6df62a31, 0xbf3f3c97ec9c313e, 0x241d03a196a1861e, 0xead3a51116e5a2ea, 0x77d479fcad9574e3, 0x18657a1af894b7a0}, + {0x10671e1a7f595522, 0xd9a00ff675d28c7b, 0x2f1edf0d2b9ba661, 0xb8ff58b8e3de45f9, 0xee29261da9865c02, 0xd1532aa4b50bdf43, 0x8bf858159b231bb1, 0xdf17439d22d4f599}, + {0xdd4b2f0870b918c0, 0x757a81f3b39b1bb6, 0x7a5c556898952e3f, 0x7dd70a16d915d87a, 0x3ae61971982b8301, 0xc3ab319e030412be, 0x17c0033ac094a8cb, 0x5a0630fc1a8dc4ef}, + {0x17708988c1632f73, 0xf92ddae090b44f4f, 0x11ac0285c43aa314, 0x509059941936b8ba, 0xd03e152fa2ce9b69, 0x3fbcbcb63a32998b, 0x6204696d692254f7, 0x915542ed93ec59b4}, + {0xf4ed94aa8879236e, 0xff6cb41cd38e03c0, 0x069b38602368aeab, 0x669495b820f0ddba, 0xf42013b1b8bf9e3d, 0xcf935efe6439734d, 0xbc1dcf42ca29e3f8, 0x7e6d3ed29f78ad67}, + {0xf3b0f6837ffcddaa, 0x3a76faef934ddf41, 0xcec7ae583a9c8e35, 0xe4dd18c68f0260af, 0x2c0e5df1ad398eaa, 0x478df5236ae22e8c, 0xfb944c46fe865f39, 0xaa48f82f028132ba}, + {0x231b9ae2b76aca77, 0x292a76a712db0b40, 0x5850625dc8134491, 0x73137dd469810fb5, 0x8a12a6a202a474fd, 0xd36fd9daa78bdb80, 0xb34c5e733505706f, 0xbaf1cdca818d9d96}, + {0x2e99781335e8c641, 0xbddfe5cce47d560e, 0xf74e9bf32e5e040c, 0x1d7a709d65996be9, 0x670df36a9cf66cdd, 0xd05ef84a176a2875, 0x0f888e828cb1c44e, 0x1a79e9c9727b052c}, + {0x83497348628d84de, 0x2e9387d51f22a754, 0xb000068da2f852d6, 0x378c9e1190fd6fe5, 0x870027c316de7293, 0xe51a9d4462e047bb, 0x90ecf7f8c6251195, 0x655953bfbed90a9c}, +}; + +static uint64_t tweaked512_rc64[10][8]; +static uint32_t tweaked256_rc32[10][8]; +static uint32_t tweaked256_rc32_sseed[10][8]; + +static inline uint32_t br_dec32le(const unsigned char *src) { + return (uint32_t)src[0] + | ((uint32_t)src[1] << 8) + | ((uint32_t)src[2] << 16) + | ((uint32_t)src[3] << 24); +} + +static void br_range_dec32le(uint32_t *v, size_t num, const unsigned char *src) { + while (num-- > 0) { + *v ++ = br_dec32le(src); + src += 4; + } +} + +static inline void br_enc32le(unsigned char *dst, uint32_t x) { + dst[0] = (unsigned char)x; + dst[1] = (unsigned char)(x >> 8); + dst[2] = (unsigned char)(x >> 16); + dst[3] = (unsigned char)(x >> 24); +} + + +static void br_range_enc32le(unsigned char *dst, const uint32_t *v, size_t num) { + while (num-- > 0) { + br_enc32le(dst, *v ++); + dst += 4; + } +} + +static void br_aes_ct64_bitslice_Sbox(uint64_t *q) { + /* + * This S-box implementation is a straightforward translation of + * the circuit described by Boyar and Peralta in "A new + * combinational logic minimization technique with applications + * to cryptology" (https://eprint.iacr.org/2009/191.pdf). + * + * Note that variables x* (input) and s* (output) are numbered + * in "reverse" order (x0 is the high bit, x7 is the low bit). + */ + + uint64_t x0, x1, x2, x3, x4, x5, x6, x7; + uint64_t y1, y2, y3, y4, y5, y6, y7, y8, y9; + uint64_t y10, y11, y12, y13, y14, y15, y16, y17, y18, y19; + uint64_t y20, y21; + uint64_t z0, z1, z2, z3, z4, z5, z6, z7, z8, z9; + uint64_t z10, z11, z12, z13, z14, z15, z16, z17; + uint64_t t0, t1, t2, t3, t4, t5, t6, t7, t8, t9; + uint64_t t10, t11, t12, t13, t14, t15, t16, t17, t18, t19; + uint64_t t20, t21, t22, t23, t24, t25, t26, t27, t28, t29; + uint64_t t30, t31, t32, t33, t34, t35, t36, t37, t38, t39; + uint64_t t40, t41, t42, t43, t44, t45, t46, t47, t48, t49; + uint64_t t50, t51, t52, t53, t54, t55, t56, t57, t58, t59; + uint64_t t60, t61, t62, t63, t64, t65, t66, t67; + uint64_t s0, s1, s2, s3, s4, s5, s6, s7; + + x0 = q[7]; + x1 = q[6]; + x2 = q[5]; + x3 = q[4]; + x4 = q[3]; + x5 = q[2]; + x6 = q[1]; + x7 = q[0]; + + /* + * Top linear transformation. + */ + y14 = x3 ^ x5; + y13 = x0 ^ x6; + y9 = x0 ^ x3; + y8 = x0 ^ x5; + t0 = x1 ^ x2; + y1 = t0 ^ x7; + y4 = y1 ^ x3; + y12 = y13 ^ y14; + y2 = y1 ^ x0; + y5 = y1 ^ x6; + y3 = y5 ^ y8; + t1 = x4 ^ y12; + y15 = t1 ^ x5; + y20 = t1 ^ x1; + y6 = y15 ^ x7; + y10 = y15 ^ t0; + y11 = y20 ^ y9; + y7 = x7 ^ y11; + y17 = y10 ^ y11; + y19 = y10 ^ y8; + y16 = t0 ^ y11; + y21 = y13 ^ y16; + y18 = x0 ^ y16; + + /* + * Non-linear section. + */ + t2 = y12 & y15; + t3 = y3 & y6; + t4 = t3 ^ t2; + t5 = y4 & x7; + t6 = t5 ^ t2; + t7 = y13 & y16; + t8 = y5 & y1; + t9 = t8 ^ t7; + t10 = y2 & y7; + t11 = t10 ^ t7; + t12 = y9 & y11; + t13 = y14 & y17; + t14 = t13 ^ t12; + t15 = y8 & y10; + t16 = t15 ^ t12; + t17 = t4 ^ t14; + t18 = t6 ^ t16; + t19 = t9 ^ t14; + t20 = t11 ^ t16; + t21 = t17 ^ y20; + t22 = t18 ^ y19; + t23 = t19 ^ y21; + t24 = t20 ^ y18; + + t25 = t21 ^ t22; + t26 = t21 & t23; + t27 = t24 ^ t26; + t28 = t25 & t27; + t29 = t28 ^ t22; + t30 = t23 ^ t24; + t31 = t22 ^ t26; + t32 = t31 & t30; + t33 = t32 ^ t24; + t34 = t23 ^ t33; + t35 = t27 ^ t33; + t36 = t24 & t35; + t37 = t36 ^ t34; + t38 = t27 ^ t36; + t39 = t29 & t38; + t40 = t25 ^ t39; + + t41 = t40 ^ t37; + t42 = t29 ^ t33; + t43 = t29 ^ t40; + t44 = t33 ^ t37; + t45 = t42 ^ t41; + z0 = t44 & y15; + z1 = t37 & y6; + z2 = t33 & x7; + z3 = t43 & y16; + z4 = t40 & y1; + z5 = t29 & y7; + z6 = t42 & y11; + z7 = t45 & y17; + z8 = t41 & y10; + z9 = t44 & y12; + z10 = t37 & y3; + z11 = t33 & y4; + z12 = t43 & y13; + z13 = t40 & y5; + z14 = t29 & y2; + z15 = t42 & y9; + z16 = t45 & y14; + z17 = t41 & y8; + + /* + * Bottom linear transformation. + */ + t46 = z15 ^ z16; + t47 = z10 ^ z11; + t48 = z5 ^ z13; + t49 = z9 ^ z10; + t50 = z2 ^ z12; + t51 = z2 ^ z5; + t52 = z7 ^ z8; + t53 = z0 ^ z3; + t54 = z6 ^ z7; + t55 = z16 ^ z17; + t56 = z12 ^ t48; + t57 = t50 ^ t53; + t58 = z4 ^ t46; + t59 = z3 ^ t54; + t60 = t46 ^ t57; + t61 = z14 ^ t57; + t62 = t52 ^ t58; + t63 = t49 ^ t58; + t64 = z4 ^ t59; + t65 = t61 ^ t62; + t66 = z1 ^ t63; + s0 = t59 ^ t63; + s6 = t56 ^ ~t62; + s7 = t48 ^ ~t60; + t67 = t64 ^ t65; + s3 = t53 ^ t66; + s4 = t51 ^ t66; + s5 = t47 ^ t65; + s1 = t64 ^ ~s3; + s2 = t55 ^ ~t67; + + q[7] = s0; + q[6] = s1; + q[5] = s2; + q[4] = s3; + q[3] = s4; + q[2] = s5; + q[1] = s6; + q[0] = s7; +} + +static void br_aes_ct_bitslice_Sbox(uint32_t *q) { + /* + * This S-box implementation is a straightforward translation of + * the circuit described by Boyar and Peralta in "A new + * combinational logic minimization technique with applications + * to cryptology" (https://eprint.iacr.org/2009/191.pdf). + * + * Note that variables x* (input) and s* (output) are numbered + * in "reverse" order (x0 is the high bit, x7 is the low bit). + */ + + uint32_t x0, x1, x2, x3, x4, x5, x6, x7; + uint32_t y1, y2, y3, y4, y5, y6, y7, y8, y9; + uint32_t y10, y11, y12, y13, y14, y15, y16, y17, y18, y19; + uint32_t y20, y21; + uint32_t z0, z1, z2, z3, z4, z5, z6, z7, z8, z9; + uint32_t z10, z11, z12, z13, z14, z15, z16, z17; + uint32_t t0, t1, t2, t3, t4, t5, t6, t7, t8, t9; + uint32_t t10, t11, t12, t13, t14, t15, t16, t17, t18, t19; + uint32_t t20, t21, t22, t23, t24, t25, t26, t27, t28, t29; + uint32_t t30, t31, t32, t33, t34, t35, t36, t37, t38, t39; + uint32_t t40, t41, t42, t43, t44, t45, t46, t47, t48, t49; + uint32_t t50, t51, t52, t53, t54, t55, t56, t57, t58, t59; + uint32_t t60, t61, t62, t63, t64, t65, t66, t67; + uint32_t s0, s1, s2, s3, s4, s5, s6, s7; + + x0 = q[7]; + x1 = q[6]; + x2 = q[5]; + x3 = q[4]; + x4 = q[3]; + x5 = q[2]; + x6 = q[1]; + x7 = q[0]; + + /* + * Top linear transformation. + */ + y14 = x3 ^ x5; + y13 = x0 ^ x6; + y9 = x0 ^ x3; + y8 = x0 ^ x5; + t0 = x1 ^ x2; + y1 = t0 ^ x7; + y4 = y1 ^ x3; + y12 = y13 ^ y14; + y2 = y1 ^ x0; + y5 = y1 ^ x6; + y3 = y5 ^ y8; + t1 = x4 ^ y12; + y15 = t1 ^ x5; + y20 = t1 ^ x1; + y6 = y15 ^ x7; + y10 = y15 ^ t0; + y11 = y20 ^ y9; + y7 = x7 ^ y11; + y17 = y10 ^ y11; + y19 = y10 ^ y8; + y16 = t0 ^ y11; + y21 = y13 ^ y16; + y18 = x0 ^ y16; + + /* + * Non-linear section. + */ + t2 = y12 & y15; + t3 = y3 & y6; + t4 = t3 ^ t2; + t5 = y4 & x7; + t6 = t5 ^ t2; + t7 = y13 & y16; + t8 = y5 & y1; + t9 = t8 ^ t7; + t10 = y2 & y7; + t11 = t10 ^ t7; + t12 = y9 & y11; + t13 = y14 & y17; + t14 = t13 ^ t12; + t15 = y8 & y10; + t16 = t15 ^ t12; + t17 = t4 ^ t14; + t18 = t6 ^ t16; + t19 = t9 ^ t14; + t20 = t11 ^ t16; + t21 = t17 ^ y20; + t22 = t18 ^ y19; + t23 = t19 ^ y21; + t24 = t20 ^ y18; + + t25 = t21 ^ t22; + t26 = t21 & t23; + t27 = t24 ^ t26; + t28 = t25 & t27; + t29 = t28 ^ t22; + t30 = t23 ^ t24; + t31 = t22 ^ t26; + t32 = t31 & t30; + t33 = t32 ^ t24; + t34 = t23 ^ t33; + t35 = t27 ^ t33; + t36 = t24 & t35; + t37 = t36 ^ t34; + t38 = t27 ^ t36; + t39 = t29 & t38; + t40 = t25 ^ t39; + + t41 = t40 ^ t37; + t42 = t29 ^ t33; + t43 = t29 ^ t40; + t44 = t33 ^ t37; + t45 = t42 ^ t41; + z0 = t44 & y15; + z1 = t37 & y6; + z2 = t33 & x7; + z3 = t43 & y16; + z4 = t40 & y1; + z5 = t29 & y7; + z6 = t42 & y11; + z7 = t45 & y17; + z8 = t41 & y10; + z9 = t44 & y12; + z10 = t37 & y3; + z11 = t33 & y4; + z12 = t43 & y13; + z13 = t40 & y5; + z14 = t29 & y2; + z15 = t42 & y9; + z16 = t45 & y14; + z17 = t41 & y8; + + /* + * Bottom linear transformation. + */ + t46 = z15 ^ z16; + t47 = z10 ^ z11; + t48 = z5 ^ z13; + t49 = z9 ^ z10; + t50 = z2 ^ z12; + t51 = z2 ^ z5; + t52 = z7 ^ z8; + t53 = z0 ^ z3; + t54 = z6 ^ z7; + t55 = z16 ^ z17; + t56 = z12 ^ t48; + t57 = t50 ^ t53; + t58 = z4 ^ t46; + t59 = z3 ^ t54; + t60 = t46 ^ t57; + t61 = z14 ^ t57; + t62 = t52 ^ t58; + t63 = t49 ^ t58; + t64 = z4 ^ t59; + t65 = t61 ^ t62; + t66 = z1 ^ t63; + s0 = t59 ^ t63; + s6 = t56 ^ ~t62; + s7 = t48 ^ ~t60; + t67 = t64 ^ t65; + s3 = t53 ^ t66; + s4 = t51 ^ t66; + s5 = t47 ^ t65; + s1 = t64 ^ ~s3; + s2 = t55 ^ ~t67; + + q[7] = s0; + q[6] = s1; + q[5] = s2; + q[4] = s3; + q[3] = s4; + q[2] = s5; + q[1] = s6; + q[0] = s7; +} + +static void br_aes_ct_ortho(uint32_t *q) { +#define SWAPN_32(cl, ch, s, x, y) do { \ + uint32_t a, b; \ + a = (x); \ + b = (y); \ + (x) = (a & (uint32_t)(cl)) | ((b & (uint32_t)(cl)) << (s)); \ + (y) = ((a & (uint32_t)(ch)) >> (s)) | (b & (uint32_t)(ch)); \ + } while (0) + +#define SWAP2_32(x, y) SWAPN_32(0x55555555, 0xAAAAAAAA, 1, x, y) +#define SWAP4_32(x, y) SWAPN_32(0x33333333, 0xCCCCCCCC, 2, x, y) +#define SWAP8_32(x, y) SWAPN_32(0x0F0F0F0F, 0xF0F0F0F0, 4, x, y) + + SWAP2_32(q[0], q[1]); + SWAP2_32(q[2], q[3]); + SWAP2_32(q[4], q[5]); + SWAP2_32(q[6], q[7]); + + SWAP4_32(q[0], q[2]); + SWAP4_32(q[1], q[3]); + SWAP4_32(q[4], q[6]); + SWAP4_32(q[5], q[7]); + + SWAP8_32(q[0], q[4]); + SWAP8_32(q[1], q[5]); + SWAP8_32(q[2], q[6]); + SWAP8_32(q[3], q[7]); +} + +static inline void add_round_key32(uint32_t *q, const uint32_t *sk) { + q[0] ^= sk[0]; + q[1] ^= sk[1]; + q[2] ^= sk[2]; + q[3] ^= sk[3]; + q[4] ^= sk[4]; + q[5] ^= sk[5]; + q[6] ^= sk[6]; + q[7] ^= sk[7]; +} + +static inline void shift_rows32(uint32_t *q) { + int i; + + for (i = 0; i < 8; i++) { + uint32_t x; + + x = q[i]; + q[i] = (x & 0x000000FF) + | ((x & 0x0000FC00) >> 2) | ((x & 0x00000300) << 6) + | ((x & 0x00F00000) >> 4) | ((x & 0x000F0000) << 4) + | ((x & 0xC0000000) >> 6) | ((x & 0x3F000000) << 2); + } +} + +static inline uint32_t rotr16(uint32_t x) { + return (x << 16) | (x >> 16); +} + +static inline void mix_columns32(uint32_t *q) { + uint32_t q0, q1, q2, q3, q4, q5, q6, q7; + uint32_t r0, r1, r2, r3, r4, r5, r6, r7; + + q0 = q[0]; + q1 = q[1]; + q2 = q[2]; + q3 = q[3]; + q4 = q[4]; + q5 = q[5]; + q6 = q[6]; + q7 = q[7]; + r0 = (q0 >> 8) | (q0 << 24); + r1 = (q1 >> 8) | (q1 << 24); + r2 = (q2 >> 8) | (q2 << 24); + r3 = (q3 >> 8) | (q3 << 24); + r4 = (q4 >> 8) | (q4 << 24); + r5 = (q5 >> 8) | (q5 << 24); + r6 = (q6 >> 8) | (q6 << 24); + r7 = (q7 >> 8) | (q7 << 24); + + q[0] = q7 ^ r7 ^ r0 ^ rotr16(q0 ^ r0); + q[1] = q0 ^ r0 ^ q7 ^ r7 ^ r1 ^ rotr16(q1 ^ r1); + q[2] = q1 ^ r1 ^ r2 ^ rotr16(q2 ^ r2); + q[3] = q2 ^ r2 ^ q7 ^ r7 ^ r3 ^ rotr16(q3 ^ r3); + q[4] = q3 ^ r3 ^ q7 ^ r7 ^ r4 ^ rotr16(q4 ^ r4); + q[5] = q4 ^ r4 ^ r5 ^ rotr16(q5 ^ r5); + q[6] = q5 ^ r5 ^ r6 ^ rotr16(q6 ^ r6); + q[7] = q6 ^ r6 ^ r7 ^ rotr16(q7 ^ r7); +} + +static void br_aes_ct64_ortho(uint64_t *q) { +#define SWAPN(cl, ch, s, x, y) do { \ + uint64_t a, b; \ + a = (x); \ + b = (y); \ + (x) = (a & (uint64_t)(cl)) | ((b & (uint64_t)(cl)) << (s)); \ + (y) = ((a & (uint64_t)(ch)) >> (s)) | (b & (uint64_t)(ch)); \ + } while (0) + +#define SWAP2(x, y) SWAPN(0x5555555555555555, 0xAAAAAAAAAAAAAAAA, 1, x, y) +#define SWAP4(x, y) SWAPN(0x3333333333333333, 0xCCCCCCCCCCCCCCCC, 2, x, y) +#define SWAP8(x, y) SWAPN(0x0F0F0F0F0F0F0F0F, 0xF0F0F0F0F0F0F0F0, 4, x, y) + + SWAP2(q[0], q[1]); + SWAP2(q[2], q[3]); + SWAP2(q[4], q[5]); + SWAP2(q[6], q[7]); + + SWAP4(q[0], q[2]); + SWAP4(q[1], q[3]); + SWAP4(q[4], q[6]); + SWAP4(q[5], q[7]); + + SWAP8(q[0], q[4]); + SWAP8(q[1], q[5]); + SWAP8(q[2], q[6]); + SWAP8(q[3], q[7]); +} + + +static void br_aes_ct64_interleave_in(uint64_t *q0, uint64_t *q1, const uint32_t *w) { + uint64_t x0, x1, x2, x3; + + x0 = w[0]; + x1 = w[1]; + x2 = w[2]; + x3 = w[3]; + x0 |= (x0 << 16); + x1 |= (x1 << 16); + x2 |= (x2 << 16); + x3 |= (x3 << 16); + x0 &= (uint64_t)0x0000FFFF0000FFFF; + x1 &= (uint64_t)0x0000FFFF0000FFFF; + x2 &= (uint64_t)0x0000FFFF0000FFFF; + x3 &= (uint64_t)0x0000FFFF0000FFFF; + x0 |= (x0 << 8); + x1 |= (x1 << 8); + x2 |= (x2 << 8); + x3 |= (x3 << 8); + x0 &= (uint64_t)0x00FF00FF00FF00FF; + x1 &= (uint64_t)0x00FF00FF00FF00FF; + x2 &= (uint64_t)0x00FF00FF00FF00FF; + x3 &= (uint64_t)0x00FF00FF00FF00FF; + *q0 = x0 | (x2 << 8); + *q1 = x1 | (x3 << 8); +} + + +static void br_aes_ct64_interleave_out(uint32_t *w, uint64_t q0, uint64_t q1) { + uint64_t x0, x1, x2, x3; + + x0 = q0 & (uint64_t)0x00FF00FF00FF00FF; + x1 = q1 & (uint64_t)0x00FF00FF00FF00FF; + x2 = (q0 >> 8) & (uint64_t)0x00FF00FF00FF00FF; + x3 = (q1 >> 8) & (uint64_t)0x00FF00FF00FF00FF; + x0 |= (x0 >> 8); + x1 |= (x1 >> 8); + x2 |= (x2 >> 8); + x3 |= (x3 >> 8); + x0 &= (uint64_t)0x0000FFFF0000FFFF; + x1 &= (uint64_t)0x0000FFFF0000FFFF; + x2 &= (uint64_t)0x0000FFFF0000FFFF; + x3 &= (uint64_t)0x0000FFFF0000FFFF; + w[0] = (uint32_t)x0 | (uint32_t)(x0 >> 16); + w[1] = (uint32_t)x1 | (uint32_t)(x1 >> 16); + w[2] = (uint32_t)x2 | (uint32_t)(x2 >> 16); + w[3] = (uint32_t)x3 | (uint32_t)(x3 >> 16); +} + +static inline void add_round_key(uint64_t *q, const uint64_t *sk) { + q[0] ^= sk[0]; + q[1] ^= sk[1]; + q[2] ^= sk[2]; + q[3] ^= sk[3]; + q[4] ^= sk[4]; + q[5] ^= sk[5]; + q[6] ^= sk[6]; + q[7] ^= sk[7]; +} + +static inline void shift_rows(uint64_t *q) { + int i; + + for (i = 0; i < 8; i++) { + uint64_t x; + + x = q[i]; + q[i] = (x & (uint64_t)0x000000000000FFFF) + | ((x & (uint64_t)0x00000000FFF00000) >> 4) + | ((x & (uint64_t)0x00000000000F0000) << 12) + | ((x & (uint64_t)0x0000FF0000000000) >> 8) + | ((x & (uint64_t)0x000000FF00000000) << 8) + | ((x & (uint64_t)0xF000000000000000) >> 12) + | ((x & (uint64_t)0x0FFF000000000000) << 4); + } +} + +static inline uint64_t rotr32(uint64_t x) { + return (x << 32) | (x >> 32); +} + +static inline void mix_columns(uint64_t *q) { + uint64_t q0, q1, q2, q3, q4, q5, q6, q7; + uint64_t r0, r1, r2, r3, r4, r5, r6, r7; + + q0 = q[0]; + q1 = q[1]; + q2 = q[2]; + q3 = q[3]; + q4 = q[4]; + q5 = q[5]; + q6 = q[6]; + q7 = q[7]; + r0 = (q0 >> 16) | (q0 << 48); + r1 = (q1 >> 16) | (q1 << 48); + r2 = (q2 >> 16) | (q2 << 48); + r3 = (q3 >> 16) | (q3 << 48); + r4 = (q4 >> 16) | (q4 << 48); + r5 = (q5 >> 16) | (q5 << 48); + r6 = (q6 >> 16) | (q6 << 48); + r7 = (q7 >> 16) | (q7 << 48); + + q[0] = q7 ^ r7 ^ r0 ^ rotr32(q0 ^ r0); + q[1] = q0 ^ r0 ^ q7 ^ r7 ^ r1 ^ rotr32(q1 ^ r1); + q[2] = q1 ^ r1 ^ r2 ^ rotr32(q2 ^ r2); + q[3] = q2 ^ r2 ^ q7 ^ r7 ^ r3 ^ rotr32(q3 ^ r3); + q[4] = q3 ^ r3 ^ q7 ^ r7 ^ r4 ^ rotr32(q4 ^ r4); + q[5] = q4 ^ r4 ^ r5 ^ rotr32(q5 ^ r5); + q[6] = q5 ^ r5 ^ r6 ^ rotr32(q6 ^ r6); + q[7] = q6 ^ r6 ^ r7 ^ rotr32(q7 ^ r7); +} + +static void interleave_constant(uint64_t *out, const unsigned char *in) { + uint32_t tmp_32_constant[16]; + int i; + + br_range_dec32le(tmp_32_constant, 16, in); + for (i = 0; i < 4; i++) { + br_aes_ct64_interleave_in(&out[i], &out[i + 4], tmp_32_constant + (i << 2)); + } + br_aes_ct64_ortho(out); +} + +static void interleave_constant32(uint32_t *out, const unsigned char *in) { + int i; + for (i = 0; i < 4; i++) { + out[2 * i] = br_dec32le(in + 4 * i); + out[2 * i + 1] = br_dec32le(in + 4 * i + 16); + } + br_aes_ct_ortho(out); +} + +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_tweak_constants( + const unsigned char *pk_seed, const unsigned char *sk_seed, + unsigned long long seed_length) { + unsigned char buf[40 * 16]; + int i; + + /* Use the standard constants to generate tweaked ones. */ + memcpy((uint8_t *)tweaked512_rc64, (uint8_t *)haraka512_rc64, 40 * 16); + + /* Constants for sk.seed */ + if (sk_seed != NULL) { + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_haraka_S( + buf, 40 * 16, sk_seed, seed_length); + + /* Interleave constants */ + for (i = 0; i < 10; i++) { + interleave_constant32(tweaked256_rc32_sseed[i], buf + 32 * i); + } + } + + /* Constants for pk.seed */ + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_haraka_S( + buf, 40 * 16, pk_seed, seed_length); + for (i = 0; i < 10; i++) { + interleave_constant32(tweaked256_rc32[i], buf + 32 * i); + interleave_constant(tweaked512_rc64[i], buf + 64 * i); + } +} + +static void haraka_S_absorb(unsigned char *s, + const unsigned char *m, unsigned long long mlen, + unsigned char p) { + unsigned long long i; + unsigned char t[HARAKAS_RATE]; + + while (mlen >= HARAKAS_RATE) { + /* XOR block to state */ + for (i = 0; i < HARAKAS_RATE; ++i) { + s[i] ^= m[i]; + } + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_haraka512_perm(s, s); + mlen -= HARAKAS_RATE; + m += HARAKAS_RATE; + } + + for (i = 0; i < HARAKAS_RATE; ++i) { + t[i] = 0; + } + for (i = 0; i < mlen; ++i) { + t[i] = m[i]; + } + t[i] = p; + t[HARAKAS_RATE - 1] |= 128; + for (i = 0; i < HARAKAS_RATE; ++i) { + s[i] ^= t[i]; + } +} + +static void haraka_S_squeezeblocks(unsigned char *h, unsigned long long nblocks, + unsigned char *s) { + while (nblocks > 0) { + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_haraka512_perm(s, s); + memcpy(h, s, HARAKAS_RATE); + h += HARAKAS_RATE; + nblocks--; + } +} + +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_haraka_S_inc_init(uint8_t *s_inc) { + size_t i; + + for (i = 0; i < 64; i++) { + s_inc[i] = 0; + } + s_inc[64] = 0; +} + +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_haraka_S_inc_absorb(uint8_t *s_inc, const uint8_t *m, size_t mlen) { + size_t i; + + /* Recall that s_inc[64] is the non-absorbed bytes xored into the state */ + while (mlen + s_inc[64] >= HARAKAS_RATE) { + for (i = 0; i < (size_t)(HARAKAS_RATE - s_inc[64]); i++) { + /* Take the i'th byte from message + xor with the s_inc[64] + i'th byte of the state */ + s_inc[s_inc[64] + i] ^= m[i]; + } + mlen -= (size_t)(HARAKAS_RATE - s_inc[64]); + m += HARAKAS_RATE - s_inc[64]; + s_inc[64] = 0; + + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_haraka512_perm(s_inc, s_inc); + } + + for (i = 0; i < mlen; i++) { + s_inc[s_inc[64] + i] ^= m[i]; + } + s_inc[64] = (uint8_t)(mlen + s_inc[64]); +} + +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_haraka_S_inc_finalize(uint8_t *s_inc) { + /* After haraka_S_inc_absorb, we are guaranteed that s_inc[64] < HARAKAS_RATE, + so we can always use one more byte for p in the current state. */ + s_inc[s_inc[64]] ^= 0x1F; + s_inc[HARAKAS_RATE - 1] ^= 128; + s_inc[64] = 0; +} + +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_haraka_S_inc_squeeze(uint8_t *out, size_t outlen, uint8_t *s_inc) { + uint8_t i; + + /* First consume any bytes we still have sitting around */ + for (i = 0; i < outlen && i < s_inc[64]; i++) { + /* There are s_inc[64] bytes left, so r - s_inc[64] is the first + available byte. We consume from there, i.e., up to r. */ + out[i] = s_inc[(HARAKAS_RATE - s_inc[64] + i)]; + } + out += i; + outlen -= i; + s_inc[64] = (uint8_t)(s_inc[64] - i); + + /* Then squeeze the remaining necessary blocks */ + while (outlen > 0) { + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_haraka512_perm(s_inc, s_inc); + + for (i = 0; i < outlen && i < HARAKAS_RATE; i++) { + out[i] = s_inc[i]; + } + out += i; + outlen -= i; + s_inc[64] = (uint8_t)(HARAKAS_RATE - i); + } +} + +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_haraka_S(unsigned char *out, unsigned long long outlen, const unsigned char *in, unsigned long long inlen) { + unsigned long long i; + unsigned char s[64]; + unsigned char d[32]; + + for (i = 0; i < 64; i++) { + s[i] = 0; + } + haraka_S_absorb(s, in, inlen, 0x1F); + + haraka_S_squeezeblocks(out, outlen / 32, s); + out += (outlen / 32) * 32; + + if (outlen % 32) { + haraka_S_squeezeblocks(d, 1, s); + for (i = 0; i < outlen % 32; i++) { + out[i] = d[i]; + } + } +} + +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_haraka512_perm(unsigned char *out, const unsigned char *in) { + uint32_t w[16]; + uint64_t q[8], tmp_q; + unsigned int i, j; + + br_range_dec32le(w, 16, in); + for (i = 0; i < 4; i++) { + br_aes_ct64_interleave_in(&q[i], &q[i + 4], w + (i << 2)); + } + br_aes_ct64_ortho(q); + + /* AES rounds */ + for (i = 0; i < 5; i++) { + for (j = 0; j < 2; j++) { + br_aes_ct64_bitslice_Sbox(q); + shift_rows(q); + mix_columns(q); + add_round_key(q, tweaked512_rc64[2 * i + j]); + } + /* Mix states */ + for (j = 0; j < 8; j++) { + tmp_q = q[j]; + q[j] = (tmp_q & 0x0001000100010001) << 5 | + (tmp_q & 0x0002000200020002) << 12 | + (tmp_q & 0x0004000400040004) >> 1 | + (tmp_q & 0x0008000800080008) << 6 | + (tmp_q & 0x0020002000200020) << 9 | + (tmp_q & 0x0040004000400040) >> 4 | + (tmp_q & 0x0080008000800080) << 3 | + (tmp_q & 0x2100210021002100) >> 5 | + (tmp_q & 0x0210021002100210) << 2 | + (tmp_q & 0x0800080008000800) << 4 | + (tmp_q & 0x1000100010001000) >> 12 | + (tmp_q & 0x4000400040004000) >> 10 | + (tmp_q & 0x8400840084008400) >> 3; + } + } + + br_aes_ct64_ortho(q); + for (i = 0; i < 4; i ++) { + br_aes_ct64_interleave_out(w + (i << 2), q[i], q[i + 4]); + } + br_range_enc32le(out, w, 16); +} + +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_haraka512(unsigned char *out, const unsigned char *in) { + int i; + + unsigned char buf[64]; + + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_haraka512_perm(buf, in); + /* Feed-forward */ + for (i = 0; i < 64; i++) { + buf[i] = buf[i] ^ in[i]; + } + + /* Truncated */ + memcpy(out, buf + 8, 8); + memcpy(out + 8, buf + 24, 8); + memcpy(out + 16, buf + 32, 8); + memcpy(out + 24, buf + 48, 8); +} + + +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_haraka256(unsigned char *out, const unsigned char *in) { + uint32_t q[8], tmp_q; + int i, j; + + for (i = 0; i < 4; i++) { + q[2 * i] = br_dec32le(in + 4 * i); + q[2 * i + 1] = br_dec32le(in + 4 * i + 16); + } + br_aes_ct_ortho(q); + + /* AES rounds */ + for (i = 0; i < 5; i++) { + for (j = 0; j < 2; j++) { + br_aes_ct_bitslice_Sbox(q); + shift_rows32(q); + mix_columns32(q); + add_round_key32(q, tweaked256_rc32[2 * i + j]); + } + + /* Mix states */ + for (j = 0; j < 8; j++) { + tmp_q = q[j]; + q[j] = (tmp_q & 0x81818181) | + (tmp_q & 0x02020202) << 1 | + (tmp_q & 0x04040404) << 2 | + (tmp_q & 0x08080808) << 3 | + (tmp_q & 0x10101010) >> 3 | + (tmp_q & 0x20202020) >> 2 | + (tmp_q & 0x40404040) >> 1; + } + } + + br_aes_ct_ortho(q); + for (i = 0; i < 4; i++) { + br_enc32le(out + 4 * i, q[2 * i]); + br_enc32le(out + 4 * i + 16, q[2 * i + 1]); + } + + for (i = 0; i < 32; i++) { + out[i] ^= in[i]; + } +} + +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_haraka256_sk(unsigned char *out, const unsigned char *in) { + uint32_t q[8], tmp_q; + int i, j; + + for (i = 0; i < 4; i++) { + q[2 * i] = br_dec32le(in + 4 * i); + q[2 * i + 1] = br_dec32le(in + 4 * i + 16); + } + br_aes_ct_ortho(q); + + /* AES rounds */ + for (i = 0; i < 5; i++) { + for (j = 0; j < 2; j++) { + br_aes_ct_bitslice_Sbox(q); + shift_rows32(q); + mix_columns32(q); + add_round_key32(q, tweaked256_rc32_sseed[2 * i + j]); + } + + /* Mix states */ + for (j = 0; j < 8; j++) { + tmp_q = q[j]; + q[j] = (tmp_q & 0x81818181) | + (tmp_q & 0x02020202) << 1 | + (tmp_q & 0x04040404) << 2 | + (tmp_q & 0x08080808) << 3 | + (tmp_q & 0x10101010) >> 3 | + (tmp_q & 0x20202020) >> 2 | + (tmp_q & 0x40404040) >> 1; + } + } + + br_aes_ct_ortho(q); + for (i = 0; i < 4; i++) { + br_enc32le(out + 4 * i, q[2 * i]); + br_enc32le(out + 4 * i + 16, q[2 * i + 1]); + } + + for (i = 0; i < 32; i++) { + out[i] ^= in[i]; + } +} diff --git a/crypto_sign/sphincs-haraka-192s-simple/clean/haraka.h b/crypto_sign/sphincs-haraka-192s-simple/clean/haraka.h new file mode 100644 index 00000000..0c477c31 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192s-simple/clean/haraka.h @@ -0,0 +1,30 @@ +#ifndef SPX_HARAKA_H +#define SPX_HARAKA_H + +/* Tweak constants with seed */ +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_tweak_constants( + const unsigned char *pk_seed, const unsigned char *sk_seed, + unsigned long long seed_length); + +/* Haraka Sponge */ +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_haraka_S_inc_init(uint8_t *s_inc); +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_haraka_S_inc_absorb(uint8_t *s_inc, const uint8_t *m, size_t mlen); +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_haraka_S_inc_finalize(uint8_t *s_inc); +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_haraka_S_inc_squeeze(uint8_t *out, size_t outlen, uint8_t *s_inc); +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_haraka_S( + unsigned char *out, unsigned long long outlen, + const unsigned char *in, unsigned long long inlen); + +/* Applies the 512-bit Haraka permutation to in. */ +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_haraka512_perm(unsigned char *out, const unsigned char *in); + +/* Implementation of Haraka-512 */ +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_haraka512(unsigned char *out, const unsigned char *in); + +/* Implementation of Haraka-256 */ +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_haraka256(unsigned char *out, const unsigned char *in); + +/* Implementation of Haraka-256 using sk.seed constants */ +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_haraka256_sk(unsigned char *out, const unsigned char *in); + +#endif diff --git a/crypto_sign/sphincs-haraka-192s-simple/clean/hash.h b/crypto_sign/sphincs-haraka-192s-simple/clean/hash.h new file mode 100644 index 00000000..dd194f67 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192s-simple/clean/hash.h @@ -0,0 +1,22 @@ +#ifndef SPX_HASH_H +#define SPX_HASH_H + +#include + +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed); + +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen); + +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen); + +#endif diff --git a/crypto_sign/sphincs-haraka-192s-simple/clean/hash_haraka.c b/crypto_sign/sphincs-haraka-192s-simple/clean/hash_haraka.c new file mode 100644 index 00000000..1c18e3fb --- /dev/null +++ b/crypto_sign/sphincs-haraka-192s-simple/clean/hash_haraka.c @@ -0,0 +1,86 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "utils.h" + +#include "haraka.h" + +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed) { + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_tweak_constants(pub_seed, sk_seed, SPX_N); +} + +/* + * Computes PRF(key, addr), given a secret key of SPX_N bytes and an address + */ +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]) { + unsigned char buf[SPX_ADDR_BYTES]; + /* Since SPX_N may be smaller than 32, we need a temporary buffer. */ + unsigned char outbuf[32]; + + (void)key; /* Suppress an 'unused parameter' warning. */ + + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_addr_to_bytes(buf, addr); + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_haraka256_sk(outbuf, buf); + memcpy(out, outbuf, SPX_N); +} + +/** + * Computes the message-dependent randomness R, using a secret seed and an + * optional randomization value as well as the message. + */ +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen) { + uint8_t s_inc[65]; + + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_haraka_S_inc_init(s_inc); + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_haraka_S_inc_absorb(s_inc, sk_prf, SPX_N); + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_haraka_S_inc_absorb(s_inc, optrand, SPX_N); + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_haraka_S_inc_absorb(s_inc, m, mlen); + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_haraka_S_inc_finalize(s_inc); + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_haraka_S_inc_squeeze(R, SPX_N, s_inc); +} + +/** + * Computes the message hash using R, the public key, and the message. + * Outputs the message digest and the index of the leaf. The index is split in + * the tree index and the leaf index, for convenient copying to an address. + */ +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen) { +#define SPX_TREE_BITS (SPX_TREE_HEIGHT * (SPX_D - 1)) +#define SPX_TREE_BYTES ((SPX_TREE_BITS + 7) / 8) +#define SPX_LEAF_BITS SPX_TREE_HEIGHT +#define SPX_LEAF_BYTES ((SPX_LEAF_BITS + 7) / 8) +#define SPX_DGST_BYTES (SPX_FORS_MSG_BYTES + SPX_TREE_BYTES + SPX_LEAF_BYTES) + + unsigned char buf[SPX_DGST_BYTES]; + unsigned char *bufp = buf; + uint8_t s_inc[65]; + + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_haraka_S_inc_init(s_inc); + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_haraka_S_inc_absorb(s_inc, R, SPX_N); + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_haraka_S_inc_absorb(s_inc, pk, SPX_PK_BYTES); + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_haraka_S_inc_absorb(s_inc, m, mlen); + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_haraka_S_inc_finalize(s_inc); + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_haraka_S_inc_squeeze(buf, SPX_DGST_BYTES, s_inc); + + memcpy(digest, bufp, SPX_FORS_MSG_BYTES); + bufp += SPX_FORS_MSG_BYTES; + + *tree = PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_bytes_to_ull(bufp, SPX_TREE_BYTES); + *tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS); + bufp += SPX_TREE_BYTES; + + *leaf_idx = (uint32_t)PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_bytes_to_ull( + bufp, SPX_LEAF_BYTES); + *leaf_idx &= (~(uint32_t)0) >> (32 - SPX_LEAF_BITS); +} diff --git a/crypto_sign/sphincs-haraka-192s-simple/clean/params.h b/crypto_sign/sphincs-haraka-192s-simple/clean/params.h new file mode 100644 index 00000000..483b5374 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192s-simple/clean/params.h @@ -0,0 +1,53 @@ +#ifndef SPX_PARAMS_H +#define SPX_PARAMS_H + +/* Hash output length in bytes. */ +#define SPX_N 24 +/* Height of the hypertree. */ +#define SPX_FULL_HEIGHT 64 +/* Number of subtree layer. */ +#define SPX_D 8 +/* FORS tree dimensions. */ +#define SPX_FORS_HEIGHT 16 +#define SPX_FORS_TREES 14 +/* Winternitz parameter, */ +#define SPX_WOTS_W 16 + +/* The hash function is defined by linking a different hash.c file, as opposed + to setting a #define constant. */ + +/* For clarity */ +#define SPX_ADDR_BYTES 32 + +/* WOTS parameters. */ +#define SPX_WOTS_LOGW 4 + +#define SPX_WOTS_LEN1 (8 * SPX_N / SPX_WOTS_LOGW) + +/* SPX_WOTS_LEN2 is floor(log(len_1 * (w - 1)) / log(w)) + 1; we precompute */ +#define SPX_WOTS_LEN2 3 + +#define SPX_WOTS_LEN (SPX_WOTS_LEN1 + SPX_WOTS_LEN2) +#define SPX_WOTS_BYTES (SPX_WOTS_LEN * SPX_N) +#define SPX_WOTS_PK_BYTES SPX_WOTS_BYTES + +/* Subtree size. */ +#define SPX_TREE_HEIGHT (SPX_FULL_HEIGHT / SPX_D) + +/* FORS parameters. */ +#define SPX_FORS_MSG_BYTES ((SPX_FORS_HEIGHT * SPX_FORS_TREES + 7) / 8) +#define SPX_FORS_BYTES ((SPX_FORS_HEIGHT + 1) * SPX_FORS_TREES * SPX_N) +#define SPX_FORS_PK_BYTES SPX_N + +/* Resulting SPX sizes. */ +#define SPX_BYTES (SPX_N + SPX_FORS_BYTES + SPX_D * SPX_WOTS_BYTES +\ + SPX_FULL_HEIGHT * SPX_N) +#define SPX_PK_BYTES (2 * SPX_N) +#define SPX_SK_BYTES (2 * SPX_N + SPX_PK_BYTES) + +/* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ +#define SPX_OPTRAND_BYTES 32 + +#endif diff --git a/crypto_sign/sphincs-haraka-192s-simple/clean/sign.c b/crypto_sign/sphincs-haraka-192s-simple/clean/sign.c new file mode 100644 index 00000000..b6a883e0 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192s-simple/clean/sign.c @@ -0,0 +1,344 @@ +#include +#include +#include + +#include "address.h" +#include "api.h" +#include "fors.h" +#include "hash.h" +#include "params.h" +#include "randombytes.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +/** + * Computes the leaf at a given address. First generates the WOTS key pair, + * then computes leaf by hashing horizontally. + */ +static void wots_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t tree_addr[8]) { + unsigned char pk[SPX_WOTS_BYTES]; + uint32_t wots_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, addr_idx); + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_wots_gen_pk( + pk, sk_seed, pub_seed, wots_addr); + + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_thash_WOTS_LEN( + leaf, pk, pub_seed, wots_pk_addr); +} + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_crypto_sign_secretkeybytes(void) { + return PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_CRYPTO_SECRETKEYBYTES; +} + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_crypto_sign_publickeybytes(void) { + return PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES; +} + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_crypto_sign_bytes(void) { + return PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_CRYPTO_BYTES; +} + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_crypto_sign_seedbytes(void) { + return PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_CRYPTO_SEEDBYTES; +} + +/* + * Generates an SPX key pair given a seed of length + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed) { + /* We do not need the auth path in key generation, but it simplifies the + code to have just one treehash routine that computes both root and path + in one function. */ + unsigned char auth_path[SPX_TREE_HEIGHT * SPX_N]; + uint32_t top_tree_addr[8] = {0}; + + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_layer_addr( + top_tree_addr, SPX_D - 1); + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_type( + top_tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */ + memcpy(sk, seed, PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_CRYPTO_SEEDBYTES); + + memcpy(pk, sk + 2 * SPX_N, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_initialize_hash_function(pk, sk); + + /* Compute root node of the top-most subtree. */ + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_treehash_TREE_HEIGHT( + sk + 3 * SPX_N, auth_path, sk, sk + 2 * SPX_N, 0, 0, + wots_gen_leaf, top_tree_addr); + + memcpy(pk + SPX_N, sk + 3 * SPX_N, SPX_N); + + return 0; +} + +/* + * Generates an SPX key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk) { + unsigned char seed[PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_CRYPTO_SEEDBYTES]; + randombytes(seed, PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_CRYPTO_SEEDBYTES); + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_crypto_sign_seed_keypair( + pk, sk, seed); + + return 0; +} + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + const unsigned char *sk_seed = sk; + const unsigned char *sk_prf = sk + SPX_N; + const unsigned char *pk = sk + 2 * SPX_N; + const unsigned char *pub_seed = pk; + + unsigned char optrand[SPX_N]; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char root[SPX_N]; + uint32_t i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_initialize_hash_function( + pub_seed, sk_seed); + + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ + randombytes(optrand, SPX_N); + /* Compute the digest randomization value. */ + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_gen_message_random( + sig, sk_prf, optrand, m, mlen); + + /* Derive the message digest and leaf index from R, PK and M. */ + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Sign the message hash using FORS. */ + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_fors_sign( + sig, root, mhash, sk_seed, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Compute a WOTS signature. */ + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_wots_sign( + sig, root, sk_seed, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the authentication path for the used WOTS leaf. */ + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_treehash_TREE_HEIGHT( + root, sig, sk_seed, pub_seed, idx_leaf, 0, + wots_gen_leaf, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + *siglen = SPX_BYTES; + + return 0; +} + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk) { + const unsigned char *pub_seed = pk; + const unsigned char *pub_root = pk + SPX_N; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char wots_pk[SPX_WOTS_BYTES]; + unsigned char root[SPX_N]; + unsigned char leaf[SPX_N]; + unsigned int i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + if (siglen != SPX_BYTES) { + return -1; + } + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_initialize_hash_function( + pub_seed, NULL); + + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + /* Derive the message digest and leaf index from R || PK || M. */ + /* The additional SPX_N is a result of the hash domain separator. */ + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + /* Layer correctly defaults to 0, so no need to set_layer_addr */ + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_fors_pk_from_sig( + root, sig, mhash, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + /* For each subtree.. */ + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + + /* The WOTS public key is only correct if the signature was correct. */ + /* Initially, root is the FORS pk, but on subsequent iterations it is + the root of the subtree below the currently processed subtree. */ + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_wots_pk_from_sig( + wots_pk, sig, root, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the leaf node using the WOTS public key. */ + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_thash_WOTS_LEN( + leaf, wots_pk, pub_seed, wots_pk_addr); + + /* Compute the root node of this subtree. */ + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_compute_root( + root, leaf, idx_leaf, 0, sig, SPX_TREE_HEIGHT, + pub_seed, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + /* Check if the root node equals the root node in the public key. */ + if (memcmp(root, pub_root, SPX_N) != 0) { + return -1; + } + + return 0; +} + + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + size_t siglen; + + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_crypto_sign_signature( + sm, &siglen, m, mlen, sk); + + memmove(sm + SPX_BYTES, m, mlen); + *smlen = siglen + mlen; + + return 0; +} + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk) { + /* The API caller does not necessarily know what size a signature should be + but SPHINCS+ signatures are always exactly SPX_BYTES. */ + if (smlen < SPX_BYTES) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + *mlen = smlen - SPX_BYTES; + + if (PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_crypto_sign_verify( + sm, SPX_BYTES, sm + SPX_BYTES, *mlen, pk)) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + /* If verification was successful, move the message to the right place. */ + memmove(m, sm + SPX_BYTES, *mlen); + + return 0; +} diff --git a/crypto_sign/sphincs-haraka-192s-simple/clean/thash.h b/crypto_sign/sphincs-haraka-192s-simple/clean/thash.h new file mode 100644 index 00000000..37e6145b --- /dev/null +++ b/crypto_sign/sphincs-haraka-192s-simple/clean/thash.h @@ -0,0 +1,22 @@ +#ifndef SPX_THASH_H +#define SPX_THASH_H + +#include + +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-haraka-192s-simple/clean/thash_haraka_simple.c b/crypto_sign/sphincs-haraka-192s-simple/clean/thash_haraka_simple.c new file mode 100644 index 00000000..609785cf --- /dev/null +++ b/crypto_sign/sphincs-haraka-192s-simple/clean/thash_haraka_simple.c @@ -0,0 +1,78 @@ +#include +#include + +#include "address.h" +#include "params.h" +#include "thash.h" + +#include "haraka.h" + +/** + * Takes an array of inblocks concatenated arrays of SPX_N bytes. + */ +static void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_thash( + unsigned char *out, unsigned char *buf, + const unsigned char *in, unsigned int inblocks, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char outbuf[32]; + unsigned char buf_tmp[64]; + + (void)pub_seed; /* Suppress an 'unused parameter' warning. */ + + if (inblocks == 1) { + /* F function */ + /* Since SPX_N may be smaller than 32, we need a temporary buffer. */ + memset(buf_tmp, 0, 64); + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_addr_to_bytes(buf_tmp, addr); + memcpy(buf_tmp + SPX_ADDR_BYTES, in, SPX_N); + + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_haraka512(outbuf, buf_tmp); + memcpy(out, outbuf, SPX_N); + } else { + /* All other tweakable hashes*/ + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_addr_to_bytes(buf, addr); + memcpy(buf + SPX_ADDR_BYTES, in, inblocks * SPX_N); + + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_haraka_S( + out, SPX_N, buf, SPX_ADDR_BYTES + inblocks * SPX_N); + } +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_ADDR_BYTES + 1 * SPX_N]; + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_thash( + out, buf, in, 1, pub_seed, addr); +} + +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_ADDR_BYTES + 2 * SPX_N]; + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_thash( + out, buf, in, 2, pub_seed, addr); +} + +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_ADDR_BYTES + SPX_WOTS_LEN * SPX_N]; + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_thash( + out, buf, in, SPX_WOTS_LEN, pub_seed, addr); +} + +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_ADDR_BYTES + SPX_FORS_TREES * SPX_N]; + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_thash( + out, buf, in, SPX_FORS_TREES, pub_seed, addr); +} diff --git a/crypto_sign/sphincs-haraka-192s-simple/clean/utils.c b/crypto_sign/sphincs-haraka-192s-simple/clean/utils.c new file mode 100644 index 00000000..f278b046 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192s-simple/clean/utils.c @@ -0,0 +1,192 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in) { + + /* Iterate over out in decreasing order, for big-endianness. */ + for (size_t i = outlen; i > 0; i--) { + out[i - 1] = in & 0xff; + in = in >> 8; + } +} + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen) { + unsigned long long retval = 0; + + for (size_t i = 0; i < inlen; i++) { + retval |= ((unsigned long long)in[i]) << (8 * (inlen - 1 - i)); + } + return retval; +} + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + unsigned char buffer[2 * SPX_N]; + + /* If leaf_idx is odd (last bit = 1), current path element is a right child + and auth_path has to go left. Otherwise it is the other way around. */ + if (leaf_idx & 1) { + memcpy(buffer + SPX_N, leaf, SPX_N); + memcpy(buffer, auth_path, SPX_N); + } else { + memcpy(buffer, leaf, SPX_N); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + + for (i = 0; i < tree_height - 1; i++) { + leaf_idx >>= 1; + idx_offset >>= 1; + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_tree_height(addr, i + 1); + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + + /* Pick the right or left neighbor, depending on parity of the node. */ + if (leaf_idx & 1) { + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_thash_2( + buffer + SPX_N, buffer, pub_seed, addr); + memcpy(buffer, auth_path, SPX_N); + } else { + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_thash_2( + buffer, buffer, pub_seed, addr); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + } + + /* The last iteration is exceptional; we do not copy an auth_path node. */ + leaf_idx >>= 1; + idx_offset >>= 1; + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_tree_height(addr, tree_height); + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_thash_2( + root, buffer, pub_seed, addr); +} + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +static void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_treehash( + unsigned char *root, unsigned char *auth_path, + unsigned char *stack, unsigned int *heights, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned int offset = 0; + uint32_t idx; + uint32_t tree_idx; + + for (idx = 0; idx < (uint32_t)(1 << tree_height); idx++) { + /* Add the next leaf node to the stack. */ + gen_leaf(stack + offset * SPX_N, + sk_seed, pub_seed, idx + idx_offset, tree_addr); + offset++; + heights[offset - 1] = 0; + + /* If this is a node we need for the auth path.. */ + if ((leaf_idx ^ 0x1) == idx) { + memcpy(auth_path, stack + (offset - 1)*SPX_N, SPX_N); + } + + /* While the top-most nodes are of equal height.. */ + while (offset >= 2 && heights[offset - 1] == heights[offset - 2]) { + /* Compute index of the new node, in the next layer. */ + tree_idx = (idx >> (heights[offset - 1] + 1)); + + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_tree_height( + tree_addr, heights[offset - 1] + 1); + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_tree_index( + tree_addr, tree_idx + (idx_offset >> (heights[offset - 1] + 1))); + /* Hash the top-most nodes from the stack together. */ + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_thash_2( + stack + (offset - 2)*SPX_N, stack + (offset - 2)*SPX_N, + pub_seed, tree_addr); + offset--; + /* Note that the top-most node is now one layer higher. */ + heights[offset - 1]++; + + /* If this is a node we need for the auth path.. */ + if (((leaf_idx >> heights[offset - 1]) ^ 0x1) == tree_idx) { + memcpy(auth_path + heights[offset - 1]*SPX_N, + stack + (offset - 1)*SPX_N, SPX_N); + } + } + } + memcpy(root, stack, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_FORS_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_FORS_HEIGHT + 1]; + + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_FORS_HEIGHT, gen_leaf, tree_addr); +} + +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_TREE_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_TREE_HEIGHT + 1]; + + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_TREE_HEIGHT, gen_leaf, tree_addr); +} diff --git a/crypto_sign/sphincs-haraka-192s-simple/clean/utils.h b/crypto_sign/sphincs-haraka-192s-simple/clean/utils.h new file mode 100644 index 00000000..ca9cdd84 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192s-simple/clean/utils.h @@ -0,0 +1,60 @@ +#ifndef SPX_UTILS_H +#define SPX_UTILS_H + +#include "params.h" +#include +#include + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in); + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen); + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-haraka-192s-simple/clean/wots.c b/crypto_sign/sphincs-haraka-192s-simple/clean/wots.c new file mode 100644 index 00000000..3f0d9161 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192s-simple/clean/wots.c @@ -0,0 +1,161 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +// TODO clarify address expectations, and make them more uniform. +// TODO i.e. do we expect types to be set already? +// TODO and do we expect modifications or copies? + +/** + * Computes the starting value for a chain, i.e. the secret key. + * Expects the address to be complete up to the chain address. + */ +static void wots_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t wots_addr[8]) { + /* Make sure that the hash address is actually zeroed. */ + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_hash_addr(wots_addr, 0); + + /* Generate sk element. */ + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_prf_addr(sk, sk_seed, wots_addr); +} + +/** + * Computes the chaining function. + * out and in have to be n-byte arrays. + * + * Interprets in as start-th value of the chain. + * addr has to contain the address of the chain. + */ +static void gen_chain(unsigned char *out, const unsigned char *in, + unsigned int start, unsigned int steps, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + /* Initialize out with the value at position 'start'. */ + memcpy(out, in, SPX_N); + + /* Iterate 'steps' calls to the hash function. */ + for (i = start; i < (start + steps) && i < SPX_WOTS_W; i++) { + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_hash_addr(addr, i); + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_thash_1( + out, out, pub_seed, addr); + } +} + +/** + * base_w algorithm as described in draft. + * Interprets an array of bytes as integers in base w. + * This only works when log_w is a divisor of 8. + */ +static void base_w(unsigned int *output, const size_t out_len, + const unsigned char *input) { + size_t in = 0; + size_t out = 0; + unsigned char total = 0; + unsigned int bits = 0; + size_t consumed; + + for (consumed = 0; consumed < out_len; consumed++) { + if (bits == 0) { + total = input[in]; + in++; + bits += 8; + } + bits -= SPX_WOTS_LOGW; + output[out] = (unsigned int)((total >> bits) & (SPX_WOTS_W - 1)); + out++; + } +} + +/* Computes the WOTS+ checksum over a message (in base_w). */ +static void wots_checksum(unsigned int *csum_base_w, + const unsigned int *msg_base_w) { + unsigned int csum = 0; + unsigned char csum_bytes[(SPX_WOTS_LEN2 * SPX_WOTS_LOGW + 7) / 8]; + unsigned int i; + + /* Compute checksum. */ + for (i = 0; i < SPX_WOTS_LEN1; i++) { + csum += SPX_WOTS_W - 1 - msg_base_w[i]; + } + + /* Convert checksum to base_w. */ + /* Make sure expected empty zero bits are the least significant bits. */ + csum = csum << (8 - ((SPX_WOTS_LEN2 * SPX_WOTS_LOGW) % 8)); + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_ull_to_bytes( + csum_bytes, sizeof(csum_bytes), csum); + base_w(csum_base_w, SPX_WOTS_LEN2, csum_bytes); +} + +/* Takes a message and derives the matching chain lengths. */ +static void chain_lengths(unsigned int *lengths, const unsigned char *msg) { + base_w(lengths, SPX_WOTS_LEN1, msg); + wots_checksum(lengths + SPX_WOTS_LEN1, lengths); +} + +/** + * WOTS key generation. Takes a 32 byte sk_seed, expands it to WOTS private key + * elements and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(pk + i * SPX_N, sk_seed, addr); + gen_chain(pk + i * SPX_N, pk + i * SPX_N, + 0, SPX_WOTS_W - 1, pub_seed, addr); + } +} + +/** + * Takes a n-byte message and the 32-byte sk_see to compute a signature 'sig'. + */ +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(sig + i * SPX_N, sk_seed, addr); + gen_chain(sig + i * SPX_N, sig + i * SPX_N, 0, lengths[i], pub_seed, addr); + } +} + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_set_chain_addr(addr, i); + gen_chain(pk + i * SPX_N, sig + i * SPX_N, + lengths[i], SPX_WOTS_W - 1 - lengths[i], pub_seed, addr); + } +} diff --git a/crypto_sign/sphincs-haraka-192s-simple/clean/wots.h b/crypto_sign/sphincs-haraka-192s-simple/clean/wots.h new file mode 100644 index 00000000..4811bb71 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192s-simple/clean/wots.h @@ -0,0 +1,38 @@ +#ifndef SPX_WOTS_H +#define SPX_WOTS_H + +#include "params.h" +#include + +/** + * WOTS key generation. Takes a 32 byte seed for the private key, expands it to + * a full WOTS private key and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * Takes a n-byte message and the 32-byte seed for the private key to compute a + * signature that is placed at 'sig'. + */ +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]); + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-haraka-256f-robust/META.yml b/crypto_sign/sphincs-haraka-256f-robust/META.yml new file mode 100644 index 00000000..69b7ba18 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256f-robust/META.yml @@ -0,0 +1,27 @@ +name: SPHINCS+ +type: signature +claimed-nist-level: 5 +length-public-key: 64 +length-secret-key: 128 +length-signature: 49216 +testvectors-sha256: b5e3a1c1dbb45751f2a4c9323a5d900b30f38e4c7e2943e234a5b9526de1146c +principal-submitter: Andreas Hülsing +auxiliary-submitters: + - Jean-Philippe Aumasson + - Daniel J. Bernstein, + - Christoph Dobraunig + - Maria Eichlseder + - Scott Fluhrer + - Stefan-Lukas Gazdag + - Panos Kampanakis + - Stefan Kölbl + - Tanja Lange + - Martin M. Lauridsen + - Florian Mendel + - Ruben Niederhagen + - Christian Rechberger + - Joost Rijneveld + - Peter Schwabe +implementations: + - name: clean + version: https://github.com/sphincs/sphincsplus/commit/77755c94d0bc744478044d6efbb888dc13156441 diff --git a/crypto_sign/sphincs-haraka-256f-robust/clean/LICENSE b/crypto_sign/sphincs-haraka-256f-robust/clean/LICENSE new file mode 100644 index 00000000..670154e3 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256f-robust/clean/LICENSE @@ -0,0 +1,116 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + diff --git a/crypto_sign/sphincs-haraka-256f-robust/clean/Makefile b/crypto_sign/sphincs-haraka-256f-robust/clean/Makefile new file mode 100644 index 00000000..f714b735 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256f-robust/clean/Makefile @@ -0,0 +1,20 @@ +# This Makefile can be used with GNU Make or BSD Make + +LIB=libsphincs-haraka-256f-robust_clean.a + +HEADERS = params.h address.h wots.h utils.h fors.h api.h hash.h thash.h haraka.h +OBJECTS = address.o wots.o utils.o fors.o sign.o hash_haraka.o thash_haraka_robust.o haraka.o + +CFLAGS=-O3 -Wall -Wconversion -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) + +all: $(LIB) + +%.o: %.c $(HEADERS) + $(CC) $(CFLAGS) -c -o $@ $< + +$(LIB): $(OBJECTS) + $(AR) -r $@ $(OBJECTS) + +clean: + $(RM) $(OBJECTS) + $(RM) $(LIB) diff --git a/crypto_sign/sphincs-haraka-256f-robust/clean/Makefile.Microsoft_nmake b/crypto_sign/sphincs-haraka-256f-robust/clean/Makefile.Microsoft_nmake new file mode 100644 index 00000000..8473eee7 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256f-robust/clean/Makefile.Microsoft_nmake @@ -0,0 +1,19 @@ +# This Makefile can be used with Microsoft Visual Studio's nmake using the command: +# nmake /f Makefile.Microsoft_nmake + +LIBRARY=libsphincs-haraka-256f-robust_clean.lib +OBJECTS=address.obj wots.obj utils.obj fors.obj sign.obj hash_haraka.obj thash_haraka_robust.obj haraka.obj + +CFLAGS=/nologo /I ..\..\..\common /W4 /WX + +all: $(LIBRARY) + +# Make sure objects are recompiled if headers change. +$(OBJECTS): *.h + +$(LIBRARY): $(OBJECTS) + LIB.EXE /NOLOGO /WX /OUT:$@ $** + +clean: + -DEL $(OBJECTS) + -DEL $(LIBRARY) diff --git a/crypto_sign/sphincs-haraka-256f-robust/clean/address.c b/crypto_sign/sphincs-haraka-256f-robust/clean/address.c new file mode 100644 index 00000000..11da5bb2 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256f-robust/clean/address.c @@ -0,0 +1,78 @@ +#include + +#include "address.h" +#include "params.h" +#include "utils.h" + +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]) { + int i; + + for (i = 0; i < 8; i++) { + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_ull_to_bytes( + bytes + i * 4, 4, addr[i]); + } +} + +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer) { + addr[0] = layer; +} + +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree) { + addr[1] = 0; + addr[2] = (uint32_t) (tree >> 32); + addr[3] = (uint32_t) tree; +} + +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_type( + uint32_t addr[8], uint32_t type) { + addr[4] = type; +} + +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; +} + +/* These functions are used for OTS addresses. */ + +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair) { + addr[5] = keypair; +} + +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; + out[5] = in[5]; +} + +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain) { + addr[6] = chain; +} + +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash) { + addr[7] = hash; +} + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height) { + addr[6] = tree_height; +} + +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index) { + addr[7] = tree_index; +} diff --git a/crypto_sign/sphincs-haraka-256f-robust/clean/address.h b/crypto_sign/sphincs-haraka-256f-robust/clean/address.h new file mode 100644 index 00000000..9a4abe1e --- /dev/null +++ b/crypto_sign/sphincs-haraka-256f-robust/clean/address.h @@ -0,0 +1,50 @@ +#ifndef SPX_ADDRESS_H +#define SPX_ADDRESS_H + +#include + +#define SPX_ADDR_TYPE_WOTS 0 +#define SPX_ADDR_TYPE_WOTSPK 1 +#define SPX_ADDR_TYPE_HASHTREE 2 +#define SPX_ADDR_TYPE_FORSTREE 3 +#define SPX_ADDR_TYPE_FORSPK 4 + +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer); + +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree); + +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_type( + uint32_t addr[8], uint32_t type); + +/* Copies the layer and tree part of one address into the other */ +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for WOTS and FORS addresses. */ + +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair); + +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain); + +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash); + +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height); + +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index); + +#endif diff --git a/crypto_sign/sphincs-haraka-256f-robust/clean/api.h b/crypto_sign/sphincs-haraka-256f-robust/clean/api.h new file mode 100644 index 00000000..0f9b18b2 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256f-robust/clean/api.h @@ -0,0 +1,78 @@ +#ifndef PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_API_H +#define PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_API_H + +#include +#include + +#define PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_CRYPTO_ALGNAME "SPHINCS+" + +#define PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_CRYPTO_SECRETKEYBYTES 128 +#define PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES 64 +#define PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_CRYPTO_BYTES 49216 +#define PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_CRYPTO_SEEDBYTES 96 + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_crypto_sign_secretkeybytes(void); + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_crypto_sign_publickeybytes(void); + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_crypto_sign_bytes(void); + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_crypto_sign_seedbytes(void); + +/* + * Generates a SPHINCS+ key pair given a seed. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed); + +/* + * Generates a SPHINCS+ key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk); + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk); + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk); + +#endif diff --git a/crypto_sign/sphincs-haraka-256f-robust/clean/fors.c b/crypto_sign/sphincs-haraka-256f-robust/clean/fors.c new file mode 100644 index 00000000..ff62962b --- /dev/null +++ b/crypto_sign/sphincs-haraka-256f-robust/clean/fors.c @@ -0,0 +1,164 @@ +#include +#include +#include + +#include "address.h" +#include "fors.h" +#include "hash.h" +#include "thash.h" +#include "utils.h" + +static void fors_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_prf_addr( + sk, sk_seed, fors_leaf_addr); +} + +static void fors_sk_to_leaf(unsigned char *leaf, const unsigned char *sk, + const unsigned char *pub_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_thash_1( + leaf, sk, pub_seed, fors_leaf_addr); +} + +static void fors_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t fors_tree_addr[8]) { + uint32_t fors_leaf_addr[8] = {0}; + + /* Only copy the parts that must be kept in fors_leaf_addr. */ + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_copy_keypair_addr( + fors_leaf_addr, fors_tree_addr); + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_type( + fors_leaf_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_tree_index( + fors_leaf_addr, addr_idx); + + fors_gen_sk(leaf, sk_seed, fors_leaf_addr); + fors_sk_to_leaf(leaf, leaf, pub_seed, fors_leaf_addr); +} + +/** + * Interprets m as SPX_FORS_HEIGHT-bit unsigned integers. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + * Assumes indices has space for SPX_FORS_TREES integers. + */ +static void message_to_indices(uint32_t *indices, const unsigned char *m) { + unsigned int i, j; + unsigned int offset = 0; + + for (i = 0; i < SPX_FORS_TREES; i++) { + indices[i] = 0; + for (j = 0; j < SPX_FORS_HEIGHT; j++) { + indices[i] ^= (((uint32_t)m[offset >> 3] >> (offset & 0x7)) & 0x1) << j; + offset++; + } + } +} + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Include the secret key part that produces the selected leaf node. */ + fors_gen_sk(sig, sk_seed, fors_tree_addr); + sig += SPX_N; + + /* Compute the authentication path for this leaf node. */ + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_treehash_FORS_HEIGHT( + roots + i * SPX_N, sig, sk_seed, pub_seed, + indices[i], idx_offset, fors_gen_leaf, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + unsigned char leaf[SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Derive the leaf from the included secret key part. */ + fors_sk_to_leaf(leaf, sig, pub_seed, fors_tree_addr); + sig += SPX_N; + + /* Derive the corresponding root node of this tree. */ + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_compute_root( + roots + i * SPX_N, leaf, indices[i], idx_offset, sig, + SPX_FORS_HEIGHT, pub_seed, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} diff --git a/crypto_sign/sphincs-haraka-256f-robust/clean/fors.h b/crypto_sign/sphincs-haraka-256f-robust/clean/fors.h new file mode 100644 index 00000000..e87300ac --- /dev/null +++ b/crypto_sign/sphincs-haraka-256f-robust/clean/fors.h @@ -0,0 +1,30 @@ +#ifndef SPX_FORS_H +#define SPX_FORS_H + +#include + +#include "params.h" + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]); + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-haraka-256f-robust/clean/haraka.c b/crypto_sign/sphincs-haraka-256f-robust/clean/haraka.c new file mode 100644 index 00000000..242feed1 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256f-robust/clean/haraka.c @@ -0,0 +1,965 @@ +/* + * Constant time implementation of the Haraka hash function. + * + * The bit-sliced implementation of the AES round functions are + * based on the AES implementation in BearSSL written + * by Thomas Pornin + */ + +#include +#include +#include + +#include "haraka.h" + +#define HARAKAS_RATE 32 + +static const uint64_t haraka512_rc64[10][8] = { + {0x24cf0ab9086f628b, 0xbdd6eeecc83b8382, 0xd96fb0306cdad0a7, 0xaace082ac8f95f89, 0x449d8e8870d7041f, 0x49bb2f80b2b3e2f8, 0x0569ae98d93bb258, 0x23dc9691e7d6a4b1}, + {0xd8ba10ede0fe5b6e, 0x7ecf7dbe424c7b8e, 0x6ea9949c6df62a31, 0xbf3f3c97ec9c313e, 0x241d03a196a1861e, 0xead3a51116e5a2ea, 0x77d479fcad9574e3, 0x18657a1af894b7a0}, + {0x10671e1a7f595522, 0xd9a00ff675d28c7b, 0x2f1edf0d2b9ba661, 0xb8ff58b8e3de45f9, 0xee29261da9865c02, 0xd1532aa4b50bdf43, 0x8bf858159b231bb1, 0xdf17439d22d4f599}, + {0xdd4b2f0870b918c0, 0x757a81f3b39b1bb6, 0x7a5c556898952e3f, 0x7dd70a16d915d87a, 0x3ae61971982b8301, 0xc3ab319e030412be, 0x17c0033ac094a8cb, 0x5a0630fc1a8dc4ef}, + {0x17708988c1632f73, 0xf92ddae090b44f4f, 0x11ac0285c43aa314, 0x509059941936b8ba, 0xd03e152fa2ce9b69, 0x3fbcbcb63a32998b, 0x6204696d692254f7, 0x915542ed93ec59b4}, + {0xf4ed94aa8879236e, 0xff6cb41cd38e03c0, 0x069b38602368aeab, 0x669495b820f0ddba, 0xf42013b1b8bf9e3d, 0xcf935efe6439734d, 0xbc1dcf42ca29e3f8, 0x7e6d3ed29f78ad67}, + {0xf3b0f6837ffcddaa, 0x3a76faef934ddf41, 0xcec7ae583a9c8e35, 0xe4dd18c68f0260af, 0x2c0e5df1ad398eaa, 0x478df5236ae22e8c, 0xfb944c46fe865f39, 0xaa48f82f028132ba}, + {0x231b9ae2b76aca77, 0x292a76a712db0b40, 0x5850625dc8134491, 0x73137dd469810fb5, 0x8a12a6a202a474fd, 0xd36fd9daa78bdb80, 0xb34c5e733505706f, 0xbaf1cdca818d9d96}, + {0x2e99781335e8c641, 0xbddfe5cce47d560e, 0xf74e9bf32e5e040c, 0x1d7a709d65996be9, 0x670df36a9cf66cdd, 0xd05ef84a176a2875, 0x0f888e828cb1c44e, 0x1a79e9c9727b052c}, + {0x83497348628d84de, 0x2e9387d51f22a754, 0xb000068da2f852d6, 0x378c9e1190fd6fe5, 0x870027c316de7293, 0xe51a9d4462e047bb, 0x90ecf7f8c6251195, 0x655953bfbed90a9c}, +}; + +static uint64_t tweaked512_rc64[10][8]; +static uint32_t tweaked256_rc32[10][8]; +static uint32_t tweaked256_rc32_sseed[10][8]; + +static inline uint32_t br_dec32le(const unsigned char *src) { + return (uint32_t)src[0] + | ((uint32_t)src[1] << 8) + | ((uint32_t)src[2] << 16) + | ((uint32_t)src[3] << 24); +} + +static void br_range_dec32le(uint32_t *v, size_t num, const unsigned char *src) { + while (num-- > 0) { + *v ++ = br_dec32le(src); + src += 4; + } +} + +static inline void br_enc32le(unsigned char *dst, uint32_t x) { + dst[0] = (unsigned char)x; + dst[1] = (unsigned char)(x >> 8); + dst[2] = (unsigned char)(x >> 16); + dst[3] = (unsigned char)(x >> 24); +} + + +static void br_range_enc32le(unsigned char *dst, const uint32_t *v, size_t num) { + while (num-- > 0) { + br_enc32le(dst, *v ++); + dst += 4; + } +} + +static void br_aes_ct64_bitslice_Sbox(uint64_t *q) { + /* + * This S-box implementation is a straightforward translation of + * the circuit described by Boyar and Peralta in "A new + * combinational logic minimization technique with applications + * to cryptology" (https://eprint.iacr.org/2009/191.pdf). + * + * Note that variables x* (input) and s* (output) are numbered + * in "reverse" order (x0 is the high bit, x7 is the low bit). + */ + + uint64_t x0, x1, x2, x3, x4, x5, x6, x7; + uint64_t y1, y2, y3, y4, y5, y6, y7, y8, y9; + uint64_t y10, y11, y12, y13, y14, y15, y16, y17, y18, y19; + uint64_t y20, y21; + uint64_t z0, z1, z2, z3, z4, z5, z6, z7, z8, z9; + uint64_t z10, z11, z12, z13, z14, z15, z16, z17; + uint64_t t0, t1, t2, t3, t4, t5, t6, t7, t8, t9; + uint64_t t10, t11, t12, t13, t14, t15, t16, t17, t18, t19; + uint64_t t20, t21, t22, t23, t24, t25, t26, t27, t28, t29; + uint64_t t30, t31, t32, t33, t34, t35, t36, t37, t38, t39; + uint64_t t40, t41, t42, t43, t44, t45, t46, t47, t48, t49; + uint64_t t50, t51, t52, t53, t54, t55, t56, t57, t58, t59; + uint64_t t60, t61, t62, t63, t64, t65, t66, t67; + uint64_t s0, s1, s2, s3, s4, s5, s6, s7; + + x0 = q[7]; + x1 = q[6]; + x2 = q[5]; + x3 = q[4]; + x4 = q[3]; + x5 = q[2]; + x6 = q[1]; + x7 = q[0]; + + /* + * Top linear transformation. + */ + y14 = x3 ^ x5; + y13 = x0 ^ x6; + y9 = x0 ^ x3; + y8 = x0 ^ x5; + t0 = x1 ^ x2; + y1 = t0 ^ x7; + y4 = y1 ^ x3; + y12 = y13 ^ y14; + y2 = y1 ^ x0; + y5 = y1 ^ x6; + y3 = y5 ^ y8; + t1 = x4 ^ y12; + y15 = t1 ^ x5; + y20 = t1 ^ x1; + y6 = y15 ^ x7; + y10 = y15 ^ t0; + y11 = y20 ^ y9; + y7 = x7 ^ y11; + y17 = y10 ^ y11; + y19 = y10 ^ y8; + y16 = t0 ^ y11; + y21 = y13 ^ y16; + y18 = x0 ^ y16; + + /* + * Non-linear section. + */ + t2 = y12 & y15; + t3 = y3 & y6; + t4 = t3 ^ t2; + t5 = y4 & x7; + t6 = t5 ^ t2; + t7 = y13 & y16; + t8 = y5 & y1; + t9 = t8 ^ t7; + t10 = y2 & y7; + t11 = t10 ^ t7; + t12 = y9 & y11; + t13 = y14 & y17; + t14 = t13 ^ t12; + t15 = y8 & y10; + t16 = t15 ^ t12; + t17 = t4 ^ t14; + t18 = t6 ^ t16; + t19 = t9 ^ t14; + t20 = t11 ^ t16; + t21 = t17 ^ y20; + t22 = t18 ^ y19; + t23 = t19 ^ y21; + t24 = t20 ^ y18; + + t25 = t21 ^ t22; + t26 = t21 & t23; + t27 = t24 ^ t26; + t28 = t25 & t27; + t29 = t28 ^ t22; + t30 = t23 ^ t24; + t31 = t22 ^ t26; + t32 = t31 & t30; + t33 = t32 ^ t24; + t34 = t23 ^ t33; + t35 = t27 ^ t33; + t36 = t24 & t35; + t37 = t36 ^ t34; + t38 = t27 ^ t36; + t39 = t29 & t38; + t40 = t25 ^ t39; + + t41 = t40 ^ t37; + t42 = t29 ^ t33; + t43 = t29 ^ t40; + t44 = t33 ^ t37; + t45 = t42 ^ t41; + z0 = t44 & y15; + z1 = t37 & y6; + z2 = t33 & x7; + z3 = t43 & y16; + z4 = t40 & y1; + z5 = t29 & y7; + z6 = t42 & y11; + z7 = t45 & y17; + z8 = t41 & y10; + z9 = t44 & y12; + z10 = t37 & y3; + z11 = t33 & y4; + z12 = t43 & y13; + z13 = t40 & y5; + z14 = t29 & y2; + z15 = t42 & y9; + z16 = t45 & y14; + z17 = t41 & y8; + + /* + * Bottom linear transformation. + */ + t46 = z15 ^ z16; + t47 = z10 ^ z11; + t48 = z5 ^ z13; + t49 = z9 ^ z10; + t50 = z2 ^ z12; + t51 = z2 ^ z5; + t52 = z7 ^ z8; + t53 = z0 ^ z3; + t54 = z6 ^ z7; + t55 = z16 ^ z17; + t56 = z12 ^ t48; + t57 = t50 ^ t53; + t58 = z4 ^ t46; + t59 = z3 ^ t54; + t60 = t46 ^ t57; + t61 = z14 ^ t57; + t62 = t52 ^ t58; + t63 = t49 ^ t58; + t64 = z4 ^ t59; + t65 = t61 ^ t62; + t66 = z1 ^ t63; + s0 = t59 ^ t63; + s6 = t56 ^ ~t62; + s7 = t48 ^ ~t60; + t67 = t64 ^ t65; + s3 = t53 ^ t66; + s4 = t51 ^ t66; + s5 = t47 ^ t65; + s1 = t64 ^ ~s3; + s2 = t55 ^ ~t67; + + q[7] = s0; + q[6] = s1; + q[5] = s2; + q[4] = s3; + q[3] = s4; + q[2] = s5; + q[1] = s6; + q[0] = s7; +} + +static void br_aes_ct_bitslice_Sbox(uint32_t *q) { + /* + * This S-box implementation is a straightforward translation of + * the circuit described by Boyar and Peralta in "A new + * combinational logic minimization technique with applications + * to cryptology" (https://eprint.iacr.org/2009/191.pdf). + * + * Note that variables x* (input) and s* (output) are numbered + * in "reverse" order (x0 is the high bit, x7 is the low bit). + */ + + uint32_t x0, x1, x2, x3, x4, x5, x6, x7; + uint32_t y1, y2, y3, y4, y5, y6, y7, y8, y9; + uint32_t y10, y11, y12, y13, y14, y15, y16, y17, y18, y19; + uint32_t y20, y21; + uint32_t z0, z1, z2, z3, z4, z5, z6, z7, z8, z9; + uint32_t z10, z11, z12, z13, z14, z15, z16, z17; + uint32_t t0, t1, t2, t3, t4, t5, t6, t7, t8, t9; + uint32_t t10, t11, t12, t13, t14, t15, t16, t17, t18, t19; + uint32_t t20, t21, t22, t23, t24, t25, t26, t27, t28, t29; + uint32_t t30, t31, t32, t33, t34, t35, t36, t37, t38, t39; + uint32_t t40, t41, t42, t43, t44, t45, t46, t47, t48, t49; + uint32_t t50, t51, t52, t53, t54, t55, t56, t57, t58, t59; + uint32_t t60, t61, t62, t63, t64, t65, t66, t67; + uint32_t s0, s1, s2, s3, s4, s5, s6, s7; + + x0 = q[7]; + x1 = q[6]; + x2 = q[5]; + x3 = q[4]; + x4 = q[3]; + x5 = q[2]; + x6 = q[1]; + x7 = q[0]; + + /* + * Top linear transformation. + */ + y14 = x3 ^ x5; + y13 = x0 ^ x6; + y9 = x0 ^ x3; + y8 = x0 ^ x5; + t0 = x1 ^ x2; + y1 = t0 ^ x7; + y4 = y1 ^ x3; + y12 = y13 ^ y14; + y2 = y1 ^ x0; + y5 = y1 ^ x6; + y3 = y5 ^ y8; + t1 = x4 ^ y12; + y15 = t1 ^ x5; + y20 = t1 ^ x1; + y6 = y15 ^ x7; + y10 = y15 ^ t0; + y11 = y20 ^ y9; + y7 = x7 ^ y11; + y17 = y10 ^ y11; + y19 = y10 ^ y8; + y16 = t0 ^ y11; + y21 = y13 ^ y16; + y18 = x0 ^ y16; + + /* + * Non-linear section. + */ + t2 = y12 & y15; + t3 = y3 & y6; + t4 = t3 ^ t2; + t5 = y4 & x7; + t6 = t5 ^ t2; + t7 = y13 & y16; + t8 = y5 & y1; + t9 = t8 ^ t7; + t10 = y2 & y7; + t11 = t10 ^ t7; + t12 = y9 & y11; + t13 = y14 & y17; + t14 = t13 ^ t12; + t15 = y8 & y10; + t16 = t15 ^ t12; + t17 = t4 ^ t14; + t18 = t6 ^ t16; + t19 = t9 ^ t14; + t20 = t11 ^ t16; + t21 = t17 ^ y20; + t22 = t18 ^ y19; + t23 = t19 ^ y21; + t24 = t20 ^ y18; + + t25 = t21 ^ t22; + t26 = t21 & t23; + t27 = t24 ^ t26; + t28 = t25 & t27; + t29 = t28 ^ t22; + t30 = t23 ^ t24; + t31 = t22 ^ t26; + t32 = t31 & t30; + t33 = t32 ^ t24; + t34 = t23 ^ t33; + t35 = t27 ^ t33; + t36 = t24 & t35; + t37 = t36 ^ t34; + t38 = t27 ^ t36; + t39 = t29 & t38; + t40 = t25 ^ t39; + + t41 = t40 ^ t37; + t42 = t29 ^ t33; + t43 = t29 ^ t40; + t44 = t33 ^ t37; + t45 = t42 ^ t41; + z0 = t44 & y15; + z1 = t37 & y6; + z2 = t33 & x7; + z3 = t43 & y16; + z4 = t40 & y1; + z5 = t29 & y7; + z6 = t42 & y11; + z7 = t45 & y17; + z8 = t41 & y10; + z9 = t44 & y12; + z10 = t37 & y3; + z11 = t33 & y4; + z12 = t43 & y13; + z13 = t40 & y5; + z14 = t29 & y2; + z15 = t42 & y9; + z16 = t45 & y14; + z17 = t41 & y8; + + /* + * Bottom linear transformation. + */ + t46 = z15 ^ z16; + t47 = z10 ^ z11; + t48 = z5 ^ z13; + t49 = z9 ^ z10; + t50 = z2 ^ z12; + t51 = z2 ^ z5; + t52 = z7 ^ z8; + t53 = z0 ^ z3; + t54 = z6 ^ z7; + t55 = z16 ^ z17; + t56 = z12 ^ t48; + t57 = t50 ^ t53; + t58 = z4 ^ t46; + t59 = z3 ^ t54; + t60 = t46 ^ t57; + t61 = z14 ^ t57; + t62 = t52 ^ t58; + t63 = t49 ^ t58; + t64 = z4 ^ t59; + t65 = t61 ^ t62; + t66 = z1 ^ t63; + s0 = t59 ^ t63; + s6 = t56 ^ ~t62; + s7 = t48 ^ ~t60; + t67 = t64 ^ t65; + s3 = t53 ^ t66; + s4 = t51 ^ t66; + s5 = t47 ^ t65; + s1 = t64 ^ ~s3; + s2 = t55 ^ ~t67; + + q[7] = s0; + q[6] = s1; + q[5] = s2; + q[4] = s3; + q[3] = s4; + q[2] = s5; + q[1] = s6; + q[0] = s7; +} + +static void br_aes_ct_ortho(uint32_t *q) { +#define SWAPN_32(cl, ch, s, x, y) do { \ + uint32_t a, b; \ + a = (x); \ + b = (y); \ + (x) = (a & (uint32_t)(cl)) | ((b & (uint32_t)(cl)) << (s)); \ + (y) = ((a & (uint32_t)(ch)) >> (s)) | (b & (uint32_t)(ch)); \ + } while (0) + +#define SWAP2_32(x, y) SWAPN_32(0x55555555, 0xAAAAAAAA, 1, x, y) +#define SWAP4_32(x, y) SWAPN_32(0x33333333, 0xCCCCCCCC, 2, x, y) +#define SWAP8_32(x, y) SWAPN_32(0x0F0F0F0F, 0xF0F0F0F0, 4, x, y) + + SWAP2_32(q[0], q[1]); + SWAP2_32(q[2], q[3]); + SWAP2_32(q[4], q[5]); + SWAP2_32(q[6], q[7]); + + SWAP4_32(q[0], q[2]); + SWAP4_32(q[1], q[3]); + SWAP4_32(q[4], q[6]); + SWAP4_32(q[5], q[7]); + + SWAP8_32(q[0], q[4]); + SWAP8_32(q[1], q[5]); + SWAP8_32(q[2], q[6]); + SWAP8_32(q[3], q[7]); +} + +static inline void add_round_key32(uint32_t *q, const uint32_t *sk) { + q[0] ^= sk[0]; + q[1] ^= sk[1]; + q[2] ^= sk[2]; + q[3] ^= sk[3]; + q[4] ^= sk[4]; + q[5] ^= sk[5]; + q[6] ^= sk[6]; + q[7] ^= sk[7]; +} + +static inline void shift_rows32(uint32_t *q) { + int i; + + for (i = 0; i < 8; i++) { + uint32_t x; + + x = q[i]; + q[i] = (x & 0x000000FF) + | ((x & 0x0000FC00) >> 2) | ((x & 0x00000300) << 6) + | ((x & 0x00F00000) >> 4) | ((x & 0x000F0000) << 4) + | ((x & 0xC0000000) >> 6) | ((x & 0x3F000000) << 2); + } +} + +static inline uint32_t rotr16(uint32_t x) { + return (x << 16) | (x >> 16); +} + +static inline void mix_columns32(uint32_t *q) { + uint32_t q0, q1, q2, q3, q4, q5, q6, q7; + uint32_t r0, r1, r2, r3, r4, r5, r6, r7; + + q0 = q[0]; + q1 = q[1]; + q2 = q[2]; + q3 = q[3]; + q4 = q[4]; + q5 = q[5]; + q6 = q[6]; + q7 = q[7]; + r0 = (q0 >> 8) | (q0 << 24); + r1 = (q1 >> 8) | (q1 << 24); + r2 = (q2 >> 8) | (q2 << 24); + r3 = (q3 >> 8) | (q3 << 24); + r4 = (q4 >> 8) | (q4 << 24); + r5 = (q5 >> 8) | (q5 << 24); + r6 = (q6 >> 8) | (q6 << 24); + r7 = (q7 >> 8) | (q7 << 24); + + q[0] = q7 ^ r7 ^ r0 ^ rotr16(q0 ^ r0); + q[1] = q0 ^ r0 ^ q7 ^ r7 ^ r1 ^ rotr16(q1 ^ r1); + q[2] = q1 ^ r1 ^ r2 ^ rotr16(q2 ^ r2); + q[3] = q2 ^ r2 ^ q7 ^ r7 ^ r3 ^ rotr16(q3 ^ r3); + q[4] = q3 ^ r3 ^ q7 ^ r7 ^ r4 ^ rotr16(q4 ^ r4); + q[5] = q4 ^ r4 ^ r5 ^ rotr16(q5 ^ r5); + q[6] = q5 ^ r5 ^ r6 ^ rotr16(q6 ^ r6); + q[7] = q6 ^ r6 ^ r7 ^ rotr16(q7 ^ r7); +} + +static void br_aes_ct64_ortho(uint64_t *q) { +#define SWAPN(cl, ch, s, x, y) do { \ + uint64_t a, b; \ + a = (x); \ + b = (y); \ + (x) = (a & (uint64_t)(cl)) | ((b & (uint64_t)(cl)) << (s)); \ + (y) = ((a & (uint64_t)(ch)) >> (s)) | (b & (uint64_t)(ch)); \ + } while (0) + +#define SWAP2(x, y) SWAPN(0x5555555555555555, 0xAAAAAAAAAAAAAAAA, 1, x, y) +#define SWAP4(x, y) SWAPN(0x3333333333333333, 0xCCCCCCCCCCCCCCCC, 2, x, y) +#define SWAP8(x, y) SWAPN(0x0F0F0F0F0F0F0F0F, 0xF0F0F0F0F0F0F0F0, 4, x, y) + + SWAP2(q[0], q[1]); + SWAP2(q[2], q[3]); + SWAP2(q[4], q[5]); + SWAP2(q[6], q[7]); + + SWAP4(q[0], q[2]); + SWAP4(q[1], q[3]); + SWAP4(q[4], q[6]); + SWAP4(q[5], q[7]); + + SWAP8(q[0], q[4]); + SWAP8(q[1], q[5]); + SWAP8(q[2], q[6]); + SWAP8(q[3], q[7]); +} + + +static void br_aes_ct64_interleave_in(uint64_t *q0, uint64_t *q1, const uint32_t *w) { + uint64_t x0, x1, x2, x3; + + x0 = w[0]; + x1 = w[1]; + x2 = w[2]; + x3 = w[3]; + x0 |= (x0 << 16); + x1 |= (x1 << 16); + x2 |= (x2 << 16); + x3 |= (x3 << 16); + x0 &= (uint64_t)0x0000FFFF0000FFFF; + x1 &= (uint64_t)0x0000FFFF0000FFFF; + x2 &= (uint64_t)0x0000FFFF0000FFFF; + x3 &= (uint64_t)0x0000FFFF0000FFFF; + x0 |= (x0 << 8); + x1 |= (x1 << 8); + x2 |= (x2 << 8); + x3 |= (x3 << 8); + x0 &= (uint64_t)0x00FF00FF00FF00FF; + x1 &= (uint64_t)0x00FF00FF00FF00FF; + x2 &= (uint64_t)0x00FF00FF00FF00FF; + x3 &= (uint64_t)0x00FF00FF00FF00FF; + *q0 = x0 | (x2 << 8); + *q1 = x1 | (x3 << 8); +} + + +static void br_aes_ct64_interleave_out(uint32_t *w, uint64_t q0, uint64_t q1) { + uint64_t x0, x1, x2, x3; + + x0 = q0 & (uint64_t)0x00FF00FF00FF00FF; + x1 = q1 & (uint64_t)0x00FF00FF00FF00FF; + x2 = (q0 >> 8) & (uint64_t)0x00FF00FF00FF00FF; + x3 = (q1 >> 8) & (uint64_t)0x00FF00FF00FF00FF; + x0 |= (x0 >> 8); + x1 |= (x1 >> 8); + x2 |= (x2 >> 8); + x3 |= (x3 >> 8); + x0 &= (uint64_t)0x0000FFFF0000FFFF; + x1 &= (uint64_t)0x0000FFFF0000FFFF; + x2 &= (uint64_t)0x0000FFFF0000FFFF; + x3 &= (uint64_t)0x0000FFFF0000FFFF; + w[0] = (uint32_t)x0 | (uint32_t)(x0 >> 16); + w[1] = (uint32_t)x1 | (uint32_t)(x1 >> 16); + w[2] = (uint32_t)x2 | (uint32_t)(x2 >> 16); + w[3] = (uint32_t)x3 | (uint32_t)(x3 >> 16); +} + +static inline void add_round_key(uint64_t *q, const uint64_t *sk) { + q[0] ^= sk[0]; + q[1] ^= sk[1]; + q[2] ^= sk[2]; + q[3] ^= sk[3]; + q[4] ^= sk[4]; + q[5] ^= sk[5]; + q[6] ^= sk[6]; + q[7] ^= sk[7]; +} + +static inline void shift_rows(uint64_t *q) { + int i; + + for (i = 0; i < 8; i++) { + uint64_t x; + + x = q[i]; + q[i] = (x & (uint64_t)0x000000000000FFFF) + | ((x & (uint64_t)0x00000000FFF00000) >> 4) + | ((x & (uint64_t)0x00000000000F0000) << 12) + | ((x & (uint64_t)0x0000FF0000000000) >> 8) + | ((x & (uint64_t)0x000000FF00000000) << 8) + | ((x & (uint64_t)0xF000000000000000) >> 12) + | ((x & (uint64_t)0x0FFF000000000000) << 4); + } +} + +static inline uint64_t rotr32(uint64_t x) { + return (x << 32) | (x >> 32); +} + +static inline void mix_columns(uint64_t *q) { + uint64_t q0, q1, q2, q3, q4, q5, q6, q7; + uint64_t r0, r1, r2, r3, r4, r5, r6, r7; + + q0 = q[0]; + q1 = q[1]; + q2 = q[2]; + q3 = q[3]; + q4 = q[4]; + q5 = q[5]; + q6 = q[6]; + q7 = q[7]; + r0 = (q0 >> 16) | (q0 << 48); + r1 = (q1 >> 16) | (q1 << 48); + r2 = (q2 >> 16) | (q2 << 48); + r3 = (q3 >> 16) | (q3 << 48); + r4 = (q4 >> 16) | (q4 << 48); + r5 = (q5 >> 16) | (q5 << 48); + r6 = (q6 >> 16) | (q6 << 48); + r7 = (q7 >> 16) | (q7 << 48); + + q[0] = q7 ^ r7 ^ r0 ^ rotr32(q0 ^ r0); + q[1] = q0 ^ r0 ^ q7 ^ r7 ^ r1 ^ rotr32(q1 ^ r1); + q[2] = q1 ^ r1 ^ r2 ^ rotr32(q2 ^ r2); + q[3] = q2 ^ r2 ^ q7 ^ r7 ^ r3 ^ rotr32(q3 ^ r3); + q[4] = q3 ^ r3 ^ q7 ^ r7 ^ r4 ^ rotr32(q4 ^ r4); + q[5] = q4 ^ r4 ^ r5 ^ rotr32(q5 ^ r5); + q[6] = q5 ^ r5 ^ r6 ^ rotr32(q6 ^ r6); + q[7] = q6 ^ r6 ^ r7 ^ rotr32(q7 ^ r7); +} + +static void interleave_constant(uint64_t *out, const unsigned char *in) { + uint32_t tmp_32_constant[16]; + int i; + + br_range_dec32le(tmp_32_constant, 16, in); + for (i = 0; i < 4; i++) { + br_aes_ct64_interleave_in(&out[i], &out[i + 4], tmp_32_constant + (i << 2)); + } + br_aes_ct64_ortho(out); +} + +static void interleave_constant32(uint32_t *out, const unsigned char *in) { + int i; + for (i = 0; i < 4; i++) { + out[2 * i] = br_dec32le(in + 4 * i); + out[2 * i + 1] = br_dec32le(in + 4 * i + 16); + } + br_aes_ct_ortho(out); +} + +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_tweak_constants( + const unsigned char *pk_seed, const unsigned char *sk_seed, + unsigned long long seed_length) { + unsigned char buf[40 * 16]; + int i; + + /* Use the standard constants to generate tweaked ones. */ + memcpy((uint8_t *)tweaked512_rc64, (uint8_t *)haraka512_rc64, 40 * 16); + + /* Constants for sk.seed */ + if (sk_seed != NULL) { + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_haraka_S( + buf, 40 * 16, sk_seed, seed_length); + + /* Interleave constants */ + for (i = 0; i < 10; i++) { + interleave_constant32(tweaked256_rc32_sseed[i], buf + 32 * i); + } + } + + /* Constants for pk.seed */ + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_haraka_S( + buf, 40 * 16, pk_seed, seed_length); + for (i = 0; i < 10; i++) { + interleave_constant32(tweaked256_rc32[i], buf + 32 * i); + interleave_constant(tweaked512_rc64[i], buf + 64 * i); + } +} + +static void haraka_S_absorb(unsigned char *s, + const unsigned char *m, unsigned long long mlen, + unsigned char p) { + unsigned long long i; + unsigned char t[HARAKAS_RATE]; + + while (mlen >= HARAKAS_RATE) { + /* XOR block to state */ + for (i = 0; i < HARAKAS_RATE; ++i) { + s[i] ^= m[i]; + } + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_haraka512_perm(s, s); + mlen -= HARAKAS_RATE; + m += HARAKAS_RATE; + } + + for (i = 0; i < HARAKAS_RATE; ++i) { + t[i] = 0; + } + for (i = 0; i < mlen; ++i) { + t[i] = m[i]; + } + t[i] = p; + t[HARAKAS_RATE - 1] |= 128; + for (i = 0; i < HARAKAS_RATE; ++i) { + s[i] ^= t[i]; + } +} + +static void haraka_S_squeezeblocks(unsigned char *h, unsigned long long nblocks, + unsigned char *s) { + while (nblocks > 0) { + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_haraka512_perm(s, s); + memcpy(h, s, HARAKAS_RATE); + h += HARAKAS_RATE; + nblocks--; + } +} + +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_haraka_S_inc_init(uint8_t *s_inc) { + size_t i; + + for (i = 0; i < 64; i++) { + s_inc[i] = 0; + } + s_inc[64] = 0; +} + +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_haraka_S_inc_absorb(uint8_t *s_inc, const uint8_t *m, size_t mlen) { + size_t i; + + /* Recall that s_inc[64] is the non-absorbed bytes xored into the state */ + while (mlen + s_inc[64] >= HARAKAS_RATE) { + for (i = 0; i < (size_t)(HARAKAS_RATE - s_inc[64]); i++) { + /* Take the i'th byte from message + xor with the s_inc[64] + i'th byte of the state */ + s_inc[s_inc[64] + i] ^= m[i]; + } + mlen -= (size_t)(HARAKAS_RATE - s_inc[64]); + m += HARAKAS_RATE - s_inc[64]; + s_inc[64] = 0; + + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_haraka512_perm(s_inc, s_inc); + } + + for (i = 0; i < mlen; i++) { + s_inc[s_inc[64] + i] ^= m[i]; + } + s_inc[64] = (uint8_t)(mlen + s_inc[64]); +} + +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_haraka_S_inc_finalize(uint8_t *s_inc) { + /* After haraka_S_inc_absorb, we are guaranteed that s_inc[64] < HARAKAS_RATE, + so we can always use one more byte for p in the current state. */ + s_inc[s_inc[64]] ^= 0x1F; + s_inc[HARAKAS_RATE - 1] ^= 128; + s_inc[64] = 0; +} + +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_haraka_S_inc_squeeze(uint8_t *out, size_t outlen, uint8_t *s_inc) { + uint8_t i; + + /* First consume any bytes we still have sitting around */ + for (i = 0; i < outlen && i < s_inc[64]; i++) { + /* There are s_inc[64] bytes left, so r - s_inc[64] is the first + available byte. We consume from there, i.e., up to r. */ + out[i] = s_inc[(HARAKAS_RATE - s_inc[64] + i)]; + } + out += i; + outlen -= i; + s_inc[64] = (uint8_t)(s_inc[64] - i); + + /* Then squeeze the remaining necessary blocks */ + while (outlen > 0) { + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_haraka512_perm(s_inc, s_inc); + + for (i = 0; i < outlen && i < HARAKAS_RATE; i++) { + out[i] = s_inc[i]; + } + out += i; + outlen -= i; + s_inc[64] = (uint8_t)(HARAKAS_RATE - i); + } +} + +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_haraka_S(unsigned char *out, unsigned long long outlen, const unsigned char *in, unsigned long long inlen) { + unsigned long long i; + unsigned char s[64]; + unsigned char d[32]; + + for (i = 0; i < 64; i++) { + s[i] = 0; + } + haraka_S_absorb(s, in, inlen, 0x1F); + + haraka_S_squeezeblocks(out, outlen / 32, s); + out += (outlen / 32) * 32; + + if (outlen % 32) { + haraka_S_squeezeblocks(d, 1, s); + for (i = 0; i < outlen % 32; i++) { + out[i] = d[i]; + } + } +} + +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_haraka512_perm(unsigned char *out, const unsigned char *in) { + uint32_t w[16]; + uint64_t q[8], tmp_q; + unsigned int i, j; + + br_range_dec32le(w, 16, in); + for (i = 0; i < 4; i++) { + br_aes_ct64_interleave_in(&q[i], &q[i + 4], w + (i << 2)); + } + br_aes_ct64_ortho(q); + + /* AES rounds */ + for (i = 0; i < 5; i++) { + for (j = 0; j < 2; j++) { + br_aes_ct64_bitslice_Sbox(q); + shift_rows(q); + mix_columns(q); + add_round_key(q, tweaked512_rc64[2 * i + j]); + } + /* Mix states */ + for (j = 0; j < 8; j++) { + tmp_q = q[j]; + q[j] = (tmp_q & 0x0001000100010001) << 5 | + (tmp_q & 0x0002000200020002) << 12 | + (tmp_q & 0x0004000400040004) >> 1 | + (tmp_q & 0x0008000800080008) << 6 | + (tmp_q & 0x0020002000200020) << 9 | + (tmp_q & 0x0040004000400040) >> 4 | + (tmp_q & 0x0080008000800080) << 3 | + (tmp_q & 0x2100210021002100) >> 5 | + (tmp_q & 0x0210021002100210) << 2 | + (tmp_q & 0x0800080008000800) << 4 | + (tmp_q & 0x1000100010001000) >> 12 | + (tmp_q & 0x4000400040004000) >> 10 | + (tmp_q & 0x8400840084008400) >> 3; + } + } + + br_aes_ct64_ortho(q); + for (i = 0; i < 4; i ++) { + br_aes_ct64_interleave_out(w + (i << 2), q[i], q[i + 4]); + } + br_range_enc32le(out, w, 16); +} + +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_haraka512(unsigned char *out, const unsigned char *in) { + int i; + + unsigned char buf[64]; + + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_haraka512_perm(buf, in); + /* Feed-forward */ + for (i = 0; i < 64; i++) { + buf[i] = buf[i] ^ in[i]; + } + + /* Truncated */ + memcpy(out, buf + 8, 8); + memcpy(out + 8, buf + 24, 8); + memcpy(out + 16, buf + 32, 8); + memcpy(out + 24, buf + 48, 8); +} + + +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_haraka256(unsigned char *out, const unsigned char *in) { + uint32_t q[8], tmp_q; + int i, j; + + for (i = 0; i < 4; i++) { + q[2 * i] = br_dec32le(in + 4 * i); + q[2 * i + 1] = br_dec32le(in + 4 * i + 16); + } + br_aes_ct_ortho(q); + + /* AES rounds */ + for (i = 0; i < 5; i++) { + for (j = 0; j < 2; j++) { + br_aes_ct_bitslice_Sbox(q); + shift_rows32(q); + mix_columns32(q); + add_round_key32(q, tweaked256_rc32[2 * i + j]); + } + + /* Mix states */ + for (j = 0; j < 8; j++) { + tmp_q = q[j]; + q[j] = (tmp_q & 0x81818181) | + (tmp_q & 0x02020202) << 1 | + (tmp_q & 0x04040404) << 2 | + (tmp_q & 0x08080808) << 3 | + (tmp_q & 0x10101010) >> 3 | + (tmp_q & 0x20202020) >> 2 | + (tmp_q & 0x40404040) >> 1; + } + } + + br_aes_ct_ortho(q); + for (i = 0; i < 4; i++) { + br_enc32le(out + 4 * i, q[2 * i]); + br_enc32le(out + 4 * i + 16, q[2 * i + 1]); + } + + for (i = 0; i < 32; i++) { + out[i] ^= in[i]; + } +} + +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_haraka256_sk(unsigned char *out, const unsigned char *in) { + uint32_t q[8], tmp_q; + int i, j; + + for (i = 0; i < 4; i++) { + q[2 * i] = br_dec32le(in + 4 * i); + q[2 * i + 1] = br_dec32le(in + 4 * i + 16); + } + br_aes_ct_ortho(q); + + /* AES rounds */ + for (i = 0; i < 5; i++) { + for (j = 0; j < 2; j++) { + br_aes_ct_bitslice_Sbox(q); + shift_rows32(q); + mix_columns32(q); + add_round_key32(q, tweaked256_rc32_sseed[2 * i + j]); + } + + /* Mix states */ + for (j = 0; j < 8; j++) { + tmp_q = q[j]; + q[j] = (tmp_q & 0x81818181) | + (tmp_q & 0x02020202) << 1 | + (tmp_q & 0x04040404) << 2 | + (tmp_q & 0x08080808) << 3 | + (tmp_q & 0x10101010) >> 3 | + (tmp_q & 0x20202020) >> 2 | + (tmp_q & 0x40404040) >> 1; + } + } + + br_aes_ct_ortho(q); + for (i = 0; i < 4; i++) { + br_enc32le(out + 4 * i, q[2 * i]); + br_enc32le(out + 4 * i + 16, q[2 * i + 1]); + } + + for (i = 0; i < 32; i++) { + out[i] ^= in[i]; + } +} diff --git a/crypto_sign/sphincs-haraka-256f-robust/clean/haraka.h b/crypto_sign/sphincs-haraka-256f-robust/clean/haraka.h new file mode 100644 index 00000000..aa62643a --- /dev/null +++ b/crypto_sign/sphincs-haraka-256f-robust/clean/haraka.h @@ -0,0 +1,30 @@ +#ifndef SPX_HARAKA_H +#define SPX_HARAKA_H + +/* Tweak constants with seed */ +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_tweak_constants( + const unsigned char *pk_seed, const unsigned char *sk_seed, + unsigned long long seed_length); + +/* Haraka Sponge */ +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_haraka_S_inc_init(uint8_t *s_inc); +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_haraka_S_inc_absorb(uint8_t *s_inc, const uint8_t *m, size_t mlen); +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_haraka_S_inc_finalize(uint8_t *s_inc); +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_haraka_S_inc_squeeze(uint8_t *out, size_t outlen, uint8_t *s_inc); +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_haraka_S( + unsigned char *out, unsigned long long outlen, + const unsigned char *in, unsigned long long inlen); + +/* Applies the 512-bit Haraka permutation to in. */ +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_haraka512_perm(unsigned char *out, const unsigned char *in); + +/* Implementation of Haraka-512 */ +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_haraka512(unsigned char *out, const unsigned char *in); + +/* Implementation of Haraka-256 */ +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_haraka256(unsigned char *out, const unsigned char *in); + +/* Implementation of Haraka-256 using sk.seed constants */ +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_haraka256_sk(unsigned char *out, const unsigned char *in); + +#endif diff --git a/crypto_sign/sphincs-haraka-256f-robust/clean/hash.h b/crypto_sign/sphincs-haraka-256f-robust/clean/hash.h new file mode 100644 index 00000000..a199b764 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256f-robust/clean/hash.h @@ -0,0 +1,22 @@ +#ifndef SPX_HASH_H +#define SPX_HASH_H + +#include + +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed); + +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen); + +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen); + +#endif diff --git a/crypto_sign/sphincs-haraka-256f-robust/clean/hash_haraka.c b/crypto_sign/sphincs-haraka-256f-robust/clean/hash_haraka.c new file mode 100644 index 00000000..dd73be43 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256f-robust/clean/hash_haraka.c @@ -0,0 +1,86 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "utils.h" + +#include "haraka.h" + +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed) { + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_tweak_constants(pub_seed, sk_seed, SPX_N); +} + +/* + * Computes PRF(key, addr), given a secret key of SPX_N bytes and an address + */ +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]) { + unsigned char buf[SPX_ADDR_BYTES]; + /* Since SPX_N may be smaller than 32, we need a temporary buffer. */ + unsigned char outbuf[32]; + + (void)key; /* Suppress an 'unused parameter' warning. */ + + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_addr_to_bytes(buf, addr); + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_haraka256_sk(outbuf, buf); + memcpy(out, outbuf, SPX_N); +} + +/** + * Computes the message-dependent randomness R, using a secret seed and an + * optional randomization value as well as the message. + */ +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen) { + uint8_t s_inc[65]; + + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_haraka_S_inc_init(s_inc); + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_haraka_S_inc_absorb(s_inc, sk_prf, SPX_N); + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_haraka_S_inc_absorb(s_inc, optrand, SPX_N); + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_haraka_S_inc_absorb(s_inc, m, mlen); + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_haraka_S_inc_finalize(s_inc); + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_haraka_S_inc_squeeze(R, SPX_N, s_inc); +} + +/** + * Computes the message hash using R, the public key, and the message. + * Outputs the message digest and the index of the leaf. The index is split in + * the tree index and the leaf index, for convenient copying to an address. + */ +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen) { +#define SPX_TREE_BITS (SPX_TREE_HEIGHT * (SPX_D - 1)) +#define SPX_TREE_BYTES ((SPX_TREE_BITS + 7) / 8) +#define SPX_LEAF_BITS SPX_TREE_HEIGHT +#define SPX_LEAF_BYTES ((SPX_LEAF_BITS + 7) / 8) +#define SPX_DGST_BYTES (SPX_FORS_MSG_BYTES + SPX_TREE_BYTES + SPX_LEAF_BYTES) + + unsigned char buf[SPX_DGST_BYTES]; + unsigned char *bufp = buf; + uint8_t s_inc[65]; + + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_haraka_S_inc_init(s_inc); + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_haraka_S_inc_absorb(s_inc, R, SPX_N); + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_haraka_S_inc_absorb(s_inc, pk, SPX_PK_BYTES); + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_haraka_S_inc_absorb(s_inc, m, mlen); + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_haraka_S_inc_finalize(s_inc); + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_haraka_S_inc_squeeze(buf, SPX_DGST_BYTES, s_inc); + + memcpy(digest, bufp, SPX_FORS_MSG_BYTES); + bufp += SPX_FORS_MSG_BYTES; + + *tree = PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_bytes_to_ull(bufp, SPX_TREE_BYTES); + *tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS); + bufp += SPX_TREE_BYTES; + + *leaf_idx = (uint32_t)PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_bytes_to_ull( + bufp, SPX_LEAF_BYTES); + *leaf_idx &= (~(uint32_t)0) >> (32 - SPX_LEAF_BITS); +} diff --git a/crypto_sign/sphincs-haraka-256f-robust/clean/params.h b/crypto_sign/sphincs-haraka-256f-robust/clean/params.h new file mode 100644 index 00000000..c2ad0531 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256f-robust/clean/params.h @@ -0,0 +1,53 @@ +#ifndef SPX_PARAMS_H +#define SPX_PARAMS_H + +/* Hash output length in bytes. */ +#define SPX_N 32 +/* Height of the hypertree. */ +#define SPX_FULL_HEIGHT 68 +/* Number of subtree layer. */ +#define SPX_D 17 +/* FORS tree dimensions. */ +#define SPX_FORS_HEIGHT 10 +#define SPX_FORS_TREES 30 +/* Winternitz parameter, */ +#define SPX_WOTS_W 16 + +/* The hash function is defined by linking a different hash.c file, as opposed + to setting a #define constant. */ + +/* For clarity */ +#define SPX_ADDR_BYTES 32 + +/* WOTS parameters. */ +#define SPX_WOTS_LOGW 4 + +#define SPX_WOTS_LEN1 (8 * SPX_N / SPX_WOTS_LOGW) + +/* SPX_WOTS_LEN2 is floor(log(len_1 * (w - 1)) / log(w)) + 1; we precompute */ +#define SPX_WOTS_LEN2 3 + +#define SPX_WOTS_LEN (SPX_WOTS_LEN1 + SPX_WOTS_LEN2) +#define SPX_WOTS_BYTES (SPX_WOTS_LEN * SPX_N) +#define SPX_WOTS_PK_BYTES SPX_WOTS_BYTES + +/* Subtree size. */ +#define SPX_TREE_HEIGHT (SPX_FULL_HEIGHT / SPX_D) + +/* FORS parameters. */ +#define SPX_FORS_MSG_BYTES ((SPX_FORS_HEIGHT * SPX_FORS_TREES + 7) / 8) +#define SPX_FORS_BYTES ((SPX_FORS_HEIGHT + 1) * SPX_FORS_TREES * SPX_N) +#define SPX_FORS_PK_BYTES SPX_N + +/* Resulting SPX sizes. */ +#define SPX_BYTES (SPX_N + SPX_FORS_BYTES + SPX_D * SPX_WOTS_BYTES +\ + SPX_FULL_HEIGHT * SPX_N) +#define SPX_PK_BYTES (2 * SPX_N) +#define SPX_SK_BYTES (2 * SPX_N + SPX_PK_BYTES) + +/* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ +#define SPX_OPTRAND_BYTES 32 + +#endif diff --git a/crypto_sign/sphincs-haraka-256f-robust/clean/sign.c b/crypto_sign/sphincs-haraka-256f-robust/clean/sign.c new file mode 100644 index 00000000..984ae28a --- /dev/null +++ b/crypto_sign/sphincs-haraka-256f-robust/clean/sign.c @@ -0,0 +1,344 @@ +#include +#include +#include + +#include "address.h" +#include "api.h" +#include "fors.h" +#include "hash.h" +#include "params.h" +#include "randombytes.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +/** + * Computes the leaf at a given address. First generates the WOTS key pair, + * then computes leaf by hashing horizontally. + */ +static void wots_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t tree_addr[8]) { + unsigned char pk[SPX_WOTS_BYTES]; + uint32_t wots_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_keypair_addr( + wots_addr, addr_idx); + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_wots_gen_pk( + pk, sk_seed, pub_seed, wots_addr); + + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_thash_WOTS_LEN( + leaf, pk, pub_seed, wots_pk_addr); +} + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_crypto_sign_secretkeybytes(void) { + return PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_CRYPTO_SECRETKEYBYTES; +} + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_crypto_sign_publickeybytes(void) { + return PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES; +} + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_crypto_sign_bytes(void) { + return PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_CRYPTO_BYTES; +} + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_crypto_sign_seedbytes(void) { + return PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_CRYPTO_SEEDBYTES; +} + +/* + * Generates an SPX key pair given a seed of length + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed) { + /* We do not need the auth path in key generation, but it simplifies the + code to have just one treehash routine that computes both root and path + in one function. */ + unsigned char auth_path[SPX_TREE_HEIGHT * SPX_N]; + uint32_t top_tree_addr[8] = {0}; + + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_layer_addr( + top_tree_addr, SPX_D - 1); + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_type( + top_tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */ + memcpy(sk, seed, PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_CRYPTO_SEEDBYTES); + + memcpy(pk, sk + 2 * SPX_N, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_initialize_hash_function(pk, sk); + + /* Compute root node of the top-most subtree. */ + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_treehash_TREE_HEIGHT( + sk + 3 * SPX_N, auth_path, sk, sk + 2 * SPX_N, 0, 0, + wots_gen_leaf, top_tree_addr); + + memcpy(pk + SPX_N, sk + 3 * SPX_N, SPX_N); + + return 0; +} + +/* + * Generates an SPX key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk) { + unsigned char seed[PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_CRYPTO_SEEDBYTES]; + randombytes(seed, PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_CRYPTO_SEEDBYTES); + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_crypto_sign_seed_keypair( + pk, sk, seed); + + return 0; +} + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + const unsigned char *sk_seed = sk; + const unsigned char *sk_prf = sk + SPX_N; + const unsigned char *pk = sk + 2 * SPX_N; + const unsigned char *pub_seed = pk; + + unsigned char optrand[SPX_N]; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char root[SPX_N]; + uint32_t i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_initialize_hash_function( + pub_seed, sk_seed); + + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ + randombytes(optrand, SPX_N); + /* Compute the digest randomization value. */ + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_gen_message_random( + sig, sk_prf, optrand, m, mlen); + + /* Derive the message digest and leaf index from R, PK and M. */ + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Sign the message hash using FORS. */ + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_fors_sign( + sig, root, mhash, sk_seed, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Compute a WOTS signature. */ + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_wots_sign( + sig, root, sk_seed, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the authentication path for the used WOTS leaf. */ + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_treehash_TREE_HEIGHT( + root, sig, sk_seed, pub_seed, idx_leaf, 0, + wots_gen_leaf, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + *siglen = SPX_BYTES; + + return 0; +} + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk) { + const unsigned char *pub_seed = pk; + const unsigned char *pub_root = pk + SPX_N; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char wots_pk[SPX_WOTS_BYTES]; + unsigned char root[SPX_N]; + unsigned char leaf[SPX_N]; + unsigned int i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + if (siglen != SPX_BYTES) { + return -1; + } + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_initialize_hash_function( + pub_seed, NULL); + + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + /* Derive the message digest and leaf index from R || PK || M. */ + /* The additional SPX_N is a result of the hash domain separator. */ + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + /* Layer correctly defaults to 0, so no need to set_layer_addr */ + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_fors_pk_from_sig( + root, sig, mhash, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + /* For each subtree.. */ + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + + /* The WOTS public key is only correct if the signature was correct. */ + /* Initially, root is the FORS pk, but on subsequent iterations it is + the root of the subtree below the currently processed subtree. */ + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_wots_pk_from_sig( + wots_pk, sig, root, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the leaf node using the WOTS public key. */ + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_thash_WOTS_LEN( + leaf, wots_pk, pub_seed, wots_pk_addr); + + /* Compute the root node of this subtree. */ + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_compute_root( + root, leaf, idx_leaf, 0, sig, SPX_TREE_HEIGHT, + pub_seed, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + /* Check if the root node equals the root node in the public key. */ + if (memcmp(root, pub_root, SPX_N) != 0) { + return -1; + } + + return 0; +} + + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + size_t siglen; + + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_crypto_sign_signature( + sm, &siglen, m, mlen, sk); + + memmove(sm + SPX_BYTES, m, mlen); + *smlen = siglen + mlen; + + return 0; +} + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk) { + /* The API caller does not necessarily know what size a signature should be + but SPHINCS+ signatures are always exactly SPX_BYTES. */ + if (smlen < SPX_BYTES) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + *mlen = smlen - SPX_BYTES; + + if (PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_crypto_sign_verify( + sm, SPX_BYTES, sm + SPX_BYTES, *mlen, pk)) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + /* If verification was successful, move the message to the right place. */ + memmove(m, sm + SPX_BYTES, *mlen); + + return 0; +} diff --git a/crypto_sign/sphincs-haraka-256f-robust/clean/thash.h b/crypto_sign/sphincs-haraka-256f-robust/clean/thash.h new file mode 100644 index 00000000..14927a8c --- /dev/null +++ b/crypto_sign/sphincs-haraka-256f-robust/clean/thash.h @@ -0,0 +1,22 @@ +#ifndef SPX_THASH_H +#define SPX_THASH_H + +#include + +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-haraka-256f-robust/clean/thash_haraka_robust.c b/crypto_sign/sphincs-haraka-256f-robust/clean/thash_haraka_robust.c new file mode 100644 index 00000000..f3d7feb3 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256f-robust/clean/thash_haraka_robust.c @@ -0,0 +1,88 @@ +#include +#include + +#include "address.h" +#include "params.h" +#include "thash.h" + +#include "haraka.h" + +/** + * Takes an array of inblocks concatenated arrays of SPX_N bytes. + */ +static void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_thash( + unsigned char *out, unsigned char *buf, + const unsigned char *in, unsigned int inblocks, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char *bitmask = buf + SPX_ADDR_BYTES; + unsigned char outbuf[32]; + unsigned char buf_tmp[64]; + unsigned int i; + + (void)pub_seed; /* Suppress an 'unused parameter' warning. */ + + if (inblocks == 1) { + /* F function */ + /* Since SPX_N may be smaller than 32, we need a temporary buffer. */ + memset(buf_tmp, 0, 64); + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_addr_to_bytes(buf_tmp, addr); + + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_haraka256(outbuf, buf_tmp); + for (i = 0; i < inblocks * SPX_N; i++) { + buf_tmp[SPX_ADDR_BYTES + i] = in[i] ^ outbuf[i]; + } + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_haraka512(outbuf, buf_tmp); + memcpy(out, outbuf, SPX_N); + } else { + /* All other tweakable hashes*/ + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_addr_to_bytes(buf, addr); + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_haraka_S( + bitmask, inblocks * SPX_N, buf, SPX_ADDR_BYTES); + + for (i = 0; i < inblocks * SPX_N; i++) { + buf[SPX_ADDR_BYTES + i] = in[i] ^ bitmask[i]; + } + + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_haraka_S( + out, SPX_N, buf, SPX_ADDR_BYTES + inblocks * SPX_N); + } +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_ADDR_BYTES + 1 * SPX_N]; + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_thash( + out, buf, in, 1, pub_seed, addr); +} + +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_ADDR_BYTES + 2 * SPX_N]; + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_thash( + out, buf, in, 2, pub_seed, addr); +} + +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_ADDR_BYTES + SPX_WOTS_LEN * SPX_N]; + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_thash( + out, buf, in, SPX_WOTS_LEN, pub_seed, addr); +} + +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_ADDR_BYTES + SPX_FORS_TREES * SPX_N]; + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_thash( + out, buf, in, SPX_FORS_TREES, pub_seed, addr); +} diff --git a/crypto_sign/sphincs-haraka-256f-robust/clean/utils.c b/crypto_sign/sphincs-haraka-256f-robust/clean/utils.c new file mode 100644 index 00000000..15c0de94 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256f-robust/clean/utils.c @@ -0,0 +1,192 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in) { + + /* Iterate over out in decreasing order, for big-endianness. */ + for (size_t i = outlen; i > 0; i--) { + out[i - 1] = in & 0xff; + in = in >> 8; + } +} + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen) { + unsigned long long retval = 0; + + for (size_t i = 0; i < inlen; i++) { + retval |= ((unsigned long long)in[i]) << (8 * (inlen - 1 - i)); + } + return retval; +} + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + unsigned char buffer[2 * SPX_N]; + + /* If leaf_idx is odd (last bit = 1), current path element is a right child + and auth_path has to go left. Otherwise it is the other way around. */ + if (leaf_idx & 1) { + memcpy(buffer + SPX_N, leaf, SPX_N); + memcpy(buffer, auth_path, SPX_N); + } else { + memcpy(buffer, leaf, SPX_N); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + + for (i = 0; i < tree_height - 1; i++) { + leaf_idx >>= 1; + idx_offset >>= 1; + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_tree_height(addr, i + 1); + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + + /* Pick the right or left neighbor, depending on parity of the node. */ + if (leaf_idx & 1) { + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_thash_2( + buffer + SPX_N, buffer, pub_seed, addr); + memcpy(buffer, auth_path, SPX_N); + } else { + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_thash_2( + buffer, buffer, pub_seed, addr); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + } + + /* The last iteration is exceptional; we do not copy an auth_path node. */ + leaf_idx >>= 1; + idx_offset >>= 1; + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_tree_height(addr, tree_height); + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_thash_2( + root, buffer, pub_seed, addr); +} + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +static void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_treehash( + unsigned char *root, unsigned char *auth_path, + unsigned char *stack, unsigned int *heights, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned int offset = 0; + uint32_t idx; + uint32_t tree_idx; + + for (idx = 0; idx < (uint32_t)(1 << tree_height); idx++) { + /* Add the next leaf node to the stack. */ + gen_leaf(stack + offset * SPX_N, + sk_seed, pub_seed, idx + idx_offset, tree_addr); + offset++; + heights[offset - 1] = 0; + + /* If this is a node we need for the auth path.. */ + if ((leaf_idx ^ 0x1) == idx) { + memcpy(auth_path, stack + (offset - 1)*SPX_N, SPX_N); + } + + /* While the top-most nodes are of equal height.. */ + while (offset >= 2 && heights[offset - 1] == heights[offset - 2]) { + /* Compute index of the new node, in the next layer. */ + tree_idx = (idx >> (heights[offset - 1] + 1)); + + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_tree_height( + tree_addr, heights[offset - 1] + 1); + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_tree_index( + tree_addr, tree_idx + (idx_offset >> (heights[offset - 1] + 1))); + /* Hash the top-most nodes from the stack together. */ + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_thash_2( + stack + (offset - 2)*SPX_N, stack + (offset - 2)*SPX_N, + pub_seed, tree_addr); + offset--; + /* Note that the top-most node is now one layer higher. */ + heights[offset - 1]++; + + /* If this is a node we need for the auth path.. */ + if (((leaf_idx >> heights[offset - 1]) ^ 0x1) == tree_idx) { + memcpy(auth_path + heights[offset - 1]*SPX_N, + stack + (offset - 1)*SPX_N, SPX_N); + } + } + } + memcpy(root, stack, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_FORS_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_FORS_HEIGHT + 1]; + + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_FORS_HEIGHT, gen_leaf, tree_addr); +} + +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_TREE_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_TREE_HEIGHT + 1]; + + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_TREE_HEIGHT, gen_leaf, tree_addr); +} diff --git a/crypto_sign/sphincs-haraka-256f-robust/clean/utils.h b/crypto_sign/sphincs-haraka-256f-robust/clean/utils.h new file mode 100644 index 00000000..570f925a --- /dev/null +++ b/crypto_sign/sphincs-haraka-256f-robust/clean/utils.h @@ -0,0 +1,60 @@ +#ifndef SPX_UTILS_H +#define SPX_UTILS_H + +#include "params.h" +#include +#include + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in); + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen); + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-haraka-256f-robust/clean/wots.c b/crypto_sign/sphincs-haraka-256f-robust/clean/wots.c new file mode 100644 index 00000000..855d0c98 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256f-robust/clean/wots.c @@ -0,0 +1,161 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +// TODO clarify address expectations, and make them more uniform. +// TODO i.e. do we expect types to be set already? +// TODO and do we expect modifications or copies? + +/** + * Computes the starting value for a chain, i.e. the secret key. + * Expects the address to be complete up to the chain address. + */ +static void wots_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t wots_addr[8]) { + /* Make sure that the hash address is actually zeroed. */ + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_hash_addr(wots_addr, 0); + + /* Generate sk element. */ + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_prf_addr(sk, sk_seed, wots_addr); +} + +/** + * Computes the chaining function. + * out and in have to be n-byte arrays. + * + * Interprets in as start-th value of the chain. + * addr has to contain the address of the chain. + */ +static void gen_chain(unsigned char *out, const unsigned char *in, + unsigned int start, unsigned int steps, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + /* Initialize out with the value at position 'start'. */ + memcpy(out, in, SPX_N); + + /* Iterate 'steps' calls to the hash function. */ + for (i = start; i < (start + steps) && i < SPX_WOTS_W; i++) { + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_hash_addr(addr, i); + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_thash_1( + out, out, pub_seed, addr); + } +} + +/** + * base_w algorithm as described in draft. + * Interprets an array of bytes as integers in base w. + * This only works when log_w is a divisor of 8. + */ +static void base_w(unsigned int *output, const size_t out_len, + const unsigned char *input) { + size_t in = 0; + size_t out = 0; + unsigned char total = 0; + unsigned int bits = 0; + size_t consumed; + + for (consumed = 0; consumed < out_len; consumed++) { + if (bits == 0) { + total = input[in]; + in++; + bits += 8; + } + bits -= SPX_WOTS_LOGW; + output[out] = (unsigned int)((total >> bits) & (SPX_WOTS_W - 1)); + out++; + } +} + +/* Computes the WOTS+ checksum over a message (in base_w). */ +static void wots_checksum(unsigned int *csum_base_w, + const unsigned int *msg_base_w) { + unsigned int csum = 0; + unsigned char csum_bytes[(SPX_WOTS_LEN2 * SPX_WOTS_LOGW + 7) / 8]; + unsigned int i; + + /* Compute checksum. */ + for (i = 0; i < SPX_WOTS_LEN1; i++) { + csum += SPX_WOTS_W - 1 - msg_base_w[i]; + } + + /* Convert checksum to base_w. */ + /* Make sure expected empty zero bits are the least significant bits. */ + csum = csum << (8 - ((SPX_WOTS_LEN2 * SPX_WOTS_LOGW) % 8)); + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_ull_to_bytes( + csum_bytes, sizeof(csum_bytes), csum); + base_w(csum_base_w, SPX_WOTS_LEN2, csum_bytes); +} + +/* Takes a message and derives the matching chain lengths. */ +static void chain_lengths(unsigned int *lengths, const unsigned char *msg) { + base_w(lengths, SPX_WOTS_LEN1, msg); + wots_checksum(lengths + SPX_WOTS_LEN1, lengths); +} + +/** + * WOTS key generation. Takes a 32 byte sk_seed, expands it to WOTS private key + * elements and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(pk + i * SPX_N, sk_seed, addr); + gen_chain(pk + i * SPX_N, pk + i * SPX_N, + 0, SPX_WOTS_W - 1, pub_seed, addr); + } +} + +/** + * Takes a n-byte message and the 32-byte sk_see to compute a signature 'sig'. + */ +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(sig + i * SPX_N, sk_seed, addr); + gen_chain(sig + i * SPX_N, sig + i * SPX_N, 0, lengths[i], pub_seed, addr); + } +} + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_set_chain_addr(addr, i); + gen_chain(pk + i * SPX_N, sig + i * SPX_N, + lengths[i], SPX_WOTS_W - 1 - lengths[i], pub_seed, addr); + } +} diff --git a/crypto_sign/sphincs-haraka-256f-robust/clean/wots.h b/crypto_sign/sphincs-haraka-256f-robust/clean/wots.h new file mode 100644 index 00000000..8eaaaba3 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256f-robust/clean/wots.h @@ -0,0 +1,38 @@ +#ifndef SPX_WOTS_H +#define SPX_WOTS_H + +#include "params.h" +#include + +/** + * WOTS key generation. Takes a 32 byte seed for the private key, expands it to + * a full WOTS private key and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * Takes a n-byte message and the 32-byte seed for the private key to compute a + * signature that is placed at 'sig'. + */ +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]); + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-haraka-256f-simple/META.yml b/crypto_sign/sphincs-haraka-256f-simple/META.yml new file mode 100644 index 00000000..a8decdf1 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256f-simple/META.yml @@ -0,0 +1,27 @@ +name: SPHINCS+ +type: signature +claimed-nist-level: 5 +length-public-key: 64 +length-secret-key: 128 +length-signature: 49216 +testvectors-sha256: 3cddd379bf490efac9a8aefaa9b59e7f70fe96bb177a8bfc404f99bfc2172aee +principal-submitter: Andreas Hülsing +auxiliary-submitters: + - Jean-Philippe Aumasson + - Daniel J. Bernstein, + - Christoph Dobraunig + - Maria Eichlseder + - Scott Fluhrer + - Stefan-Lukas Gazdag + - Panos Kampanakis + - Stefan Kölbl + - Tanja Lange + - Martin M. Lauridsen + - Florian Mendel + - Ruben Niederhagen + - Christian Rechberger + - Joost Rijneveld + - Peter Schwabe +implementations: + - name: clean + version: https://github.com/sphincs/sphincsplus/commit/77755c94d0bc744478044d6efbb888dc13156441 diff --git a/crypto_sign/sphincs-haraka-256f-simple/clean/LICENSE b/crypto_sign/sphincs-haraka-256f-simple/clean/LICENSE new file mode 100644 index 00000000..670154e3 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256f-simple/clean/LICENSE @@ -0,0 +1,116 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + diff --git a/crypto_sign/sphincs-haraka-256f-simple/clean/Makefile b/crypto_sign/sphincs-haraka-256f-simple/clean/Makefile new file mode 100644 index 00000000..2db645ad --- /dev/null +++ b/crypto_sign/sphincs-haraka-256f-simple/clean/Makefile @@ -0,0 +1,20 @@ +# This Makefile can be used with GNU Make or BSD Make + +LIB=libsphincs-haraka-256f-simple_clean.a + +HEADERS = params.h address.h wots.h utils.h fors.h api.h hash.h thash.h haraka.h +OBJECTS = address.o wots.o utils.o fors.o sign.o hash_haraka.o thash_haraka_simple.o haraka.o + +CFLAGS=-O3 -Wall -Wconversion -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) + +all: $(LIB) + +%.o: %.c $(HEADERS) + $(CC) $(CFLAGS) -c -o $@ $< + +$(LIB): $(OBJECTS) + $(AR) -r $@ $(OBJECTS) + +clean: + $(RM) $(OBJECTS) + $(RM) $(LIB) diff --git a/crypto_sign/sphincs-haraka-256f-simple/clean/Makefile.Microsoft_nmake b/crypto_sign/sphincs-haraka-256f-simple/clean/Makefile.Microsoft_nmake new file mode 100644 index 00000000..716d1c85 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256f-simple/clean/Makefile.Microsoft_nmake @@ -0,0 +1,19 @@ +# This Makefile can be used with Microsoft Visual Studio's nmake using the command: +# nmake /f Makefile.Microsoft_nmake + +LIBRARY=libsphincs-haraka-256f-simple_clean.lib +OBJECTS=address.obj wots.obj utils.obj fors.obj sign.obj hash_haraka.obj thash_haraka_simple.obj haraka.obj + +CFLAGS=/nologo /I ..\..\..\common /W4 /WX + +all: $(LIBRARY) + +# Make sure objects are recompiled if headers change. +$(OBJECTS): *.h + +$(LIBRARY): $(OBJECTS) + LIB.EXE /NOLOGO /WX /OUT:$@ $** + +clean: + -DEL $(OBJECTS) + -DEL $(LIBRARY) diff --git a/crypto_sign/sphincs-haraka-256f-simple/clean/address.c b/crypto_sign/sphincs-haraka-256f-simple/clean/address.c new file mode 100644 index 00000000..1ce4c0d9 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256f-simple/clean/address.c @@ -0,0 +1,78 @@ +#include + +#include "address.h" +#include "params.h" +#include "utils.h" + +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]) { + int i; + + for (i = 0; i < 8; i++) { + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_ull_to_bytes( + bytes + i * 4, 4, addr[i]); + } +} + +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer) { + addr[0] = layer; +} + +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree) { + addr[1] = 0; + addr[2] = (uint32_t) (tree >> 32); + addr[3] = (uint32_t) tree; +} + +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_type( + uint32_t addr[8], uint32_t type) { + addr[4] = type; +} + +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; +} + +/* These functions are used for OTS addresses. */ + +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair) { + addr[5] = keypair; +} + +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; + out[5] = in[5]; +} + +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain) { + addr[6] = chain; +} + +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash) { + addr[7] = hash; +} + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height) { + addr[6] = tree_height; +} + +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index) { + addr[7] = tree_index; +} diff --git a/crypto_sign/sphincs-haraka-256f-simple/clean/address.h b/crypto_sign/sphincs-haraka-256f-simple/clean/address.h new file mode 100644 index 00000000..0fd30570 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256f-simple/clean/address.h @@ -0,0 +1,50 @@ +#ifndef SPX_ADDRESS_H +#define SPX_ADDRESS_H + +#include + +#define SPX_ADDR_TYPE_WOTS 0 +#define SPX_ADDR_TYPE_WOTSPK 1 +#define SPX_ADDR_TYPE_HASHTREE 2 +#define SPX_ADDR_TYPE_FORSTREE 3 +#define SPX_ADDR_TYPE_FORSPK 4 + +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer); + +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree); + +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_type( + uint32_t addr[8], uint32_t type); + +/* Copies the layer and tree part of one address into the other */ +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for WOTS and FORS addresses. */ + +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair); + +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain); + +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash); + +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height); + +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index); + +#endif diff --git a/crypto_sign/sphincs-haraka-256f-simple/clean/api.h b/crypto_sign/sphincs-haraka-256f-simple/clean/api.h new file mode 100644 index 00000000..ba837023 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256f-simple/clean/api.h @@ -0,0 +1,78 @@ +#ifndef PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_API_H +#define PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_API_H + +#include +#include + +#define PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_CRYPTO_ALGNAME "SPHINCS+" + +#define PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_CRYPTO_SECRETKEYBYTES 128 +#define PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES 64 +#define PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_CRYPTO_BYTES 49216 +#define PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_CRYPTO_SEEDBYTES 96 + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_crypto_sign_secretkeybytes(void); + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_crypto_sign_publickeybytes(void); + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_crypto_sign_bytes(void); + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_crypto_sign_seedbytes(void); + +/* + * Generates a SPHINCS+ key pair given a seed. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed); + +/* + * Generates a SPHINCS+ key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk); + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk); + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk); + +#endif diff --git a/crypto_sign/sphincs-haraka-256f-simple/clean/fors.c b/crypto_sign/sphincs-haraka-256f-simple/clean/fors.c new file mode 100644 index 00000000..024fb4fb --- /dev/null +++ b/crypto_sign/sphincs-haraka-256f-simple/clean/fors.c @@ -0,0 +1,164 @@ +#include +#include +#include + +#include "address.h" +#include "fors.h" +#include "hash.h" +#include "thash.h" +#include "utils.h" + +static void fors_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_prf_addr( + sk, sk_seed, fors_leaf_addr); +} + +static void fors_sk_to_leaf(unsigned char *leaf, const unsigned char *sk, + const unsigned char *pub_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_thash_1( + leaf, sk, pub_seed, fors_leaf_addr); +} + +static void fors_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t fors_tree_addr[8]) { + uint32_t fors_leaf_addr[8] = {0}; + + /* Only copy the parts that must be kept in fors_leaf_addr. */ + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_copy_keypair_addr( + fors_leaf_addr, fors_tree_addr); + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_type( + fors_leaf_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_tree_index( + fors_leaf_addr, addr_idx); + + fors_gen_sk(leaf, sk_seed, fors_leaf_addr); + fors_sk_to_leaf(leaf, leaf, pub_seed, fors_leaf_addr); +} + +/** + * Interprets m as SPX_FORS_HEIGHT-bit unsigned integers. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + * Assumes indices has space for SPX_FORS_TREES integers. + */ +static void message_to_indices(uint32_t *indices, const unsigned char *m) { + unsigned int i, j; + unsigned int offset = 0; + + for (i = 0; i < SPX_FORS_TREES; i++) { + indices[i] = 0; + for (j = 0; j < SPX_FORS_HEIGHT; j++) { + indices[i] ^= (((uint32_t)m[offset >> 3] >> (offset & 0x7)) & 0x1) << j; + offset++; + } + } +} + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Include the secret key part that produces the selected leaf node. */ + fors_gen_sk(sig, sk_seed, fors_tree_addr); + sig += SPX_N; + + /* Compute the authentication path for this leaf node. */ + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_treehash_FORS_HEIGHT( + roots + i * SPX_N, sig, sk_seed, pub_seed, + indices[i], idx_offset, fors_gen_leaf, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + unsigned char leaf[SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Derive the leaf from the included secret key part. */ + fors_sk_to_leaf(leaf, sig, pub_seed, fors_tree_addr); + sig += SPX_N; + + /* Derive the corresponding root node of this tree. */ + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_compute_root( + roots + i * SPX_N, leaf, indices[i], idx_offset, sig, + SPX_FORS_HEIGHT, pub_seed, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} diff --git a/crypto_sign/sphincs-haraka-256f-simple/clean/fors.h b/crypto_sign/sphincs-haraka-256f-simple/clean/fors.h new file mode 100644 index 00000000..c9ea37a6 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256f-simple/clean/fors.h @@ -0,0 +1,30 @@ +#ifndef SPX_FORS_H +#define SPX_FORS_H + +#include + +#include "params.h" + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]); + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-haraka-256f-simple/clean/haraka.c b/crypto_sign/sphincs-haraka-256f-simple/clean/haraka.c new file mode 100644 index 00000000..3b85173c --- /dev/null +++ b/crypto_sign/sphincs-haraka-256f-simple/clean/haraka.c @@ -0,0 +1,965 @@ +/* + * Constant time implementation of the Haraka hash function. + * + * The bit-sliced implementation of the AES round functions are + * based on the AES implementation in BearSSL written + * by Thomas Pornin + */ + +#include +#include +#include + +#include "haraka.h" + +#define HARAKAS_RATE 32 + +static const uint64_t haraka512_rc64[10][8] = { + {0x24cf0ab9086f628b, 0xbdd6eeecc83b8382, 0xd96fb0306cdad0a7, 0xaace082ac8f95f89, 0x449d8e8870d7041f, 0x49bb2f80b2b3e2f8, 0x0569ae98d93bb258, 0x23dc9691e7d6a4b1}, + {0xd8ba10ede0fe5b6e, 0x7ecf7dbe424c7b8e, 0x6ea9949c6df62a31, 0xbf3f3c97ec9c313e, 0x241d03a196a1861e, 0xead3a51116e5a2ea, 0x77d479fcad9574e3, 0x18657a1af894b7a0}, + {0x10671e1a7f595522, 0xd9a00ff675d28c7b, 0x2f1edf0d2b9ba661, 0xb8ff58b8e3de45f9, 0xee29261da9865c02, 0xd1532aa4b50bdf43, 0x8bf858159b231bb1, 0xdf17439d22d4f599}, + {0xdd4b2f0870b918c0, 0x757a81f3b39b1bb6, 0x7a5c556898952e3f, 0x7dd70a16d915d87a, 0x3ae61971982b8301, 0xc3ab319e030412be, 0x17c0033ac094a8cb, 0x5a0630fc1a8dc4ef}, + {0x17708988c1632f73, 0xf92ddae090b44f4f, 0x11ac0285c43aa314, 0x509059941936b8ba, 0xd03e152fa2ce9b69, 0x3fbcbcb63a32998b, 0x6204696d692254f7, 0x915542ed93ec59b4}, + {0xf4ed94aa8879236e, 0xff6cb41cd38e03c0, 0x069b38602368aeab, 0x669495b820f0ddba, 0xf42013b1b8bf9e3d, 0xcf935efe6439734d, 0xbc1dcf42ca29e3f8, 0x7e6d3ed29f78ad67}, + {0xf3b0f6837ffcddaa, 0x3a76faef934ddf41, 0xcec7ae583a9c8e35, 0xe4dd18c68f0260af, 0x2c0e5df1ad398eaa, 0x478df5236ae22e8c, 0xfb944c46fe865f39, 0xaa48f82f028132ba}, + {0x231b9ae2b76aca77, 0x292a76a712db0b40, 0x5850625dc8134491, 0x73137dd469810fb5, 0x8a12a6a202a474fd, 0xd36fd9daa78bdb80, 0xb34c5e733505706f, 0xbaf1cdca818d9d96}, + {0x2e99781335e8c641, 0xbddfe5cce47d560e, 0xf74e9bf32e5e040c, 0x1d7a709d65996be9, 0x670df36a9cf66cdd, 0xd05ef84a176a2875, 0x0f888e828cb1c44e, 0x1a79e9c9727b052c}, + {0x83497348628d84de, 0x2e9387d51f22a754, 0xb000068da2f852d6, 0x378c9e1190fd6fe5, 0x870027c316de7293, 0xe51a9d4462e047bb, 0x90ecf7f8c6251195, 0x655953bfbed90a9c}, +}; + +static uint64_t tweaked512_rc64[10][8]; +static uint32_t tweaked256_rc32[10][8]; +static uint32_t tweaked256_rc32_sseed[10][8]; + +static inline uint32_t br_dec32le(const unsigned char *src) { + return (uint32_t)src[0] + | ((uint32_t)src[1] << 8) + | ((uint32_t)src[2] << 16) + | ((uint32_t)src[3] << 24); +} + +static void br_range_dec32le(uint32_t *v, size_t num, const unsigned char *src) { + while (num-- > 0) { + *v ++ = br_dec32le(src); + src += 4; + } +} + +static inline void br_enc32le(unsigned char *dst, uint32_t x) { + dst[0] = (unsigned char)x; + dst[1] = (unsigned char)(x >> 8); + dst[2] = (unsigned char)(x >> 16); + dst[3] = (unsigned char)(x >> 24); +} + + +static void br_range_enc32le(unsigned char *dst, const uint32_t *v, size_t num) { + while (num-- > 0) { + br_enc32le(dst, *v ++); + dst += 4; + } +} + +static void br_aes_ct64_bitslice_Sbox(uint64_t *q) { + /* + * This S-box implementation is a straightforward translation of + * the circuit described by Boyar and Peralta in "A new + * combinational logic minimization technique with applications + * to cryptology" (https://eprint.iacr.org/2009/191.pdf). + * + * Note that variables x* (input) and s* (output) are numbered + * in "reverse" order (x0 is the high bit, x7 is the low bit). + */ + + uint64_t x0, x1, x2, x3, x4, x5, x6, x7; + uint64_t y1, y2, y3, y4, y5, y6, y7, y8, y9; + uint64_t y10, y11, y12, y13, y14, y15, y16, y17, y18, y19; + uint64_t y20, y21; + uint64_t z0, z1, z2, z3, z4, z5, z6, z7, z8, z9; + uint64_t z10, z11, z12, z13, z14, z15, z16, z17; + uint64_t t0, t1, t2, t3, t4, t5, t6, t7, t8, t9; + uint64_t t10, t11, t12, t13, t14, t15, t16, t17, t18, t19; + uint64_t t20, t21, t22, t23, t24, t25, t26, t27, t28, t29; + uint64_t t30, t31, t32, t33, t34, t35, t36, t37, t38, t39; + uint64_t t40, t41, t42, t43, t44, t45, t46, t47, t48, t49; + uint64_t t50, t51, t52, t53, t54, t55, t56, t57, t58, t59; + uint64_t t60, t61, t62, t63, t64, t65, t66, t67; + uint64_t s0, s1, s2, s3, s4, s5, s6, s7; + + x0 = q[7]; + x1 = q[6]; + x2 = q[5]; + x3 = q[4]; + x4 = q[3]; + x5 = q[2]; + x6 = q[1]; + x7 = q[0]; + + /* + * Top linear transformation. + */ + y14 = x3 ^ x5; + y13 = x0 ^ x6; + y9 = x0 ^ x3; + y8 = x0 ^ x5; + t0 = x1 ^ x2; + y1 = t0 ^ x7; + y4 = y1 ^ x3; + y12 = y13 ^ y14; + y2 = y1 ^ x0; + y5 = y1 ^ x6; + y3 = y5 ^ y8; + t1 = x4 ^ y12; + y15 = t1 ^ x5; + y20 = t1 ^ x1; + y6 = y15 ^ x7; + y10 = y15 ^ t0; + y11 = y20 ^ y9; + y7 = x7 ^ y11; + y17 = y10 ^ y11; + y19 = y10 ^ y8; + y16 = t0 ^ y11; + y21 = y13 ^ y16; + y18 = x0 ^ y16; + + /* + * Non-linear section. + */ + t2 = y12 & y15; + t3 = y3 & y6; + t4 = t3 ^ t2; + t5 = y4 & x7; + t6 = t5 ^ t2; + t7 = y13 & y16; + t8 = y5 & y1; + t9 = t8 ^ t7; + t10 = y2 & y7; + t11 = t10 ^ t7; + t12 = y9 & y11; + t13 = y14 & y17; + t14 = t13 ^ t12; + t15 = y8 & y10; + t16 = t15 ^ t12; + t17 = t4 ^ t14; + t18 = t6 ^ t16; + t19 = t9 ^ t14; + t20 = t11 ^ t16; + t21 = t17 ^ y20; + t22 = t18 ^ y19; + t23 = t19 ^ y21; + t24 = t20 ^ y18; + + t25 = t21 ^ t22; + t26 = t21 & t23; + t27 = t24 ^ t26; + t28 = t25 & t27; + t29 = t28 ^ t22; + t30 = t23 ^ t24; + t31 = t22 ^ t26; + t32 = t31 & t30; + t33 = t32 ^ t24; + t34 = t23 ^ t33; + t35 = t27 ^ t33; + t36 = t24 & t35; + t37 = t36 ^ t34; + t38 = t27 ^ t36; + t39 = t29 & t38; + t40 = t25 ^ t39; + + t41 = t40 ^ t37; + t42 = t29 ^ t33; + t43 = t29 ^ t40; + t44 = t33 ^ t37; + t45 = t42 ^ t41; + z0 = t44 & y15; + z1 = t37 & y6; + z2 = t33 & x7; + z3 = t43 & y16; + z4 = t40 & y1; + z5 = t29 & y7; + z6 = t42 & y11; + z7 = t45 & y17; + z8 = t41 & y10; + z9 = t44 & y12; + z10 = t37 & y3; + z11 = t33 & y4; + z12 = t43 & y13; + z13 = t40 & y5; + z14 = t29 & y2; + z15 = t42 & y9; + z16 = t45 & y14; + z17 = t41 & y8; + + /* + * Bottom linear transformation. + */ + t46 = z15 ^ z16; + t47 = z10 ^ z11; + t48 = z5 ^ z13; + t49 = z9 ^ z10; + t50 = z2 ^ z12; + t51 = z2 ^ z5; + t52 = z7 ^ z8; + t53 = z0 ^ z3; + t54 = z6 ^ z7; + t55 = z16 ^ z17; + t56 = z12 ^ t48; + t57 = t50 ^ t53; + t58 = z4 ^ t46; + t59 = z3 ^ t54; + t60 = t46 ^ t57; + t61 = z14 ^ t57; + t62 = t52 ^ t58; + t63 = t49 ^ t58; + t64 = z4 ^ t59; + t65 = t61 ^ t62; + t66 = z1 ^ t63; + s0 = t59 ^ t63; + s6 = t56 ^ ~t62; + s7 = t48 ^ ~t60; + t67 = t64 ^ t65; + s3 = t53 ^ t66; + s4 = t51 ^ t66; + s5 = t47 ^ t65; + s1 = t64 ^ ~s3; + s2 = t55 ^ ~t67; + + q[7] = s0; + q[6] = s1; + q[5] = s2; + q[4] = s3; + q[3] = s4; + q[2] = s5; + q[1] = s6; + q[0] = s7; +} + +static void br_aes_ct_bitslice_Sbox(uint32_t *q) { + /* + * This S-box implementation is a straightforward translation of + * the circuit described by Boyar and Peralta in "A new + * combinational logic minimization technique with applications + * to cryptology" (https://eprint.iacr.org/2009/191.pdf). + * + * Note that variables x* (input) and s* (output) are numbered + * in "reverse" order (x0 is the high bit, x7 is the low bit). + */ + + uint32_t x0, x1, x2, x3, x4, x5, x6, x7; + uint32_t y1, y2, y3, y4, y5, y6, y7, y8, y9; + uint32_t y10, y11, y12, y13, y14, y15, y16, y17, y18, y19; + uint32_t y20, y21; + uint32_t z0, z1, z2, z3, z4, z5, z6, z7, z8, z9; + uint32_t z10, z11, z12, z13, z14, z15, z16, z17; + uint32_t t0, t1, t2, t3, t4, t5, t6, t7, t8, t9; + uint32_t t10, t11, t12, t13, t14, t15, t16, t17, t18, t19; + uint32_t t20, t21, t22, t23, t24, t25, t26, t27, t28, t29; + uint32_t t30, t31, t32, t33, t34, t35, t36, t37, t38, t39; + uint32_t t40, t41, t42, t43, t44, t45, t46, t47, t48, t49; + uint32_t t50, t51, t52, t53, t54, t55, t56, t57, t58, t59; + uint32_t t60, t61, t62, t63, t64, t65, t66, t67; + uint32_t s0, s1, s2, s3, s4, s5, s6, s7; + + x0 = q[7]; + x1 = q[6]; + x2 = q[5]; + x3 = q[4]; + x4 = q[3]; + x5 = q[2]; + x6 = q[1]; + x7 = q[0]; + + /* + * Top linear transformation. + */ + y14 = x3 ^ x5; + y13 = x0 ^ x6; + y9 = x0 ^ x3; + y8 = x0 ^ x5; + t0 = x1 ^ x2; + y1 = t0 ^ x7; + y4 = y1 ^ x3; + y12 = y13 ^ y14; + y2 = y1 ^ x0; + y5 = y1 ^ x6; + y3 = y5 ^ y8; + t1 = x4 ^ y12; + y15 = t1 ^ x5; + y20 = t1 ^ x1; + y6 = y15 ^ x7; + y10 = y15 ^ t0; + y11 = y20 ^ y9; + y7 = x7 ^ y11; + y17 = y10 ^ y11; + y19 = y10 ^ y8; + y16 = t0 ^ y11; + y21 = y13 ^ y16; + y18 = x0 ^ y16; + + /* + * Non-linear section. + */ + t2 = y12 & y15; + t3 = y3 & y6; + t4 = t3 ^ t2; + t5 = y4 & x7; + t6 = t5 ^ t2; + t7 = y13 & y16; + t8 = y5 & y1; + t9 = t8 ^ t7; + t10 = y2 & y7; + t11 = t10 ^ t7; + t12 = y9 & y11; + t13 = y14 & y17; + t14 = t13 ^ t12; + t15 = y8 & y10; + t16 = t15 ^ t12; + t17 = t4 ^ t14; + t18 = t6 ^ t16; + t19 = t9 ^ t14; + t20 = t11 ^ t16; + t21 = t17 ^ y20; + t22 = t18 ^ y19; + t23 = t19 ^ y21; + t24 = t20 ^ y18; + + t25 = t21 ^ t22; + t26 = t21 & t23; + t27 = t24 ^ t26; + t28 = t25 & t27; + t29 = t28 ^ t22; + t30 = t23 ^ t24; + t31 = t22 ^ t26; + t32 = t31 & t30; + t33 = t32 ^ t24; + t34 = t23 ^ t33; + t35 = t27 ^ t33; + t36 = t24 & t35; + t37 = t36 ^ t34; + t38 = t27 ^ t36; + t39 = t29 & t38; + t40 = t25 ^ t39; + + t41 = t40 ^ t37; + t42 = t29 ^ t33; + t43 = t29 ^ t40; + t44 = t33 ^ t37; + t45 = t42 ^ t41; + z0 = t44 & y15; + z1 = t37 & y6; + z2 = t33 & x7; + z3 = t43 & y16; + z4 = t40 & y1; + z5 = t29 & y7; + z6 = t42 & y11; + z7 = t45 & y17; + z8 = t41 & y10; + z9 = t44 & y12; + z10 = t37 & y3; + z11 = t33 & y4; + z12 = t43 & y13; + z13 = t40 & y5; + z14 = t29 & y2; + z15 = t42 & y9; + z16 = t45 & y14; + z17 = t41 & y8; + + /* + * Bottom linear transformation. + */ + t46 = z15 ^ z16; + t47 = z10 ^ z11; + t48 = z5 ^ z13; + t49 = z9 ^ z10; + t50 = z2 ^ z12; + t51 = z2 ^ z5; + t52 = z7 ^ z8; + t53 = z0 ^ z3; + t54 = z6 ^ z7; + t55 = z16 ^ z17; + t56 = z12 ^ t48; + t57 = t50 ^ t53; + t58 = z4 ^ t46; + t59 = z3 ^ t54; + t60 = t46 ^ t57; + t61 = z14 ^ t57; + t62 = t52 ^ t58; + t63 = t49 ^ t58; + t64 = z4 ^ t59; + t65 = t61 ^ t62; + t66 = z1 ^ t63; + s0 = t59 ^ t63; + s6 = t56 ^ ~t62; + s7 = t48 ^ ~t60; + t67 = t64 ^ t65; + s3 = t53 ^ t66; + s4 = t51 ^ t66; + s5 = t47 ^ t65; + s1 = t64 ^ ~s3; + s2 = t55 ^ ~t67; + + q[7] = s0; + q[6] = s1; + q[5] = s2; + q[4] = s3; + q[3] = s4; + q[2] = s5; + q[1] = s6; + q[0] = s7; +} + +static void br_aes_ct_ortho(uint32_t *q) { +#define SWAPN_32(cl, ch, s, x, y) do { \ + uint32_t a, b; \ + a = (x); \ + b = (y); \ + (x) = (a & (uint32_t)(cl)) | ((b & (uint32_t)(cl)) << (s)); \ + (y) = ((a & (uint32_t)(ch)) >> (s)) | (b & (uint32_t)(ch)); \ + } while (0) + +#define SWAP2_32(x, y) SWAPN_32(0x55555555, 0xAAAAAAAA, 1, x, y) +#define SWAP4_32(x, y) SWAPN_32(0x33333333, 0xCCCCCCCC, 2, x, y) +#define SWAP8_32(x, y) SWAPN_32(0x0F0F0F0F, 0xF0F0F0F0, 4, x, y) + + SWAP2_32(q[0], q[1]); + SWAP2_32(q[2], q[3]); + SWAP2_32(q[4], q[5]); + SWAP2_32(q[6], q[7]); + + SWAP4_32(q[0], q[2]); + SWAP4_32(q[1], q[3]); + SWAP4_32(q[4], q[6]); + SWAP4_32(q[5], q[7]); + + SWAP8_32(q[0], q[4]); + SWAP8_32(q[1], q[5]); + SWAP8_32(q[2], q[6]); + SWAP8_32(q[3], q[7]); +} + +static inline void add_round_key32(uint32_t *q, const uint32_t *sk) { + q[0] ^= sk[0]; + q[1] ^= sk[1]; + q[2] ^= sk[2]; + q[3] ^= sk[3]; + q[4] ^= sk[4]; + q[5] ^= sk[5]; + q[6] ^= sk[6]; + q[7] ^= sk[7]; +} + +static inline void shift_rows32(uint32_t *q) { + int i; + + for (i = 0; i < 8; i++) { + uint32_t x; + + x = q[i]; + q[i] = (x & 0x000000FF) + | ((x & 0x0000FC00) >> 2) | ((x & 0x00000300) << 6) + | ((x & 0x00F00000) >> 4) | ((x & 0x000F0000) << 4) + | ((x & 0xC0000000) >> 6) | ((x & 0x3F000000) << 2); + } +} + +static inline uint32_t rotr16(uint32_t x) { + return (x << 16) | (x >> 16); +} + +static inline void mix_columns32(uint32_t *q) { + uint32_t q0, q1, q2, q3, q4, q5, q6, q7; + uint32_t r0, r1, r2, r3, r4, r5, r6, r7; + + q0 = q[0]; + q1 = q[1]; + q2 = q[2]; + q3 = q[3]; + q4 = q[4]; + q5 = q[5]; + q6 = q[6]; + q7 = q[7]; + r0 = (q0 >> 8) | (q0 << 24); + r1 = (q1 >> 8) | (q1 << 24); + r2 = (q2 >> 8) | (q2 << 24); + r3 = (q3 >> 8) | (q3 << 24); + r4 = (q4 >> 8) | (q4 << 24); + r5 = (q5 >> 8) | (q5 << 24); + r6 = (q6 >> 8) | (q6 << 24); + r7 = (q7 >> 8) | (q7 << 24); + + q[0] = q7 ^ r7 ^ r0 ^ rotr16(q0 ^ r0); + q[1] = q0 ^ r0 ^ q7 ^ r7 ^ r1 ^ rotr16(q1 ^ r1); + q[2] = q1 ^ r1 ^ r2 ^ rotr16(q2 ^ r2); + q[3] = q2 ^ r2 ^ q7 ^ r7 ^ r3 ^ rotr16(q3 ^ r3); + q[4] = q3 ^ r3 ^ q7 ^ r7 ^ r4 ^ rotr16(q4 ^ r4); + q[5] = q4 ^ r4 ^ r5 ^ rotr16(q5 ^ r5); + q[6] = q5 ^ r5 ^ r6 ^ rotr16(q6 ^ r6); + q[7] = q6 ^ r6 ^ r7 ^ rotr16(q7 ^ r7); +} + +static void br_aes_ct64_ortho(uint64_t *q) { +#define SWAPN(cl, ch, s, x, y) do { \ + uint64_t a, b; \ + a = (x); \ + b = (y); \ + (x) = (a & (uint64_t)(cl)) | ((b & (uint64_t)(cl)) << (s)); \ + (y) = ((a & (uint64_t)(ch)) >> (s)) | (b & (uint64_t)(ch)); \ + } while (0) + +#define SWAP2(x, y) SWAPN(0x5555555555555555, 0xAAAAAAAAAAAAAAAA, 1, x, y) +#define SWAP4(x, y) SWAPN(0x3333333333333333, 0xCCCCCCCCCCCCCCCC, 2, x, y) +#define SWAP8(x, y) SWAPN(0x0F0F0F0F0F0F0F0F, 0xF0F0F0F0F0F0F0F0, 4, x, y) + + SWAP2(q[0], q[1]); + SWAP2(q[2], q[3]); + SWAP2(q[4], q[5]); + SWAP2(q[6], q[7]); + + SWAP4(q[0], q[2]); + SWAP4(q[1], q[3]); + SWAP4(q[4], q[6]); + SWAP4(q[5], q[7]); + + SWAP8(q[0], q[4]); + SWAP8(q[1], q[5]); + SWAP8(q[2], q[6]); + SWAP8(q[3], q[7]); +} + + +static void br_aes_ct64_interleave_in(uint64_t *q0, uint64_t *q1, const uint32_t *w) { + uint64_t x0, x1, x2, x3; + + x0 = w[0]; + x1 = w[1]; + x2 = w[2]; + x3 = w[3]; + x0 |= (x0 << 16); + x1 |= (x1 << 16); + x2 |= (x2 << 16); + x3 |= (x3 << 16); + x0 &= (uint64_t)0x0000FFFF0000FFFF; + x1 &= (uint64_t)0x0000FFFF0000FFFF; + x2 &= (uint64_t)0x0000FFFF0000FFFF; + x3 &= (uint64_t)0x0000FFFF0000FFFF; + x0 |= (x0 << 8); + x1 |= (x1 << 8); + x2 |= (x2 << 8); + x3 |= (x3 << 8); + x0 &= (uint64_t)0x00FF00FF00FF00FF; + x1 &= (uint64_t)0x00FF00FF00FF00FF; + x2 &= (uint64_t)0x00FF00FF00FF00FF; + x3 &= (uint64_t)0x00FF00FF00FF00FF; + *q0 = x0 | (x2 << 8); + *q1 = x1 | (x3 << 8); +} + + +static void br_aes_ct64_interleave_out(uint32_t *w, uint64_t q0, uint64_t q1) { + uint64_t x0, x1, x2, x3; + + x0 = q0 & (uint64_t)0x00FF00FF00FF00FF; + x1 = q1 & (uint64_t)0x00FF00FF00FF00FF; + x2 = (q0 >> 8) & (uint64_t)0x00FF00FF00FF00FF; + x3 = (q1 >> 8) & (uint64_t)0x00FF00FF00FF00FF; + x0 |= (x0 >> 8); + x1 |= (x1 >> 8); + x2 |= (x2 >> 8); + x3 |= (x3 >> 8); + x0 &= (uint64_t)0x0000FFFF0000FFFF; + x1 &= (uint64_t)0x0000FFFF0000FFFF; + x2 &= (uint64_t)0x0000FFFF0000FFFF; + x3 &= (uint64_t)0x0000FFFF0000FFFF; + w[0] = (uint32_t)x0 | (uint32_t)(x0 >> 16); + w[1] = (uint32_t)x1 | (uint32_t)(x1 >> 16); + w[2] = (uint32_t)x2 | (uint32_t)(x2 >> 16); + w[3] = (uint32_t)x3 | (uint32_t)(x3 >> 16); +} + +static inline void add_round_key(uint64_t *q, const uint64_t *sk) { + q[0] ^= sk[0]; + q[1] ^= sk[1]; + q[2] ^= sk[2]; + q[3] ^= sk[3]; + q[4] ^= sk[4]; + q[5] ^= sk[5]; + q[6] ^= sk[6]; + q[7] ^= sk[7]; +} + +static inline void shift_rows(uint64_t *q) { + int i; + + for (i = 0; i < 8; i++) { + uint64_t x; + + x = q[i]; + q[i] = (x & (uint64_t)0x000000000000FFFF) + | ((x & (uint64_t)0x00000000FFF00000) >> 4) + | ((x & (uint64_t)0x00000000000F0000) << 12) + | ((x & (uint64_t)0x0000FF0000000000) >> 8) + | ((x & (uint64_t)0x000000FF00000000) << 8) + | ((x & (uint64_t)0xF000000000000000) >> 12) + | ((x & (uint64_t)0x0FFF000000000000) << 4); + } +} + +static inline uint64_t rotr32(uint64_t x) { + return (x << 32) | (x >> 32); +} + +static inline void mix_columns(uint64_t *q) { + uint64_t q0, q1, q2, q3, q4, q5, q6, q7; + uint64_t r0, r1, r2, r3, r4, r5, r6, r7; + + q0 = q[0]; + q1 = q[1]; + q2 = q[2]; + q3 = q[3]; + q4 = q[4]; + q5 = q[5]; + q6 = q[6]; + q7 = q[7]; + r0 = (q0 >> 16) | (q0 << 48); + r1 = (q1 >> 16) | (q1 << 48); + r2 = (q2 >> 16) | (q2 << 48); + r3 = (q3 >> 16) | (q3 << 48); + r4 = (q4 >> 16) | (q4 << 48); + r5 = (q5 >> 16) | (q5 << 48); + r6 = (q6 >> 16) | (q6 << 48); + r7 = (q7 >> 16) | (q7 << 48); + + q[0] = q7 ^ r7 ^ r0 ^ rotr32(q0 ^ r0); + q[1] = q0 ^ r0 ^ q7 ^ r7 ^ r1 ^ rotr32(q1 ^ r1); + q[2] = q1 ^ r1 ^ r2 ^ rotr32(q2 ^ r2); + q[3] = q2 ^ r2 ^ q7 ^ r7 ^ r3 ^ rotr32(q3 ^ r3); + q[4] = q3 ^ r3 ^ q7 ^ r7 ^ r4 ^ rotr32(q4 ^ r4); + q[5] = q4 ^ r4 ^ r5 ^ rotr32(q5 ^ r5); + q[6] = q5 ^ r5 ^ r6 ^ rotr32(q6 ^ r6); + q[7] = q6 ^ r6 ^ r7 ^ rotr32(q7 ^ r7); +} + +static void interleave_constant(uint64_t *out, const unsigned char *in) { + uint32_t tmp_32_constant[16]; + int i; + + br_range_dec32le(tmp_32_constant, 16, in); + for (i = 0; i < 4; i++) { + br_aes_ct64_interleave_in(&out[i], &out[i + 4], tmp_32_constant + (i << 2)); + } + br_aes_ct64_ortho(out); +} + +static void interleave_constant32(uint32_t *out, const unsigned char *in) { + int i; + for (i = 0; i < 4; i++) { + out[2 * i] = br_dec32le(in + 4 * i); + out[2 * i + 1] = br_dec32le(in + 4 * i + 16); + } + br_aes_ct_ortho(out); +} + +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_tweak_constants( + const unsigned char *pk_seed, const unsigned char *sk_seed, + unsigned long long seed_length) { + unsigned char buf[40 * 16]; + int i; + + /* Use the standard constants to generate tweaked ones. */ + memcpy((uint8_t *)tweaked512_rc64, (uint8_t *)haraka512_rc64, 40 * 16); + + /* Constants for sk.seed */ + if (sk_seed != NULL) { + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_haraka_S( + buf, 40 * 16, sk_seed, seed_length); + + /* Interleave constants */ + for (i = 0; i < 10; i++) { + interleave_constant32(tweaked256_rc32_sseed[i], buf + 32 * i); + } + } + + /* Constants for pk.seed */ + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_haraka_S( + buf, 40 * 16, pk_seed, seed_length); + for (i = 0; i < 10; i++) { + interleave_constant32(tweaked256_rc32[i], buf + 32 * i); + interleave_constant(tweaked512_rc64[i], buf + 64 * i); + } +} + +static void haraka_S_absorb(unsigned char *s, + const unsigned char *m, unsigned long long mlen, + unsigned char p) { + unsigned long long i; + unsigned char t[HARAKAS_RATE]; + + while (mlen >= HARAKAS_RATE) { + /* XOR block to state */ + for (i = 0; i < HARAKAS_RATE; ++i) { + s[i] ^= m[i]; + } + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_haraka512_perm(s, s); + mlen -= HARAKAS_RATE; + m += HARAKAS_RATE; + } + + for (i = 0; i < HARAKAS_RATE; ++i) { + t[i] = 0; + } + for (i = 0; i < mlen; ++i) { + t[i] = m[i]; + } + t[i] = p; + t[HARAKAS_RATE - 1] |= 128; + for (i = 0; i < HARAKAS_RATE; ++i) { + s[i] ^= t[i]; + } +} + +static void haraka_S_squeezeblocks(unsigned char *h, unsigned long long nblocks, + unsigned char *s) { + while (nblocks > 0) { + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_haraka512_perm(s, s); + memcpy(h, s, HARAKAS_RATE); + h += HARAKAS_RATE; + nblocks--; + } +} + +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_haraka_S_inc_init(uint8_t *s_inc) { + size_t i; + + for (i = 0; i < 64; i++) { + s_inc[i] = 0; + } + s_inc[64] = 0; +} + +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_haraka_S_inc_absorb(uint8_t *s_inc, const uint8_t *m, size_t mlen) { + size_t i; + + /* Recall that s_inc[64] is the non-absorbed bytes xored into the state */ + while (mlen + s_inc[64] >= HARAKAS_RATE) { + for (i = 0; i < (size_t)(HARAKAS_RATE - s_inc[64]); i++) { + /* Take the i'th byte from message + xor with the s_inc[64] + i'th byte of the state */ + s_inc[s_inc[64] + i] ^= m[i]; + } + mlen -= (size_t)(HARAKAS_RATE - s_inc[64]); + m += HARAKAS_RATE - s_inc[64]; + s_inc[64] = 0; + + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_haraka512_perm(s_inc, s_inc); + } + + for (i = 0; i < mlen; i++) { + s_inc[s_inc[64] + i] ^= m[i]; + } + s_inc[64] = (uint8_t)(mlen + s_inc[64]); +} + +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_haraka_S_inc_finalize(uint8_t *s_inc) { + /* After haraka_S_inc_absorb, we are guaranteed that s_inc[64] < HARAKAS_RATE, + so we can always use one more byte for p in the current state. */ + s_inc[s_inc[64]] ^= 0x1F; + s_inc[HARAKAS_RATE - 1] ^= 128; + s_inc[64] = 0; +} + +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_haraka_S_inc_squeeze(uint8_t *out, size_t outlen, uint8_t *s_inc) { + uint8_t i; + + /* First consume any bytes we still have sitting around */ + for (i = 0; i < outlen && i < s_inc[64]; i++) { + /* There are s_inc[64] bytes left, so r - s_inc[64] is the first + available byte. We consume from there, i.e., up to r. */ + out[i] = s_inc[(HARAKAS_RATE - s_inc[64] + i)]; + } + out += i; + outlen -= i; + s_inc[64] = (uint8_t)(s_inc[64] - i); + + /* Then squeeze the remaining necessary blocks */ + while (outlen > 0) { + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_haraka512_perm(s_inc, s_inc); + + for (i = 0; i < outlen && i < HARAKAS_RATE; i++) { + out[i] = s_inc[i]; + } + out += i; + outlen -= i; + s_inc[64] = (uint8_t)(HARAKAS_RATE - i); + } +} + +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_haraka_S(unsigned char *out, unsigned long long outlen, const unsigned char *in, unsigned long long inlen) { + unsigned long long i; + unsigned char s[64]; + unsigned char d[32]; + + for (i = 0; i < 64; i++) { + s[i] = 0; + } + haraka_S_absorb(s, in, inlen, 0x1F); + + haraka_S_squeezeblocks(out, outlen / 32, s); + out += (outlen / 32) * 32; + + if (outlen % 32) { + haraka_S_squeezeblocks(d, 1, s); + for (i = 0; i < outlen % 32; i++) { + out[i] = d[i]; + } + } +} + +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_haraka512_perm(unsigned char *out, const unsigned char *in) { + uint32_t w[16]; + uint64_t q[8], tmp_q; + unsigned int i, j; + + br_range_dec32le(w, 16, in); + for (i = 0; i < 4; i++) { + br_aes_ct64_interleave_in(&q[i], &q[i + 4], w + (i << 2)); + } + br_aes_ct64_ortho(q); + + /* AES rounds */ + for (i = 0; i < 5; i++) { + for (j = 0; j < 2; j++) { + br_aes_ct64_bitslice_Sbox(q); + shift_rows(q); + mix_columns(q); + add_round_key(q, tweaked512_rc64[2 * i + j]); + } + /* Mix states */ + for (j = 0; j < 8; j++) { + tmp_q = q[j]; + q[j] = (tmp_q & 0x0001000100010001) << 5 | + (tmp_q & 0x0002000200020002) << 12 | + (tmp_q & 0x0004000400040004) >> 1 | + (tmp_q & 0x0008000800080008) << 6 | + (tmp_q & 0x0020002000200020) << 9 | + (tmp_q & 0x0040004000400040) >> 4 | + (tmp_q & 0x0080008000800080) << 3 | + (tmp_q & 0x2100210021002100) >> 5 | + (tmp_q & 0x0210021002100210) << 2 | + (tmp_q & 0x0800080008000800) << 4 | + (tmp_q & 0x1000100010001000) >> 12 | + (tmp_q & 0x4000400040004000) >> 10 | + (tmp_q & 0x8400840084008400) >> 3; + } + } + + br_aes_ct64_ortho(q); + for (i = 0; i < 4; i ++) { + br_aes_ct64_interleave_out(w + (i << 2), q[i], q[i + 4]); + } + br_range_enc32le(out, w, 16); +} + +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_haraka512(unsigned char *out, const unsigned char *in) { + int i; + + unsigned char buf[64]; + + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_haraka512_perm(buf, in); + /* Feed-forward */ + for (i = 0; i < 64; i++) { + buf[i] = buf[i] ^ in[i]; + } + + /* Truncated */ + memcpy(out, buf + 8, 8); + memcpy(out + 8, buf + 24, 8); + memcpy(out + 16, buf + 32, 8); + memcpy(out + 24, buf + 48, 8); +} + + +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_haraka256(unsigned char *out, const unsigned char *in) { + uint32_t q[8], tmp_q; + int i, j; + + for (i = 0; i < 4; i++) { + q[2 * i] = br_dec32le(in + 4 * i); + q[2 * i + 1] = br_dec32le(in + 4 * i + 16); + } + br_aes_ct_ortho(q); + + /* AES rounds */ + for (i = 0; i < 5; i++) { + for (j = 0; j < 2; j++) { + br_aes_ct_bitslice_Sbox(q); + shift_rows32(q); + mix_columns32(q); + add_round_key32(q, tweaked256_rc32[2 * i + j]); + } + + /* Mix states */ + for (j = 0; j < 8; j++) { + tmp_q = q[j]; + q[j] = (tmp_q & 0x81818181) | + (tmp_q & 0x02020202) << 1 | + (tmp_q & 0x04040404) << 2 | + (tmp_q & 0x08080808) << 3 | + (tmp_q & 0x10101010) >> 3 | + (tmp_q & 0x20202020) >> 2 | + (tmp_q & 0x40404040) >> 1; + } + } + + br_aes_ct_ortho(q); + for (i = 0; i < 4; i++) { + br_enc32le(out + 4 * i, q[2 * i]); + br_enc32le(out + 4 * i + 16, q[2 * i + 1]); + } + + for (i = 0; i < 32; i++) { + out[i] ^= in[i]; + } +} + +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_haraka256_sk(unsigned char *out, const unsigned char *in) { + uint32_t q[8], tmp_q; + int i, j; + + for (i = 0; i < 4; i++) { + q[2 * i] = br_dec32le(in + 4 * i); + q[2 * i + 1] = br_dec32le(in + 4 * i + 16); + } + br_aes_ct_ortho(q); + + /* AES rounds */ + for (i = 0; i < 5; i++) { + for (j = 0; j < 2; j++) { + br_aes_ct_bitslice_Sbox(q); + shift_rows32(q); + mix_columns32(q); + add_round_key32(q, tweaked256_rc32_sseed[2 * i + j]); + } + + /* Mix states */ + for (j = 0; j < 8; j++) { + tmp_q = q[j]; + q[j] = (tmp_q & 0x81818181) | + (tmp_q & 0x02020202) << 1 | + (tmp_q & 0x04040404) << 2 | + (tmp_q & 0x08080808) << 3 | + (tmp_q & 0x10101010) >> 3 | + (tmp_q & 0x20202020) >> 2 | + (tmp_q & 0x40404040) >> 1; + } + } + + br_aes_ct_ortho(q); + for (i = 0; i < 4; i++) { + br_enc32le(out + 4 * i, q[2 * i]); + br_enc32le(out + 4 * i + 16, q[2 * i + 1]); + } + + for (i = 0; i < 32; i++) { + out[i] ^= in[i]; + } +} diff --git a/crypto_sign/sphincs-haraka-256f-simple/clean/haraka.h b/crypto_sign/sphincs-haraka-256f-simple/clean/haraka.h new file mode 100644 index 00000000..a19a7445 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256f-simple/clean/haraka.h @@ -0,0 +1,30 @@ +#ifndef SPX_HARAKA_H +#define SPX_HARAKA_H + +/* Tweak constants with seed */ +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_tweak_constants( + const unsigned char *pk_seed, const unsigned char *sk_seed, + unsigned long long seed_length); + +/* Haraka Sponge */ +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_haraka_S_inc_init(uint8_t *s_inc); +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_haraka_S_inc_absorb(uint8_t *s_inc, const uint8_t *m, size_t mlen); +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_haraka_S_inc_finalize(uint8_t *s_inc); +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_haraka_S_inc_squeeze(uint8_t *out, size_t outlen, uint8_t *s_inc); +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_haraka_S( + unsigned char *out, unsigned long long outlen, + const unsigned char *in, unsigned long long inlen); + +/* Applies the 512-bit Haraka permutation to in. */ +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_haraka512_perm(unsigned char *out, const unsigned char *in); + +/* Implementation of Haraka-512 */ +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_haraka512(unsigned char *out, const unsigned char *in); + +/* Implementation of Haraka-256 */ +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_haraka256(unsigned char *out, const unsigned char *in); + +/* Implementation of Haraka-256 using sk.seed constants */ +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_haraka256_sk(unsigned char *out, const unsigned char *in); + +#endif diff --git a/crypto_sign/sphincs-haraka-256f-simple/clean/hash.h b/crypto_sign/sphincs-haraka-256f-simple/clean/hash.h new file mode 100644 index 00000000..16135385 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256f-simple/clean/hash.h @@ -0,0 +1,22 @@ +#ifndef SPX_HASH_H +#define SPX_HASH_H + +#include + +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed); + +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen); + +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen); + +#endif diff --git a/crypto_sign/sphincs-haraka-256f-simple/clean/hash_haraka.c b/crypto_sign/sphincs-haraka-256f-simple/clean/hash_haraka.c new file mode 100644 index 00000000..cac765c3 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256f-simple/clean/hash_haraka.c @@ -0,0 +1,86 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "utils.h" + +#include "haraka.h" + +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed) { + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_tweak_constants(pub_seed, sk_seed, SPX_N); +} + +/* + * Computes PRF(key, addr), given a secret key of SPX_N bytes and an address + */ +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]) { + unsigned char buf[SPX_ADDR_BYTES]; + /* Since SPX_N may be smaller than 32, we need a temporary buffer. */ + unsigned char outbuf[32]; + + (void)key; /* Suppress an 'unused parameter' warning. */ + + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_addr_to_bytes(buf, addr); + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_haraka256_sk(outbuf, buf); + memcpy(out, outbuf, SPX_N); +} + +/** + * Computes the message-dependent randomness R, using a secret seed and an + * optional randomization value as well as the message. + */ +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen) { + uint8_t s_inc[65]; + + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_haraka_S_inc_init(s_inc); + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_haraka_S_inc_absorb(s_inc, sk_prf, SPX_N); + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_haraka_S_inc_absorb(s_inc, optrand, SPX_N); + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_haraka_S_inc_absorb(s_inc, m, mlen); + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_haraka_S_inc_finalize(s_inc); + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_haraka_S_inc_squeeze(R, SPX_N, s_inc); +} + +/** + * Computes the message hash using R, the public key, and the message. + * Outputs the message digest and the index of the leaf. The index is split in + * the tree index and the leaf index, for convenient copying to an address. + */ +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen) { +#define SPX_TREE_BITS (SPX_TREE_HEIGHT * (SPX_D - 1)) +#define SPX_TREE_BYTES ((SPX_TREE_BITS + 7) / 8) +#define SPX_LEAF_BITS SPX_TREE_HEIGHT +#define SPX_LEAF_BYTES ((SPX_LEAF_BITS + 7) / 8) +#define SPX_DGST_BYTES (SPX_FORS_MSG_BYTES + SPX_TREE_BYTES + SPX_LEAF_BYTES) + + unsigned char buf[SPX_DGST_BYTES]; + unsigned char *bufp = buf; + uint8_t s_inc[65]; + + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_haraka_S_inc_init(s_inc); + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_haraka_S_inc_absorb(s_inc, R, SPX_N); + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_haraka_S_inc_absorb(s_inc, pk, SPX_PK_BYTES); + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_haraka_S_inc_absorb(s_inc, m, mlen); + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_haraka_S_inc_finalize(s_inc); + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_haraka_S_inc_squeeze(buf, SPX_DGST_BYTES, s_inc); + + memcpy(digest, bufp, SPX_FORS_MSG_BYTES); + bufp += SPX_FORS_MSG_BYTES; + + *tree = PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_bytes_to_ull(bufp, SPX_TREE_BYTES); + *tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS); + bufp += SPX_TREE_BYTES; + + *leaf_idx = (uint32_t)PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_bytes_to_ull( + bufp, SPX_LEAF_BYTES); + *leaf_idx &= (~(uint32_t)0) >> (32 - SPX_LEAF_BITS); +} diff --git a/crypto_sign/sphincs-haraka-256f-simple/clean/params.h b/crypto_sign/sphincs-haraka-256f-simple/clean/params.h new file mode 100644 index 00000000..c2ad0531 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256f-simple/clean/params.h @@ -0,0 +1,53 @@ +#ifndef SPX_PARAMS_H +#define SPX_PARAMS_H + +/* Hash output length in bytes. */ +#define SPX_N 32 +/* Height of the hypertree. */ +#define SPX_FULL_HEIGHT 68 +/* Number of subtree layer. */ +#define SPX_D 17 +/* FORS tree dimensions. */ +#define SPX_FORS_HEIGHT 10 +#define SPX_FORS_TREES 30 +/* Winternitz parameter, */ +#define SPX_WOTS_W 16 + +/* The hash function is defined by linking a different hash.c file, as opposed + to setting a #define constant. */ + +/* For clarity */ +#define SPX_ADDR_BYTES 32 + +/* WOTS parameters. */ +#define SPX_WOTS_LOGW 4 + +#define SPX_WOTS_LEN1 (8 * SPX_N / SPX_WOTS_LOGW) + +/* SPX_WOTS_LEN2 is floor(log(len_1 * (w - 1)) / log(w)) + 1; we precompute */ +#define SPX_WOTS_LEN2 3 + +#define SPX_WOTS_LEN (SPX_WOTS_LEN1 + SPX_WOTS_LEN2) +#define SPX_WOTS_BYTES (SPX_WOTS_LEN * SPX_N) +#define SPX_WOTS_PK_BYTES SPX_WOTS_BYTES + +/* Subtree size. */ +#define SPX_TREE_HEIGHT (SPX_FULL_HEIGHT / SPX_D) + +/* FORS parameters. */ +#define SPX_FORS_MSG_BYTES ((SPX_FORS_HEIGHT * SPX_FORS_TREES + 7) / 8) +#define SPX_FORS_BYTES ((SPX_FORS_HEIGHT + 1) * SPX_FORS_TREES * SPX_N) +#define SPX_FORS_PK_BYTES SPX_N + +/* Resulting SPX sizes. */ +#define SPX_BYTES (SPX_N + SPX_FORS_BYTES + SPX_D * SPX_WOTS_BYTES +\ + SPX_FULL_HEIGHT * SPX_N) +#define SPX_PK_BYTES (2 * SPX_N) +#define SPX_SK_BYTES (2 * SPX_N + SPX_PK_BYTES) + +/* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ +#define SPX_OPTRAND_BYTES 32 + +#endif diff --git a/crypto_sign/sphincs-haraka-256f-simple/clean/sign.c b/crypto_sign/sphincs-haraka-256f-simple/clean/sign.c new file mode 100644 index 00000000..9eb8ba28 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256f-simple/clean/sign.c @@ -0,0 +1,344 @@ +#include +#include +#include + +#include "address.h" +#include "api.h" +#include "fors.h" +#include "hash.h" +#include "params.h" +#include "randombytes.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +/** + * Computes the leaf at a given address. First generates the WOTS key pair, + * then computes leaf by hashing horizontally. + */ +static void wots_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t tree_addr[8]) { + unsigned char pk[SPX_WOTS_BYTES]; + uint32_t wots_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_keypair_addr( + wots_addr, addr_idx); + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_wots_gen_pk( + pk, sk_seed, pub_seed, wots_addr); + + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_thash_WOTS_LEN( + leaf, pk, pub_seed, wots_pk_addr); +} + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_crypto_sign_secretkeybytes(void) { + return PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_CRYPTO_SECRETKEYBYTES; +} + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_crypto_sign_publickeybytes(void) { + return PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES; +} + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_crypto_sign_bytes(void) { + return PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_CRYPTO_BYTES; +} + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_crypto_sign_seedbytes(void) { + return PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_CRYPTO_SEEDBYTES; +} + +/* + * Generates an SPX key pair given a seed of length + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed) { + /* We do not need the auth path in key generation, but it simplifies the + code to have just one treehash routine that computes both root and path + in one function. */ + unsigned char auth_path[SPX_TREE_HEIGHT * SPX_N]; + uint32_t top_tree_addr[8] = {0}; + + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_layer_addr( + top_tree_addr, SPX_D - 1); + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_type( + top_tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */ + memcpy(sk, seed, PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_CRYPTO_SEEDBYTES); + + memcpy(pk, sk + 2 * SPX_N, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_initialize_hash_function(pk, sk); + + /* Compute root node of the top-most subtree. */ + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_treehash_TREE_HEIGHT( + sk + 3 * SPX_N, auth_path, sk, sk + 2 * SPX_N, 0, 0, + wots_gen_leaf, top_tree_addr); + + memcpy(pk + SPX_N, sk + 3 * SPX_N, SPX_N); + + return 0; +} + +/* + * Generates an SPX key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk) { + unsigned char seed[PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_CRYPTO_SEEDBYTES]; + randombytes(seed, PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_CRYPTO_SEEDBYTES); + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_crypto_sign_seed_keypair( + pk, sk, seed); + + return 0; +} + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + const unsigned char *sk_seed = sk; + const unsigned char *sk_prf = sk + SPX_N; + const unsigned char *pk = sk + 2 * SPX_N; + const unsigned char *pub_seed = pk; + + unsigned char optrand[SPX_N]; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char root[SPX_N]; + uint32_t i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_initialize_hash_function( + pub_seed, sk_seed); + + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ + randombytes(optrand, SPX_N); + /* Compute the digest randomization value. */ + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_gen_message_random( + sig, sk_prf, optrand, m, mlen); + + /* Derive the message digest and leaf index from R, PK and M. */ + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Sign the message hash using FORS. */ + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_fors_sign( + sig, root, mhash, sk_seed, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Compute a WOTS signature. */ + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_wots_sign( + sig, root, sk_seed, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the authentication path for the used WOTS leaf. */ + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_treehash_TREE_HEIGHT( + root, sig, sk_seed, pub_seed, idx_leaf, 0, + wots_gen_leaf, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + *siglen = SPX_BYTES; + + return 0; +} + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk) { + const unsigned char *pub_seed = pk; + const unsigned char *pub_root = pk + SPX_N; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char wots_pk[SPX_WOTS_BYTES]; + unsigned char root[SPX_N]; + unsigned char leaf[SPX_N]; + unsigned int i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + if (siglen != SPX_BYTES) { + return -1; + } + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_initialize_hash_function( + pub_seed, NULL); + + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + /* Derive the message digest and leaf index from R || PK || M. */ + /* The additional SPX_N is a result of the hash domain separator. */ + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + /* Layer correctly defaults to 0, so no need to set_layer_addr */ + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_fors_pk_from_sig( + root, sig, mhash, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + /* For each subtree.. */ + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + + /* The WOTS public key is only correct if the signature was correct. */ + /* Initially, root is the FORS pk, but on subsequent iterations it is + the root of the subtree below the currently processed subtree. */ + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_wots_pk_from_sig( + wots_pk, sig, root, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the leaf node using the WOTS public key. */ + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_thash_WOTS_LEN( + leaf, wots_pk, pub_seed, wots_pk_addr); + + /* Compute the root node of this subtree. */ + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_compute_root( + root, leaf, idx_leaf, 0, sig, SPX_TREE_HEIGHT, + pub_seed, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + /* Check if the root node equals the root node in the public key. */ + if (memcmp(root, pub_root, SPX_N) != 0) { + return -1; + } + + return 0; +} + + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + size_t siglen; + + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_crypto_sign_signature( + sm, &siglen, m, mlen, sk); + + memmove(sm + SPX_BYTES, m, mlen); + *smlen = siglen + mlen; + + return 0; +} + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk) { + /* The API caller does not necessarily know what size a signature should be + but SPHINCS+ signatures are always exactly SPX_BYTES. */ + if (smlen < SPX_BYTES) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + *mlen = smlen - SPX_BYTES; + + if (PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_crypto_sign_verify( + sm, SPX_BYTES, sm + SPX_BYTES, *mlen, pk)) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + /* If verification was successful, move the message to the right place. */ + memmove(m, sm + SPX_BYTES, *mlen); + + return 0; +} diff --git a/crypto_sign/sphincs-haraka-256f-simple/clean/thash.h b/crypto_sign/sphincs-haraka-256f-simple/clean/thash.h new file mode 100644 index 00000000..26645c77 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256f-simple/clean/thash.h @@ -0,0 +1,22 @@ +#ifndef SPX_THASH_H +#define SPX_THASH_H + +#include + +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-haraka-256f-simple/clean/thash_haraka_simple.c b/crypto_sign/sphincs-haraka-256f-simple/clean/thash_haraka_simple.c new file mode 100644 index 00000000..c3fe7342 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256f-simple/clean/thash_haraka_simple.c @@ -0,0 +1,78 @@ +#include +#include + +#include "address.h" +#include "params.h" +#include "thash.h" + +#include "haraka.h" + +/** + * Takes an array of inblocks concatenated arrays of SPX_N bytes. + */ +static void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_thash( + unsigned char *out, unsigned char *buf, + const unsigned char *in, unsigned int inblocks, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char outbuf[32]; + unsigned char buf_tmp[64]; + + (void)pub_seed; /* Suppress an 'unused parameter' warning. */ + + if (inblocks == 1) { + /* F function */ + /* Since SPX_N may be smaller than 32, we need a temporary buffer. */ + memset(buf_tmp, 0, 64); + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_addr_to_bytes(buf_tmp, addr); + memcpy(buf_tmp + SPX_ADDR_BYTES, in, SPX_N); + + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_haraka512(outbuf, buf_tmp); + memcpy(out, outbuf, SPX_N); + } else { + /* All other tweakable hashes*/ + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_addr_to_bytes(buf, addr); + memcpy(buf + SPX_ADDR_BYTES, in, inblocks * SPX_N); + + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_haraka_S( + out, SPX_N, buf, SPX_ADDR_BYTES + inblocks * SPX_N); + } +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_ADDR_BYTES + 1 * SPX_N]; + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_thash( + out, buf, in, 1, pub_seed, addr); +} + +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_ADDR_BYTES + 2 * SPX_N]; + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_thash( + out, buf, in, 2, pub_seed, addr); +} + +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_ADDR_BYTES + SPX_WOTS_LEN * SPX_N]; + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_thash( + out, buf, in, SPX_WOTS_LEN, pub_seed, addr); +} + +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_ADDR_BYTES + SPX_FORS_TREES * SPX_N]; + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_thash( + out, buf, in, SPX_FORS_TREES, pub_seed, addr); +} diff --git a/crypto_sign/sphincs-haraka-256f-simple/clean/utils.c b/crypto_sign/sphincs-haraka-256f-simple/clean/utils.c new file mode 100644 index 00000000..51c90dcc --- /dev/null +++ b/crypto_sign/sphincs-haraka-256f-simple/clean/utils.c @@ -0,0 +1,192 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in) { + + /* Iterate over out in decreasing order, for big-endianness. */ + for (size_t i = outlen; i > 0; i--) { + out[i - 1] = in & 0xff; + in = in >> 8; + } +} + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen) { + unsigned long long retval = 0; + + for (size_t i = 0; i < inlen; i++) { + retval |= ((unsigned long long)in[i]) << (8 * (inlen - 1 - i)); + } + return retval; +} + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + unsigned char buffer[2 * SPX_N]; + + /* If leaf_idx is odd (last bit = 1), current path element is a right child + and auth_path has to go left. Otherwise it is the other way around. */ + if (leaf_idx & 1) { + memcpy(buffer + SPX_N, leaf, SPX_N); + memcpy(buffer, auth_path, SPX_N); + } else { + memcpy(buffer, leaf, SPX_N); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + + for (i = 0; i < tree_height - 1; i++) { + leaf_idx >>= 1; + idx_offset >>= 1; + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_tree_height(addr, i + 1); + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + + /* Pick the right or left neighbor, depending on parity of the node. */ + if (leaf_idx & 1) { + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_thash_2( + buffer + SPX_N, buffer, pub_seed, addr); + memcpy(buffer, auth_path, SPX_N); + } else { + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_thash_2( + buffer, buffer, pub_seed, addr); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + } + + /* The last iteration is exceptional; we do not copy an auth_path node. */ + leaf_idx >>= 1; + idx_offset >>= 1; + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_tree_height(addr, tree_height); + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_thash_2( + root, buffer, pub_seed, addr); +} + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +static void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_treehash( + unsigned char *root, unsigned char *auth_path, + unsigned char *stack, unsigned int *heights, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned int offset = 0; + uint32_t idx; + uint32_t tree_idx; + + for (idx = 0; idx < (uint32_t)(1 << tree_height); idx++) { + /* Add the next leaf node to the stack. */ + gen_leaf(stack + offset * SPX_N, + sk_seed, pub_seed, idx + idx_offset, tree_addr); + offset++; + heights[offset - 1] = 0; + + /* If this is a node we need for the auth path.. */ + if ((leaf_idx ^ 0x1) == idx) { + memcpy(auth_path, stack + (offset - 1)*SPX_N, SPX_N); + } + + /* While the top-most nodes are of equal height.. */ + while (offset >= 2 && heights[offset - 1] == heights[offset - 2]) { + /* Compute index of the new node, in the next layer. */ + tree_idx = (idx >> (heights[offset - 1] + 1)); + + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_tree_height( + tree_addr, heights[offset - 1] + 1); + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_tree_index( + tree_addr, tree_idx + (idx_offset >> (heights[offset - 1] + 1))); + /* Hash the top-most nodes from the stack together. */ + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_thash_2( + stack + (offset - 2)*SPX_N, stack + (offset - 2)*SPX_N, + pub_seed, tree_addr); + offset--; + /* Note that the top-most node is now one layer higher. */ + heights[offset - 1]++; + + /* If this is a node we need for the auth path.. */ + if (((leaf_idx >> heights[offset - 1]) ^ 0x1) == tree_idx) { + memcpy(auth_path + heights[offset - 1]*SPX_N, + stack + (offset - 1)*SPX_N, SPX_N); + } + } + } + memcpy(root, stack, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_FORS_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_FORS_HEIGHT + 1]; + + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_FORS_HEIGHT, gen_leaf, tree_addr); +} + +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_TREE_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_TREE_HEIGHT + 1]; + + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_TREE_HEIGHT, gen_leaf, tree_addr); +} diff --git a/crypto_sign/sphincs-haraka-256f-simple/clean/utils.h b/crypto_sign/sphincs-haraka-256f-simple/clean/utils.h new file mode 100644 index 00000000..8b028569 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256f-simple/clean/utils.h @@ -0,0 +1,60 @@ +#ifndef SPX_UTILS_H +#define SPX_UTILS_H + +#include "params.h" +#include +#include + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in); + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen); + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-haraka-256f-simple/clean/wots.c b/crypto_sign/sphincs-haraka-256f-simple/clean/wots.c new file mode 100644 index 00000000..00d6654e --- /dev/null +++ b/crypto_sign/sphincs-haraka-256f-simple/clean/wots.c @@ -0,0 +1,161 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +// TODO clarify address expectations, and make them more uniform. +// TODO i.e. do we expect types to be set already? +// TODO and do we expect modifications or copies? + +/** + * Computes the starting value for a chain, i.e. the secret key. + * Expects the address to be complete up to the chain address. + */ +static void wots_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t wots_addr[8]) { + /* Make sure that the hash address is actually zeroed. */ + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_hash_addr(wots_addr, 0); + + /* Generate sk element. */ + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_prf_addr(sk, sk_seed, wots_addr); +} + +/** + * Computes the chaining function. + * out and in have to be n-byte arrays. + * + * Interprets in as start-th value of the chain. + * addr has to contain the address of the chain. + */ +static void gen_chain(unsigned char *out, const unsigned char *in, + unsigned int start, unsigned int steps, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + /* Initialize out with the value at position 'start'. */ + memcpy(out, in, SPX_N); + + /* Iterate 'steps' calls to the hash function. */ + for (i = start; i < (start + steps) && i < SPX_WOTS_W; i++) { + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_hash_addr(addr, i); + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_thash_1( + out, out, pub_seed, addr); + } +} + +/** + * base_w algorithm as described in draft. + * Interprets an array of bytes as integers in base w. + * This only works when log_w is a divisor of 8. + */ +static void base_w(unsigned int *output, const size_t out_len, + const unsigned char *input) { + size_t in = 0; + size_t out = 0; + unsigned char total = 0; + unsigned int bits = 0; + size_t consumed; + + for (consumed = 0; consumed < out_len; consumed++) { + if (bits == 0) { + total = input[in]; + in++; + bits += 8; + } + bits -= SPX_WOTS_LOGW; + output[out] = (unsigned int)((total >> bits) & (SPX_WOTS_W - 1)); + out++; + } +} + +/* Computes the WOTS+ checksum over a message (in base_w). */ +static void wots_checksum(unsigned int *csum_base_w, + const unsigned int *msg_base_w) { + unsigned int csum = 0; + unsigned char csum_bytes[(SPX_WOTS_LEN2 * SPX_WOTS_LOGW + 7) / 8]; + unsigned int i; + + /* Compute checksum. */ + for (i = 0; i < SPX_WOTS_LEN1; i++) { + csum += SPX_WOTS_W - 1 - msg_base_w[i]; + } + + /* Convert checksum to base_w. */ + /* Make sure expected empty zero bits are the least significant bits. */ + csum = csum << (8 - ((SPX_WOTS_LEN2 * SPX_WOTS_LOGW) % 8)); + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_ull_to_bytes( + csum_bytes, sizeof(csum_bytes), csum); + base_w(csum_base_w, SPX_WOTS_LEN2, csum_bytes); +} + +/* Takes a message and derives the matching chain lengths. */ +static void chain_lengths(unsigned int *lengths, const unsigned char *msg) { + base_w(lengths, SPX_WOTS_LEN1, msg); + wots_checksum(lengths + SPX_WOTS_LEN1, lengths); +} + +/** + * WOTS key generation. Takes a 32 byte sk_seed, expands it to WOTS private key + * elements and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(pk + i * SPX_N, sk_seed, addr); + gen_chain(pk + i * SPX_N, pk + i * SPX_N, + 0, SPX_WOTS_W - 1, pub_seed, addr); + } +} + +/** + * Takes a n-byte message and the 32-byte sk_see to compute a signature 'sig'. + */ +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(sig + i * SPX_N, sk_seed, addr); + gen_chain(sig + i * SPX_N, sig + i * SPX_N, 0, lengths[i], pub_seed, addr); + } +} + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_set_chain_addr(addr, i); + gen_chain(pk + i * SPX_N, sig + i * SPX_N, + lengths[i], SPX_WOTS_W - 1 - lengths[i], pub_seed, addr); + } +} diff --git a/crypto_sign/sphincs-haraka-256f-simple/clean/wots.h b/crypto_sign/sphincs-haraka-256f-simple/clean/wots.h new file mode 100644 index 00000000..4ccf585b --- /dev/null +++ b/crypto_sign/sphincs-haraka-256f-simple/clean/wots.h @@ -0,0 +1,38 @@ +#ifndef SPX_WOTS_H +#define SPX_WOTS_H + +#include "params.h" +#include + +/** + * WOTS key generation. Takes a 32 byte seed for the private key, expands it to + * a full WOTS private key and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * Takes a n-byte message and the 32-byte seed for the private key to compute a + * signature that is placed at 'sig'. + */ +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]); + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-haraka-256s-robust/META.yml b/crypto_sign/sphincs-haraka-256s-robust/META.yml new file mode 100644 index 00000000..5bb978d9 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256s-robust/META.yml @@ -0,0 +1,27 @@ +name: SPHINCS+ +type: signature +claimed-nist-level: 5 +length-public-key: 64 +length-secret-key: 128 +length-signature: 29792 +testvectors-sha256: feb4f482dd5ab66dd09f2e5e02175e7109de4385da5704f78cc1dac074368c56 +principal-submitter: Andreas Hülsing +auxiliary-submitters: + - Jean-Philippe Aumasson + - Daniel J. Bernstein, + - Christoph Dobraunig + - Maria Eichlseder + - Scott Fluhrer + - Stefan-Lukas Gazdag + - Panos Kampanakis + - Stefan Kölbl + - Tanja Lange + - Martin M. Lauridsen + - Florian Mendel + - Ruben Niederhagen + - Christian Rechberger + - Joost Rijneveld + - Peter Schwabe +implementations: + - name: clean + version: https://github.com/sphincs/sphincsplus/commit/77755c94d0bc744478044d6efbb888dc13156441 diff --git a/crypto_sign/sphincs-haraka-256s-robust/clean/LICENSE b/crypto_sign/sphincs-haraka-256s-robust/clean/LICENSE new file mode 100644 index 00000000..670154e3 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256s-robust/clean/LICENSE @@ -0,0 +1,116 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + diff --git a/crypto_sign/sphincs-haraka-256s-robust/clean/Makefile b/crypto_sign/sphincs-haraka-256s-robust/clean/Makefile new file mode 100644 index 00000000..c97db733 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256s-robust/clean/Makefile @@ -0,0 +1,20 @@ +# This Makefile can be used with GNU Make or BSD Make + +LIB=libsphincs-haraka-256s-robust_clean.a + +HEADERS = params.h address.h wots.h utils.h fors.h api.h hash.h thash.h haraka.h +OBJECTS = address.o wots.o utils.o fors.o sign.o hash_haraka.o thash_haraka_robust.o haraka.o + +CFLAGS=-O3 -Wall -Wconversion -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) + +all: $(LIB) + +%.o: %.c $(HEADERS) + $(CC) $(CFLAGS) -c -o $@ $< + +$(LIB): $(OBJECTS) + $(AR) -r $@ $(OBJECTS) + +clean: + $(RM) $(OBJECTS) + $(RM) $(LIB) diff --git a/crypto_sign/sphincs-haraka-256s-robust/clean/Makefile.Microsoft_nmake b/crypto_sign/sphincs-haraka-256s-robust/clean/Makefile.Microsoft_nmake new file mode 100644 index 00000000..a2c65e59 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256s-robust/clean/Makefile.Microsoft_nmake @@ -0,0 +1,19 @@ +# This Makefile can be used with Microsoft Visual Studio's nmake using the command: +# nmake /f Makefile.Microsoft_nmake + +LIBRARY=libsphincs-haraka-256s-robust_clean.lib +OBJECTS=address.obj wots.obj utils.obj fors.obj sign.obj hash_haraka.obj thash_haraka_robust.obj haraka.obj + +CFLAGS=/nologo /I ..\..\..\common /W4 /WX + +all: $(LIBRARY) + +# Make sure objects are recompiled if headers change. +$(OBJECTS): *.h + +$(LIBRARY): $(OBJECTS) + LIB.EXE /NOLOGO /WX /OUT:$@ $** + +clean: + -DEL $(OBJECTS) + -DEL $(LIBRARY) diff --git a/crypto_sign/sphincs-haraka-256s-robust/clean/address.c b/crypto_sign/sphincs-haraka-256s-robust/clean/address.c new file mode 100644 index 00000000..fdb232ef --- /dev/null +++ b/crypto_sign/sphincs-haraka-256s-robust/clean/address.c @@ -0,0 +1,78 @@ +#include + +#include "address.h" +#include "params.h" +#include "utils.h" + +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]) { + int i; + + for (i = 0; i < 8; i++) { + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_ull_to_bytes( + bytes + i * 4, 4, addr[i]); + } +} + +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer) { + addr[0] = layer; +} + +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree) { + addr[1] = 0; + addr[2] = (uint32_t) (tree >> 32); + addr[3] = (uint32_t) tree; +} + +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_type( + uint32_t addr[8], uint32_t type) { + addr[4] = type; +} + +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; +} + +/* These functions are used for OTS addresses. */ + +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair) { + addr[5] = keypair; +} + +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; + out[5] = in[5]; +} + +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain) { + addr[6] = chain; +} + +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash) { + addr[7] = hash; +} + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height) { + addr[6] = tree_height; +} + +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index) { + addr[7] = tree_index; +} diff --git a/crypto_sign/sphincs-haraka-256s-robust/clean/address.h b/crypto_sign/sphincs-haraka-256s-robust/clean/address.h new file mode 100644 index 00000000..988b9a6e --- /dev/null +++ b/crypto_sign/sphincs-haraka-256s-robust/clean/address.h @@ -0,0 +1,50 @@ +#ifndef SPX_ADDRESS_H +#define SPX_ADDRESS_H + +#include + +#define SPX_ADDR_TYPE_WOTS 0 +#define SPX_ADDR_TYPE_WOTSPK 1 +#define SPX_ADDR_TYPE_HASHTREE 2 +#define SPX_ADDR_TYPE_FORSTREE 3 +#define SPX_ADDR_TYPE_FORSPK 4 + +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer); + +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree); + +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_type( + uint32_t addr[8], uint32_t type); + +/* Copies the layer and tree part of one address into the other */ +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for WOTS and FORS addresses. */ + +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair); + +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain); + +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash); + +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height); + +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index); + +#endif diff --git a/crypto_sign/sphincs-haraka-256s-robust/clean/api.h b/crypto_sign/sphincs-haraka-256s-robust/clean/api.h new file mode 100644 index 00000000..32c836d5 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256s-robust/clean/api.h @@ -0,0 +1,78 @@ +#ifndef PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_API_H +#define PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_API_H + +#include +#include + +#define PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_CRYPTO_ALGNAME "SPHINCS+" + +#define PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_CRYPTO_SECRETKEYBYTES 128 +#define PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES 64 +#define PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_CRYPTO_BYTES 29792 +#define PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_CRYPTO_SEEDBYTES 96 + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_crypto_sign_secretkeybytes(void); + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_crypto_sign_publickeybytes(void); + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_crypto_sign_bytes(void); + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_crypto_sign_seedbytes(void); + +/* + * Generates a SPHINCS+ key pair given a seed. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed); + +/* + * Generates a SPHINCS+ key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk); + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk); + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk); + +#endif diff --git a/crypto_sign/sphincs-haraka-256s-robust/clean/fors.c b/crypto_sign/sphincs-haraka-256s-robust/clean/fors.c new file mode 100644 index 00000000..f00476f8 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256s-robust/clean/fors.c @@ -0,0 +1,164 @@ +#include +#include +#include + +#include "address.h" +#include "fors.h" +#include "hash.h" +#include "thash.h" +#include "utils.h" + +static void fors_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_prf_addr( + sk, sk_seed, fors_leaf_addr); +} + +static void fors_sk_to_leaf(unsigned char *leaf, const unsigned char *sk, + const unsigned char *pub_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_thash_1( + leaf, sk, pub_seed, fors_leaf_addr); +} + +static void fors_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t fors_tree_addr[8]) { + uint32_t fors_leaf_addr[8] = {0}; + + /* Only copy the parts that must be kept in fors_leaf_addr. */ + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_copy_keypair_addr( + fors_leaf_addr, fors_tree_addr); + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_type( + fors_leaf_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_tree_index( + fors_leaf_addr, addr_idx); + + fors_gen_sk(leaf, sk_seed, fors_leaf_addr); + fors_sk_to_leaf(leaf, leaf, pub_seed, fors_leaf_addr); +} + +/** + * Interprets m as SPX_FORS_HEIGHT-bit unsigned integers. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + * Assumes indices has space for SPX_FORS_TREES integers. + */ +static void message_to_indices(uint32_t *indices, const unsigned char *m) { + unsigned int i, j; + unsigned int offset = 0; + + for (i = 0; i < SPX_FORS_TREES; i++) { + indices[i] = 0; + for (j = 0; j < SPX_FORS_HEIGHT; j++) { + indices[i] ^= (((uint32_t)m[offset >> 3] >> (offset & 0x7)) & 0x1) << j; + offset++; + } + } +} + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Include the secret key part that produces the selected leaf node. */ + fors_gen_sk(sig, sk_seed, fors_tree_addr); + sig += SPX_N; + + /* Compute the authentication path for this leaf node. */ + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_treehash_FORS_HEIGHT( + roots + i * SPX_N, sig, sk_seed, pub_seed, + indices[i], idx_offset, fors_gen_leaf, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + unsigned char leaf[SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Derive the leaf from the included secret key part. */ + fors_sk_to_leaf(leaf, sig, pub_seed, fors_tree_addr); + sig += SPX_N; + + /* Derive the corresponding root node of this tree. */ + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_compute_root( + roots + i * SPX_N, leaf, indices[i], idx_offset, sig, + SPX_FORS_HEIGHT, pub_seed, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} diff --git a/crypto_sign/sphincs-haraka-256s-robust/clean/fors.h b/crypto_sign/sphincs-haraka-256s-robust/clean/fors.h new file mode 100644 index 00000000..6729899d --- /dev/null +++ b/crypto_sign/sphincs-haraka-256s-robust/clean/fors.h @@ -0,0 +1,30 @@ +#ifndef SPX_FORS_H +#define SPX_FORS_H + +#include + +#include "params.h" + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]); + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-haraka-256s-robust/clean/haraka.c b/crypto_sign/sphincs-haraka-256s-robust/clean/haraka.c new file mode 100644 index 00000000..9eddddd3 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256s-robust/clean/haraka.c @@ -0,0 +1,965 @@ +/* + * Constant time implementation of the Haraka hash function. + * + * The bit-sliced implementation of the AES round functions are + * based on the AES implementation in BearSSL written + * by Thomas Pornin + */ + +#include +#include +#include + +#include "haraka.h" + +#define HARAKAS_RATE 32 + +static const uint64_t haraka512_rc64[10][8] = { + {0x24cf0ab9086f628b, 0xbdd6eeecc83b8382, 0xd96fb0306cdad0a7, 0xaace082ac8f95f89, 0x449d8e8870d7041f, 0x49bb2f80b2b3e2f8, 0x0569ae98d93bb258, 0x23dc9691e7d6a4b1}, + {0xd8ba10ede0fe5b6e, 0x7ecf7dbe424c7b8e, 0x6ea9949c6df62a31, 0xbf3f3c97ec9c313e, 0x241d03a196a1861e, 0xead3a51116e5a2ea, 0x77d479fcad9574e3, 0x18657a1af894b7a0}, + {0x10671e1a7f595522, 0xd9a00ff675d28c7b, 0x2f1edf0d2b9ba661, 0xb8ff58b8e3de45f9, 0xee29261da9865c02, 0xd1532aa4b50bdf43, 0x8bf858159b231bb1, 0xdf17439d22d4f599}, + {0xdd4b2f0870b918c0, 0x757a81f3b39b1bb6, 0x7a5c556898952e3f, 0x7dd70a16d915d87a, 0x3ae61971982b8301, 0xc3ab319e030412be, 0x17c0033ac094a8cb, 0x5a0630fc1a8dc4ef}, + {0x17708988c1632f73, 0xf92ddae090b44f4f, 0x11ac0285c43aa314, 0x509059941936b8ba, 0xd03e152fa2ce9b69, 0x3fbcbcb63a32998b, 0x6204696d692254f7, 0x915542ed93ec59b4}, + {0xf4ed94aa8879236e, 0xff6cb41cd38e03c0, 0x069b38602368aeab, 0x669495b820f0ddba, 0xf42013b1b8bf9e3d, 0xcf935efe6439734d, 0xbc1dcf42ca29e3f8, 0x7e6d3ed29f78ad67}, + {0xf3b0f6837ffcddaa, 0x3a76faef934ddf41, 0xcec7ae583a9c8e35, 0xe4dd18c68f0260af, 0x2c0e5df1ad398eaa, 0x478df5236ae22e8c, 0xfb944c46fe865f39, 0xaa48f82f028132ba}, + {0x231b9ae2b76aca77, 0x292a76a712db0b40, 0x5850625dc8134491, 0x73137dd469810fb5, 0x8a12a6a202a474fd, 0xd36fd9daa78bdb80, 0xb34c5e733505706f, 0xbaf1cdca818d9d96}, + {0x2e99781335e8c641, 0xbddfe5cce47d560e, 0xf74e9bf32e5e040c, 0x1d7a709d65996be9, 0x670df36a9cf66cdd, 0xd05ef84a176a2875, 0x0f888e828cb1c44e, 0x1a79e9c9727b052c}, + {0x83497348628d84de, 0x2e9387d51f22a754, 0xb000068da2f852d6, 0x378c9e1190fd6fe5, 0x870027c316de7293, 0xe51a9d4462e047bb, 0x90ecf7f8c6251195, 0x655953bfbed90a9c}, +}; + +static uint64_t tweaked512_rc64[10][8]; +static uint32_t tweaked256_rc32[10][8]; +static uint32_t tweaked256_rc32_sseed[10][8]; + +static inline uint32_t br_dec32le(const unsigned char *src) { + return (uint32_t)src[0] + | ((uint32_t)src[1] << 8) + | ((uint32_t)src[2] << 16) + | ((uint32_t)src[3] << 24); +} + +static void br_range_dec32le(uint32_t *v, size_t num, const unsigned char *src) { + while (num-- > 0) { + *v ++ = br_dec32le(src); + src += 4; + } +} + +static inline void br_enc32le(unsigned char *dst, uint32_t x) { + dst[0] = (unsigned char)x; + dst[1] = (unsigned char)(x >> 8); + dst[2] = (unsigned char)(x >> 16); + dst[3] = (unsigned char)(x >> 24); +} + + +static void br_range_enc32le(unsigned char *dst, const uint32_t *v, size_t num) { + while (num-- > 0) { + br_enc32le(dst, *v ++); + dst += 4; + } +} + +static void br_aes_ct64_bitslice_Sbox(uint64_t *q) { + /* + * This S-box implementation is a straightforward translation of + * the circuit described by Boyar and Peralta in "A new + * combinational logic minimization technique with applications + * to cryptology" (https://eprint.iacr.org/2009/191.pdf). + * + * Note that variables x* (input) and s* (output) are numbered + * in "reverse" order (x0 is the high bit, x7 is the low bit). + */ + + uint64_t x0, x1, x2, x3, x4, x5, x6, x7; + uint64_t y1, y2, y3, y4, y5, y6, y7, y8, y9; + uint64_t y10, y11, y12, y13, y14, y15, y16, y17, y18, y19; + uint64_t y20, y21; + uint64_t z0, z1, z2, z3, z4, z5, z6, z7, z8, z9; + uint64_t z10, z11, z12, z13, z14, z15, z16, z17; + uint64_t t0, t1, t2, t3, t4, t5, t6, t7, t8, t9; + uint64_t t10, t11, t12, t13, t14, t15, t16, t17, t18, t19; + uint64_t t20, t21, t22, t23, t24, t25, t26, t27, t28, t29; + uint64_t t30, t31, t32, t33, t34, t35, t36, t37, t38, t39; + uint64_t t40, t41, t42, t43, t44, t45, t46, t47, t48, t49; + uint64_t t50, t51, t52, t53, t54, t55, t56, t57, t58, t59; + uint64_t t60, t61, t62, t63, t64, t65, t66, t67; + uint64_t s0, s1, s2, s3, s4, s5, s6, s7; + + x0 = q[7]; + x1 = q[6]; + x2 = q[5]; + x3 = q[4]; + x4 = q[3]; + x5 = q[2]; + x6 = q[1]; + x7 = q[0]; + + /* + * Top linear transformation. + */ + y14 = x3 ^ x5; + y13 = x0 ^ x6; + y9 = x0 ^ x3; + y8 = x0 ^ x5; + t0 = x1 ^ x2; + y1 = t0 ^ x7; + y4 = y1 ^ x3; + y12 = y13 ^ y14; + y2 = y1 ^ x0; + y5 = y1 ^ x6; + y3 = y5 ^ y8; + t1 = x4 ^ y12; + y15 = t1 ^ x5; + y20 = t1 ^ x1; + y6 = y15 ^ x7; + y10 = y15 ^ t0; + y11 = y20 ^ y9; + y7 = x7 ^ y11; + y17 = y10 ^ y11; + y19 = y10 ^ y8; + y16 = t0 ^ y11; + y21 = y13 ^ y16; + y18 = x0 ^ y16; + + /* + * Non-linear section. + */ + t2 = y12 & y15; + t3 = y3 & y6; + t4 = t3 ^ t2; + t5 = y4 & x7; + t6 = t5 ^ t2; + t7 = y13 & y16; + t8 = y5 & y1; + t9 = t8 ^ t7; + t10 = y2 & y7; + t11 = t10 ^ t7; + t12 = y9 & y11; + t13 = y14 & y17; + t14 = t13 ^ t12; + t15 = y8 & y10; + t16 = t15 ^ t12; + t17 = t4 ^ t14; + t18 = t6 ^ t16; + t19 = t9 ^ t14; + t20 = t11 ^ t16; + t21 = t17 ^ y20; + t22 = t18 ^ y19; + t23 = t19 ^ y21; + t24 = t20 ^ y18; + + t25 = t21 ^ t22; + t26 = t21 & t23; + t27 = t24 ^ t26; + t28 = t25 & t27; + t29 = t28 ^ t22; + t30 = t23 ^ t24; + t31 = t22 ^ t26; + t32 = t31 & t30; + t33 = t32 ^ t24; + t34 = t23 ^ t33; + t35 = t27 ^ t33; + t36 = t24 & t35; + t37 = t36 ^ t34; + t38 = t27 ^ t36; + t39 = t29 & t38; + t40 = t25 ^ t39; + + t41 = t40 ^ t37; + t42 = t29 ^ t33; + t43 = t29 ^ t40; + t44 = t33 ^ t37; + t45 = t42 ^ t41; + z0 = t44 & y15; + z1 = t37 & y6; + z2 = t33 & x7; + z3 = t43 & y16; + z4 = t40 & y1; + z5 = t29 & y7; + z6 = t42 & y11; + z7 = t45 & y17; + z8 = t41 & y10; + z9 = t44 & y12; + z10 = t37 & y3; + z11 = t33 & y4; + z12 = t43 & y13; + z13 = t40 & y5; + z14 = t29 & y2; + z15 = t42 & y9; + z16 = t45 & y14; + z17 = t41 & y8; + + /* + * Bottom linear transformation. + */ + t46 = z15 ^ z16; + t47 = z10 ^ z11; + t48 = z5 ^ z13; + t49 = z9 ^ z10; + t50 = z2 ^ z12; + t51 = z2 ^ z5; + t52 = z7 ^ z8; + t53 = z0 ^ z3; + t54 = z6 ^ z7; + t55 = z16 ^ z17; + t56 = z12 ^ t48; + t57 = t50 ^ t53; + t58 = z4 ^ t46; + t59 = z3 ^ t54; + t60 = t46 ^ t57; + t61 = z14 ^ t57; + t62 = t52 ^ t58; + t63 = t49 ^ t58; + t64 = z4 ^ t59; + t65 = t61 ^ t62; + t66 = z1 ^ t63; + s0 = t59 ^ t63; + s6 = t56 ^ ~t62; + s7 = t48 ^ ~t60; + t67 = t64 ^ t65; + s3 = t53 ^ t66; + s4 = t51 ^ t66; + s5 = t47 ^ t65; + s1 = t64 ^ ~s3; + s2 = t55 ^ ~t67; + + q[7] = s0; + q[6] = s1; + q[5] = s2; + q[4] = s3; + q[3] = s4; + q[2] = s5; + q[1] = s6; + q[0] = s7; +} + +static void br_aes_ct_bitslice_Sbox(uint32_t *q) { + /* + * This S-box implementation is a straightforward translation of + * the circuit described by Boyar and Peralta in "A new + * combinational logic minimization technique with applications + * to cryptology" (https://eprint.iacr.org/2009/191.pdf). + * + * Note that variables x* (input) and s* (output) are numbered + * in "reverse" order (x0 is the high bit, x7 is the low bit). + */ + + uint32_t x0, x1, x2, x3, x4, x5, x6, x7; + uint32_t y1, y2, y3, y4, y5, y6, y7, y8, y9; + uint32_t y10, y11, y12, y13, y14, y15, y16, y17, y18, y19; + uint32_t y20, y21; + uint32_t z0, z1, z2, z3, z4, z5, z6, z7, z8, z9; + uint32_t z10, z11, z12, z13, z14, z15, z16, z17; + uint32_t t0, t1, t2, t3, t4, t5, t6, t7, t8, t9; + uint32_t t10, t11, t12, t13, t14, t15, t16, t17, t18, t19; + uint32_t t20, t21, t22, t23, t24, t25, t26, t27, t28, t29; + uint32_t t30, t31, t32, t33, t34, t35, t36, t37, t38, t39; + uint32_t t40, t41, t42, t43, t44, t45, t46, t47, t48, t49; + uint32_t t50, t51, t52, t53, t54, t55, t56, t57, t58, t59; + uint32_t t60, t61, t62, t63, t64, t65, t66, t67; + uint32_t s0, s1, s2, s3, s4, s5, s6, s7; + + x0 = q[7]; + x1 = q[6]; + x2 = q[5]; + x3 = q[4]; + x4 = q[3]; + x5 = q[2]; + x6 = q[1]; + x7 = q[0]; + + /* + * Top linear transformation. + */ + y14 = x3 ^ x5; + y13 = x0 ^ x6; + y9 = x0 ^ x3; + y8 = x0 ^ x5; + t0 = x1 ^ x2; + y1 = t0 ^ x7; + y4 = y1 ^ x3; + y12 = y13 ^ y14; + y2 = y1 ^ x0; + y5 = y1 ^ x6; + y3 = y5 ^ y8; + t1 = x4 ^ y12; + y15 = t1 ^ x5; + y20 = t1 ^ x1; + y6 = y15 ^ x7; + y10 = y15 ^ t0; + y11 = y20 ^ y9; + y7 = x7 ^ y11; + y17 = y10 ^ y11; + y19 = y10 ^ y8; + y16 = t0 ^ y11; + y21 = y13 ^ y16; + y18 = x0 ^ y16; + + /* + * Non-linear section. + */ + t2 = y12 & y15; + t3 = y3 & y6; + t4 = t3 ^ t2; + t5 = y4 & x7; + t6 = t5 ^ t2; + t7 = y13 & y16; + t8 = y5 & y1; + t9 = t8 ^ t7; + t10 = y2 & y7; + t11 = t10 ^ t7; + t12 = y9 & y11; + t13 = y14 & y17; + t14 = t13 ^ t12; + t15 = y8 & y10; + t16 = t15 ^ t12; + t17 = t4 ^ t14; + t18 = t6 ^ t16; + t19 = t9 ^ t14; + t20 = t11 ^ t16; + t21 = t17 ^ y20; + t22 = t18 ^ y19; + t23 = t19 ^ y21; + t24 = t20 ^ y18; + + t25 = t21 ^ t22; + t26 = t21 & t23; + t27 = t24 ^ t26; + t28 = t25 & t27; + t29 = t28 ^ t22; + t30 = t23 ^ t24; + t31 = t22 ^ t26; + t32 = t31 & t30; + t33 = t32 ^ t24; + t34 = t23 ^ t33; + t35 = t27 ^ t33; + t36 = t24 & t35; + t37 = t36 ^ t34; + t38 = t27 ^ t36; + t39 = t29 & t38; + t40 = t25 ^ t39; + + t41 = t40 ^ t37; + t42 = t29 ^ t33; + t43 = t29 ^ t40; + t44 = t33 ^ t37; + t45 = t42 ^ t41; + z0 = t44 & y15; + z1 = t37 & y6; + z2 = t33 & x7; + z3 = t43 & y16; + z4 = t40 & y1; + z5 = t29 & y7; + z6 = t42 & y11; + z7 = t45 & y17; + z8 = t41 & y10; + z9 = t44 & y12; + z10 = t37 & y3; + z11 = t33 & y4; + z12 = t43 & y13; + z13 = t40 & y5; + z14 = t29 & y2; + z15 = t42 & y9; + z16 = t45 & y14; + z17 = t41 & y8; + + /* + * Bottom linear transformation. + */ + t46 = z15 ^ z16; + t47 = z10 ^ z11; + t48 = z5 ^ z13; + t49 = z9 ^ z10; + t50 = z2 ^ z12; + t51 = z2 ^ z5; + t52 = z7 ^ z8; + t53 = z0 ^ z3; + t54 = z6 ^ z7; + t55 = z16 ^ z17; + t56 = z12 ^ t48; + t57 = t50 ^ t53; + t58 = z4 ^ t46; + t59 = z3 ^ t54; + t60 = t46 ^ t57; + t61 = z14 ^ t57; + t62 = t52 ^ t58; + t63 = t49 ^ t58; + t64 = z4 ^ t59; + t65 = t61 ^ t62; + t66 = z1 ^ t63; + s0 = t59 ^ t63; + s6 = t56 ^ ~t62; + s7 = t48 ^ ~t60; + t67 = t64 ^ t65; + s3 = t53 ^ t66; + s4 = t51 ^ t66; + s5 = t47 ^ t65; + s1 = t64 ^ ~s3; + s2 = t55 ^ ~t67; + + q[7] = s0; + q[6] = s1; + q[5] = s2; + q[4] = s3; + q[3] = s4; + q[2] = s5; + q[1] = s6; + q[0] = s7; +} + +static void br_aes_ct_ortho(uint32_t *q) { +#define SWAPN_32(cl, ch, s, x, y) do { \ + uint32_t a, b; \ + a = (x); \ + b = (y); \ + (x) = (a & (uint32_t)(cl)) | ((b & (uint32_t)(cl)) << (s)); \ + (y) = ((a & (uint32_t)(ch)) >> (s)) | (b & (uint32_t)(ch)); \ + } while (0) + +#define SWAP2_32(x, y) SWAPN_32(0x55555555, 0xAAAAAAAA, 1, x, y) +#define SWAP4_32(x, y) SWAPN_32(0x33333333, 0xCCCCCCCC, 2, x, y) +#define SWAP8_32(x, y) SWAPN_32(0x0F0F0F0F, 0xF0F0F0F0, 4, x, y) + + SWAP2_32(q[0], q[1]); + SWAP2_32(q[2], q[3]); + SWAP2_32(q[4], q[5]); + SWAP2_32(q[6], q[7]); + + SWAP4_32(q[0], q[2]); + SWAP4_32(q[1], q[3]); + SWAP4_32(q[4], q[6]); + SWAP4_32(q[5], q[7]); + + SWAP8_32(q[0], q[4]); + SWAP8_32(q[1], q[5]); + SWAP8_32(q[2], q[6]); + SWAP8_32(q[3], q[7]); +} + +static inline void add_round_key32(uint32_t *q, const uint32_t *sk) { + q[0] ^= sk[0]; + q[1] ^= sk[1]; + q[2] ^= sk[2]; + q[3] ^= sk[3]; + q[4] ^= sk[4]; + q[5] ^= sk[5]; + q[6] ^= sk[6]; + q[7] ^= sk[7]; +} + +static inline void shift_rows32(uint32_t *q) { + int i; + + for (i = 0; i < 8; i++) { + uint32_t x; + + x = q[i]; + q[i] = (x & 0x000000FF) + | ((x & 0x0000FC00) >> 2) | ((x & 0x00000300) << 6) + | ((x & 0x00F00000) >> 4) | ((x & 0x000F0000) << 4) + | ((x & 0xC0000000) >> 6) | ((x & 0x3F000000) << 2); + } +} + +static inline uint32_t rotr16(uint32_t x) { + return (x << 16) | (x >> 16); +} + +static inline void mix_columns32(uint32_t *q) { + uint32_t q0, q1, q2, q3, q4, q5, q6, q7; + uint32_t r0, r1, r2, r3, r4, r5, r6, r7; + + q0 = q[0]; + q1 = q[1]; + q2 = q[2]; + q3 = q[3]; + q4 = q[4]; + q5 = q[5]; + q6 = q[6]; + q7 = q[7]; + r0 = (q0 >> 8) | (q0 << 24); + r1 = (q1 >> 8) | (q1 << 24); + r2 = (q2 >> 8) | (q2 << 24); + r3 = (q3 >> 8) | (q3 << 24); + r4 = (q4 >> 8) | (q4 << 24); + r5 = (q5 >> 8) | (q5 << 24); + r6 = (q6 >> 8) | (q6 << 24); + r7 = (q7 >> 8) | (q7 << 24); + + q[0] = q7 ^ r7 ^ r0 ^ rotr16(q0 ^ r0); + q[1] = q0 ^ r0 ^ q7 ^ r7 ^ r1 ^ rotr16(q1 ^ r1); + q[2] = q1 ^ r1 ^ r2 ^ rotr16(q2 ^ r2); + q[3] = q2 ^ r2 ^ q7 ^ r7 ^ r3 ^ rotr16(q3 ^ r3); + q[4] = q3 ^ r3 ^ q7 ^ r7 ^ r4 ^ rotr16(q4 ^ r4); + q[5] = q4 ^ r4 ^ r5 ^ rotr16(q5 ^ r5); + q[6] = q5 ^ r5 ^ r6 ^ rotr16(q6 ^ r6); + q[7] = q6 ^ r6 ^ r7 ^ rotr16(q7 ^ r7); +} + +static void br_aes_ct64_ortho(uint64_t *q) { +#define SWAPN(cl, ch, s, x, y) do { \ + uint64_t a, b; \ + a = (x); \ + b = (y); \ + (x) = (a & (uint64_t)(cl)) | ((b & (uint64_t)(cl)) << (s)); \ + (y) = ((a & (uint64_t)(ch)) >> (s)) | (b & (uint64_t)(ch)); \ + } while (0) + +#define SWAP2(x, y) SWAPN(0x5555555555555555, 0xAAAAAAAAAAAAAAAA, 1, x, y) +#define SWAP4(x, y) SWAPN(0x3333333333333333, 0xCCCCCCCCCCCCCCCC, 2, x, y) +#define SWAP8(x, y) SWAPN(0x0F0F0F0F0F0F0F0F, 0xF0F0F0F0F0F0F0F0, 4, x, y) + + SWAP2(q[0], q[1]); + SWAP2(q[2], q[3]); + SWAP2(q[4], q[5]); + SWAP2(q[6], q[7]); + + SWAP4(q[0], q[2]); + SWAP4(q[1], q[3]); + SWAP4(q[4], q[6]); + SWAP4(q[5], q[7]); + + SWAP8(q[0], q[4]); + SWAP8(q[1], q[5]); + SWAP8(q[2], q[6]); + SWAP8(q[3], q[7]); +} + + +static void br_aes_ct64_interleave_in(uint64_t *q0, uint64_t *q1, const uint32_t *w) { + uint64_t x0, x1, x2, x3; + + x0 = w[0]; + x1 = w[1]; + x2 = w[2]; + x3 = w[3]; + x0 |= (x0 << 16); + x1 |= (x1 << 16); + x2 |= (x2 << 16); + x3 |= (x3 << 16); + x0 &= (uint64_t)0x0000FFFF0000FFFF; + x1 &= (uint64_t)0x0000FFFF0000FFFF; + x2 &= (uint64_t)0x0000FFFF0000FFFF; + x3 &= (uint64_t)0x0000FFFF0000FFFF; + x0 |= (x0 << 8); + x1 |= (x1 << 8); + x2 |= (x2 << 8); + x3 |= (x3 << 8); + x0 &= (uint64_t)0x00FF00FF00FF00FF; + x1 &= (uint64_t)0x00FF00FF00FF00FF; + x2 &= (uint64_t)0x00FF00FF00FF00FF; + x3 &= (uint64_t)0x00FF00FF00FF00FF; + *q0 = x0 | (x2 << 8); + *q1 = x1 | (x3 << 8); +} + + +static void br_aes_ct64_interleave_out(uint32_t *w, uint64_t q0, uint64_t q1) { + uint64_t x0, x1, x2, x3; + + x0 = q0 & (uint64_t)0x00FF00FF00FF00FF; + x1 = q1 & (uint64_t)0x00FF00FF00FF00FF; + x2 = (q0 >> 8) & (uint64_t)0x00FF00FF00FF00FF; + x3 = (q1 >> 8) & (uint64_t)0x00FF00FF00FF00FF; + x0 |= (x0 >> 8); + x1 |= (x1 >> 8); + x2 |= (x2 >> 8); + x3 |= (x3 >> 8); + x0 &= (uint64_t)0x0000FFFF0000FFFF; + x1 &= (uint64_t)0x0000FFFF0000FFFF; + x2 &= (uint64_t)0x0000FFFF0000FFFF; + x3 &= (uint64_t)0x0000FFFF0000FFFF; + w[0] = (uint32_t)x0 | (uint32_t)(x0 >> 16); + w[1] = (uint32_t)x1 | (uint32_t)(x1 >> 16); + w[2] = (uint32_t)x2 | (uint32_t)(x2 >> 16); + w[3] = (uint32_t)x3 | (uint32_t)(x3 >> 16); +} + +static inline void add_round_key(uint64_t *q, const uint64_t *sk) { + q[0] ^= sk[0]; + q[1] ^= sk[1]; + q[2] ^= sk[2]; + q[3] ^= sk[3]; + q[4] ^= sk[4]; + q[5] ^= sk[5]; + q[6] ^= sk[6]; + q[7] ^= sk[7]; +} + +static inline void shift_rows(uint64_t *q) { + int i; + + for (i = 0; i < 8; i++) { + uint64_t x; + + x = q[i]; + q[i] = (x & (uint64_t)0x000000000000FFFF) + | ((x & (uint64_t)0x00000000FFF00000) >> 4) + | ((x & (uint64_t)0x00000000000F0000) << 12) + | ((x & (uint64_t)0x0000FF0000000000) >> 8) + | ((x & (uint64_t)0x000000FF00000000) << 8) + | ((x & (uint64_t)0xF000000000000000) >> 12) + | ((x & (uint64_t)0x0FFF000000000000) << 4); + } +} + +static inline uint64_t rotr32(uint64_t x) { + return (x << 32) | (x >> 32); +} + +static inline void mix_columns(uint64_t *q) { + uint64_t q0, q1, q2, q3, q4, q5, q6, q7; + uint64_t r0, r1, r2, r3, r4, r5, r6, r7; + + q0 = q[0]; + q1 = q[1]; + q2 = q[2]; + q3 = q[3]; + q4 = q[4]; + q5 = q[5]; + q6 = q[6]; + q7 = q[7]; + r0 = (q0 >> 16) | (q0 << 48); + r1 = (q1 >> 16) | (q1 << 48); + r2 = (q2 >> 16) | (q2 << 48); + r3 = (q3 >> 16) | (q3 << 48); + r4 = (q4 >> 16) | (q4 << 48); + r5 = (q5 >> 16) | (q5 << 48); + r6 = (q6 >> 16) | (q6 << 48); + r7 = (q7 >> 16) | (q7 << 48); + + q[0] = q7 ^ r7 ^ r0 ^ rotr32(q0 ^ r0); + q[1] = q0 ^ r0 ^ q7 ^ r7 ^ r1 ^ rotr32(q1 ^ r1); + q[2] = q1 ^ r1 ^ r2 ^ rotr32(q2 ^ r2); + q[3] = q2 ^ r2 ^ q7 ^ r7 ^ r3 ^ rotr32(q3 ^ r3); + q[4] = q3 ^ r3 ^ q7 ^ r7 ^ r4 ^ rotr32(q4 ^ r4); + q[5] = q4 ^ r4 ^ r5 ^ rotr32(q5 ^ r5); + q[6] = q5 ^ r5 ^ r6 ^ rotr32(q6 ^ r6); + q[7] = q6 ^ r6 ^ r7 ^ rotr32(q7 ^ r7); +} + +static void interleave_constant(uint64_t *out, const unsigned char *in) { + uint32_t tmp_32_constant[16]; + int i; + + br_range_dec32le(tmp_32_constant, 16, in); + for (i = 0; i < 4; i++) { + br_aes_ct64_interleave_in(&out[i], &out[i + 4], tmp_32_constant + (i << 2)); + } + br_aes_ct64_ortho(out); +} + +static void interleave_constant32(uint32_t *out, const unsigned char *in) { + int i; + for (i = 0; i < 4; i++) { + out[2 * i] = br_dec32le(in + 4 * i); + out[2 * i + 1] = br_dec32le(in + 4 * i + 16); + } + br_aes_ct_ortho(out); +} + +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_tweak_constants( + const unsigned char *pk_seed, const unsigned char *sk_seed, + unsigned long long seed_length) { + unsigned char buf[40 * 16]; + int i; + + /* Use the standard constants to generate tweaked ones. */ + memcpy((uint8_t *)tweaked512_rc64, (uint8_t *)haraka512_rc64, 40 * 16); + + /* Constants for sk.seed */ + if (sk_seed != NULL) { + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_haraka_S( + buf, 40 * 16, sk_seed, seed_length); + + /* Interleave constants */ + for (i = 0; i < 10; i++) { + interleave_constant32(tweaked256_rc32_sseed[i], buf + 32 * i); + } + } + + /* Constants for pk.seed */ + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_haraka_S( + buf, 40 * 16, pk_seed, seed_length); + for (i = 0; i < 10; i++) { + interleave_constant32(tweaked256_rc32[i], buf + 32 * i); + interleave_constant(tweaked512_rc64[i], buf + 64 * i); + } +} + +static void haraka_S_absorb(unsigned char *s, + const unsigned char *m, unsigned long long mlen, + unsigned char p) { + unsigned long long i; + unsigned char t[HARAKAS_RATE]; + + while (mlen >= HARAKAS_RATE) { + /* XOR block to state */ + for (i = 0; i < HARAKAS_RATE; ++i) { + s[i] ^= m[i]; + } + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_haraka512_perm(s, s); + mlen -= HARAKAS_RATE; + m += HARAKAS_RATE; + } + + for (i = 0; i < HARAKAS_RATE; ++i) { + t[i] = 0; + } + for (i = 0; i < mlen; ++i) { + t[i] = m[i]; + } + t[i] = p; + t[HARAKAS_RATE - 1] |= 128; + for (i = 0; i < HARAKAS_RATE; ++i) { + s[i] ^= t[i]; + } +} + +static void haraka_S_squeezeblocks(unsigned char *h, unsigned long long nblocks, + unsigned char *s) { + while (nblocks > 0) { + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_haraka512_perm(s, s); + memcpy(h, s, HARAKAS_RATE); + h += HARAKAS_RATE; + nblocks--; + } +} + +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_haraka_S_inc_init(uint8_t *s_inc) { + size_t i; + + for (i = 0; i < 64; i++) { + s_inc[i] = 0; + } + s_inc[64] = 0; +} + +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_haraka_S_inc_absorb(uint8_t *s_inc, const uint8_t *m, size_t mlen) { + size_t i; + + /* Recall that s_inc[64] is the non-absorbed bytes xored into the state */ + while (mlen + s_inc[64] >= HARAKAS_RATE) { + for (i = 0; i < (size_t)(HARAKAS_RATE - s_inc[64]); i++) { + /* Take the i'th byte from message + xor with the s_inc[64] + i'th byte of the state */ + s_inc[s_inc[64] + i] ^= m[i]; + } + mlen -= (size_t)(HARAKAS_RATE - s_inc[64]); + m += HARAKAS_RATE - s_inc[64]; + s_inc[64] = 0; + + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_haraka512_perm(s_inc, s_inc); + } + + for (i = 0; i < mlen; i++) { + s_inc[s_inc[64] + i] ^= m[i]; + } + s_inc[64] = (uint8_t)(mlen + s_inc[64]); +} + +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_haraka_S_inc_finalize(uint8_t *s_inc) { + /* After haraka_S_inc_absorb, we are guaranteed that s_inc[64] < HARAKAS_RATE, + so we can always use one more byte for p in the current state. */ + s_inc[s_inc[64]] ^= 0x1F; + s_inc[HARAKAS_RATE - 1] ^= 128; + s_inc[64] = 0; +} + +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_haraka_S_inc_squeeze(uint8_t *out, size_t outlen, uint8_t *s_inc) { + uint8_t i; + + /* First consume any bytes we still have sitting around */ + for (i = 0; i < outlen && i < s_inc[64]; i++) { + /* There are s_inc[64] bytes left, so r - s_inc[64] is the first + available byte. We consume from there, i.e., up to r. */ + out[i] = s_inc[(HARAKAS_RATE - s_inc[64] + i)]; + } + out += i; + outlen -= i; + s_inc[64] = (uint8_t)(s_inc[64] - i); + + /* Then squeeze the remaining necessary blocks */ + while (outlen > 0) { + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_haraka512_perm(s_inc, s_inc); + + for (i = 0; i < outlen && i < HARAKAS_RATE; i++) { + out[i] = s_inc[i]; + } + out += i; + outlen -= i; + s_inc[64] = (uint8_t)(HARAKAS_RATE - i); + } +} + +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_haraka_S(unsigned char *out, unsigned long long outlen, const unsigned char *in, unsigned long long inlen) { + unsigned long long i; + unsigned char s[64]; + unsigned char d[32]; + + for (i = 0; i < 64; i++) { + s[i] = 0; + } + haraka_S_absorb(s, in, inlen, 0x1F); + + haraka_S_squeezeblocks(out, outlen / 32, s); + out += (outlen / 32) * 32; + + if (outlen % 32) { + haraka_S_squeezeblocks(d, 1, s); + for (i = 0; i < outlen % 32; i++) { + out[i] = d[i]; + } + } +} + +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_haraka512_perm(unsigned char *out, const unsigned char *in) { + uint32_t w[16]; + uint64_t q[8], tmp_q; + unsigned int i, j; + + br_range_dec32le(w, 16, in); + for (i = 0; i < 4; i++) { + br_aes_ct64_interleave_in(&q[i], &q[i + 4], w + (i << 2)); + } + br_aes_ct64_ortho(q); + + /* AES rounds */ + for (i = 0; i < 5; i++) { + for (j = 0; j < 2; j++) { + br_aes_ct64_bitslice_Sbox(q); + shift_rows(q); + mix_columns(q); + add_round_key(q, tweaked512_rc64[2 * i + j]); + } + /* Mix states */ + for (j = 0; j < 8; j++) { + tmp_q = q[j]; + q[j] = (tmp_q & 0x0001000100010001) << 5 | + (tmp_q & 0x0002000200020002) << 12 | + (tmp_q & 0x0004000400040004) >> 1 | + (tmp_q & 0x0008000800080008) << 6 | + (tmp_q & 0x0020002000200020) << 9 | + (tmp_q & 0x0040004000400040) >> 4 | + (tmp_q & 0x0080008000800080) << 3 | + (tmp_q & 0x2100210021002100) >> 5 | + (tmp_q & 0x0210021002100210) << 2 | + (tmp_q & 0x0800080008000800) << 4 | + (tmp_q & 0x1000100010001000) >> 12 | + (tmp_q & 0x4000400040004000) >> 10 | + (tmp_q & 0x8400840084008400) >> 3; + } + } + + br_aes_ct64_ortho(q); + for (i = 0; i < 4; i ++) { + br_aes_ct64_interleave_out(w + (i << 2), q[i], q[i + 4]); + } + br_range_enc32le(out, w, 16); +} + +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_haraka512(unsigned char *out, const unsigned char *in) { + int i; + + unsigned char buf[64]; + + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_haraka512_perm(buf, in); + /* Feed-forward */ + for (i = 0; i < 64; i++) { + buf[i] = buf[i] ^ in[i]; + } + + /* Truncated */ + memcpy(out, buf + 8, 8); + memcpy(out + 8, buf + 24, 8); + memcpy(out + 16, buf + 32, 8); + memcpy(out + 24, buf + 48, 8); +} + + +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_haraka256(unsigned char *out, const unsigned char *in) { + uint32_t q[8], tmp_q; + int i, j; + + for (i = 0; i < 4; i++) { + q[2 * i] = br_dec32le(in + 4 * i); + q[2 * i + 1] = br_dec32le(in + 4 * i + 16); + } + br_aes_ct_ortho(q); + + /* AES rounds */ + for (i = 0; i < 5; i++) { + for (j = 0; j < 2; j++) { + br_aes_ct_bitslice_Sbox(q); + shift_rows32(q); + mix_columns32(q); + add_round_key32(q, tweaked256_rc32[2 * i + j]); + } + + /* Mix states */ + for (j = 0; j < 8; j++) { + tmp_q = q[j]; + q[j] = (tmp_q & 0x81818181) | + (tmp_q & 0x02020202) << 1 | + (tmp_q & 0x04040404) << 2 | + (tmp_q & 0x08080808) << 3 | + (tmp_q & 0x10101010) >> 3 | + (tmp_q & 0x20202020) >> 2 | + (tmp_q & 0x40404040) >> 1; + } + } + + br_aes_ct_ortho(q); + for (i = 0; i < 4; i++) { + br_enc32le(out + 4 * i, q[2 * i]); + br_enc32le(out + 4 * i + 16, q[2 * i + 1]); + } + + for (i = 0; i < 32; i++) { + out[i] ^= in[i]; + } +} + +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_haraka256_sk(unsigned char *out, const unsigned char *in) { + uint32_t q[8], tmp_q; + int i, j; + + for (i = 0; i < 4; i++) { + q[2 * i] = br_dec32le(in + 4 * i); + q[2 * i + 1] = br_dec32le(in + 4 * i + 16); + } + br_aes_ct_ortho(q); + + /* AES rounds */ + for (i = 0; i < 5; i++) { + for (j = 0; j < 2; j++) { + br_aes_ct_bitslice_Sbox(q); + shift_rows32(q); + mix_columns32(q); + add_round_key32(q, tweaked256_rc32_sseed[2 * i + j]); + } + + /* Mix states */ + for (j = 0; j < 8; j++) { + tmp_q = q[j]; + q[j] = (tmp_q & 0x81818181) | + (tmp_q & 0x02020202) << 1 | + (tmp_q & 0x04040404) << 2 | + (tmp_q & 0x08080808) << 3 | + (tmp_q & 0x10101010) >> 3 | + (tmp_q & 0x20202020) >> 2 | + (tmp_q & 0x40404040) >> 1; + } + } + + br_aes_ct_ortho(q); + for (i = 0; i < 4; i++) { + br_enc32le(out + 4 * i, q[2 * i]); + br_enc32le(out + 4 * i + 16, q[2 * i + 1]); + } + + for (i = 0; i < 32; i++) { + out[i] ^= in[i]; + } +} diff --git a/crypto_sign/sphincs-haraka-256s-robust/clean/haraka.h b/crypto_sign/sphincs-haraka-256s-robust/clean/haraka.h new file mode 100644 index 00000000..7332a6f2 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256s-robust/clean/haraka.h @@ -0,0 +1,30 @@ +#ifndef SPX_HARAKA_H +#define SPX_HARAKA_H + +/* Tweak constants with seed */ +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_tweak_constants( + const unsigned char *pk_seed, const unsigned char *sk_seed, + unsigned long long seed_length); + +/* Haraka Sponge */ +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_haraka_S_inc_init(uint8_t *s_inc); +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_haraka_S_inc_absorb(uint8_t *s_inc, const uint8_t *m, size_t mlen); +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_haraka_S_inc_finalize(uint8_t *s_inc); +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_haraka_S_inc_squeeze(uint8_t *out, size_t outlen, uint8_t *s_inc); +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_haraka_S( + unsigned char *out, unsigned long long outlen, + const unsigned char *in, unsigned long long inlen); + +/* Applies the 512-bit Haraka permutation to in. */ +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_haraka512_perm(unsigned char *out, const unsigned char *in); + +/* Implementation of Haraka-512 */ +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_haraka512(unsigned char *out, const unsigned char *in); + +/* Implementation of Haraka-256 */ +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_haraka256(unsigned char *out, const unsigned char *in); + +/* Implementation of Haraka-256 using sk.seed constants */ +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_haraka256_sk(unsigned char *out, const unsigned char *in); + +#endif diff --git a/crypto_sign/sphincs-haraka-256s-robust/clean/hash.h b/crypto_sign/sphincs-haraka-256s-robust/clean/hash.h new file mode 100644 index 00000000..255560f4 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256s-robust/clean/hash.h @@ -0,0 +1,22 @@ +#ifndef SPX_HASH_H +#define SPX_HASH_H + +#include + +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed); + +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen); + +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen); + +#endif diff --git a/crypto_sign/sphincs-haraka-256s-robust/clean/hash_haraka.c b/crypto_sign/sphincs-haraka-256s-robust/clean/hash_haraka.c new file mode 100644 index 00000000..b4bcfddb --- /dev/null +++ b/crypto_sign/sphincs-haraka-256s-robust/clean/hash_haraka.c @@ -0,0 +1,86 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "utils.h" + +#include "haraka.h" + +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed) { + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_tweak_constants(pub_seed, sk_seed, SPX_N); +} + +/* + * Computes PRF(key, addr), given a secret key of SPX_N bytes and an address + */ +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]) { + unsigned char buf[SPX_ADDR_BYTES]; + /* Since SPX_N may be smaller than 32, we need a temporary buffer. */ + unsigned char outbuf[32]; + + (void)key; /* Suppress an 'unused parameter' warning. */ + + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_addr_to_bytes(buf, addr); + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_haraka256_sk(outbuf, buf); + memcpy(out, outbuf, SPX_N); +} + +/** + * Computes the message-dependent randomness R, using a secret seed and an + * optional randomization value as well as the message. + */ +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen) { + uint8_t s_inc[65]; + + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_haraka_S_inc_init(s_inc); + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_haraka_S_inc_absorb(s_inc, sk_prf, SPX_N); + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_haraka_S_inc_absorb(s_inc, optrand, SPX_N); + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_haraka_S_inc_absorb(s_inc, m, mlen); + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_haraka_S_inc_finalize(s_inc); + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_haraka_S_inc_squeeze(R, SPX_N, s_inc); +} + +/** + * Computes the message hash using R, the public key, and the message. + * Outputs the message digest and the index of the leaf. The index is split in + * the tree index and the leaf index, for convenient copying to an address. + */ +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen) { +#define SPX_TREE_BITS (SPX_TREE_HEIGHT * (SPX_D - 1)) +#define SPX_TREE_BYTES ((SPX_TREE_BITS + 7) / 8) +#define SPX_LEAF_BITS SPX_TREE_HEIGHT +#define SPX_LEAF_BYTES ((SPX_LEAF_BITS + 7) / 8) +#define SPX_DGST_BYTES (SPX_FORS_MSG_BYTES + SPX_TREE_BYTES + SPX_LEAF_BYTES) + + unsigned char buf[SPX_DGST_BYTES]; + unsigned char *bufp = buf; + uint8_t s_inc[65]; + + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_haraka_S_inc_init(s_inc); + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_haraka_S_inc_absorb(s_inc, R, SPX_N); + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_haraka_S_inc_absorb(s_inc, pk, SPX_PK_BYTES); + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_haraka_S_inc_absorb(s_inc, m, mlen); + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_haraka_S_inc_finalize(s_inc); + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_haraka_S_inc_squeeze(buf, SPX_DGST_BYTES, s_inc); + + memcpy(digest, bufp, SPX_FORS_MSG_BYTES); + bufp += SPX_FORS_MSG_BYTES; + + *tree = PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_bytes_to_ull(bufp, SPX_TREE_BYTES); + *tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS); + bufp += SPX_TREE_BYTES; + + *leaf_idx = (uint32_t)PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_bytes_to_ull( + bufp, SPX_LEAF_BYTES); + *leaf_idx &= (~(uint32_t)0) >> (32 - SPX_LEAF_BITS); +} diff --git a/crypto_sign/sphincs-haraka-256s-robust/clean/params.h b/crypto_sign/sphincs-haraka-256s-robust/clean/params.h new file mode 100644 index 00000000..f023d687 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256s-robust/clean/params.h @@ -0,0 +1,53 @@ +#ifndef SPX_PARAMS_H +#define SPX_PARAMS_H + +/* Hash output length in bytes. */ +#define SPX_N 32 +/* Height of the hypertree. */ +#define SPX_FULL_HEIGHT 64 +/* Number of subtree layer. */ +#define SPX_D 8 +/* FORS tree dimensions. */ +#define SPX_FORS_HEIGHT 14 +#define SPX_FORS_TREES 22 +/* Winternitz parameter, */ +#define SPX_WOTS_W 16 + +/* The hash function is defined by linking a different hash.c file, as opposed + to setting a #define constant. */ + +/* For clarity */ +#define SPX_ADDR_BYTES 32 + +/* WOTS parameters. */ +#define SPX_WOTS_LOGW 4 + +#define SPX_WOTS_LEN1 (8 * SPX_N / SPX_WOTS_LOGW) + +/* SPX_WOTS_LEN2 is floor(log(len_1 * (w - 1)) / log(w)) + 1; we precompute */ +#define SPX_WOTS_LEN2 3 + +#define SPX_WOTS_LEN (SPX_WOTS_LEN1 + SPX_WOTS_LEN2) +#define SPX_WOTS_BYTES (SPX_WOTS_LEN * SPX_N) +#define SPX_WOTS_PK_BYTES SPX_WOTS_BYTES + +/* Subtree size. */ +#define SPX_TREE_HEIGHT (SPX_FULL_HEIGHT / SPX_D) + +/* FORS parameters. */ +#define SPX_FORS_MSG_BYTES ((SPX_FORS_HEIGHT * SPX_FORS_TREES + 7) / 8) +#define SPX_FORS_BYTES ((SPX_FORS_HEIGHT + 1) * SPX_FORS_TREES * SPX_N) +#define SPX_FORS_PK_BYTES SPX_N + +/* Resulting SPX sizes. */ +#define SPX_BYTES (SPX_N + SPX_FORS_BYTES + SPX_D * SPX_WOTS_BYTES +\ + SPX_FULL_HEIGHT * SPX_N) +#define SPX_PK_BYTES (2 * SPX_N) +#define SPX_SK_BYTES (2 * SPX_N + SPX_PK_BYTES) + +/* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ +#define SPX_OPTRAND_BYTES 32 + +#endif diff --git a/crypto_sign/sphincs-haraka-256s-robust/clean/sign.c b/crypto_sign/sphincs-haraka-256s-robust/clean/sign.c new file mode 100644 index 00000000..5cd3b037 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256s-robust/clean/sign.c @@ -0,0 +1,344 @@ +#include +#include +#include + +#include "address.h" +#include "api.h" +#include "fors.h" +#include "hash.h" +#include "params.h" +#include "randombytes.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +/** + * Computes the leaf at a given address. First generates the WOTS key pair, + * then computes leaf by hashing horizontally. + */ +static void wots_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t tree_addr[8]) { + unsigned char pk[SPX_WOTS_BYTES]; + uint32_t wots_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_keypair_addr( + wots_addr, addr_idx); + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_wots_gen_pk( + pk, sk_seed, pub_seed, wots_addr); + + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_thash_WOTS_LEN( + leaf, pk, pub_seed, wots_pk_addr); +} + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_crypto_sign_secretkeybytes(void) { + return PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_CRYPTO_SECRETKEYBYTES; +} + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_crypto_sign_publickeybytes(void) { + return PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES; +} + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_crypto_sign_bytes(void) { + return PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_CRYPTO_BYTES; +} + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_crypto_sign_seedbytes(void) { + return PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_CRYPTO_SEEDBYTES; +} + +/* + * Generates an SPX key pair given a seed of length + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed) { + /* We do not need the auth path in key generation, but it simplifies the + code to have just one treehash routine that computes both root and path + in one function. */ + unsigned char auth_path[SPX_TREE_HEIGHT * SPX_N]; + uint32_t top_tree_addr[8] = {0}; + + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_layer_addr( + top_tree_addr, SPX_D - 1); + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_type( + top_tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */ + memcpy(sk, seed, PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_CRYPTO_SEEDBYTES); + + memcpy(pk, sk + 2 * SPX_N, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_initialize_hash_function(pk, sk); + + /* Compute root node of the top-most subtree. */ + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_treehash_TREE_HEIGHT( + sk + 3 * SPX_N, auth_path, sk, sk + 2 * SPX_N, 0, 0, + wots_gen_leaf, top_tree_addr); + + memcpy(pk + SPX_N, sk + 3 * SPX_N, SPX_N); + + return 0; +} + +/* + * Generates an SPX key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk) { + unsigned char seed[PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_CRYPTO_SEEDBYTES]; + randombytes(seed, PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_CRYPTO_SEEDBYTES); + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_crypto_sign_seed_keypair( + pk, sk, seed); + + return 0; +} + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + const unsigned char *sk_seed = sk; + const unsigned char *sk_prf = sk + SPX_N; + const unsigned char *pk = sk + 2 * SPX_N; + const unsigned char *pub_seed = pk; + + unsigned char optrand[SPX_N]; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char root[SPX_N]; + uint32_t i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_initialize_hash_function( + pub_seed, sk_seed); + + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ + randombytes(optrand, SPX_N); + /* Compute the digest randomization value. */ + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_gen_message_random( + sig, sk_prf, optrand, m, mlen); + + /* Derive the message digest and leaf index from R, PK and M. */ + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Sign the message hash using FORS. */ + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_fors_sign( + sig, root, mhash, sk_seed, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Compute a WOTS signature. */ + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_wots_sign( + sig, root, sk_seed, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the authentication path for the used WOTS leaf. */ + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_treehash_TREE_HEIGHT( + root, sig, sk_seed, pub_seed, idx_leaf, 0, + wots_gen_leaf, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + *siglen = SPX_BYTES; + + return 0; +} + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk) { + const unsigned char *pub_seed = pk; + const unsigned char *pub_root = pk + SPX_N; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char wots_pk[SPX_WOTS_BYTES]; + unsigned char root[SPX_N]; + unsigned char leaf[SPX_N]; + unsigned int i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + if (siglen != SPX_BYTES) { + return -1; + } + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_initialize_hash_function( + pub_seed, NULL); + + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + /* Derive the message digest and leaf index from R || PK || M. */ + /* The additional SPX_N is a result of the hash domain separator. */ + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + /* Layer correctly defaults to 0, so no need to set_layer_addr */ + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_fors_pk_from_sig( + root, sig, mhash, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + /* For each subtree.. */ + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + + /* The WOTS public key is only correct if the signature was correct. */ + /* Initially, root is the FORS pk, but on subsequent iterations it is + the root of the subtree below the currently processed subtree. */ + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_wots_pk_from_sig( + wots_pk, sig, root, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the leaf node using the WOTS public key. */ + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_thash_WOTS_LEN( + leaf, wots_pk, pub_seed, wots_pk_addr); + + /* Compute the root node of this subtree. */ + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_compute_root( + root, leaf, idx_leaf, 0, sig, SPX_TREE_HEIGHT, + pub_seed, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + /* Check if the root node equals the root node in the public key. */ + if (memcmp(root, pub_root, SPX_N) != 0) { + return -1; + } + + return 0; +} + + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + size_t siglen; + + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_crypto_sign_signature( + sm, &siglen, m, mlen, sk); + + memmove(sm + SPX_BYTES, m, mlen); + *smlen = siglen + mlen; + + return 0; +} + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk) { + /* The API caller does not necessarily know what size a signature should be + but SPHINCS+ signatures are always exactly SPX_BYTES. */ + if (smlen < SPX_BYTES) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + *mlen = smlen - SPX_BYTES; + + if (PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_crypto_sign_verify( + sm, SPX_BYTES, sm + SPX_BYTES, *mlen, pk)) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + /* If verification was successful, move the message to the right place. */ + memmove(m, sm + SPX_BYTES, *mlen); + + return 0; +} diff --git a/crypto_sign/sphincs-haraka-256s-robust/clean/thash.h b/crypto_sign/sphincs-haraka-256s-robust/clean/thash.h new file mode 100644 index 00000000..60c5f631 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256s-robust/clean/thash.h @@ -0,0 +1,22 @@ +#ifndef SPX_THASH_H +#define SPX_THASH_H + +#include + +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-haraka-256s-robust/clean/thash_haraka_robust.c b/crypto_sign/sphincs-haraka-256s-robust/clean/thash_haraka_robust.c new file mode 100644 index 00000000..ef7b8f0a --- /dev/null +++ b/crypto_sign/sphincs-haraka-256s-robust/clean/thash_haraka_robust.c @@ -0,0 +1,88 @@ +#include +#include + +#include "address.h" +#include "params.h" +#include "thash.h" + +#include "haraka.h" + +/** + * Takes an array of inblocks concatenated arrays of SPX_N bytes. + */ +static void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_thash( + unsigned char *out, unsigned char *buf, + const unsigned char *in, unsigned int inblocks, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char *bitmask = buf + SPX_ADDR_BYTES; + unsigned char outbuf[32]; + unsigned char buf_tmp[64]; + unsigned int i; + + (void)pub_seed; /* Suppress an 'unused parameter' warning. */ + + if (inblocks == 1) { + /* F function */ + /* Since SPX_N may be smaller than 32, we need a temporary buffer. */ + memset(buf_tmp, 0, 64); + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_addr_to_bytes(buf_tmp, addr); + + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_haraka256(outbuf, buf_tmp); + for (i = 0; i < inblocks * SPX_N; i++) { + buf_tmp[SPX_ADDR_BYTES + i] = in[i] ^ outbuf[i]; + } + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_haraka512(outbuf, buf_tmp); + memcpy(out, outbuf, SPX_N); + } else { + /* All other tweakable hashes*/ + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_addr_to_bytes(buf, addr); + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_haraka_S( + bitmask, inblocks * SPX_N, buf, SPX_ADDR_BYTES); + + for (i = 0; i < inblocks * SPX_N; i++) { + buf[SPX_ADDR_BYTES + i] = in[i] ^ bitmask[i]; + } + + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_haraka_S( + out, SPX_N, buf, SPX_ADDR_BYTES + inblocks * SPX_N); + } +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_ADDR_BYTES + 1 * SPX_N]; + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_thash( + out, buf, in, 1, pub_seed, addr); +} + +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_ADDR_BYTES + 2 * SPX_N]; + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_thash( + out, buf, in, 2, pub_seed, addr); +} + +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_ADDR_BYTES + SPX_WOTS_LEN * SPX_N]; + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_thash( + out, buf, in, SPX_WOTS_LEN, pub_seed, addr); +} + +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_ADDR_BYTES + SPX_FORS_TREES * SPX_N]; + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_thash( + out, buf, in, SPX_FORS_TREES, pub_seed, addr); +} diff --git a/crypto_sign/sphincs-haraka-256s-robust/clean/utils.c b/crypto_sign/sphincs-haraka-256s-robust/clean/utils.c new file mode 100644 index 00000000..dd3a3bd4 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256s-robust/clean/utils.c @@ -0,0 +1,192 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in) { + + /* Iterate over out in decreasing order, for big-endianness. */ + for (size_t i = outlen; i > 0; i--) { + out[i - 1] = in & 0xff; + in = in >> 8; + } +} + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen) { + unsigned long long retval = 0; + + for (size_t i = 0; i < inlen; i++) { + retval |= ((unsigned long long)in[i]) << (8 * (inlen - 1 - i)); + } + return retval; +} + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + unsigned char buffer[2 * SPX_N]; + + /* If leaf_idx is odd (last bit = 1), current path element is a right child + and auth_path has to go left. Otherwise it is the other way around. */ + if (leaf_idx & 1) { + memcpy(buffer + SPX_N, leaf, SPX_N); + memcpy(buffer, auth_path, SPX_N); + } else { + memcpy(buffer, leaf, SPX_N); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + + for (i = 0; i < tree_height - 1; i++) { + leaf_idx >>= 1; + idx_offset >>= 1; + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_tree_height(addr, i + 1); + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + + /* Pick the right or left neighbor, depending on parity of the node. */ + if (leaf_idx & 1) { + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_thash_2( + buffer + SPX_N, buffer, pub_seed, addr); + memcpy(buffer, auth_path, SPX_N); + } else { + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_thash_2( + buffer, buffer, pub_seed, addr); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + } + + /* The last iteration is exceptional; we do not copy an auth_path node. */ + leaf_idx >>= 1; + idx_offset >>= 1; + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_tree_height(addr, tree_height); + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_thash_2( + root, buffer, pub_seed, addr); +} + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +static void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_treehash( + unsigned char *root, unsigned char *auth_path, + unsigned char *stack, unsigned int *heights, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned int offset = 0; + uint32_t idx; + uint32_t tree_idx; + + for (idx = 0; idx < (uint32_t)(1 << tree_height); idx++) { + /* Add the next leaf node to the stack. */ + gen_leaf(stack + offset * SPX_N, + sk_seed, pub_seed, idx + idx_offset, tree_addr); + offset++; + heights[offset - 1] = 0; + + /* If this is a node we need for the auth path.. */ + if ((leaf_idx ^ 0x1) == idx) { + memcpy(auth_path, stack + (offset - 1)*SPX_N, SPX_N); + } + + /* While the top-most nodes are of equal height.. */ + while (offset >= 2 && heights[offset - 1] == heights[offset - 2]) { + /* Compute index of the new node, in the next layer. */ + tree_idx = (idx >> (heights[offset - 1] + 1)); + + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_tree_height( + tree_addr, heights[offset - 1] + 1); + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_tree_index( + tree_addr, tree_idx + (idx_offset >> (heights[offset - 1] + 1))); + /* Hash the top-most nodes from the stack together. */ + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_thash_2( + stack + (offset - 2)*SPX_N, stack + (offset - 2)*SPX_N, + pub_seed, tree_addr); + offset--; + /* Note that the top-most node is now one layer higher. */ + heights[offset - 1]++; + + /* If this is a node we need for the auth path.. */ + if (((leaf_idx >> heights[offset - 1]) ^ 0x1) == tree_idx) { + memcpy(auth_path + heights[offset - 1]*SPX_N, + stack + (offset - 1)*SPX_N, SPX_N); + } + } + } + memcpy(root, stack, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_FORS_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_FORS_HEIGHT + 1]; + + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_FORS_HEIGHT, gen_leaf, tree_addr); +} + +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_TREE_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_TREE_HEIGHT + 1]; + + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_TREE_HEIGHT, gen_leaf, tree_addr); +} diff --git a/crypto_sign/sphincs-haraka-256s-robust/clean/utils.h b/crypto_sign/sphincs-haraka-256s-robust/clean/utils.h new file mode 100644 index 00000000..71552b79 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256s-robust/clean/utils.h @@ -0,0 +1,60 @@ +#ifndef SPX_UTILS_H +#define SPX_UTILS_H + +#include "params.h" +#include +#include + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in); + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen); + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-haraka-256s-robust/clean/wots.c b/crypto_sign/sphincs-haraka-256s-robust/clean/wots.c new file mode 100644 index 00000000..86ef18ab --- /dev/null +++ b/crypto_sign/sphincs-haraka-256s-robust/clean/wots.c @@ -0,0 +1,161 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +// TODO clarify address expectations, and make them more uniform. +// TODO i.e. do we expect types to be set already? +// TODO and do we expect modifications or copies? + +/** + * Computes the starting value for a chain, i.e. the secret key. + * Expects the address to be complete up to the chain address. + */ +static void wots_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t wots_addr[8]) { + /* Make sure that the hash address is actually zeroed. */ + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_hash_addr(wots_addr, 0); + + /* Generate sk element. */ + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_prf_addr(sk, sk_seed, wots_addr); +} + +/** + * Computes the chaining function. + * out and in have to be n-byte arrays. + * + * Interprets in as start-th value of the chain. + * addr has to contain the address of the chain. + */ +static void gen_chain(unsigned char *out, const unsigned char *in, + unsigned int start, unsigned int steps, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + /* Initialize out with the value at position 'start'. */ + memcpy(out, in, SPX_N); + + /* Iterate 'steps' calls to the hash function. */ + for (i = start; i < (start + steps) && i < SPX_WOTS_W; i++) { + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_hash_addr(addr, i); + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_thash_1( + out, out, pub_seed, addr); + } +} + +/** + * base_w algorithm as described in draft. + * Interprets an array of bytes as integers in base w. + * This only works when log_w is a divisor of 8. + */ +static void base_w(unsigned int *output, const size_t out_len, + const unsigned char *input) { + size_t in = 0; + size_t out = 0; + unsigned char total = 0; + unsigned int bits = 0; + size_t consumed; + + for (consumed = 0; consumed < out_len; consumed++) { + if (bits == 0) { + total = input[in]; + in++; + bits += 8; + } + bits -= SPX_WOTS_LOGW; + output[out] = (unsigned int)((total >> bits) & (SPX_WOTS_W - 1)); + out++; + } +} + +/* Computes the WOTS+ checksum over a message (in base_w). */ +static void wots_checksum(unsigned int *csum_base_w, + const unsigned int *msg_base_w) { + unsigned int csum = 0; + unsigned char csum_bytes[(SPX_WOTS_LEN2 * SPX_WOTS_LOGW + 7) / 8]; + unsigned int i; + + /* Compute checksum. */ + for (i = 0; i < SPX_WOTS_LEN1; i++) { + csum += SPX_WOTS_W - 1 - msg_base_w[i]; + } + + /* Convert checksum to base_w. */ + /* Make sure expected empty zero bits are the least significant bits. */ + csum = csum << (8 - ((SPX_WOTS_LEN2 * SPX_WOTS_LOGW) % 8)); + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_ull_to_bytes( + csum_bytes, sizeof(csum_bytes), csum); + base_w(csum_base_w, SPX_WOTS_LEN2, csum_bytes); +} + +/* Takes a message and derives the matching chain lengths. */ +static void chain_lengths(unsigned int *lengths, const unsigned char *msg) { + base_w(lengths, SPX_WOTS_LEN1, msg); + wots_checksum(lengths + SPX_WOTS_LEN1, lengths); +} + +/** + * WOTS key generation. Takes a 32 byte sk_seed, expands it to WOTS private key + * elements and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(pk + i * SPX_N, sk_seed, addr); + gen_chain(pk + i * SPX_N, pk + i * SPX_N, + 0, SPX_WOTS_W - 1, pub_seed, addr); + } +} + +/** + * Takes a n-byte message and the 32-byte sk_see to compute a signature 'sig'. + */ +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(sig + i * SPX_N, sk_seed, addr); + gen_chain(sig + i * SPX_N, sig + i * SPX_N, 0, lengths[i], pub_seed, addr); + } +} + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_set_chain_addr(addr, i); + gen_chain(pk + i * SPX_N, sig + i * SPX_N, + lengths[i], SPX_WOTS_W - 1 - lengths[i], pub_seed, addr); + } +} diff --git a/crypto_sign/sphincs-haraka-256s-robust/clean/wots.h b/crypto_sign/sphincs-haraka-256s-robust/clean/wots.h new file mode 100644 index 00000000..10401439 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256s-robust/clean/wots.h @@ -0,0 +1,38 @@ +#ifndef SPX_WOTS_H +#define SPX_WOTS_H + +#include "params.h" +#include + +/** + * WOTS key generation. Takes a 32 byte seed for the private key, expands it to + * a full WOTS private key and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * Takes a n-byte message and the 32-byte seed for the private key to compute a + * signature that is placed at 'sig'. + */ +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]); + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-haraka-256s-simple/META.yml b/crypto_sign/sphincs-haraka-256s-simple/META.yml new file mode 100644 index 00000000..fee133e2 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256s-simple/META.yml @@ -0,0 +1,27 @@ +name: SPHINCS+ +type: signature +claimed-nist-level: 5 +length-public-key: 64 +length-secret-key: 128 +length-signature: 29792 +testvectors-sha256: 25fcc82aa371d06c8b494c2d0a3ac4920cfb8134bef9962491669ef2c6a0b820 +principal-submitter: Andreas Hülsing +auxiliary-submitters: + - Jean-Philippe Aumasson + - Daniel J. Bernstein, + - Christoph Dobraunig + - Maria Eichlseder + - Scott Fluhrer + - Stefan-Lukas Gazdag + - Panos Kampanakis + - Stefan Kölbl + - Tanja Lange + - Martin M. Lauridsen + - Florian Mendel + - Ruben Niederhagen + - Christian Rechberger + - Joost Rijneveld + - Peter Schwabe +implementations: + - name: clean + version: https://github.com/sphincs/sphincsplus/commit/77755c94d0bc744478044d6efbb888dc13156441 diff --git a/crypto_sign/sphincs-haraka-256s-simple/clean/LICENSE b/crypto_sign/sphincs-haraka-256s-simple/clean/LICENSE new file mode 100644 index 00000000..670154e3 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256s-simple/clean/LICENSE @@ -0,0 +1,116 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + diff --git a/crypto_sign/sphincs-haraka-256s-simple/clean/Makefile b/crypto_sign/sphincs-haraka-256s-simple/clean/Makefile new file mode 100644 index 00000000..be89decc --- /dev/null +++ b/crypto_sign/sphincs-haraka-256s-simple/clean/Makefile @@ -0,0 +1,20 @@ +# This Makefile can be used with GNU Make or BSD Make + +LIB=libsphincs-haraka-256s-simple_clean.a + +HEADERS = params.h address.h wots.h utils.h fors.h api.h hash.h thash.h haraka.h +OBJECTS = address.o wots.o utils.o fors.o sign.o hash_haraka.o thash_haraka_simple.o haraka.o + +CFLAGS=-O3 -Wall -Wconversion -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) + +all: $(LIB) + +%.o: %.c $(HEADERS) + $(CC) $(CFLAGS) -c -o $@ $< + +$(LIB): $(OBJECTS) + $(AR) -r $@ $(OBJECTS) + +clean: + $(RM) $(OBJECTS) + $(RM) $(LIB) diff --git a/crypto_sign/sphincs-haraka-256s-simple/clean/Makefile.Microsoft_nmake b/crypto_sign/sphincs-haraka-256s-simple/clean/Makefile.Microsoft_nmake new file mode 100644 index 00000000..1daae8ce --- /dev/null +++ b/crypto_sign/sphincs-haraka-256s-simple/clean/Makefile.Microsoft_nmake @@ -0,0 +1,19 @@ +# This Makefile can be used with Microsoft Visual Studio's nmake using the command: +# nmake /f Makefile.Microsoft_nmake + +LIBRARY=libsphincs-haraka-256s-simple_clean.lib +OBJECTS=address.obj wots.obj utils.obj fors.obj sign.obj hash_haraka.obj thash_haraka_simple.obj haraka.obj + +CFLAGS=/nologo /I ..\..\..\common /W4 /WX + +all: $(LIBRARY) + +# Make sure objects are recompiled if headers change. +$(OBJECTS): *.h + +$(LIBRARY): $(OBJECTS) + LIB.EXE /NOLOGO /WX /OUT:$@ $** + +clean: + -DEL $(OBJECTS) + -DEL $(LIBRARY) diff --git a/crypto_sign/sphincs-haraka-256s-simple/clean/address.c b/crypto_sign/sphincs-haraka-256s-simple/clean/address.c new file mode 100644 index 00000000..89206539 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256s-simple/clean/address.c @@ -0,0 +1,78 @@ +#include + +#include "address.h" +#include "params.h" +#include "utils.h" + +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]) { + int i; + + for (i = 0; i < 8; i++) { + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_ull_to_bytes( + bytes + i * 4, 4, addr[i]); + } +} + +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer) { + addr[0] = layer; +} + +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree) { + addr[1] = 0; + addr[2] = (uint32_t) (tree >> 32); + addr[3] = (uint32_t) tree; +} + +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_type( + uint32_t addr[8], uint32_t type) { + addr[4] = type; +} + +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; +} + +/* These functions are used for OTS addresses. */ + +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair) { + addr[5] = keypair; +} + +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; + out[5] = in[5]; +} + +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain) { + addr[6] = chain; +} + +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash) { + addr[7] = hash; +} + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height) { + addr[6] = tree_height; +} + +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index) { + addr[7] = tree_index; +} diff --git a/crypto_sign/sphincs-haraka-256s-simple/clean/address.h b/crypto_sign/sphincs-haraka-256s-simple/clean/address.h new file mode 100644 index 00000000..a1094cf2 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256s-simple/clean/address.h @@ -0,0 +1,50 @@ +#ifndef SPX_ADDRESS_H +#define SPX_ADDRESS_H + +#include + +#define SPX_ADDR_TYPE_WOTS 0 +#define SPX_ADDR_TYPE_WOTSPK 1 +#define SPX_ADDR_TYPE_HASHTREE 2 +#define SPX_ADDR_TYPE_FORSTREE 3 +#define SPX_ADDR_TYPE_FORSPK 4 + +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer); + +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree); + +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_type( + uint32_t addr[8], uint32_t type); + +/* Copies the layer and tree part of one address into the other */ +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for WOTS and FORS addresses. */ + +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair); + +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain); + +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash); + +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height); + +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index); + +#endif diff --git a/crypto_sign/sphincs-haraka-256s-simple/clean/api.h b/crypto_sign/sphincs-haraka-256s-simple/clean/api.h new file mode 100644 index 00000000..34f3bb42 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256s-simple/clean/api.h @@ -0,0 +1,78 @@ +#ifndef PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_API_H +#define PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_API_H + +#include +#include + +#define PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_CRYPTO_ALGNAME "SPHINCS+" + +#define PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_CRYPTO_SECRETKEYBYTES 128 +#define PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES 64 +#define PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_CRYPTO_BYTES 29792 +#define PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_CRYPTO_SEEDBYTES 96 + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_crypto_sign_secretkeybytes(void); + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_crypto_sign_publickeybytes(void); + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_crypto_sign_bytes(void); + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_crypto_sign_seedbytes(void); + +/* + * Generates a SPHINCS+ key pair given a seed. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed); + +/* + * Generates a SPHINCS+ key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk); + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk); + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk); + +#endif diff --git a/crypto_sign/sphincs-haraka-256s-simple/clean/fors.c b/crypto_sign/sphincs-haraka-256s-simple/clean/fors.c new file mode 100644 index 00000000..3f41d5e7 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256s-simple/clean/fors.c @@ -0,0 +1,164 @@ +#include +#include +#include + +#include "address.h" +#include "fors.h" +#include "hash.h" +#include "thash.h" +#include "utils.h" + +static void fors_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_prf_addr( + sk, sk_seed, fors_leaf_addr); +} + +static void fors_sk_to_leaf(unsigned char *leaf, const unsigned char *sk, + const unsigned char *pub_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_thash_1( + leaf, sk, pub_seed, fors_leaf_addr); +} + +static void fors_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t fors_tree_addr[8]) { + uint32_t fors_leaf_addr[8] = {0}; + + /* Only copy the parts that must be kept in fors_leaf_addr. */ + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_copy_keypair_addr( + fors_leaf_addr, fors_tree_addr); + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_type( + fors_leaf_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_tree_index( + fors_leaf_addr, addr_idx); + + fors_gen_sk(leaf, sk_seed, fors_leaf_addr); + fors_sk_to_leaf(leaf, leaf, pub_seed, fors_leaf_addr); +} + +/** + * Interprets m as SPX_FORS_HEIGHT-bit unsigned integers. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + * Assumes indices has space for SPX_FORS_TREES integers. + */ +static void message_to_indices(uint32_t *indices, const unsigned char *m) { + unsigned int i, j; + unsigned int offset = 0; + + for (i = 0; i < SPX_FORS_TREES; i++) { + indices[i] = 0; + for (j = 0; j < SPX_FORS_HEIGHT; j++) { + indices[i] ^= (((uint32_t)m[offset >> 3] >> (offset & 0x7)) & 0x1) << j; + offset++; + } + } +} + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Include the secret key part that produces the selected leaf node. */ + fors_gen_sk(sig, sk_seed, fors_tree_addr); + sig += SPX_N; + + /* Compute the authentication path for this leaf node. */ + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_treehash_FORS_HEIGHT( + roots + i * SPX_N, sig, sk_seed, pub_seed, + indices[i], idx_offset, fors_gen_leaf, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + unsigned char leaf[SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Derive the leaf from the included secret key part. */ + fors_sk_to_leaf(leaf, sig, pub_seed, fors_tree_addr); + sig += SPX_N; + + /* Derive the corresponding root node of this tree. */ + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_compute_root( + roots + i * SPX_N, leaf, indices[i], idx_offset, sig, + SPX_FORS_HEIGHT, pub_seed, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} diff --git a/crypto_sign/sphincs-haraka-256s-simple/clean/fors.h b/crypto_sign/sphincs-haraka-256s-simple/clean/fors.h new file mode 100644 index 00000000..f6f5cb8d --- /dev/null +++ b/crypto_sign/sphincs-haraka-256s-simple/clean/fors.h @@ -0,0 +1,30 @@ +#ifndef SPX_FORS_H +#define SPX_FORS_H + +#include + +#include "params.h" + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]); + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-haraka-256s-simple/clean/haraka.c b/crypto_sign/sphincs-haraka-256s-simple/clean/haraka.c new file mode 100644 index 00000000..4c535f23 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256s-simple/clean/haraka.c @@ -0,0 +1,965 @@ +/* + * Constant time implementation of the Haraka hash function. + * + * The bit-sliced implementation of the AES round functions are + * based on the AES implementation in BearSSL written + * by Thomas Pornin + */ + +#include +#include +#include + +#include "haraka.h" + +#define HARAKAS_RATE 32 + +static const uint64_t haraka512_rc64[10][8] = { + {0x24cf0ab9086f628b, 0xbdd6eeecc83b8382, 0xd96fb0306cdad0a7, 0xaace082ac8f95f89, 0x449d8e8870d7041f, 0x49bb2f80b2b3e2f8, 0x0569ae98d93bb258, 0x23dc9691e7d6a4b1}, + {0xd8ba10ede0fe5b6e, 0x7ecf7dbe424c7b8e, 0x6ea9949c6df62a31, 0xbf3f3c97ec9c313e, 0x241d03a196a1861e, 0xead3a51116e5a2ea, 0x77d479fcad9574e3, 0x18657a1af894b7a0}, + {0x10671e1a7f595522, 0xd9a00ff675d28c7b, 0x2f1edf0d2b9ba661, 0xb8ff58b8e3de45f9, 0xee29261da9865c02, 0xd1532aa4b50bdf43, 0x8bf858159b231bb1, 0xdf17439d22d4f599}, + {0xdd4b2f0870b918c0, 0x757a81f3b39b1bb6, 0x7a5c556898952e3f, 0x7dd70a16d915d87a, 0x3ae61971982b8301, 0xc3ab319e030412be, 0x17c0033ac094a8cb, 0x5a0630fc1a8dc4ef}, + {0x17708988c1632f73, 0xf92ddae090b44f4f, 0x11ac0285c43aa314, 0x509059941936b8ba, 0xd03e152fa2ce9b69, 0x3fbcbcb63a32998b, 0x6204696d692254f7, 0x915542ed93ec59b4}, + {0xf4ed94aa8879236e, 0xff6cb41cd38e03c0, 0x069b38602368aeab, 0x669495b820f0ddba, 0xf42013b1b8bf9e3d, 0xcf935efe6439734d, 0xbc1dcf42ca29e3f8, 0x7e6d3ed29f78ad67}, + {0xf3b0f6837ffcddaa, 0x3a76faef934ddf41, 0xcec7ae583a9c8e35, 0xe4dd18c68f0260af, 0x2c0e5df1ad398eaa, 0x478df5236ae22e8c, 0xfb944c46fe865f39, 0xaa48f82f028132ba}, + {0x231b9ae2b76aca77, 0x292a76a712db0b40, 0x5850625dc8134491, 0x73137dd469810fb5, 0x8a12a6a202a474fd, 0xd36fd9daa78bdb80, 0xb34c5e733505706f, 0xbaf1cdca818d9d96}, + {0x2e99781335e8c641, 0xbddfe5cce47d560e, 0xf74e9bf32e5e040c, 0x1d7a709d65996be9, 0x670df36a9cf66cdd, 0xd05ef84a176a2875, 0x0f888e828cb1c44e, 0x1a79e9c9727b052c}, + {0x83497348628d84de, 0x2e9387d51f22a754, 0xb000068da2f852d6, 0x378c9e1190fd6fe5, 0x870027c316de7293, 0xe51a9d4462e047bb, 0x90ecf7f8c6251195, 0x655953bfbed90a9c}, +}; + +static uint64_t tweaked512_rc64[10][8]; +static uint32_t tweaked256_rc32[10][8]; +static uint32_t tweaked256_rc32_sseed[10][8]; + +static inline uint32_t br_dec32le(const unsigned char *src) { + return (uint32_t)src[0] + | ((uint32_t)src[1] << 8) + | ((uint32_t)src[2] << 16) + | ((uint32_t)src[3] << 24); +} + +static void br_range_dec32le(uint32_t *v, size_t num, const unsigned char *src) { + while (num-- > 0) { + *v ++ = br_dec32le(src); + src += 4; + } +} + +static inline void br_enc32le(unsigned char *dst, uint32_t x) { + dst[0] = (unsigned char)x; + dst[1] = (unsigned char)(x >> 8); + dst[2] = (unsigned char)(x >> 16); + dst[3] = (unsigned char)(x >> 24); +} + + +static void br_range_enc32le(unsigned char *dst, const uint32_t *v, size_t num) { + while (num-- > 0) { + br_enc32le(dst, *v ++); + dst += 4; + } +} + +static void br_aes_ct64_bitslice_Sbox(uint64_t *q) { + /* + * This S-box implementation is a straightforward translation of + * the circuit described by Boyar and Peralta in "A new + * combinational logic minimization technique with applications + * to cryptology" (https://eprint.iacr.org/2009/191.pdf). + * + * Note that variables x* (input) and s* (output) are numbered + * in "reverse" order (x0 is the high bit, x7 is the low bit). + */ + + uint64_t x0, x1, x2, x3, x4, x5, x6, x7; + uint64_t y1, y2, y3, y4, y5, y6, y7, y8, y9; + uint64_t y10, y11, y12, y13, y14, y15, y16, y17, y18, y19; + uint64_t y20, y21; + uint64_t z0, z1, z2, z3, z4, z5, z6, z7, z8, z9; + uint64_t z10, z11, z12, z13, z14, z15, z16, z17; + uint64_t t0, t1, t2, t3, t4, t5, t6, t7, t8, t9; + uint64_t t10, t11, t12, t13, t14, t15, t16, t17, t18, t19; + uint64_t t20, t21, t22, t23, t24, t25, t26, t27, t28, t29; + uint64_t t30, t31, t32, t33, t34, t35, t36, t37, t38, t39; + uint64_t t40, t41, t42, t43, t44, t45, t46, t47, t48, t49; + uint64_t t50, t51, t52, t53, t54, t55, t56, t57, t58, t59; + uint64_t t60, t61, t62, t63, t64, t65, t66, t67; + uint64_t s0, s1, s2, s3, s4, s5, s6, s7; + + x0 = q[7]; + x1 = q[6]; + x2 = q[5]; + x3 = q[4]; + x4 = q[3]; + x5 = q[2]; + x6 = q[1]; + x7 = q[0]; + + /* + * Top linear transformation. + */ + y14 = x3 ^ x5; + y13 = x0 ^ x6; + y9 = x0 ^ x3; + y8 = x0 ^ x5; + t0 = x1 ^ x2; + y1 = t0 ^ x7; + y4 = y1 ^ x3; + y12 = y13 ^ y14; + y2 = y1 ^ x0; + y5 = y1 ^ x6; + y3 = y5 ^ y8; + t1 = x4 ^ y12; + y15 = t1 ^ x5; + y20 = t1 ^ x1; + y6 = y15 ^ x7; + y10 = y15 ^ t0; + y11 = y20 ^ y9; + y7 = x7 ^ y11; + y17 = y10 ^ y11; + y19 = y10 ^ y8; + y16 = t0 ^ y11; + y21 = y13 ^ y16; + y18 = x0 ^ y16; + + /* + * Non-linear section. + */ + t2 = y12 & y15; + t3 = y3 & y6; + t4 = t3 ^ t2; + t5 = y4 & x7; + t6 = t5 ^ t2; + t7 = y13 & y16; + t8 = y5 & y1; + t9 = t8 ^ t7; + t10 = y2 & y7; + t11 = t10 ^ t7; + t12 = y9 & y11; + t13 = y14 & y17; + t14 = t13 ^ t12; + t15 = y8 & y10; + t16 = t15 ^ t12; + t17 = t4 ^ t14; + t18 = t6 ^ t16; + t19 = t9 ^ t14; + t20 = t11 ^ t16; + t21 = t17 ^ y20; + t22 = t18 ^ y19; + t23 = t19 ^ y21; + t24 = t20 ^ y18; + + t25 = t21 ^ t22; + t26 = t21 & t23; + t27 = t24 ^ t26; + t28 = t25 & t27; + t29 = t28 ^ t22; + t30 = t23 ^ t24; + t31 = t22 ^ t26; + t32 = t31 & t30; + t33 = t32 ^ t24; + t34 = t23 ^ t33; + t35 = t27 ^ t33; + t36 = t24 & t35; + t37 = t36 ^ t34; + t38 = t27 ^ t36; + t39 = t29 & t38; + t40 = t25 ^ t39; + + t41 = t40 ^ t37; + t42 = t29 ^ t33; + t43 = t29 ^ t40; + t44 = t33 ^ t37; + t45 = t42 ^ t41; + z0 = t44 & y15; + z1 = t37 & y6; + z2 = t33 & x7; + z3 = t43 & y16; + z4 = t40 & y1; + z5 = t29 & y7; + z6 = t42 & y11; + z7 = t45 & y17; + z8 = t41 & y10; + z9 = t44 & y12; + z10 = t37 & y3; + z11 = t33 & y4; + z12 = t43 & y13; + z13 = t40 & y5; + z14 = t29 & y2; + z15 = t42 & y9; + z16 = t45 & y14; + z17 = t41 & y8; + + /* + * Bottom linear transformation. + */ + t46 = z15 ^ z16; + t47 = z10 ^ z11; + t48 = z5 ^ z13; + t49 = z9 ^ z10; + t50 = z2 ^ z12; + t51 = z2 ^ z5; + t52 = z7 ^ z8; + t53 = z0 ^ z3; + t54 = z6 ^ z7; + t55 = z16 ^ z17; + t56 = z12 ^ t48; + t57 = t50 ^ t53; + t58 = z4 ^ t46; + t59 = z3 ^ t54; + t60 = t46 ^ t57; + t61 = z14 ^ t57; + t62 = t52 ^ t58; + t63 = t49 ^ t58; + t64 = z4 ^ t59; + t65 = t61 ^ t62; + t66 = z1 ^ t63; + s0 = t59 ^ t63; + s6 = t56 ^ ~t62; + s7 = t48 ^ ~t60; + t67 = t64 ^ t65; + s3 = t53 ^ t66; + s4 = t51 ^ t66; + s5 = t47 ^ t65; + s1 = t64 ^ ~s3; + s2 = t55 ^ ~t67; + + q[7] = s0; + q[6] = s1; + q[5] = s2; + q[4] = s3; + q[3] = s4; + q[2] = s5; + q[1] = s6; + q[0] = s7; +} + +static void br_aes_ct_bitslice_Sbox(uint32_t *q) { + /* + * This S-box implementation is a straightforward translation of + * the circuit described by Boyar and Peralta in "A new + * combinational logic minimization technique with applications + * to cryptology" (https://eprint.iacr.org/2009/191.pdf). + * + * Note that variables x* (input) and s* (output) are numbered + * in "reverse" order (x0 is the high bit, x7 is the low bit). + */ + + uint32_t x0, x1, x2, x3, x4, x5, x6, x7; + uint32_t y1, y2, y3, y4, y5, y6, y7, y8, y9; + uint32_t y10, y11, y12, y13, y14, y15, y16, y17, y18, y19; + uint32_t y20, y21; + uint32_t z0, z1, z2, z3, z4, z5, z6, z7, z8, z9; + uint32_t z10, z11, z12, z13, z14, z15, z16, z17; + uint32_t t0, t1, t2, t3, t4, t5, t6, t7, t8, t9; + uint32_t t10, t11, t12, t13, t14, t15, t16, t17, t18, t19; + uint32_t t20, t21, t22, t23, t24, t25, t26, t27, t28, t29; + uint32_t t30, t31, t32, t33, t34, t35, t36, t37, t38, t39; + uint32_t t40, t41, t42, t43, t44, t45, t46, t47, t48, t49; + uint32_t t50, t51, t52, t53, t54, t55, t56, t57, t58, t59; + uint32_t t60, t61, t62, t63, t64, t65, t66, t67; + uint32_t s0, s1, s2, s3, s4, s5, s6, s7; + + x0 = q[7]; + x1 = q[6]; + x2 = q[5]; + x3 = q[4]; + x4 = q[3]; + x5 = q[2]; + x6 = q[1]; + x7 = q[0]; + + /* + * Top linear transformation. + */ + y14 = x3 ^ x5; + y13 = x0 ^ x6; + y9 = x0 ^ x3; + y8 = x0 ^ x5; + t0 = x1 ^ x2; + y1 = t0 ^ x7; + y4 = y1 ^ x3; + y12 = y13 ^ y14; + y2 = y1 ^ x0; + y5 = y1 ^ x6; + y3 = y5 ^ y8; + t1 = x4 ^ y12; + y15 = t1 ^ x5; + y20 = t1 ^ x1; + y6 = y15 ^ x7; + y10 = y15 ^ t0; + y11 = y20 ^ y9; + y7 = x7 ^ y11; + y17 = y10 ^ y11; + y19 = y10 ^ y8; + y16 = t0 ^ y11; + y21 = y13 ^ y16; + y18 = x0 ^ y16; + + /* + * Non-linear section. + */ + t2 = y12 & y15; + t3 = y3 & y6; + t4 = t3 ^ t2; + t5 = y4 & x7; + t6 = t5 ^ t2; + t7 = y13 & y16; + t8 = y5 & y1; + t9 = t8 ^ t7; + t10 = y2 & y7; + t11 = t10 ^ t7; + t12 = y9 & y11; + t13 = y14 & y17; + t14 = t13 ^ t12; + t15 = y8 & y10; + t16 = t15 ^ t12; + t17 = t4 ^ t14; + t18 = t6 ^ t16; + t19 = t9 ^ t14; + t20 = t11 ^ t16; + t21 = t17 ^ y20; + t22 = t18 ^ y19; + t23 = t19 ^ y21; + t24 = t20 ^ y18; + + t25 = t21 ^ t22; + t26 = t21 & t23; + t27 = t24 ^ t26; + t28 = t25 & t27; + t29 = t28 ^ t22; + t30 = t23 ^ t24; + t31 = t22 ^ t26; + t32 = t31 & t30; + t33 = t32 ^ t24; + t34 = t23 ^ t33; + t35 = t27 ^ t33; + t36 = t24 & t35; + t37 = t36 ^ t34; + t38 = t27 ^ t36; + t39 = t29 & t38; + t40 = t25 ^ t39; + + t41 = t40 ^ t37; + t42 = t29 ^ t33; + t43 = t29 ^ t40; + t44 = t33 ^ t37; + t45 = t42 ^ t41; + z0 = t44 & y15; + z1 = t37 & y6; + z2 = t33 & x7; + z3 = t43 & y16; + z4 = t40 & y1; + z5 = t29 & y7; + z6 = t42 & y11; + z7 = t45 & y17; + z8 = t41 & y10; + z9 = t44 & y12; + z10 = t37 & y3; + z11 = t33 & y4; + z12 = t43 & y13; + z13 = t40 & y5; + z14 = t29 & y2; + z15 = t42 & y9; + z16 = t45 & y14; + z17 = t41 & y8; + + /* + * Bottom linear transformation. + */ + t46 = z15 ^ z16; + t47 = z10 ^ z11; + t48 = z5 ^ z13; + t49 = z9 ^ z10; + t50 = z2 ^ z12; + t51 = z2 ^ z5; + t52 = z7 ^ z8; + t53 = z0 ^ z3; + t54 = z6 ^ z7; + t55 = z16 ^ z17; + t56 = z12 ^ t48; + t57 = t50 ^ t53; + t58 = z4 ^ t46; + t59 = z3 ^ t54; + t60 = t46 ^ t57; + t61 = z14 ^ t57; + t62 = t52 ^ t58; + t63 = t49 ^ t58; + t64 = z4 ^ t59; + t65 = t61 ^ t62; + t66 = z1 ^ t63; + s0 = t59 ^ t63; + s6 = t56 ^ ~t62; + s7 = t48 ^ ~t60; + t67 = t64 ^ t65; + s3 = t53 ^ t66; + s4 = t51 ^ t66; + s5 = t47 ^ t65; + s1 = t64 ^ ~s3; + s2 = t55 ^ ~t67; + + q[7] = s0; + q[6] = s1; + q[5] = s2; + q[4] = s3; + q[3] = s4; + q[2] = s5; + q[1] = s6; + q[0] = s7; +} + +static void br_aes_ct_ortho(uint32_t *q) { +#define SWAPN_32(cl, ch, s, x, y) do { \ + uint32_t a, b; \ + a = (x); \ + b = (y); \ + (x) = (a & (uint32_t)(cl)) | ((b & (uint32_t)(cl)) << (s)); \ + (y) = ((a & (uint32_t)(ch)) >> (s)) | (b & (uint32_t)(ch)); \ + } while (0) + +#define SWAP2_32(x, y) SWAPN_32(0x55555555, 0xAAAAAAAA, 1, x, y) +#define SWAP4_32(x, y) SWAPN_32(0x33333333, 0xCCCCCCCC, 2, x, y) +#define SWAP8_32(x, y) SWAPN_32(0x0F0F0F0F, 0xF0F0F0F0, 4, x, y) + + SWAP2_32(q[0], q[1]); + SWAP2_32(q[2], q[3]); + SWAP2_32(q[4], q[5]); + SWAP2_32(q[6], q[7]); + + SWAP4_32(q[0], q[2]); + SWAP4_32(q[1], q[3]); + SWAP4_32(q[4], q[6]); + SWAP4_32(q[5], q[7]); + + SWAP8_32(q[0], q[4]); + SWAP8_32(q[1], q[5]); + SWAP8_32(q[2], q[6]); + SWAP8_32(q[3], q[7]); +} + +static inline void add_round_key32(uint32_t *q, const uint32_t *sk) { + q[0] ^= sk[0]; + q[1] ^= sk[1]; + q[2] ^= sk[2]; + q[3] ^= sk[3]; + q[4] ^= sk[4]; + q[5] ^= sk[5]; + q[6] ^= sk[6]; + q[7] ^= sk[7]; +} + +static inline void shift_rows32(uint32_t *q) { + int i; + + for (i = 0; i < 8; i++) { + uint32_t x; + + x = q[i]; + q[i] = (x & 0x000000FF) + | ((x & 0x0000FC00) >> 2) | ((x & 0x00000300) << 6) + | ((x & 0x00F00000) >> 4) | ((x & 0x000F0000) << 4) + | ((x & 0xC0000000) >> 6) | ((x & 0x3F000000) << 2); + } +} + +static inline uint32_t rotr16(uint32_t x) { + return (x << 16) | (x >> 16); +} + +static inline void mix_columns32(uint32_t *q) { + uint32_t q0, q1, q2, q3, q4, q5, q6, q7; + uint32_t r0, r1, r2, r3, r4, r5, r6, r7; + + q0 = q[0]; + q1 = q[1]; + q2 = q[2]; + q3 = q[3]; + q4 = q[4]; + q5 = q[5]; + q6 = q[6]; + q7 = q[7]; + r0 = (q0 >> 8) | (q0 << 24); + r1 = (q1 >> 8) | (q1 << 24); + r2 = (q2 >> 8) | (q2 << 24); + r3 = (q3 >> 8) | (q3 << 24); + r4 = (q4 >> 8) | (q4 << 24); + r5 = (q5 >> 8) | (q5 << 24); + r6 = (q6 >> 8) | (q6 << 24); + r7 = (q7 >> 8) | (q7 << 24); + + q[0] = q7 ^ r7 ^ r0 ^ rotr16(q0 ^ r0); + q[1] = q0 ^ r0 ^ q7 ^ r7 ^ r1 ^ rotr16(q1 ^ r1); + q[2] = q1 ^ r1 ^ r2 ^ rotr16(q2 ^ r2); + q[3] = q2 ^ r2 ^ q7 ^ r7 ^ r3 ^ rotr16(q3 ^ r3); + q[4] = q3 ^ r3 ^ q7 ^ r7 ^ r4 ^ rotr16(q4 ^ r4); + q[5] = q4 ^ r4 ^ r5 ^ rotr16(q5 ^ r5); + q[6] = q5 ^ r5 ^ r6 ^ rotr16(q6 ^ r6); + q[7] = q6 ^ r6 ^ r7 ^ rotr16(q7 ^ r7); +} + +static void br_aes_ct64_ortho(uint64_t *q) { +#define SWAPN(cl, ch, s, x, y) do { \ + uint64_t a, b; \ + a = (x); \ + b = (y); \ + (x) = (a & (uint64_t)(cl)) | ((b & (uint64_t)(cl)) << (s)); \ + (y) = ((a & (uint64_t)(ch)) >> (s)) | (b & (uint64_t)(ch)); \ + } while (0) + +#define SWAP2(x, y) SWAPN(0x5555555555555555, 0xAAAAAAAAAAAAAAAA, 1, x, y) +#define SWAP4(x, y) SWAPN(0x3333333333333333, 0xCCCCCCCCCCCCCCCC, 2, x, y) +#define SWAP8(x, y) SWAPN(0x0F0F0F0F0F0F0F0F, 0xF0F0F0F0F0F0F0F0, 4, x, y) + + SWAP2(q[0], q[1]); + SWAP2(q[2], q[3]); + SWAP2(q[4], q[5]); + SWAP2(q[6], q[7]); + + SWAP4(q[0], q[2]); + SWAP4(q[1], q[3]); + SWAP4(q[4], q[6]); + SWAP4(q[5], q[7]); + + SWAP8(q[0], q[4]); + SWAP8(q[1], q[5]); + SWAP8(q[2], q[6]); + SWAP8(q[3], q[7]); +} + + +static void br_aes_ct64_interleave_in(uint64_t *q0, uint64_t *q1, const uint32_t *w) { + uint64_t x0, x1, x2, x3; + + x0 = w[0]; + x1 = w[1]; + x2 = w[2]; + x3 = w[3]; + x0 |= (x0 << 16); + x1 |= (x1 << 16); + x2 |= (x2 << 16); + x3 |= (x3 << 16); + x0 &= (uint64_t)0x0000FFFF0000FFFF; + x1 &= (uint64_t)0x0000FFFF0000FFFF; + x2 &= (uint64_t)0x0000FFFF0000FFFF; + x3 &= (uint64_t)0x0000FFFF0000FFFF; + x0 |= (x0 << 8); + x1 |= (x1 << 8); + x2 |= (x2 << 8); + x3 |= (x3 << 8); + x0 &= (uint64_t)0x00FF00FF00FF00FF; + x1 &= (uint64_t)0x00FF00FF00FF00FF; + x2 &= (uint64_t)0x00FF00FF00FF00FF; + x3 &= (uint64_t)0x00FF00FF00FF00FF; + *q0 = x0 | (x2 << 8); + *q1 = x1 | (x3 << 8); +} + + +static void br_aes_ct64_interleave_out(uint32_t *w, uint64_t q0, uint64_t q1) { + uint64_t x0, x1, x2, x3; + + x0 = q0 & (uint64_t)0x00FF00FF00FF00FF; + x1 = q1 & (uint64_t)0x00FF00FF00FF00FF; + x2 = (q0 >> 8) & (uint64_t)0x00FF00FF00FF00FF; + x3 = (q1 >> 8) & (uint64_t)0x00FF00FF00FF00FF; + x0 |= (x0 >> 8); + x1 |= (x1 >> 8); + x2 |= (x2 >> 8); + x3 |= (x3 >> 8); + x0 &= (uint64_t)0x0000FFFF0000FFFF; + x1 &= (uint64_t)0x0000FFFF0000FFFF; + x2 &= (uint64_t)0x0000FFFF0000FFFF; + x3 &= (uint64_t)0x0000FFFF0000FFFF; + w[0] = (uint32_t)x0 | (uint32_t)(x0 >> 16); + w[1] = (uint32_t)x1 | (uint32_t)(x1 >> 16); + w[2] = (uint32_t)x2 | (uint32_t)(x2 >> 16); + w[3] = (uint32_t)x3 | (uint32_t)(x3 >> 16); +} + +static inline void add_round_key(uint64_t *q, const uint64_t *sk) { + q[0] ^= sk[0]; + q[1] ^= sk[1]; + q[2] ^= sk[2]; + q[3] ^= sk[3]; + q[4] ^= sk[4]; + q[5] ^= sk[5]; + q[6] ^= sk[6]; + q[7] ^= sk[7]; +} + +static inline void shift_rows(uint64_t *q) { + int i; + + for (i = 0; i < 8; i++) { + uint64_t x; + + x = q[i]; + q[i] = (x & (uint64_t)0x000000000000FFFF) + | ((x & (uint64_t)0x00000000FFF00000) >> 4) + | ((x & (uint64_t)0x00000000000F0000) << 12) + | ((x & (uint64_t)0x0000FF0000000000) >> 8) + | ((x & (uint64_t)0x000000FF00000000) << 8) + | ((x & (uint64_t)0xF000000000000000) >> 12) + | ((x & (uint64_t)0x0FFF000000000000) << 4); + } +} + +static inline uint64_t rotr32(uint64_t x) { + return (x << 32) | (x >> 32); +} + +static inline void mix_columns(uint64_t *q) { + uint64_t q0, q1, q2, q3, q4, q5, q6, q7; + uint64_t r0, r1, r2, r3, r4, r5, r6, r7; + + q0 = q[0]; + q1 = q[1]; + q2 = q[2]; + q3 = q[3]; + q4 = q[4]; + q5 = q[5]; + q6 = q[6]; + q7 = q[7]; + r0 = (q0 >> 16) | (q0 << 48); + r1 = (q1 >> 16) | (q1 << 48); + r2 = (q2 >> 16) | (q2 << 48); + r3 = (q3 >> 16) | (q3 << 48); + r4 = (q4 >> 16) | (q4 << 48); + r5 = (q5 >> 16) | (q5 << 48); + r6 = (q6 >> 16) | (q6 << 48); + r7 = (q7 >> 16) | (q7 << 48); + + q[0] = q7 ^ r7 ^ r0 ^ rotr32(q0 ^ r0); + q[1] = q0 ^ r0 ^ q7 ^ r7 ^ r1 ^ rotr32(q1 ^ r1); + q[2] = q1 ^ r1 ^ r2 ^ rotr32(q2 ^ r2); + q[3] = q2 ^ r2 ^ q7 ^ r7 ^ r3 ^ rotr32(q3 ^ r3); + q[4] = q3 ^ r3 ^ q7 ^ r7 ^ r4 ^ rotr32(q4 ^ r4); + q[5] = q4 ^ r4 ^ r5 ^ rotr32(q5 ^ r5); + q[6] = q5 ^ r5 ^ r6 ^ rotr32(q6 ^ r6); + q[7] = q6 ^ r6 ^ r7 ^ rotr32(q7 ^ r7); +} + +static void interleave_constant(uint64_t *out, const unsigned char *in) { + uint32_t tmp_32_constant[16]; + int i; + + br_range_dec32le(tmp_32_constant, 16, in); + for (i = 0; i < 4; i++) { + br_aes_ct64_interleave_in(&out[i], &out[i + 4], tmp_32_constant + (i << 2)); + } + br_aes_ct64_ortho(out); +} + +static void interleave_constant32(uint32_t *out, const unsigned char *in) { + int i; + for (i = 0; i < 4; i++) { + out[2 * i] = br_dec32le(in + 4 * i); + out[2 * i + 1] = br_dec32le(in + 4 * i + 16); + } + br_aes_ct_ortho(out); +} + +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_tweak_constants( + const unsigned char *pk_seed, const unsigned char *sk_seed, + unsigned long long seed_length) { + unsigned char buf[40 * 16]; + int i; + + /* Use the standard constants to generate tweaked ones. */ + memcpy((uint8_t *)tweaked512_rc64, (uint8_t *)haraka512_rc64, 40 * 16); + + /* Constants for sk.seed */ + if (sk_seed != NULL) { + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_haraka_S( + buf, 40 * 16, sk_seed, seed_length); + + /* Interleave constants */ + for (i = 0; i < 10; i++) { + interleave_constant32(tweaked256_rc32_sseed[i], buf + 32 * i); + } + } + + /* Constants for pk.seed */ + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_haraka_S( + buf, 40 * 16, pk_seed, seed_length); + for (i = 0; i < 10; i++) { + interleave_constant32(tweaked256_rc32[i], buf + 32 * i); + interleave_constant(tweaked512_rc64[i], buf + 64 * i); + } +} + +static void haraka_S_absorb(unsigned char *s, + const unsigned char *m, unsigned long long mlen, + unsigned char p) { + unsigned long long i; + unsigned char t[HARAKAS_RATE]; + + while (mlen >= HARAKAS_RATE) { + /* XOR block to state */ + for (i = 0; i < HARAKAS_RATE; ++i) { + s[i] ^= m[i]; + } + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_haraka512_perm(s, s); + mlen -= HARAKAS_RATE; + m += HARAKAS_RATE; + } + + for (i = 0; i < HARAKAS_RATE; ++i) { + t[i] = 0; + } + for (i = 0; i < mlen; ++i) { + t[i] = m[i]; + } + t[i] = p; + t[HARAKAS_RATE - 1] |= 128; + for (i = 0; i < HARAKAS_RATE; ++i) { + s[i] ^= t[i]; + } +} + +static void haraka_S_squeezeblocks(unsigned char *h, unsigned long long nblocks, + unsigned char *s) { + while (nblocks > 0) { + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_haraka512_perm(s, s); + memcpy(h, s, HARAKAS_RATE); + h += HARAKAS_RATE; + nblocks--; + } +} + +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_haraka_S_inc_init(uint8_t *s_inc) { + size_t i; + + for (i = 0; i < 64; i++) { + s_inc[i] = 0; + } + s_inc[64] = 0; +} + +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_haraka_S_inc_absorb(uint8_t *s_inc, const uint8_t *m, size_t mlen) { + size_t i; + + /* Recall that s_inc[64] is the non-absorbed bytes xored into the state */ + while (mlen + s_inc[64] >= HARAKAS_RATE) { + for (i = 0; i < (size_t)(HARAKAS_RATE - s_inc[64]); i++) { + /* Take the i'th byte from message + xor with the s_inc[64] + i'th byte of the state */ + s_inc[s_inc[64] + i] ^= m[i]; + } + mlen -= (size_t)(HARAKAS_RATE - s_inc[64]); + m += HARAKAS_RATE - s_inc[64]; + s_inc[64] = 0; + + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_haraka512_perm(s_inc, s_inc); + } + + for (i = 0; i < mlen; i++) { + s_inc[s_inc[64] + i] ^= m[i]; + } + s_inc[64] = (uint8_t)(mlen + s_inc[64]); +} + +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_haraka_S_inc_finalize(uint8_t *s_inc) { + /* After haraka_S_inc_absorb, we are guaranteed that s_inc[64] < HARAKAS_RATE, + so we can always use one more byte for p in the current state. */ + s_inc[s_inc[64]] ^= 0x1F; + s_inc[HARAKAS_RATE - 1] ^= 128; + s_inc[64] = 0; +} + +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_haraka_S_inc_squeeze(uint8_t *out, size_t outlen, uint8_t *s_inc) { + uint8_t i; + + /* First consume any bytes we still have sitting around */ + for (i = 0; i < outlen && i < s_inc[64]; i++) { + /* There are s_inc[64] bytes left, so r - s_inc[64] is the first + available byte. We consume from there, i.e., up to r. */ + out[i] = s_inc[(HARAKAS_RATE - s_inc[64] + i)]; + } + out += i; + outlen -= i; + s_inc[64] = (uint8_t)(s_inc[64] - i); + + /* Then squeeze the remaining necessary blocks */ + while (outlen > 0) { + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_haraka512_perm(s_inc, s_inc); + + for (i = 0; i < outlen && i < HARAKAS_RATE; i++) { + out[i] = s_inc[i]; + } + out += i; + outlen -= i; + s_inc[64] = (uint8_t)(HARAKAS_RATE - i); + } +} + +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_haraka_S(unsigned char *out, unsigned long long outlen, const unsigned char *in, unsigned long long inlen) { + unsigned long long i; + unsigned char s[64]; + unsigned char d[32]; + + for (i = 0; i < 64; i++) { + s[i] = 0; + } + haraka_S_absorb(s, in, inlen, 0x1F); + + haraka_S_squeezeblocks(out, outlen / 32, s); + out += (outlen / 32) * 32; + + if (outlen % 32) { + haraka_S_squeezeblocks(d, 1, s); + for (i = 0; i < outlen % 32; i++) { + out[i] = d[i]; + } + } +} + +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_haraka512_perm(unsigned char *out, const unsigned char *in) { + uint32_t w[16]; + uint64_t q[8], tmp_q; + unsigned int i, j; + + br_range_dec32le(w, 16, in); + for (i = 0; i < 4; i++) { + br_aes_ct64_interleave_in(&q[i], &q[i + 4], w + (i << 2)); + } + br_aes_ct64_ortho(q); + + /* AES rounds */ + for (i = 0; i < 5; i++) { + for (j = 0; j < 2; j++) { + br_aes_ct64_bitslice_Sbox(q); + shift_rows(q); + mix_columns(q); + add_round_key(q, tweaked512_rc64[2 * i + j]); + } + /* Mix states */ + for (j = 0; j < 8; j++) { + tmp_q = q[j]; + q[j] = (tmp_q & 0x0001000100010001) << 5 | + (tmp_q & 0x0002000200020002) << 12 | + (tmp_q & 0x0004000400040004) >> 1 | + (tmp_q & 0x0008000800080008) << 6 | + (tmp_q & 0x0020002000200020) << 9 | + (tmp_q & 0x0040004000400040) >> 4 | + (tmp_q & 0x0080008000800080) << 3 | + (tmp_q & 0x2100210021002100) >> 5 | + (tmp_q & 0x0210021002100210) << 2 | + (tmp_q & 0x0800080008000800) << 4 | + (tmp_q & 0x1000100010001000) >> 12 | + (tmp_q & 0x4000400040004000) >> 10 | + (tmp_q & 0x8400840084008400) >> 3; + } + } + + br_aes_ct64_ortho(q); + for (i = 0; i < 4; i ++) { + br_aes_ct64_interleave_out(w + (i << 2), q[i], q[i + 4]); + } + br_range_enc32le(out, w, 16); +} + +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_haraka512(unsigned char *out, const unsigned char *in) { + int i; + + unsigned char buf[64]; + + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_haraka512_perm(buf, in); + /* Feed-forward */ + for (i = 0; i < 64; i++) { + buf[i] = buf[i] ^ in[i]; + } + + /* Truncated */ + memcpy(out, buf + 8, 8); + memcpy(out + 8, buf + 24, 8); + memcpy(out + 16, buf + 32, 8); + memcpy(out + 24, buf + 48, 8); +} + + +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_haraka256(unsigned char *out, const unsigned char *in) { + uint32_t q[8], tmp_q; + int i, j; + + for (i = 0; i < 4; i++) { + q[2 * i] = br_dec32le(in + 4 * i); + q[2 * i + 1] = br_dec32le(in + 4 * i + 16); + } + br_aes_ct_ortho(q); + + /* AES rounds */ + for (i = 0; i < 5; i++) { + for (j = 0; j < 2; j++) { + br_aes_ct_bitslice_Sbox(q); + shift_rows32(q); + mix_columns32(q); + add_round_key32(q, tweaked256_rc32[2 * i + j]); + } + + /* Mix states */ + for (j = 0; j < 8; j++) { + tmp_q = q[j]; + q[j] = (tmp_q & 0x81818181) | + (tmp_q & 0x02020202) << 1 | + (tmp_q & 0x04040404) << 2 | + (tmp_q & 0x08080808) << 3 | + (tmp_q & 0x10101010) >> 3 | + (tmp_q & 0x20202020) >> 2 | + (tmp_q & 0x40404040) >> 1; + } + } + + br_aes_ct_ortho(q); + for (i = 0; i < 4; i++) { + br_enc32le(out + 4 * i, q[2 * i]); + br_enc32le(out + 4 * i + 16, q[2 * i + 1]); + } + + for (i = 0; i < 32; i++) { + out[i] ^= in[i]; + } +} + +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_haraka256_sk(unsigned char *out, const unsigned char *in) { + uint32_t q[8], tmp_q; + int i, j; + + for (i = 0; i < 4; i++) { + q[2 * i] = br_dec32le(in + 4 * i); + q[2 * i + 1] = br_dec32le(in + 4 * i + 16); + } + br_aes_ct_ortho(q); + + /* AES rounds */ + for (i = 0; i < 5; i++) { + for (j = 0; j < 2; j++) { + br_aes_ct_bitslice_Sbox(q); + shift_rows32(q); + mix_columns32(q); + add_round_key32(q, tweaked256_rc32_sseed[2 * i + j]); + } + + /* Mix states */ + for (j = 0; j < 8; j++) { + tmp_q = q[j]; + q[j] = (tmp_q & 0x81818181) | + (tmp_q & 0x02020202) << 1 | + (tmp_q & 0x04040404) << 2 | + (tmp_q & 0x08080808) << 3 | + (tmp_q & 0x10101010) >> 3 | + (tmp_q & 0x20202020) >> 2 | + (tmp_q & 0x40404040) >> 1; + } + } + + br_aes_ct_ortho(q); + for (i = 0; i < 4; i++) { + br_enc32le(out + 4 * i, q[2 * i]); + br_enc32le(out + 4 * i + 16, q[2 * i + 1]); + } + + for (i = 0; i < 32; i++) { + out[i] ^= in[i]; + } +} diff --git a/crypto_sign/sphincs-haraka-256s-simple/clean/haraka.h b/crypto_sign/sphincs-haraka-256s-simple/clean/haraka.h new file mode 100644 index 00000000..56d02197 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256s-simple/clean/haraka.h @@ -0,0 +1,30 @@ +#ifndef SPX_HARAKA_H +#define SPX_HARAKA_H + +/* Tweak constants with seed */ +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_tweak_constants( + const unsigned char *pk_seed, const unsigned char *sk_seed, + unsigned long long seed_length); + +/* Haraka Sponge */ +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_haraka_S_inc_init(uint8_t *s_inc); +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_haraka_S_inc_absorb(uint8_t *s_inc, const uint8_t *m, size_t mlen); +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_haraka_S_inc_finalize(uint8_t *s_inc); +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_haraka_S_inc_squeeze(uint8_t *out, size_t outlen, uint8_t *s_inc); +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_haraka_S( + unsigned char *out, unsigned long long outlen, + const unsigned char *in, unsigned long long inlen); + +/* Applies the 512-bit Haraka permutation to in. */ +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_haraka512_perm(unsigned char *out, const unsigned char *in); + +/* Implementation of Haraka-512 */ +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_haraka512(unsigned char *out, const unsigned char *in); + +/* Implementation of Haraka-256 */ +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_haraka256(unsigned char *out, const unsigned char *in); + +/* Implementation of Haraka-256 using sk.seed constants */ +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_haraka256_sk(unsigned char *out, const unsigned char *in); + +#endif diff --git a/crypto_sign/sphincs-haraka-256s-simple/clean/hash.h b/crypto_sign/sphincs-haraka-256s-simple/clean/hash.h new file mode 100644 index 00000000..0e799850 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256s-simple/clean/hash.h @@ -0,0 +1,22 @@ +#ifndef SPX_HASH_H +#define SPX_HASH_H + +#include + +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed); + +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen); + +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen); + +#endif diff --git a/crypto_sign/sphincs-haraka-256s-simple/clean/hash_haraka.c b/crypto_sign/sphincs-haraka-256s-simple/clean/hash_haraka.c new file mode 100644 index 00000000..eac03302 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256s-simple/clean/hash_haraka.c @@ -0,0 +1,86 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "utils.h" + +#include "haraka.h" + +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed) { + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_tweak_constants(pub_seed, sk_seed, SPX_N); +} + +/* + * Computes PRF(key, addr), given a secret key of SPX_N bytes and an address + */ +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]) { + unsigned char buf[SPX_ADDR_BYTES]; + /* Since SPX_N may be smaller than 32, we need a temporary buffer. */ + unsigned char outbuf[32]; + + (void)key; /* Suppress an 'unused parameter' warning. */ + + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_addr_to_bytes(buf, addr); + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_haraka256_sk(outbuf, buf); + memcpy(out, outbuf, SPX_N); +} + +/** + * Computes the message-dependent randomness R, using a secret seed and an + * optional randomization value as well as the message. + */ +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen) { + uint8_t s_inc[65]; + + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_haraka_S_inc_init(s_inc); + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_haraka_S_inc_absorb(s_inc, sk_prf, SPX_N); + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_haraka_S_inc_absorb(s_inc, optrand, SPX_N); + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_haraka_S_inc_absorb(s_inc, m, mlen); + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_haraka_S_inc_finalize(s_inc); + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_haraka_S_inc_squeeze(R, SPX_N, s_inc); +} + +/** + * Computes the message hash using R, the public key, and the message. + * Outputs the message digest and the index of the leaf. The index is split in + * the tree index and the leaf index, for convenient copying to an address. + */ +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen) { +#define SPX_TREE_BITS (SPX_TREE_HEIGHT * (SPX_D - 1)) +#define SPX_TREE_BYTES ((SPX_TREE_BITS + 7) / 8) +#define SPX_LEAF_BITS SPX_TREE_HEIGHT +#define SPX_LEAF_BYTES ((SPX_LEAF_BITS + 7) / 8) +#define SPX_DGST_BYTES (SPX_FORS_MSG_BYTES + SPX_TREE_BYTES + SPX_LEAF_BYTES) + + unsigned char buf[SPX_DGST_BYTES]; + unsigned char *bufp = buf; + uint8_t s_inc[65]; + + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_haraka_S_inc_init(s_inc); + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_haraka_S_inc_absorb(s_inc, R, SPX_N); + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_haraka_S_inc_absorb(s_inc, pk, SPX_PK_BYTES); + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_haraka_S_inc_absorb(s_inc, m, mlen); + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_haraka_S_inc_finalize(s_inc); + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_haraka_S_inc_squeeze(buf, SPX_DGST_BYTES, s_inc); + + memcpy(digest, bufp, SPX_FORS_MSG_BYTES); + bufp += SPX_FORS_MSG_BYTES; + + *tree = PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_bytes_to_ull(bufp, SPX_TREE_BYTES); + *tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS); + bufp += SPX_TREE_BYTES; + + *leaf_idx = (uint32_t)PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_bytes_to_ull( + bufp, SPX_LEAF_BYTES); + *leaf_idx &= (~(uint32_t)0) >> (32 - SPX_LEAF_BITS); +} diff --git a/crypto_sign/sphincs-haraka-256s-simple/clean/params.h b/crypto_sign/sphincs-haraka-256s-simple/clean/params.h new file mode 100644 index 00000000..f023d687 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256s-simple/clean/params.h @@ -0,0 +1,53 @@ +#ifndef SPX_PARAMS_H +#define SPX_PARAMS_H + +/* Hash output length in bytes. */ +#define SPX_N 32 +/* Height of the hypertree. */ +#define SPX_FULL_HEIGHT 64 +/* Number of subtree layer. */ +#define SPX_D 8 +/* FORS tree dimensions. */ +#define SPX_FORS_HEIGHT 14 +#define SPX_FORS_TREES 22 +/* Winternitz parameter, */ +#define SPX_WOTS_W 16 + +/* The hash function is defined by linking a different hash.c file, as opposed + to setting a #define constant. */ + +/* For clarity */ +#define SPX_ADDR_BYTES 32 + +/* WOTS parameters. */ +#define SPX_WOTS_LOGW 4 + +#define SPX_WOTS_LEN1 (8 * SPX_N / SPX_WOTS_LOGW) + +/* SPX_WOTS_LEN2 is floor(log(len_1 * (w - 1)) / log(w)) + 1; we precompute */ +#define SPX_WOTS_LEN2 3 + +#define SPX_WOTS_LEN (SPX_WOTS_LEN1 + SPX_WOTS_LEN2) +#define SPX_WOTS_BYTES (SPX_WOTS_LEN * SPX_N) +#define SPX_WOTS_PK_BYTES SPX_WOTS_BYTES + +/* Subtree size. */ +#define SPX_TREE_HEIGHT (SPX_FULL_HEIGHT / SPX_D) + +/* FORS parameters. */ +#define SPX_FORS_MSG_BYTES ((SPX_FORS_HEIGHT * SPX_FORS_TREES + 7) / 8) +#define SPX_FORS_BYTES ((SPX_FORS_HEIGHT + 1) * SPX_FORS_TREES * SPX_N) +#define SPX_FORS_PK_BYTES SPX_N + +/* Resulting SPX sizes. */ +#define SPX_BYTES (SPX_N + SPX_FORS_BYTES + SPX_D * SPX_WOTS_BYTES +\ + SPX_FULL_HEIGHT * SPX_N) +#define SPX_PK_BYTES (2 * SPX_N) +#define SPX_SK_BYTES (2 * SPX_N + SPX_PK_BYTES) + +/* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ +#define SPX_OPTRAND_BYTES 32 + +#endif diff --git a/crypto_sign/sphincs-haraka-256s-simple/clean/sign.c b/crypto_sign/sphincs-haraka-256s-simple/clean/sign.c new file mode 100644 index 00000000..83b28bd1 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256s-simple/clean/sign.c @@ -0,0 +1,344 @@ +#include +#include +#include + +#include "address.h" +#include "api.h" +#include "fors.h" +#include "hash.h" +#include "params.h" +#include "randombytes.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +/** + * Computes the leaf at a given address. First generates the WOTS key pair, + * then computes leaf by hashing horizontally. + */ +static void wots_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t tree_addr[8]) { + unsigned char pk[SPX_WOTS_BYTES]; + uint32_t wots_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, addr_idx); + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_wots_gen_pk( + pk, sk_seed, pub_seed, wots_addr); + + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_thash_WOTS_LEN( + leaf, pk, pub_seed, wots_pk_addr); +} + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_crypto_sign_secretkeybytes(void) { + return PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_CRYPTO_SECRETKEYBYTES; +} + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_crypto_sign_publickeybytes(void) { + return PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES; +} + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_crypto_sign_bytes(void) { + return PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_CRYPTO_BYTES; +} + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_crypto_sign_seedbytes(void) { + return PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_CRYPTO_SEEDBYTES; +} + +/* + * Generates an SPX key pair given a seed of length + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed) { + /* We do not need the auth path in key generation, but it simplifies the + code to have just one treehash routine that computes both root and path + in one function. */ + unsigned char auth_path[SPX_TREE_HEIGHT * SPX_N]; + uint32_t top_tree_addr[8] = {0}; + + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_layer_addr( + top_tree_addr, SPX_D - 1); + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_type( + top_tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */ + memcpy(sk, seed, PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_CRYPTO_SEEDBYTES); + + memcpy(pk, sk + 2 * SPX_N, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_initialize_hash_function(pk, sk); + + /* Compute root node of the top-most subtree. */ + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_treehash_TREE_HEIGHT( + sk + 3 * SPX_N, auth_path, sk, sk + 2 * SPX_N, 0, 0, + wots_gen_leaf, top_tree_addr); + + memcpy(pk + SPX_N, sk + 3 * SPX_N, SPX_N); + + return 0; +} + +/* + * Generates an SPX key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk) { + unsigned char seed[PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_CRYPTO_SEEDBYTES]; + randombytes(seed, PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_CRYPTO_SEEDBYTES); + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_crypto_sign_seed_keypair( + pk, sk, seed); + + return 0; +} + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + const unsigned char *sk_seed = sk; + const unsigned char *sk_prf = sk + SPX_N; + const unsigned char *pk = sk + 2 * SPX_N; + const unsigned char *pub_seed = pk; + + unsigned char optrand[SPX_N]; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char root[SPX_N]; + uint32_t i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_initialize_hash_function( + pub_seed, sk_seed); + + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ + randombytes(optrand, SPX_N); + /* Compute the digest randomization value. */ + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_gen_message_random( + sig, sk_prf, optrand, m, mlen); + + /* Derive the message digest and leaf index from R, PK and M. */ + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Sign the message hash using FORS. */ + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_fors_sign( + sig, root, mhash, sk_seed, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Compute a WOTS signature. */ + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_wots_sign( + sig, root, sk_seed, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the authentication path for the used WOTS leaf. */ + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_treehash_TREE_HEIGHT( + root, sig, sk_seed, pub_seed, idx_leaf, 0, + wots_gen_leaf, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + *siglen = SPX_BYTES; + + return 0; +} + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk) { + const unsigned char *pub_seed = pk; + const unsigned char *pub_root = pk + SPX_N; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char wots_pk[SPX_WOTS_BYTES]; + unsigned char root[SPX_N]; + unsigned char leaf[SPX_N]; + unsigned int i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + if (siglen != SPX_BYTES) { + return -1; + } + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_initialize_hash_function( + pub_seed, NULL); + + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + /* Derive the message digest and leaf index from R || PK || M. */ + /* The additional SPX_N is a result of the hash domain separator. */ + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + /* Layer correctly defaults to 0, so no need to set_layer_addr */ + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_fors_pk_from_sig( + root, sig, mhash, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + /* For each subtree.. */ + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + + /* The WOTS public key is only correct if the signature was correct. */ + /* Initially, root is the FORS pk, but on subsequent iterations it is + the root of the subtree below the currently processed subtree. */ + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_wots_pk_from_sig( + wots_pk, sig, root, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the leaf node using the WOTS public key. */ + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_thash_WOTS_LEN( + leaf, wots_pk, pub_seed, wots_pk_addr); + + /* Compute the root node of this subtree. */ + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_compute_root( + root, leaf, idx_leaf, 0, sig, SPX_TREE_HEIGHT, + pub_seed, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + /* Check if the root node equals the root node in the public key. */ + if (memcmp(root, pub_root, SPX_N) != 0) { + return -1; + } + + return 0; +} + + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + size_t siglen; + + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_crypto_sign_signature( + sm, &siglen, m, mlen, sk); + + memmove(sm + SPX_BYTES, m, mlen); + *smlen = siglen + mlen; + + return 0; +} + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk) { + /* The API caller does not necessarily know what size a signature should be + but SPHINCS+ signatures are always exactly SPX_BYTES. */ + if (smlen < SPX_BYTES) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + *mlen = smlen - SPX_BYTES; + + if (PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_crypto_sign_verify( + sm, SPX_BYTES, sm + SPX_BYTES, *mlen, pk)) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + /* If verification was successful, move the message to the right place. */ + memmove(m, sm + SPX_BYTES, *mlen); + + return 0; +} diff --git a/crypto_sign/sphincs-haraka-256s-simple/clean/thash.h b/crypto_sign/sphincs-haraka-256s-simple/clean/thash.h new file mode 100644 index 00000000..fa9b7f08 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256s-simple/clean/thash.h @@ -0,0 +1,22 @@ +#ifndef SPX_THASH_H +#define SPX_THASH_H + +#include + +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-haraka-256s-simple/clean/thash_haraka_simple.c b/crypto_sign/sphincs-haraka-256s-simple/clean/thash_haraka_simple.c new file mode 100644 index 00000000..4d5a905d --- /dev/null +++ b/crypto_sign/sphincs-haraka-256s-simple/clean/thash_haraka_simple.c @@ -0,0 +1,78 @@ +#include +#include + +#include "address.h" +#include "params.h" +#include "thash.h" + +#include "haraka.h" + +/** + * Takes an array of inblocks concatenated arrays of SPX_N bytes. + */ +static void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_thash( + unsigned char *out, unsigned char *buf, + const unsigned char *in, unsigned int inblocks, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char outbuf[32]; + unsigned char buf_tmp[64]; + + (void)pub_seed; /* Suppress an 'unused parameter' warning. */ + + if (inblocks == 1) { + /* F function */ + /* Since SPX_N may be smaller than 32, we need a temporary buffer. */ + memset(buf_tmp, 0, 64); + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_addr_to_bytes(buf_tmp, addr); + memcpy(buf_tmp + SPX_ADDR_BYTES, in, SPX_N); + + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_haraka512(outbuf, buf_tmp); + memcpy(out, outbuf, SPX_N); + } else { + /* All other tweakable hashes*/ + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_addr_to_bytes(buf, addr); + memcpy(buf + SPX_ADDR_BYTES, in, inblocks * SPX_N); + + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_haraka_S( + out, SPX_N, buf, SPX_ADDR_BYTES + inblocks * SPX_N); + } +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_ADDR_BYTES + 1 * SPX_N]; + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_thash( + out, buf, in, 1, pub_seed, addr); +} + +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_ADDR_BYTES + 2 * SPX_N]; + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_thash( + out, buf, in, 2, pub_seed, addr); +} + +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_ADDR_BYTES + SPX_WOTS_LEN * SPX_N]; + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_thash( + out, buf, in, SPX_WOTS_LEN, pub_seed, addr); +} + +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_ADDR_BYTES + SPX_FORS_TREES * SPX_N]; + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_thash( + out, buf, in, SPX_FORS_TREES, pub_seed, addr); +} diff --git a/crypto_sign/sphincs-haraka-256s-simple/clean/utils.c b/crypto_sign/sphincs-haraka-256s-simple/clean/utils.c new file mode 100644 index 00000000..5a1ae891 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256s-simple/clean/utils.c @@ -0,0 +1,192 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in) { + + /* Iterate over out in decreasing order, for big-endianness. */ + for (size_t i = outlen; i > 0; i--) { + out[i - 1] = in & 0xff; + in = in >> 8; + } +} + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen) { + unsigned long long retval = 0; + + for (size_t i = 0; i < inlen; i++) { + retval |= ((unsigned long long)in[i]) << (8 * (inlen - 1 - i)); + } + return retval; +} + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + unsigned char buffer[2 * SPX_N]; + + /* If leaf_idx is odd (last bit = 1), current path element is a right child + and auth_path has to go left. Otherwise it is the other way around. */ + if (leaf_idx & 1) { + memcpy(buffer + SPX_N, leaf, SPX_N); + memcpy(buffer, auth_path, SPX_N); + } else { + memcpy(buffer, leaf, SPX_N); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + + for (i = 0; i < tree_height - 1; i++) { + leaf_idx >>= 1; + idx_offset >>= 1; + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_tree_height(addr, i + 1); + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + + /* Pick the right or left neighbor, depending on parity of the node. */ + if (leaf_idx & 1) { + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_thash_2( + buffer + SPX_N, buffer, pub_seed, addr); + memcpy(buffer, auth_path, SPX_N); + } else { + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_thash_2( + buffer, buffer, pub_seed, addr); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + } + + /* The last iteration is exceptional; we do not copy an auth_path node. */ + leaf_idx >>= 1; + idx_offset >>= 1; + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_tree_height(addr, tree_height); + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_thash_2( + root, buffer, pub_seed, addr); +} + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +static void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_treehash( + unsigned char *root, unsigned char *auth_path, + unsigned char *stack, unsigned int *heights, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned int offset = 0; + uint32_t idx; + uint32_t tree_idx; + + for (idx = 0; idx < (uint32_t)(1 << tree_height); idx++) { + /* Add the next leaf node to the stack. */ + gen_leaf(stack + offset * SPX_N, + sk_seed, pub_seed, idx + idx_offset, tree_addr); + offset++; + heights[offset - 1] = 0; + + /* If this is a node we need for the auth path.. */ + if ((leaf_idx ^ 0x1) == idx) { + memcpy(auth_path, stack + (offset - 1)*SPX_N, SPX_N); + } + + /* While the top-most nodes are of equal height.. */ + while (offset >= 2 && heights[offset - 1] == heights[offset - 2]) { + /* Compute index of the new node, in the next layer. */ + tree_idx = (idx >> (heights[offset - 1] + 1)); + + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_tree_height( + tree_addr, heights[offset - 1] + 1); + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_tree_index( + tree_addr, tree_idx + (idx_offset >> (heights[offset - 1] + 1))); + /* Hash the top-most nodes from the stack together. */ + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_thash_2( + stack + (offset - 2)*SPX_N, stack + (offset - 2)*SPX_N, + pub_seed, tree_addr); + offset--; + /* Note that the top-most node is now one layer higher. */ + heights[offset - 1]++; + + /* If this is a node we need for the auth path.. */ + if (((leaf_idx >> heights[offset - 1]) ^ 0x1) == tree_idx) { + memcpy(auth_path + heights[offset - 1]*SPX_N, + stack + (offset - 1)*SPX_N, SPX_N); + } + } + } + memcpy(root, stack, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_FORS_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_FORS_HEIGHT + 1]; + + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_FORS_HEIGHT, gen_leaf, tree_addr); +} + +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_TREE_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_TREE_HEIGHT + 1]; + + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_TREE_HEIGHT, gen_leaf, tree_addr); +} diff --git a/crypto_sign/sphincs-haraka-256s-simple/clean/utils.h b/crypto_sign/sphincs-haraka-256s-simple/clean/utils.h new file mode 100644 index 00000000..ea9e9031 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256s-simple/clean/utils.h @@ -0,0 +1,60 @@ +#ifndef SPX_UTILS_H +#define SPX_UTILS_H + +#include "params.h" +#include +#include + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in); + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen); + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-haraka-256s-simple/clean/wots.c b/crypto_sign/sphincs-haraka-256s-simple/clean/wots.c new file mode 100644 index 00000000..cdaf4501 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256s-simple/clean/wots.c @@ -0,0 +1,161 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +// TODO clarify address expectations, and make them more uniform. +// TODO i.e. do we expect types to be set already? +// TODO and do we expect modifications or copies? + +/** + * Computes the starting value for a chain, i.e. the secret key. + * Expects the address to be complete up to the chain address. + */ +static void wots_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t wots_addr[8]) { + /* Make sure that the hash address is actually zeroed. */ + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_hash_addr(wots_addr, 0); + + /* Generate sk element. */ + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_prf_addr(sk, sk_seed, wots_addr); +} + +/** + * Computes the chaining function. + * out and in have to be n-byte arrays. + * + * Interprets in as start-th value of the chain. + * addr has to contain the address of the chain. + */ +static void gen_chain(unsigned char *out, const unsigned char *in, + unsigned int start, unsigned int steps, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + /* Initialize out with the value at position 'start'. */ + memcpy(out, in, SPX_N); + + /* Iterate 'steps' calls to the hash function. */ + for (i = start; i < (start + steps) && i < SPX_WOTS_W; i++) { + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_hash_addr(addr, i); + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_thash_1( + out, out, pub_seed, addr); + } +} + +/** + * base_w algorithm as described in draft. + * Interprets an array of bytes as integers in base w. + * This only works when log_w is a divisor of 8. + */ +static void base_w(unsigned int *output, const size_t out_len, + const unsigned char *input) { + size_t in = 0; + size_t out = 0; + unsigned char total = 0; + unsigned int bits = 0; + size_t consumed; + + for (consumed = 0; consumed < out_len; consumed++) { + if (bits == 0) { + total = input[in]; + in++; + bits += 8; + } + bits -= SPX_WOTS_LOGW; + output[out] = (unsigned int)((total >> bits) & (SPX_WOTS_W - 1)); + out++; + } +} + +/* Computes the WOTS+ checksum over a message (in base_w). */ +static void wots_checksum(unsigned int *csum_base_w, + const unsigned int *msg_base_w) { + unsigned int csum = 0; + unsigned char csum_bytes[(SPX_WOTS_LEN2 * SPX_WOTS_LOGW + 7) / 8]; + unsigned int i; + + /* Compute checksum. */ + for (i = 0; i < SPX_WOTS_LEN1; i++) { + csum += SPX_WOTS_W - 1 - msg_base_w[i]; + } + + /* Convert checksum to base_w. */ + /* Make sure expected empty zero bits are the least significant bits. */ + csum = csum << (8 - ((SPX_WOTS_LEN2 * SPX_WOTS_LOGW) % 8)); + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_ull_to_bytes( + csum_bytes, sizeof(csum_bytes), csum); + base_w(csum_base_w, SPX_WOTS_LEN2, csum_bytes); +} + +/* Takes a message and derives the matching chain lengths. */ +static void chain_lengths(unsigned int *lengths, const unsigned char *msg) { + base_w(lengths, SPX_WOTS_LEN1, msg); + wots_checksum(lengths + SPX_WOTS_LEN1, lengths); +} + +/** + * WOTS key generation. Takes a 32 byte sk_seed, expands it to WOTS private key + * elements and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(pk + i * SPX_N, sk_seed, addr); + gen_chain(pk + i * SPX_N, pk + i * SPX_N, + 0, SPX_WOTS_W - 1, pub_seed, addr); + } +} + +/** + * Takes a n-byte message and the 32-byte sk_see to compute a signature 'sig'. + */ +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(sig + i * SPX_N, sk_seed, addr); + gen_chain(sig + i * SPX_N, sig + i * SPX_N, 0, lengths[i], pub_seed, addr); + } +} + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_set_chain_addr(addr, i); + gen_chain(pk + i * SPX_N, sig + i * SPX_N, + lengths[i], SPX_WOTS_W - 1 - lengths[i], pub_seed, addr); + } +} diff --git a/crypto_sign/sphincs-haraka-256s-simple/clean/wots.h b/crypto_sign/sphincs-haraka-256s-simple/clean/wots.h new file mode 100644 index 00000000..22d300da --- /dev/null +++ b/crypto_sign/sphincs-haraka-256s-simple/clean/wots.h @@ -0,0 +1,38 @@ +#ifndef SPX_WOTS_H +#define SPX_WOTS_H + +#include "params.h" +#include + +/** + * WOTS key generation. Takes a 32 byte seed for the private key, expands it to + * a full WOTS private key and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * Takes a n-byte message and the 32-byte seed for the private key to compute a + * signature that is placed at 'sig'. + */ +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]); + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-sha256-128f-robust/META.yml b/crypto_sign/sphincs-sha256-128f-robust/META.yml new file mode 100644 index 00000000..a352f117 --- /dev/null +++ b/crypto_sign/sphincs-sha256-128f-robust/META.yml @@ -0,0 +1,27 @@ +name: SPHINCS+ +type: signature +claimed-nist-level: 1 +length-public-key: 32 +length-secret-key: 64 +length-signature: 16976 +testvectors-sha256: 3e7c782b25e405940160468c2d777a5ab6eb9b6cfe318efed257f3270cca8c72 +principal-submitter: Andreas Hülsing +auxiliary-submitters: + - Jean-Philippe Aumasson + - Daniel J. Bernstein, + - Christoph Dobraunig + - Maria Eichlseder + - Scott Fluhrer + - Stefan-Lukas Gazdag + - Panos Kampanakis + - Stefan Kölbl + - Tanja Lange + - Martin M. Lauridsen + - Florian Mendel + - Ruben Niederhagen + - Christian Rechberger + - Joost Rijneveld + - Peter Schwabe +implementations: + - name: clean + version: https://github.com/sphincs/sphincsplus/commit/77755c94d0bc744478044d6efbb888dc13156441 diff --git a/crypto_sign/sphincs-sha256-128f-robust/clean/LICENSE b/crypto_sign/sphincs-sha256-128f-robust/clean/LICENSE new file mode 100644 index 00000000..670154e3 --- /dev/null +++ b/crypto_sign/sphincs-sha256-128f-robust/clean/LICENSE @@ -0,0 +1,116 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + diff --git a/crypto_sign/sphincs-sha256-128f-robust/clean/Makefile b/crypto_sign/sphincs-sha256-128f-robust/clean/Makefile new file mode 100644 index 00000000..a882a93d --- /dev/null +++ b/crypto_sign/sphincs-sha256-128f-robust/clean/Makefile @@ -0,0 +1,20 @@ +# This Makefile can be used with GNU Make or BSD Make + +LIB=libsphincs-sha256-128f-robust_clean.a + +HEADERS = params.h address.h wots.h utils.h fors.h api.h hash.h thash.h sha256.h +OBJECTS = address.o wots.o utils.o fors.o sign.o hash_sha256.o thash_sha256_robust.o sha256.o + +CFLAGS=-O3 -Wall -Wconversion -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) + +all: $(LIB) + +%.o: %.c $(HEADERS) + $(CC) $(CFLAGS) -c -o $@ $< + +$(LIB): $(OBJECTS) + $(AR) -r $@ $(OBJECTS) + +clean: + $(RM) $(OBJECTS) + $(RM) $(LIB) diff --git a/crypto_sign/sphincs-sha256-128f-robust/clean/Makefile.Microsoft_nmake b/crypto_sign/sphincs-sha256-128f-robust/clean/Makefile.Microsoft_nmake new file mode 100644 index 00000000..cecec6f8 --- /dev/null +++ b/crypto_sign/sphincs-sha256-128f-robust/clean/Makefile.Microsoft_nmake @@ -0,0 +1,19 @@ +# This Makefile can be used with Microsoft Visual Studio's nmake using the command: +# nmake /f Makefile.Microsoft_nmake + +LIBRARY=libsphincs-sha256-128f-robust_clean.lib +OBJECTS=address.obj wots.obj utils.obj fors.obj sign.obj hash_sha256.obj thash_sha256_robust.obj sha256.obj + +CFLAGS=/nologo /I ..\..\..\common /W4 /WX + +all: $(LIBRARY) + +# Make sure objects are recompiled if headers change. +$(OBJECTS): *.h + +$(LIBRARY): $(OBJECTS) + LIB.EXE /NOLOGO /WX /OUT:$@ $** + +clean: + -DEL $(OBJECTS) + -DEL $(LIBRARY) diff --git a/crypto_sign/sphincs-sha256-128f-robust/clean/address.c b/crypto_sign/sphincs-sha256-128f-robust/clean/address.c new file mode 100644 index 00000000..eff46d7f --- /dev/null +++ b/crypto_sign/sphincs-sha256-128f-robust/clean/address.c @@ -0,0 +1,78 @@ +#include + +#include "address.h" +#include "params.h" +#include "utils.h" + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]) { + int i; + + for (i = 0; i < 8; i++) { + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_ull_to_bytes( + bytes + i * 4, 4, addr[i]); + } +} + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer) { + addr[0] = layer; +} + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree) { + addr[1] = 0; + addr[2] = (uint32_t) (tree >> 32); + addr[3] = (uint32_t) tree; +} + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_type( + uint32_t addr[8], uint32_t type) { + addr[4] = type; +} + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; +} + +/* These functions are used for OTS addresses. */ + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair) { + addr[5] = keypair; +} + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; + out[5] = in[5]; +} + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain) { + addr[6] = chain; +} + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash) { + addr[7] = hash; +} + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height) { + addr[6] = tree_height; +} + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index) { + addr[7] = tree_index; +} diff --git a/crypto_sign/sphincs-sha256-128f-robust/clean/address.h b/crypto_sign/sphincs-sha256-128f-robust/clean/address.h new file mode 100644 index 00000000..2464391c --- /dev/null +++ b/crypto_sign/sphincs-sha256-128f-robust/clean/address.h @@ -0,0 +1,50 @@ +#ifndef SPX_ADDRESS_H +#define SPX_ADDRESS_H + +#include + +#define SPX_ADDR_TYPE_WOTS 0 +#define SPX_ADDR_TYPE_WOTSPK 1 +#define SPX_ADDR_TYPE_HASHTREE 2 +#define SPX_ADDR_TYPE_FORSTREE 3 +#define SPX_ADDR_TYPE_FORSPK 4 + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer); + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree); + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_type( + uint32_t addr[8], uint32_t type); + +/* Copies the layer and tree part of one address into the other */ +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for WOTS and FORS addresses. */ + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair); + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain); + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash); + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height); + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index); + +#endif diff --git a/crypto_sign/sphincs-sha256-128f-robust/clean/api.h b/crypto_sign/sphincs-sha256-128f-robust/clean/api.h new file mode 100644 index 00000000..2b084704 --- /dev/null +++ b/crypto_sign/sphincs-sha256-128f-robust/clean/api.h @@ -0,0 +1,78 @@ +#ifndef PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_API_H +#define PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_API_H + +#include +#include + +#define PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_CRYPTO_ALGNAME "SPHINCS+" + +#define PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_CRYPTO_SECRETKEYBYTES 64 +#define PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES 32 +#define PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_CRYPTO_BYTES 16976 +#define PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_CRYPTO_SEEDBYTES 48 + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_crypto_sign_secretkeybytes(void); + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_crypto_sign_publickeybytes(void); + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_crypto_sign_bytes(void); + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_crypto_sign_seedbytes(void); + +/* + * Generates a SPHINCS+ key pair given a seed. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed); + +/* + * Generates a SPHINCS+ key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk); + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk); + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk); + +#endif diff --git a/crypto_sign/sphincs-sha256-128f-robust/clean/fors.c b/crypto_sign/sphincs-sha256-128f-robust/clean/fors.c new file mode 100644 index 00000000..b4257d96 --- /dev/null +++ b/crypto_sign/sphincs-sha256-128f-robust/clean/fors.c @@ -0,0 +1,164 @@ +#include +#include +#include + +#include "address.h" +#include "fors.h" +#include "hash.h" +#include "thash.h" +#include "utils.h" + +static void fors_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_prf_addr( + sk, sk_seed, fors_leaf_addr); +} + +static void fors_sk_to_leaf(unsigned char *leaf, const unsigned char *sk, + const unsigned char *pub_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_thash_1( + leaf, sk, pub_seed, fors_leaf_addr); +} + +static void fors_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t fors_tree_addr[8]) { + uint32_t fors_leaf_addr[8] = {0}; + + /* Only copy the parts that must be kept in fors_leaf_addr. */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_copy_keypair_addr( + fors_leaf_addr, fors_tree_addr); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_type( + fors_leaf_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_tree_index( + fors_leaf_addr, addr_idx); + + fors_gen_sk(leaf, sk_seed, fors_leaf_addr); + fors_sk_to_leaf(leaf, leaf, pub_seed, fors_leaf_addr); +} + +/** + * Interprets m as SPX_FORS_HEIGHT-bit unsigned integers. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + * Assumes indices has space for SPX_FORS_TREES integers. + */ +static void message_to_indices(uint32_t *indices, const unsigned char *m) { + unsigned int i, j; + unsigned int offset = 0; + + for (i = 0; i < SPX_FORS_TREES; i++) { + indices[i] = 0; + for (j = 0; j < SPX_FORS_HEIGHT; j++) { + indices[i] ^= (((uint32_t)m[offset >> 3] >> (offset & 0x7)) & 0x1) << j; + offset++; + } + } +} + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Include the secret key part that produces the selected leaf node. */ + fors_gen_sk(sig, sk_seed, fors_tree_addr); + sig += SPX_N; + + /* Compute the authentication path for this leaf node. */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_treehash_FORS_HEIGHT( + roots + i * SPX_N, sig, sk_seed, pub_seed, + indices[i], idx_offset, fors_gen_leaf, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + unsigned char leaf[SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Derive the leaf from the included secret key part. */ + fors_sk_to_leaf(leaf, sig, pub_seed, fors_tree_addr); + sig += SPX_N; + + /* Derive the corresponding root node of this tree. */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_compute_root( + roots + i * SPX_N, leaf, indices[i], idx_offset, sig, + SPX_FORS_HEIGHT, pub_seed, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} diff --git a/crypto_sign/sphincs-sha256-128f-robust/clean/fors.h b/crypto_sign/sphincs-sha256-128f-robust/clean/fors.h new file mode 100644 index 00000000..d61f9d47 --- /dev/null +++ b/crypto_sign/sphincs-sha256-128f-robust/clean/fors.h @@ -0,0 +1,30 @@ +#ifndef SPX_FORS_H +#define SPX_FORS_H + +#include + +#include "params.h" + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]); + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-sha256-128f-robust/clean/hash.h b/crypto_sign/sphincs-sha256-128f-robust/clean/hash.h new file mode 100644 index 00000000..5761e849 --- /dev/null +++ b/crypto_sign/sphincs-sha256-128f-robust/clean/hash.h @@ -0,0 +1,22 @@ +#ifndef SPX_HASH_H +#define SPX_HASH_H + +#include + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed); + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen); + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen); + +#endif diff --git a/crypto_sign/sphincs-sha256-128f-robust/clean/hash_sha256.c b/crypto_sign/sphincs-sha256-128f-robust/clean/hash_sha256.c new file mode 100644 index 00000000..7af46b0b --- /dev/null +++ b/crypto_sign/sphincs-sha256-128f-robust/clean/hash_sha256.c @@ -0,0 +1,148 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "utils.h" + +#include "sha2.h" +#include "sha256.h" + +/* For SHA256, there is no immediate reason to initialize at the start, + so this function is an empty operation. */ +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed) { + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_seed_state(pub_seed); + (void)sk_seed; /* Suppress an 'unused parameter' warning. */ +} + +/* + * Computes PRF(key, addr), given a secret key of SPX_N bytes and an address + */ +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]) { + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES]; + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + + memcpy(buf, key, SPX_N); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_compress_address(buf + SPX_N, addr); + + sha256(outbuf, buf, SPX_N + SPX_SHA256_ADDR_BYTES); + memcpy(out, outbuf, SPX_N); +} + +/** + * Computes the message-dependent randomness R, using a secret seed as a key + * for HMAC, and an optional randomization value prefixed to the message. + * This requires m to have at least SPX_SHA256_BLOCK_BYTES + SPX_N space + * available in front of the pointer, i.e. before the message to use for the + * prefix. This is necessary to prevent having to move the message around (and + * allocate memory for it). + */ +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen) { + unsigned char buf[SPX_SHA256_BLOCK_BYTES + SPX_SHA256_OUTPUT_BYTES]; + uint8_t state[40]; + int i; + + /* This implements HMAC-SHA256 */ + for (i = 0; i < SPX_N; i++) { + buf[i] = 0x36 ^ sk_prf[i]; + } + memset(buf + SPX_N, 0x36, SPX_SHA256_BLOCK_BYTES - SPX_N); + + sha256_inc_init(state); + sha256_inc_blocks(state, buf, 1); + + memcpy(buf, optrand, SPX_N); + + /* If optrand + message cannot fill up an entire block */ + if (SPX_N + mlen < SPX_SHA256_BLOCK_BYTES) { + memcpy(buf + SPX_N, m, mlen); + sha256_inc_finalize(buf + SPX_SHA256_BLOCK_BYTES, state, + buf, mlen + SPX_N); + } + /* Otherwise first fill a block, so that finalize only uses the message */ + else { + memcpy(buf + SPX_N, m, SPX_SHA256_BLOCK_BYTES - SPX_N); + sha256_inc_blocks(state, buf, 1); + + m += SPX_SHA256_BLOCK_BYTES - SPX_N; + mlen -= SPX_SHA256_BLOCK_BYTES - SPX_N; + sha256_inc_finalize(buf + SPX_SHA256_BLOCK_BYTES, state, m, mlen); + } + + for (i = 0; i < SPX_N; i++) { + buf[i] = 0x5c ^ sk_prf[i]; + } + memset(buf + SPX_N, 0x5c, SPX_SHA256_BLOCK_BYTES - SPX_N); + + sha256(buf, buf, SPX_SHA256_BLOCK_BYTES + SPX_SHA256_OUTPUT_BYTES); + memcpy(R, buf, SPX_N); +} + +/** + * Computes the message hash using R, the public key, and the message. + * Outputs the message digest and the index of the leaf. The index is split in + * the tree index and the leaf index, for convenient copying to an address. + */ +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen) { +#define SPX_TREE_BITS (SPX_TREE_HEIGHT * (SPX_D - 1)) +#define SPX_TREE_BYTES ((SPX_TREE_BITS + 7) / 8) +#define SPX_LEAF_BITS SPX_TREE_HEIGHT +#define SPX_LEAF_BYTES ((SPX_LEAF_BITS + 7) / 8) +#define SPX_DGST_BYTES (SPX_FORS_MSG_BYTES + SPX_TREE_BYTES + SPX_LEAF_BYTES) + + unsigned char seed[SPX_SHA256_OUTPUT_BYTES + 4]; + + /* Round to nearest multiple of SPX_SHA256_BLOCK_BYTES */ +#define SPX_INBLOCKS (((SPX_N + SPX_PK_BYTES + SPX_SHA256_BLOCK_BYTES - 1) & \ + -SPX_SHA256_BLOCK_BYTES) / SPX_SHA256_BLOCK_BYTES) + unsigned char inbuf[SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES]; + + unsigned char buf[SPX_DGST_BYTES]; + unsigned char *bufp = buf; + uint8_t state[40]; + + sha256_inc_init(state); + + memcpy(inbuf, R, SPX_N); + memcpy(inbuf + SPX_N, pk, SPX_PK_BYTES); + + /* If R + pk + message cannot fill up an entire block */ + if (SPX_N + SPX_PK_BYTES + mlen < SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES) { + memcpy(inbuf + SPX_N + SPX_PK_BYTES, m, mlen); + sha256_inc_finalize(seed, state, inbuf, SPX_N + SPX_PK_BYTES + mlen); + } + /* Otherwise first fill a block, so that finalize only uses the message */ + else { + memcpy(inbuf + SPX_N + SPX_PK_BYTES, m, + SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES); + sha256_inc_blocks(state, inbuf, SPX_INBLOCKS); + + m += SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES; + mlen -= SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES; + sha256_inc_finalize(seed, state, m, mlen); + } + + /* By doing this in two steps, we prevent hashing the message twice; + otherwise each iteration in MGF1 would hash the message again. */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_mgf1(bufp, SPX_DGST_BYTES, seed, SPX_SHA256_OUTPUT_BYTES); + + memcpy(digest, bufp, SPX_FORS_MSG_BYTES); + bufp += SPX_FORS_MSG_BYTES; + + *tree = PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_bytes_to_ull(bufp, SPX_TREE_BYTES); + *tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS); + bufp += SPX_TREE_BYTES; + + *leaf_idx = (uint32_t)PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_bytes_to_ull( + bufp, SPX_LEAF_BYTES); + *leaf_idx &= (~(uint32_t)0) >> (32 - SPX_LEAF_BITS); +} diff --git a/crypto_sign/sphincs-sha256-128f-robust/clean/params.h b/crypto_sign/sphincs-sha256-128f-robust/clean/params.h new file mode 100644 index 00000000..367ef88d --- /dev/null +++ b/crypto_sign/sphincs-sha256-128f-robust/clean/params.h @@ -0,0 +1,53 @@ +#ifndef SPX_PARAMS_H +#define SPX_PARAMS_H + +/* Hash output length in bytes. */ +#define SPX_N 16 +/* Height of the hypertree. */ +#define SPX_FULL_HEIGHT 60 +/* Number of subtree layer. */ +#define SPX_D 20 +/* FORS tree dimensions. */ +#define SPX_FORS_HEIGHT 9 +#define SPX_FORS_TREES 30 +/* Winternitz parameter, */ +#define SPX_WOTS_W 16 + +/* The hash function is defined by linking a different hash.c file, as opposed + to setting a #define constant. */ + +/* For clarity */ +#define SPX_ADDR_BYTES 32 + +/* WOTS parameters. */ +#define SPX_WOTS_LOGW 4 + +#define SPX_WOTS_LEN1 (8 * SPX_N / SPX_WOTS_LOGW) + +/* SPX_WOTS_LEN2 is floor(log(len_1 * (w - 1)) / log(w)) + 1; we precompute */ +#define SPX_WOTS_LEN2 3 + +#define SPX_WOTS_LEN (SPX_WOTS_LEN1 + SPX_WOTS_LEN2) +#define SPX_WOTS_BYTES (SPX_WOTS_LEN * SPX_N) +#define SPX_WOTS_PK_BYTES SPX_WOTS_BYTES + +/* Subtree size. */ +#define SPX_TREE_HEIGHT (SPX_FULL_HEIGHT / SPX_D) + +/* FORS parameters. */ +#define SPX_FORS_MSG_BYTES ((SPX_FORS_HEIGHT * SPX_FORS_TREES + 7) / 8) +#define SPX_FORS_BYTES ((SPX_FORS_HEIGHT + 1) * SPX_FORS_TREES * SPX_N) +#define SPX_FORS_PK_BYTES SPX_N + +/* Resulting SPX sizes. */ +#define SPX_BYTES (SPX_N + SPX_FORS_BYTES + SPX_D * SPX_WOTS_BYTES +\ + SPX_FULL_HEIGHT * SPX_N) +#define SPX_PK_BYTES (2 * SPX_N) +#define SPX_SK_BYTES (2 * SPX_N + SPX_PK_BYTES) + +/* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ +#define SPX_OPTRAND_BYTES 32 + +#endif diff --git a/crypto_sign/sphincs-sha256-128f-robust/clean/sha256.c b/crypto_sign/sphincs-sha256-128f-robust/clean/sha256.c new file mode 100644 index 00000000..abecf7d7 --- /dev/null +++ b/crypto_sign/sphincs-sha256-128f-robust/clean/sha256.c @@ -0,0 +1,72 @@ +/* Based on the public domain implementation in + * crypto_hash/sha512/ref/ from http://bench.cr.yp.to/supercop.html + * by D. J. Bernstein */ + +#include +#include +#include + +#include "sha2.h" +#include "sha256.h" +#include "utils.h" + +/* + * Compresses an address to a 22-byte sequence. + * This reduces the number of required SHA256 compression calls, as the last + * block of input is padded with at least 65 bits. + */ +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_compress_address(unsigned char *out, const uint32_t addr[8]) { + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_ull_to_bytes(out, 1, addr[0]); /* drop 3 bytes of the layer field */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_ull_to_bytes(out + 1, 4, addr[2]); /* drop the highest tree address word */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_ull_to_bytes(out + 5, 4, addr[3]); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_ull_to_bytes(out + 9, 1, addr[4]); /* drop 3 bytes of the type field */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_ull_to_bytes(out + 10, 4, addr[5]); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_ull_to_bytes(out + 14, 4, addr[6]); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_ull_to_bytes(out + 18, 4, addr[7]); +} + +/** + * Requires 'input_plus_four_bytes' to have 'inlen' + 4 bytes, so that the last + * four bytes can be used for the counter. Typically 'input' is merely a seed. + * Outputs outlen number of bytes + */ +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + unsigned char *input_plus_four_bytes, unsigned long inlen) { + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + unsigned long i; + + /* While we can fit in at least another full block of SHA256 output.. */ + for (i = 0; (i + 1)*SPX_SHA256_OUTPUT_BYTES <= outlen; i++) { + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_ull_to_bytes(input_plus_four_bytes + inlen, 4, i); + sha256(out, input_plus_four_bytes, inlen + 4); + out += SPX_SHA256_OUTPUT_BYTES; + } + /* Until we cannot anymore, and we fill the remainder. */ + if (outlen > i * SPX_SHA256_OUTPUT_BYTES) { + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_ull_to_bytes(input_plus_four_bytes + inlen, 4, i); + sha256(outbuf, input_plus_four_bytes, inlen + 4); + memcpy(out, outbuf, outlen - i * SPX_SHA256_OUTPUT_BYTES); + } +} + +uint8_t PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_state_seeded[40]; + +/** + * Absorb the constant pub_seed using one round of the compression function + * This initializes state_seeded, which can then be reused in thash + **/ +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_seed_state(const unsigned char *pub_seed) { + uint8_t block[SPX_SHA256_BLOCK_BYTES]; + size_t i; + + for (i = 0; i < SPX_N; ++i) { + block[i] = pub_seed[i]; + } + for (i = SPX_N; i < SPX_SHA256_BLOCK_BYTES; ++i) { + block[i] = 0; + } + + sha256_inc_init(PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_state_seeded); + sha256_inc_blocks(PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_state_seeded, block, 1); +} diff --git a/crypto_sign/sphincs-sha256-128f-robust/clean/sha256.h b/crypto_sign/sphincs-sha256-128f-robust/clean/sha256.h new file mode 100644 index 00000000..c2e16d0b --- /dev/null +++ b/crypto_sign/sphincs-sha256-128f-robust/clean/sha256.h @@ -0,0 +1,21 @@ +#ifndef SPX_SHA256_H +#define SPX_SHA256_H + +#define SPX_SHA256_BLOCK_BYTES 64 +#define SPX_SHA256_OUTPUT_BYTES 32 /* This does not necessarily equal SPX_N */ +#define SPX_SHA256_ADDR_BYTES 22 + +#include +#include + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_compress_address(unsigned char *out, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + unsigned char *input_plus_four_bytes, unsigned long inlen); + +uint8_t PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_state_seeded[40]; + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_seed_state(const unsigned char *pub_seed); + +#endif diff --git a/crypto_sign/sphincs-sha256-128f-robust/clean/sign.c b/crypto_sign/sphincs-sha256-128f-robust/clean/sign.c new file mode 100644 index 00000000..ff367ce5 --- /dev/null +++ b/crypto_sign/sphincs-sha256-128f-robust/clean/sign.c @@ -0,0 +1,344 @@ +#include +#include +#include + +#include "address.h" +#include "api.h" +#include "fors.h" +#include "hash.h" +#include "params.h" +#include "randombytes.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +/** + * Computes the leaf at a given address. First generates the WOTS key pair, + * then computes leaf by hashing horizontally. + */ +static void wots_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t tree_addr[8]) { + unsigned char pk[SPX_WOTS_BYTES]; + uint32_t wots_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_keypair_addr( + wots_addr, addr_idx); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_wots_gen_pk( + pk, sk_seed, pub_seed, wots_addr); + + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_thash_WOTS_LEN( + leaf, pk, pub_seed, wots_pk_addr); +} + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_crypto_sign_secretkeybytes(void) { + return PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_CRYPTO_SECRETKEYBYTES; +} + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_crypto_sign_publickeybytes(void) { + return PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES; +} + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_crypto_sign_bytes(void) { + return PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_CRYPTO_BYTES; +} + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_crypto_sign_seedbytes(void) { + return PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_CRYPTO_SEEDBYTES; +} + +/* + * Generates an SPX key pair given a seed of length + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed) { + /* We do not need the auth path in key generation, but it simplifies the + code to have just one treehash routine that computes both root and path + in one function. */ + unsigned char auth_path[SPX_TREE_HEIGHT * SPX_N]; + uint32_t top_tree_addr[8] = {0}; + + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_layer_addr( + top_tree_addr, SPX_D - 1); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_type( + top_tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */ + memcpy(sk, seed, PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_CRYPTO_SEEDBYTES); + + memcpy(pk, sk + 2 * SPX_N, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_initialize_hash_function(pk, sk); + + /* Compute root node of the top-most subtree. */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_treehash_TREE_HEIGHT( + sk + 3 * SPX_N, auth_path, sk, sk + 2 * SPX_N, 0, 0, + wots_gen_leaf, top_tree_addr); + + memcpy(pk + SPX_N, sk + 3 * SPX_N, SPX_N); + + return 0; +} + +/* + * Generates an SPX key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk) { + unsigned char seed[PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_CRYPTO_SEEDBYTES]; + randombytes(seed, PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_CRYPTO_SEEDBYTES); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_crypto_sign_seed_keypair( + pk, sk, seed); + + return 0; +} + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + const unsigned char *sk_seed = sk; + const unsigned char *sk_prf = sk + SPX_N; + const unsigned char *pk = sk + 2 * SPX_N; + const unsigned char *pub_seed = pk; + + unsigned char optrand[SPX_N]; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char root[SPX_N]; + uint32_t i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_initialize_hash_function( + pub_seed, sk_seed); + + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ + randombytes(optrand, SPX_N); + /* Compute the digest randomization value. */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_gen_message_random( + sig, sk_prf, optrand, m, mlen); + + /* Derive the message digest and leaf index from R, PK and M. */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Sign the message hash using FORS. */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_fors_sign( + sig, root, mhash, sk_seed, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Compute a WOTS signature. */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_wots_sign( + sig, root, sk_seed, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the authentication path for the used WOTS leaf. */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_treehash_TREE_HEIGHT( + root, sig, sk_seed, pub_seed, idx_leaf, 0, + wots_gen_leaf, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + *siglen = SPX_BYTES; + + return 0; +} + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk) { + const unsigned char *pub_seed = pk; + const unsigned char *pub_root = pk + SPX_N; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char wots_pk[SPX_WOTS_BYTES]; + unsigned char root[SPX_N]; + unsigned char leaf[SPX_N]; + unsigned int i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + if (siglen != SPX_BYTES) { + return -1; + } + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_initialize_hash_function( + pub_seed, NULL); + + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + /* Derive the message digest and leaf index from R || PK || M. */ + /* The additional SPX_N is a result of the hash domain separator. */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + /* Layer correctly defaults to 0, so no need to set_layer_addr */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_fors_pk_from_sig( + root, sig, mhash, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + /* For each subtree.. */ + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + + /* The WOTS public key is only correct if the signature was correct. */ + /* Initially, root is the FORS pk, but on subsequent iterations it is + the root of the subtree below the currently processed subtree. */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_wots_pk_from_sig( + wots_pk, sig, root, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the leaf node using the WOTS public key. */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_thash_WOTS_LEN( + leaf, wots_pk, pub_seed, wots_pk_addr); + + /* Compute the root node of this subtree. */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_compute_root( + root, leaf, idx_leaf, 0, sig, SPX_TREE_HEIGHT, + pub_seed, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + /* Check if the root node equals the root node in the public key. */ + if (memcmp(root, pub_root, SPX_N) != 0) { + return -1; + } + + return 0; +} + + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + size_t siglen; + + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_crypto_sign_signature( + sm, &siglen, m, mlen, sk); + + memmove(sm + SPX_BYTES, m, mlen); + *smlen = siglen + mlen; + + return 0; +} + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk) { + /* The API caller does not necessarily know what size a signature should be + but SPHINCS+ signatures are always exactly SPX_BYTES. */ + if (smlen < SPX_BYTES) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + *mlen = smlen - SPX_BYTES; + + if (PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_crypto_sign_verify( + sm, SPX_BYTES, sm + SPX_BYTES, *mlen, pk)) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + /* If verification was successful, move the message to the right place. */ + memmove(m, sm + SPX_BYTES, *mlen); + + return 0; +} diff --git a/crypto_sign/sphincs-sha256-128f-robust/clean/thash.h b/crypto_sign/sphincs-sha256-128f-robust/clean/thash.h new file mode 100644 index 00000000..5e82607e --- /dev/null +++ b/crypto_sign/sphincs-sha256-128f-robust/clean/thash.h @@ -0,0 +1,22 @@ +#ifndef SPX_THASH_H +#define SPX_THASH_H + +#include + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-sha256-128f-robust/clean/thash_sha256_robust.c b/crypto_sign/sphincs-sha256-128f-robust/clean/thash_sha256_robust.c new file mode 100644 index 00000000..c2f6688c --- /dev/null +++ b/crypto_sign/sphincs-sha256-128f-robust/clean/thash_sha256_robust.c @@ -0,0 +1,77 @@ +#include +#include + +#include "address.h" +#include "params.h" +#include "thash.h" + +#include "sha2.h" +#include "sha256.h" + +/** + * Takes an array of inblocks concatenated arrays of SPX_N bytes. + */ +static void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_thash( + unsigned char *out, unsigned char *buf, + const unsigned char *in, unsigned int inblocks, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + unsigned char *bitmask = buf + SPX_N + SPX_SHA256_ADDR_BYTES + 4; + uint8_t sha2_state[40]; + unsigned int i; + + memcpy(buf, pub_seed, SPX_N); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_compress_address(buf + SPX_N, addr); + /* MGF1 requires us to have 4 extra bytes in 'buf' */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_mgf1(bitmask, inblocks * SPX_N, buf, SPX_N + SPX_SHA256_ADDR_BYTES); + + /* Retrieve precomputed state containing pub_seed */ + memcpy(sha2_state, PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_state_seeded, 40 * sizeof(uint8_t)); + + for (i = 0; i < inblocks * SPX_N; i++) { + buf[SPX_N + SPX_SHA256_ADDR_BYTES + i] = in[i] ^ bitmask[i]; + } + + sha256_inc_finalize(outbuf, sha2_state, buf + SPX_N, + SPX_SHA256_ADDR_BYTES + inblocks * SPX_N); + memcpy(out, outbuf, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES + 4 + 1 * SPX_N]; + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_thash( + out, buf, in, 1, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES + 4 + 2 * SPX_N]; + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_thash( + out, buf, in, 2, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES + 4 + SPX_WOTS_LEN * SPX_N]; + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_thash( + out, buf, in, SPX_WOTS_LEN, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES + 4 + SPX_FORS_TREES * SPX_N]; + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_thash( + out, buf, in, SPX_FORS_TREES, pub_seed, addr); +} diff --git a/crypto_sign/sphincs-sha256-128f-robust/clean/utils.c b/crypto_sign/sphincs-sha256-128f-robust/clean/utils.c new file mode 100644 index 00000000..f749f865 --- /dev/null +++ b/crypto_sign/sphincs-sha256-128f-robust/clean/utils.c @@ -0,0 +1,192 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in) { + + /* Iterate over out in decreasing order, for big-endianness. */ + for (size_t i = outlen; i > 0; i--) { + out[i - 1] = in & 0xff; + in = in >> 8; + } +} + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen) { + unsigned long long retval = 0; + + for (size_t i = 0; i < inlen; i++) { + retval |= ((unsigned long long)in[i]) << (8 * (inlen - 1 - i)); + } + return retval; +} + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + unsigned char buffer[2 * SPX_N]; + + /* If leaf_idx is odd (last bit = 1), current path element is a right child + and auth_path has to go left. Otherwise it is the other way around. */ + if (leaf_idx & 1) { + memcpy(buffer + SPX_N, leaf, SPX_N); + memcpy(buffer, auth_path, SPX_N); + } else { + memcpy(buffer, leaf, SPX_N); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + + for (i = 0; i < tree_height - 1; i++) { + leaf_idx >>= 1; + idx_offset >>= 1; + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_tree_height(addr, i + 1); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + + /* Pick the right or left neighbor, depending on parity of the node. */ + if (leaf_idx & 1) { + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_thash_2( + buffer + SPX_N, buffer, pub_seed, addr); + memcpy(buffer, auth_path, SPX_N); + } else { + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_thash_2( + buffer, buffer, pub_seed, addr); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + } + + /* The last iteration is exceptional; we do not copy an auth_path node. */ + leaf_idx >>= 1; + idx_offset >>= 1; + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_tree_height(addr, tree_height); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_thash_2( + root, buffer, pub_seed, addr); +} + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +static void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_treehash( + unsigned char *root, unsigned char *auth_path, + unsigned char *stack, unsigned int *heights, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned int offset = 0; + uint32_t idx; + uint32_t tree_idx; + + for (idx = 0; idx < (uint32_t)(1 << tree_height); idx++) { + /* Add the next leaf node to the stack. */ + gen_leaf(stack + offset * SPX_N, + sk_seed, pub_seed, idx + idx_offset, tree_addr); + offset++; + heights[offset - 1] = 0; + + /* If this is a node we need for the auth path.. */ + if ((leaf_idx ^ 0x1) == idx) { + memcpy(auth_path, stack + (offset - 1)*SPX_N, SPX_N); + } + + /* While the top-most nodes are of equal height.. */ + while (offset >= 2 && heights[offset - 1] == heights[offset - 2]) { + /* Compute index of the new node, in the next layer. */ + tree_idx = (idx >> (heights[offset - 1] + 1)); + + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_tree_height( + tree_addr, heights[offset - 1] + 1); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_tree_index( + tree_addr, tree_idx + (idx_offset >> (heights[offset - 1] + 1))); + /* Hash the top-most nodes from the stack together. */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_thash_2( + stack + (offset - 2)*SPX_N, stack + (offset - 2)*SPX_N, + pub_seed, tree_addr); + offset--; + /* Note that the top-most node is now one layer higher. */ + heights[offset - 1]++; + + /* If this is a node we need for the auth path.. */ + if (((leaf_idx >> heights[offset - 1]) ^ 0x1) == tree_idx) { + memcpy(auth_path + heights[offset - 1]*SPX_N, + stack + (offset - 1)*SPX_N, SPX_N); + } + } + } + memcpy(root, stack, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_FORS_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_FORS_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_FORS_HEIGHT, gen_leaf, tree_addr); +} + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_TREE_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_TREE_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_TREE_HEIGHT, gen_leaf, tree_addr); +} diff --git a/crypto_sign/sphincs-sha256-128f-robust/clean/utils.h b/crypto_sign/sphincs-sha256-128f-robust/clean/utils.h new file mode 100644 index 00000000..2cb52bc0 --- /dev/null +++ b/crypto_sign/sphincs-sha256-128f-robust/clean/utils.h @@ -0,0 +1,60 @@ +#ifndef SPX_UTILS_H +#define SPX_UTILS_H + +#include "params.h" +#include +#include + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in); + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen); + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-sha256-128f-robust/clean/wots.c b/crypto_sign/sphincs-sha256-128f-robust/clean/wots.c new file mode 100644 index 00000000..ac7e4a8b --- /dev/null +++ b/crypto_sign/sphincs-sha256-128f-robust/clean/wots.c @@ -0,0 +1,161 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +// TODO clarify address expectations, and make them more uniform. +// TODO i.e. do we expect types to be set already? +// TODO and do we expect modifications or copies? + +/** + * Computes the starting value for a chain, i.e. the secret key. + * Expects the address to be complete up to the chain address. + */ +static void wots_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t wots_addr[8]) { + /* Make sure that the hash address is actually zeroed. */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_hash_addr(wots_addr, 0); + + /* Generate sk element. */ + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_prf_addr(sk, sk_seed, wots_addr); +} + +/** + * Computes the chaining function. + * out and in have to be n-byte arrays. + * + * Interprets in as start-th value of the chain. + * addr has to contain the address of the chain. + */ +static void gen_chain(unsigned char *out, const unsigned char *in, + unsigned int start, unsigned int steps, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + /* Initialize out with the value at position 'start'. */ + memcpy(out, in, SPX_N); + + /* Iterate 'steps' calls to the hash function. */ + for (i = start; i < (start + steps) && i < SPX_WOTS_W; i++) { + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_hash_addr(addr, i); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_thash_1( + out, out, pub_seed, addr); + } +} + +/** + * base_w algorithm as described in draft. + * Interprets an array of bytes as integers in base w. + * This only works when log_w is a divisor of 8. + */ +static void base_w(unsigned int *output, const size_t out_len, + const unsigned char *input) { + size_t in = 0; + size_t out = 0; + unsigned char total = 0; + unsigned int bits = 0; + size_t consumed; + + for (consumed = 0; consumed < out_len; consumed++) { + if (bits == 0) { + total = input[in]; + in++; + bits += 8; + } + bits -= SPX_WOTS_LOGW; + output[out] = (unsigned int)((total >> bits) & (SPX_WOTS_W - 1)); + out++; + } +} + +/* Computes the WOTS+ checksum over a message (in base_w). */ +static void wots_checksum(unsigned int *csum_base_w, + const unsigned int *msg_base_w) { + unsigned int csum = 0; + unsigned char csum_bytes[(SPX_WOTS_LEN2 * SPX_WOTS_LOGW + 7) / 8]; + unsigned int i; + + /* Compute checksum. */ + for (i = 0; i < SPX_WOTS_LEN1; i++) { + csum += SPX_WOTS_W - 1 - msg_base_w[i]; + } + + /* Convert checksum to base_w. */ + /* Make sure expected empty zero bits are the least significant bits. */ + csum = csum << (8 - ((SPX_WOTS_LEN2 * SPX_WOTS_LOGW) % 8)); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_ull_to_bytes( + csum_bytes, sizeof(csum_bytes), csum); + base_w(csum_base_w, SPX_WOTS_LEN2, csum_bytes); +} + +/* Takes a message and derives the matching chain lengths. */ +static void chain_lengths(unsigned int *lengths, const unsigned char *msg) { + base_w(lengths, SPX_WOTS_LEN1, msg); + wots_checksum(lengths + SPX_WOTS_LEN1, lengths); +} + +/** + * WOTS key generation. Takes a 32 byte sk_seed, expands it to WOTS private key + * elements and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(pk + i * SPX_N, sk_seed, addr); + gen_chain(pk + i * SPX_N, pk + i * SPX_N, + 0, SPX_WOTS_W - 1, pub_seed, addr); + } +} + +/** + * Takes a n-byte message and the 32-byte sk_see to compute a signature 'sig'. + */ +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(sig + i * SPX_N, sk_seed, addr); + gen_chain(sig + i * SPX_N, sig + i * SPX_N, 0, lengths[i], pub_seed, addr); + } +} + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_set_chain_addr(addr, i); + gen_chain(pk + i * SPX_N, sig + i * SPX_N, + lengths[i], SPX_WOTS_W - 1 - lengths[i], pub_seed, addr); + } +} diff --git a/crypto_sign/sphincs-sha256-128f-robust/clean/wots.h b/crypto_sign/sphincs-sha256-128f-robust/clean/wots.h new file mode 100644 index 00000000..3ea8888a --- /dev/null +++ b/crypto_sign/sphincs-sha256-128f-robust/clean/wots.h @@ -0,0 +1,38 @@ +#ifndef SPX_WOTS_H +#define SPX_WOTS_H + +#include "params.h" +#include + +/** + * WOTS key generation. Takes a 32 byte seed for the private key, expands it to + * a full WOTS private key and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * Takes a n-byte message and the 32-byte seed for the private key to compute a + * signature that is placed at 'sig'. + */ +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]); + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-sha256-128f-simple/META.yml b/crypto_sign/sphincs-sha256-128f-simple/META.yml new file mode 100644 index 00000000..b0520b3c --- /dev/null +++ b/crypto_sign/sphincs-sha256-128f-simple/META.yml @@ -0,0 +1,27 @@ +name: SPHINCS+ +type: signature +claimed-nist-level: 1 +length-public-key: 32 +length-secret-key: 64 +length-signature: 16976 +testvectors-sha256: 5ce16422e028eb7a6198d0a276a1760a6bbcd4ba9457ddbbfd5e08f34985c0ce +principal-submitter: Andreas Hülsing +auxiliary-submitters: + - Jean-Philippe Aumasson + - Daniel J. Bernstein, + - Christoph Dobraunig + - Maria Eichlseder + - Scott Fluhrer + - Stefan-Lukas Gazdag + - Panos Kampanakis + - Stefan Kölbl + - Tanja Lange + - Martin M. Lauridsen + - Florian Mendel + - Ruben Niederhagen + - Christian Rechberger + - Joost Rijneveld + - Peter Schwabe +implementations: + - name: clean + version: https://github.com/sphincs/sphincsplus/commit/77755c94d0bc744478044d6efbb888dc13156441 diff --git a/crypto_sign/sphincs-sha256-128f-simple/clean/LICENSE b/crypto_sign/sphincs-sha256-128f-simple/clean/LICENSE new file mode 100644 index 00000000..670154e3 --- /dev/null +++ b/crypto_sign/sphincs-sha256-128f-simple/clean/LICENSE @@ -0,0 +1,116 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + diff --git a/crypto_sign/sphincs-sha256-128f-simple/clean/Makefile b/crypto_sign/sphincs-sha256-128f-simple/clean/Makefile new file mode 100644 index 00000000..00341164 --- /dev/null +++ b/crypto_sign/sphincs-sha256-128f-simple/clean/Makefile @@ -0,0 +1,20 @@ +# This Makefile can be used with GNU Make or BSD Make + +LIB=libsphincs-sha256-128f-simple_clean.a + +HEADERS = params.h address.h wots.h utils.h fors.h api.h hash.h thash.h sha256.h +OBJECTS = address.o wots.o utils.o fors.o sign.o hash_sha256.o thash_sha256_simple.o sha256.o + +CFLAGS=-O3 -Wall -Wconversion -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) + +all: $(LIB) + +%.o: %.c $(HEADERS) + $(CC) $(CFLAGS) -c -o $@ $< + +$(LIB): $(OBJECTS) + $(AR) -r $@ $(OBJECTS) + +clean: + $(RM) $(OBJECTS) + $(RM) $(LIB) diff --git a/crypto_sign/sphincs-sha256-128f-simple/clean/Makefile.Microsoft_nmake b/crypto_sign/sphincs-sha256-128f-simple/clean/Makefile.Microsoft_nmake new file mode 100644 index 00000000..b924204d --- /dev/null +++ b/crypto_sign/sphincs-sha256-128f-simple/clean/Makefile.Microsoft_nmake @@ -0,0 +1,19 @@ +# This Makefile can be used with Microsoft Visual Studio's nmake using the command: +# nmake /f Makefile.Microsoft_nmake + +LIBRARY=libsphincs-sha256-128f-simple_clean.lib +OBJECTS=address.obj wots.obj utils.obj fors.obj sign.obj hash_sha256.obj thash_sha256_simple.obj sha256.obj + +CFLAGS=/nologo /I ..\..\..\common /W4 /WX + +all: $(LIBRARY) + +# Make sure objects are recompiled if headers change. +$(OBJECTS): *.h + +$(LIBRARY): $(OBJECTS) + LIB.EXE /NOLOGO /WX /OUT:$@ $** + +clean: + -DEL $(OBJECTS) + -DEL $(LIBRARY) diff --git a/crypto_sign/sphincs-sha256-128f-simple/clean/address.c b/crypto_sign/sphincs-sha256-128f-simple/clean/address.c new file mode 100644 index 00000000..6e5311ff --- /dev/null +++ b/crypto_sign/sphincs-sha256-128f-simple/clean/address.c @@ -0,0 +1,78 @@ +#include + +#include "address.h" +#include "params.h" +#include "utils.h" + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]) { + int i; + + for (i = 0; i < 8; i++) { + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_ull_to_bytes( + bytes + i * 4, 4, addr[i]); + } +} + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer) { + addr[0] = layer; +} + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree) { + addr[1] = 0; + addr[2] = (uint32_t) (tree >> 32); + addr[3] = (uint32_t) tree; +} + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_type( + uint32_t addr[8], uint32_t type) { + addr[4] = type; +} + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; +} + +/* These functions are used for OTS addresses. */ + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair) { + addr[5] = keypair; +} + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; + out[5] = in[5]; +} + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain) { + addr[6] = chain; +} + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash) { + addr[7] = hash; +} + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height) { + addr[6] = tree_height; +} + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index) { + addr[7] = tree_index; +} diff --git a/crypto_sign/sphincs-sha256-128f-simple/clean/address.h b/crypto_sign/sphincs-sha256-128f-simple/clean/address.h new file mode 100644 index 00000000..904e775e --- /dev/null +++ b/crypto_sign/sphincs-sha256-128f-simple/clean/address.h @@ -0,0 +1,50 @@ +#ifndef SPX_ADDRESS_H +#define SPX_ADDRESS_H + +#include + +#define SPX_ADDR_TYPE_WOTS 0 +#define SPX_ADDR_TYPE_WOTSPK 1 +#define SPX_ADDR_TYPE_HASHTREE 2 +#define SPX_ADDR_TYPE_FORSTREE 3 +#define SPX_ADDR_TYPE_FORSPK 4 + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer); + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree); + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_type( + uint32_t addr[8], uint32_t type); + +/* Copies the layer and tree part of one address into the other */ +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for WOTS and FORS addresses. */ + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair); + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain); + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash); + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height); + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index); + +#endif diff --git a/crypto_sign/sphincs-sha256-128f-simple/clean/api.h b/crypto_sign/sphincs-sha256-128f-simple/clean/api.h new file mode 100644 index 00000000..1b9b7cf4 --- /dev/null +++ b/crypto_sign/sphincs-sha256-128f-simple/clean/api.h @@ -0,0 +1,78 @@ +#ifndef PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_API_H +#define PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_API_H + +#include +#include + +#define PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_CRYPTO_ALGNAME "SPHINCS+" + +#define PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_CRYPTO_SECRETKEYBYTES 64 +#define PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES 32 +#define PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_CRYPTO_BYTES 16976 +#define PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_CRYPTO_SEEDBYTES 48 + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_crypto_sign_secretkeybytes(void); + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_crypto_sign_publickeybytes(void); + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_crypto_sign_bytes(void); + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_crypto_sign_seedbytes(void); + +/* + * Generates a SPHINCS+ key pair given a seed. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed); + +/* + * Generates a SPHINCS+ key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk); + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk); + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk); + +#endif diff --git a/crypto_sign/sphincs-sha256-128f-simple/clean/fors.c b/crypto_sign/sphincs-sha256-128f-simple/clean/fors.c new file mode 100644 index 00000000..fbbdb334 --- /dev/null +++ b/crypto_sign/sphincs-sha256-128f-simple/clean/fors.c @@ -0,0 +1,164 @@ +#include +#include +#include + +#include "address.h" +#include "fors.h" +#include "hash.h" +#include "thash.h" +#include "utils.h" + +static void fors_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_prf_addr( + sk, sk_seed, fors_leaf_addr); +} + +static void fors_sk_to_leaf(unsigned char *leaf, const unsigned char *sk, + const unsigned char *pub_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_thash_1( + leaf, sk, pub_seed, fors_leaf_addr); +} + +static void fors_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t fors_tree_addr[8]) { + uint32_t fors_leaf_addr[8] = {0}; + + /* Only copy the parts that must be kept in fors_leaf_addr. */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_copy_keypair_addr( + fors_leaf_addr, fors_tree_addr); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_type( + fors_leaf_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_tree_index( + fors_leaf_addr, addr_idx); + + fors_gen_sk(leaf, sk_seed, fors_leaf_addr); + fors_sk_to_leaf(leaf, leaf, pub_seed, fors_leaf_addr); +} + +/** + * Interprets m as SPX_FORS_HEIGHT-bit unsigned integers. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + * Assumes indices has space for SPX_FORS_TREES integers. + */ +static void message_to_indices(uint32_t *indices, const unsigned char *m) { + unsigned int i, j; + unsigned int offset = 0; + + for (i = 0; i < SPX_FORS_TREES; i++) { + indices[i] = 0; + for (j = 0; j < SPX_FORS_HEIGHT; j++) { + indices[i] ^= (((uint32_t)m[offset >> 3] >> (offset & 0x7)) & 0x1) << j; + offset++; + } + } +} + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Include the secret key part that produces the selected leaf node. */ + fors_gen_sk(sig, sk_seed, fors_tree_addr); + sig += SPX_N; + + /* Compute the authentication path for this leaf node. */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_treehash_FORS_HEIGHT( + roots + i * SPX_N, sig, sk_seed, pub_seed, + indices[i], idx_offset, fors_gen_leaf, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + unsigned char leaf[SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Derive the leaf from the included secret key part. */ + fors_sk_to_leaf(leaf, sig, pub_seed, fors_tree_addr); + sig += SPX_N; + + /* Derive the corresponding root node of this tree. */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_compute_root( + roots + i * SPX_N, leaf, indices[i], idx_offset, sig, + SPX_FORS_HEIGHT, pub_seed, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} diff --git a/crypto_sign/sphincs-sha256-128f-simple/clean/fors.h b/crypto_sign/sphincs-sha256-128f-simple/clean/fors.h new file mode 100644 index 00000000..951d879c --- /dev/null +++ b/crypto_sign/sphincs-sha256-128f-simple/clean/fors.h @@ -0,0 +1,30 @@ +#ifndef SPX_FORS_H +#define SPX_FORS_H + +#include + +#include "params.h" + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]); + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-sha256-128f-simple/clean/hash.h b/crypto_sign/sphincs-sha256-128f-simple/clean/hash.h new file mode 100644 index 00000000..7963d16f --- /dev/null +++ b/crypto_sign/sphincs-sha256-128f-simple/clean/hash.h @@ -0,0 +1,22 @@ +#ifndef SPX_HASH_H +#define SPX_HASH_H + +#include + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed); + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen); + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen); + +#endif diff --git a/crypto_sign/sphincs-sha256-128f-simple/clean/hash_sha256.c b/crypto_sign/sphincs-sha256-128f-simple/clean/hash_sha256.c new file mode 100644 index 00000000..96d67fe7 --- /dev/null +++ b/crypto_sign/sphincs-sha256-128f-simple/clean/hash_sha256.c @@ -0,0 +1,148 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "utils.h" + +#include "sha2.h" +#include "sha256.h" + +/* For SHA256, there is no immediate reason to initialize at the start, + so this function is an empty operation. */ +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed) { + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_seed_state(pub_seed); + (void)sk_seed; /* Suppress an 'unused parameter' warning. */ +} + +/* + * Computes PRF(key, addr), given a secret key of SPX_N bytes and an address + */ +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]) { + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES]; + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + + memcpy(buf, key, SPX_N); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_compress_address(buf + SPX_N, addr); + + sha256(outbuf, buf, SPX_N + SPX_SHA256_ADDR_BYTES); + memcpy(out, outbuf, SPX_N); +} + +/** + * Computes the message-dependent randomness R, using a secret seed as a key + * for HMAC, and an optional randomization value prefixed to the message. + * This requires m to have at least SPX_SHA256_BLOCK_BYTES + SPX_N space + * available in front of the pointer, i.e. before the message to use for the + * prefix. This is necessary to prevent having to move the message around (and + * allocate memory for it). + */ +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen) { + unsigned char buf[SPX_SHA256_BLOCK_BYTES + SPX_SHA256_OUTPUT_BYTES]; + uint8_t state[40]; + int i; + + /* This implements HMAC-SHA256 */ + for (i = 0; i < SPX_N; i++) { + buf[i] = 0x36 ^ sk_prf[i]; + } + memset(buf + SPX_N, 0x36, SPX_SHA256_BLOCK_BYTES - SPX_N); + + sha256_inc_init(state); + sha256_inc_blocks(state, buf, 1); + + memcpy(buf, optrand, SPX_N); + + /* If optrand + message cannot fill up an entire block */ + if (SPX_N + mlen < SPX_SHA256_BLOCK_BYTES) { + memcpy(buf + SPX_N, m, mlen); + sha256_inc_finalize(buf + SPX_SHA256_BLOCK_BYTES, state, + buf, mlen + SPX_N); + } + /* Otherwise first fill a block, so that finalize only uses the message */ + else { + memcpy(buf + SPX_N, m, SPX_SHA256_BLOCK_BYTES - SPX_N); + sha256_inc_blocks(state, buf, 1); + + m += SPX_SHA256_BLOCK_BYTES - SPX_N; + mlen -= SPX_SHA256_BLOCK_BYTES - SPX_N; + sha256_inc_finalize(buf + SPX_SHA256_BLOCK_BYTES, state, m, mlen); + } + + for (i = 0; i < SPX_N; i++) { + buf[i] = 0x5c ^ sk_prf[i]; + } + memset(buf + SPX_N, 0x5c, SPX_SHA256_BLOCK_BYTES - SPX_N); + + sha256(buf, buf, SPX_SHA256_BLOCK_BYTES + SPX_SHA256_OUTPUT_BYTES); + memcpy(R, buf, SPX_N); +} + +/** + * Computes the message hash using R, the public key, and the message. + * Outputs the message digest and the index of the leaf. The index is split in + * the tree index and the leaf index, for convenient copying to an address. + */ +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen) { +#define SPX_TREE_BITS (SPX_TREE_HEIGHT * (SPX_D - 1)) +#define SPX_TREE_BYTES ((SPX_TREE_BITS + 7) / 8) +#define SPX_LEAF_BITS SPX_TREE_HEIGHT +#define SPX_LEAF_BYTES ((SPX_LEAF_BITS + 7) / 8) +#define SPX_DGST_BYTES (SPX_FORS_MSG_BYTES + SPX_TREE_BYTES + SPX_LEAF_BYTES) + + unsigned char seed[SPX_SHA256_OUTPUT_BYTES + 4]; + + /* Round to nearest multiple of SPX_SHA256_BLOCK_BYTES */ +#define SPX_INBLOCKS (((SPX_N + SPX_PK_BYTES + SPX_SHA256_BLOCK_BYTES - 1) & \ + -SPX_SHA256_BLOCK_BYTES) / SPX_SHA256_BLOCK_BYTES) + unsigned char inbuf[SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES]; + + unsigned char buf[SPX_DGST_BYTES]; + unsigned char *bufp = buf; + uint8_t state[40]; + + sha256_inc_init(state); + + memcpy(inbuf, R, SPX_N); + memcpy(inbuf + SPX_N, pk, SPX_PK_BYTES); + + /* If R + pk + message cannot fill up an entire block */ + if (SPX_N + SPX_PK_BYTES + mlen < SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES) { + memcpy(inbuf + SPX_N + SPX_PK_BYTES, m, mlen); + sha256_inc_finalize(seed, state, inbuf, SPX_N + SPX_PK_BYTES + mlen); + } + /* Otherwise first fill a block, so that finalize only uses the message */ + else { + memcpy(inbuf + SPX_N + SPX_PK_BYTES, m, + SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES); + sha256_inc_blocks(state, inbuf, SPX_INBLOCKS); + + m += SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES; + mlen -= SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES; + sha256_inc_finalize(seed, state, m, mlen); + } + + /* By doing this in two steps, we prevent hashing the message twice; + otherwise each iteration in MGF1 would hash the message again. */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_mgf1(bufp, SPX_DGST_BYTES, seed, SPX_SHA256_OUTPUT_BYTES); + + memcpy(digest, bufp, SPX_FORS_MSG_BYTES); + bufp += SPX_FORS_MSG_BYTES; + + *tree = PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_bytes_to_ull(bufp, SPX_TREE_BYTES); + *tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS); + bufp += SPX_TREE_BYTES; + + *leaf_idx = (uint32_t)PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_bytes_to_ull( + bufp, SPX_LEAF_BYTES); + *leaf_idx &= (~(uint32_t)0) >> (32 - SPX_LEAF_BITS); +} diff --git a/crypto_sign/sphincs-sha256-128f-simple/clean/params.h b/crypto_sign/sphincs-sha256-128f-simple/clean/params.h new file mode 100644 index 00000000..367ef88d --- /dev/null +++ b/crypto_sign/sphincs-sha256-128f-simple/clean/params.h @@ -0,0 +1,53 @@ +#ifndef SPX_PARAMS_H +#define SPX_PARAMS_H + +/* Hash output length in bytes. */ +#define SPX_N 16 +/* Height of the hypertree. */ +#define SPX_FULL_HEIGHT 60 +/* Number of subtree layer. */ +#define SPX_D 20 +/* FORS tree dimensions. */ +#define SPX_FORS_HEIGHT 9 +#define SPX_FORS_TREES 30 +/* Winternitz parameter, */ +#define SPX_WOTS_W 16 + +/* The hash function is defined by linking a different hash.c file, as opposed + to setting a #define constant. */ + +/* For clarity */ +#define SPX_ADDR_BYTES 32 + +/* WOTS parameters. */ +#define SPX_WOTS_LOGW 4 + +#define SPX_WOTS_LEN1 (8 * SPX_N / SPX_WOTS_LOGW) + +/* SPX_WOTS_LEN2 is floor(log(len_1 * (w - 1)) / log(w)) + 1; we precompute */ +#define SPX_WOTS_LEN2 3 + +#define SPX_WOTS_LEN (SPX_WOTS_LEN1 + SPX_WOTS_LEN2) +#define SPX_WOTS_BYTES (SPX_WOTS_LEN * SPX_N) +#define SPX_WOTS_PK_BYTES SPX_WOTS_BYTES + +/* Subtree size. */ +#define SPX_TREE_HEIGHT (SPX_FULL_HEIGHT / SPX_D) + +/* FORS parameters. */ +#define SPX_FORS_MSG_BYTES ((SPX_FORS_HEIGHT * SPX_FORS_TREES + 7) / 8) +#define SPX_FORS_BYTES ((SPX_FORS_HEIGHT + 1) * SPX_FORS_TREES * SPX_N) +#define SPX_FORS_PK_BYTES SPX_N + +/* Resulting SPX sizes. */ +#define SPX_BYTES (SPX_N + SPX_FORS_BYTES + SPX_D * SPX_WOTS_BYTES +\ + SPX_FULL_HEIGHT * SPX_N) +#define SPX_PK_BYTES (2 * SPX_N) +#define SPX_SK_BYTES (2 * SPX_N + SPX_PK_BYTES) + +/* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ +#define SPX_OPTRAND_BYTES 32 + +#endif diff --git a/crypto_sign/sphincs-sha256-128f-simple/clean/sha256.c b/crypto_sign/sphincs-sha256-128f-simple/clean/sha256.c new file mode 100644 index 00000000..f37d567b --- /dev/null +++ b/crypto_sign/sphincs-sha256-128f-simple/clean/sha256.c @@ -0,0 +1,72 @@ +/* Based on the public domain implementation in + * crypto_hash/sha512/ref/ from http://bench.cr.yp.to/supercop.html + * by D. J. Bernstein */ + +#include +#include +#include + +#include "sha2.h" +#include "sha256.h" +#include "utils.h" + +/* + * Compresses an address to a 22-byte sequence. + * This reduces the number of required SHA256 compression calls, as the last + * block of input is padded with at least 65 bits. + */ +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_compress_address(unsigned char *out, const uint32_t addr[8]) { + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_ull_to_bytes(out, 1, addr[0]); /* drop 3 bytes of the layer field */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_ull_to_bytes(out + 1, 4, addr[2]); /* drop the highest tree address word */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_ull_to_bytes(out + 5, 4, addr[3]); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_ull_to_bytes(out + 9, 1, addr[4]); /* drop 3 bytes of the type field */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_ull_to_bytes(out + 10, 4, addr[5]); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_ull_to_bytes(out + 14, 4, addr[6]); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_ull_to_bytes(out + 18, 4, addr[7]); +} + +/** + * Requires 'input_plus_four_bytes' to have 'inlen' + 4 bytes, so that the last + * four bytes can be used for the counter. Typically 'input' is merely a seed. + * Outputs outlen number of bytes + */ +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + unsigned char *input_plus_four_bytes, unsigned long inlen) { + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + unsigned long i; + + /* While we can fit in at least another full block of SHA256 output.. */ + for (i = 0; (i + 1)*SPX_SHA256_OUTPUT_BYTES <= outlen; i++) { + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_ull_to_bytes(input_plus_four_bytes + inlen, 4, i); + sha256(out, input_plus_four_bytes, inlen + 4); + out += SPX_SHA256_OUTPUT_BYTES; + } + /* Until we cannot anymore, and we fill the remainder. */ + if (outlen > i * SPX_SHA256_OUTPUT_BYTES) { + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_ull_to_bytes(input_plus_four_bytes + inlen, 4, i); + sha256(outbuf, input_plus_four_bytes, inlen + 4); + memcpy(out, outbuf, outlen - i * SPX_SHA256_OUTPUT_BYTES); + } +} + +uint8_t PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_state_seeded[40]; + +/** + * Absorb the constant pub_seed using one round of the compression function + * This initializes state_seeded, which can then be reused in thash + **/ +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_seed_state(const unsigned char *pub_seed) { + uint8_t block[SPX_SHA256_BLOCK_BYTES]; + size_t i; + + for (i = 0; i < SPX_N; ++i) { + block[i] = pub_seed[i]; + } + for (i = SPX_N; i < SPX_SHA256_BLOCK_BYTES; ++i) { + block[i] = 0; + } + + sha256_inc_init(PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_state_seeded); + sha256_inc_blocks(PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_state_seeded, block, 1); +} diff --git a/crypto_sign/sphincs-sha256-128f-simple/clean/sha256.h b/crypto_sign/sphincs-sha256-128f-simple/clean/sha256.h new file mode 100644 index 00000000..0fe2fd3c --- /dev/null +++ b/crypto_sign/sphincs-sha256-128f-simple/clean/sha256.h @@ -0,0 +1,21 @@ +#ifndef SPX_SHA256_H +#define SPX_SHA256_H + +#define SPX_SHA256_BLOCK_BYTES 64 +#define SPX_SHA256_OUTPUT_BYTES 32 /* This does not necessarily equal SPX_N */ +#define SPX_SHA256_ADDR_BYTES 22 + +#include +#include + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_compress_address(unsigned char *out, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + unsigned char *input_plus_four_bytes, unsigned long inlen); + +uint8_t PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_state_seeded[40]; + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_seed_state(const unsigned char *pub_seed); + +#endif diff --git a/crypto_sign/sphincs-sha256-128f-simple/clean/sign.c b/crypto_sign/sphincs-sha256-128f-simple/clean/sign.c new file mode 100644 index 00000000..e32ad165 --- /dev/null +++ b/crypto_sign/sphincs-sha256-128f-simple/clean/sign.c @@ -0,0 +1,344 @@ +#include +#include +#include + +#include "address.h" +#include "api.h" +#include "fors.h" +#include "hash.h" +#include "params.h" +#include "randombytes.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +/** + * Computes the leaf at a given address. First generates the WOTS key pair, + * then computes leaf by hashing horizontally. + */ +static void wots_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t tree_addr[8]) { + unsigned char pk[SPX_WOTS_BYTES]; + uint32_t wots_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_keypair_addr( + wots_addr, addr_idx); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_wots_gen_pk( + pk, sk_seed, pub_seed, wots_addr); + + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_thash_WOTS_LEN( + leaf, pk, pub_seed, wots_pk_addr); +} + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_crypto_sign_secretkeybytes(void) { + return PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_CRYPTO_SECRETKEYBYTES; +} + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_crypto_sign_publickeybytes(void) { + return PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES; +} + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_crypto_sign_bytes(void) { + return PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_CRYPTO_BYTES; +} + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_crypto_sign_seedbytes(void) { + return PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_CRYPTO_SEEDBYTES; +} + +/* + * Generates an SPX key pair given a seed of length + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed) { + /* We do not need the auth path in key generation, but it simplifies the + code to have just one treehash routine that computes both root and path + in one function. */ + unsigned char auth_path[SPX_TREE_HEIGHT * SPX_N]; + uint32_t top_tree_addr[8] = {0}; + + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_layer_addr( + top_tree_addr, SPX_D - 1); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_type( + top_tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */ + memcpy(sk, seed, PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_CRYPTO_SEEDBYTES); + + memcpy(pk, sk + 2 * SPX_N, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_initialize_hash_function(pk, sk); + + /* Compute root node of the top-most subtree. */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_treehash_TREE_HEIGHT( + sk + 3 * SPX_N, auth_path, sk, sk + 2 * SPX_N, 0, 0, + wots_gen_leaf, top_tree_addr); + + memcpy(pk + SPX_N, sk + 3 * SPX_N, SPX_N); + + return 0; +} + +/* + * Generates an SPX key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk) { + unsigned char seed[PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_CRYPTO_SEEDBYTES]; + randombytes(seed, PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_CRYPTO_SEEDBYTES); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_crypto_sign_seed_keypair( + pk, sk, seed); + + return 0; +} + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + const unsigned char *sk_seed = sk; + const unsigned char *sk_prf = sk + SPX_N; + const unsigned char *pk = sk + 2 * SPX_N; + const unsigned char *pub_seed = pk; + + unsigned char optrand[SPX_N]; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char root[SPX_N]; + uint32_t i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_initialize_hash_function( + pub_seed, sk_seed); + + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ + randombytes(optrand, SPX_N); + /* Compute the digest randomization value. */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_gen_message_random( + sig, sk_prf, optrand, m, mlen); + + /* Derive the message digest and leaf index from R, PK and M. */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Sign the message hash using FORS. */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_fors_sign( + sig, root, mhash, sk_seed, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Compute a WOTS signature. */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_wots_sign( + sig, root, sk_seed, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the authentication path for the used WOTS leaf. */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_treehash_TREE_HEIGHT( + root, sig, sk_seed, pub_seed, idx_leaf, 0, + wots_gen_leaf, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + *siglen = SPX_BYTES; + + return 0; +} + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk) { + const unsigned char *pub_seed = pk; + const unsigned char *pub_root = pk + SPX_N; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char wots_pk[SPX_WOTS_BYTES]; + unsigned char root[SPX_N]; + unsigned char leaf[SPX_N]; + unsigned int i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + if (siglen != SPX_BYTES) { + return -1; + } + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_initialize_hash_function( + pub_seed, NULL); + + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + /* Derive the message digest and leaf index from R || PK || M. */ + /* The additional SPX_N is a result of the hash domain separator. */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + /* Layer correctly defaults to 0, so no need to set_layer_addr */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_fors_pk_from_sig( + root, sig, mhash, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + /* For each subtree.. */ + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + + /* The WOTS public key is only correct if the signature was correct. */ + /* Initially, root is the FORS pk, but on subsequent iterations it is + the root of the subtree below the currently processed subtree. */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_wots_pk_from_sig( + wots_pk, sig, root, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the leaf node using the WOTS public key. */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_thash_WOTS_LEN( + leaf, wots_pk, pub_seed, wots_pk_addr); + + /* Compute the root node of this subtree. */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_compute_root( + root, leaf, idx_leaf, 0, sig, SPX_TREE_HEIGHT, + pub_seed, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + /* Check if the root node equals the root node in the public key. */ + if (memcmp(root, pub_root, SPX_N) != 0) { + return -1; + } + + return 0; +} + + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + size_t siglen; + + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_crypto_sign_signature( + sm, &siglen, m, mlen, sk); + + memmove(sm + SPX_BYTES, m, mlen); + *smlen = siglen + mlen; + + return 0; +} + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk) { + /* The API caller does not necessarily know what size a signature should be + but SPHINCS+ signatures are always exactly SPX_BYTES. */ + if (smlen < SPX_BYTES) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + *mlen = smlen - SPX_BYTES; + + if (PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_crypto_sign_verify( + sm, SPX_BYTES, sm + SPX_BYTES, *mlen, pk)) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + /* If verification was successful, move the message to the right place. */ + memmove(m, sm + SPX_BYTES, *mlen); + + return 0; +} diff --git a/crypto_sign/sphincs-sha256-128f-simple/clean/thash.h b/crypto_sign/sphincs-sha256-128f-simple/clean/thash.h new file mode 100644 index 00000000..88c1f456 --- /dev/null +++ b/crypto_sign/sphincs-sha256-128f-simple/clean/thash.h @@ -0,0 +1,22 @@ +#ifndef SPX_THASH_H +#define SPX_THASH_H + +#include + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-sha256-128f-simple/clean/thash_sha256_simple.c b/crypto_sign/sphincs-sha256-128f-simple/clean/thash_sha256_simple.c new file mode 100644 index 00000000..4c14be56 --- /dev/null +++ b/crypto_sign/sphincs-sha256-128f-simple/clean/thash_sha256_simple.c @@ -0,0 +1,70 @@ +#include +#include + +#include "address.h" +#include "params.h" +#include "thash.h" + +#include "sha2.h" +#include "sha256.h" + +/** + * Takes an array of inblocks concatenated arrays of SPX_N bytes. + */ +static void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_thash( + unsigned char *out, unsigned char *buf, + const unsigned char *in, unsigned int inblocks, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + uint8_t sha2_state[40]; + + (void)pub_seed; /* Suppress an 'unused parameter' warning. */ + + /* Retrieve precomputed state containing pub_seed */ + memcpy(sha2_state, PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_state_seeded, 40 * sizeof(uint8_t)); + + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_compress_address(buf, addr); + memcpy(buf + SPX_SHA256_ADDR_BYTES, in, inblocks * SPX_N); + + sha256_inc_finalize(outbuf, sha2_state, buf, SPX_SHA256_ADDR_BYTES + inblocks * SPX_N); + memcpy(out, outbuf, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_SHA256_ADDR_BYTES + 1 * SPX_N]; + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_thash( + out, buf, in, 1, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_SHA256_ADDR_BYTES + 2 * SPX_N]; + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_thash( + out, buf, in, 2, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_SHA256_ADDR_BYTES + SPX_WOTS_LEN * SPX_N]; + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_thash( + out, buf, in, SPX_WOTS_LEN, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_SHA256_ADDR_BYTES + SPX_FORS_TREES * SPX_N]; + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_thash( + out, buf, in, SPX_FORS_TREES, pub_seed, addr); +} diff --git a/crypto_sign/sphincs-sha256-128f-simple/clean/utils.c b/crypto_sign/sphincs-sha256-128f-simple/clean/utils.c new file mode 100644 index 00000000..8fdc9c16 --- /dev/null +++ b/crypto_sign/sphincs-sha256-128f-simple/clean/utils.c @@ -0,0 +1,192 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in) { + + /* Iterate over out in decreasing order, for big-endianness. */ + for (size_t i = outlen; i > 0; i--) { + out[i - 1] = in & 0xff; + in = in >> 8; + } +} + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen) { + unsigned long long retval = 0; + + for (size_t i = 0; i < inlen; i++) { + retval |= ((unsigned long long)in[i]) << (8 * (inlen - 1 - i)); + } + return retval; +} + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + unsigned char buffer[2 * SPX_N]; + + /* If leaf_idx is odd (last bit = 1), current path element is a right child + and auth_path has to go left. Otherwise it is the other way around. */ + if (leaf_idx & 1) { + memcpy(buffer + SPX_N, leaf, SPX_N); + memcpy(buffer, auth_path, SPX_N); + } else { + memcpy(buffer, leaf, SPX_N); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + + for (i = 0; i < tree_height - 1; i++) { + leaf_idx >>= 1; + idx_offset >>= 1; + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_tree_height(addr, i + 1); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + + /* Pick the right or left neighbor, depending on parity of the node. */ + if (leaf_idx & 1) { + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_thash_2( + buffer + SPX_N, buffer, pub_seed, addr); + memcpy(buffer, auth_path, SPX_N); + } else { + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_thash_2( + buffer, buffer, pub_seed, addr); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + } + + /* The last iteration is exceptional; we do not copy an auth_path node. */ + leaf_idx >>= 1; + idx_offset >>= 1; + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_tree_height(addr, tree_height); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_thash_2( + root, buffer, pub_seed, addr); +} + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +static void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_treehash( + unsigned char *root, unsigned char *auth_path, + unsigned char *stack, unsigned int *heights, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned int offset = 0; + uint32_t idx; + uint32_t tree_idx; + + for (idx = 0; idx < (uint32_t)(1 << tree_height); idx++) { + /* Add the next leaf node to the stack. */ + gen_leaf(stack + offset * SPX_N, + sk_seed, pub_seed, idx + idx_offset, tree_addr); + offset++; + heights[offset - 1] = 0; + + /* If this is a node we need for the auth path.. */ + if ((leaf_idx ^ 0x1) == idx) { + memcpy(auth_path, stack + (offset - 1)*SPX_N, SPX_N); + } + + /* While the top-most nodes are of equal height.. */ + while (offset >= 2 && heights[offset - 1] == heights[offset - 2]) { + /* Compute index of the new node, in the next layer. */ + tree_idx = (idx >> (heights[offset - 1] + 1)); + + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_tree_height( + tree_addr, heights[offset - 1] + 1); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_tree_index( + tree_addr, tree_idx + (idx_offset >> (heights[offset - 1] + 1))); + /* Hash the top-most nodes from the stack together. */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_thash_2( + stack + (offset - 2)*SPX_N, stack + (offset - 2)*SPX_N, + pub_seed, tree_addr); + offset--; + /* Note that the top-most node is now one layer higher. */ + heights[offset - 1]++; + + /* If this is a node we need for the auth path.. */ + if (((leaf_idx >> heights[offset - 1]) ^ 0x1) == tree_idx) { + memcpy(auth_path + heights[offset - 1]*SPX_N, + stack + (offset - 1)*SPX_N, SPX_N); + } + } + } + memcpy(root, stack, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_FORS_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_FORS_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_FORS_HEIGHT, gen_leaf, tree_addr); +} + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_TREE_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_TREE_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_TREE_HEIGHT, gen_leaf, tree_addr); +} diff --git a/crypto_sign/sphincs-sha256-128f-simple/clean/utils.h b/crypto_sign/sphincs-sha256-128f-simple/clean/utils.h new file mode 100644 index 00000000..354e5c7e --- /dev/null +++ b/crypto_sign/sphincs-sha256-128f-simple/clean/utils.h @@ -0,0 +1,60 @@ +#ifndef SPX_UTILS_H +#define SPX_UTILS_H + +#include "params.h" +#include +#include + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in); + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen); + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-sha256-128f-simple/clean/wots.c b/crypto_sign/sphincs-sha256-128f-simple/clean/wots.c new file mode 100644 index 00000000..d511a042 --- /dev/null +++ b/crypto_sign/sphincs-sha256-128f-simple/clean/wots.c @@ -0,0 +1,161 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +// TODO clarify address expectations, and make them more uniform. +// TODO i.e. do we expect types to be set already? +// TODO and do we expect modifications or copies? + +/** + * Computes the starting value for a chain, i.e. the secret key. + * Expects the address to be complete up to the chain address. + */ +static void wots_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t wots_addr[8]) { + /* Make sure that the hash address is actually zeroed. */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_hash_addr(wots_addr, 0); + + /* Generate sk element. */ + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_prf_addr(sk, sk_seed, wots_addr); +} + +/** + * Computes the chaining function. + * out and in have to be n-byte arrays. + * + * Interprets in as start-th value of the chain. + * addr has to contain the address of the chain. + */ +static void gen_chain(unsigned char *out, const unsigned char *in, + unsigned int start, unsigned int steps, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + /* Initialize out with the value at position 'start'. */ + memcpy(out, in, SPX_N); + + /* Iterate 'steps' calls to the hash function. */ + for (i = start; i < (start + steps) && i < SPX_WOTS_W; i++) { + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_hash_addr(addr, i); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_thash_1( + out, out, pub_seed, addr); + } +} + +/** + * base_w algorithm as described in draft. + * Interprets an array of bytes as integers in base w. + * This only works when log_w is a divisor of 8. + */ +static void base_w(unsigned int *output, const size_t out_len, + const unsigned char *input) { + size_t in = 0; + size_t out = 0; + unsigned char total = 0; + unsigned int bits = 0; + size_t consumed; + + for (consumed = 0; consumed < out_len; consumed++) { + if (bits == 0) { + total = input[in]; + in++; + bits += 8; + } + bits -= SPX_WOTS_LOGW; + output[out] = (unsigned int)((total >> bits) & (SPX_WOTS_W - 1)); + out++; + } +} + +/* Computes the WOTS+ checksum over a message (in base_w). */ +static void wots_checksum(unsigned int *csum_base_w, + const unsigned int *msg_base_w) { + unsigned int csum = 0; + unsigned char csum_bytes[(SPX_WOTS_LEN2 * SPX_WOTS_LOGW + 7) / 8]; + unsigned int i; + + /* Compute checksum. */ + for (i = 0; i < SPX_WOTS_LEN1; i++) { + csum += SPX_WOTS_W - 1 - msg_base_w[i]; + } + + /* Convert checksum to base_w. */ + /* Make sure expected empty zero bits are the least significant bits. */ + csum = csum << (8 - ((SPX_WOTS_LEN2 * SPX_WOTS_LOGW) % 8)); + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_ull_to_bytes( + csum_bytes, sizeof(csum_bytes), csum); + base_w(csum_base_w, SPX_WOTS_LEN2, csum_bytes); +} + +/* Takes a message and derives the matching chain lengths. */ +static void chain_lengths(unsigned int *lengths, const unsigned char *msg) { + base_w(lengths, SPX_WOTS_LEN1, msg); + wots_checksum(lengths + SPX_WOTS_LEN1, lengths); +} + +/** + * WOTS key generation. Takes a 32 byte sk_seed, expands it to WOTS private key + * elements and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(pk + i * SPX_N, sk_seed, addr); + gen_chain(pk + i * SPX_N, pk + i * SPX_N, + 0, SPX_WOTS_W - 1, pub_seed, addr); + } +} + +/** + * Takes a n-byte message and the 32-byte sk_see to compute a signature 'sig'. + */ +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(sig + i * SPX_N, sk_seed, addr); + gen_chain(sig + i * SPX_N, sig + i * SPX_N, 0, lengths[i], pub_seed, addr); + } +} + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_set_chain_addr(addr, i); + gen_chain(pk + i * SPX_N, sig + i * SPX_N, + lengths[i], SPX_WOTS_W - 1 - lengths[i], pub_seed, addr); + } +} diff --git a/crypto_sign/sphincs-sha256-128f-simple/clean/wots.h b/crypto_sign/sphincs-sha256-128f-simple/clean/wots.h new file mode 100644 index 00000000..e353186e --- /dev/null +++ b/crypto_sign/sphincs-sha256-128f-simple/clean/wots.h @@ -0,0 +1,38 @@ +#ifndef SPX_WOTS_H +#define SPX_WOTS_H + +#include "params.h" +#include + +/** + * WOTS key generation. Takes a 32 byte seed for the private key, expands it to + * a full WOTS private key and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * Takes a n-byte message and the 32-byte seed for the private key to compute a + * signature that is placed at 'sig'. + */ +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]); + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-sha256-128s-robust/META.yml b/crypto_sign/sphincs-sha256-128s-robust/META.yml new file mode 100644 index 00000000..7f85adfd --- /dev/null +++ b/crypto_sign/sphincs-sha256-128s-robust/META.yml @@ -0,0 +1,27 @@ +name: SPHINCS+ +type: signature +claimed-nist-level: 1 +length-public-key: 32 +length-secret-key: 64 +length-signature: 8080 +testvectors-sha256: 29d6d0dd732078d177779a61b7654bbe59fcf2ecb9bcd2ade8391791a6570a63 +principal-submitter: Andreas Hülsing +auxiliary-submitters: + - Jean-Philippe Aumasson + - Daniel J. Bernstein, + - Christoph Dobraunig + - Maria Eichlseder + - Scott Fluhrer + - Stefan-Lukas Gazdag + - Panos Kampanakis + - Stefan Kölbl + - Tanja Lange + - Martin M. Lauridsen + - Florian Mendel + - Ruben Niederhagen + - Christian Rechberger + - Joost Rijneveld + - Peter Schwabe +implementations: + - name: clean + version: https://github.com/sphincs/sphincsplus/commit/77755c94d0bc744478044d6efbb888dc13156441 diff --git a/crypto_sign/sphincs-sha256-128s-robust/clean/LICENSE b/crypto_sign/sphincs-sha256-128s-robust/clean/LICENSE new file mode 100644 index 00000000..670154e3 --- /dev/null +++ b/crypto_sign/sphincs-sha256-128s-robust/clean/LICENSE @@ -0,0 +1,116 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + diff --git a/crypto_sign/sphincs-sha256-128s-robust/clean/Makefile b/crypto_sign/sphincs-sha256-128s-robust/clean/Makefile new file mode 100644 index 00000000..54dc7ba3 --- /dev/null +++ b/crypto_sign/sphincs-sha256-128s-robust/clean/Makefile @@ -0,0 +1,20 @@ +# This Makefile can be used with GNU Make or BSD Make + +LIB=libsphincs-sha256-128s-robust_clean.a + +HEADERS = params.h address.h wots.h utils.h fors.h api.h hash.h thash.h sha256.h +OBJECTS = address.o wots.o utils.o fors.o sign.o hash_sha256.o thash_sha256_robust.o sha256.o + +CFLAGS=-O3 -Wall -Wconversion -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) + +all: $(LIB) + +%.o: %.c $(HEADERS) + $(CC) $(CFLAGS) -c -o $@ $< + +$(LIB): $(OBJECTS) + $(AR) -r $@ $(OBJECTS) + +clean: + $(RM) $(OBJECTS) + $(RM) $(LIB) diff --git a/crypto_sign/sphincs-sha256-128s-robust/clean/Makefile.Microsoft_nmake b/crypto_sign/sphincs-sha256-128s-robust/clean/Makefile.Microsoft_nmake new file mode 100644 index 00000000..e92444e3 --- /dev/null +++ b/crypto_sign/sphincs-sha256-128s-robust/clean/Makefile.Microsoft_nmake @@ -0,0 +1,19 @@ +# This Makefile can be used with Microsoft Visual Studio's nmake using the command: +# nmake /f Makefile.Microsoft_nmake + +LIBRARY=libsphincs-sha256-128s-robust_clean.lib +OBJECTS=address.obj wots.obj utils.obj fors.obj sign.obj hash_sha256.obj thash_sha256_robust.obj sha256.obj + +CFLAGS=/nologo /I ..\..\..\common /W4 /WX + +all: $(LIBRARY) + +# Make sure objects are recompiled if headers change. +$(OBJECTS): *.h + +$(LIBRARY): $(OBJECTS) + LIB.EXE /NOLOGO /WX /OUT:$@ $** + +clean: + -DEL $(OBJECTS) + -DEL $(LIBRARY) diff --git a/crypto_sign/sphincs-sha256-128s-robust/clean/address.c b/crypto_sign/sphincs-sha256-128s-robust/clean/address.c new file mode 100644 index 00000000..5b946b75 --- /dev/null +++ b/crypto_sign/sphincs-sha256-128s-robust/clean/address.c @@ -0,0 +1,78 @@ +#include + +#include "address.h" +#include "params.h" +#include "utils.h" + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]) { + int i; + + for (i = 0; i < 8; i++) { + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_ull_to_bytes( + bytes + i * 4, 4, addr[i]); + } +} + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer) { + addr[0] = layer; +} + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree) { + addr[1] = 0; + addr[2] = (uint32_t) (tree >> 32); + addr[3] = (uint32_t) tree; +} + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_type( + uint32_t addr[8], uint32_t type) { + addr[4] = type; +} + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; +} + +/* These functions are used for OTS addresses. */ + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair) { + addr[5] = keypair; +} + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; + out[5] = in[5]; +} + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain) { + addr[6] = chain; +} + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash) { + addr[7] = hash; +} + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height) { + addr[6] = tree_height; +} + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index) { + addr[7] = tree_index; +} diff --git a/crypto_sign/sphincs-sha256-128s-robust/clean/address.h b/crypto_sign/sphincs-sha256-128s-robust/clean/address.h new file mode 100644 index 00000000..974c86f4 --- /dev/null +++ b/crypto_sign/sphincs-sha256-128s-robust/clean/address.h @@ -0,0 +1,50 @@ +#ifndef SPX_ADDRESS_H +#define SPX_ADDRESS_H + +#include + +#define SPX_ADDR_TYPE_WOTS 0 +#define SPX_ADDR_TYPE_WOTSPK 1 +#define SPX_ADDR_TYPE_HASHTREE 2 +#define SPX_ADDR_TYPE_FORSTREE 3 +#define SPX_ADDR_TYPE_FORSPK 4 + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer); + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree); + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_type( + uint32_t addr[8], uint32_t type); + +/* Copies the layer and tree part of one address into the other */ +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for WOTS and FORS addresses. */ + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair); + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain); + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash); + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height); + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index); + +#endif diff --git a/crypto_sign/sphincs-sha256-128s-robust/clean/api.h b/crypto_sign/sphincs-sha256-128s-robust/clean/api.h new file mode 100644 index 00000000..1f339b06 --- /dev/null +++ b/crypto_sign/sphincs-sha256-128s-robust/clean/api.h @@ -0,0 +1,78 @@ +#ifndef PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_API_H +#define PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_API_H + +#include +#include + +#define PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_CRYPTO_ALGNAME "SPHINCS+" + +#define PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_CRYPTO_SECRETKEYBYTES 64 +#define PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES 32 +#define PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_CRYPTO_BYTES 8080 +#define PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_CRYPTO_SEEDBYTES 48 + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_crypto_sign_secretkeybytes(void); + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_crypto_sign_publickeybytes(void); + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_crypto_sign_bytes(void); + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_crypto_sign_seedbytes(void); + +/* + * Generates a SPHINCS+ key pair given a seed. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed); + +/* + * Generates a SPHINCS+ key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk); + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk); + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk); + +#endif diff --git a/crypto_sign/sphincs-sha256-128s-robust/clean/fors.c b/crypto_sign/sphincs-sha256-128s-robust/clean/fors.c new file mode 100644 index 00000000..7bb50cdc --- /dev/null +++ b/crypto_sign/sphincs-sha256-128s-robust/clean/fors.c @@ -0,0 +1,164 @@ +#include +#include +#include + +#include "address.h" +#include "fors.h" +#include "hash.h" +#include "thash.h" +#include "utils.h" + +static void fors_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_prf_addr( + sk, sk_seed, fors_leaf_addr); +} + +static void fors_sk_to_leaf(unsigned char *leaf, const unsigned char *sk, + const unsigned char *pub_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_thash_1( + leaf, sk, pub_seed, fors_leaf_addr); +} + +static void fors_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t fors_tree_addr[8]) { + uint32_t fors_leaf_addr[8] = {0}; + + /* Only copy the parts that must be kept in fors_leaf_addr. */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_copy_keypair_addr( + fors_leaf_addr, fors_tree_addr); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_type( + fors_leaf_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_tree_index( + fors_leaf_addr, addr_idx); + + fors_gen_sk(leaf, sk_seed, fors_leaf_addr); + fors_sk_to_leaf(leaf, leaf, pub_seed, fors_leaf_addr); +} + +/** + * Interprets m as SPX_FORS_HEIGHT-bit unsigned integers. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + * Assumes indices has space for SPX_FORS_TREES integers. + */ +static void message_to_indices(uint32_t *indices, const unsigned char *m) { + unsigned int i, j; + unsigned int offset = 0; + + for (i = 0; i < SPX_FORS_TREES; i++) { + indices[i] = 0; + for (j = 0; j < SPX_FORS_HEIGHT; j++) { + indices[i] ^= (((uint32_t)m[offset >> 3] >> (offset & 0x7)) & 0x1) << j; + offset++; + } + } +} + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Include the secret key part that produces the selected leaf node. */ + fors_gen_sk(sig, sk_seed, fors_tree_addr); + sig += SPX_N; + + /* Compute the authentication path for this leaf node. */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_treehash_FORS_HEIGHT( + roots + i * SPX_N, sig, sk_seed, pub_seed, + indices[i], idx_offset, fors_gen_leaf, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + unsigned char leaf[SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Derive the leaf from the included secret key part. */ + fors_sk_to_leaf(leaf, sig, pub_seed, fors_tree_addr); + sig += SPX_N; + + /* Derive the corresponding root node of this tree. */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_compute_root( + roots + i * SPX_N, leaf, indices[i], idx_offset, sig, + SPX_FORS_HEIGHT, pub_seed, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} diff --git a/crypto_sign/sphincs-sha256-128s-robust/clean/fors.h b/crypto_sign/sphincs-sha256-128s-robust/clean/fors.h new file mode 100644 index 00000000..4ae034c9 --- /dev/null +++ b/crypto_sign/sphincs-sha256-128s-robust/clean/fors.h @@ -0,0 +1,30 @@ +#ifndef SPX_FORS_H +#define SPX_FORS_H + +#include + +#include "params.h" + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]); + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-sha256-128s-robust/clean/hash.h b/crypto_sign/sphincs-sha256-128s-robust/clean/hash.h new file mode 100644 index 00000000..2027c791 --- /dev/null +++ b/crypto_sign/sphincs-sha256-128s-robust/clean/hash.h @@ -0,0 +1,22 @@ +#ifndef SPX_HASH_H +#define SPX_HASH_H + +#include + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed); + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen); + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen); + +#endif diff --git a/crypto_sign/sphincs-sha256-128s-robust/clean/hash_sha256.c b/crypto_sign/sphincs-sha256-128s-robust/clean/hash_sha256.c new file mode 100644 index 00000000..c15b44b1 --- /dev/null +++ b/crypto_sign/sphincs-sha256-128s-robust/clean/hash_sha256.c @@ -0,0 +1,148 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "utils.h" + +#include "sha2.h" +#include "sha256.h" + +/* For SHA256, there is no immediate reason to initialize at the start, + so this function is an empty operation. */ +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed) { + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_seed_state(pub_seed); + (void)sk_seed; /* Suppress an 'unused parameter' warning. */ +} + +/* + * Computes PRF(key, addr), given a secret key of SPX_N bytes and an address + */ +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]) { + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES]; + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + + memcpy(buf, key, SPX_N); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_compress_address(buf + SPX_N, addr); + + sha256(outbuf, buf, SPX_N + SPX_SHA256_ADDR_BYTES); + memcpy(out, outbuf, SPX_N); +} + +/** + * Computes the message-dependent randomness R, using a secret seed as a key + * for HMAC, and an optional randomization value prefixed to the message. + * This requires m to have at least SPX_SHA256_BLOCK_BYTES + SPX_N space + * available in front of the pointer, i.e. before the message to use for the + * prefix. This is necessary to prevent having to move the message around (and + * allocate memory for it). + */ +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen) { + unsigned char buf[SPX_SHA256_BLOCK_BYTES + SPX_SHA256_OUTPUT_BYTES]; + uint8_t state[40]; + int i; + + /* This implements HMAC-SHA256 */ + for (i = 0; i < SPX_N; i++) { + buf[i] = 0x36 ^ sk_prf[i]; + } + memset(buf + SPX_N, 0x36, SPX_SHA256_BLOCK_BYTES - SPX_N); + + sha256_inc_init(state); + sha256_inc_blocks(state, buf, 1); + + memcpy(buf, optrand, SPX_N); + + /* If optrand + message cannot fill up an entire block */ + if (SPX_N + mlen < SPX_SHA256_BLOCK_BYTES) { + memcpy(buf + SPX_N, m, mlen); + sha256_inc_finalize(buf + SPX_SHA256_BLOCK_BYTES, state, + buf, mlen + SPX_N); + } + /* Otherwise first fill a block, so that finalize only uses the message */ + else { + memcpy(buf + SPX_N, m, SPX_SHA256_BLOCK_BYTES - SPX_N); + sha256_inc_blocks(state, buf, 1); + + m += SPX_SHA256_BLOCK_BYTES - SPX_N; + mlen -= SPX_SHA256_BLOCK_BYTES - SPX_N; + sha256_inc_finalize(buf + SPX_SHA256_BLOCK_BYTES, state, m, mlen); + } + + for (i = 0; i < SPX_N; i++) { + buf[i] = 0x5c ^ sk_prf[i]; + } + memset(buf + SPX_N, 0x5c, SPX_SHA256_BLOCK_BYTES - SPX_N); + + sha256(buf, buf, SPX_SHA256_BLOCK_BYTES + SPX_SHA256_OUTPUT_BYTES); + memcpy(R, buf, SPX_N); +} + +/** + * Computes the message hash using R, the public key, and the message. + * Outputs the message digest and the index of the leaf. The index is split in + * the tree index and the leaf index, for convenient copying to an address. + */ +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen) { +#define SPX_TREE_BITS (SPX_TREE_HEIGHT * (SPX_D - 1)) +#define SPX_TREE_BYTES ((SPX_TREE_BITS + 7) / 8) +#define SPX_LEAF_BITS SPX_TREE_HEIGHT +#define SPX_LEAF_BYTES ((SPX_LEAF_BITS + 7) / 8) +#define SPX_DGST_BYTES (SPX_FORS_MSG_BYTES + SPX_TREE_BYTES + SPX_LEAF_BYTES) + + unsigned char seed[SPX_SHA256_OUTPUT_BYTES + 4]; + + /* Round to nearest multiple of SPX_SHA256_BLOCK_BYTES */ +#define SPX_INBLOCKS (((SPX_N + SPX_PK_BYTES + SPX_SHA256_BLOCK_BYTES - 1) & \ + -SPX_SHA256_BLOCK_BYTES) / SPX_SHA256_BLOCK_BYTES) + unsigned char inbuf[SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES]; + + unsigned char buf[SPX_DGST_BYTES]; + unsigned char *bufp = buf; + uint8_t state[40]; + + sha256_inc_init(state); + + memcpy(inbuf, R, SPX_N); + memcpy(inbuf + SPX_N, pk, SPX_PK_BYTES); + + /* If R + pk + message cannot fill up an entire block */ + if (SPX_N + SPX_PK_BYTES + mlen < SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES) { + memcpy(inbuf + SPX_N + SPX_PK_BYTES, m, mlen); + sha256_inc_finalize(seed, state, inbuf, SPX_N + SPX_PK_BYTES + mlen); + } + /* Otherwise first fill a block, so that finalize only uses the message */ + else { + memcpy(inbuf + SPX_N + SPX_PK_BYTES, m, + SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES); + sha256_inc_blocks(state, inbuf, SPX_INBLOCKS); + + m += SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES; + mlen -= SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES; + sha256_inc_finalize(seed, state, m, mlen); + } + + /* By doing this in two steps, we prevent hashing the message twice; + otherwise each iteration in MGF1 would hash the message again. */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_mgf1(bufp, SPX_DGST_BYTES, seed, SPX_SHA256_OUTPUT_BYTES); + + memcpy(digest, bufp, SPX_FORS_MSG_BYTES); + bufp += SPX_FORS_MSG_BYTES; + + *tree = PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_bytes_to_ull(bufp, SPX_TREE_BYTES); + *tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS); + bufp += SPX_TREE_BYTES; + + *leaf_idx = (uint32_t)PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_bytes_to_ull( + bufp, SPX_LEAF_BYTES); + *leaf_idx &= (~(uint32_t)0) >> (32 - SPX_LEAF_BITS); +} diff --git a/crypto_sign/sphincs-sha256-128s-robust/clean/params.h b/crypto_sign/sphincs-sha256-128s-robust/clean/params.h new file mode 100644 index 00000000..ac30fdea --- /dev/null +++ b/crypto_sign/sphincs-sha256-128s-robust/clean/params.h @@ -0,0 +1,53 @@ +#ifndef SPX_PARAMS_H +#define SPX_PARAMS_H + +/* Hash output length in bytes. */ +#define SPX_N 16 +/* Height of the hypertree. */ +#define SPX_FULL_HEIGHT 64 +/* Number of subtree layer. */ +#define SPX_D 8 +/* FORS tree dimensions. */ +#define SPX_FORS_HEIGHT 15 +#define SPX_FORS_TREES 10 +/* Winternitz parameter, */ +#define SPX_WOTS_W 16 + +/* The hash function is defined by linking a different hash.c file, as opposed + to setting a #define constant. */ + +/* For clarity */ +#define SPX_ADDR_BYTES 32 + +/* WOTS parameters. */ +#define SPX_WOTS_LOGW 4 + +#define SPX_WOTS_LEN1 (8 * SPX_N / SPX_WOTS_LOGW) + +/* SPX_WOTS_LEN2 is floor(log(len_1 * (w - 1)) / log(w)) + 1; we precompute */ +#define SPX_WOTS_LEN2 3 + +#define SPX_WOTS_LEN (SPX_WOTS_LEN1 + SPX_WOTS_LEN2) +#define SPX_WOTS_BYTES (SPX_WOTS_LEN * SPX_N) +#define SPX_WOTS_PK_BYTES SPX_WOTS_BYTES + +/* Subtree size. */ +#define SPX_TREE_HEIGHT (SPX_FULL_HEIGHT / SPX_D) + +/* FORS parameters. */ +#define SPX_FORS_MSG_BYTES ((SPX_FORS_HEIGHT * SPX_FORS_TREES + 7) / 8) +#define SPX_FORS_BYTES ((SPX_FORS_HEIGHT + 1) * SPX_FORS_TREES * SPX_N) +#define SPX_FORS_PK_BYTES SPX_N + +/* Resulting SPX sizes. */ +#define SPX_BYTES (SPX_N + SPX_FORS_BYTES + SPX_D * SPX_WOTS_BYTES +\ + SPX_FULL_HEIGHT * SPX_N) +#define SPX_PK_BYTES (2 * SPX_N) +#define SPX_SK_BYTES (2 * SPX_N + SPX_PK_BYTES) + +/* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ +#define SPX_OPTRAND_BYTES 32 + +#endif diff --git a/crypto_sign/sphincs-sha256-128s-robust/clean/sha256.c b/crypto_sign/sphincs-sha256-128s-robust/clean/sha256.c new file mode 100644 index 00000000..27d2fbad --- /dev/null +++ b/crypto_sign/sphincs-sha256-128s-robust/clean/sha256.c @@ -0,0 +1,72 @@ +/* Based on the public domain implementation in + * crypto_hash/sha512/ref/ from http://bench.cr.yp.to/supercop.html + * by D. J. Bernstein */ + +#include +#include +#include + +#include "sha2.h" +#include "sha256.h" +#include "utils.h" + +/* + * Compresses an address to a 22-byte sequence. + * This reduces the number of required SHA256 compression calls, as the last + * block of input is padded with at least 65 bits. + */ +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_compress_address(unsigned char *out, const uint32_t addr[8]) { + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_ull_to_bytes(out, 1, addr[0]); /* drop 3 bytes of the layer field */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_ull_to_bytes(out + 1, 4, addr[2]); /* drop the highest tree address word */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_ull_to_bytes(out + 5, 4, addr[3]); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_ull_to_bytes(out + 9, 1, addr[4]); /* drop 3 bytes of the type field */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_ull_to_bytes(out + 10, 4, addr[5]); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_ull_to_bytes(out + 14, 4, addr[6]); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_ull_to_bytes(out + 18, 4, addr[7]); +} + +/** + * Requires 'input_plus_four_bytes' to have 'inlen' + 4 bytes, so that the last + * four bytes can be used for the counter. Typically 'input' is merely a seed. + * Outputs outlen number of bytes + */ +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + unsigned char *input_plus_four_bytes, unsigned long inlen) { + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + unsigned long i; + + /* While we can fit in at least another full block of SHA256 output.. */ + for (i = 0; (i + 1)*SPX_SHA256_OUTPUT_BYTES <= outlen; i++) { + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_ull_to_bytes(input_plus_four_bytes + inlen, 4, i); + sha256(out, input_plus_four_bytes, inlen + 4); + out += SPX_SHA256_OUTPUT_BYTES; + } + /* Until we cannot anymore, and we fill the remainder. */ + if (outlen > i * SPX_SHA256_OUTPUT_BYTES) { + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_ull_to_bytes(input_plus_four_bytes + inlen, 4, i); + sha256(outbuf, input_plus_four_bytes, inlen + 4); + memcpy(out, outbuf, outlen - i * SPX_SHA256_OUTPUT_BYTES); + } +} + +uint8_t PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_state_seeded[40]; + +/** + * Absorb the constant pub_seed using one round of the compression function + * This initializes state_seeded, which can then be reused in thash + **/ +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_seed_state(const unsigned char *pub_seed) { + uint8_t block[SPX_SHA256_BLOCK_BYTES]; + size_t i; + + for (i = 0; i < SPX_N; ++i) { + block[i] = pub_seed[i]; + } + for (i = SPX_N; i < SPX_SHA256_BLOCK_BYTES; ++i) { + block[i] = 0; + } + + sha256_inc_init(PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_state_seeded); + sha256_inc_blocks(PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_state_seeded, block, 1); +} diff --git a/crypto_sign/sphincs-sha256-128s-robust/clean/sha256.h b/crypto_sign/sphincs-sha256-128s-robust/clean/sha256.h new file mode 100644 index 00000000..8b610064 --- /dev/null +++ b/crypto_sign/sphincs-sha256-128s-robust/clean/sha256.h @@ -0,0 +1,21 @@ +#ifndef SPX_SHA256_H +#define SPX_SHA256_H + +#define SPX_SHA256_BLOCK_BYTES 64 +#define SPX_SHA256_OUTPUT_BYTES 32 /* This does not necessarily equal SPX_N */ +#define SPX_SHA256_ADDR_BYTES 22 + +#include +#include + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_compress_address(unsigned char *out, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + unsigned char *input_plus_four_bytes, unsigned long inlen); + +uint8_t PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_state_seeded[40]; + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_seed_state(const unsigned char *pub_seed); + +#endif diff --git a/crypto_sign/sphincs-sha256-128s-robust/clean/sign.c b/crypto_sign/sphincs-sha256-128s-robust/clean/sign.c new file mode 100644 index 00000000..25d53263 --- /dev/null +++ b/crypto_sign/sphincs-sha256-128s-robust/clean/sign.c @@ -0,0 +1,344 @@ +#include +#include +#include + +#include "address.h" +#include "api.h" +#include "fors.h" +#include "hash.h" +#include "params.h" +#include "randombytes.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +/** + * Computes the leaf at a given address. First generates the WOTS key pair, + * then computes leaf by hashing horizontally. + */ +static void wots_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t tree_addr[8]) { + unsigned char pk[SPX_WOTS_BYTES]; + uint32_t wots_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_keypair_addr( + wots_addr, addr_idx); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_wots_gen_pk( + pk, sk_seed, pub_seed, wots_addr); + + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_thash_WOTS_LEN( + leaf, pk, pub_seed, wots_pk_addr); +} + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_crypto_sign_secretkeybytes(void) { + return PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_CRYPTO_SECRETKEYBYTES; +} + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_crypto_sign_publickeybytes(void) { + return PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES; +} + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_crypto_sign_bytes(void) { + return PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_CRYPTO_BYTES; +} + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_crypto_sign_seedbytes(void) { + return PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_CRYPTO_SEEDBYTES; +} + +/* + * Generates an SPX key pair given a seed of length + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed) { + /* We do not need the auth path in key generation, but it simplifies the + code to have just one treehash routine that computes both root and path + in one function. */ + unsigned char auth_path[SPX_TREE_HEIGHT * SPX_N]; + uint32_t top_tree_addr[8] = {0}; + + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_layer_addr( + top_tree_addr, SPX_D - 1); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_type( + top_tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */ + memcpy(sk, seed, PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_CRYPTO_SEEDBYTES); + + memcpy(pk, sk + 2 * SPX_N, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_initialize_hash_function(pk, sk); + + /* Compute root node of the top-most subtree. */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_treehash_TREE_HEIGHT( + sk + 3 * SPX_N, auth_path, sk, sk + 2 * SPX_N, 0, 0, + wots_gen_leaf, top_tree_addr); + + memcpy(pk + SPX_N, sk + 3 * SPX_N, SPX_N); + + return 0; +} + +/* + * Generates an SPX key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk) { + unsigned char seed[PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_CRYPTO_SEEDBYTES]; + randombytes(seed, PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_CRYPTO_SEEDBYTES); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_crypto_sign_seed_keypair( + pk, sk, seed); + + return 0; +} + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + const unsigned char *sk_seed = sk; + const unsigned char *sk_prf = sk + SPX_N; + const unsigned char *pk = sk + 2 * SPX_N; + const unsigned char *pub_seed = pk; + + unsigned char optrand[SPX_N]; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char root[SPX_N]; + uint32_t i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_initialize_hash_function( + pub_seed, sk_seed); + + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ + randombytes(optrand, SPX_N); + /* Compute the digest randomization value. */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_gen_message_random( + sig, sk_prf, optrand, m, mlen); + + /* Derive the message digest and leaf index from R, PK and M. */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Sign the message hash using FORS. */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_fors_sign( + sig, root, mhash, sk_seed, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Compute a WOTS signature. */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_wots_sign( + sig, root, sk_seed, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the authentication path for the used WOTS leaf. */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_treehash_TREE_HEIGHT( + root, sig, sk_seed, pub_seed, idx_leaf, 0, + wots_gen_leaf, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + *siglen = SPX_BYTES; + + return 0; +} + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk) { + const unsigned char *pub_seed = pk; + const unsigned char *pub_root = pk + SPX_N; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char wots_pk[SPX_WOTS_BYTES]; + unsigned char root[SPX_N]; + unsigned char leaf[SPX_N]; + unsigned int i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + if (siglen != SPX_BYTES) { + return -1; + } + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_initialize_hash_function( + pub_seed, NULL); + + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + /* Derive the message digest and leaf index from R || PK || M. */ + /* The additional SPX_N is a result of the hash domain separator. */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + /* Layer correctly defaults to 0, so no need to set_layer_addr */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_fors_pk_from_sig( + root, sig, mhash, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + /* For each subtree.. */ + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + + /* The WOTS public key is only correct if the signature was correct. */ + /* Initially, root is the FORS pk, but on subsequent iterations it is + the root of the subtree below the currently processed subtree. */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_wots_pk_from_sig( + wots_pk, sig, root, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the leaf node using the WOTS public key. */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_thash_WOTS_LEN( + leaf, wots_pk, pub_seed, wots_pk_addr); + + /* Compute the root node of this subtree. */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_compute_root( + root, leaf, idx_leaf, 0, sig, SPX_TREE_HEIGHT, + pub_seed, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + /* Check if the root node equals the root node in the public key. */ + if (memcmp(root, pub_root, SPX_N) != 0) { + return -1; + } + + return 0; +} + + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + size_t siglen; + + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_crypto_sign_signature( + sm, &siglen, m, mlen, sk); + + memmove(sm + SPX_BYTES, m, mlen); + *smlen = siglen + mlen; + + return 0; +} + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk) { + /* The API caller does not necessarily know what size a signature should be + but SPHINCS+ signatures are always exactly SPX_BYTES. */ + if (smlen < SPX_BYTES) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + *mlen = smlen - SPX_BYTES; + + if (PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_crypto_sign_verify( + sm, SPX_BYTES, sm + SPX_BYTES, *mlen, pk)) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + /* If verification was successful, move the message to the right place. */ + memmove(m, sm + SPX_BYTES, *mlen); + + return 0; +} diff --git a/crypto_sign/sphincs-sha256-128s-robust/clean/thash.h b/crypto_sign/sphincs-sha256-128s-robust/clean/thash.h new file mode 100644 index 00000000..1e8dcd67 --- /dev/null +++ b/crypto_sign/sphincs-sha256-128s-robust/clean/thash.h @@ -0,0 +1,22 @@ +#ifndef SPX_THASH_H +#define SPX_THASH_H + +#include + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-sha256-128s-robust/clean/thash_sha256_robust.c b/crypto_sign/sphincs-sha256-128s-robust/clean/thash_sha256_robust.c new file mode 100644 index 00000000..5c468dae --- /dev/null +++ b/crypto_sign/sphincs-sha256-128s-robust/clean/thash_sha256_robust.c @@ -0,0 +1,77 @@ +#include +#include + +#include "address.h" +#include "params.h" +#include "thash.h" + +#include "sha2.h" +#include "sha256.h" + +/** + * Takes an array of inblocks concatenated arrays of SPX_N bytes. + */ +static void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_thash( + unsigned char *out, unsigned char *buf, + const unsigned char *in, unsigned int inblocks, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + unsigned char *bitmask = buf + SPX_N + SPX_SHA256_ADDR_BYTES + 4; + uint8_t sha2_state[40]; + unsigned int i; + + memcpy(buf, pub_seed, SPX_N); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_compress_address(buf + SPX_N, addr); + /* MGF1 requires us to have 4 extra bytes in 'buf' */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_mgf1(bitmask, inblocks * SPX_N, buf, SPX_N + SPX_SHA256_ADDR_BYTES); + + /* Retrieve precomputed state containing pub_seed */ + memcpy(sha2_state, PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_state_seeded, 40 * sizeof(uint8_t)); + + for (i = 0; i < inblocks * SPX_N; i++) { + buf[SPX_N + SPX_SHA256_ADDR_BYTES + i] = in[i] ^ bitmask[i]; + } + + sha256_inc_finalize(outbuf, sha2_state, buf + SPX_N, + SPX_SHA256_ADDR_BYTES + inblocks * SPX_N); + memcpy(out, outbuf, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES + 4 + 1 * SPX_N]; + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_thash( + out, buf, in, 1, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES + 4 + 2 * SPX_N]; + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_thash( + out, buf, in, 2, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES + 4 + SPX_WOTS_LEN * SPX_N]; + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_thash( + out, buf, in, SPX_WOTS_LEN, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES + 4 + SPX_FORS_TREES * SPX_N]; + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_thash( + out, buf, in, SPX_FORS_TREES, pub_seed, addr); +} diff --git a/crypto_sign/sphincs-sha256-128s-robust/clean/utils.c b/crypto_sign/sphincs-sha256-128s-robust/clean/utils.c new file mode 100644 index 00000000..7b028499 --- /dev/null +++ b/crypto_sign/sphincs-sha256-128s-robust/clean/utils.c @@ -0,0 +1,192 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in) { + + /* Iterate over out in decreasing order, for big-endianness. */ + for (size_t i = outlen; i > 0; i--) { + out[i - 1] = in & 0xff; + in = in >> 8; + } +} + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen) { + unsigned long long retval = 0; + + for (size_t i = 0; i < inlen; i++) { + retval |= ((unsigned long long)in[i]) << (8 * (inlen - 1 - i)); + } + return retval; +} + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + unsigned char buffer[2 * SPX_N]; + + /* If leaf_idx is odd (last bit = 1), current path element is a right child + and auth_path has to go left. Otherwise it is the other way around. */ + if (leaf_idx & 1) { + memcpy(buffer + SPX_N, leaf, SPX_N); + memcpy(buffer, auth_path, SPX_N); + } else { + memcpy(buffer, leaf, SPX_N); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + + for (i = 0; i < tree_height - 1; i++) { + leaf_idx >>= 1; + idx_offset >>= 1; + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_tree_height(addr, i + 1); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + + /* Pick the right or left neighbor, depending on parity of the node. */ + if (leaf_idx & 1) { + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_thash_2( + buffer + SPX_N, buffer, pub_seed, addr); + memcpy(buffer, auth_path, SPX_N); + } else { + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_thash_2( + buffer, buffer, pub_seed, addr); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + } + + /* The last iteration is exceptional; we do not copy an auth_path node. */ + leaf_idx >>= 1; + idx_offset >>= 1; + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_tree_height(addr, tree_height); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_thash_2( + root, buffer, pub_seed, addr); +} + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +static void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_treehash( + unsigned char *root, unsigned char *auth_path, + unsigned char *stack, unsigned int *heights, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned int offset = 0; + uint32_t idx; + uint32_t tree_idx; + + for (idx = 0; idx < (uint32_t)(1 << tree_height); idx++) { + /* Add the next leaf node to the stack. */ + gen_leaf(stack + offset * SPX_N, + sk_seed, pub_seed, idx + idx_offset, tree_addr); + offset++; + heights[offset - 1] = 0; + + /* If this is a node we need for the auth path.. */ + if ((leaf_idx ^ 0x1) == idx) { + memcpy(auth_path, stack + (offset - 1)*SPX_N, SPX_N); + } + + /* While the top-most nodes are of equal height.. */ + while (offset >= 2 && heights[offset - 1] == heights[offset - 2]) { + /* Compute index of the new node, in the next layer. */ + tree_idx = (idx >> (heights[offset - 1] + 1)); + + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_tree_height( + tree_addr, heights[offset - 1] + 1); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_tree_index( + tree_addr, tree_idx + (idx_offset >> (heights[offset - 1] + 1))); + /* Hash the top-most nodes from the stack together. */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_thash_2( + stack + (offset - 2)*SPX_N, stack + (offset - 2)*SPX_N, + pub_seed, tree_addr); + offset--; + /* Note that the top-most node is now one layer higher. */ + heights[offset - 1]++; + + /* If this is a node we need for the auth path.. */ + if (((leaf_idx >> heights[offset - 1]) ^ 0x1) == tree_idx) { + memcpy(auth_path + heights[offset - 1]*SPX_N, + stack + (offset - 1)*SPX_N, SPX_N); + } + } + } + memcpy(root, stack, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_FORS_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_FORS_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_FORS_HEIGHT, gen_leaf, tree_addr); +} + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_TREE_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_TREE_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_TREE_HEIGHT, gen_leaf, tree_addr); +} diff --git a/crypto_sign/sphincs-sha256-128s-robust/clean/utils.h b/crypto_sign/sphincs-sha256-128s-robust/clean/utils.h new file mode 100644 index 00000000..bf9938dc --- /dev/null +++ b/crypto_sign/sphincs-sha256-128s-robust/clean/utils.h @@ -0,0 +1,60 @@ +#ifndef SPX_UTILS_H +#define SPX_UTILS_H + +#include "params.h" +#include +#include + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in); + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen); + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-sha256-128s-robust/clean/wots.c b/crypto_sign/sphincs-sha256-128s-robust/clean/wots.c new file mode 100644 index 00000000..a465a70b --- /dev/null +++ b/crypto_sign/sphincs-sha256-128s-robust/clean/wots.c @@ -0,0 +1,161 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +// TODO clarify address expectations, and make them more uniform. +// TODO i.e. do we expect types to be set already? +// TODO and do we expect modifications or copies? + +/** + * Computes the starting value for a chain, i.e. the secret key. + * Expects the address to be complete up to the chain address. + */ +static void wots_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t wots_addr[8]) { + /* Make sure that the hash address is actually zeroed. */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_hash_addr(wots_addr, 0); + + /* Generate sk element. */ + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_prf_addr(sk, sk_seed, wots_addr); +} + +/** + * Computes the chaining function. + * out and in have to be n-byte arrays. + * + * Interprets in as start-th value of the chain. + * addr has to contain the address of the chain. + */ +static void gen_chain(unsigned char *out, const unsigned char *in, + unsigned int start, unsigned int steps, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + /* Initialize out with the value at position 'start'. */ + memcpy(out, in, SPX_N); + + /* Iterate 'steps' calls to the hash function. */ + for (i = start; i < (start + steps) && i < SPX_WOTS_W; i++) { + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_hash_addr(addr, i); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_thash_1( + out, out, pub_seed, addr); + } +} + +/** + * base_w algorithm as described in draft. + * Interprets an array of bytes as integers in base w. + * This only works when log_w is a divisor of 8. + */ +static void base_w(unsigned int *output, const size_t out_len, + const unsigned char *input) { + size_t in = 0; + size_t out = 0; + unsigned char total = 0; + unsigned int bits = 0; + size_t consumed; + + for (consumed = 0; consumed < out_len; consumed++) { + if (bits == 0) { + total = input[in]; + in++; + bits += 8; + } + bits -= SPX_WOTS_LOGW; + output[out] = (unsigned int)((total >> bits) & (SPX_WOTS_W - 1)); + out++; + } +} + +/* Computes the WOTS+ checksum over a message (in base_w). */ +static void wots_checksum(unsigned int *csum_base_w, + const unsigned int *msg_base_w) { + unsigned int csum = 0; + unsigned char csum_bytes[(SPX_WOTS_LEN2 * SPX_WOTS_LOGW + 7) / 8]; + unsigned int i; + + /* Compute checksum. */ + for (i = 0; i < SPX_WOTS_LEN1; i++) { + csum += SPX_WOTS_W - 1 - msg_base_w[i]; + } + + /* Convert checksum to base_w. */ + /* Make sure expected empty zero bits are the least significant bits. */ + csum = csum << (8 - ((SPX_WOTS_LEN2 * SPX_WOTS_LOGW) % 8)); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_ull_to_bytes( + csum_bytes, sizeof(csum_bytes), csum); + base_w(csum_base_w, SPX_WOTS_LEN2, csum_bytes); +} + +/* Takes a message and derives the matching chain lengths. */ +static void chain_lengths(unsigned int *lengths, const unsigned char *msg) { + base_w(lengths, SPX_WOTS_LEN1, msg); + wots_checksum(lengths + SPX_WOTS_LEN1, lengths); +} + +/** + * WOTS key generation. Takes a 32 byte sk_seed, expands it to WOTS private key + * elements and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(pk + i * SPX_N, sk_seed, addr); + gen_chain(pk + i * SPX_N, pk + i * SPX_N, + 0, SPX_WOTS_W - 1, pub_seed, addr); + } +} + +/** + * Takes a n-byte message and the 32-byte sk_see to compute a signature 'sig'. + */ +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(sig + i * SPX_N, sk_seed, addr); + gen_chain(sig + i * SPX_N, sig + i * SPX_N, 0, lengths[i], pub_seed, addr); + } +} + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_set_chain_addr(addr, i); + gen_chain(pk + i * SPX_N, sig + i * SPX_N, + lengths[i], SPX_WOTS_W - 1 - lengths[i], pub_seed, addr); + } +} diff --git a/crypto_sign/sphincs-sha256-128s-robust/clean/wots.h b/crypto_sign/sphincs-sha256-128s-robust/clean/wots.h new file mode 100644 index 00000000..fdabeef4 --- /dev/null +++ b/crypto_sign/sphincs-sha256-128s-robust/clean/wots.h @@ -0,0 +1,38 @@ +#ifndef SPX_WOTS_H +#define SPX_WOTS_H + +#include "params.h" +#include + +/** + * WOTS key generation. Takes a 32 byte seed for the private key, expands it to + * a full WOTS private key and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * Takes a n-byte message and the 32-byte seed for the private key to compute a + * signature that is placed at 'sig'. + */ +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]); + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-sha256-128s-simple/META.yml b/crypto_sign/sphincs-sha256-128s-simple/META.yml new file mode 100644 index 00000000..2f2e6b53 --- /dev/null +++ b/crypto_sign/sphincs-sha256-128s-simple/META.yml @@ -0,0 +1,27 @@ +name: SPHINCS+ +type: signature +claimed-nist-level: 1 +length-public-key: 32 +length-secret-key: 64 +length-signature: 8080 +testvectors-sha256: edf1b76246ac560558d7938f8ac7bbf820f1e697ef4f5b5e1962f04fadb84a76 +principal-submitter: Andreas Hülsing +auxiliary-submitters: + - Jean-Philippe Aumasson + - Daniel J. Bernstein, + - Christoph Dobraunig + - Maria Eichlseder + - Scott Fluhrer + - Stefan-Lukas Gazdag + - Panos Kampanakis + - Stefan Kölbl + - Tanja Lange + - Martin M. Lauridsen + - Florian Mendel + - Ruben Niederhagen + - Christian Rechberger + - Joost Rijneveld + - Peter Schwabe +implementations: + - name: clean + version: https://github.com/sphincs/sphincsplus/commit/77755c94d0bc744478044d6efbb888dc13156441 diff --git a/crypto_sign/sphincs-sha256-128s-simple/clean/LICENSE b/crypto_sign/sphincs-sha256-128s-simple/clean/LICENSE new file mode 100644 index 00000000..670154e3 --- /dev/null +++ b/crypto_sign/sphincs-sha256-128s-simple/clean/LICENSE @@ -0,0 +1,116 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + diff --git a/crypto_sign/sphincs-sha256-128s-simple/clean/Makefile b/crypto_sign/sphincs-sha256-128s-simple/clean/Makefile new file mode 100644 index 00000000..4264ce00 --- /dev/null +++ b/crypto_sign/sphincs-sha256-128s-simple/clean/Makefile @@ -0,0 +1,20 @@ +# This Makefile can be used with GNU Make or BSD Make + +LIB=libsphincs-sha256-128s-simple_clean.a + +HEADERS = params.h address.h wots.h utils.h fors.h api.h hash.h thash.h sha256.h +OBJECTS = address.o wots.o utils.o fors.o sign.o hash_sha256.o thash_sha256_simple.o sha256.o + +CFLAGS=-O3 -Wall -Wconversion -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) + +all: $(LIB) + +%.o: %.c $(HEADERS) + $(CC) $(CFLAGS) -c -o $@ $< + +$(LIB): $(OBJECTS) + $(AR) -r $@ $(OBJECTS) + +clean: + $(RM) $(OBJECTS) + $(RM) $(LIB) diff --git a/crypto_sign/sphincs-sha256-128s-simple/clean/Makefile.Microsoft_nmake b/crypto_sign/sphincs-sha256-128s-simple/clean/Makefile.Microsoft_nmake new file mode 100644 index 00000000..f67c55cc --- /dev/null +++ b/crypto_sign/sphincs-sha256-128s-simple/clean/Makefile.Microsoft_nmake @@ -0,0 +1,19 @@ +# This Makefile can be used with Microsoft Visual Studio's nmake using the command: +# nmake /f Makefile.Microsoft_nmake + +LIBRARY=libsphincs-sha256-128s-simple_clean.lib +OBJECTS=address.obj wots.obj utils.obj fors.obj sign.obj hash_sha256.obj thash_sha256_simple.obj sha256.obj + +CFLAGS=/nologo /I ..\..\..\common /W4 /WX + +all: $(LIBRARY) + +# Make sure objects are recompiled if headers change. +$(OBJECTS): *.h + +$(LIBRARY): $(OBJECTS) + LIB.EXE /NOLOGO /WX /OUT:$@ $** + +clean: + -DEL $(OBJECTS) + -DEL $(LIBRARY) diff --git a/crypto_sign/sphincs-sha256-128s-simple/clean/address.c b/crypto_sign/sphincs-sha256-128s-simple/clean/address.c new file mode 100644 index 00000000..f58ec0ca --- /dev/null +++ b/crypto_sign/sphincs-sha256-128s-simple/clean/address.c @@ -0,0 +1,78 @@ +#include + +#include "address.h" +#include "params.h" +#include "utils.h" + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]) { + int i; + + for (i = 0; i < 8; i++) { + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_ull_to_bytes( + bytes + i * 4, 4, addr[i]); + } +} + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer) { + addr[0] = layer; +} + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree) { + addr[1] = 0; + addr[2] = (uint32_t) (tree >> 32); + addr[3] = (uint32_t) tree; +} + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_type( + uint32_t addr[8], uint32_t type) { + addr[4] = type; +} + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; +} + +/* These functions are used for OTS addresses. */ + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair) { + addr[5] = keypair; +} + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; + out[5] = in[5]; +} + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain) { + addr[6] = chain; +} + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash) { + addr[7] = hash; +} + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height) { + addr[6] = tree_height; +} + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index) { + addr[7] = tree_index; +} diff --git a/crypto_sign/sphincs-sha256-128s-simple/clean/address.h b/crypto_sign/sphincs-sha256-128s-simple/clean/address.h new file mode 100644 index 00000000..cca73fa2 --- /dev/null +++ b/crypto_sign/sphincs-sha256-128s-simple/clean/address.h @@ -0,0 +1,50 @@ +#ifndef SPX_ADDRESS_H +#define SPX_ADDRESS_H + +#include + +#define SPX_ADDR_TYPE_WOTS 0 +#define SPX_ADDR_TYPE_WOTSPK 1 +#define SPX_ADDR_TYPE_HASHTREE 2 +#define SPX_ADDR_TYPE_FORSTREE 3 +#define SPX_ADDR_TYPE_FORSPK 4 + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer); + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree); + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_type( + uint32_t addr[8], uint32_t type); + +/* Copies the layer and tree part of one address into the other */ +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for WOTS and FORS addresses. */ + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair); + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain); + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash); + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height); + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index); + +#endif diff --git a/crypto_sign/sphincs-sha256-128s-simple/clean/api.h b/crypto_sign/sphincs-sha256-128s-simple/clean/api.h new file mode 100644 index 00000000..2ccd88e6 --- /dev/null +++ b/crypto_sign/sphincs-sha256-128s-simple/clean/api.h @@ -0,0 +1,78 @@ +#ifndef PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_API_H +#define PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_API_H + +#include +#include + +#define PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_CRYPTO_ALGNAME "SPHINCS+" + +#define PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_CRYPTO_SECRETKEYBYTES 64 +#define PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES 32 +#define PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_CRYPTO_BYTES 8080 +#define PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_CRYPTO_SEEDBYTES 48 + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_crypto_sign_secretkeybytes(void); + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_crypto_sign_publickeybytes(void); + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_crypto_sign_bytes(void); + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_crypto_sign_seedbytes(void); + +/* + * Generates a SPHINCS+ key pair given a seed. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed); + +/* + * Generates a SPHINCS+ key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk); + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk); + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk); + +#endif diff --git a/crypto_sign/sphincs-sha256-128s-simple/clean/fors.c b/crypto_sign/sphincs-sha256-128s-simple/clean/fors.c new file mode 100644 index 00000000..e795313a --- /dev/null +++ b/crypto_sign/sphincs-sha256-128s-simple/clean/fors.c @@ -0,0 +1,164 @@ +#include +#include +#include + +#include "address.h" +#include "fors.h" +#include "hash.h" +#include "thash.h" +#include "utils.h" + +static void fors_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_prf_addr( + sk, sk_seed, fors_leaf_addr); +} + +static void fors_sk_to_leaf(unsigned char *leaf, const unsigned char *sk, + const unsigned char *pub_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_thash_1( + leaf, sk, pub_seed, fors_leaf_addr); +} + +static void fors_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t fors_tree_addr[8]) { + uint32_t fors_leaf_addr[8] = {0}; + + /* Only copy the parts that must be kept in fors_leaf_addr. */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_copy_keypair_addr( + fors_leaf_addr, fors_tree_addr); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_type( + fors_leaf_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_tree_index( + fors_leaf_addr, addr_idx); + + fors_gen_sk(leaf, sk_seed, fors_leaf_addr); + fors_sk_to_leaf(leaf, leaf, pub_seed, fors_leaf_addr); +} + +/** + * Interprets m as SPX_FORS_HEIGHT-bit unsigned integers. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + * Assumes indices has space for SPX_FORS_TREES integers. + */ +static void message_to_indices(uint32_t *indices, const unsigned char *m) { + unsigned int i, j; + unsigned int offset = 0; + + for (i = 0; i < SPX_FORS_TREES; i++) { + indices[i] = 0; + for (j = 0; j < SPX_FORS_HEIGHT; j++) { + indices[i] ^= (((uint32_t)m[offset >> 3] >> (offset & 0x7)) & 0x1) << j; + offset++; + } + } +} + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Include the secret key part that produces the selected leaf node. */ + fors_gen_sk(sig, sk_seed, fors_tree_addr); + sig += SPX_N; + + /* Compute the authentication path for this leaf node. */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_treehash_FORS_HEIGHT( + roots + i * SPX_N, sig, sk_seed, pub_seed, + indices[i], idx_offset, fors_gen_leaf, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + unsigned char leaf[SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Derive the leaf from the included secret key part. */ + fors_sk_to_leaf(leaf, sig, pub_seed, fors_tree_addr); + sig += SPX_N; + + /* Derive the corresponding root node of this tree. */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_compute_root( + roots + i * SPX_N, leaf, indices[i], idx_offset, sig, + SPX_FORS_HEIGHT, pub_seed, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} diff --git a/crypto_sign/sphincs-sha256-128s-simple/clean/fors.h b/crypto_sign/sphincs-sha256-128s-simple/clean/fors.h new file mode 100644 index 00000000..11dcc58c --- /dev/null +++ b/crypto_sign/sphincs-sha256-128s-simple/clean/fors.h @@ -0,0 +1,30 @@ +#ifndef SPX_FORS_H +#define SPX_FORS_H + +#include + +#include "params.h" + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]); + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-sha256-128s-simple/clean/hash.h b/crypto_sign/sphincs-sha256-128s-simple/clean/hash.h new file mode 100644 index 00000000..c1cde116 --- /dev/null +++ b/crypto_sign/sphincs-sha256-128s-simple/clean/hash.h @@ -0,0 +1,22 @@ +#ifndef SPX_HASH_H +#define SPX_HASH_H + +#include + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed); + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen); + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen); + +#endif diff --git a/crypto_sign/sphincs-sha256-128s-simple/clean/hash_sha256.c b/crypto_sign/sphincs-sha256-128s-simple/clean/hash_sha256.c new file mode 100644 index 00000000..154c489e --- /dev/null +++ b/crypto_sign/sphincs-sha256-128s-simple/clean/hash_sha256.c @@ -0,0 +1,148 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "utils.h" + +#include "sha2.h" +#include "sha256.h" + +/* For SHA256, there is no immediate reason to initialize at the start, + so this function is an empty operation. */ +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed) { + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_seed_state(pub_seed); + (void)sk_seed; /* Suppress an 'unused parameter' warning. */ +} + +/* + * Computes PRF(key, addr), given a secret key of SPX_N bytes and an address + */ +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]) { + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES]; + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + + memcpy(buf, key, SPX_N); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_compress_address(buf + SPX_N, addr); + + sha256(outbuf, buf, SPX_N + SPX_SHA256_ADDR_BYTES); + memcpy(out, outbuf, SPX_N); +} + +/** + * Computes the message-dependent randomness R, using a secret seed as a key + * for HMAC, and an optional randomization value prefixed to the message. + * This requires m to have at least SPX_SHA256_BLOCK_BYTES + SPX_N space + * available in front of the pointer, i.e. before the message to use for the + * prefix. This is necessary to prevent having to move the message around (and + * allocate memory for it). + */ +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen) { + unsigned char buf[SPX_SHA256_BLOCK_BYTES + SPX_SHA256_OUTPUT_BYTES]; + uint8_t state[40]; + int i; + + /* This implements HMAC-SHA256 */ + for (i = 0; i < SPX_N; i++) { + buf[i] = 0x36 ^ sk_prf[i]; + } + memset(buf + SPX_N, 0x36, SPX_SHA256_BLOCK_BYTES - SPX_N); + + sha256_inc_init(state); + sha256_inc_blocks(state, buf, 1); + + memcpy(buf, optrand, SPX_N); + + /* If optrand + message cannot fill up an entire block */ + if (SPX_N + mlen < SPX_SHA256_BLOCK_BYTES) { + memcpy(buf + SPX_N, m, mlen); + sha256_inc_finalize(buf + SPX_SHA256_BLOCK_BYTES, state, + buf, mlen + SPX_N); + } + /* Otherwise first fill a block, so that finalize only uses the message */ + else { + memcpy(buf + SPX_N, m, SPX_SHA256_BLOCK_BYTES - SPX_N); + sha256_inc_blocks(state, buf, 1); + + m += SPX_SHA256_BLOCK_BYTES - SPX_N; + mlen -= SPX_SHA256_BLOCK_BYTES - SPX_N; + sha256_inc_finalize(buf + SPX_SHA256_BLOCK_BYTES, state, m, mlen); + } + + for (i = 0; i < SPX_N; i++) { + buf[i] = 0x5c ^ sk_prf[i]; + } + memset(buf + SPX_N, 0x5c, SPX_SHA256_BLOCK_BYTES - SPX_N); + + sha256(buf, buf, SPX_SHA256_BLOCK_BYTES + SPX_SHA256_OUTPUT_BYTES); + memcpy(R, buf, SPX_N); +} + +/** + * Computes the message hash using R, the public key, and the message. + * Outputs the message digest and the index of the leaf. The index is split in + * the tree index and the leaf index, for convenient copying to an address. + */ +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen) { +#define SPX_TREE_BITS (SPX_TREE_HEIGHT * (SPX_D - 1)) +#define SPX_TREE_BYTES ((SPX_TREE_BITS + 7) / 8) +#define SPX_LEAF_BITS SPX_TREE_HEIGHT +#define SPX_LEAF_BYTES ((SPX_LEAF_BITS + 7) / 8) +#define SPX_DGST_BYTES (SPX_FORS_MSG_BYTES + SPX_TREE_BYTES + SPX_LEAF_BYTES) + + unsigned char seed[SPX_SHA256_OUTPUT_BYTES + 4]; + + /* Round to nearest multiple of SPX_SHA256_BLOCK_BYTES */ +#define SPX_INBLOCKS (((SPX_N + SPX_PK_BYTES + SPX_SHA256_BLOCK_BYTES - 1) & \ + -SPX_SHA256_BLOCK_BYTES) / SPX_SHA256_BLOCK_BYTES) + unsigned char inbuf[SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES]; + + unsigned char buf[SPX_DGST_BYTES]; + unsigned char *bufp = buf; + uint8_t state[40]; + + sha256_inc_init(state); + + memcpy(inbuf, R, SPX_N); + memcpy(inbuf + SPX_N, pk, SPX_PK_BYTES); + + /* If R + pk + message cannot fill up an entire block */ + if (SPX_N + SPX_PK_BYTES + mlen < SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES) { + memcpy(inbuf + SPX_N + SPX_PK_BYTES, m, mlen); + sha256_inc_finalize(seed, state, inbuf, SPX_N + SPX_PK_BYTES + mlen); + } + /* Otherwise first fill a block, so that finalize only uses the message */ + else { + memcpy(inbuf + SPX_N + SPX_PK_BYTES, m, + SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES); + sha256_inc_blocks(state, inbuf, SPX_INBLOCKS); + + m += SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES; + mlen -= SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES; + sha256_inc_finalize(seed, state, m, mlen); + } + + /* By doing this in two steps, we prevent hashing the message twice; + otherwise each iteration in MGF1 would hash the message again. */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_mgf1(bufp, SPX_DGST_BYTES, seed, SPX_SHA256_OUTPUT_BYTES); + + memcpy(digest, bufp, SPX_FORS_MSG_BYTES); + bufp += SPX_FORS_MSG_BYTES; + + *tree = PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_bytes_to_ull(bufp, SPX_TREE_BYTES); + *tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS); + bufp += SPX_TREE_BYTES; + + *leaf_idx = (uint32_t)PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_bytes_to_ull( + bufp, SPX_LEAF_BYTES); + *leaf_idx &= (~(uint32_t)0) >> (32 - SPX_LEAF_BITS); +} diff --git a/crypto_sign/sphincs-sha256-128s-simple/clean/params.h b/crypto_sign/sphincs-sha256-128s-simple/clean/params.h new file mode 100644 index 00000000..ac30fdea --- /dev/null +++ b/crypto_sign/sphincs-sha256-128s-simple/clean/params.h @@ -0,0 +1,53 @@ +#ifndef SPX_PARAMS_H +#define SPX_PARAMS_H + +/* Hash output length in bytes. */ +#define SPX_N 16 +/* Height of the hypertree. */ +#define SPX_FULL_HEIGHT 64 +/* Number of subtree layer. */ +#define SPX_D 8 +/* FORS tree dimensions. */ +#define SPX_FORS_HEIGHT 15 +#define SPX_FORS_TREES 10 +/* Winternitz parameter, */ +#define SPX_WOTS_W 16 + +/* The hash function is defined by linking a different hash.c file, as opposed + to setting a #define constant. */ + +/* For clarity */ +#define SPX_ADDR_BYTES 32 + +/* WOTS parameters. */ +#define SPX_WOTS_LOGW 4 + +#define SPX_WOTS_LEN1 (8 * SPX_N / SPX_WOTS_LOGW) + +/* SPX_WOTS_LEN2 is floor(log(len_1 * (w - 1)) / log(w)) + 1; we precompute */ +#define SPX_WOTS_LEN2 3 + +#define SPX_WOTS_LEN (SPX_WOTS_LEN1 + SPX_WOTS_LEN2) +#define SPX_WOTS_BYTES (SPX_WOTS_LEN * SPX_N) +#define SPX_WOTS_PK_BYTES SPX_WOTS_BYTES + +/* Subtree size. */ +#define SPX_TREE_HEIGHT (SPX_FULL_HEIGHT / SPX_D) + +/* FORS parameters. */ +#define SPX_FORS_MSG_BYTES ((SPX_FORS_HEIGHT * SPX_FORS_TREES + 7) / 8) +#define SPX_FORS_BYTES ((SPX_FORS_HEIGHT + 1) * SPX_FORS_TREES * SPX_N) +#define SPX_FORS_PK_BYTES SPX_N + +/* Resulting SPX sizes. */ +#define SPX_BYTES (SPX_N + SPX_FORS_BYTES + SPX_D * SPX_WOTS_BYTES +\ + SPX_FULL_HEIGHT * SPX_N) +#define SPX_PK_BYTES (2 * SPX_N) +#define SPX_SK_BYTES (2 * SPX_N + SPX_PK_BYTES) + +/* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ +#define SPX_OPTRAND_BYTES 32 + +#endif diff --git a/crypto_sign/sphincs-sha256-128s-simple/clean/sha256.c b/crypto_sign/sphincs-sha256-128s-simple/clean/sha256.c new file mode 100644 index 00000000..b52f6c49 --- /dev/null +++ b/crypto_sign/sphincs-sha256-128s-simple/clean/sha256.c @@ -0,0 +1,72 @@ +/* Based on the public domain implementation in + * crypto_hash/sha512/ref/ from http://bench.cr.yp.to/supercop.html + * by D. J. Bernstein */ + +#include +#include +#include + +#include "sha2.h" +#include "sha256.h" +#include "utils.h" + +/* + * Compresses an address to a 22-byte sequence. + * This reduces the number of required SHA256 compression calls, as the last + * block of input is padded with at least 65 bits. + */ +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_compress_address(unsigned char *out, const uint32_t addr[8]) { + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_ull_to_bytes(out, 1, addr[0]); /* drop 3 bytes of the layer field */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_ull_to_bytes(out + 1, 4, addr[2]); /* drop the highest tree address word */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_ull_to_bytes(out + 5, 4, addr[3]); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_ull_to_bytes(out + 9, 1, addr[4]); /* drop 3 bytes of the type field */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_ull_to_bytes(out + 10, 4, addr[5]); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_ull_to_bytes(out + 14, 4, addr[6]); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_ull_to_bytes(out + 18, 4, addr[7]); +} + +/** + * Requires 'input_plus_four_bytes' to have 'inlen' + 4 bytes, so that the last + * four bytes can be used for the counter. Typically 'input' is merely a seed. + * Outputs outlen number of bytes + */ +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + unsigned char *input_plus_four_bytes, unsigned long inlen) { + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + unsigned long i; + + /* While we can fit in at least another full block of SHA256 output.. */ + for (i = 0; (i + 1)*SPX_SHA256_OUTPUT_BYTES <= outlen; i++) { + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_ull_to_bytes(input_plus_four_bytes + inlen, 4, i); + sha256(out, input_plus_four_bytes, inlen + 4); + out += SPX_SHA256_OUTPUT_BYTES; + } + /* Until we cannot anymore, and we fill the remainder. */ + if (outlen > i * SPX_SHA256_OUTPUT_BYTES) { + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_ull_to_bytes(input_plus_four_bytes + inlen, 4, i); + sha256(outbuf, input_plus_four_bytes, inlen + 4); + memcpy(out, outbuf, outlen - i * SPX_SHA256_OUTPUT_BYTES); + } +} + +uint8_t PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_state_seeded[40]; + +/** + * Absorb the constant pub_seed using one round of the compression function + * This initializes state_seeded, which can then be reused in thash + **/ +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_seed_state(const unsigned char *pub_seed) { + uint8_t block[SPX_SHA256_BLOCK_BYTES]; + size_t i; + + for (i = 0; i < SPX_N; ++i) { + block[i] = pub_seed[i]; + } + for (i = SPX_N; i < SPX_SHA256_BLOCK_BYTES; ++i) { + block[i] = 0; + } + + sha256_inc_init(PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_state_seeded); + sha256_inc_blocks(PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_state_seeded, block, 1); +} diff --git a/crypto_sign/sphincs-sha256-128s-simple/clean/sha256.h b/crypto_sign/sphincs-sha256-128s-simple/clean/sha256.h new file mode 100644 index 00000000..5ae56166 --- /dev/null +++ b/crypto_sign/sphincs-sha256-128s-simple/clean/sha256.h @@ -0,0 +1,21 @@ +#ifndef SPX_SHA256_H +#define SPX_SHA256_H + +#define SPX_SHA256_BLOCK_BYTES 64 +#define SPX_SHA256_OUTPUT_BYTES 32 /* This does not necessarily equal SPX_N */ +#define SPX_SHA256_ADDR_BYTES 22 + +#include +#include + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_compress_address(unsigned char *out, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + unsigned char *input_plus_four_bytes, unsigned long inlen); + +uint8_t PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_state_seeded[40]; + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_seed_state(const unsigned char *pub_seed); + +#endif diff --git a/crypto_sign/sphincs-sha256-128s-simple/clean/sign.c b/crypto_sign/sphincs-sha256-128s-simple/clean/sign.c new file mode 100644 index 00000000..20cfbea1 --- /dev/null +++ b/crypto_sign/sphincs-sha256-128s-simple/clean/sign.c @@ -0,0 +1,344 @@ +#include +#include +#include + +#include "address.h" +#include "api.h" +#include "fors.h" +#include "hash.h" +#include "params.h" +#include "randombytes.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +/** + * Computes the leaf at a given address. First generates the WOTS key pair, + * then computes leaf by hashing horizontally. + */ +static void wots_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t tree_addr[8]) { + unsigned char pk[SPX_WOTS_BYTES]; + uint32_t wots_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, addr_idx); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_wots_gen_pk( + pk, sk_seed, pub_seed, wots_addr); + + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_thash_WOTS_LEN( + leaf, pk, pub_seed, wots_pk_addr); +} + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_crypto_sign_secretkeybytes(void) { + return PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_CRYPTO_SECRETKEYBYTES; +} + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_crypto_sign_publickeybytes(void) { + return PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES; +} + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_crypto_sign_bytes(void) { + return PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_CRYPTO_BYTES; +} + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_crypto_sign_seedbytes(void) { + return PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_CRYPTO_SEEDBYTES; +} + +/* + * Generates an SPX key pair given a seed of length + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed) { + /* We do not need the auth path in key generation, but it simplifies the + code to have just one treehash routine that computes both root and path + in one function. */ + unsigned char auth_path[SPX_TREE_HEIGHT * SPX_N]; + uint32_t top_tree_addr[8] = {0}; + + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_layer_addr( + top_tree_addr, SPX_D - 1); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_type( + top_tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */ + memcpy(sk, seed, PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_CRYPTO_SEEDBYTES); + + memcpy(pk, sk + 2 * SPX_N, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_initialize_hash_function(pk, sk); + + /* Compute root node of the top-most subtree. */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_treehash_TREE_HEIGHT( + sk + 3 * SPX_N, auth_path, sk, sk + 2 * SPX_N, 0, 0, + wots_gen_leaf, top_tree_addr); + + memcpy(pk + SPX_N, sk + 3 * SPX_N, SPX_N); + + return 0; +} + +/* + * Generates an SPX key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk) { + unsigned char seed[PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_CRYPTO_SEEDBYTES]; + randombytes(seed, PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_CRYPTO_SEEDBYTES); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_crypto_sign_seed_keypair( + pk, sk, seed); + + return 0; +} + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + const unsigned char *sk_seed = sk; + const unsigned char *sk_prf = sk + SPX_N; + const unsigned char *pk = sk + 2 * SPX_N; + const unsigned char *pub_seed = pk; + + unsigned char optrand[SPX_N]; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char root[SPX_N]; + uint32_t i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_initialize_hash_function( + pub_seed, sk_seed); + + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ + randombytes(optrand, SPX_N); + /* Compute the digest randomization value. */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_gen_message_random( + sig, sk_prf, optrand, m, mlen); + + /* Derive the message digest and leaf index from R, PK and M. */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Sign the message hash using FORS. */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_fors_sign( + sig, root, mhash, sk_seed, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Compute a WOTS signature. */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_wots_sign( + sig, root, sk_seed, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the authentication path for the used WOTS leaf. */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_treehash_TREE_HEIGHT( + root, sig, sk_seed, pub_seed, idx_leaf, 0, + wots_gen_leaf, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + *siglen = SPX_BYTES; + + return 0; +} + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk) { + const unsigned char *pub_seed = pk; + const unsigned char *pub_root = pk + SPX_N; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char wots_pk[SPX_WOTS_BYTES]; + unsigned char root[SPX_N]; + unsigned char leaf[SPX_N]; + unsigned int i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + if (siglen != SPX_BYTES) { + return -1; + } + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_initialize_hash_function( + pub_seed, NULL); + + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + /* Derive the message digest and leaf index from R || PK || M. */ + /* The additional SPX_N is a result of the hash domain separator. */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + /* Layer correctly defaults to 0, so no need to set_layer_addr */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_fors_pk_from_sig( + root, sig, mhash, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + /* For each subtree.. */ + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + + /* The WOTS public key is only correct if the signature was correct. */ + /* Initially, root is the FORS pk, but on subsequent iterations it is + the root of the subtree below the currently processed subtree. */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_wots_pk_from_sig( + wots_pk, sig, root, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the leaf node using the WOTS public key. */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_thash_WOTS_LEN( + leaf, wots_pk, pub_seed, wots_pk_addr); + + /* Compute the root node of this subtree. */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_compute_root( + root, leaf, idx_leaf, 0, sig, SPX_TREE_HEIGHT, + pub_seed, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + /* Check if the root node equals the root node in the public key. */ + if (memcmp(root, pub_root, SPX_N) != 0) { + return -1; + } + + return 0; +} + + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + size_t siglen; + + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_crypto_sign_signature( + sm, &siglen, m, mlen, sk); + + memmove(sm + SPX_BYTES, m, mlen); + *smlen = siglen + mlen; + + return 0; +} + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk) { + /* The API caller does not necessarily know what size a signature should be + but SPHINCS+ signatures are always exactly SPX_BYTES. */ + if (smlen < SPX_BYTES) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + *mlen = smlen - SPX_BYTES; + + if (PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_crypto_sign_verify( + sm, SPX_BYTES, sm + SPX_BYTES, *mlen, pk)) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + /* If verification was successful, move the message to the right place. */ + memmove(m, sm + SPX_BYTES, *mlen); + + return 0; +} diff --git a/crypto_sign/sphincs-sha256-128s-simple/clean/thash.h b/crypto_sign/sphincs-sha256-128s-simple/clean/thash.h new file mode 100644 index 00000000..7e951838 --- /dev/null +++ b/crypto_sign/sphincs-sha256-128s-simple/clean/thash.h @@ -0,0 +1,22 @@ +#ifndef SPX_THASH_H +#define SPX_THASH_H + +#include + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-sha256-128s-simple/clean/thash_sha256_simple.c b/crypto_sign/sphincs-sha256-128s-simple/clean/thash_sha256_simple.c new file mode 100644 index 00000000..b6303756 --- /dev/null +++ b/crypto_sign/sphincs-sha256-128s-simple/clean/thash_sha256_simple.c @@ -0,0 +1,70 @@ +#include +#include + +#include "address.h" +#include "params.h" +#include "thash.h" + +#include "sha2.h" +#include "sha256.h" + +/** + * Takes an array of inblocks concatenated arrays of SPX_N bytes. + */ +static void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_thash( + unsigned char *out, unsigned char *buf, + const unsigned char *in, unsigned int inblocks, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + uint8_t sha2_state[40]; + + (void)pub_seed; /* Suppress an 'unused parameter' warning. */ + + /* Retrieve precomputed state containing pub_seed */ + memcpy(sha2_state, PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_state_seeded, 40 * sizeof(uint8_t)); + + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_compress_address(buf, addr); + memcpy(buf + SPX_SHA256_ADDR_BYTES, in, inblocks * SPX_N); + + sha256_inc_finalize(outbuf, sha2_state, buf, SPX_SHA256_ADDR_BYTES + inblocks * SPX_N); + memcpy(out, outbuf, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_SHA256_ADDR_BYTES + 1 * SPX_N]; + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_thash( + out, buf, in, 1, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_SHA256_ADDR_BYTES + 2 * SPX_N]; + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_thash( + out, buf, in, 2, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_SHA256_ADDR_BYTES + SPX_WOTS_LEN * SPX_N]; + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_thash( + out, buf, in, SPX_WOTS_LEN, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_SHA256_ADDR_BYTES + SPX_FORS_TREES * SPX_N]; + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_thash( + out, buf, in, SPX_FORS_TREES, pub_seed, addr); +} diff --git a/crypto_sign/sphincs-sha256-128s-simple/clean/utils.c b/crypto_sign/sphincs-sha256-128s-simple/clean/utils.c new file mode 100644 index 00000000..328dc0b3 --- /dev/null +++ b/crypto_sign/sphincs-sha256-128s-simple/clean/utils.c @@ -0,0 +1,192 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in) { + + /* Iterate over out in decreasing order, for big-endianness. */ + for (size_t i = outlen; i > 0; i--) { + out[i - 1] = in & 0xff; + in = in >> 8; + } +} + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen) { + unsigned long long retval = 0; + + for (size_t i = 0; i < inlen; i++) { + retval |= ((unsigned long long)in[i]) << (8 * (inlen - 1 - i)); + } + return retval; +} + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + unsigned char buffer[2 * SPX_N]; + + /* If leaf_idx is odd (last bit = 1), current path element is a right child + and auth_path has to go left. Otherwise it is the other way around. */ + if (leaf_idx & 1) { + memcpy(buffer + SPX_N, leaf, SPX_N); + memcpy(buffer, auth_path, SPX_N); + } else { + memcpy(buffer, leaf, SPX_N); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + + for (i = 0; i < tree_height - 1; i++) { + leaf_idx >>= 1; + idx_offset >>= 1; + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_tree_height(addr, i + 1); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + + /* Pick the right or left neighbor, depending on parity of the node. */ + if (leaf_idx & 1) { + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_thash_2( + buffer + SPX_N, buffer, pub_seed, addr); + memcpy(buffer, auth_path, SPX_N); + } else { + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_thash_2( + buffer, buffer, pub_seed, addr); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + } + + /* The last iteration is exceptional; we do not copy an auth_path node. */ + leaf_idx >>= 1; + idx_offset >>= 1; + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_tree_height(addr, tree_height); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_thash_2( + root, buffer, pub_seed, addr); +} + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +static void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_treehash( + unsigned char *root, unsigned char *auth_path, + unsigned char *stack, unsigned int *heights, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned int offset = 0; + uint32_t idx; + uint32_t tree_idx; + + for (idx = 0; idx < (uint32_t)(1 << tree_height); idx++) { + /* Add the next leaf node to the stack. */ + gen_leaf(stack + offset * SPX_N, + sk_seed, pub_seed, idx + idx_offset, tree_addr); + offset++; + heights[offset - 1] = 0; + + /* If this is a node we need for the auth path.. */ + if ((leaf_idx ^ 0x1) == idx) { + memcpy(auth_path, stack + (offset - 1)*SPX_N, SPX_N); + } + + /* While the top-most nodes are of equal height.. */ + while (offset >= 2 && heights[offset - 1] == heights[offset - 2]) { + /* Compute index of the new node, in the next layer. */ + tree_idx = (idx >> (heights[offset - 1] + 1)); + + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_tree_height( + tree_addr, heights[offset - 1] + 1); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_tree_index( + tree_addr, tree_idx + (idx_offset >> (heights[offset - 1] + 1))); + /* Hash the top-most nodes from the stack together. */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_thash_2( + stack + (offset - 2)*SPX_N, stack + (offset - 2)*SPX_N, + pub_seed, tree_addr); + offset--; + /* Note that the top-most node is now one layer higher. */ + heights[offset - 1]++; + + /* If this is a node we need for the auth path.. */ + if (((leaf_idx >> heights[offset - 1]) ^ 0x1) == tree_idx) { + memcpy(auth_path + heights[offset - 1]*SPX_N, + stack + (offset - 1)*SPX_N, SPX_N); + } + } + } + memcpy(root, stack, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_FORS_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_FORS_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_FORS_HEIGHT, gen_leaf, tree_addr); +} + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_TREE_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_TREE_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_TREE_HEIGHT, gen_leaf, tree_addr); +} diff --git a/crypto_sign/sphincs-sha256-128s-simple/clean/utils.h b/crypto_sign/sphincs-sha256-128s-simple/clean/utils.h new file mode 100644 index 00000000..cc6dfb93 --- /dev/null +++ b/crypto_sign/sphincs-sha256-128s-simple/clean/utils.h @@ -0,0 +1,60 @@ +#ifndef SPX_UTILS_H +#define SPX_UTILS_H + +#include "params.h" +#include +#include + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in); + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen); + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-sha256-128s-simple/clean/wots.c b/crypto_sign/sphincs-sha256-128s-simple/clean/wots.c new file mode 100644 index 00000000..978f756a --- /dev/null +++ b/crypto_sign/sphincs-sha256-128s-simple/clean/wots.c @@ -0,0 +1,161 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +// TODO clarify address expectations, and make them more uniform. +// TODO i.e. do we expect types to be set already? +// TODO and do we expect modifications or copies? + +/** + * Computes the starting value for a chain, i.e. the secret key. + * Expects the address to be complete up to the chain address. + */ +static void wots_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t wots_addr[8]) { + /* Make sure that the hash address is actually zeroed. */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_hash_addr(wots_addr, 0); + + /* Generate sk element. */ + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_prf_addr(sk, sk_seed, wots_addr); +} + +/** + * Computes the chaining function. + * out and in have to be n-byte arrays. + * + * Interprets in as start-th value of the chain. + * addr has to contain the address of the chain. + */ +static void gen_chain(unsigned char *out, const unsigned char *in, + unsigned int start, unsigned int steps, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + /* Initialize out with the value at position 'start'. */ + memcpy(out, in, SPX_N); + + /* Iterate 'steps' calls to the hash function. */ + for (i = start; i < (start + steps) && i < SPX_WOTS_W; i++) { + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_hash_addr(addr, i); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_thash_1( + out, out, pub_seed, addr); + } +} + +/** + * base_w algorithm as described in draft. + * Interprets an array of bytes as integers in base w. + * This only works when log_w is a divisor of 8. + */ +static void base_w(unsigned int *output, const size_t out_len, + const unsigned char *input) { + size_t in = 0; + size_t out = 0; + unsigned char total = 0; + unsigned int bits = 0; + size_t consumed; + + for (consumed = 0; consumed < out_len; consumed++) { + if (bits == 0) { + total = input[in]; + in++; + bits += 8; + } + bits -= SPX_WOTS_LOGW; + output[out] = (unsigned int)((total >> bits) & (SPX_WOTS_W - 1)); + out++; + } +} + +/* Computes the WOTS+ checksum over a message (in base_w). */ +static void wots_checksum(unsigned int *csum_base_w, + const unsigned int *msg_base_w) { + unsigned int csum = 0; + unsigned char csum_bytes[(SPX_WOTS_LEN2 * SPX_WOTS_LOGW + 7) / 8]; + unsigned int i; + + /* Compute checksum. */ + for (i = 0; i < SPX_WOTS_LEN1; i++) { + csum += SPX_WOTS_W - 1 - msg_base_w[i]; + } + + /* Convert checksum to base_w. */ + /* Make sure expected empty zero bits are the least significant bits. */ + csum = csum << (8 - ((SPX_WOTS_LEN2 * SPX_WOTS_LOGW) % 8)); + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_ull_to_bytes( + csum_bytes, sizeof(csum_bytes), csum); + base_w(csum_base_w, SPX_WOTS_LEN2, csum_bytes); +} + +/* Takes a message and derives the matching chain lengths. */ +static void chain_lengths(unsigned int *lengths, const unsigned char *msg) { + base_w(lengths, SPX_WOTS_LEN1, msg); + wots_checksum(lengths + SPX_WOTS_LEN1, lengths); +} + +/** + * WOTS key generation. Takes a 32 byte sk_seed, expands it to WOTS private key + * elements and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(pk + i * SPX_N, sk_seed, addr); + gen_chain(pk + i * SPX_N, pk + i * SPX_N, + 0, SPX_WOTS_W - 1, pub_seed, addr); + } +} + +/** + * Takes a n-byte message and the 32-byte sk_see to compute a signature 'sig'. + */ +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(sig + i * SPX_N, sk_seed, addr); + gen_chain(sig + i * SPX_N, sig + i * SPX_N, 0, lengths[i], pub_seed, addr); + } +} + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_set_chain_addr(addr, i); + gen_chain(pk + i * SPX_N, sig + i * SPX_N, + lengths[i], SPX_WOTS_W - 1 - lengths[i], pub_seed, addr); + } +} diff --git a/crypto_sign/sphincs-sha256-128s-simple/clean/wots.h b/crypto_sign/sphincs-sha256-128s-simple/clean/wots.h new file mode 100644 index 00000000..3961168a --- /dev/null +++ b/crypto_sign/sphincs-sha256-128s-simple/clean/wots.h @@ -0,0 +1,38 @@ +#ifndef SPX_WOTS_H +#define SPX_WOTS_H + +#include "params.h" +#include + +/** + * WOTS key generation. Takes a 32 byte seed for the private key, expands it to + * a full WOTS private key and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * Takes a n-byte message and the 32-byte seed for the private key to compute a + * signature that is placed at 'sig'. + */ +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]); + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-sha256-192f-robust/META.yml b/crypto_sign/sphincs-sha256-192f-robust/META.yml new file mode 100644 index 00000000..8ede894f --- /dev/null +++ b/crypto_sign/sphincs-sha256-192f-robust/META.yml @@ -0,0 +1,27 @@ +name: SPHINCS+ +type: signature +claimed-nist-level: 3 +length-public-key: 48 +length-secret-key: 96 +length-signature: 35664 +testvectors-sha256: ca61e66c0377fd367ab0c920d2190855a64348668a336d300ec7f2c72e721be4 +principal-submitter: Andreas Hülsing +auxiliary-submitters: + - Jean-Philippe Aumasson + - Daniel J. Bernstein, + - Christoph Dobraunig + - Maria Eichlseder + - Scott Fluhrer + - Stefan-Lukas Gazdag + - Panos Kampanakis + - Stefan Kölbl + - Tanja Lange + - Martin M. Lauridsen + - Florian Mendel + - Ruben Niederhagen + - Christian Rechberger + - Joost Rijneveld + - Peter Schwabe +implementations: + - name: clean + version: https://github.com/sphincs/sphincsplus/commit/77755c94d0bc744478044d6efbb888dc13156441 diff --git a/crypto_sign/sphincs-sha256-192f-robust/clean/LICENSE b/crypto_sign/sphincs-sha256-192f-robust/clean/LICENSE new file mode 100644 index 00000000..670154e3 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192f-robust/clean/LICENSE @@ -0,0 +1,116 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + diff --git a/crypto_sign/sphincs-sha256-192f-robust/clean/Makefile b/crypto_sign/sphincs-sha256-192f-robust/clean/Makefile new file mode 100644 index 00000000..a06d5196 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192f-robust/clean/Makefile @@ -0,0 +1,20 @@ +# This Makefile can be used with GNU Make or BSD Make + +LIB=libsphincs-sha256-192f-robust_clean.a + +HEADERS = params.h address.h wots.h utils.h fors.h api.h hash.h thash.h sha256.h +OBJECTS = address.o wots.o utils.o fors.o sign.o hash_sha256.o thash_sha256_robust.o sha256.o + +CFLAGS=-O3 -Wall -Wconversion -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) + +all: $(LIB) + +%.o: %.c $(HEADERS) + $(CC) $(CFLAGS) -c -o $@ $< + +$(LIB): $(OBJECTS) + $(AR) -r $@ $(OBJECTS) + +clean: + $(RM) $(OBJECTS) + $(RM) $(LIB) diff --git a/crypto_sign/sphincs-sha256-192f-robust/clean/Makefile.Microsoft_nmake b/crypto_sign/sphincs-sha256-192f-robust/clean/Makefile.Microsoft_nmake new file mode 100644 index 00000000..d67e2005 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192f-robust/clean/Makefile.Microsoft_nmake @@ -0,0 +1,19 @@ +# This Makefile can be used with Microsoft Visual Studio's nmake using the command: +# nmake /f Makefile.Microsoft_nmake + +LIBRARY=libsphincs-sha256-192f-robust_clean.lib +OBJECTS=address.obj wots.obj utils.obj fors.obj sign.obj hash_sha256.obj thash_sha256_robust.obj sha256.obj + +CFLAGS=/nologo /I ..\..\..\common /W4 /WX + +all: $(LIBRARY) + +# Make sure objects are recompiled if headers change. +$(OBJECTS): *.h + +$(LIBRARY): $(OBJECTS) + LIB.EXE /NOLOGO /WX /OUT:$@ $** + +clean: + -DEL $(OBJECTS) + -DEL $(LIBRARY) diff --git a/crypto_sign/sphincs-sha256-192f-robust/clean/address.c b/crypto_sign/sphincs-sha256-192f-robust/clean/address.c new file mode 100644 index 00000000..220bf4a3 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192f-robust/clean/address.c @@ -0,0 +1,78 @@ +#include + +#include "address.h" +#include "params.h" +#include "utils.h" + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]) { + int i; + + for (i = 0; i < 8; i++) { + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_ull_to_bytes( + bytes + i * 4, 4, addr[i]); + } +} + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer) { + addr[0] = layer; +} + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree) { + addr[1] = 0; + addr[2] = (uint32_t) (tree >> 32); + addr[3] = (uint32_t) tree; +} + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_type( + uint32_t addr[8], uint32_t type) { + addr[4] = type; +} + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; +} + +/* These functions are used for OTS addresses. */ + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair) { + addr[5] = keypair; +} + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; + out[5] = in[5]; +} + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain) { + addr[6] = chain; +} + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash) { + addr[7] = hash; +} + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height) { + addr[6] = tree_height; +} + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index) { + addr[7] = tree_index; +} diff --git a/crypto_sign/sphincs-sha256-192f-robust/clean/address.h b/crypto_sign/sphincs-sha256-192f-robust/clean/address.h new file mode 100644 index 00000000..4b69abb5 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192f-robust/clean/address.h @@ -0,0 +1,50 @@ +#ifndef SPX_ADDRESS_H +#define SPX_ADDRESS_H + +#include + +#define SPX_ADDR_TYPE_WOTS 0 +#define SPX_ADDR_TYPE_WOTSPK 1 +#define SPX_ADDR_TYPE_HASHTREE 2 +#define SPX_ADDR_TYPE_FORSTREE 3 +#define SPX_ADDR_TYPE_FORSPK 4 + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer); + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree); + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_type( + uint32_t addr[8], uint32_t type); + +/* Copies the layer and tree part of one address into the other */ +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for WOTS and FORS addresses. */ + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair); + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain); + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash); + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height); + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index); + +#endif diff --git a/crypto_sign/sphincs-sha256-192f-robust/clean/api.h b/crypto_sign/sphincs-sha256-192f-robust/clean/api.h new file mode 100644 index 00000000..5c25fb58 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192f-robust/clean/api.h @@ -0,0 +1,78 @@ +#ifndef PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_API_H +#define PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_API_H + +#include +#include + +#define PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_CRYPTO_ALGNAME "SPHINCS+" + +#define PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_CRYPTO_SECRETKEYBYTES 96 +#define PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES 48 +#define PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_CRYPTO_BYTES 35664 +#define PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_CRYPTO_SEEDBYTES 72 + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_crypto_sign_secretkeybytes(void); + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_crypto_sign_publickeybytes(void); + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_crypto_sign_bytes(void); + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_crypto_sign_seedbytes(void); + +/* + * Generates a SPHINCS+ key pair given a seed. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed); + +/* + * Generates a SPHINCS+ key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk); + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk); + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk); + +#endif diff --git a/crypto_sign/sphincs-sha256-192f-robust/clean/fors.c b/crypto_sign/sphincs-sha256-192f-robust/clean/fors.c new file mode 100644 index 00000000..0a51b70a --- /dev/null +++ b/crypto_sign/sphincs-sha256-192f-robust/clean/fors.c @@ -0,0 +1,164 @@ +#include +#include +#include + +#include "address.h" +#include "fors.h" +#include "hash.h" +#include "thash.h" +#include "utils.h" + +static void fors_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_prf_addr( + sk, sk_seed, fors_leaf_addr); +} + +static void fors_sk_to_leaf(unsigned char *leaf, const unsigned char *sk, + const unsigned char *pub_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_thash_1( + leaf, sk, pub_seed, fors_leaf_addr); +} + +static void fors_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t fors_tree_addr[8]) { + uint32_t fors_leaf_addr[8] = {0}; + + /* Only copy the parts that must be kept in fors_leaf_addr. */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_copy_keypair_addr( + fors_leaf_addr, fors_tree_addr); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_type( + fors_leaf_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_tree_index( + fors_leaf_addr, addr_idx); + + fors_gen_sk(leaf, sk_seed, fors_leaf_addr); + fors_sk_to_leaf(leaf, leaf, pub_seed, fors_leaf_addr); +} + +/** + * Interprets m as SPX_FORS_HEIGHT-bit unsigned integers. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + * Assumes indices has space for SPX_FORS_TREES integers. + */ +static void message_to_indices(uint32_t *indices, const unsigned char *m) { + unsigned int i, j; + unsigned int offset = 0; + + for (i = 0; i < SPX_FORS_TREES; i++) { + indices[i] = 0; + for (j = 0; j < SPX_FORS_HEIGHT; j++) { + indices[i] ^= (((uint32_t)m[offset >> 3] >> (offset & 0x7)) & 0x1) << j; + offset++; + } + } +} + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Include the secret key part that produces the selected leaf node. */ + fors_gen_sk(sig, sk_seed, fors_tree_addr); + sig += SPX_N; + + /* Compute the authentication path for this leaf node. */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_treehash_FORS_HEIGHT( + roots + i * SPX_N, sig, sk_seed, pub_seed, + indices[i], idx_offset, fors_gen_leaf, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + unsigned char leaf[SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Derive the leaf from the included secret key part. */ + fors_sk_to_leaf(leaf, sig, pub_seed, fors_tree_addr); + sig += SPX_N; + + /* Derive the corresponding root node of this tree. */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_compute_root( + roots + i * SPX_N, leaf, indices[i], idx_offset, sig, + SPX_FORS_HEIGHT, pub_seed, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} diff --git a/crypto_sign/sphincs-sha256-192f-robust/clean/fors.h b/crypto_sign/sphincs-sha256-192f-robust/clean/fors.h new file mode 100644 index 00000000..65e7d66c --- /dev/null +++ b/crypto_sign/sphincs-sha256-192f-robust/clean/fors.h @@ -0,0 +1,30 @@ +#ifndef SPX_FORS_H +#define SPX_FORS_H + +#include + +#include "params.h" + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]); + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-sha256-192f-robust/clean/hash.h b/crypto_sign/sphincs-sha256-192f-robust/clean/hash.h new file mode 100644 index 00000000..513103d1 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192f-robust/clean/hash.h @@ -0,0 +1,22 @@ +#ifndef SPX_HASH_H +#define SPX_HASH_H + +#include + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed); + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen); + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen); + +#endif diff --git a/crypto_sign/sphincs-sha256-192f-robust/clean/hash_sha256.c b/crypto_sign/sphincs-sha256-192f-robust/clean/hash_sha256.c new file mode 100644 index 00000000..73d3bbd1 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192f-robust/clean/hash_sha256.c @@ -0,0 +1,148 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "utils.h" + +#include "sha2.h" +#include "sha256.h" + +/* For SHA256, there is no immediate reason to initialize at the start, + so this function is an empty operation. */ +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed) { + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_seed_state(pub_seed); + (void)sk_seed; /* Suppress an 'unused parameter' warning. */ +} + +/* + * Computes PRF(key, addr), given a secret key of SPX_N bytes and an address + */ +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]) { + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES]; + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + + memcpy(buf, key, SPX_N); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_compress_address(buf + SPX_N, addr); + + sha256(outbuf, buf, SPX_N + SPX_SHA256_ADDR_BYTES); + memcpy(out, outbuf, SPX_N); +} + +/** + * Computes the message-dependent randomness R, using a secret seed as a key + * for HMAC, and an optional randomization value prefixed to the message. + * This requires m to have at least SPX_SHA256_BLOCK_BYTES + SPX_N space + * available in front of the pointer, i.e. before the message to use for the + * prefix. This is necessary to prevent having to move the message around (and + * allocate memory for it). + */ +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen) { + unsigned char buf[SPX_SHA256_BLOCK_BYTES + SPX_SHA256_OUTPUT_BYTES]; + uint8_t state[40]; + int i; + + /* This implements HMAC-SHA256 */ + for (i = 0; i < SPX_N; i++) { + buf[i] = 0x36 ^ sk_prf[i]; + } + memset(buf + SPX_N, 0x36, SPX_SHA256_BLOCK_BYTES - SPX_N); + + sha256_inc_init(state); + sha256_inc_blocks(state, buf, 1); + + memcpy(buf, optrand, SPX_N); + + /* If optrand + message cannot fill up an entire block */ + if (SPX_N + mlen < SPX_SHA256_BLOCK_BYTES) { + memcpy(buf + SPX_N, m, mlen); + sha256_inc_finalize(buf + SPX_SHA256_BLOCK_BYTES, state, + buf, mlen + SPX_N); + } + /* Otherwise first fill a block, so that finalize only uses the message */ + else { + memcpy(buf + SPX_N, m, SPX_SHA256_BLOCK_BYTES - SPX_N); + sha256_inc_blocks(state, buf, 1); + + m += SPX_SHA256_BLOCK_BYTES - SPX_N; + mlen -= SPX_SHA256_BLOCK_BYTES - SPX_N; + sha256_inc_finalize(buf + SPX_SHA256_BLOCK_BYTES, state, m, mlen); + } + + for (i = 0; i < SPX_N; i++) { + buf[i] = 0x5c ^ sk_prf[i]; + } + memset(buf + SPX_N, 0x5c, SPX_SHA256_BLOCK_BYTES - SPX_N); + + sha256(buf, buf, SPX_SHA256_BLOCK_BYTES + SPX_SHA256_OUTPUT_BYTES); + memcpy(R, buf, SPX_N); +} + +/** + * Computes the message hash using R, the public key, and the message. + * Outputs the message digest and the index of the leaf. The index is split in + * the tree index and the leaf index, for convenient copying to an address. + */ +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen) { +#define SPX_TREE_BITS (SPX_TREE_HEIGHT * (SPX_D - 1)) +#define SPX_TREE_BYTES ((SPX_TREE_BITS + 7) / 8) +#define SPX_LEAF_BITS SPX_TREE_HEIGHT +#define SPX_LEAF_BYTES ((SPX_LEAF_BITS + 7) / 8) +#define SPX_DGST_BYTES (SPX_FORS_MSG_BYTES + SPX_TREE_BYTES + SPX_LEAF_BYTES) + + unsigned char seed[SPX_SHA256_OUTPUT_BYTES + 4]; + + /* Round to nearest multiple of SPX_SHA256_BLOCK_BYTES */ +#define SPX_INBLOCKS (((SPX_N + SPX_PK_BYTES + SPX_SHA256_BLOCK_BYTES - 1) & \ + -SPX_SHA256_BLOCK_BYTES) / SPX_SHA256_BLOCK_BYTES) + unsigned char inbuf[SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES]; + + unsigned char buf[SPX_DGST_BYTES]; + unsigned char *bufp = buf; + uint8_t state[40]; + + sha256_inc_init(state); + + memcpy(inbuf, R, SPX_N); + memcpy(inbuf + SPX_N, pk, SPX_PK_BYTES); + + /* If R + pk + message cannot fill up an entire block */ + if (SPX_N + SPX_PK_BYTES + mlen < SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES) { + memcpy(inbuf + SPX_N + SPX_PK_BYTES, m, mlen); + sha256_inc_finalize(seed, state, inbuf, SPX_N + SPX_PK_BYTES + mlen); + } + /* Otherwise first fill a block, so that finalize only uses the message */ + else { + memcpy(inbuf + SPX_N + SPX_PK_BYTES, m, + SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES); + sha256_inc_blocks(state, inbuf, SPX_INBLOCKS); + + m += SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES; + mlen -= SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES; + sha256_inc_finalize(seed, state, m, mlen); + } + + /* By doing this in two steps, we prevent hashing the message twice; + otherwise each iteration in MGF1 would hash the message again. */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_mgf1(bufp, SPX_DGST_BYTES, seed, SPX_SHA256_OUTPUT_BYTES); + + memcpy(digest, bufp, SPX_FORS_MSG_BYTES); + bufp += SPX_FORS_MSG_BYTES; + + *tree = PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_bytes_to_ull(bufp, SPX_TREE_BYTES); + *tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS); + bufp += SPX_TREE_BYTES; + + *leaf_idx = (uint32_t)PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_bytes_to_ull( + bufp, SPX_LEAF_BYTES); + *leaf_idx &= (~(uint32_t)0) >> (32 - SPX_LEAF_BITS); +} diff --git a/crypto_sign/sphincs-sha256-192f-robust/clean/params.h b/crypto_sign/sphincs-sha256-192f-robust/clean/params.h new file mode 100644 index 00000000..898f177f --- /dev/null +++ b/crypto_sign/sphincs-sha256-192f-robust/clean/params.h @@ -0,0 +1,53 @@ +#ifndef SPX_PARAMS_H +#define SPX_PARAMS_H + +/* Hash output length in bytes. */ +#define SPX_N 24 +/* Height of the hypertree. */ +#define SPX_FULL_HEIGHT 66 +/* Number of subtree layer. */ +#define SPX_D 22 +/* FORS tree dimensions. */ +#define SPX_FORS_HEIGHT 8 +#define SPX_FORS_TREES 33 +/* Winternitz parameter, */ +#define SPX_WOTS_W 16 + +/* The hash function is defined by linking a different hash.c file, as opposed + to setting a #define constant. */ + +/* For clarity */ +#define SPX_ADDR_BYTES 32 + +/* WOTS parameters. */ +#define SPX_WOTS_LOGW 4 + +#define SPX_WOTS_LEN1 (8 * SPX_N / SPX_WOTS_LOGW) + +/* SPX_WOTS_LEN2 is floor(log(len_1 * (w - 1)) / log(w)) + 1; we precompute */ +#define SPX_WOTS_LEN2 3 + +#define SPX_WOTS_LEN (SPX_WOTS_LEN1 + SPX_WOTS_LEN2) +#define SPX_WOTS_BYTES (SPX_WOTS_LEN * SPX_N) +#define SPX_WOTS_PK_BYTES SPX_WOTS_BYTES + +/* Subtree size. */ +#define SPX_TREE_HEIGHT (SPX_FULL_HEIGHT / SPX_D) + +/* FORS parameters. */ +#define SPX_FORS_MSG_BYTES ((SPX_FORS_HEIGHT * SPX_FORS_TREES + 7) / 8) +#define SPX_FORS_BYTES ((SPX_FORS_HEIGHT + 1) * SPX_FORS_TREES * SPX_N) +#define SPX_FORS_PK_BYTES SPX_N + +/* Resulting SPX sizes. */ +#define SPX_BYTES (SPX_N + SPX_FORS_BYTES + SPX_D * SPX_WOTS_BYTES +\ + SPX_FULL_HEIGHT * SPX_N) +#define SPX_PK_BYTES (2 * SPX_N) +#define SPX_SK_BYTES (2 * SPX_N + SPX_PK_BYTES) + +/* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ +#define SPX_OPTRAND_BYTES 32 + +#endif diff --git a/crypto_sign/sphincs-sha256-192f-robust/clean/sha256.c b/crypto_sign/sphincs-sha256-192f-robust/clean/sha256.c new file mode 100644 index 00000000..4f112ae9 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192f-robust/clean/sha256.c @@ -0,0 +1,72 @@ +/* Based on the public domain implementation in + * crypto_hash/sha512/ref/ from http://bench.cr.yp.to/supercop.html + * by D. J. Bernstein */ + +#include +#include +#include + +#include "sha2.h" +#include "sha256.h" +#include "utils.h" + +/* + * Compresses an address to a 22-byte sequence. + * This reduces the number of required SHA256 compression calls, as the last + * block of input is padded with at least 65 bits. + */ +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_compress_address(unsigned char *out, const uint32_t addr[8]) { + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_ull_to_bytes(out, 1, addr[0]); /* drop 3 bytes of the layer field */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_ull_to_bytes(out + 1, 4, addr[2]); /* drop the highest tree address word */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_ull_to_bytes(out + 5, 4, addr[3]); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_ull_to_bytes(out + 9, 1, addr[4]); /* drop 3 bytes of the type field */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_ull_to_bytes(out + 10, 4, addr[5]); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_ull_to_bytes(out + 14, 4, addr[6]); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_ull_to_bytes(out + 18, 4, addr[7]); +} + +/** + * Requires 'input_plus_four_bytes' to have 'inlen' + 4 bytes, so that the last + * four bytes can be used for the counter. Typically 'input' is merely a seed. + * Outputs outlen number of bytes + */ +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + unsigned char *input_plus_four_bytes, unsigned long inlen) { + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + unsigned long i; + + /* While we can fit in at least another full block of SHA256 output.. */ + for (i = 0; (i + 1)*SPX_SHA256_OUTPUT_BYTES <= outlen; i++) { + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_ull_to_bytes(input_plus_four_bytes + inlen, 4, i); + sha256(out, input_plus_four_bytes, inlen + 4); + out += SPX_SHA256_OUTPUT_BYTES; + } + /* Until we cannot anymore, and we fill the remainder. */ + if (outlen > i * SPX_SHA256_OUTPUT_BYTES) { + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_ull_to_bytes(input_plus_four_bytes + inlen, 4, i); + sha256(outbuf, input_plus_four_bytes, inlen + 4); + memcpy(out, outbuf, outlen - i * SPX_SHA256_OUTPUT_BYTES); + } +} + +uint8_t PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_state_seeded[40]; + +/** + * Absorb the constant pub_seed using one round of the compression function + * This initializes state_seeded, which can then be reused in thash + **/ +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_seed_state(const unsigned char *pub_seed) { + uint8_t block[SPX_SHA256_BLOCK_BYTES]; + size_t i; + + for (i = 0; i < SPX_N; ++i) { + block[i] = pub_seed[i]; + } + for (i = SPX_N; i < SPX_SHA256_BLOCK_BYTES; ++i) { + block[i] = 0; + } + + sha256_inc_init(PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_state_seeded); + sha256_inc_blocks(PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_state_seeded, block, 1); +} diff --git a/crypto_sign/sphincs-sha256-192f-robust/clean/sha256.h b/crypto_sign/sphincs-sha256-192f-robust/clean/sha256.h new file mode 100644 index 00000000..d5a2342b --- /dev/null +++ b/crypto_sign/sphincs-sha256-192f-robust/clean/sha256.h @@ -0,0 +1,21 @@ +#ifndef SPX_SHA256_H +#define SPX_SHA256_H + +#define SPX_SHA256_BLOCK_BYTES 64 +#define SPX_SHA256_OUTPUT_BYTES 32 /* This does not necessarily equal SPX_N */ +#define SPX_SHA256_ADDR_BYTES 22 + +#include +#include + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_compress_address(unsigned char *out, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + unsigned char *input_plus_four_bytes, unsigned long inlen); + +uint8_t PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_state_seeded[40]; + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_seed_state(const unsigned char *pub_seed); + +#endif diff --git a/crypto_sign/sphincs-sha256-192f-robust/clean/sign.c b/crypto_sign/sphincs-sha256-192f-robust/clean/sign.c new file mode 100644 index 00000000..6d1a9c9f --- /dev/null +++ b/crypto_sign/sphincs-sha256-192f-robust/clean/sign.c @@ -0,0 +1,344 @@ +#include +#include +#include + +#include "address.h" +#include "api.h" +#include "fors.h" +#include "hash.h" +#include "params.h" +#include "randombytes.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +/** + * Computes the leaf at a given address. First generates the WOTS key pair, + * then computes leaf by hashing horizontally. + */ +static void wots_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t tree_addr[8]) { + unsigned char pk[SPX_WOTS_BYTES]; + uint32_t wots_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_keypair_addr( + wots_addr, addr_idx); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_wots_gen_pk( + pk, sk_seed, pub_seed, wots_addr); + + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_thash_WOTS_LEN( + leaf, pk, pub_seed, wots_pk_addr); +} + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_crypto_sign_secretkeybytes(void) { + return PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_CRYPTO_SECRETKEYBYTES; +} + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_crypto_sign_publickeybytes(void) { + return PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES; +} + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_crypto_sign_bytes(void) { + return PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_CRYPTO_BYTES; +} + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_crypto_sign_seedbytes(void) { + return PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_CRYPTO_SEEDBYTES; +} + +/* + * Generates an SPX key pair given a seed of length + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed) { + /* We do not need the auth path in key generation, but it simplifies the + code to have just one treehash routine that computes both root and path + in one function. */ + unsigned char auth_path[SPX_TREE_HEIGHT * SPX_N]; + uint32_t top_tree_addr[8] = {0}; + + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_layer_addr( + top_tree_addr, SPX_D - 1); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_type( + top_tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */ + memcpy(sk, seed, PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_CRYPTO_SEEDBYTES); + + memcpy(pk, sk + 2 * SPX_N, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_initialize_hash_function(pk, sk); + + /* Compute root node of the top-most subtree. */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_treehash_TREE_HEIGHT( + sk + 3 * SPX_N, auth_path, sk, sk + 2 * SPX_N, 0, 0, + wots_gen_leaf, top_tree_addr); + + memcpy(pk + SPX_N, sk + 3 * SPX_N, SPX_N); + + return 0; +} + +/* + * Generates an SPX key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk) { + unsigned char seed[PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_CRYPTO_SEEDBYTES]; + randombytes(seed, PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_CRYPTO_SEEDBYTES); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_crypto_sign_seed_keypair( + pk, sk, seed); + + return 0; +} + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + const unsigned char *sk_seed = sk; + const unsigned char *sk_prf = sk + SPX_N; + const unsigned char *pk = sk + 2 * SPX_N; + const unsigned char *pub_seed = pk; + + unsigned char optrand[SPX_N]; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char root[SPX_N]; + uint32_t i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_initialize_hash_function( + pub_seed, sk_seed); + + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ + randombytes(optrand, SPX_N); + /* Compute the digest randomization value. */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_gen_message_random( + sig, sk_prf, optrand, m, mlen); + + /* Derive the message digest and leaf index from R, PK and M. */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Sign the message hash using FORS. */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_fors_sign( + sig, root, mhash, sk_seed, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Compute a WOTS signature. */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_wots_sign( + sig, root, sk_seed, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the authentication path for the used WOTS leaf. */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_treehash_TREE_HEIGHT( + root, sig, sk_seed, pub_seed, idx_leaf, 0, + wots_gen_leaf, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + *siglen = SPX_BYTES; + + return 0; +} + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk) { + const unsigned char *pub_seed = pk; + const unsigned char *pub_root = pk + SPX_N; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char wots_pk[SPX_WOTS_BYTES]; + unsigned char root[SPX_N]; + unsigned char leaf[SPX_N]; + unsigned int i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + if (siglen != SPX_BYTES) { + return -1; + } + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_initialize_hash_function( + pub_seed, NULL); + + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + /* Derive the message digest and leaf index from R || PK || M. */ + /* The additional SPX_N is a result of the hash domain separator. */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + /* Layer correctly defaults to 0, so no need to set_layer_addr */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_fors_pk_from_sig( + root, sig, mhash, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + /* For each subtree.. */ + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + + /* The WOTS public key is only correct if the signature was correct. */ + /* Initially, root is the FORS pk, but on subsequent iterations it is + the root of the subtree below the currently processed subtree. */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_wots_pk_from_sig( + wots_pk, sig, root, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the leaf node using the WOTS public key. */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_thash_WOTS_LEN( + leaf, wots_pk, pub_seed, wots_pk_addr); + + /* Compute the root node of this subtree. */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_compute_root( + root, leaf, idx_leaf, 0, sig, SPX_TREE_HEIGHT, + pub_seed, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + /* Check if the root node equals the root node in the public key. */ + if (memcmp(root, pub_root, SPX_N) != 0) { + return -1; + } + + return 0; +} + + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + size_t siglen; + + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_crypto_sign_signature( + sm, &siglen, m, mlen, sk); + + memmove(sm + SPX_BYTES, m, mlen); + *smlen = siglen + mlen; + + return 0; +} + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk) { + /* The API caller does not necessarily know what size a signature should be + but SPHINCS+ signatures are always exactly SPX_BYTES. */ + if (smlen < SPX_BYTES) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + *mlen = smlen - SPX_BYTES; + + if (PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_crypto_sign_verify( + sm, SPX_BYTES, sm + SPX_BYTES, *mlen, pk)) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + /* If verification was successful, move the message to the right place. */ + memmove(m, sm + SPX_BYTES, *mlen); + + return 0; +} diff --git a/crypto_sign/sphincs-sha256-192f-robust/clean/thash.h b/crypto_sign/sphincs-sha256-192f-robust/clean/thash.h new file mode 100644 index 00000000..e9bc0ced --- /dev/null +++ b/crypto_sign/sphincs-sha256-192f-robust/clean/thash.h @@ -0,0 +1,22 @@ +#ifndef SPX_THASH_H +#define SPX_THASH_H + +#include + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-sha256-192f-robust/clean/thash_sha256_robust.c b/crypto_sign/sphincs-sha256-192f-robust/clean/thash_sha256_robust.c new file mode 100644 index 00000000..32924361 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192f-robust/clean/thash_sha256_robust.c @@ -0,0 +1,77 @@ +#include +#include + +#include "address.h" +#include "params.h" +#include "thash.h" + +#include "sha2.h" +#include "sha256.h" + +/** + * Takes an array of inblocks concatenated arrays of SPX_N bytes. + */ +static void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_thash( + unsigned char *out, unsigned char *buf, + const unsigned char *in, unsigned int inblocks, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + unsigned char *bitmask = buf + SPX_N + SPX_SHA256_ADDR_BYTES + 4; + uint8_t sha2_state[40]; + unsigned int i; + + memcpy(buf, pub_seed, SPX_N); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_compress_address(buf + SPX_N, addr); + /* MGF1 requires us to have 4 extra bytes in 'buf' */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_mgf1(bitmask, inblocks * SPX_N, buf, SPX_N + SPX_SHA256_ADDR_BYTES); + + /* Retrieve precomputed state containing pub_seed */ + memcpy(sha2_state, PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_state_seeded, 40 * sizeof(uint8_t)); + + for (i = 0; i < inblocks * SPX_N; i++) { + buf[SPX_N + SPX_SHA256_ADDR_BYTES + i] = in[i] ^ bitmask[i]; + } + + sha256_inc_finalize(outbuf, sha2_state, buf + SPX_N, + SPX_SHA256_ADDR_BYTES + inblocks * SPX_N); + memcpy(out, outbuf, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES + 4 + 1 * SPX_N]; + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_thash( + out, buf, in, 1, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES + 4 + 2 * SPX_N]; + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_thash( + out, buf, in, 2, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES + 4 + SPX_WOTS_LEN * SPX_N]; + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_thash( + out, buf, in, SPX_WOTS_LEN, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES + 4 + SPX_FORS_TREES * SPX_N]; + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_thash( + out, buf, in, SPX_FORS_TREES, pub_seed, addr); +} diff --git a/crypto_sign/sphincs-sha256-192f-robust/clean/utils.c b/crypto_sign/sphincs-sha256-192f-robust/clean/utils.c new file mode 100644 index 00000000..675436b3 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192f-robust/clean/utils.c @@ -0,0 +1,192 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in) { + + /* Iterate over out in decreasing order, for big-endianness. */ + for (size_t i = outlen; i > 0; i--) { + out[i - 1] = in & 0xff; + in = in >> 8; + } +} + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen) { + unsigned long long retval = 0; + + for (size_t i = 0; i < inlen; i++) { + retval |= ((unsigned long long)in[i]) << (8 * (inlen - 1 - i)); + } + return retval; +} + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + unsigned char buffer[2 * SPX_N]; + + /* If leaf_idx is odd (last bit = 1), current path element is a right child + and auth_path has to go left. Otherwise it is the other way around. */ + if (leaf_idx & 1) { + memcpy(buffer + SPX_N, leaf, SPX_N); + memcpy(buffer, auth_path, SPX_N); + } else { + memcpy(buffer, leaf, SPX_N); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + + for (i = 0; i < tree_height - 1; i++) { + leaf_idx >>= 1; + idx_offset >>= 1; + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_tree_height(addr, i + 1); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + + /* Pick the right or left neighbor, depending on parity of the node. */ + if (leaf_idx & 1) { + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_thash_2( + buffer + SPX_N, buffer, pub_seed, addr); + memcpy(buffer, auth_path, SPX_N); + } else { + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_thash_2( + buffer, buffer, pub_seed, addr); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + } + + /* The last iteration is exceptional; we do not copy an auth_path node. */ + leaf_idx >>= 1; + idx_offset >>= 1; + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_tree_height(addr, tree_height); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_thash_2( + root, buffer, pub_seed, addr); +} + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +static void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_treehash( + unsigned char *root, unsigned char *auth_path, + unsigned char *stack, unsigned int *heights, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned int offset = 0; + uint32_t idx; + uint32_t tree_idx; + + for (idx = 0; idx < (uint32_t)(1 << tree_height); idx++) { + /* Add the next leaf node to the stack. */ + gen_leaf(stack + offset * SPX_N, + sk_seed, pub_seed, idx + idx_offset, tree_addr); + offset++; + heights[offset - 1] = 0; + + /* If this is a node we need for the auth path.. */ + if ((leaf_idx ^ 0x1) == idx) { + memcpy(auth_path, stack + (offset - 1)*SPX_N, SPX_N); + } + + /* While the top-most nodes are of equal height.. */ + while (offset >= 2 && heights[offset - 1] == heights[offset - 2]) { + /* Compute index of the new node, in the next layer. */ + tree_idx = (idx >> (heights[offset - 1] + 1)); + + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_tree_height( + tree_addr, heights[offset - 1] + 1); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_tree_index( + tree_addr, tree_idx + (idx_offset >> (heights[offset - 1] + 1))); + /* Hash the top-most nodes from the stack together. */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_thash_2( + stack + (offset - 2)*SPX_N, stack + (offset - 2)*SPX_N, + pub_seed, tree_addr); + offset--; + /* Note that the top-most node is now one layer higher. */ + heights[offset - 1]++; + + /* If this is a node we need for the auth path.. */ + if (((leaf_idx >> heights[offset - 1]) ^ 0x1) == tree_idx) { + memcpy(auth_path + heights[offset - 1]*SPX_N, + stack + (offset - 1)*SPX_N, SPX_N); + } + } + } + memcpy(root, stack, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_FORS_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_FORS_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_FORS_HEIGHT, gen_leaf, tree_addr); +} + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_TREE_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_TREE_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_TREE_HEIGHT, gen_leaf, tree_addr); +} diff --git a/crypto_sign/sphincs-sha256-192f-robust/clean/utils.h b/crypto_sign/sphincs-sha256-192f-robust/clean/utils.h new file mode 100644 index 00000000..8bbc26c2 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192f-robust/clean/utils.h @@ -0,0 +1,60 @@ +#ifndef SPX_UTILS_H +#define SPX_UTILS_H + +#include "params.h" +#include +#include + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in); + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen); + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-sha256-192f-robust/clean/wots.c b/crypto_sign/sphincs-sha256-192f-robust/clean/wots.c new file mode 100644 index 00000000..04cb2ec4 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192f-robust/clean/wots.c @@ -0,0 +1,161 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +// TODO clarify address expectations, and make them more uniform. +// TODO i.e. do we expect types to be set already? +// TODO and do we expect modifications or copies? + +/** + * Computes the starting value for a chain, i.e. the secret key. + * Expects the address to be complete up to the chain address. + */ +static void wots_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t wots_addr[8]) { + /* Make sure that the hash address is actually zeroed. */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_hash_addr(wots_addr, 0); + + /* Generate sk element. */ + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_prf_addr(sk, sk_seed, wots_addr); +} + +/** + * Computes the chaining function. + * out and in have to be n-byte arrays. + * + * Interprets in as start-th value of the chain. + * addr has to contain the address of the chain. + */ +static void gen_chain(unsigned char *out, const unsigned char *in, + unsigned int start, unsigned int steps, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + /* Initialize out with the value at position 'start'. */ + memcpy(out, in, SPX_N); + + /* Iterate 'steps' calls to the hash function. */ + for (i = start; i < (start + steps) && i < SPX_WOTS_W; i++) { + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_hash_addr(addr, i); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_thash_1( + out, out, pub_seed, addr); + } +} + +/** + * base_w algorithm as described in draft. + * Interprets an array of bytes as integers in base w. + * This only works when log_w is a divisor of 8. + */ +static void base_w(unsigned int *output, const size_t out_len, + const unsigned char *input) { + size_t in = 0; + size_t out = 0; + unsigned char total = 0; + unsigned int bits = 0; + size_t consumed; + + for (consumed = 0; consumed < out_len; consumed++) { + if (bits == 0) { + total = input[in]; + in++; + bits += 8; + } + bits -= SPX_WOTS_LOGW; + output[out] = (unsigned int)((total >> bits) & (SPX_WOTS_W - 1)); + out++; + } +} + +/* Computes the WOTS+ checksum over a message (in base_w). */ +static void wots_checksum(unsigned int *csum_base_w, + const unsigned int *msg_base_w) { + unsigned int csum = 0; + unsigned char csum_bytes[(SPX_WOTS_LEN2 * SPX_WOTS_LOGW + 7) / 8]; + unsigned int i; + + /* Compute checksum. */ + for (i = 0; i < SPX_WOTS_LEN1; i++) { + csum += SPX_WOTS_W - 1 - msg_base_w[i]; + } + + /* Convert checksum to base_w. */ + /* Make sure expected empty zero bits are the least significant bits. */ + csum = csum << (8 - ((SPX_WOTS_LEN2 * SPX_WOTS_LOGW) % 8)); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_ull_to_bytes( + csum_bytes, sizeof(csum_bytes), csum); + base_w(csum_base_w, SPX_WOTS_LEN2, csum_bytes); +} + +/* Takes a message and derives the matching chain lengths. */ +static void chain_lengths(unsigned int *lengths, const unsigned char *msg) { + base_w(lengths, SPX_WOTS_LEN1, msg); + wots_checksum(lengths + SPX_WOTS_LEN1, lengths); +} + +/** + * WOTS key generation. Takes a 32 byte sk_seed, expands it to WOTS private key + * elements and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(pk + i * SPX_N, sk_seed, addr); + gen_chain(pk + i * SPX_N, pk + i * SPX_N, + 0, SPX_WOTS_W - 1, pub_seed, addr); + } +} + +/** + * Takes a n-byte message and the 32-byte sk_see to compute a signature 'sig'. + */ +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(sig + i * SPX_N, sk_seed, addr); + gen_chain(sig + i * SPX_N, sig + i * SPX_N, 0, lengths[i], pub_seed, addr); + } +} + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_set_chain_addr(addr, i); + gen_chain(pk + i * SPX_N, sig + i * SPX_N, + lengths[i], SPX_WOTS_W - 1 - lengths[i], pub_seed, addr); + } +} diff --git a/crypto_sign/sphincs-sha256-192f-robust/clean/wots.h b/crypto_sign/sphincs-sha256-192f-robust/clean/wots.h new file mode 100644 index 00000000..93710b4b --- /dev/null +++ b/crypto_sign/sphincs-sha256-192f-robust/clean/wots.h @@ -0,0 +1,38 @@ +#ifndef SPX_WOTS_H +#define SPX_WOTS_H + +#include "params.h" +#include + +/** + * WOTS key generation. Takes a 32 byte seed for the private key, expands it to + * a full WOTS private key and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * Takes a n-byte message and the 32-byte seed for the private key to compute a + * signature that is placed at 'sig'. + */ +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]); + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-sha256-192f-simple/META.yml b/crypto_sign/sphincs-sha256-192f-simple/META.yml new file mode 100644 index 00000000..22134e40 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192f-simple/META.yml @@ -0,0 +1,27 @@ +name: SPHINCS+ +type: signature +claimed-nist-level: 3 +length-public-key: 48 +length-secret-key: 96 +length-signature: 35664 +testvectors-sha256: b25e0f2560f500d8988809522c72ea3ab0f81be52476a6cdf9d05a890a2d2ce0 +principal-submitter: Andreas Hülsing +auxiliary-submitters: + - Jean-Philippe Aumasson + - Daniel J. Bernstein, + - Christoph Dobraunig + - Maria Eichlseder + - Scott Fluhrer + - Stefan-Lukas Gazdag + - Panos Kampanakis + - Stefan Kölbl + - Tanja Lange + - Martin M. Lauridsen + - Florian Mendel + - Ruben Niederhagen + - Christian Rechberger + - Joost Rijneveld + - Peter Schwabe +implementations: + - name: clean + version: https://github.com/sphincs/sphincsplus/commit/77755c94d0bc744478044d6efbb888dc13156441 diff --git a/crypto_sign/sphincs-sha256-192f-simple/clean/LICENSE b/crypto_sign/sphincs-sha256-192f-simple/clean/LICENSE new file mode 100644 index 00000000..670154e3 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192f-simple/clean/LICENSE @@ -0,0 +1,116 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + diff --git a/crypto_sign/sphincs-sha256-192f-simple/clean/Makefile b/crypto_sign/sphincs-sha256-192f-simple/clean/Makefile new file mode 100644 index 00000000..f847f77a --- /dev/null +++ b/crypto_sign/sphincs-sha256-192f-simple/clean/Makefile @@ -0,0 +1,20 @@ +# This Makefile can be used with GNU Make or BSD Make + +LIB=libsphincs-sha256-192f-simple_clean.a + +HEADERS = params.h address.h wots.h utils.h fors.h api.h hash.h thash.h sha256.h +OBJECTS = address.o wots.o utils.o fors.o sign.o hash_sha256.o thash_sha256_simple.o sha256.o + +CFLAGS=-O3 -Wall -Wconversion -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) + +all: $(LIB) + +%.o: %.c $(HEADERS) + $(CC) $(CFLAGS) -c -o $@ $< + +$(LIB): $(OBJECTS) + $(AR) -r $@ $(OBJECTS) + +clean: + $(RM) $(OBJECTS) + $(RM) $(LIB) diff --git a/crypto_sign/sphincs-sha256-192f-simple/clean/Makefile.Microsoft_nmake b/crypto_sign/sphincs-sha256-192f-simple/clean/Makefile.Microsoft_nmake new file mode 100644 index 00000000..ec7d8429 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192f-simple/clean/Makefile.Microsoft_nmake @@ -0,0 +1,19 @@ +# This Makefile can be used with Microsoft Visual Studio's nmake using the command: +# nmake /f Makefile.Microsoft_nmake + +LIBRARY=libsphincs-sha256-192f-simple_clean.lib +OBJECTS=address.obj wots.obj utils.obj fors.obj sign.obj hash_sha256.obj thash_sha256_simple.obj sha256.obj + +CFLAGS=/nologo /I ..\..\..\common /W4 /WX + +all: $(LIBRARY) + +# Make sure objects are recompiled if headers change. +$(OBJECTS): *.h + +$(LIBRARY): $(OBJECTS) + LIB.EXE /NOLOGO /WX /OUT:$@ $** + +clean: + -DEL $(OBJECTS) + -DEL $(LIBRARY) diff --git a/crypto_sign/sphincs-sha256-192f-simple/clean/address.c b/crypto_sign/sphincs-sha256-192f-simple/clean/address.c new file mode 100644 index 00000000..d268f11f --- /dev/null +++ b/crypto_sign/sphincs-sha256-192f-simple/clean/address.c @@ -0,0 +1,78 @@ +#include + +#include "address.h" +#include "params.h" +#include "utils.h" + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]) { + int i; + + for (i = 0; i < 8; i++) { + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_ull_to_bytes( + bytes + i * 4, 4, addr[i]); + } +} + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer) { + addr[0] = layer; +} + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree) { + addr[1] = 0; + addr[2] = (uint32_t) (tree >> 32); + addr[3] = (uint32_t) tree; +} + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_type( + uint32_t addr[8], uint32_t type) { + addr[4] = type; +} + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; +} + +/* These functions are used for OTS addresses. */ + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair) { + addr[5] = keypair; +} + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; + out[5] = in[5]; +} + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain) { + addr[6] = chain; +} + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash) { + addr[7] = hash; +} + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height) { + addr[6] = tree_height; +} + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index) { + addr[7] = tree_index; +} diff --git a/crypto_sign/sphincs-sha256-192f-simple/clean/address.h b/crypto_sign/sphincs-sha256-192f-simple/clean/address.h new file mode 100644 index 00000000..b42f2672 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192f-simple/clean/address.h @@ -0,0 +1,50 @@ +#ifndef SPX_ADDRESS_H +#define SPX_ADDRESS_H + +#include + +#define SPX_ADDR_TYPE_WOTS 0 +#define SPX_ADDR_TYPE_WOTSPK 1 +#define SPX_ADDR_TYPE_HASHTREE 2 +#define SPX_ADDR_TYPE_FORSTREE 3 +#define SPX_ADDR_TYPE_FORSPK 4 + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer); + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree); + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_type( + uint32_t addr[8], uint32_t type); + +/* Copies the layer and tree part of one address into the other */ +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for WOTS and FORS addresses. */ + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair); + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain); + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash); + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height); + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index); + +#endif diff --git a/crypto_sign/sphincs-sha256-192f-simple/clean/api.h b/crypto_sign/sphincs-sha256-192f-simple/clean/api.h new file mode 100644 index 00000000..ffe81106 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192f-simple/clean/api.h @@ -0,0 +1,78 @@ +#ifndef PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_API_H +#define PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_API_H + +#include +#include + +#define PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_CRYPTO_ALGNAME "SPHINCS+" + +#define PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_CRYPTO_SECRETKEYBYTES 96 +#define PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES 48 +#define PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_CRYPTO_BYTES 35664 +#define PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_CRYPTO_SEEDBYTES 72 + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_crypto_sign_secretkeybytes(void); + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_crypto_sign_publickeybytes(void); + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_crypto_sign_bytes(void); + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_crypto_sign_seedbytes(void); + +/* + * Generates a SPHINCS+ key pair given a seed. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed); + +/* + * Generates a SPHINCS+ key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk); + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk); + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk); + +#endif diff --git a/crypto_sign/sphincs-sha256-192f-simple/clean/fors.c b/crypto_sign/sphincs-sha256-192f-simple/clean/fors.c new file mode 100644 index 00000000..c2dac6c3 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192f-simple/clean/fors.c @@ -0,0 +1,164 @@ +#include +#include +#include + +#include "address.h" +#include "fors.h" +#include "hash.h" +#include "thash.h" +#include "utils.h" + +static void fors_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_prf_addr( + sk, sk_seed, fors_leaf_addr); +} + +static void fors_sk_to_leaf(unsigned char *leaf, const unsigned char *sk, + const unsigned char *pub_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_thash_1( + leaf, sk, pub_seed, fors_leaf_addr); +} + +static void fors_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t fors_tree_addr[8]) { + uint32_t fors_leaf_addr[8] = {0}; + + /* Only copy the parts that must be kept in fors_leaf_addr. */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_copy_keypair_addr( + fors_leaf_addr, fors_tree_addr); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_type( + fors_leaf_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_tree_index( + fors_leaf_addr, addr_idx); + + fors_gen_sk(leaf, sk_seed, fors_leaf_addr); + fors_sk_to_leaf(leaf, leaf, pub_seed, fors_leaf_addr); +} + +/** + * Interprets m as SPX_FORS_HEIGHT-bit unsigned integers. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + * Assumes indices has space for SPX_FORS_TREES integers. + */ +static void message_to_indices(uint32_t *indices, const unsigned char *m) { + unsigned int i, j; + unsigned int offset = 0; + + for (i = 0; i < SPX_FORS_TREES; i++) { + indices[i] = 0; + for (j = 0; j < SPX_FORS_HEIGHT; j++) { + indices[i] ^= (((uint32_t)m[offset >> 3] >> (offset & 0x7)) & 0x1) << j; + offset++; + } + } +} + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Include the secret key part that produces the selected leaf node. */ + fors_gen_sk(sig, sk_seed, fors_tree_addr); + sig += SPX_N; + + /* Compute the authentication path for this leaf node. */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_treehash_FORS_HEIGHT( + roots + i * SPX_N, sig, sk_seed, pub_seed, + indices[i], idx_offset, fors_gen_leaf, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + unsigned char leaf[SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Derive the leaf from the included secret key part. */ + fors_sk_to_leaf(leaf, sig, pub_seed, fors_tree_addr); + sig += SPX_N; + + /* Derive the corresponding root node of this tree. */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_compute_root( + roots + i * SPX_N, leaf, indices[i], idx_offset, sig, + SPX_FORS_HEIGHT, pub_seed, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} diff --git a/crypto_sign/sphincs-sha256-192f-simple/clean/fors.h b/crypto_sign/sphincs-sha256-192f-simple/clean/fors.h new file mode 100644 index 00000000..f8f254b9 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192f-simple/clean/fors.h @@ -0,0 +1,30 @@ +#ifndef SPX_FORS_H +#define SPX_FORS_H + +#include + +#include "params.h" + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]); + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-sha256-192f-simple/clean/hash.h b/crypto_sign/sphincs-sha256-192f-simple/clean/hash.h new file mode 100644 index 00000000..e1b62d39 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192f-simple/clean/hash.h @@ -0,0 +1,22 @@ +#ifndef SPX_HASH_H +#define SPX_HASH_H + +#include + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed); + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen); + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen); + +#endif diff --git a/crypto_sign/sphincs-sha256-192f-simple/clean/hash_sha256.c b/crypto_sign/sphincs-sha256-192f-simple/clean/hash_sha256.c new file mode 100644 index 00000000..6944d0ff --- /dev/null +++ b/crypto_sign/sphincs-sha256-192f-simple/clean/hash_sha256.c @@ -0,0 +1,148 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "utils.h" + +#include "sha2.h" +#include "sha256.h" + +/* For SHA256, there is no immediate reason to initialize at the start, + so this function is an empty operation. */ +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed) { + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_seed_state(pub_seed); + (void)sk_seed; /* Suppress an 'unused parameter' warning. */ +} + +/* + * Computes PRF(key, addr), given a secret key of SPX_N bytes and an address + */ +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]) { + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES]; + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + + memcpy(buf, key, SPX_N); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_compress_address(buf + SPX_N, addr); + + sha256(outbuf, buf, SPX_N + SPX_SHA256_ADDR_BYTES); + memcpy(out, outbuf, SPX_N); +} + +/** + * Computes the message-dependent randomness R, using a secret seed as a key + * for HMAC, and an optional randomization value prefixed to the message. + * This requires m to have at least SPX_SHA256_BLOCK_BYTES + SPX_N space + * available in front of the pointer, i.e. before the message to use for the + * prefix. This is necessary to prevent having to move the message around (and + * allocate memory for it). + */ +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen) { + unsigned char buf[SPX_SHA256_BLOCK_BYTES + SPX_SHA256_OUTPUT_BYTES]; + uint8_t state[40]; + int i; + + /* This implements HMAC-SHA256 */ + for (i = 0; i < SPX_N; i++) { + buf[i] = 0x36 ^ sk_prf[i]; + } + memset(buf + SPX_N, 0x36, SPX_SHA256_BLOCK_BYTES - SPX_N); + + sha256_inc_init(state); + sha256_inc_blocks(state, buf, 1); + + memcpy(buf, optrand, SPX_N); + + /* If optrand + message cannot fill up an entire block */ + if (SPX_N + mlen < SPX_SHA256_BLOCK_BYTES) { + memcpy(buf + SPX_N, m, mlen); + sha256_inc_finalize(buf + SPX_SHA256_BLOCK_BYTES, state, + buf, mlen + SPX_N); + } + /* Otherwise first fill a block, so that finalize only uses the message */ + else { + memcpy(buf + SPX_N, m, SPX_SHA256_BLOCK_BYTES - SPX_N); + sha256_inc_blocks(state, buf, 1); + + m += SPX_SHA256_BLOCK_BYTES - SPX_N; + mlen -= SPX_SHA256_BLOCK_BYTES - SPX_N; + sha256_inc_finalize(buf + SPX_SHA256_BLOCK_BYTES, state, m, mlen); + } + + for (i = 0; i < SPX_N; i++) { + buf[i] = 0x5c ^ sk_prf[i]; + } + memset(buf + SPX_N, 0x5c, SPX_SHA256_BLOCK_BYTES - SPX_N); + + sha256(buf, buf, SPX_SHA256_BLOCK_BYTES + SPX_SHA256_OUTPUT_BYTES); + memcpy(R, buf, SPX_N); +} + +/** + * Computes the message hash using R, the public key, and the message. + * Outputs the message digest and the index of the leaf. The index is split in + * the tree index and the leaf index, for convenient copying to an address. + */ +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen) { +#define SPX_TREE_BITS (SPX_TREE_HEIGHT * (SPX_D - 1)) +#define SPX_TREE_BYTES ((SPX_TREE_BITS + 7) / 8) +#define SPX_LEAF_BITS SPX_TREE_HEIGHT +#define SPX_LEAF_BYTES ((SPX_LEAF_BITS + 7) / 8) +#define SPX_DGST_BYTES (SPX_FORS_MSG_BYTES + SPX_TREE_BYTES + SPX_LEAF_BYTES) + + unsigned char seed[SPX_SHA256_OUTPUT_BYTES + 4]; + + /* Round to nearest multiple of SPX_SHA256_BLOCK_BYTES */ +#define SPX_INBLOCKS (((SPX_N + SPX_PK_BYTES + SPX_SHA256_BLOCK_BYTES - 1) & \ + -SPX_SHA256_BLOCK_BYTES) / SPX_SHA256_BLOCK_BYTES) + unsigned char inbuf[SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES]; + + unsigned char buf[SPX_DGST_BYTES]; + unsigned char *bufp = buf; + uint8_t state[40]; + + sha256_inc_init(state); + + memcpy(inbuf, R, SPX_N); + memcpy(inbuf + SPX_N, pk, SPX_PK_BYTES); + + /* If R + pk + message cannot fill up an entire block */ + if (SPX_N + SPX_PK_BYTES + mlen < SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES) { + memcpy(inbuf + SPX_N + SPX_PK_BYTES, m, mlen); + sha256_inc_finalize(seed, state, inbuf, SPX_N + SPX_PK_BYTES + mlen); + } + /* Otherwise first fill a block, so that finalize only uses the message */ + else { + memcpy(inbuf + SPX_N + SPX_PK_BYTES, m, + SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES); + sha256_inc_blocks(state, inbuf, SPX_INBLOCKS); + + m += SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES; + mlen -= SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES; + sha256_inc_finalize(seed, state, m, mlen); + } + + /* By doing this in two steps, we prevent hashing the message twice; + otherwise each iteration in MGF1 would hash the message again. */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_mgf1(bufp, SPX_DGST_BYTES, seed, SPX_SHA256_OUTPUT_BYTES); + + memcpy(digest, bufp, SPX_FORS_MSG_BYTES); + bufp += SPX_FORS_MSG_BYTES; + + *tree = PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_bytes_to_ull(bufp, SPX_TREE_BYTES); + *tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS); + bufp += SPX_TREE_BYTES; + + *leaf_idx = (uint32_t)PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_bytes_to_ull( + bufp, SPX_LEAF_BYTES); + *leaf_idx &= (~(uint32_t)0) >> (32 - SPX_LEAF_BITS); +} diff --git a/crypto_sign/sphincs-sha256-192f-simple/clean/params.h b/crypto_sign/sphincs-sha256-192f-simple/clean/params.h new file mode 100644 index 00000000..898f177f --- /dev/null +++ b/crypto_sign/sphincs-sha256-192f-simple/clean/params.h @@ -0,0 +1,53 @@ +#ifndef SPX_PARAMS_H +#define SPX_PARAMS_H + +/* Hash output length in bytes. */ +#define SPX_N 24 +/* Height of the hypertree. */ +#define SPX_FULL_HEIGHT 66 +/* Number of subtree layer. */ +#define SPX_D 22 +/* FORS tree dimensions. */ +#define SPX_FORS_HEIGHT 8 +#define SPX_FORS_TREES 33 +/* Winternitz parameter, */ +#define SPX_WOTS_W 16 + +/* The hash function is defined by linking a different hash.c file, as opposed + to setting a #define constant. */ + +/* For clarity */ +#define SPX_ADDR_BYTES 32 + +/* WOTS parameters. */ +#define SPX_WOTS_LOGW 4 + +#define SPX_WOTS_LEN1 (8 * SPX_N / SPX_WOTS_LOGW) + +/* SPX_WOTS_LEN2 is floor(log(len_1 * (w - 1)) / log(w)) + 1; we precompute */ +#define SPX_WOTS_LEN2 3 + +#define SPX_WOTS_LEN (SPX_WOTS_LEN1 + SPX_WOTS_LEN2) +#define SPX_WOTS_BYTES (SPX_WOTS_LEN * SPX_N) +#define SPX_WOTS_PK_BYTES SPX_WOTS_BYTES + +/* Subtree size. */ +#define SPX_TREE_HEIGHT (SPX_FULL_HEIGHT / SPX_D) + +/* FORS parameters. */ +#define SPX_FORS_MSG_BYTES ((SPX_FORS_HEIGHT * SPX_FORS_TREES + 7) / 8) +#define SPX_FORS_BYTES ((SPX_FORS_HEIGHT + 1) * SPX_FORS_TREES * SPX_N) +#define SPX_FORS_PK_BYTES SPX_N + +/* Resulting SPX sizes. */ +#define SPX_BYTES (SPX_N + SPX_FORS_BYTES + SPX_D * SPX_WOTS_BYTES +\ + SPX_FULL_HEIGHT * SPX_N) +#define SPX_PK_BYTES (2 * SPX_N) +#define SPX_SK_BYTES (2 * SPX_N + SPX_PK_BYTES) + +/* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ +#define SPX_OPTRAND_BYTES 32 + +#endif diff --git a/crypto_sign/sphincs-sha256-192f-simple/clean/sha256.c b/crypto_sign/sphincs-sha256-192f-simple/clean/sha256.c new file mode 100644 index 00000000..425fd631 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192f-simple/clean/sha256.c @@ -0,0 +1,72 @@ +/* Based on the public domain implementation in + * crypto_hash/sha512/ref/ from http://bench.cr.yp.to/supercop.html + * by D. J. Bernstein */ + +#include +#include +#include + +#include "sha2.h" +#include "sha256.h" +#include "utils.h" + +/* + * Compresses an address to a 22-byte sequence. + * This reduces the number of required SHA256 compression calls, as the last + * block of input is padded with at least 65 bits. + */ +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_compress_address(unsigned char *out, const uint32_t addr[8]) { + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_ull_to_bytes(out, 1, addr[0]); /* drop 3 bytes of the layer field */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_ull_to_bytes(out + 1, 4, addr[2]); /* drop the highest tree address word */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_ull_to_bytes(out + 5, 4, addr[3]); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_ull_to_bytes(out + 9, 1, addr[4]); /* drop 3 bytes of the type field */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_ull_to_bytes(out + 10, 4, addr[5]); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_ull_to_bytes(out + 14, 4, addr[6]); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_ull_to_bytes(out + 18, 4, addr[7]); +} + +/** + * Requires 'input_plus_four_bytes' to have 'inlen' + 4 bytes, so that the last + * four bytes can be used for the counter. Typically 'input' is merely a seed. + * Outputs outlen number of bytes + */ +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + unsigned char *input_plus_four_bytes, unsigned long inlen) { + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + unsigned long i; + + /* While we can fit in at least another full block of SHA256 output.. */ + for (i = 0; (i + 1)*SPX_SHA256_OUTPUT_BYTES <= outlen; i++) { + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_ull_to_bytes(input_plus_four_bytes + inlen, 4, i); + sha256(out, input_plus_four_bytes, inlen + 4); + out += SPX_SHA256_OUTPUT_BYTES; + } + /* Until we cannot anymore, and we fill the remainder. */ + if (outlen > i * SPX_SHA256_OUTPUT_BYTES) { + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_ull_to_bytes(input_plus_four_bytes + inlen, 4, i); + sha256(outbuf, input_plus_four_bytes, inlen + 4); + memcpy(out, outbuf, outlen - i * SPX_SHA256_OUTPUT_BYTES); + } +} + +uint8_t PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_state_seeded[40]; + +/** + * Absorb the constant pub_seed using one round of the compression function + * This initializes state_seeded, which can then be reused in thash + **/ +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_seed_state(const unsigned char *pub_seed) { + uint8_t block[SPX_SHA256_BLOCK_BYTES]; + size_t i; + + for (i = 0; i < SPX_N; ++i) { + block[i] = pub_seed[i]; + } + for (i = SPX_N; i < SPX_SHA256_BLOCK_BYTES; ++i) { + block[i] = 0; + } + + sha256_inc_init(PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_state_seeded); + sha256_inc_blocks(PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_state_seeded, block, 1); +} diff --git a/crypto_sign/sphincs-sha256-192f-simple/clean/sha256.h b/crypto_sign/sphincs-sha256-192f-simple/clean/sha256.h new file mode 100644 index 00000000..4d46f657 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192f-simple/clean/sha256.h @@ -0,0 +1,21 @@ +#ifndef SPX_SHA256_H +#define SPX_SHA256_H + +#define SPX_SHA256_BLOCK_BYTES 64 +#define SPX_SHA256_OUTPUT_BYTES 32 /* This does not necessarily equal SPX_N */ +#define SPX_SHA256_ADDR_BYTES 22 + +#include +#include + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_compress_address(unsigned char *out, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + unsigned char *input_plus_four_bytes, unsigned long inlen); + +uint8_t PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_state_seeded[40]; + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_seed_state(const unsigned char *pub_seed); + +#endif diff --git a/crypto_sign/sphincs-sha256-192f-simple/clean/sign.c b/crypto_sign/sphincs-sha256-192f-simple/clean/sign.c new file mode 100644 index 00000000..ee981f80 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192f-simple/clean/sign.c @@ -0,0 +1,344 @@ +#include +#include +#include + +#include "address.h" +#include "api.h" +#include "fors.h" +#include "hash.h" +#include "params.h" +#include "randombytes.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +/** + * Computes the leaf at a given address. First generates the WOTS key pair, + * then computes leaf by hashing horizontally. + */ +static void wots_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t tree_addr[8]) { + unsigned char pk[SPX_WOTS_BYTES]; + uint32_t wots_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_keypair_addr( + wots_addr, addr_idx); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_wots_gen_pk( + pk, sk_seed, pub_seed, wots_addr); + + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_thash_WOTS_LEN( + leaf, pk, pub_seed, wots_pk_addr); +} + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_crypto_sign_secretkeybytes(void) { + return PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_CRYPTO_SECRETKEYBYTES; +} + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_crypto_sign_publickeybytes(void) { + return PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES; +} + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_crypto_sign_bytes(void) { + return PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_CRYPTO_BYTES; +} + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_crypto_sign_seedbytes(void) { + return PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_CRYPTO_SEEDBYTES; +} + +/* + * Generates an SPX key pair given a seed of length + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed) { + /* We do not need the auth path in key generation, but it simplifies the + code to have just one treehash routine that computes both root and path + in one function. */ + unsigned char auth_path[SPX_TREE_HEIGHT * SPX_N]; + uint32_t top_tree_addr[8] = {0}; + + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_layer_addr( + top_tree_addr, SPX_D - 1); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_type( + top_tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */ + memcpy(sk, seed, PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_CRYPTO_SEEDBYTES); + + memcpy(pk, sk + 2 * SPX_N, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_initialize_hash_function(pk, sk); + + /* Compute root node of the top-most subtree. */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_treehash_TREE_HEIGHT( + sk + 3 * SPX_N, auth_path, sk, sk + 2 * SPX_N, 0, 0, + wots_gen_leaf, top_tree_addr); + + memcpy(pk + SPX_N, sk + 3 * SPX_N, SPX_N); + + return 0; +} + +/* + * Generates an SPX key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk) { + unsigned char seed[PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_CRYPTO_SEEDBYTES]; + randombytes(seed, PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_CRYPTO_SEEDBYTES); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_crypto_sign_seed_keypair( + pk, sk, seed); + + return 0; +} + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + const unsigned char *sk_seed = sk; + const unsigned char *sk_prf = sk + SPX_N; + const unsigned char *pk = sk + 2 * SPX_N; + const unsigned char *pub_seed = pk; + + unsigned char optrand[SPX_N]; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char root[SPX_N]; + uint32_t i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_initialize_hash_function( + pub_seed, sk_seed); + + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ + randombytes(optrand, SPX_N); + /* Compute the digest randomization value. */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_gen_message_random( + sig, sk_prf, optrand, m, mlen); + + /* Derive the message digest and leaf index from R, PK and M. */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Sign the message hash using FORS. */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_fors_sign( + sig, root, mhash, sk_seed, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Compute a WOTS signature. */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_wots_sign( + sig, root, sk_seed, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the authentication path for the used WOTS leaf. */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_treehash_TREE_HEIGHT( + root, sig, sk_seed, pub_seed, idx_leaf, 0, + wots_gen_leaf, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + *siglen = SPX_BYTES; + + return 0; +} + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk) { + const unsigned char *pub_seed = pk; + const unsigned char *pub_root = pk + SPX_N; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char wots_pk[SPX_WOTS_BYTES]; + unsigned char root[SPX_N]; + unsigned char leaf[SPX_N]; + unsigned int i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + if (siglen != SPX_BYTES) { + return -1; + } + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_initialize_hash_function( + pub_seed, NULL); + + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + /* Derive the message digest and leaf index from R || PK || M. */ + /* The additional SPX_N is a result of the hash domain separator. */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + /* Layer correctly defaults to 0, so no need to set_layer_addr */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_fors_pk_from_sig( + root, sig, mhash, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + /* For each subtree.. */ + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + + /* The WOTS public key is only correct if the signature was correct. */ + /* Initially, root is the FORS pk, but on subsequent iterations it is + the root of the subtree below the currently processed subtree. */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_wots_pk_from_sig( + wots_pk, sig, root, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the leaf node using the WOTS public key. */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_thash_WOTS_LEN( + leaf, wots_pk, pub_seed, wots_pk_addr); + + /* Compute the root node of this subtree. */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_compute_root( + root, leaf, idx_leaf, 0, sig, SPX_TREE_HEIGHT, + pub_seed, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + /* Check if the root node equals the root node in the public key. */ + if (memcmp(root, pub_root, SPX_N) != 0) { + return -1; + } + + return 0; +} + + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + size_t siglen; + + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_crypto_sign_signature( + sm, &siglen, m, mlen, sk); + + memmove(sm + SPX_BYTES, m, mlen); + *smlen = siglen + mlen; + + return 0; +} + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk) { + /* The API caller does not necessarily know what size a signature should be + but SPHINCS+ signatures are always exactly SPX_BYTES. */ + if (smlen < SPX_BYTES) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + *mlen = smlen - SPX_BYTES; + + if (PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_crypto_sign_verify( + sm, SPX_BYTES, sm + SPX_BYTES, *mlen, pk)) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + /* If verification was successful, move the message to the right place. */ + memmove(m, sm + SPX_BYTES, *mlen); + + return 0; +} diff --git a/crypto_sign/sphincs-sha256-192f-simple/clean/thash.h b/crypto_sign/sphincs-sha256-192f-simple/clean/thash.h new file mode 100644 index 00000000..fccdc472 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192f-simple/clean/thash.h @@ -0,0 +1,22 @@ +#ifndef SPX_THASH_H +#define SPX_THASH_H + +#include + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-sha256-192f-simple/clean/thash_sha256_simple.c b/crypto_sign/sphincs-sha256-192f-simple/clean/thash_sha256_simple.c new file mode 100644 index 00000000..0f843507 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192f-simple/clean/thash_sha256_simple.c @@ -0,0 +1,70 @@ +#include +#include + +#include "address.h" +#include "params.h" +#include "thash.h" + +#include "sha2.h" +#include "sha256.h" + +/** + * Takes an array of inblocks concatenated arrays of SPX_N bytes. + */ +static void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_thash( + unsigned char *out, unsigned char *buf, + const unsigned char *in, unsigned int inblocks, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + uint8_t sha2_state[40]; + + (void)pub_seed; /* Suppress an 'unused parameter' warning. */ + + /* Retrieve precomputed state containing pub_seed */ + memcpy(sha2_state, PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_state_seeded, 40 * sizeof(uint8_t)); + + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_compress_address(buf, addr); + memcpy(buf + SPX_SHA256_ADDR_BYTES, in, inblocks * SPX_N); + + sha256_inc_finalize(outbuf, sha2_state, buf, SPX_SHA256_ADDR_BYTES + inblocks * SPX_N); + memcpy(out, outbuf, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_SHA256_ADDR_BYTES + 1 * SPX_N]; + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_thash( + out, buf, in, 1, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_SHA256_ADDR_BYTES + 2 * SPX_N]; + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_thash( + out, buf, in, 2, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_SHA256_ADDR_BYTES + SPX_WOTS_LEN * SPX_N]; + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_thash( + out, buf, in, SPX_WOTS_LEN, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_SHA256_ADDR_BYTES + SPX_FORS_TREES * SPX_N]; + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_thash( + out, buf, in, SPX_FORS_TREES, pub_seed, addr); +} diff --git a/crypto_sign/sphincs-sha256-192f-simple/clean/utils.c b/crypto_sign/sphincs-sha256-192f-simple/clean/utils.c new file mode 100644 index 00000000..bc441df7 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192f-simple/clean/utils.c @@ -0,0 +1,192 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in) { + + /* Iterate over out in decreasing order, for big-endianness. */ + for (size_t i = outlen; i > 0; i--) { + out[i - 1] = in & 0xff; + in = in >> 8; + } +} + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen) { + unsigned long long retval = 0; + + for (size_t i = 0; i < inlen; i++) { + retval |= ((unsigned long long)in[i]) << (8 * (inlen - 1 - i)); + } + return retval; +} + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + unsigned char buffer[2 * SPX_N]; + + /* If leaf_idx is odd (last bit = 1), current path element is a right child + and auth_path has to go left. Otherwise it is the other way around. */ + if (leaf_idx & 1) { + memcpy(buffer + SPX_N, leaf, SPX_N); + memcpy(buffer, auth_path, SPX_N); + } else { + memcpy(buffer, leaf, SPX_N); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + + for (i = 0; i < tree_height - 1; i++) { + leaf_idx >>= 1; + idx_offset >>= 1; + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_tree_height(addr, i + 1); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + + /* Pick the right or left neighbor, depending on parity of the node. */ + if (leaf_idx & 1) { + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_thash_2( + buffer + SPX_N, buffer, pub_seed, addr); + memcpy(buffer, auth_path, SPX_N); + } else { + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_thash_2( + buffer, buffer, pub_seed, addr); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + } + + /* The last iteration is exceptional; we do not copy an auth_path node. */ + leaf_idx >>= 1; + idx_offset >>= 1; + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_tree_height(addr, tree_height); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_thash_2( + root, buffer, pub_seed, addr); +} + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +static void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_treehash( + unsigned char *root, unsigned char *auth_path, + unsigned char *stack, unsigned int *heights, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned int offset = 0; + uint32_t idx; + uint32_t tree_idx; + + for (idx = 0; idx < (uint32_t)(1 << tree_height); idx++) { + /* Add the next leaf node to the stack. */ + gen_leaf(stack + offset * SPX_N, + sk_seed, pub_seed, idx + idx_offset, tree_addr); + offset++; + heights[offset - 1] = 0; + + /* If this is a node we need for the auth path.. */ + if ((leaf_idx ^ 0x1) == idx) { + memcpy(auth_path, stack + (offset - 1)*SPX_N, SPX_N); + } + + /* While the top-most nodes are of equal height.. */ + while (offset >= 2 && heights[offset - 1] == heights[offset - 2]) { + /* Compute index of the new node, in the next layer. */ + tree_idx = (idx >> (heights[offset - 1] + 1)); + + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_tree_height( + tree_addr, heights[offset - 1] + 1); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_tree_index( + tree_addr, tree_idx + (idx_offset >> (heights[offset - 1] + 1))); + /* Hash the top-most nodes from the stack together. */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_thash_2( + stack + (offset - 2)*SPX_N, stack + (offset - 2)*SPX_N, + pub_seed, tree_addr); + offset--; + /* Note that the top-most node is now one layer higher. */ + heights[offset - 1]++; + + /* If this is a node we need for the auth path.. */ + if (((leaf_idx >> heights[offset - 1]) ^ 0x1) == tree_idx) { + memcpy(auth_path + heights[offset - 1]*SPX_N, + stack + (offset - 1)*SPX_N, SPX_N); + } + } + } + memcpy(root, stack, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_FORS_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_FORS_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_FORS_HEIGHT, gen_leaf, tree_addr); +} + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_TREE_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_TREE_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_TREE_HEIGHT, gen_leaf, tree_addr); +} diff --git a/crypto_sign/sphincs-sha256-192f-simple/clean/utils.h b/crypto_sign/sphincs-sha256-192f-simple/clean/utils.h new file mode 100644 index 00000000..50450414 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192f-simple/clean/utils.h @@ -0,0 +1,60 @@ +#ifndef SPX_UTILS_H +#define SPX_UTILS_H + +#include "params.h" +#include +#include + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in); + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen); + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-sha256-192f-simple/clean/wots.c b/crypto_sign/sphincs-sha256-192f-simple/clean/wots.c new file mode 100644 index 00000000..a090a0fb --- /dev/null +++ b/crypto_sign/sphincs-sha256-192f-simple/clean/wots.c @@ -0,0 +1,161 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +// TODO clarify address expectations, and make them more uniform. +// TODO i.e. do we expect types to be set already? +// TODO and do we expect modifications or copies? + +/** + * Computes the starting value for a chain, i.e. the secret key. + * Expects the address to be complete up to the chain address. + */ +static void wots_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t wots_addr[8]) { + /* Make sure that the hash address is actually zeroed. */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_hash_addr(wots_addr, 0); + + /* Generate sk element. */ + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_prf_addr(sk, sk_seed, wots_addr); +} + +/** + * Computes the chaining function. + * out and in have to be n-byte arrays. + * + * Interprets in as start-th value of the chain. + * addr has to contain the address of the chain. + */ +static void gen_chain(unsigned char *out, const unsigned char *in, + unsigned int start, unsigned int steps, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + /* Initialize out with the value at position 'start'. */ + memcpy(out, in, SPX_N); + + /* Iterate 'steps' calls to the hash function. */ + for (i = start; i < (start + steps) && i < SPX_WOTS_W; i++) { + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_hash_addr(addr, i); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_thash_1( + out, out, pub_seed, addr); + } +} + +/** + * base_w algorithm as described in draft. + * Interprets an array of bytes as integers in base w. + * This only works when log_w is a divisor of 8. + */ +static void base_w(unsigned int *output, const size_t out_len, + const unsigned char *input) { + size_t in = 0; + size_t out = 0; + unsigned char total = 0; + unsigned int bits = 0; + size_t consumed; + + for (consumed = 0; consumed < out_len; consumed++) { + if (bits == 0) { + total = input[in]; + in++; + bits += 8; + } + bits -= SPX_WOTS_LOGW; + output[out] = (unsigned int)((total >> bits) & (SPX_WOTS_W - 1)); + out++; + } +} + +/* Computes the WOTS+ checksum over a message (in base_w). */ +static void wots_checksum(unsigned int *csum_base_w, + const unsigned int *msg_base_w) { + unsigned int csum = 0; + unsigned char csum_bytes[(SPX_WOTS_LEN2 * SPX_WOTS_LOGW + 7) / 8]; + unsigned int i; + + /* Compute checksum. */ + for (i = 0; i < SPX_WOTS_LEN1; i++) { + csum += SPX_WOTS_W - 1 - msg_base_w[i]; + } + + /* Convert checksum to base_w. */ + /* Make sure expected empty zero bits are the least significant bits. */ + csum = csum << (8 - ((SPX_WOTS_LEN2 * SPX_WOTS_LOGW) % 8)); + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_ull_to_bytes( + csum_bytes, sizeof(csum_bytes), csum); + base_w(csum_base_w, SPX_WOTS_LEN2, csum_bytes); +} + +/* Takes a message and derives the matching chain lengths. */ +static void chain_lengths(unsigned int *lengths, const unsigned char *msg) { + base_w(lengths, SPX_WOTS_LEN1, msg); + wots_checksum(lengths + SPX_WOTS_LEN1, lengths); +} + +/** + * WOTS key generation. Takes a 32 byte sk_seed, expands it to WOTS private key + * elements and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(pk + i * SPX_N, sk_seed, addr); + gen_chain(pk + i * SPX_N, pk + i * SPX_N, + 0, SPX_WOTS_W - 1, pub_seed, addr); + } +} + +/** + * Takes a n-byte message and the 32-byte sk_see to compute a signature 'sig'. + */ +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(sig + i * SPX_N, sk_seed, addr); + gen_chain(sig + i * SPX_N, sig + i * SPX_N, 0, lengths[i], pub_seed, addr); + } +} + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_set_chain_addr(addr, i); + gen_chain(pk + i * SPX_N, sig + i * SPX_N, + lengths[i], SPX_WOTS_W - 1 - lengths[i], pub_seed, addr); + } +} diff --git a/crypto_sign/sphincs-sha256-192f-simple/clean/wots.h b/crypto_sign/sphincs-sha256-192f-simple/clean/wots.h new file mode 100644 index 00000000..c9f821fb --- /dev/null +++ b/crypto_sign/sphincs-sha256-192f-simple/clean/wots.h @@ -0,0 +1,38 @@ +#ifndef SPX_WOTS_H +#define SPX_WOTS_H + +#include "params.h" +#include + +/** + * WOTS key generation. Takes a 32 byte seed for the private key, expands it to + * a full WOTS private key and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * Takes a n-byte message and the 32-byte seed for the private key to compute a + * signature that is placed at 'sig'. + */ +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]); + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-sha256-192s-robust/META.yml b/crypto_sign/sphincs-sha256-192s-robust/META.yml new file mode 100644 index 00000000..72efa7d9 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192s-robust/META.yml @@ -0,0 +1,27 @@ +name: SPHINCS+ +type: signature +claimed-nist-level: 3 +length-public-key: 48 +length-secret-key: 96 +length-signature: 17064 +testvectors-sha256: 1be5c30de6d0b856b1b51f0ff50a2acf9c3a359ee2178004e153bdfc50a68832 +principal-submitter: Andreas Hülsing +auxiliary-submitters: + - Jean-Philippe Aumasson + - Daniel J. Bernstein, + - Christoph Dobraunig + - Maria Eichlseder + - Scott Fluhrer + - Stefan-Lukas Gazdag + - Panos Kampanakis + - Stefan Kölbl + - Tanja Lange + - Martin M. Lauridsen + - Florian Mendel + - Ruben Niederhagen + - Christian Rechberger + - Joost Rijneveld + - Peter Schwabe +implementations: + - name: clean + version: https://github.com/sphincs/sphincsplus/commit/77755c94d0bc744478044d6efbb888dc13156441 diff --git a/crypto_sign/sphincs-sha256-192s-robust/clean/LICENSE b/crypto_sign/sphincs-sha256-192s-robust/clean/LICENSE new file mode 100644 index 00000000..670154e3 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192s-robust/clean/LICENSE @@ -0,0 +1,116 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + diff --git a/crypto_sign/sphincs-sha256-192s-robust/clean/Makefile b/crypto_sign/sphincs-sha256-192s-robust/clean/Makefile new file mode 100644 index 00000000..2f134259 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192s-robust/clean/Makefile @@ -0,0 +1,20 @@ +# This Makefile can be used with GNU Make or BSD Make + +LIB=libsphincs-sha256-192s-robust_clean.a + +HEADERS = params.h address.h wots.h utils.h fors.h api.h hash.h thash.h sha256.h +OBJECTS = address.o wots.o utils.o fors.o sign.o hash_sha256.o thash_sha256_robust.o sha256.o + +CFLAGS=-O3 -Wall -Wconversion -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) + +all: $(LIB) + +%.o: %.c $(HEADERS) + $(CC) $(CFLAGS) -c -o $@ $< + +$(LIB): $(OBJECTS) + $(AR) -r $@ $(OBJECTS) + +clean: + $(RM) $(OBJECTS) + $(RM) $(LIB) diff --git a/crypto_sign/sphincs-sha256-192s-robust/clean/Makefile.Microsoft_nmake b/crypto_sign/sphincs-sha256-192s-robust/clean/Makefile.Microsoft_nmake new file mode 100644 index 00000000..41beee45 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192s-robust/clean/Makefile.Microsoft_nmake @@ -0,0 +1,19 @@ +# This Makefile can be used with Microsoft Visual Studio's nmake using the command: +# nmake /f Makefile.Microsoft_nmake + +LIBRARY=libsphincs-sha256-192s-robust_clean.lib +OBJECTS=address.obj wots.obj utils.obj fors.obj sign.obj hash_sha256.obj thash_sha256_robust.obj sha256.obj + +CFLAGS=/nologo /I ..\..\..\common /W4 /WX + +all: $(LIBRARY) + +# Make sure objects are recompiled if headers change. +$(OBJECTS): *.h + +$(LIBRARY): $(OBJECTS) + LIB.EXE /NOLOGO /WX /OUT:$@ $** + +clean: + -DEL $(OBJECTS) + -DEL $(LIBRARY) diff --git a/crypto_sign/sphincs-sha256-192s-robust/clean/address.c b/crypto_sign/sphincs-sha256-192s-robust/clean/address.c new file mode 100644 index 00000000..0da3ca4b --- /dev/null +++ b/crypto_sign/sphincs-sha256-192s-robust/clean/address.c @@ -0,0 +1,78 @@ +#include + +#include "address.h" +#include "params.h" +#include "utils.h" + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]) { + int i; + + for (i = 0; i < 8; i++) { + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_ull_to_bytes( + bytes + i * 4, 4, addr[i]); + } +} + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer) { + addr[0] = layer; +} + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree) { + addr[1] = 0; + addr[2] = (uint32_t) (tree >> 32); + addr[3] = (uint32_t) tree; +} + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_type( + uint32_t addr[8], uint32_t type) { + addr[4] = type; +} + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; +} + +/* These functions are used for OTS addresses. */ + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair) { + addr[5] = keypair; +} + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; + out[5] = in[5]; +} + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain) { + addr[6] = chain; +} + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash) { + addr[7] = hash; +} + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height) { + addr[6] = tree_height; +} + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index) { + addr[7] = tree_index; +} diff --git a/crypto_sign/sphincs-sha256-192s-robust/clean/address.h b/crypto_sign/sphincs-sha256-192s-robust/clean/address.h new file mode 100644 index 00000000..3ca030f1 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192s-robust/clean/address.h @@ -0,0 +1,50 @@ +#ifndef SPX_ADDRESS_H +#define SPX_ADDRESS_H + +#include + +#define SPX_ADDR_TYPE_WOTS 0 +#define SPX_ADDR_TYPE_WOTSPK 1 +#define SPX_ADDR_TYPE_HASHTREE 2 +#define SPX_ADDR_TYPE_FORSTREE 3 +#define SPX_ADDR_TYPE_FORSPK 4 + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer); + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree); + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_type( + uint32_t addr[8], uint32_t type); + +/* Copies the layer and tree part of one address into the other */ +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for WOTS and FORS addresses. */ + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair); + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain); + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash); + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height); + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index); + +#endif diff --git a/crypto_sign/sphincs-sha256-192s-robust/clean/api.h b/crypto_sign/sphincs-sha256-192s-robust/clean/api.h new file mode 100644 index 00000000..92fd25e6 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192s-robust/clean/api.h @@ -0,0 +1,78 @@ +#ifndef PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_API_H +#define PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_API_H + +#include +#include + +#define PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_CRYPTO_ALGNAME "SPHINCS+" + +#define PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_CRYPTO_SECRETKEYBYTES 96 +#define PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES 48 +#define PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_CRYPTO_BYTES 17064 +#define PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_CRYPTO_SEEDBYTES 72 + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_secretkeybytes(void); + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_publickeybytes(void); + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_bytes(void); + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_seedbytes(void); + +/* + * Generates a SPHINCS+ key pair given a seed. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed); + +/* + * Generates a SPHINCS+ key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk); + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk); + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk); + +#endif diff --git a/crypto_sign/sphincs-sha256-192s-robust/clean/fors.c b/crypto_sign/sphincs-sha256-192s-robust/clean/fors.c new file mode 100644 index 00000000..cb994c84 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192s-robust/clean/fors.c @@ -0,0 +1,164 @@ +#include +#include +#include + +#include "address.h" +#include "fors.h" +#include "hash.h" +#include "thash.h" +#include "utils.h" + +static void fors_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_prf_addr( + sk, sk_seed, fors_leaf_addr); +} + +static void fors_sk_to_leaf(unsigned char *leaf, const unsigned char *sk, + const unsigned char *pub_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_thash_1( + leaf, sk, pub_seed, fors_leaf_addr); +} + +static void fors_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t fors_tree_addr[8]) { + uint32_t fors_leaf_addr[8] = {0}; + + /* Only copy the parts that must be kept in fors_leaf_addr. */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_copy_keypair_addr( + fors_leaf_addr, fors_tree_addr); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_type( + fors_leaf_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_tree_index( + fors_leaf_addr, addr_idx); + + fors_gen_sk(leaf, sk_seed, fors_leaf_addr); + fors_sk_to_leaf(leaf, leaf, pub_seed, fors_leaf_addr); +} + +/** + * Interprets m as SPX_FORS_HEIGHT-bit unsigned integers. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + * Assumes indices has space for SPX_FORS_TREES integers. + */ +static void message_to_indices(uint32_t *indices, const unsigned char *m) { + unsigned int i, j; + unsigned int offset = 0; + + for (i = 0; i < SPX_FORS_TREES; i++) { + indices[i] = 0; + for (j = 0; j < SPX_FORS_HEIGHT; j++) { + indices[i] ^= (((uint32_t)m[offset >> 3] >> (offset & 0x7)) & 0x1) << j; + offset++; + } + } +} + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Include the secret key part that produces the selected leaf node. */ + fors_gen_sk(sig, sk_seed, fors_tree_addr); + sig += SPX_N; + + /* Compute the authentication path for this leaf node. */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_treehash_FORS_HEIGHT( + roots + i * SPX_N, sig, sk_seed, pub_seed, + indices[i], idx_offset, fors_gen_leaf, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + unsigned char leaf[SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Derive the leaf from the included secret key part. */ + fors_sk_to_leaf(leaf, sig, pub_seed, fors_tree_addr); + sig += SPX_N; + + /* Derive the corresponding root node of this tree. */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_compute_root( + roots + i * SPX_N, leaf, indices[i], idx_offset, sig, + SPX_FORS_HEIGHT, pub_seed, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} diff --git a/crypto_sign/sphincs-sha256-192s-robust/clean/fors.h b/crypto_sign/sphincs-sha256-192s-robust/clean/fors.h new file mode 100644 index 00000000..ae69574f --- /dev/null +++ b/crypto_sign/sphincs-sha256-192s-robust/clean/fors.h @@ -0,0 +1,30 @@ +#ifndef SPX_FORS_H +#define SPX_FORS_H + +#include + +#include "params.h" + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]); + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-sha256-192s-robust/clean/hash.h b/crypto_sign/sphincs-sha256-192s-robust/clean/hash.h new file mode 100644 index 00000000..b276d86c --- /dev/null +++ b/crypto_sign/sphincs-sha256-192s-robust/clean/hash.h @@ -0,0 +1,22 @@ +#ifndef SPX_HASH_H +#define SPX_HASH_H + +#include + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed); + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen); + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen); + +#endif diff --git a/crypto_sign/sphincs-sha256-192s-robust/clean/hash_sha256.c b/crypto_sign/sphincs-sha256-192s-robust/clean/hash_sha256.c new file mode 100644 index 00000000..99850139 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192s-robust/clean/hash_sha256.c @@ -0,0 +1,148 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "utils.h" + +#include "sha2.h" +#include "sha256.h" + +/* For SHA256, there is no immediate reason to initialize at the start, + so this function is an empty operation. */ +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed) { + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_seed_state(pub_seed); + (void)sk_seed; /* Suppress an 'unused parameter' warning. */ +} + +/* + * Computes PRF(key, addr), given a secret key of SPX_N bytes and an address + */ +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]) { + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES]; + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + + memcpy(buf, key, SPX_N); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_compress_address(buf + SPX_N, addr); + + sha256(outbuf, buf, SPX_N + SPX_SHA256_ADDR_BYTES); + memcpy(out, outbuf, SPX_N); +} + +/** + * Computes the message-dependent randomness R, using a secret seed as a key + * for HMAC, and an optional randomization value prefixed to the message. + * This requires m to have at least SPX_SHA256_BLOCK_BYTES + SPX_N space + * available in front of the pointer, i.e. before the message to use for the + * prefix. This is necessary to prevent having to move the message around (and + * allocate memory for it). + */ +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen) { + unsigned char buf[SPX_SHA256_BLOCK_BYTES + SPX_SHA256_OUTPUT_BYTES]; + uint8_t state[40]; + int i; + + /* This implements HMAC-SHA256 */ + for (i = 0; i < SPX_N; i++) { + buf[i] = 0x36 ^ sk_prf[i]; + } + memset(buf + SPX_N, 0x36, SPX_SHA256_BLOCK_BYTES - SPX_N); + + sha256_inc_init(state); + sha256_inc_blocks(state, buf, 1); + + memcpy(buf, optrand, SPX_N); + + /* If optrand + message cannot fill up an entire block */ + if (SPX_N + mlen < SPX_SHA256_BLOCK_BYTES) { + memcpy(buf + SPX_N, m, mlen); + sha256_inc_finalize(buf + SPX_SHA256_BLOCK_BYTES, state, + buf, mlen + SPX_N); + } + /* Otherwise first fill a block, so that finalize only uses the message */ + else { + memcpy(buf + SPX_N, m, SPX_SHA256_BLOCK_BYTES - SPX_N); + sha256_inc_blocks(state, buf, 1); + + m += SPX_SHA256_BLOCK_BYTES - SPX_N; + mlen -= SPX_SHA256_BLOCK_BYTES - SPX_N; + sha256_inc_finalize(buf + SPX_SHA256_BLOCK_BYTES, state, m, mlen); + } + + for (i = 0; i < SPX_N; i++) { + buf[i] = 0x5c ^ sk_prf[i]; + } + memset(buf + SPX_N, 0x5c, SPX_SHA256_BLOCK_BYTES - SPX_N); + + sha256(buf, buf, SPX_SHA256_BLOCK_BYTES + SPX_SHA256_OUTPUT_BYTES); + memcpy(R, buf, SPX_N); +} + +/** + * Computes the message hash using R, the public key, and the message. + * Outputs the message digest and the index of the leaf. The index is split in + * the tree index and the leaf index, for convenient copying to an address. + */ +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen) { +#define SPX_TREE_BITS (SPX_TREE_HEIGHT * (SPX_D - 1)) +#define SPX_TREE_BYTES ((SPX_TREE_BITS + 7) / 8) +#define SPX_LEAF_BITS SPX_TREE_HEIGHT +#define SPX_LEAF_BYTES ((SPX_LEAF_BITS + 7) / 8) +#define SPX_DGST_BYTES (SPX_FORS_MSG_BYTES + SPX_TREE_BYTES + SPX_LEAF_BYTES) + + unsigned char seed[SPX_SHA256_OUTPUT_BYTES + 4]; + + /* Round to nearest multiple of SPX_SHA256_BLOCK_BYTES */ +#define SPX_INBLOCKS (((SPX_N + SPX_PK_BYTES + SPX_SHA256_BLOCK_BYTES - 1) & \ + -SPX_SHA256_BLOCK_BYTES) / SPX_SHA256_BLOCK_BYTES) + unsigned char inbuf[SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES]; + + unsigned char buf[SPX_DGST_BYTES]; + unsigned char *bufp = buf; + uint8_t state[40]; + + sha256_inc_init(state); + + memcpy(inbuf, R, SPX_N); + memcpy(inbuf + SPX_N, pk, SPX_PK_BYTES); + + /* If R + pk + message cannot fill up an entire block */ + if (SPX_N + SPX_PK_BYTES + mlen < SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES) { + memcpy(inbuf + SPX_N + SPX_PK_BYTES, m, mlen); + sha256_inc_finalize(seed, state, inbuf, SPX_N + SPX_PK_BYTES + mlen); + } + /* Otherwise first fill a block, so that finalize only uses the message */ + else { + memcpy(inbuf + SPX_N + SPX_PK_BYTES, m, + SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES); + sha256_inc_blocks(state, inbuf, SPX_INBLOCKS); + + m += SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES; + mlen -= SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES; + sha256_inc_finalize(seed, state, m, mlen); + } + + /* By doing this in two steps, we prevent hashing the message twice; + otherwise each iteration in MGF1 would hash the message again. */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_mgf1(bufp, SPX_DGST_BYTES, seed, SPX_SHA256_OUTPUT_BYTES); + + memcpy(digest, bufp, SPX_FORS_MSG_BYTES); + bufp += SPX_FORS_MSG_BYTES; + + *tree = PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_bytes_to_ull(bufp, SPX_TREE_BYTES); + *tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS); + bufp += SPX_TREE_BYTES; + + *leaf_idx = (uint32_t)PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_bytes_to_ull( + bufp, SPX_LEAF_BYTES); + *leaf_idx &= (~(uint32_t)0) >> (32 - SPX_LEAF_BITS); +} diff --git a/crypto_sign/sphincs-sha256-192s-robust/clean/params.h b/crypto_sign/sphincs-sha256-192s-robust/clean/params.h new file mode 100644 index 00000000..483b5374 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192s-robust/clean/params.h @@ -0,0 +1,53 @@ +#ifndef SPX_PARAMS_H +#define SPX_PARAMS_H + +/* Hash output length in bytes. */ +#define SPX_N 24 +/* Height of the hypertree. */ +#define SPX_FULL_HEIGHT 64 +/* Number of subtree layer. */ +#define SPX_D 8 +/* FORS tree dimensions. */ +#define SPX_FORS_HEIGHT 16 +#define SPX_FORS_TREES 14 +/* Winternitz parameter, */ +#define SPX_WOTS_W 16 + +/* The hash function is defined by linking a different hash.c file, as opposed + to setting a #define constant. */ + +/* For clarity */ +#define SPX_ADDR_BYTES 32 + +/* WOTS parameters. */ +#define SPX_WOTS_LOGW 4 + +#define SPX_WOTS_LEN1 (8 * SPX_N / SPX_WOTS_LOGW) + +/* SPX_WOTS_LEN2 is floor(log(len_1 * (w - 1)) / log(w)) + 1; we precompute */ +#define SPX_WOTS_LEN2 3 + +#define SPX_WOTS_LEN (SPX_WOTS_LEN1 + SPX_WOTS_LEN2) +#define SPX_WOTS_BYTES (SPX_WOTS_LEN * SPX_N) +#define SPX_WOTS_PK_BYTES SPX_WOTS_BYTES + +/* Subtree size. */ +#define SPX_TREE_HEIGHT (SPX_FULL_HEIGHT / SPX_D) + +/* FORS parameters. */ +#define SPX_FORS_MSG_BYTES ((SPX_FORS_HEIGHT * SPX_FORS_TREES + 7) / 8) +#define SPX_FORS_BYTES ((SPX_FORS_HEIGHT + 1) * SPX_FORS_TREES * SPX_N) +#define SPX_FORS_PK_BYTES SPX_N + +/* Resulting SPX sizes. */ +#define SPX_BYTES (SPX_N + SPX_FORS_BYTES + SPX_D * SPX_WOTS_BYTES +\ + SPX_FULL_HEIGHT * SPX_N) +#define SPX_PK_BYTES (2 * SPX_N) +#define SPX_SK_BYTES (2 * SPX_N + SPX_PK_BYTES) + +/* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ +#define SPX_OPTRAND_BYTES 32 + +#endif diff --git a/crypto_sign/sphincs-sha256-192s-robust/clean/sha256.c b/crypto_sign/sphincs-sha256-192s-robust/clean/sha256.c new file mode 100644 index 00000000..2d94d069 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192s-robust/clean/sha256.c @@ -0,0 +1,72 @@ +/* Based on the public domain implementation in + * crypto_hash/sha512/ref/ from http://bench.cr.yp.to/supercop.html + * by D. J. Bernstein */ + +#include +#include +#include + +#include "sha2.h" +#include "sha256.h" +#include "utils.h" + +/* + * Compresses an address to a 22-byte sequence. + * This reduces the number of required SHA256 compression calls, as the last + * block of input is padded with at least 65 bits. + */ +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_compress_address(unsigned char *out, const uint32_t addr[8]) { + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_ull_to_bytes(out, 1, addr[0]); /* drop 3 bytes of the layer field */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_ull_to_bytes(out + 1, 4, addr[2]); /* drop the highest tree address word */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_ull_to_bytes(out + 5, 4, addr[3]); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_ull_to_bytes(out + 9, 1, addr[4]); /* drop 3 bytes of the type field */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_ull_to_bytes(out + 10, 4, addr[5]); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_ull_to_bytes(out + 14, 4, addr[6]); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_ull_to_bytes(out + 18, 4, addr[7]); +} + +/** + * Requires 'input_plus_four_bytes' to have 'inlen' + 4 bytes, so that the last + * four bytes can be used for the counter. Typically 'input' is merely a seed. + * Outputs outlen number of bytes + */ +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + unsigned char *input_plus_four_bytes, unsigned long inlen) { + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + unsigned long i; + + /* While we can fit in at least another full block of SHA256 output.. */ + for (i = 0; (i + 1)*SPX_SHA256_OUTPUT_BYTES <= outlen; i++) { + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_ull_to_bytes(input_plus_four_bytes + inlen, 4, i); + sha256(out, input_plus_four_bytes, inlen + 4); + out += SPX_SHA256_OUTPUT_BYTES; + } + /* Until we cannot anymore, and we fill the remainder. */ + if (outlen > i * SPX_SHA256_OUTPUT_BYTES) { + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_ull_to_bytes(input_plus_four_bytes + inlen, 4, i); + sha256(outbuf, input_plus_four_bytes, inlen + 4); + memcpy(out, outbuf, outlen - i * SPX_SHA256_OUTPUT_BYTES); + } +} + +uint8_t PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_state_seeded[40]; + +/** + * Absorb the constant pub_seed using one round of the compression function + * This initializes state_seeded, which can then be reused in thash + **/ +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_seed_state(const unsigned char *pub_seed) { + uint8_t block[SPX_SHA256_BLOCK_BYTES]; + size_t i; + + for (i = 0; i < SPX_N; ++i) { + block[i] = pub_seed[i]; + } + for (i = SPX_N; i < SPX_SHA256_BLOCK_BYTES; ++i) { + block[i] = 0; + } + + sha256_inc_init(PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_state_seeded); + sha256_inc_blocks(PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_state_seeded, block, 1); +} diff --git a/crypto_sign/sphincs-sha256-192s-robust/clean/sha256.h b/crypto_sign/sphincs-sha256-192s-robust/clean/sha256.h new file mode 100644 index 00000000..6ac0513f --- /dev/null +++ b/crypto_sign/sphincs-sha256-192s-robust/clean/sha256.h @@ -0,0 +1,21 @@ +#ifndef SPX_SHA256_H +#define SPX_SHA256_H + +#define SPX_SHA256_BLOCK_BYTES 64 +#define SPX_SHA256_OUTPUT_BYTES 32 /* This does not necessarily equal SPX_N */ +#define SPX_SHA256_ADDR_BYTES 22 + +#include +#include + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_compress_address(unsigned char *out, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + unsigned char *input_plus_four_bytes, unsigned long inlen); + +uint8_t PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_state_seeded[40]; + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_seed_state(const unsigned char *pub_seed); + +#endif diff --git a/crypto_sign/sphincs-sha256-192s-robust/clean/sign.c b/crypto_sign/sphincs-sha256-192s-robust/clean/sign.c new file mode 100644 index 00000000..880c521a --- /dev/null +++ b/crypto_sign/sphincs-sha256-192s-robust/clean/sign.c @@ -0,0 +1,344 @@ +#include +#include +#include + +#include "address.h" +#include "api.h" +#include "fors.h" +#include "hash.h" +#include "params.h" +#include "randombytes.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +/** + * Computes the leaf at a given address. First generates the WOTS key pair, + * then computes leaf by hashing horizontally. + */ +static void wots_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t tree_addr[8]) { + unsigned char pk[SPX_WOTS_BYTES]; + uint32_t wots_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_keypair_addr( + wots_addr, addr_idx); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_wots_gen_pk( + pk, sk_seed, pub_seed, wots_addr); + + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_thash_WOTS_LEN( + leaf, pk, pub_seed, wots_pk_addr); +} + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_secretkeybytes(void) { + return PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_CRYPTO_SECRETKEYBYTES; +} + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_publickeybytes(void) { + return PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES; +} + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_bytes(void) { + return PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_CRYPTO_BYTES; +} + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_seedbytes(void) { + return PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_CRYPTO_SEEDBYTES; +} + +/* + * Generates an SPX key pair given a seed of length + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed) { + /* We do not need the auth path in key generation, but it simplifies the + code to have just one treehash routine that computes both root and path + in one function. */ + unsigned char auth_path[SPX_TREE_HEIGHT * SPX_N]; + uint32_t top_tree_addr[8] = {0}; + + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_layer_addr( + top_tree_addr, SPX_D - 1); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_type( + top_tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */ + memcpy(sk, seed, PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_CRYPTO_SEEDBYTES); + + memcpy(pk, sk + 2 * SPX_N, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_initialize_hash_function(pk, sk); + + /* Compute root node of the top-most subtree. */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_treehash_TREE_HEIGHT( + sk + 3 * SPX_N, auth_path, sk, sk + 2 * SPX_N, 0, 0, + wots_gen_leaf, top_tree_addr); + + memcpy(pk + SPX_N, sk + 3 * SPX_N, SPX_N); + + return 0; +} + +/* + * Generates an SPX key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk) { + unsigned char seed[PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_CRYPTO_SEEDBYTES]; + randombytes(seed, PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_CRYPTO_SEEDBYTES); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_seed_keypair( + pk, sk, seed); + + return 0; +} + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + const unsigned char *sk_seed = sk; + const unsigned char *sk_prf = sk + SPX_N; + const unsigned char *pk = sk + 2 * SPX_N; + const unsigned char *pub_seed = pk; + + unsigned char optrand[SPX_N]; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char root[SPX_N]; + uint32_t i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_initialize_hash_function( + pub_seed, sk_seed); + + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ + randombytes(optrand, SPX_N); + /* Compute the digest randomization value. */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_gen_message_random( + sig, sk_prf, optrand, m, mlen); + + /* Derive the message digest and leaf index from R, PK and M. */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Sign the message hash using FORS. */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_fors_sign( + sig, root, mhash, sk_seed, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Compute a WOTS signature. */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_wots_sign( + sig, root, sk_seed, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the authentication path for the used WOTS leaf. */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_treehash_TREE_HEIGHT( + root, sig, sk_seed, pub_seed, idx_leaf, 0, + wots_gen_leaf, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + *siglen = SPX_BYTES; + + return 0; +} + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk) { + const unsigned char *pub_seed = pk; + const unsigned char *pub_root = pk + SPX_N; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char wots_pk[SPX_WOTS_BYTES]; + unsigned char root[SPX_N]; + unsigned char leaf[SPX_N]; + unsigned int i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + if (siglen != SPX_BYTES) { + return -1; + } + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_initialize_hash_function( + pub_seed, NULL); + + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + /* Derive the message digest and leaf index from R || PK || M. */ + /* The additional SPX_N is a result of the hash domain separator. */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + /* Layer correctly defaults to 0, so no need to set_layer_addr */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_fors_pk_from_sig( + root, sig, mhash, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + /* For each subtree.. */ + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + + /* The WOTS public key is only correct if the signature was correct. */ + /* Initially, root is the FORS pk, but on subsequent iterations it is + the root of the subtree below the currently processed subtree. */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_wots_pk_from_sig( + wots_pk, sig, root, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the leaf node using the WOTS public key. */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_thash_WOTS_LEN( + leaf, wots_pk, pub_seed, wots_pk_addr); + + /* Compute the root node of this subtree. */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_compute_root( + root, leaf, idx_leaf, 0, sig, SPX_TREE_HEIGHT, + pub_seed, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + /* Check if the root node equals the root node in the public key. */ + if (memcmp(root, pub_root, SPX_N) != 0) { + return -1; + } + + return 0; +} + + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + size_t siglen; + + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_signature( + sm, &siglen, m, mlen, sk); + + memmove(sm + SPX_BYTES, m, mlen); + *smlen = siglen + mlen; + + return 0; +} + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk) { + /* The API caller does not necessarily know what size a signature should be + but SPHINCS+ signatures are always exactly SPX_BYTES. */ + if (smlen < SPX_BYTES) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + *mlen = smlen - SPX_BYTES; + + if (PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_crypto_sign_verify( + sm, SPX_BYTES, sm + SPX_BYTES, *mlen, pk)) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + /* If verification was successful, move the message to the right place. */ + memmove(m, sm + SPX_BYTES, *mlen); + + return 0; +} diff --git a/crypto_sign/sphincs-sha256-192s-robust/clean/thash.h b/crypto_sign/sphincs-sha256-192s-robust/clean/thash.h new file mode 100644 index 00000000..e15617cd --- /dev/null +++ b/crypto_sign/sphincs-sha256-192s-robust/clean/thash.h @@ -0,0 +1,22 @@ +#ifndef SPX_THASH_H +#define SPX_THASH_H + +#include + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-sha256-192s-robust/clean/thash_sha256_robust.c b/crypto_sign/sphincs-sha256-192s-robust/clean/thash_sha256_robust.c new file mode 100644 index 00000000..c2c09379 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192s-robust/clean/thash_sha256_robust.c @@ -0,0 +1,77 @@ +#include +#include + +#include "address.h" +#include "params.h" +#include "thash.h" + +#include "sha2.h" +#include "sha256.h" + +/** + * Takes an array of inblocks concatenated arrays of SPX_N bytes. + */ +static void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_thash( + unsigned char *out, unsigned char *buf, + const unsigned char *in, unsigned int inblocks, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + unsigned char *bitmask = buf + SPX_N + SPX_SHA256_ADDR_BYTES + 4; + uint8_t sha2_state[40]; + unsigned int i; + + memcpy(buf, pub_seed, SPX_N); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_compress_address(buf + SPX_N, addr); + /* MGF1 requires us to have 4 extra bytes in 'buf' */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_mgf1(bitmask, inblocks * SPX_N, buf, SPX_N + SPX_SHA256_ADDR_BYTES); + + /* Retrieve precomputed state containing pub_seed */ + memcpy(sha2_state, PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_state_seeded, 40 * sizeof(uint8_t)); + + for (i = 0; i < inblocks * SPX_N; i++) { + buf[SPX_N + SPX_SHA256_ADDR_BYTES + i] = in[i] ^ bitmask[i]; + } + + sha256_inc_finalize(outbuf, sha2_state, buf + SPX_N, + SPX_SHA256_ADDR_BYTES + inblocks * SPX_N); + memcpy(out, outbuf, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES + 4 + 1 * SPX_N]; + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_thash( + out, buf, in, 1, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES + 4 + 2 * SPX_N]; + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_thash( + out, buf, in, 2, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES + 4 + SPX_WOTS_LEN * SPX_N]; + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_thash( + out, buf, in, SPX_WOTS_LEN, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES + 4 + SPX_FORS_TREES * SPX_N]; + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_thash( + out, buf, in, SPX_FORS_TREES, pub_seed, addr); +} diff --git a/crypto_sign/sphincs-sha256-192s-robust/clean/utils.c b/crypto_sign/sphincs-sha256-192s-robust/clean/utils.c new file mode 100644 index 00000000..3ad8080d --- /dev/null +++ b/crypto_sign/sphincs-sha256-192s-robust/clean/utils.c @@ -0,0 +1,192 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in) { + + /* Iterate over out in decreasing order, for big-endianness. */ + for (size_t i = outlen; i > 0; i--) { + out[i - 1] = in & 0xff; + in = in >> 8; + } +} + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen) { + unsigned long long retval = 0; + + for (size_t i = 0; i < inlen; i++) { + retval |= ((unsigned long long)in[i]) << (8 * (inlen - 1 - i)); + } + return retval; +} + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + unsigned char buffer[2 * SPX_N]; + + /* If leaf_idx is odd (last bit = 1), current path element is a right child + and auth_path has to go left. Otherwise it is the other way around. */ + if (leaf_idx & 1) { + memcpy(buffer + SPX_N, leaf, SPX_N); + memcpy(buffer, auth_path, SPX_N); + } else { + memcpy(buffer, leaf, SPX_N); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + + for (i = 0; i < tree_height - 1; i++) { + leaf_idx >>= 1; + idx_offset >>= 1; + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_tree_height(addr, i + 1); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + + /* Pick the right or left neighbor, depending on parity of the node. */ + if (leaf_idx & 1) { + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_thash_2( + buffer + SPX_N, buffer, pub_seed, addr); + memcpy(buffer, auth_path, SPX_N); + } else { + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_thash_2( + buffer, buffer, pub_seed, addr); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + } + + /* The last iteration is exceptional; we do not copy an auth_path node. */ + leaf_idx >>= 1; + idx_offset >>= 1; + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_tree_height(addr, tree_height); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_thash_2( + root, buffer, pub_seed, addr); +} + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +static void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_treehash( + unsigned char *root, unsigned char *auth_path, + unsigned char *stack, unsigned int *heights, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned int offset = 0; + uint32_t idx; + uint32_t tree_idx; + + for (idx = 0; idx < (uint32_t)(1 << tree_height); idx++) { + /* Add the next leaf node to the stack. */ + gen_leaf(stack + offset * SPX_N, + sk_seed, pub_seed, idx + idx_offset, tree_addr); + offset++; + heights[offset - 1] = 0; + + /* If this is a node we need for the auth path.. */ + if ((leaf_idx ^ 0x1) == idx) { + memcpy(auth_path, stack + (offset - 1)*SPX_N, SPX_N); + } + + /* While the top-most nodes are of equal height.. */ + while (offset >= 2 && heights[offset - 1] == heights[offset - 2]) { + /* Compute index of the new node, in the next layer. */ + tree_idx = (idx >> (heights[offset - 1] + 1)); + + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_tree_height( + tree_addr, heights[offset - 1] + 1); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_tree_index( + tree_addr, tree_idx + (idx_offset >> (heights[offset - 1] + 1))); + /* Hash the top-most nodes from the stack together. */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_thash_2( + stack + (offset - 2)*SPX_N, stack + (offset - 2)*SPX_N, + pub_seed, tree_addr); + offset--; + /* Note that the top-most node is now one layer higher. */ + heights[offset - 1]++; + + /* If this is a node we need for the auth path.. */ + if (((leaf_idx >> heights[offset - 1]) ^ 0x1) == tree_idx) { + memcpy(auth_path + heights[offset - 1]*SPX_N, + stack + (offset - 1)*SPX_N, SPX_N); + } + } + } + memcpy(root, stack, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_FORS_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_FORS_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_FORS_HEIGHT, gen_leaf, tree_addr); +} + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_TREE_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_TREE_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_TREE_HEIGHT, gen_leaf, tree_addr); +} diff --git a/crypto_sign/sphincs-sha256-192s-robust/clean/utils.h b/crypto_sign/sphincs-sha256-192s-robust/clean/utils.h new file mode 100644 index 00000000..a49dafc5 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192s-robust/clean/utils.h @@ -0,0 +1,60 @@ +#ifndef SPX_UTILS_H +#define SPX_UTILS_H + +#include "params.h" +#include +#include + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in); + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen); + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-sha256-192s-robust/clean/wots.c b/crypto_sign/sphincs-sha256-192s-robust/clean/wots.c new file mode 100644 index 00000000..fb50573f --- /dev/null +++ b/crypto_sign/sphincs-sha256-192s-robust/clean/wots.c @@ -0,0 +1,161 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +// TODO clarify address expectations, and make them more uniform. +// TODO i.e. do we expect types to be set already? +// TODO and do we expect modifications or copies? + +/** + * Computes the starting value for a chain, i.e. the secret key. + * Expects the address to be complete up to the chain address. + */ +static void wots_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t wots_addr[8]) { + /* Make sure that the hash address is actually zeroed. */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_hash_addr(wots_addr, 0); + + /* Generate sk element. */ + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_prf_addr(sk, sk_seed, wots_addr); +} + +/** + * Computes the chaining function. + * out and in have to be n-byte arrays. + * + * Interprets in as start-th value of the chain. + * addr has to contain the address of the chain. + */ +static void gen_chain(unsigned char *out, const unsigned char *in, + unsigned int start, unsigned int steps, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + /* Initialize out with the value at position 'start'. */ + memcpy(out, in, SPX_N); + + /* Iterate 'steps' calls to the hash function. */ + for (i = start; i < (start + steps) && i < SPX_WOTS_W; i++) { + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_hash_addr(addr, i); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_thash_1( + out, out, pub_seed, addr); + } +} + +/** + * base_w algorithm as described in draft. + * Interprets an array of bytes as integers in base w. + * This only works when log_w is a divisor of 8. + */ +static void base_w(unsigned int *output, const size_t out_len, + const unsigned char *input) { + size_t in = 0; + size_t out = 0; + unsigned char total = 0; + unsigned int bits = 0; + size_t consumed; + + for (consumed = 0; consumed < out_len; consumed++) { + if (bits == 0) { + total = input[in]; + in++; + bits += 8; + } + bits -= SPX_WOTS_LOGW; + output[out] = (unsigned int)((total >> bits) & (SPX_WOTS_W - 1)); + out++; + } +} + +/* Computes the WOTS+ checksum over a message (in base_w). */ +static void wots_checksum(unsigned int *csum_base_w, + const unsigned int *msg_base_w) { + unsigned int csum = 0; + unsigned char csum_bytes[(SPX_WOTS_LEN2 * SPX_WOTS_LOGW + 7) / 8]; + unsigned int i; + + /* Compute checksum. */ + for (i = 0; i < SPX_WOTS_LEN1; i++) { + csum += SPX_WOTS_W - 1 - msg_base_w[i]; + } + + /* Convert checksum to base_w. */ + /* Make sure expected empty zero bits are the least significant bits. */ + csum = csum << (8 - ((SPX_WOTS_LEN2 * SPX_WOTS_LOGW) % 8)); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_ull_to_bytes( + csum_bytes, sizeof(csum_bytes), csum); + base_w(csum_base_w, SPX_WOTS_LEN2, csum_bytes); +} + +/* Takes a message and derives the matching chain lengths. */ +static void chain_lengths(unsigned int *lengths, const unsigned char *msg) { + base_w(lengths, SPX_WOTS_LEN1, msg); + wots_checksum(lengths + SPX_WOTS_LEN1, lengths); +} + +/** + * WOTS key generation. Takes a 32 byte sk_seed, expands it to WOTS private key + * elements and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(pk + i * SPX_N, sk_seed, addr); + gen_chain(pk + i * SPX_N, pk + i * SPX_N, + 0, SPX_WOTS_W - 1, pub_seed, addr); + } +} + +/** + * Takes a n-byte message and the 32-byte sk_see to compute a signature 'sig'. + */ +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(sig + i * SPX_N, sk_seed, addr); + gen_chain(sig + i * SPX_N, sig + i * SPX_N, 0, lengths[i], pub_seed, addr); + } +} + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_set_chain_addr(addr, i); + gen_chain(pk + i * SPX_N, sig + i * SPX_N, + lengths[i], SPX_WOTS_W - 1 - lengths[i], pub_seed, addr); + } +} diff --git a/crypto_sign/sphincs-sha256-192s-robust/clean/wots.h b/crypto_sign/sphincs-sha256-192s-robust/clean/wots.h new file mode 100644 index 00000000..0808c5f5 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192s-robust/clean/wots.h @@ -0,0 +1,38 @@ +#ifndef SPX_WOTS_H +#define SPX_WOTS_H + +#include "params.h" +#include + +/** + * WOTS key generation. Takes a 32 byte seed for the private key, expands it to + * a full WOTS private key and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * Takes a n-byte message and the 32-byte seed for the private key to compute a + * signature that is placed at 'sig'. + */ +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]); + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-sha256-192s-simple/META.yml b/crypto_sign/sphincs-sha256-192s-simple/META.yml new file mode 100644 index 00000000..53a29b4f --- /dev/null +++ b/crypto_sign/sphincs-sha256-192s-simple/META.yml @@ -0,0 +1,27 @@ +name: SPHINCS+ +type: signature +claimed-nist-level: 3 +length-public-key: 48 +length-secret-key: 96 +length-signature: 17064 +testvectors-sha256: ee413e410a29274a9647b9440d6a554670e0f9587efaaddedf82e4923f68f80e +principal-submitter: Andreas Hülsing +auxiliary-submitters: + - Jean-Philippe Aumasson + - Daniel J. Bernstein, + - Christoph Dobraunig + - Maria Eichlseder + - Scott Fluhrer + - Stefan-Lukas Gazdag + - Panos Kampanakis + - Stefan Kölbl + - Tanja Lange + - Martin M. Lauridsen + - Florian Mendel + - Ruben Niederhagen + - Christian Rechberger + - Joost Rijneveld + - Peter Schwabe +implementations: + - name: clean + version: https://github.com/sphincs/sphincsplus/commit/77755c94d0bc744478044d6efbb888dc13156441 diff --git a/crypto_sign/sphincs-sha256-192s-simple/clean/LICENSE b/crypto_sign/sphincs-sha256-192s-simple/clean/LICENSE new file mode 100644 index 00000000..670154e3 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192s-simple/clean/LICENSE @@ -0,0 +1,116 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + diff --git a/crypto_sign/sphincs-sha256-192s-simple/clean/Makefile b/crypto_sign/sphincs-sha256-192s-simple/clean/Makefile new file mode 100644 index 00000000..087324db --- /dev/null +++ b/crypto_sign/sphincs-sha256-192s-simple/clean/Makefile @@ -0,0 +1,20 @@ +# This Makefile can be used with GNU Make or BSD Make + +LIB=libsphincs-sha256-192s-simple_clean.a + +HEADERS = params.h address.h wots.h utils.h fors.h api.h hash.h thash.h sha256.h +OBJECTS = address.o wots.o utils.o fors.o sign.o hash_sha256.o thash_sha256_simple.o sha256.o + +CFLAGS=-O3 -Wall -Wconversion -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) + +all: $(LIB) + +%.o: %.c $(HEADERS) + $(CC) $(CFLAGS) -c -o $@ $< + +$(LIB): $(OBJECTS) + $(AR) -r $@ $(OBJECTS) + +clean: + $(RM) $(OBJECTS) + $(RM) $(LIB) diff --git a/crypto_sign/sphincs-sha256-192s-simple/clean/Makefile.Microsoft_nmake b/crypto_sign/sphincs-sha256-192s-simple/clean/Makefile.Microsoft_nmake new file mode 100644 index 00000000..1a441ba3 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192s-simple/clean/Makefile.Microsoft_nmake @@ -0,0 +1,19 @@ +# This Makefile can be used with Microsoft Visual Studio's nmake using the command: +# nmake /f Makefile.Microsoft_nmake + +LIBRARY=libsphincs-sha256-192s-simple_clean.lib +OBJECTS=address.obj wots.obj utils.obj fors.obj sign.obj hash_sha256.obj thash_sha256_simple.obj sha256.obj + +CFLAGS=/nologo /I ..\..\..\common /W4 /WX + +all: $(LIBRARY) + +# Make sure objects are recompiled if headers change. +$(OBJECTS): *.h + +$(LIBRARY): $(OBJECTS) + LIB.EXE /NOLOGO /WX /OUT:$@ $** + +clean: + -DEL $(OBJECTS) + -DEL $(LIBRARY) diff --git a/crypto_sign/sphincs-sha256-192s-simple/clean/address.c b/crypto_sign/sphincs-sha256-192s-simple/clean/address.c new file mode 100644 index 00000000..6167103d --- /dev/null +++ b/crypto_sign/sphincs-sha256-192s-simple/clean/address.c @@ -0,0 +1,78 @@ +#include + +#include "address.h" +#include "params.h" +#include "utils.h" + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]) { + int i; + + for (i = 0; i < 8; i++) { + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_ull_to_bytes( + bytes + i * 4, 4, addr[i]); + } +} + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer) { + addr[0] = layer; +} + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree) { + addr[1] = 0; + addr[2] = (uint32_t) (tree >> 32); + addr[3] = (uint32_t) tree; +} + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_type( + uint32_t addr[8], uint32_t type) { + addr[4] = type; +} + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; +} + +/* These functions are used for OTS addresses. */ + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair) { + addr[5] = keypair; +} + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; + out[5] = in[5]; +} + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain) { + addr[6] = chain; +} + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash) { + addr[7] = hash; +} + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height) { + addr[6] = tree_height; +} + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index) { + addr[7] = tree_index; +} diff --git a/crypto_sign/sphincs-sha256-192s-simple/clean/address.h b/crypto_sign/sphincs-sha256-192s-simple/clean/address.h new file mode 100644 index 00000000..92441daf --- /dev/null +++ b/crypto_sign/sphincs-sha256-192s-simple/clean/address.h @@ -0,0 +1,50 @@ +#ifndef SPX_ADDRESS_H +#define SPX_ADDRESS_H + +#include + +#define SPX_ADDR_TYPE_WOTS 0 +#define SPX_ADDR_TYPE_WOTSPK 1 +#define SPX_ADDR_TYPE_HASHTREE 2 +#define SPX_ADDR_TYPE_FORSTREE 3 +#define SPX_ADDR_TYPE_FORSPK 4 + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer); + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree); + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_type( + uint32_t addr[8], uint32_t type); + +/* Copies the layer and tree part of one address into the other */ +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for WOTS and FORS addresses. */ + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair); + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain); + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash); + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height); + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index); + +#endif diff --git a/crypto_sign/sphincs-sha256-192s-simple/clean/api.h b/crypto_sign/sphincs-sha256-192s-simple/clean/api.h new file mode 100644 index 00000000..34b7b06f --- /dev/null +++ b/crypto_sign/sphincs-sha256-192s-simple/clean/api.h @@ -0,0 +1,78 @@ +#ifndef PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_API_H +#define PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_API_H + +#include +#include + +#define PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_CRYPTO_ALGNAME "SPHINCS+" + +#define PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_CRYPTO_SECRETKEYBYTES 96 +#define PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES 48 +#define PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_CRYPTO_BYTES 17064 +#define PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_CRYPTO_SEEDBYTES 72 + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_crypto_sign_secretkeybytes(void); + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_crypto_sign_publickeybytes(void); + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_crypto_sign_bytes(void); + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_crypto_sign_seedbytes(void); + +/* + * Generates a SPHINCS+ key pair given a seed. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed); + +/* + * Generates a SPHINCS+ key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk); + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk); + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk); + +#endif diff --git a/crypto_sign/sphincs-sha256-192s-simple/clean/fors.c b/crypto_sign/sphincs-sha256-192s-simple/clean/fors.c new file mode 100644 index 00000000..84dacf46 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192s-simple/clean/fors.c @@ -0,0 +1,164 @@ +#include +#include +#include + +#include "address.h" +#include "fors.h" +#include "hash.h" +#include "thash.h" +#include "utils.h" + +static void fors_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_prf_addr( + sk, sk_seed, fors_leaf_addr); +} + +static void fors_sk_to_leaf(unsigned char *leaf, const unsigned char *sk, + const unsigned char *pub_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_thash_1( + leaf, sk, pub_seed, fors_leaf_addr); +} + +static void fors_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t fors_tree_addr[8]) { + uint32_t fors_leaf_addr[8] = {0}; + + /* Only copy the parts that must be kept in fors_leaf_addr. */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_copy_keypair_addr( + fors_leaf_addr, fors_tree_addr); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_type( + fors_leaf_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_tree_index( + fors_leaf_addr, addr_idx); + + fors_gen_sk(leaf, sk_seed, fors_leaf_addr); + fors_sk_to_leaf(leaf, leaf, pub_seed, fors_leaf_addr); +} + +/** + * Interprets m as SPX_FORS_HEIGHT-bit unsigned integers. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + * Assumes indices has space for SPX_FORS_TREES integers. + */ +static void message_to_indices(uint32_t *indices, const unsigned char *m) { + unsigned int i, j; + unsigned int offset = 0; + + for (i = 0; i < SPX_FORS_TREES; i++) { + indices[i] = 0; + for (j = 0; j < SPX_FORS_HEIGHT; j++) { + indices[i] ^= (((uint32_t)m[offset >> 3] >> (offset & 0x7)) & 0x1) << j; + offset++; + } + } +} + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Include the secret key part that produces the selected leaf node. */ + fors_gen_sk(sig, sk_seed, fors_tree_addr); + sig += SPX_N; + + /* Compute the authentication path for this leaf node. */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_treehash_FORS_HEIGHT( + roots + i * SPX_N, sig, sk_seed, pub_seed, + indices[i], idx_offset, fors_gen_leaf, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + unsigned char leaf[SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Derive the leaf from the included secret key part. */ + fors_sk_to_leaf(leaf, sig, pub_seed, fors_tree_addr); + sig += SPX_N; + + /* Derive the corresponding root node of this tree. */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_compute_root( + roots + i * SPX_N, leaf, indices[i], idx_offset, sig, + SPX_FORS_HEIGHT, pub_seed, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} diff --git a/crypto_sign/sphincs-sha256-192s-simple/clean/fors.h b/crypto_sign/sphincs-sha256-192s-simple/clean/fors.h new file mode 100644 index 00000000..71672dae --- /dev/null +++ b/crypto_sign/sphincs-sha256-192s-simple/clean/fors.h @@ -0,0 +1,30 @@ +#ifndef SPX_FORS_H +#define SPX_FORS_H + +#include + +#include "params.h" + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]); + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-sha256-192s-simple/clean/hash.h b/crypto_sign/sphincs-sha256-192s-simple/clean/hash.h new file mode 100644 index 00000000..51023671 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192s-simple/clean/hash.h @@ -0,0 +1,22 @@ +#ifndef SPX_HASH_H +#define SPX_HASH_H + +#include + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed); + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen); + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen); + +#endif diff --git a/crypto_sign/sphincs-sha256-192s-simple/clean/hash_sha256.c b/crypto_sign/sphincs-sha256-192s-simple/clean/hash_sha256.c new file mode 100644 index 00000000..230ad7de --- /dev/null +++ b/crypto_sign/sphincs-sha256-192s-simple/clean/hash_sha256.c @@ -0,0 +1,148 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "utils.h" + +#include "sha2.h" +#include "sha256.h" + +/* For SHA256, there is no immediate reason to initialize at the start, + so this function is an empty operation. */ +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed) { + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_seed_state(pub_seed); + (void)sk_seed; /* Suppress an 'unused parameter' warning. */ +} + +/* + * Computes PRF(key, addr), given a secret key of SPX_N bytes and an address + */ +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]) { + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES]; + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + + memcpy(buf, key, SPX_N); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_compress_address(buf + SPX_N, addr); + + sha256(outbuf, buf, SPX_N + SPX_SHA256_ADDR_BYTES); + memcpy(out, outbuf, SPX_N); +} + +/** + * Computes the message-dependent randomness R, using a secret seed as a key + * for HMAC, and an optional randomization value prefixed to the message. + * This requires m to have at least SPX_SHA256_BLOCK_BYTES + SPX_N space + * available in front of the pointer, i.e. before the message to use for the + * prefix. This is necessary to prevent having to move the message around (and + * allocate memory for it). + */ +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen) { + unsigned char buf[SPX_SHA256_BLOCK_BYTES + SPX_SHA256_OUTPUT_BYTES]; + uint8_t state[40]; + int i; + + /* This implements HMAC-SHA256 */ + for (i = 0; i < SPX_N; i++) { + buf[i] = 0x36 ^ sk_prf[i]; + } + memset(buf + SPX_N, 0x36, SPX_SHA256_BLOCK_BYTES - SPX_N); + + sha256_inc_init(state); + sha256_inc_blocks(state, buf, 1); + + memcpy(buf, optrand, SPX_N); + + /* If optrand + message cannot fill up an entire block */ + if (SPX_N + mlen < SPX_SHA256_BLOCK_BYTES) { + memcpy(buf + SPX_N, m, mlen); + sha256_inc_finalize(buf + SPX_SHA256_BLOCK_BYTES, state, + buf, mlen + SPX_N); + } + /* Otherwise first fill a block, so that finalize only uses the message */ + else { + memcpy(buf + SPX_N, m, SPX_SHA256_BLOCK_BYTES - SPX_N); + sha256_inc_blocks(state, buf, 1); + + m += SPX_SHA256_BLOCK_BYTES - SPX_N; + mlen -= SPX_SHA256_BLOCK_BYTES - SPX_N; + sha256_inc_finalize(buf + SPX_SHA256_BLOCK_BYTES, state, m, mlen); + } + + for (i = 0; i < SPX_N; i++) { + buf[i] = 0x5c ^ sk_prf[i]; + } + memset(buf + SPX_N, 0x5c, SPX_SHA256_BLOCK_BYTES - SPX_N); + + sha256(buf, buf, SPX_SHA256_BLOCK_BYTES + SPX_SHA256_OUTPUT_BYTES); + memcpy(R, buf, SPX_N); +} + +/** + * Computes the message hash using R, the public key, and the message. + * Outputs the message digest and the index of the leaf. The index is split in + * the tree index and the leaf index, for convenient copying to an address. + */ +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen) { +#define SPX_TREE_BITS (SPX_TREE_HEIGHT * (SPX_D - 1)) +#define SPX_TREE_BYTES ((SPX_TREE_BITS + 7) / 8) +#define SPX_LEAF_BITS SPX_TREE_HEIGHT +#define SPX_LEAF_BYTES ((SPX_LEAF_BITS + 7) / 8) +#define SPX_DGST_BYTES (SPX_FORS_MSG_BYTES + SPX_TREE_BYTES + SPX_LEAF_BYTES) + + unsigned char seed[SPX_SHA256_OUTPUT_BYTES + 4]; + + /* Round to nearest multiple of SPX_SHA256_BLOCK_BYTES */ +#define SPX_INBLOCKS (((SPX_N + SPX_PK_BYTES + SPX_SHA256_BLOCK_BYTES - 1) & \ + -SPX_SHA256_BLOCK_BYTES) / SPX_SHA256_BLOCK_BYTES) + unsigned char inbuf[SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES]; + + unsigned char buf[SPX_DGST_BYTES]; + unsigned char *bufp = buf; + uint8_t state[40]; + + sha256_inc_init(state); + + memcpy(inbuf, R, SPX_N); + memcpy(inbuf + SPX_N, pk, SPX_PK_BYTES); + + /* If R + pk + message cannot fill up an entire block */ + if (SPX_N + SPX_PK_BYTES + mlen < SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES) { + memcpy(inbuf + SPX_N + SPX_PK_BYTES, m, mlen); + sha256_inc_finalize(seed, state, inbuf, SPX_N + SPX_PK_BYTES + mlen); + } + /* Otherwise first fill a block, so that finalize only uses the message */ + else { + memcpy(inbuf + SPX_N + SPX_PK_BYTES, m, + SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES); + sha256_inc_blocks(state, inbuf, SPX_INBLOCKS); + + m += SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES; + mlen -= SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES; + sha256_inc_finalize(seed, state, m, mlen); + } + + /* By doing this in two steps, we prevent hashing the message twice; + otherwise each iteration in MGF1 would hash the message again. */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_mgf1(bufp, SPX_DGST_BYTES, seed, SPX_SHA256_OUTPUT_BYTES); + + memcpy(digest, bufp, SPX_FORS_MSG_BYTES); + bufp += SPX_FORS_MSG_BYTES; + + *tree = PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_bytes_to_ull(bufp, SPX_TREE_BYTES); + *tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS); + bufp += SPX_TREE_BYTES; + + *leaf_idx = (uint32_t)PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_bytes_to_ull( + bufp, SPX_LEAF_BYTES); + *leaf_idx &= (~(uint32_t)0) >> (32 - SPX_LEAF_BITS); +} diff --git a/crypto_sign/sphincs-sha256-192s-simple/clean/params.h b/crypto_sign/sphincs-sha256-192s-simple/clean/params.h new file mode 100644 index 00000000..483b5374 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192s-simple/clean/params.h @@ -0,0 +1,53 @@ +#ifndef SPX_PARAMS_H +#define SPX_PARAMS_H + +/* Hash output length in bytes. */ +#define SPX_N 24 +/* Height of the hypertree. */ +#define SPX_FULL_HEIGHT 64 +/* Number of subtree layer. */ +#define SPX_D 8 +/* FORS tree dimensions. */ +#define SPX_FORS_HEIGHT 16 +#define SPX_FORS_TREES 14 +/* Winternitz parameter, */ +#define SPX_WOTS_W 16 + +/* The hash function is defined by linking a different hash.c file, as opposed + to setting a #define constant. */ + +/* For clarity */ +#define SPX_ADDR_BYTES 32 + +/* WOTS parameters. */ +#define SPX_WOTS_LOGW 4 + +#define SPX_WOTS_LEN1 (8 * SPX_N / SPX_WOTS_LOGW) + +/* SPX_WOTS_LEN2 is floor(log(len_1 * (w - 1)) / log(w)) + 1; we precompute */ +#define SPX_WOTS_LEN2 3 + +#define SPX_WOTS_LEN (SPX_WOTS_LEN1 + SPX_WOTS_LEN2) +#define SPX_WOTS_BYTES (SPX_WOTS_LEN * SPX_N) +#define SPX_WOTS_PK_BYTES SPX_WOTS_BYTES + +/* Subtree size. */ +#define SPX_TREE_HEIGHT (SPX_FULL_HEIGHT / SPX_D) + +/* FORS parameters. */ +#define SPX_FORS_MSG_BYTES ((SPX_FORS_HEIGHT * SPX_FORS_TREES + 7) / 8) +#define SPX_FORS_BYTES ((SPX_FORS_HEIGHT + 1) * SPX_FORS_TREES * SPX_N) +#define SPX_FORS_PK_BYTES SPX_N + +/* Resulting SPX sizes. */ +#define SPX_BYTES (SPX_N + SPX_FORS_BYTES + SPX_D * SPX_WOTS_BYTES +\ + SPX_FULL_HEIGHT * SPX_N) +#define SPX_PK_BYTES (2 * SPX_N) +#define SPX_SK_BYTES (2 * SPX_N + SPX_PK_BYTES) + +/* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ +#define SPX_OPTRAND_BYTES 32 + +#endif diff --git a/crypto_sign/sphincs-sha256-192s-simple/clean/sha256.c b/crypto_sign/sphincs-sha256-192s-simple/clean/sha256.c new file mode 100644 index 00000000..d6fe5626 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192s-simple/clean/sha256.c @@ -0,0 +1,72 @@ +/* Based on the public domain implementation in + * crypto_hash/sha512/ref/ from http://bench.cr.yp.to/supercop.html + * by D. J. Bernstein */ + +#include +#include +#include + +#include "sha2.h" +#include "sha256.h" +#include "utils.h" + +/* + * Compresses an address to a 22-byte sequence. + * This reduces the number of required SHA256 compression calls, as the last + * block of input is padded with at least 65 bits. + */ +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_compress_address(unsigned char *out, const uint32_t addr[8]) { + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_ull_to_bytes(out, 1, addr[0]); /* drop 3 bytes of the layer field */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_ull_to_bytes(out + 1, 4, addr[2]); /* drop the highest tree address word */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_ull_to_bytes(out + 5, 4, addr[3]); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_ull_to_bytes(out + 9, 1, addr[4]); /* drop 3 bytes of the type field */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_ull_to_bytes(out + 10, 4, addr[5]); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_ull_to_bytes(out + 14, 4, addr[6]); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_ull_to_bytes(out + 18, 4, addr[7]); +} + +/** + * Requires 'input_plus_four_bytes' to have 'inlen' + 4 bytes, so that the last + * four bytes can be used for the counter. Typically 'input' is merely a seed. + * Outputs outlen number of bytes + */ +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + unsigned char *input_plus_four_bytes, unsigned long inlen) { + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + unsigned long i; + + /* While we can fit in at least another full block of SHA256 output.. */ + for (i = 0; (i + 1)*SPX_SHA256_OUTPUT_BYTES <= outlen; i++) { + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_ull_to_bytes(input_plus_four_bytes + inlen, 4, i); + sha256(out, input_plus_four_bytes, inlen + 4); + out += SPX_SHA256_OUTPUT_BYTES; + } + /* Until we cannot anymore, and we fill the remainder. */ + if (outlen > i * SPX_SHA256_OUTPUT_BYTES) { + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_ull_to_bytes(input_plus_four_bytes + inlen, 4, i); + sha256(outbuf, input_plus_four_bytes, inlen + 4); + memcpy(out, outbuf, outlen - i * SPX_SHA256_OUTPUT_BYTES); + } +} + +uint8_t PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_state_seeded[40]; + +/** + * Absorb the constant pub_seed using one round of the compression function + * This initializes state_seeded, which can then be reused in thash + **/ +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_seed_state(const unsigned char *pub_seed) { + uint8_t block[SPX_SHA256_BLOCK_BYTES]; + size_t i; + + for (i = 0; i < SPX_N; ++i) { + block[i] = pub_seed[i]; + } + for (i = SPX_N; i < SPX_SHA256_BLOCK_BYTES; ++i) { + block[i] = 0; + } + + sha256_inc_init(PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_state_seeded); + sha256_inc_blocks(PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_state_seeded, block, 1); +} diff --git a/crypto_sign/sphincs-sha256-192s-simple/clean/sha256.h b/crypto_sign/sphincs-sha256-192s-simple/clean/sha256.h new file mode 100644 index 00000000..4a224372 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192s-simple/clean/sha256.h @@ -0,0 +1,21 @@ +#ifndef SPX_SHA256_H +#define SPX_SHA256_H + +#define SPX_SHA256_BLOCK_BYTES 64 +#define SPX_SHA256_OUTPUT_BYTES 32 /* This does not necessarily equal SPX_N */ +#define SPX_SHA256_ADDR_BYTES 22 + +#include +#include + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_compress_address(unsigned char *out, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + unsigned char *input_plus_four_bytes, unsigned long inlen); + +uint8_t PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_state_seeded[40]; + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_seed_state(const unsigned char *pub_seed); + +#endif diff --git a/crypto_sign/sphincs-sha256-192s-simple/clean/sign.c b/crypto_sign/sphincs-sha256-192s-simple/clean/sign.c new file mode 100644 index 00000000..a99b3778 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192s-simple/clean/sign.c @@ -0,0 +1,344 @@ +#include +#include +#include + +#include "address.h" +#include "api.h" +#include "fors.h" +#include "hash.h" +#include "params.h" +#include "randombytes.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +/** + * Computes the leaf at a given address. First generates the WOTS key pair, + * then computes leaf by hashing horizontally. + */ +static void wots_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t tree_addr[8]) { + unsigned char pk[SPX_WOTS_BYTES]; + uint32_t wots_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, addr_idx); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_wots_gen_pk( + pk, sk_seed, pub_seed, wots_addr); + + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_thash_WOTS_LEN( + leaf, pk, pub_seed, wots_pk_addr); +} + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_crypto_sign_secretkeybytes(void) { + return PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_CRYPTO_SECRETKEYBYTES; +} + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_crypto_sign_publickeybytes(void) { + return PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES; +} + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_crypto_sign_bytes(void) { + return PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_CRYPTO_BYTES; +} + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_crypto_sign_seedbytes(void) { + return PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_CRYPTO_SEEDBYTES; +} + +/* + * Generates an SPX key pair given a seed of length + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed) { + /* We do not need the auth path in key generation, but it simplifies the + code to have just one treehash routine that computes both root and path + in one function. */ + unsigned char auth_path[SPX_TREE_HEIGHT * SPX_N]; + uint32_t top_tree_addr[8] = {0}; + + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_layer_addr( + top_tree_addr, SPX_D - 1); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_type( + top_tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */ + memcpy(sk, seed, PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_CRYPTO_SEEDBYTES); + + memcpy(pk, sk + 2 * SPX_N, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_initialize_hash_function(pk, sk); + + /* Compute root node of the top-most subtree. */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_treehash_TREE_HEIGHT( + sk + 3 * SPX_N, auth_path, sk, sk + 2 * SPX_N, 0, 0, + wots_gen_leaf, top_tree_addr); + + memcpy(pk + SPX_N, sk + 3 * SPX_N, SPX_N); + + return 0; +} + +/* + * Generates an SPX key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk) { + unsigned char seed[PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_CRYPTO_SEEDBYTES]; + randombytes(seed, PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_CRYPTO_SEEDBYTES); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_crypto_sign_seed_keypair( + pk, sk, seed); + + return 0; +} + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + const unsigned char *sk_seed = sk; + const unsigned char *sk_prf = sk + SPX_N; + const unsigned char *pk = sk + 2 * SPX_N; + const unsigned char *pub_seed = pk; + + unsigned char optrand[SPX_N]; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char root[SPX_N]; + uint32_t i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_initialize_hash_function( + pub_seed, sk_seed); + + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ + randombytes(optrand, SPX_N); + /* Compute the digest randomization value. */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_gen_message_random( + sig, sk_prf, optrand, m, mlen); + + /* Derive the message digest and leaf index from R, PK and M. */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Sign the message hash using FORS. */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_fors_sign( + sig, root, mhash, sk_seed, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Compute a WOTS signature. */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_wots_sign( + sig, root, sk_seed, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the authentication path for the used WOTS leaf. */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_treehash_TREE_HEIGHT( + root, sig, sk_seed, pub_seed, idx_leaf, 0, + wots_gen_leaf, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + *siglen = SPX_BYTES; + + return 0; +} + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk) { + const unsigned char *pub_seed = pk; + const unsigned char *pub_root = pk + SPX_N; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char wots_pk[SPX_WOTS_BYTES]; + unsigned char root[SPX_N]; + unsigned char leaf[SPX_N]; + unsigned int i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + if (siglen != SPX_BYTES) { + return -1; + } + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_initialize_hash_function( + pub_seed, NULL); + + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + /* Derive the message digest and leaf index from R || PK || M. */ + /* The additional SPX_N is a result of the hash domain separator. */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + /* Layer correctly defaults to 0, so no need to set_layer_addr */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_fors_pk_from_sig( + root, sig, mhash, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + /* For each subtree.. */ + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + + /* The WOTS public key is only correct if the signature was correct. */ + /* Initially, root is the FORS pk, but on subsequent iterations it is + the root of the subtree below the currently processed subtree. */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_wots_pk_from_sig( + wots_pk, sig, root, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the leaf node using the WOTS public key. */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_thash_WOTS_LEN( + leaf, wots_pk, pub_seed, wots_pk_addr); + + /* Compute the root node of this subtree. */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_compute_root( + root, leaf, idx_leaf, 0, sig, SPX_TREE_HEIGHT, + pub_seed, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + /* Check if the root node equals the root node in the public key. */ + if (memcmp(root, pub_root, SPX_N) != 0) { + return -1; + } + + return 0; +} + + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + size_t siglen; + + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_crypto_sign_signature( + sm, &siglen, m, mlen, sk); + + memmove(sm + SPX_BYTES, m, mlen); + *smlen = siglen + mlen; + + return 0; +} + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk) { + /* The API caller does not necessarily know what size a signature should be + but SPHINCS+ signatures are always exactly SPX_BYTES. */ + if (smlen < SPX_BYTES) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + *mlen = smlen - SPX_BYTES; + + if (PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_crypto_sign_verify( + sm, SPX_BYTES, sm + SPX_BYTES, *mlen, pk)) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + /* If verification was successful, move the message to the right place. */ + memmove(m, sm + SPX_BYTES, *mlen); + + return 0; +} diff --git a/crypto_sign/sphincs-sha256-192s-simple/clean/thash.h b/crypto_sign/sphincs-sha256-192s-simple/clean/thash.h new file mode 100644 index 00000000..62613ccd --- /dev/null +++ b/crypto_sign/sphincs-sha256-192s-simple/clean/thash.h @@ -0,0 +1,22 @@ +#ifndef SPX_THASH_H +#define SPX_THASH_H + +#include + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-sha256-192s-simple/clean/thash_sha256_simple.c b/crypto_sign/sphincs-sha256-192s-simple/clean/thash_sha256_simple.c new file mode 100644 index 00000000..64745d6d --- /dev/null +++ b/crypto_sign/sphincs-sha256-192s-simple/clean/thash_sha256_simple.c @@ -0,0 +1,70 @@ +#include +#include + +#include "address.h" +#include "params.h" +#include "thash.h" + +#include "sha2.h" +#include "sha256.h" + +/** + * Takes an array of inblocks concatenated arrays of SPX_N bytes. + */ +static void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_thash( + unsigned char *out, unsigned char *buf, + const unsigned char *in, unsigned int inblocks, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + uint8_t sha2_state[40]; + + (void)pub_seed; /* Suppress an 'unused parameter' warning. */ + + /* Retrieve precomputed state containing pub_seed */ + memcpy(sha2_state, PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_state_seeded, 40 * sizeof(uint8_t)); + + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_compress_address(buf, addr); + memcpy(buf + SPX_SHA256_ADDR_BYTES, in, inblocks * SPX_N); + + sha256_inc_finalize(outbuf, sha2_state, buf, SPX_SHA256_ADDR_BYTES + inblocks * SPX_N); + memcpy(out, outbuf, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_SHA256_ADDR_BYTES + 1 * SPX_N]; + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_thash( + out, buf, in, 1, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_SHA256_ADDR_BYTES + 2 * SPX_N]; + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_thash( + out, buf, in, 2, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_SHA256_ADDR_BYTES + SPX_WOTS_LEN * SPX_N]; + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_thash( + out, buf, in, SPX_WOTS_LEN, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_SHA256_ADDR_BYTES + SPX_FORS_TREES * SPX_N]; + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_thash( + out, buf, in, SPX_FORS_TREES, pub_seed, addr); +} diff --git a/crypto_sign/sphincs-sha256-192s-simple/clean/utils.c b/crypto_sign/sphincs-sha256-192s-simple/clean/utils.c new file mode 100644 index 00000000..db1daf6b --- /dev/null +++ b/crypto_sign/sphincs-sha256-192s-simple/clean/utils.c @@ -0,0 +1,192 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in) { + + /* Iterate over out in decreasing order, for big-endianness. */ + for (size_t i = outlen; i > 0; i--) { + out[i - 1] = in & 0xff; + in = in >> 8; + } +} + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen) { + unsigned long long retval = 0; + + for (size_t i = 0; i < inlen; i++) { + retval |= ((unsigned long long)in[i]) << (8 * (inlen - 1 - i)); + } + return retval; +} + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + unsigned char buffer[2 * SPX_N]; + + /* If leaf_idx is odd (last bit = 1), current path element is a right child + and auth_path has to go left. Otherwise it is the other way around. */ + if (leaf_idx & 1) { + memcpy(buffer + SPX_N, leaf, SPX_N); + memcpy(buffer, auth_path, SPX_N); + } else { + memcpy(buffer, leaf, SPX_N); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + + for (i = 0; i < tree_height - 1; i++) { + leaf_idx >>= 1; + idx_offset >>= 1; + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_tree_height(addr, i + 1); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + + /* Pick the right or left neighbor, depending on parity of the node. */ + if (leaf_idx & 1) { + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_thash_2( + buffer + SPX_N, buffer, pub_seed, addr); + memcpy(buffer, auth_path, SPX_N); + } else { + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_thash_2( + buffer, buffer, pub_seed, addr); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + } + + /* The last iteration is exceptional; we do not copy an auth_path node. */ + leaf_idx >>= 1; + idx_offset >>= 1; + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_tree_height(addr, tree_height); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_thash_2( + root, buffer, pub_seed, addr); +} + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +static void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_treehash( + unsigned char *root, unsigned char *auth_path, + unsigned char *stack, unsigned int *heights, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned int offset = 0; + uint32_t idx; + uint32_t tree_idx; + + for (idx = 0; idx < (uint32_t)(1 << tree_height); idx++) { + /* Add the next leaf node to the stack. */ + gen_leaf(stack + offset * SPX_N, + sk_seed, pub_seed, idx + idx_offset, tree_addr); + offset++; + heights[offset - 1] = 0; + + /* If this is a node we need for the auth path.. */ + if ((leaf_idx ^ 0x1) == idx) { + memcpy(auth_path, stack + (offset - 1)*SPX_N, SPX_N); + } + + /* While the top-most nodes are of equal height.. */ + while (offset >= 2 && heights[offset - 1] == heights[offset - 2]) { + /* Compute index of the new node, in the next layer. */ + tree_idx = (idx >> (heights[offset - 1] + 1)); + + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_tree_height( + tree_addr, heights[offset - 1] + 1); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_tree_index( + tree_addr, tree_idx + (idx_offset >> (heights[offset - 1] + 1))); + /* Hash the top-most nodes from the stack together. */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_thash_2( + stack + (offset - 2)*SPX_N, stack + (offset - 2)*SPX_N, + pub_seed, tree_addr); + offset--; + /* Note that the top-most node is now one layer higher. */ + heights[offset - 1]++; + + /* If this is a node we need for the auth path.. */ + if (((leaf_idx >> heights[offset - 1]) ^ 0x1) == tree_idx) { + memcpy(auth_path + heights[offset - 1]*SPX_N, + stack + (offset - 1)*SPX_N, SPX_N); + } + } + } + memcpy(root, stack, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_FORS_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_FORS_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_FORS_HEIGHT, gen_leaf, tree_addr); +} + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_TREE_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_TREE_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_TREE_HEIGHT, gen_leaf, tree_addr); +} diff --git a/crypto_sign/sphincs-sha256-192s-simple/clean/utils.h b/crypto_sign/sphincs-sha256-192s-simple/clean/utils.h new file mode 100644 index 00000000..bacec333 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192s-simple/clean/utils.h @@ -0,0 +1,60 @@ +#ifndef SPX_UTILS_H +#define SPX_UTILS_H + +#include "params.h" +#include +#include + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in); + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen); + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-sha256-192s-simple/clean/wots.c b/crypto_sign/sphincs-sha256-192s-simple/clean/wots.c new file mode 100644 index 00000000..20179cdf --- /dev/null +++ b/crypto_sign/sphincs-sha256-192s-simple/clean/wots.c @@ -0,0 +1,161 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +// TODO clarify address expectations, and make them more uniform. +// TODO i.e. do we expect types to be set already? +// TODO and do we expect modifications or copies? + +/** + * Computes the starting value for a chain, i.e. the secret key. + * Expects the address to be complete up to the chain address. + */ +static void wots_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t wots_addr[8]) { + /* Make sure that the hash address is actually zeroed. */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_hash_addr(wots_addr, 0); + + /* Generate sk element. */ + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_prf_addr(sk, sk_seed, wots_addr); +} + +/** + * Computes the chaining function. + * out and in have to be n-byte arrays. + * + * Interprets in as start-th value of the chain. + * addr has to contain the address of the chain. + */ +static void gen_chain(unsigned char *out, const unsigned char *in, + unsigned int start, unsigned int steps, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + /* Initialize out with the value at position 'start'. */ + memcpy(out, in, SPX_N); + + /* Iterate 'steps' calls to the hash function. */ + for (i = start; i < (start + steps) && i < SPX_WOTS_W; i++) { + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_hash_addr(addr, i); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_thash_1( + out, out, pub_seed, addr); + } +} + +/** + * base_w algorithm as described in draft. + * Interprets an array of bytes as integers in base w. + * This only works when log_w is a divisor of 8. + */ +static void base_w(unsigned int *output, const size_t out_len, + const unsigned char *input) { + size_t in = 0; + size_t out = 0; + unsigned char total = 0; + unsigned int bits = 0; + size_t consumed; + + for (consumed = 0; consumed < out_len; consumed++) { + if (bits == 0) { + total = input[in]; + in++; + bits += 8; + } + bits -= SPX_WOTS_LOGW; + output[out] = (unsigned int)((total >> bits) & (SPX_WOTS_W - 1)); + out++; + } +} + +/* Computes the WOTS+ checksum over a message (in base_w). */ +static void wots_checksum(unsigned int *csum_base_w, + const unsigned int *msg_base_w) { + unsigned int csum = 0; + unsigned char csum_bytes[(SPX_WOTS_LEN2 * SPX_WOTS_LOGW + 7) / 8]; + unsigned int i; + + /* Compute checksum. */ + for (i = 0; i < SPX_WOTS_LEN1; i++) { + csum += SPX_WOTS_W - 1 - msg_base_w[i]; + } + + /* Convert checksum to base_w. */ + /* Make sure expected empty zero bits are the least significant bits. */ + csum = csum << (8 - ((SPX_WOTS_LEN2 * SPX_WOTS_LOGW) % 8)); + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_ull_to_bytes( + csum_bytes, sizeof(csum_bytes), csum); + base_w(csum_base_w, SPX_WOTS_LEN2, csum_bytes); +} + +/* Takes a message and derives the matching chain lengths. */ +static void chain_lengths(unsigned int *lengths, const unsigned char *msg) { + base_w(lengths, SPX_WOTS_LEN1, msg); + wots_checksum(lengths + SPX_WOTS_LEN1, lengths); +} + +/** + * WOTS key generation. Takes a 32 byte sk_seed, expands it to WOTS private key + * elements and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(pk + i * SPX_N, sk_seed, addr); + gen_chain(pk + i * SPX_N, pk + i * SPX_N, + 0, SPX_WOTS_W - 1, pub_seed, addr); + } +} + +/** + * Takes a n-byte message and the 32-byte sk_see to compute a signature 'sig'. + */ +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(sig + i * SPX_N, sk_seed, addr); + gen_chain(sig + i * SPX_N, sig + i * SPX_N, 0, lengths[i], pub_seed, addr); + } +} + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_set_chain_addr(addr, i); + gen_chain(pk + i * SPX_N, sig + i * SPX_N, + lengths[i], SPX_WOTS_W - 1 - lengths[i], pub_seed, addr); + } +} diff --git a/crypto_sign/sphincs-sha256-192s-simple/clean/wots.h b/crypto_sign/sphincs-sha256-192s-simple/clean/wots.h new file mode 100644 index 00000000..9f6ae328 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192s-simple/clean/wots.h @@ -0,0 +1,38 @@ +#ifndef SPX_WOTS_H +#define SPX_WOTS_H + +#include "params.h" +#include + +/** + * WOTS key generation. Takes a 32 byte seed for the private key, expands it to + * a full WOTS private key and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * Takes a n-byte message and the 32-byte seed for the private key to compute a + * signature that is placed at 'sig'. + */ +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]); + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-sha256-256f-robust/META.yml b/crypto_sign/sphincs-sha256-256f-robust/META.yml new file mode 100644 index 00000000..996632a5 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256f-robust/META.yml @@ -0,0 +1,27 @@ +name: SPHINCS+ +type: signature +claimed-nist-level: 5 +length-public-key: 64 +length-secret-key: 128 +length-signature: 49216 +testvectors-sha256: 14dd19ba3ff75bad890949050289ab0f178d7baa6dcb8ff6bcd6a873692a5686 +principal-submitter: Andreas Hülsing +auxiliary-submitters: + - Jean-Philippe Aumasson + - Daniel J. Bernstein, + - Christoph Dobraunig + - Maria Eichlseder + - Scott Fluhrer + - Stefan-Lukas Gazdag + - Panos Kampanakis + - Stefan Kölbl + - Tanja Lange + - Martin M. Lauridsen + - Florian Mendel + - Ruben Niederhagen + - Christian Rechberger + - Joost Rijneveld + - Peter Schwabe +implementations: + - name: clean + version: https://github.com/sphincs/sphincsplus/commit/77755c94d0bc744478044d6efbb888dc13156441 diff --git a/crypto_sign/sphincs-sha256-256f-robust/clean/LICENSE b/crypto_sign/sphincs-sha256-256f-robust/clean/LICENSE new file mode 100644 index 00000000..670154e3 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256f-robust/clean/LICENSE @@ -0,0 +1,116 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + diff --git a/crypto_sign/sphincs-sha256-256f-robust/clean/Makefile b/crypto_sign/sphincs-sha256-256f-robust/clean/Makefile new file mode 100644 index 00000000..cae42e69 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256f-robust/clean/Makefile @@ -0,0 +1,20 @@ +# This Makefile can be used with GNU Make or BSD Make + +LIB=libsphincs-sha256-256f-robust_clean.a + +HEADERS = params.h address.h wots.h utils.h fors.h api.h hash.h thash.h sha256.h +OBJECTS = address.o wots.o utils.o fors.o sign.o hash_sha256.o thash_sha256_robust.o sha256.o + +CFLAGS=-O3 -Wall -Wconversion -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) + +all: $(LIB) + +%.o: %.c $(HEADERS) + $(CC) $(CFLAGS) -c -o $@ $< + +$(LIB): $(OBJECTS) + $(AR) -r $@ $(OBJECTS) + +clean: + $(RM) $(OBJECTS) + $(RM) $(LIB) diff --git a/crypto_sign/sphincs-sha256-256f-robust/clean/Makefile.Microsoft_nmake b/crypto_sign/sphincs-sha256-256f-robust/clean/Makefile.Microsoft_nmake new file mode 100644 index 00000000..ba2eab10 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256f-robust/clean/Makefile.Microsoft_nmake @@ -0,0 +1,19 @@ +# This Makefile can be used with Microsoft Visual Studio's nmake using the command: +# nmake /f Makefile.Microsoft_nmake + +LIBRARY=libsphincs-sha256-256f-robust_clean.lib +OBJECTS=address.obj wots.obj utils.obj fors.obj sign.obj hash_sha256.obj thash_sha256_robust.obj sha256.obj + +CFLAGS=/nologo /I ..\..\..\common /W4 /WX + +all: $(LIBRARY) + +# Make sure objects are recompiled if headers change. +$(OBJECTS): *.h + +$(LIBRARY): $(OBJECTS) + LIB.EXE /NOLOGO /WX /OUT:$@ $** + +clean: + -DEL $(OBJECTS) + -DEL $(LIBRARY) diff --git a/crypto_sign/sphincs-sha256-256f-robust/clean/address.c b/crypto_sign/sphincs-sha256-256f-robust/clean/address.c new file mode 100644 index 00000000..8bae3cf3 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256f-robust/clean/address.c @@ -0,0 +1,78 @@ +#include + +#include "address.h" +#include "params.h" +#include "utils.h" + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]) { + int i; + + for (i = 0; i < 8; i++) { + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_ull_to_bytes( + bytes + i * 4, 4, addr[i]); + } +} + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer) { + addr[0] = layer; +} + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree) { + addr[1] = 0; + addr[2] = (uint32_t) (tree >> 32); + addr[3] = (uint32_t) tree; +} + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_type( + uint32_t addr[8], uint32_t type) { + addr[4] = type; +} + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; +} + +/* These functions are used for OTS addresses. */ + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair) { + addr[5] = keypair; +} + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; + out[5] = in[5]; +} + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain) { + addr[6] = chain; +} + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash) { + addr[7] = hash; +} + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height) { + addr[6] = tree_height; +} + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index) { + addr[7] = tree_index; +} diff --git a/crypto_sign/sphincs-sha256-256f-robust/clean/address.h b/crypto_sign/sphincs-sha256-256f-robust/clean/address.h new file mode 100644 index 00000000..2a277cc1 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256f-robust/clean/address.h @@ -0,0 +1,50 @@ +#ifndef SPX_ADDRESS_H +#define SPX_ADDRESS_H + +#include + +#define SPX_ADDR_TYPE_WOTS 0 +#define SPX_ADDR_TYPE_WOTSPK 1 +#define SPX_ADDR_TYPE_HASHTREE 2 +#define SPX_ADDR_TYPE_FORSTREE 3 +#define SPX_ADDR_TYPE_FORSPK 4 + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer); + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree); + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_type( + uint32_t addr[8], uint32_t type); + +/* Copies the layer and tree part of one address into the other */ +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for WOTS and FORS addresses. */ + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair); + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain); + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash); + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height); + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index); + +#endif diff --git a/crypto_sign/sphincs-sha256-256f-robust/clean/api.h b/crypto_sign/sphincs-sha256-256f-robust/clean/api.h new file mode 100644 index 00000000..004aabe2 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256f-robust/clean/api.h @@ -0,0 +1,78 @@ +#ifndef PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_API_H +#define PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_API_H + +#include +#include + +#define PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_CRYPTO_ALGNAME "SPHINCS+" + +#define PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_CRYPTO_SECRETKEYBYTES 128 +#define PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES 64 +#define PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_CRYPTO_BYTES 49216 +#define PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_CRYPTO_SEEDBYTES 96 + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_crypto_sign_secretkeybytes(void); + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_crypto_sign_publickeybytes(void); + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_crypto_sign_bytes(void); + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_crypto_sign_seedbytes(void); + +/* + * Generates a SPHINCS+ key pair given a seed. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed); + +/* + * Generates a SPHINCS+ key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk); + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk); + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk); + +#endif diff --git a/crypto_sign/sphincs-sha256-256f-robust/clean/fors.c b/crypto_sign/sphincs-sha256-256f-robust/clean/fors.c new file mode 100644 index 00000000..4b5fb854 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256f-robust/clean/fors.c @@ -0,0 +1,164 @@ +#include +#include +#include + +#include "address.h" +#include "fors.h" +#include "hash.h" +#include "thash.h" +#include "utils.h" + +static void fors_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_prf_addr( + sk, sk_seed, fors_leaf_addr); +} + +static void fors_sk_to_leaf(unsigned char *leaf, const unsigned char *sk, + const unsigned char *pub_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_thash_1( + leaf, sk, pub_seed, fors_leaf_addr); +} + +static void fors_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t fors_tree_addr[8]) { + uint32_t fors_leaf_addr[8] = {0}; + + /* Only copy the parts that must be kept in fors_leaf_addr. */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_copy_keypair_addr( + fors_leaf_addr, fors_tree_addr); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_type( + fors_leaf_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_tree_index( + fors_leaf_addr, addr_idx); + + fors_gen_sk(leaf, sk_seed, fors_leaf_addr); + fors_sk_to_leaf(leaf, leaf, pub_seed, fors_leaf_addr); +} + +/** + * Interprets m as SPX_FORS_HEIGHT-bit unsigned integers. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + * Assumes indices has space for SPX_FORS_TREES integers. + */ +static void message_to_indices(uint32_t *indices, const unsigned char *m) { + unsigned int i, j; + unsigned int offset = 0; + + for (i = 0; i < SPX_FORS_TREES; i++) { + indices[i] = 0; + for (j = 0; j < SPX_FORS_HEIGHT; j++) { + indices[i] ^= (((uint32_t)m[offset >> 3] >> (offset & 0x7)) & 0x1) << j; + offset++; + } + } +} + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Include the secret key part that produces the selected leaf node. */ + fors_gen_sk(sig, sk_seed, fors_tree_addr); + sig += SPX_N; + + /* Compute the authentication path for this leaf node. */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_treehash_FORS_HEIGHT( + roots + i * SPX_N, sig, sk_seed, pub_seed, + indices[i], idx_offset, fors_gen_leaf, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + unsigned char leaf[SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Derive the leaf from the included secret key part. */ + fors_sk_to_leaf(leaf, sig, pub_seed, fors_tree_addr); + sig += SPX_N; + + /* Derive the corresponding root node of this tree. */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_compute_root( + roots + i * SPX_N, leaf, indices[i], idx_offset, sig, + SPX_FORS_HEIGHT, pub_seed, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} diff --git a/crypto_sign/sphincs-sha256-256f-robust/clean/fors.h b/crypto_sign/sphincs-sha256-256f-robust/clean/fors.h new file mode 100644 index 00000000..29892ef4 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256f-robust/clean/fors.h @@ -0,0 +1,30 @@ +#ifndef SPX_FORS_H +#define SPX_FORS_H + +#include + +#include "params.h" + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]); + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-sha256-256f-robust/clean/hash.h b/crypto_sign/sphincs-sha256-256f-robust/clean/hash.h new file mode 100644 index 00000000..25ccbefb --- /dev/null +++ b/crypto_sign/sphincs-sha256-256f-robust/clean/hash.h @@ -0,0 +1,22 @@ +#ifndef SPX_HASH_H +#define SPX_HASH_H + +#include + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed); + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen); + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen); + +#endif diff --git a/crypto_sign/sphincs-sha256-256f-robust/clean/hash_sha256.c b/crypto_sign/sphincs-sha256-256f-robust/clean/hash_sha256.c new file mode 100644 index 00000000..699f52e1 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256f-robust/clean/hash_sha256.c @@ -0,0 +1,148 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "utils.h" + +#include "sha2.h" +#include "sha256.h" + +/* For SHA256, there is no immediate reason to initialize at the start, + so this function is an empty operation. */ +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed) { + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_seed_state(pub_seed); + (void)sk_seed; /* Suppress an 'unused parameter' warning. */ +} + +/* + * Computes PRF(key, addr), given a secret key of SPX_N bytes and an address + */ +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]) { + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES]; + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + + memcpy(buf, key, SPX_N); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_compress_address(buf + SPX_N, addr); + + sha256(outbuf, buf, SPX_N + SPX_SHA256_ADDR_BYTES); + memcpy(out, outbuf, SPX_N); +} + +/** + * Computes the message-dependent randomness R, using a secret seed as a key + * for HMAC, and an optional randomization value prefixed to the message. + * This requires m to have at least SPX_SHA256_BLOCK_BYTES + SPX_N space + * available in front of the pointer, i.e. before the message to use for the + * prefix. This is necessary to prevent having to move the message around (and + * allocate memory for it). + */ +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen) { + unsigned char buf[SPX_SHA256_BLOCK_BYTES + SPX_SHA256_OUTPUT_BYTES]; + uint8_t state[40]; + int i; + + /* This implements HMAC-SHA256 */ + for (i = 0; i < SPX_N; i++) { + buf[i] = 0x36 ^ sk_prf[i]; + } + memset(buf + SPX_N, 0x36, SPX_SHA256_BLOCK_BYTES - SPX_N); + + sha256_inc_init(state); + sha256_inc_blocks(state, buf, 1); + + memcpy(buf, optrand, SPX_N); + + /* If optrand + message cannot fill up an entire block */ + if (SPX_N + mlen < SPX_SHA256_BLOCK_BYTES) { + memcpy(buf + SPX_N, m, mlen); + sha256_inc_finalize(buf + SPX_SHA256_BLOCK_BYTES, state, + buf, mlen + SPX_N); + } + /* Otherwise first fill a block, so that finalize only uses the message */ + else { + memcpy(buf + SPX_N, m, SPX_SHA256_BLOCK_BYTES - SPX_N); + sha256_inc_blocks(state, buf, 1); + + m += SPX_SHA256_BLOCK_BYTES - SPX_N; + mlen -= SPX_SHA256_BLOCK_BYTES - SPX_N; + sha256_inc_finalize(buf + SPX_SHA256_BLOCK_BYTES, state, m, mlen); + } + + for (i = 0; i < SPX_N; i++) { + buf[i] = 0x5c ^ sk_prf[i]; + } + memset(buf + SPX_N, 0x5c, SPX_SHA256_BLOCK_BYTES - SPX_N); + + sha256(buf, buf, SPX_SHA256_BLOCK_BYTES + SPX_SHA256_OUTPUT_BYTES); + memcpy(R, buf, SPX_N); +} + +/** + * Computes the message hash using R, the public key, and the message. + * Outputs the message digest and the index of the leaf. The index is split in + * the tree index and the leaf index, for convenient copying to an address. + */ +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen) { +#define SPX_TREE_BITS (SPX_TREE_HEIGHT * (SPX_D - 1)) +#define SPX_TREE_BYTES ((SPX_TREE_BITS + 7) / 8) +#define SPX_LEAF_BITS SPX_TREE_HEIGHT +#define SPX_LEAF_BYTES ((SPX_LEAF_BITS + 7) / 8) +#define SPX_DGST_BYTES (SPX_FORS_MSG_BYTES + SPX_TREE_BYTES + SPX_LEAF_BYTES) + + unsigned char seed[SPX_SHA256_OUTPUT_BYTES + 4]; + + /* Round to nearest multiple of SPX_SHA256_BLOCK_BYTES */ +#define SPX_INBLOCKS (((SPX_N + SPX_PK_BYTES + SPX_SHA256_BLOCK_BYTES - 1) & \ + -SPX_SHA256_BLOCK_BYTES) / SPX_SHA256_BLOCK_BYTES) + unsigned char inbuf[SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES]; + + unsigned char buf[SPX_DGST_BYTES]; + unsigned char *bufp = buf; + uint8_t state[40]; + + sha256_inc_init(state); + + memcpy(inbuf, R, SPX_N); + memcpy(inbuf + SPX_N, pk, SPX_PK_BYTES); + + /* If R + pk + message cannot fill up an entire block */ + if (SPX_N + SPX_PK_BYTES + mlen < SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES) { + memcpy(inbuf + SPX_N + SPX_PK_BYTES, m, mlen); + sha256_inc_finalize(seed, state, inbuf, SPX_N + SPX_PK_BYTES + mlen); + } + /* Otherwise first fill a block, so that finalize only uses the message */ + else { + memcpy(inbuf + SPX_N + SPX_PK_BYTES, m, + SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES); + sha256_inc_blocks(state, inbuf, SPX_INBLOCKS); + + m += SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES; + mlen -= SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES; + sha256_inc_finalize(seed, state, m, mlen); + } + + /* By doing this in two steps, we prevent hashing the message twice; + otherwise each iteration in MGF1 would hash the message again. */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_mgf1(bufp, SPX_DGST_BYTES, seed, SPX_SHA256_OUTPUT_BYTES); + + memcpy(digest, bufp, SPX_FORS_MSG_BYTES); + bufp += SPX_FORS_MSG_BYTES; + + *tree = PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_bytes_to_ull(bufp, SPX_TREE_BYTES); + *tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS); + bufp += SPX_TREE_BYTES; + + *leaf_idx = (uint32_t)PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_bytes_to_ull( + bufp, SPX_LEAF_BYTES); + *leaf_idx &= (~(uint32_t)0) >> (32 - SPX_LEAF_BITS); +} diff --git a/crypto_sign/sphincs-sha256-256f-robust/clean/params.h b/crypto_sign/sphincs-sha256-256f-robust/clean/params.h new file mode 100644 index 00000000..c2ad0531 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256f-robust/clean/params.h @@ -0,0 +1,53 @@ +#ifndef SPX_PARAMS_H +#define SPX_PARAMS_H + +/* Hash output length in bytes. */ +#define SPX_N 32 +/* Height of the hypertree. */ +#define SPX_FULL_HEIGHT 68 +/* Number of subtree layer. */ +#define SPX_D 17 +/* FORS tree dimensions. */ +#define SPX_FORS_HEIGHT 10 +#define SPX_FORS_TREES 30 +/* Winternitz parameter, */ +#define SPX_WOTS_W 16 + +/* The hash function is defined by linking a different hash.c file, as opposed + to setting a #define constant. */ + +/* For clarity */ +#define SPX_ADDR_BYTES 32 + +/* WOTS parameters. */ +#define SPX_WOTS_LOGW 4 + +#define SPX_WOTS_LEN1 (8 * SPX_N / SPX_WOTS_LOGW) + +/* SPX_WOTS_LEN2 is floor(log(len_1 * (w - 1)) / log(w)) + 1; we precompute */ +#define SPX_WOTS_LEN2 3 + +#define SPX_WOTS_LEN (SPX_WOTS_LEN1 + SPX_WOTS_LEN2) +#define SPX_WOTS_BYTES (SPX_WOTS_LEN * SPX_N) +#define SPX_WOTS_PK_BYTES SPX_WOTS_BYTES + +/* Subtree size. */ +#define SPX_TREE_HEIGHT (SPX_FULL_HEIGHT / SPX_D) + +/* FORS parameters. */ +#define SPX_FORS_MSG_BYTES ((SPX_FORS_HEIGHT * SPX_FORS_TREES + 7) / 8) +#define SPX_FORS_BYTES ((SPX_FORS_HEIGHT + 1) * SPX_FORS_TREES * SPX_N) +#define SPX_FORS_PK_BYTES SPX_N + +/* Resulting SPX sizes. */ +#define SPX_BYTES (SPX_N + SPX_FORS_BYTES + SPX_D * SPX_WOTS_BYTES +\ + SPX_FULL_HEIGHT * SPX_N) +#define SPX_PK_BYTES (2 * SPX_N) +#define SPX_SK_BYTES (2 * SPX_N + SPX_PK_BYTES) + +/* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ +#define SPX_OPTRAND_BYTES 32 + +#endif diff --git a/crypto_sign/sphincs-sha256-256f-robust/clean/sha256.c b/crypto_sign/sphincs-sha256-256f-robust/clean/sha256.c new file mode 100644 index 00000000..294b0de6 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256f-robust/clean/sha256.c @@ -0,0 +1,72 @@ +/* Based on the public domain implementation in + * crypto_hash/sha512/ref/ from http://bench.cr.yp.to/supercop.html + * by D. J. Bernstein */ + +#include +#include +#include + +#include "sha2.h" +#include "sha256.h" +#include "utils.h" + +/* + * Compresses an address to a 22-byte sequence. + * This reduces the number of required SHA256 compression calls, as the last + * block of input is padded with at least 65 bits. + */ +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_compress_address(unsigned char *out, const uint32_t addr[8]) { + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_ull_to_bytes(out, 1, addr[0]); /* drop 3 bytes of the layer field */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_ull_to_bytes(out + 1, 4, addr[2]); /* drop the highest tree address word */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_ull_to_bytes(out + 5, 4, addr[3]); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_ull_to_bytes(out + 9, 1, addr[4]); /* drop 3 bytes of the type field */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_ull_to_bytes(out + 10, 4, addr[5]); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_ull_to_bytes(out + 14, 4, addr[6]); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_ull_to_bytes(out + 18, 4, addr[7]); +} + +/** + * Requires 'input_plus_four_bytes' to have 'inlen' + 4 bytes, so that the last + * four bytes can be used for the counter. Typically 'input' is merely a seed. + * Outputs outlen number of bytes + */ +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + unsigned char *input_plus_four_bytes, unsigned long inlen) { + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + unsigned long i; + + /* While we can fit in at least another full block of SHA256 output.. */ + for (i = 0; (i + 1)*SPX_SHA256_OUTPUT_BYTES <= outlen; i++) { + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_ull_to_bytes(input_plus_four_bytes + inlen, 4, i); + sha256(out, input_plus_four_bytes, inlen + 4); + out += SPX_SHA256_OUTPUT_BYTES; + } + /* Until we cannot anymore, and we fill the remainder. */ + if (outlen > i * SPX_SHA256_OUTPUT_BYTES) { + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_ull_to_bytes(input_plus_four_bytes + inlen, 4, i); + sha256(outbuf, input_plus_four_bytes, inlen + 4); + memcpy(out, outbuf, outlen - i * SPX_SHA256_OUTPUT_BYTES); + } +} + +uint8_t PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_state_seeded[40]; + +/** + * Absorb the constant pub_seed using one round of the compression function + * This initializes state_seeded, which can then be reused in thash + **/ +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_seed_state(const unsigned char *pub_seed) { + uint8_t block[SPX_SHA256_BLOCK_BYTES]; + size_t i; + + for (i = 0; i < SPX_N; ++i) { + block[i] = pub_seed[i]; + } + for (i = SPX_N; i < SPX_SHA256_BLOCK_BYTES; ++i) { + block[i] = 0; + } + + sha256_inc_init(PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_state_seeded); + sha256_inc_blocks(PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_state_seeded, block, 1); +} diff --git a/crypto_sign/sphincs-sha256-256f-robust/clean/sha256.h b/crypto_sign/sphincs-sha256-256f-robust/clean/sha256.h new file mode 100644 index 00000000..a2301daf --- /dev/null +++ b/crypto_sign/sphincs-sha256-256f-robust/clean/sha256.h @@ -0,0 +1,21 @@ +#ifndef SPX_SHA256_H +#define SPX_SHA256_H + +#define SPX_SHA256_BLOCK_BYTES 64 +#define SPX_SHA256_OUTPUT_BYTES 32 /* This does not necessarily equal SPX_N */ +#define SPX_SHA256_ADDR_BYTES 22 + +#include +#include + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_compress_address(unsigned char *out, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + unsigned char *input_plus_four_bytes, unsigned long inlen); + +uint8_t PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_state_seeded[40]; + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_seed_state(const unsigned char *pub_seed); + +#endif diff --git a/crypto_sign/sphincs-sha256-256f-robust/clean/sign.c b/crypto_sign/sphincs-sha256-256f-robust/clean/sign.c new file mode 100644 index 00000000..8577b14c --- /dev/null +++ b/crypto_sign/sphincs-sha256-256f-robust/clean/sign.c @@ -0,0 +1,344 @@ +#include +#include +#include + +#include "address.h" +#include "api.h" +#include "fors.h" +#include "hash.h" +#include "params.h" +#include "randombytes.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +/** + * Computes the leaf at a given address. First generates the WOTS key pair, + * then computes leaf by hashing horizontally. + */ +static void wots_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t tree_addr[8]) { + unsigned char pk[SPX_WOTS_BYTES]; + uint32_t wots_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_keypair_addr( + wots_addr, addr_idx); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_wots_gen_pk( + pk, sk_seed, pub_seed, wots_addr); + + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_thash_WOTS_LEN( + leaf, pk, pub_seed, wots_pk_addr); +} + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_crypto_sign_secretkeybytes(void) { + return PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_CRYPTO_SECRETKEYBYTES; +} + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_crypto_sign_publickeybytes(void) { + return PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES; +} + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_crypto_sign_bytes(void) { + return PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_CRYPTO_BYTES; +} + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_crypto_sign_seedbytes(void) { + return PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_CRYPTO_SEEDBYTES; +} + +/* + * Generates an SPX key pair given a seed of length + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed) { + /* We do not need the auth path in key generation, but it simplifies the + code to have just one treehash routine that computes both root and path + in one function. */ + unsigned char auth_path[SPX_TREE_HEIGHT * SPX_N]; + uint32_t top_tree_addr[8] = {0}; + + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_layer_addr( + top_tree_addr, SPX_D - 1); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_type( + top_tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */ + memcpy(sk, seed, PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_CRYPTO_SEEDBYTES); + + memcpy(pk, sk + 2 * SPX_N, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_initialize_hash_function(pk, sk); + + /* Compute root node of the top-most subtree. */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_treehash_TREE_HEIGHT( + sk + 3 * SPX_N, auth_path, sk, sk + 2 * SPX_N, 0, 0, + wots_gen_leaf, top_tree_addr); + + memcpy(pk + SPX_N, sk + 3 * SPX_N, SPX_N); + + return 0; +} + +/* + * Generates an SPX key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk) { + unsigned char seed[PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_CRYPTO_SEEDBYTES]; + randombytes(seed, PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_CRYPTO_SEEDBYTES); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_crypto_sign_seed_keypair( + pk, sk, seed); + + return 0; +} + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + const unsigned char *sk_seed = sk; + const unsigned char *sk_prf = sk + SPX_N; + const unsigned char *pk = sk + 2 * SPX_N; + const unsigned char *pub_seed = pk; + + unsigned char optrand[SPX_N]; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char root[SPX_N]; + uint32_t i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_initialize_hash_function( + pub_seed, sk_seed); + + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ + randombytes(optrand, SPX_N); + /* Compute the digest randomization value. */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_gen_message_random( + sig, sk_prf, optrand, m, mlen); + + /* Derive the message digest and leaf index from R, PK and M. */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Sign the message hash using FORS. */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_fors_sign( + sig, root, mhash, sk_seed, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Compute a WOTS signature. */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_wots_sign( + sig, root, sk_seed, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the authentication path for the used WOTS leaf. */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_treehash_TREE_HEIGHT( + root, sig, sk_seed, pub_seed, idx_leaf, 0, + wots_gen_leaf, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + *siglen = SPX_BYTES; + + return 0; +} + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk) { + const unsigned char *pub_seed = pk; + const unsigned char *pub_root = pk + SPX_N; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char wots_pk[SPX_WOTS_BYTES]; + unsigned char root[SPX_N]; + unsigned char leaf[SPX_N]; + unsigned int i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + if (siglen != SPX_BYTES) { + return -1; + } + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_initialize_hash_function( + pub_seed, NULL); + + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + /* Derive the message digest and leaf index from R || PK || M. */ + /* The additional SPX_N is a result of the hash domain separator. */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + /* Layer correctly defaults to 0, so no need to set_layer_addr */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_fors_pk_from_sig( + root, sig, mhash, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + /* For each subtree.. */ + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + + /* The WOTS public key is only correct if the signature was correct. */ + /* Initially, root is the FORS pk, but on subsequent iterations it is + the root of the subtree below the currently processed subtree. */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_wots_pk_from_sig( + wots_pk, sig, root, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the leaf node using the WOTS public key. */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_thash_WOTS_LEN( + leaf, wots_pk, pub_seed, wots_pk_addr); + + /* Compute the root node of this subtree. */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_compute_root( + root, leaf, idx_leaf, 0, sig, SPX_TREE_HEIGHT, + pub_seed, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + /* Check if the root node equals the root node in the public key. */ + if (memcmp(root, pub_root, SPX_N) != 0) { + return -1; + } + + return 0; +} + + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + size_t siglen; + + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_crypto_sign_signature( + sm, &siglen, m, mlen, sk); + + memmove(sm + SPX_BYTES, m, mlen); + *smlen = siglen + mlen; + + return 0; +} + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk) { + /* The API caller does not necessarily know what size a signature should be + but SPHINCS+ signatures are always exactly SPX_BYTES. */ + if (smlen < SPX_BYTES) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + *mlen = smlen - SPX_BYTES; + + if (PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_crypto_sign_verify( + sm, SPX_BYTES, sm + SPX_BYTES, *mlen, pk)) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + /* If verification was successful, move the message to the right place. */ + memmove(m, sm + SPX_BYTES, *mlen); + + return 0; +} diff --git a/crypto_sign/sphincs-sha256-256f-robust/clean/thash.h b/crypto_sign/sphincs-sha256-256f-robust/clean/thash.h new file mode 100644 index 00000000..12bd7af2 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256f-robust/clean/thash.h @@ -0,0 +1,22 @@ +#ifndef SPX_THASH_H +#define SPX_THASH_H + +#include + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-sha256-256f-robust/clean/thash_sha256_robust.c b/crypto_sign/sphincs-sha256-256f-robust/clean/thash_sha256_robust.c new file mode 100644 index 00000000..37f81ded --- /dev/null +++ b/crypto_sign/sphincs-sha256-256f-robust/clean/thash_sha256_robust.c @@ -0,0 +1,77 @@ +#include +#include + +#include "address.h" +#include "params.h" +#include "thash.h" + +#include "sha2.h" +#include "sha256.h" + +/** + * Takes an array of inblocks concatenated arrays of SPX_N bytes. + */ +static void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_thash( + unsigned char *out, unsigned char *buf, + const unsigned char *in, unsigned int inblocks, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + unsigned char *bitmask = buf + SPX_N + SPX_SHA256_ADDR_BYTES + 4; + uint8_t sha2_state[40]; + unsigned int i; + + memcpy(buf, pub_seed, SPX_N); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_compress_address(buf + SPX_N, addr); + /* MGF1 requires us to have 4 extra bytes in 'buf' */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_mgf1(bitmask, inblocks * SPX_N, buf, SPX_N + SPX_SHA256_ADDR_BYTES); + + /* Retrieve precomputed state containing pub_seed */ + memcpy(sha2_state, PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_state_seeded, 40 * sizeof(uint8_t)); + + for (i = 0; i < inblocks * SPX_N; i++) { + buf[SPX_N + SPX_SHA256_ADDR_BYTES + i] = in[i] ^ bitmask[i]; + } + + sha256_inc_finalize(outbuf, sha2_state, buf + SPX_N, + SPX_SHA256_ADDR_BYTES + inblocks * SPX_N); + memcpy(out, outbuf, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES + 4 + 1 * SPX_N]; + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_thash( + out, buf, in, 1, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES + 4 + 2 * SPX_N]; + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_thash( + out, buf, in, 2, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES + 4 + SPX_WOTS_LEN * SPX_N]; + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_thash( + out, buf, in, SPX_WOTS_LEN, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES + 4 + SPX_FORS_TREES * SPX_N]; + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_thash( + out, buf, in, SPX_FORS_TREES, pub_seed, addr); +} diff --git a/crypto_sign/sphincs-sha256-256f-robust/clean/utils.c b/crypto_sign/sphincs-sha256-256f-robust/clean/utils.c new file mode 100644 index 00000000..f81551ac --- /dev/null +++ b/crypto_sign/sphincs-sha256-256f-robust/clean/utils.c @@ -0,0 +1,192 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in) { + + /* Iterate over out in decreasing order, for big-endianness. */ + for (size_t i = outlen; i > 0; i--) { + out[i - 1] = in & 0xff; + in = in >> 8; + } +} + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen) { + unsigned long long retval = 0; + + for (size_t i = 0; i < inlen; i++) { + retval |= ((unsigned long long)in[i]) << (8 * (inlen - 1 - i)); + } + return retval; +} + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + unsigned char buffer[2 * SPX_N]; + + /* If leaf_idx is odd (last bit = 1), current path element is a right child + and auth_path has to go left. Otherwise it is the other way around. */ + if (leaf_idx & 1) { + memcpy(buffer + SPX_N, leaf, SPX_N); + memcpy(buffer, auth_path, SPX_N); + } else { + memcpy(buffer, leaf, SPX_N); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + + for (i = 0; i < tree_height - 1; i++) { + leaf_idx >>= 1; + idx_offset >>= 1; + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_tree_height(addr, i + 1); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + + /* Pick the right or left neighbor, depending on parity of the node. */ + if (leaf_idx & 1) { + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_thash_2( + buffer + SPX_N, buffer, pub_seed, addr); + memcpy(buffer, auth_path, SPX_N); + } else { + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_thash_2( + buffer, buffer, pub_seed, addr); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + } + + /* The last iteration is exceptional; we do not copy an auth_path node. */ + leaf_idx >>= 1; + idx_offset >>= 1; + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_tree_height(addr, tree_height); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_thash_2( + root, buffer, pub_seed, addr); +} + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +static void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_treehash( + unsigned char *root, unsigned char *auth_path, + unsigned char *stack, unsigned int *heights, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned int offset = 0; + uint32_t idx; + uint32_t tree_idx; + + for (idx = 0; idx < (uint32_t)(1 << tree_height); idx++) { + /* Add the next leaf node to the stack. */ + gen_leaf(stack + offset * SPX_N, + sk_seed, pub_seed, idx + idx_offset, tree_addr); + offset++; + heights[offset - 1] = 0; + + /* If this is a node we need for the auth path.. */ + if ((leaf_idx ^ 0x1) == idx) { + memcpy(auth_path, stack + (offset - 1)*SPX_N, SPX_N); + } + + /* While the top-most nodes are of equal height.. */ + while (offset >= 2 && heights[offset - 1] == heights[offset - 2]) { + /* Compute index of the new node, in the next layer. */ + tree_idx = (idx >> (heights[offset - 1] + 1)); + + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_tree_height( + tree_addr, heights[offset - 1] + 1); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_tree_index( + tree_addr, tree_idx + (idx_offset >> (heights[offset - 1] + 1))); + /* Hash the top-most nodes from the stack together. */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_thash_2( + stack + (offset - 2)*SPX_N, stack + (offset - 2)*SPX_N, + pub_seed, tree_addr); + offset--; + /* Note that the top-most node is now one layer higher. */ + heights[offset - 1]++; + + /* If this is a node we need for the auth path.. */ + if (((leaf_idx >> heights[offset - 1]) ^ 0x1) == tree_idx) { + memcpy(auth_path + heights[offset - 1]*SPX_N, + stack + (offset - 1)*SPX_N, SPX_N); + } + } + } + memcpy(root, stack, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_FORS_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_FORS_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_FORS_HEIGHT, gen_leaf, tree_addr); +} + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_TREE_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_TREE_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_TREE_HEIGHT, gen_leaf, tree_addr); +} diff --git a/crypto_sign/sphincs-sha256-256f-robust/clean/utils.h b/crypto_sign/sphincs-sha256-256f-robust/clean/utils.h new file mode 100644 index 00000000..42ef914a --- /dev/null +++ b/crypto_sign/sphincs-sha256-256f-robust/clean/utils.h @@ -0,0 +1,60 @@ +#ifndef SPX_UTILS_H +#define SPX_UTILS_H + +#include "params.h" +#include +#include + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in); + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen); + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-sha256-256f-robust/clean/wots.c b/crypto_sign/sphincs-sha256-256f-robust/clean/wots.c new file mode 100644 index 00000000..b7dfb359 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256f-robust/clean/wots.c @@ -0,0 +1,161 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +// TODO clarify address expectations, and make them more uniform. +// TODO i.e. do we expect types to be set already? +// TODO and do we expect modifications or copies? + +/** + * Computes the starting value for a chain, i.e. the secret key. + * Expects the address to be complete up to the chain address. + */ +static void wots_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t wots_addr[8]) { + /* Make sure that the hash address is actually zeroed. */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_hash_addr(wots_addr, 0); + + /* Generate sk element. */ + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_prf_addr(sk, sk_seed, wots_addr); +} + +/** + * Computes the chaining function. + * out and in have to be n-byte arrays. + * + * Interprets in as start-th value of the chain. + * addr has to contain the address of the chain. + */ +static void gen_chain(unsigned char *out, const unsigned char *in, + unsigned int start, unsigned int steps, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + /* Initialize out with the value at position 'start'. */ + memcpy(out, in, SPX_N); + + /* Iterate 'steps' calls to the hash function. */ + for (i = start; i < (start + steps) && i < SPX_WOTS_W; i++) { + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_hash_addr(addr, i); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_thash_1( + out, out, pub_seed, addr); + } +} + +/** + * base_w algorithm as described in draft. + * Interprets an array of bytes as integers in base w. + * This only works when log_w is a divisor of 8. + */ +static void base_w(unsigned int *output, const size_t out_len, + const unsigned char *input) { + size_t in = 0; + size_t out = 0; + unsigned char total = 0; + unsigned int bits = 0; + size_t consumed; + + for (consumed = 0; consumed < out_len; consumed++) { + if (bits == 0) { + total = input[in]; + in++; + bits += 8; + } + bits -= SPX_WOTS_LOGW; + output[out] = (unsigned int)((total >> bits) & (SPX_WOTS_W - 1)); + out++; + } +} + +/* Computes the WOTS+ checksum over a message (in base_w). */ +static void wots_checksum(unsigned int *csum_base_w, + const unsigned int *msg_base_w) { + unsigned int csum = 0; + unsigned char csum_bytes[(SPX_WOTS_LEN2 * SPX_WOTS_LOGW + 7) / 8]; + unsigned int i; + + /* Compute checksum. */ + for (i = 0; i < SPX_WOTS_LEN1; i++) { + csum += SPX_WOTS_W - 1 - msg_base_w[i]; + } + + /* Convert checksum to base_w. */ + /* Make sure expected empty zero bits are the least significant bits. */ + csum = csum << (8 - ((SPX_WOTS_LEN2 * SPX_WOTS_LOGW) % 8)); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_ull_to_bytes( + csum_bytes, sizeof(csum_bytes), csum); + base_w(csum_base_w, SPX_WOTS_LEN2, csum_bytes); +} + +/* Takes a message and derives the matching chain lengths. */ +static void chain_lengths(unsigned int *lengths, const unsigned char *msg) { + base_w(lengths, SPX_WOTS_LEN1, msg); + wots_checksum(lengths + SPX_WOTS_LEN1, lengths); +} + +/** + * WOTS key generation. Takes a 32 byte sk_seed, expands it to WOTS private key + * elements and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(pk + i * SPX_N, sk_seed, addr); + gen_chain(pk + i * SPX_N, pk + i * SPX_N, + 0, SPX_WOTS_W - 1, pub_seed, addr); + } +} + +/** + * Takes a n-byte message and the 32-byte sk_see to compute a signature 'sig'. + */ +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(sig + i * SPX_N, sk_seed, addr); + gen_chain(sig + i * SPX_N, sig + i * SPX_N, 0, lengths[i], pub_seed, addr); + } +} + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_set_chain_addr(addr, i); + gen_chain(pk + i * SPX_N, sig + i * SPX_N, + lengths[i], SPX_WOTS_W - 1 - lengths[i], pub_seed, addr); + } +} diff --git a/crypto_sign/sphincs-sha256-256f-robust/clean/wots.h b/crypto_sign/sphincs-sha256-256f-robust/clean/wots.h new file mode 100644 index 00000000..d7c72f6e --- /dev/null +++ b/crypto_sign/sphincs-sha256-256f-robust/clean/wots.h @@ -0,0 +1,38 @@ +#ifndef SPX_WOTS_H +#define SPX_WOTS_H + +#include "params.h" +#include + +/** + * WOTS key generation. Takes a 32 byte seed for the private key, expands it to + * a full WOTS private key and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * Takes a n-byte message and the 32-byte seed for the private key to compute a + * signature that is placed at 'sig'. + */ +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]); + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-sha256-256f-simple/META.yml b/crypto_sign/sphincs-sha256-256f-simple/META.yml new file mode 100644 index 00000000..0a52e059 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256f-simple/META.yml @@ -0,0 +1,27 @@ +name: SPHINCS+ +type: signature +claimed-nist-level: 5 +length-public-key: 64 +length-secret-key: 128 +length-signature: 49216 +testvectors-sha256: b4755edf8351c51225921af38a724d2bd9ff9f3afe4ae2abbc3a59763ecc897d +principal-submitter: Andreas Hülsing +auxiliary-submitters: + - Jean-Philippe Aumasson + - Daniel J. Bernstein, + - Christoph Dobraunig + - Maria Eichlseder + - Scott Fluhrer + - Stefan-Lukas Gazdag + - Panos Kampanakis + - Stefan Kölbl + - Tanja Lange + - Martin M. Lauridsen + - Florian Mendel + - Ruben Niederhagen + - Christian Rechberger + - Joost Rijneveld + - Peter Schwabe +implementations: + - name: clean + version: https://github.com/sphincs/sphincsplus/commit/77755c94d0bc744478044d6efbb888dc13156441 diff --git a/crypto_sign/sphincs-sha256-256f-simple/clean/LICENSE b/crypto_sign/sphincs-sha256-256f-simple/clean/LICENSE new file mode 100644 index 00000000..670154e3 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256f-simple/clean/LICENSE @@ -0,0 +1,116 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + diff --git a/crypto_sign/sphincs-sha256-256f-simple/clean/Makefile b/crypto_sign/sphincs-sha256-256f-simple/clean/Makefile new file mode 100644 index 00000000..df2ed05c --- /dev/null +++ b/crypto_sign/sphincs-sha256-256f-simple/clean/Makefile @@ -0,0 +1,20 @@ +# This Makefile can be used with GNU Make or BSD Make + +LIB=libsphincs-sha256-256f-simple_clean.a + +HEADERS = params.h address.h wots.h utils.h fors.h api.h hash.h thash.h sha256.h +OBJECTS = address.o wots.o utils.o fors.o sign.o hash_sha256.o thash_sha256_simple.o sha256.o + +CFLAGS=-O3 -Wall -Wconversion -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) + +all: $(LIB) + +%.o: %.c $(HEADERS) + $(CC) $(CFLAGS) -c -o $@ $< + +$(LIB): $(OBJECTS) + $(AR) -r $@ $(OBJECTS) + +clean: + $(RM) $(OBJECTS) + $(RM) $(LIB) diff --git a/crypto_sign/sphincs-sha256-256f-simple/clean/Makefile.Microsoft_nmake b/crypto_sign/sphincs-sha256-256f-simple/clean/Makefile.Microsoft_nmake new file mode 100644 index 00000000..6a540acd --- /dev/null +++ b/crypto_sign/sphincs-sha256-256f-simple/clean/Makefile.Microsoft_nmake @@ -0,0 +1,19 @@ +# This Makefile can be used with Microsoft Visual Studio's nmake using the command: +# nmake /f Makefile.Microsoft_nmake + +LIBRARY=libsphincs-sha256-256f-simple_clean.lib +OBJECTS=address.obj wots.obj utils.obj fors.obj sign.obj hash_sha256.obj thash_sha256_simple.obj sha256.obj + +CFLAGS=/nologo /I ..\..\..\common /W4 /WX + +all: $(LIBRARY) + +# Make sure objects are recompiled if headers change. +$(OBJECTS): *.h + +$(LIBRARY): $(OBJECTS) + LIB.EXE /NOLOGO /WX /OUT:$@ $** + +clean: + -DEL $(OBJECTS) + -DEL $(LIBRARY) diff --git a/crypto_sign/sphincs-sha256-256f-simple/clean/address.c b/crypto_sign/sphincs-sha256-256f-simple/clean/address.c new file mode 100644 index 00000000..29f4ff70 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256f-simple/clean/address.c @@ -0,0 +1,78 @@ +#include + +#include "address.h" +#include "params.h" +#include "utils.h" + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]) { + int i; + + for (i = 0; i < 8; i++) { + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_ull_to_bytes( + bytes + i * 4, 4, addr[i]); + } +} + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer) { + addr[0] = layer; +} + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree) { + addr[1] = 0; + addr[2] = (uint32_t) (tree >> 32); + addr[3] = (uint32_t) tree; +} + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_type( + uint32_t addr[8], uint32_t type) { + addr[4] = type; +} + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; +} + +/* These functions are used for OTS addresses. */ + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair) { + addr[5] = keypair; +} + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; + out[5] = in[5]; +} + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain) { + addr[6] = chain; +} + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash) { + addr[7] = hash; +} + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height) { + addr[6] = tree_height; +} + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index) { + addr[7] = tree_index; +} diff --git a/crypto_sign/sphincs-sha256-256f-simple/clean/address.h b/crypto_sign/sphincs-sha256-256f-simple/clean/address.h new file mode 100644 index 00000000..ee6e9cd9 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256f-simple/clean/address.h @@ -0,0 +1,50 @@ +#ifndef SPX_ADDRESS_H +#define SPX_ADDRESS_H + +#include + +#define SPX_ADDR_TYPE_WOTS 0 +#define SPX_ADDR_TYPE_WOTSPK 1 +#define SPX_ADDR_TYPE_HASHTREE 2 +#define SPX_ADDR_TYPE_FORSTREE 3 +#define SPX_ADDR_TYPE_FORSPK 4 + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer); + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree); + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_type( + uint32_t addr[8], uint32_t type); + +/* Copies the layer and tree part of one address into the other */ +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for WOTS and FORS addresses. */ + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair); + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain); + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash); + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height); + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index); + +#endif diff --git a/crypto_sign/sphincs-sha256-256f-simple/clean/api.h b/crypto_sign/sphincs-sha256-256f-simple/clean/api.h new file mode 100644 index 00000000..c40d207f --- /dev/null +++ b/crypto_sign/sphincs-sha256-256f-simple/clean/api.h @@ -0,0 +1,78 @@ +#ifndef PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_API_H +#define PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_API_H + +#include +#include + +#define PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_CRYPTO_ALGNAME "SPHINCS+" + +#define PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_CRYPTO_SECRETKEYBYTES 128 +#define PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES 64 +#define PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_CRYPTO_BYTES 49216 +#define PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_CRYPTO_SEEDBYTES 96 + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_crypto_sign_secretkeybytes(void); + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_crypto_sign_publickeybytes(void); + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_crypto_sign_bytes(void); + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_crypto_sign_seedbytes(void); + +/* + * Generates a SPHINCS+ key pair given a seed. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed); + +/* + * Generates a SPHINCS+ key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk); + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk); + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk); + +#endif diff --git a/crypto_sign/sphincs-sha256-256f-simple/clean/fors.c b/crypto_sign/sphincs-sha256-256f-simple/clean/fors.c new file mode 100644 index 00000000..7a037880 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256f-simple/clean/fors.c @@ -0,0 +1,164 @@ +#include +#include +#include + +#include "address.h" +#include "fors.h" +#include "hash.h" +#include "thash.h" +#include "utils.h" + +static void fors_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_prf_addr( + sk, sk_seed, fors_leaf_addr); +} + +static void fors_sk_to_leaf(unsigned char *leaf, const unsigned char *sk, + const unsigned char *pub_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_thash_1( + leaf, sk, pub_seed, fors_leaf_addr); +} + +static void fors_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t fors_tree_addr[8]) { + uint32_t fors_leaf_addr[8] = {0}; + + /* Only copy the parts that must be kept in fors_leaf_addr. */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_copy_keypair_addr( + fors_leaf_addr, fors_tree_addr); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_type( + fors_leaf_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_tree_index( + fors_leaf_addr, addr_idx); + + fors_gen_sk(leaf, sk_seed, fors_leaf_addr); + fors_sk_to_leaf(leaf, leaf, pub_seed, fors_leaf_addr); +} + +/** + * Interprets m as SPX_FORS_HEIGHT-bit unsigned integers. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + * Assumes indices has space for SPX_FORS_TREES integers. + */ +static void message_to_indices(uint32_t *indices, const unsigned char *m) { + unsigned int i, j; + unsigned int offset = 0; + + for (i = 0; i < SPX_FORS_TREES; i++) { + indices[i] = 0; + for (j = 0; j < SPX_FORS_HEIGHT; j++) { + indices[i] ^= (((uint32_t)m[offset >> 3] >> (offset & 0x7)) & 0x1) << j; + offset++; + } + } +} + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Include the secret key part that produces the selected leaf node. */ + fors_gen_sk(sig, sk_seed, fors_tree_addr); + sig += SPX_N; + + /* Compute the authentication path for this leaf node. */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_treehash_FORS_HEIGHT( + roots + i * SPX_N, sig, sk_seed, pub_seed, + indices[i], idx_offset, fors_gen_leaf, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + unsigned char leaf[SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Derive the leaf from the included secret key part. */ + fors_sk_to_leaf(leaf, sig, pub_seed, fors_tree_addr); + sig += SPX_N; + + /* Derive the corresponding root node of this tree. */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_compute_root( + roots + i * SPX_N, leaf, indices[i], idx_offset, sig, + SPX_FORS_HEIGHT, pub_seed, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} diff --git a/crypto_sign/sphincs-sha256-256f-simple/clean/fors.h b/crypto_sign/sphincs-sha256-256f-simple/clean/fors.h new file mode 100644 index 00000000..c41017b3 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256f-simple/clean/fors.h @@ -0,0 +1,30 @@ +#ifndef SPX_FORS_H +#define SPX_FORS_H + +#include + +#include "params.h" + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]); + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-sha256-256f-simple/clean/hash.h b/crypto_sign/sphincs-sha256-256f-simple/clean/hash.h new file mode 100644 index 00000000..a79a28ed --- /dev/null +++ b/crypto_sign/sphincs-sha256-256f-simple/clean/hash.h @@ -0,0 +1,22 @@ +#ifndef SPX_HASH_H +#define SPX_HASH_H + +#include + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed); + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen); + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen); + +#endif diff --git a/crypto_sign/sphincs-sha256-256f-simple/clean/hash_sha256.c b/crypto_sign/sphincs-sha256-256f-simple/clean/hash_sha256.c new file mode 100644 index 00000000..f8c840fb --- /dev/null +++ b/crypto_sign/sphincs-sha256-256f-simple/clean/hash_sha256.c @@ -0,0 +1,148 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "utils.h" + +#include "sha2.h" +#include "sha256.h" + +/* For SHA256, there is no immediate reason to initialize at the start, + so this function is an empty operation. */ +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed) { + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_seed_state(pub_seed); + (void)sk_seed; /* Suppress an 'unused parameter' warning. */ +} + +/* + * Computes PRF(key, addr), given a secret key of SPX_N bytes and an address + */ +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]) { + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES]; + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + + memcpy(buf, key, SPX_N); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_compress_address(buf + SPX_N, addr); + + sha256(outbuf, buf, SPX_N + SPX_SHA256_ADDR_BYTES); + memcpy(out, outbuf, SPX_N); +} + +/** + * Computes the message-dependent randomness R, using a secret seed as a key + * for HMAC, and an optional randomization value prefixed to the message. + * This requires m to have at least SPX_SHA256_BLOCK_BYTES + SPX_N space + * available in front of the pointer, i.e. before the message to use for the + * prefix. This is necessary to prevent having to move the message around (and + * allocate memory for it). + */ +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen) { + unsigned char buf[SPX_SHA256_BLOCK_BYTES + SPX_SHA256_OUTPUT_BYTES]; + uint8_t state[40]; + int i; + + /* This implements HMAC-SHA256 */ + for (i = 0; i < SPX_N; i++) { + buf[i] = 0x36 ^ sk_prf[i]; + } + memset(buf + SPX_N, 0x36, SPX_SHA256_BLOCK_BYTES - SPX_N); + + sha256_inc_init(state); + sha256_inc_blocks(state, buf, 1); + + memcpy(buf, optrand, SPX_N); + + /* If optrand + message cannot fill up an entire block */ + if (SPX_N + mlen < SPX_SHA256_BLOCK_BYTES) { + memcpy(buf + SPX_N, m, mlen); + sha256_inc_finalize(buf + SPX_SHA256_BLOCK_BYTES, state, + buf, mlen + SPX_N); + } + /* Otherwise first fill a block, so that finalize only uses the message */ + else { + memcpy(buf + SPX_N, m, SPX_SHA256_BLOCK_BYTES - SPX_N); + sha256_inc_blocks(state, buf, 1); + + m += SPX_SHA256_BLOCK_BYTES - SPX_N; + mlen -= SPX_SHA256_BLOCK_BYTES - SPX_N; + sha256_inc_finalize(buf + SPX_SHA256_BLOCK_BYTES, state, m, mlen); + } + + for (i = 0; i < SPX_N; i++) { + buf[i] = 0x5c ^ sk_prf[i]; + } + memset(buf + SPX_N, 0x5c, SPX_SHA256_BLOCK_BYTES - SPX_N); + + sha256(buf, buf, SPX_SHA256_BLOCK_BYTES + SPX_SHA256_OUTPUT_BYTES); + memcpy(R, buf, SPX_N); +} + +/** + * Computes the message hash using R, the public key, and the message. + * Outputs the message digest and the index of the leaf. The index is split in + * the tree index and the leaf index, for convenient copying to an address. + */ +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen) { +#define SPX_TREE_BITS (SPX_TREE_HEIGHT * (SPX_D - 1)) +#define SPX_TREE_BYTES ((SPX_TREE_BITS + 7) / 8) +#define SPX_LEAF_BITS SPX_TREE_HEIGHT +#define SPX_LEAF_BYTES ((SPX_LEAF_BITS + 7) / 8) +#define SPX_DGST_BYTES (SPX_FORS_MSG_BYTES + SPX_TREE_BYTES + SPX_LEAF_BYTES) + + unsigned char seed[SPX_SHA256_OUTPUT_BYTES + 4]; + + /* Round to nearest multiple of SPX_SHA256_BLOCK_BYTES */ +#define SPX_INBLOCKS (((SPX_N + SPX_PK_BYTES + SPX_SHA256_BLOCK_BYTES - 1) & \ + -SPX_SHA256_BLOCK_BYTES) / SPX_SHA256_BLOCK_BYTES) + unsigned char inbuf[SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES]; + + unsigned char buf[SPX_DGST_BYTES]; + unsigned char *bufp = buf; + uint8_t state[40]; + + sha256_inc_init(state); + + memcpy(inbuf, R, SPX_N); + memcpy(inbuf + SPX_N, pk, SPX_PK_BYTES); + + /* If R + pk + message cannot fill up an entire block */ + if (SPX_N + SPX_PK_BYTES + mlen < SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES) { + memcpy(inbuf + SPX_N + SPX_PK_BYTES, m, mlen); + sha256_inc_finalize(seed, state, inbuf, SPX_N + SPX_PK_BYTES + mlen); + } + /* Otherwise first fill a block, so that finalize only uses the message */ + else { + memcpy(inbuf + SPX_N + SPX_PK_BYTES, m, + SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES); + sha256_inc_blocks(state, inbuf, SPX_INBLOCKS); + + m += SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES; + mlen -= SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES; + sha256_inc_finalize(seed, state, m, mlen); + } + + /* By doing this in two steps, we prevent hashing the message twice; + otherwise each iteration in MGF1 would hash the message again. */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_mgf1(bufp, SPX_DGST_BYTES, seed, SPX_SHA256_OUTPUT_BYTES); + + memcpy(digest, bufp, SPX_FORS_MSG_BYTES); + bufp += SPX_FORS_MSG_BYTES; + + *tree = PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_bytes_to_ull(bufp, SPX_TREE_BYTES); + *tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS); + bufp += SPX_TREE_BYTES; + + *leaf_idx = (uint32_t)PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_bytes_to_ull( + bufp, SPX_LEAF_BYTES); + *leaf_idx &= (~(uint32_t)0) >> (32 - SPX_LEAF_BITS); +} diff --git a/crypto_sign/sphincs-sha256-256f-simple/clean/params.h b/crypto_sign/sphincs-sha256-256f-simple/clean/params.h new file mode 100644 index 00000000..c2ad0531 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256f-simple/clean/params.h @@ -0,0 +1,53 @@ +#ifndef SPX_PARAMS_H +#define SPX_PARAMS_H + +/* Hash output length in bytes. */ +#define SPX_N 32 +/* Height of the hypertree. */ +#define SPX_FULL_HEIGHT 68 +/* Number of subtree layer. */ +#define SPX_D 17 +/* FORS tree dimensions. */ +#define SPX_FORS_HEIGHT 10 +#define SPX_FORS_TREES 30 +/* Winternitz parameter, */ +#define SPX_WOTS_W 16 + +/* The hash function is defined by linking a different hash.c file, as opposed + to setting a #define constant. */ + +/* For clarity */ +#define SPX_ADDR_BYTES 32 + +/* WOTS parameters. */ +#define SPX_WOTS_LOGW 4 + +#define SPX_WOTS_LEN1 (8 * SPX_N / SPX_WOTS_LOGW) + +/* SPX_WOTS_LEN2 is floor(log(len_1 * (w - 1)) / log(w)) + 1; we precompute */ +#define SPX_WOTS_LEN2 3 + +#define SPX_WOTS_LEN (SPX_WOTS_LEN1 + SPX_WOTS_LEN2) +#define SPX_WOTS_BYTES (SPX_WOTS_LEN * SPX_N) +#define SPX_WOTS_PK_BYTES SPX_WOTS_BYTES + +/* Subtree size. */ +#define SPX_TREE_HEIGHT (SPX_FULL_HEIGHT / SPX_D) + +/* FORS parameters. */ +#define SPX_FORS_MSG_BYTES ((SPX_FORS_HEIGHT * SPX_FORS_TREES + 7) / 8) +#define SPX_FORS_BYTES ((SPX_FORS_HEIGHT + 1) * SPX_FORS_TREES * SPX_N) +#define SPX_FORS_PK_BYTES SPX_N + +/* Resulting SPX sizes. */ +#define SPX_BYTES (SPX_N + SPX_FORS_BYTES + SPX_D * SPX_WOTS_BYTES +\ + SPX_FULL_HEIGHT * SPX_N) +#define SPX_PK_BYTES (2 * SPX_N) +#define SPX_SK_BYTES (2 * SPX_N + SPX_PK_BYTES) + +/* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ +#define SPX_OPTRAND_BYTES 32 + +#endif diff --git a/crypto_sign/sphincs-sha256-256f-simple/clean/sha256.c b/crypto_sign/sphincs-sha256-256f-simple/clean/sha256.c new file mode 100644 index 00000000..13d1aece --- /dev/null +++ b/crypto_sign/sphincs-sha256-256f-simple/clean/sha256.c @@ -0,0 +1,72 @@ +/* Based on the public domain implementation in + * crypto_hash/sha512/ref/ from http://bench.cr.yp.to/supercop.html + * by D. J. Bernstein */ + +#include +#include +#include + +#include "sha2.h" +#include "sha256.h" +#include "utils.h" + +/* + * Compresses an address to a 22-byte sequence. + * This reduces the number of required SHA256 compression calls, as the last + * block of input is padded with at least 65 bits. + */ +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_compress_address(unsigned char *out, const uint32_t addr[8]) { + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_ull_to_bytes(out, 1, addr[0]); /* drop 3 bytes of the layer field */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_ull_to_bytes(out + 1, 4, addr[2]); /* drop the highest tree address word */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_ull_to_bytes(out + 5, 4, addr[3]); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_ull_to_bytes(out + 9, 1, addr[4]); /* drop 3 bytes of the type field */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_ull_to_bytes(out + 10, 4, addr[5]); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_ull_to_bytes(out + 14, 4, addr[6]); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_ull_to_bytes(out + 18, 4, addr[7]); +} + +/** + * Requires 'input_plus_four_bytes' to have 'inlen' + 4 bytes, so that the last + * four bytes can be used for the counter. Typically 'input' is merely a seed. + * Outputs outlen number of bytes + */ +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + unsigned char *input_plus_four_bytes, unsigned long inlen) { + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + unsigned long i; + + /* While we can fit in at least another full block of SHA256 output.. */ + for (i = 0; (i + 1)*SPX_SHA256_OUTPUT_BYTES <= outlen; i++) { + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_ull_to_bytes(input_plus_four_bytes + inlen, 4, i); + sha256(out, input_plus_four_bytes, inlen + 4); + out += SPX_SHA256_OUTPUT_BYTES; + } + /* Until we cannot anymore, and we fill the remainder. */ + if (outlen > i * SPX_SHA256_OUTPUT_BYTES) { + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_ull_to_bytes(input_plus_four_bytes + inlen, 4, i); + sha256(outbuf, input_plus_four_bytes, inlen + 4); + memcpy(out, outbuf, outlen - i * SPX_SHA256_OUTPUT_BYTES); + } +} + +uint8_t PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_state_seeded[40]; + +/** + * Absorb the constant pub_seed using one round of the compression function + * This initializes state_seeded, which can then be reused in thash + **/ +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_seed_state(const unsigned char *pub_seed) { + uint8_t block[SPX_SHA256_BLOCK_BYTES]; + size_t i; + + for (i = 0; i < SPX_N; ++i) { + block[i] = pub_seed[i]; + } + for (i = SPX_N; i < SPX_SHA256_BLOCK_BYTES; ++i) { + block[i] = 0; + } + + sha256_inc_init(PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_state_seeded); + sha256_inc_blocks(PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_state_seeded, block, 1); +} diff --git a/crypto_sign/sphincs-sha256-256f-simple/clean/sha256.h b/crypto_sign/sphincs-sha256-256f-simple/clean/sha256.h new file mode 100644 index 00000000..9860691b --- /dev/null +++ b/crypto_sign/sphincs-sha256-256f-simple/clean/sha256.h @@ -0,0 +1,21 @@ +#ifndef SPX_SHA256_H +#define SPX_SHA256_H + +#define SPX_SHA256_BLOCK_BYTES 64 +#define SPX_SHA256_OUTPUT_BYTES 32 /* This does not necessarily equal SPX_N */ +#define SPX_SHA256_ADDR_BYTES 22 + +#include +#include + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_compress_address(unsigned char *out, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + unsigned char *input_plus_four_bytes, unsigned long inlen); + +uint8_t PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_state_seeded[40]; + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_seed_state(const unsigned char *pub_seed); + +#endif diff --git a/crypto_sign/sphincs-sha256-256f-simple/clean/sign.c b/crypto_sign/sphincs-sha256-256f-simple/clean/sign.c new file mode 100644 index 00000000..37b92e7b --- /dev/null +++ b/crypto_sign/sphincs-sha256-256f-simple/clean/sign.c @@ -0,0 +1,344 @@ +#include +#include +#include + +#include "address.h" +#include "api.h" +#include "fors.h" +#include "hash.h" +#include "params.h" +#include "randombytes.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +/** + * Computes the leaf at a given address. First generates the WOTS key pair, + * then computes leaf by hashing horizontally. + */ +static void wots_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t tree_addr[8]) { + unsigned char pk[SPX_WOTS_BYTES]; + uint32_t wots_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_keypair_addr( + wots_addr, addr_idx); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_wots_gen_pk( + pk, sk_seed, pub_seed, wots_addr); + + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_thash_WOTS_LEN( + leaf, pk, pub_seed, wots_pk_addr); +} + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_crypto_sign_secretkeybytes(void) { + return PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_CRYPTO_SECRETKEYBYTES; +} + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_crypto_sign_publickeybytes(void) { + return PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES; +} + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_crypto_sign_bytes(void) { + return PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_CRYPTO_BYTES; +} + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_crypto_sign_seedbytes(void) { + return PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_CRYPTO_SEEDBYTES; +} + +/* + * Generates an SPX key pair given a seed of length + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed) { + /* We do not need the auth path in key generation, but it simplifies the + code to have just one treehash routine that computes both root and path + in one function. */ + unsigned char auth_path[SPX_TREE_HEIGHT * SPX_N]; + uint32_t top_tree_addr[8] = {0}; + + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_layer_addr( + top_tree_addr, SPX_D - 1); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_type( + top_tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */ + memcpy(sk, seed, PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_CRYPTO_SEEDBYTES); + + memcpy(pk, sk + 2 * SPX_N, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_initialize_hash_function(pk, sk); + + /* Compute root node of the top-most subtree. */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_treehash_TREE_HEIGHT( + sk + 3 * SPX_N, auth_path, sk, sk + 2 * SPX_N, 0, 0, + wots_gen_leaf, top_tree_addr); + + memcpy(pk + SPX_N, sk + 3 * SPX_N, SPX_N); + + return 0; +} + +/* + * Generates an SPX key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk) { + unsigned char seed[PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_CRYPTO_SEEDBYTES]; + randombytes(seed, PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_CRYPTO_SEEDBYTES); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_crypto_sign_seed_keypair( + pk, sk, seed); + + return 0; +} + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + const unsigned char *sk_seed = sk; + const unsigned char *sk_prf = sk + SPX_N; + const unsigned char *pk = sk + 2 * SPX_N; + const unsigned char *pub_seed = pk; + + unsigned char optrand[SPX_N]; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char root[SPX_N]; + uint32_t i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_initialize_hash_function( + pub_seed, sk_seed); + + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ + randombytes(optrand, SPX_N); + /* Compute the digest randomization value. */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_gen_message_random( + sig, sk_prf, optrand, m, mlen); + + /* Derive the message digest and leaf index from R, PK and M. */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Sign the message hash using FORS. */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_fors_sign( + sig, root, mhash, sk_seed, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Compute a WOTS signature. */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_wots_sign( + sig, root, sk_seed, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the authentication path for the used WOTS leaf. */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_treehash_TREE_HEIGHT( + root, sig, sk_seed, pub_seed, idx_leaf, 0, + wots_gen_leaf, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + *siglen = SPX_BYTES; + + return 0; +} + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk) { + const unsigned char *pub_seed = pk; + const unsigned char *pub_root = pk + SPX_N; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char wots_pk[SPX_WOTS_BYTES]; + unsigned char root[SPX_N]; + unsigned char leaf[SPX_N]; + unsigned int i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + if (siglen != SPX_BYTES) { + return -1; + } + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_initialize_hash_function( + pub_seed, NULL); + + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + /* Derive the message digest and leaf index from R || PK || M. */ + /* The additional SPX_N is a result of the hash domain separator. */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + /* Layer correctly defaults to 0, so no need to set_layer_addr */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_fors_pk_from_sig( + root, sig, mhash, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + /* For each subtree.. */ + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + + /* The WOTS public key is only correct if the signature was correct. */ + /* Initially, root is the FORS pk, but on subsequent iterations it is + the root of the subtree below the currently processed subtree. */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_wots_pk_from_sig( + wots_pk, sig, root, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the leaf node using the WOTS public key. */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_thash_WOTS_LEN( + leaf, wots_pk, pub_seed, wots_pk_addr); + + /* Compute the root node of this subtree. */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_compute_root( + root, leaf, idx_leaf, 0, sig, SPX_TREE_HEIGHT, + pub_seed, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + /* Check if the root node equals the root node in the public key. */ + if (memcmp(root, pub_root, SPX_N) != 0) { + return -1; + } + + return 0; +} + + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + size_t siglen; + + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_crypto_sign_signature( + sm, &siglen, m, mlen, sk); + + memmove(sm + SPX_BYTES, m, mlen); + *smlen = siglen + mlen; + + return 0; +} + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk) { + /* The API caller does not necessarily know what size a signature should be + but SPHINCS+ signatures are always exactly SPX_BYTES. */ + if (smlen < SPX_BYTES) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + *mlen = smlen - SPX_BYTES; + + if (PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_crypto_sign_verify( + sm, SPX_BYTES, sm + SPX_BYTES, *mlen, pk)) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + /* If verification was successful, move the message to the right place. */ + memmove(m, sm + SPX_BYTES, *mlen); + + return 0; +} diff --git a/crypto_sign/sphincs-sha256-256f-simple/clean/thash.h b/crypto_sign/sphincs-sha256-256f-simple/clean/thash.h new file mode 100644 index 00000000..cdcfaa93 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256f-simple/clean/thash.h @@ -0,0 +1,22 @@ +#ifndef SPX_THASH_H +#define SPX_THASH_H + +#include + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-sha256-256f-simple/clean/thash_sha256_simple.c b/crypto_sign/sphincs-sha256-256f-simple/clean/thash_sha256_simple.c new file mode 100644 index 00000000..ab8c6af8 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256f-simple/clean/thash_sha256_simple.c @@ -0,0 +1,70 @@ +#include +#include + +#include "address.h" +#include "params.h" +#include "thash.h" + +#include "sha2.h" +#include "sha256.h" + +/** + * Takes an array of inblocks concatenated arrays of SPX_N bytes. + */ +static void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_thash( + unsigned char *out, unsigned char *buf, + const unsigned char *in, unsigned int inblocks, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + uint8_t sha2_state[40]; + + (void)pub_seed; /* Suppress an 'unused parameter' warning. */ + + /* Retrieve precomputed state containing pub_seed */ + memcpy(sha2_state, PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_state_seeded, 40 * sizeof(uint8_t)); + + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_compress_address(buf, addr); + memcpy(buf + SPX_SHA256_ADDR_BYTES, in, inblocks * SPX_N); + + sha256_inc_finalize(outbuf, sha2_state, buf, SPX_SHA256_ADDR_BYTES + inblocks * SPX_N); + memcpy(out, outbuf, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_SHA256_ADDR_BYTES + 1 * SPX_N]; + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_thash( + out, buf, in, 1, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_SHA256_ADDR_BYTES + 2 * SPX_N]; + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_thash( + out, buf, in, 2, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_SHA256_ADDR_BYTES + SPX_WOTS_LEN * SPX_N]; + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_thash( + out, buf, in, SPX_WOTS_LEN, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_SHA256_ADDR_BYTES + SPX_FORS_TREES * SPX_N]; + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_thash( + out, buf, in, SPX_FORS_TREES, pub_seed, addr); +} diff --git a/crypto_sign/sphincs-sha256-256f-simple/clean/utils.c b/crypto_sign/sphincs-sha256-256f-simple/clean/utils.c new file mode 100644 index 00000000..d67c5c41 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256f-simple/clean/utils.c @@ -0,0 +1,192 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in) { + + /* Iterate over out in decreasing order, for big-endianness. */ + for (size_t i = outlen; i > 0; i--) { + out[i - 1] = in & 0xff; + in = in >> 8; + } +} + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen) { + unsigned long long retval = 0; + + for (size_t i = 0; i < inlen; i++) { + retval |= ((unsigned long long)in[i]) << (8 * (inlen - 1 - i)); + } + return retval; +} + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + unsigned char buffer[2 * SPX_N]; + + /* If leaf_idx is odd (last bit = 1), current path element is a right child + and auth_path has to go left. Otherwise it is the other way around. */ + if (leaf_idx & 1) { + memcpy(buffer + SPX_N, leaf, SPX_N); + memcpy(buffer, auth_path, SPX_N); + } else { + memcpy(buffer, leaf, SPX_N); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + + for (i = 0; i < tree_height - 1; i++) { + leaf_idx >>= 1; + idx_offset >>= 1; + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_tree_height(addr, i + 1); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + + /* Pick the right or left neighbor, depending on parity of the node. */ + if (leaf_idx & 1) { + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_thash_2( + buffer + SPX_N, buffer, pub_seed, addr); + memcpy(buffer, auth_path, SPX_N); + } else { + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_thash_2( + buffer, buffer, pub_seed, addr); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + } + + /* The last iteration is exceptional; we do not copy an auth_path node. */ + leaf_idx >>= 1; + idx_offset >>= 1; + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_tree_height(addr, tree_height); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_thash_2( + root, buffer, pub_seed, addr); +} + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +static void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_treehash( + unsigned char *root, unsigned char *auth_path, + unsigned char *stack, unsigned int *heights, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned int offset = 0; + uint32_t idx; + uint32_t tree_idx; + + for (idx = 0; idx < (uint32_t)(1 << tree_height); idx++) { + /* Add the next leaf node to the stack. */ + gen_leaf(stack + offset * SPX_N, + sk_seed, pub_seed, idx + idx_offset, tree_addr); + offset++; + heights[offset - 1] = 0; + + /* If this is a node we need for the auth path.. */ + if ((leaf_idx ^ 0x1) == idx) { + memcpy(auth_path, stack + (offset - 1)*SPX_N, SPX_N); + } + + /* While the top-most nodes are of equal height.. */ + while (offset >= 2 && heights[offset - 1] == heights[offset - 2]) { + /* Compute index of the new node, in the next layer. */ + tree_idx = (idx >> (heights[offset - 1] + 1)); + + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_tree_height( + tree_addr, heights[offset - 1] + 1); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_tree_index( + tree_addr, tree_idx + (idx_offset >> (heights[offset - 1] + 1))); + /* Hash the top-most nodes from the stack together. */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_thash_2( + stack + (offset - 2)*SPX_N, stack + (offset - 2)*SPX_N, + pub_seed, tree_addr); + offset--; + /* Note that the top-most node is now one layer higher. */ + heights[offset - 1]++; + + /* If this is a node we need for the auth path.. */ + if (((leaf_idx >> heights[offset - 1]) ^ 0x1) == tree_idx) { + memcpy(auth_path + heights[offset - 1]*SPX_N, + stack + (offset - 1)*SPX_N, SPX_N); + } + } + } + memcpy(root, stack, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_FORS_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_FORS_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_FORS_HEIGHT, gen_leaf, tree_addr); +} + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_TREE_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_TREE_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_TREE_HEIGHT, gen_leaf, tree_addr); +} diff --git a/crypto_sign/sphincs-sha256-256f-simple/clean/utils.h b/crypto_sign/sphincs-sha256-256f-simple/clean/utils.h new file mode 100644 index 00000000..86c2a7f0 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256f-simple/clean/utils.h @@ -0,0 +1,60 @@ +#ifndef SPX_UTILS_H +#define SPX_UTILS_H + +#include "params.h" +#include +#include + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in); + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen); + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-sha256-256f-simple/clean/wots.c b/crypto_sign/sphincs-sha256-256f-simple/clean/wots.c new file mode 100644 index 00000000..62a0fa4f --- /dev/null +++ b/crypto_sign/sphincs-sha256-256f-simple/clean/wots.c @@ -0,0 +1,161 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +// TODO clarify address expectations, and make them more uniform. +// TODO i.e. do we expect types to be set already? +// TODO and do we expect modifications or copies? + +/** + * Computes the starting value for a chain, i.e. the secret key. + * Expects the address to be complete up to the chain address. + */ +static void wots_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t wots_addr[8]) { + /* Make sure that the hash address is actually zeroed. */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_hash_addr(wots_addr, 0); + + /* Generate sk element. */ + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_prf_addr(sk, sk_seed, wots_addr); +} + +/** + * Computes the chaining function. + * out and in have to be n-byte arrays. + * + * Interprets in as start-th value of the chain. + * addr has to contain the address of the chain. + */ +static void gen_chain(unsigned char *out, const unsigned char *in, + unsigned int start, unsigned int steps, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + /* Initialize out with the value at position 'start'. */ + memcpy(out, in, SPX_N); + + /* Iterate 'steps' calls to the hash function. */ + for (i = start; i < (start + steps) && i < SPX_WOTS_W; i++) { + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_hash_addr(addr, i); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_thash_1( + out, out, pub_seed, addr); + } +} + +/** + * base_w algorithm as described in draft. + * Interprets an array of bytes as integers in base w. + * This only works when log_w is a divisor of 8. + */ +static void base_w(unsigned int *output, const size_t out_len, + const unsigned char *input) { + size_t in = 0; + size_t out = 0; + unsigned char total = 0; + unsigned int bits = 0; + size_t consumed; + + for (consumed = 0; consumed < out_len; consumed++) { + if (bits == 0) { + total = input[in]; + in++; + bits += 8; + } + bits -= SPX_WOTS_LOGW; + output[out] = (unsigned int)((total >> bits) & (SPX_WOTS_W - 1)); + out++; + } +} + +/* Computes the WOTS+ checksum over a message (in base_w). */ +static void wots_checksum(unsigned int *csum_base_w, + const unsigned int *msg_base_w) { + unsigned int csum = 0; + unsigned char csum_bytes[(SPX_WOTS_LEN2 * SPX_WOTS_LOGW + 7) / 8]; + unsigned int i; + + /* Compute checksum. */ + for (i = 0; i < SPX_WOTS_LEN1; i++) { + csum += SPX_WOTS_W - 1 - msg_base_w[i]; + } + + /* Convert checksum to base_w. */ + /* Make sure expected empty zero bits are the least significant bits. */ + csum = csum << (8 - ((SPX_WOTS_LEN2 * SPX_WOTS_LOGW) % 8)); + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_ull_to_bytes( + csum_bytes, sizeof(csum_bytes), csum); + base_w(csum_base_w, SPX_WOTS_LEN2, csum_bytes); +} + +/* Takes a message and derives the matching chain lengths. */ +static void chain_lengths(unsigned int *lengths, const unsigned char *msg) { + base_w(lengths, SPX_WOTS_LEN1, msg); + wots_checksum(lengths + SPX_WOTS_LEN1, lengths); +} + +/** + * WOTS key generation. Takes a 32 byte sk_seed, expands it to WOTS private key + * elements and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(pk + i * SPX_N, sk_seed, addr); + gen_chain(pk + i * SPX_N, pk + i * SPX_N, + 0, SPX_WOTS_W - 1, pub_seed, addr); + } +} + +/** + * Takes a n-byte message and the 32-byte sk_see to compute a signature 'sig'. + */ +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(sig + i * SPX_N, sk_seed, addr); + gen_chain(sig + i * SPX_N, sig + i * SPX_N, 0, lengths[i], pub_seed, addr); + } +} + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_set_chain_addr(addr, i); + gen_chain(pk + i * SPX_N, sig + i * SPX_N, + lengths[i], SPX_WOTS_W - 1 - lengths[i], pub_seed, addr); + } +} diff --git a/crypto_sign/sphincs-sha256-256f-simple/clean/wots.h b/crypto_sign/sphincs-sha256-256f-simple/clean/wots.h new file mode 100644 index 00000000..4e4bab9a --- /dev/null +++ b/crypto_sign/sphincs-sha256-256f-simple/clean/wots.h @@ -0,0 +1,38 @@ +#ifndef SPX_WOTS_H +#define SPX_WOTS_H + +#include "params.h" +#include + +/** + * WOTS key generation. Takes a 32 byte seed for the private key, expands it to + * a full WOTS private key and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * Takes a n-byte message and the 32-byte seed for the private key to compute a + * signature that is placed at 'sig'. + */ +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]); + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-sha256-256s-robust/META.yml b/crypto_sign/sphincs-sha256-256s-robust/META.yml new file mode 100644 index 00000000..f4ab2c23 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256s-robust/META.yml @@ -0,0 +1,27 @@ +name: SPHINCS+ +type: signature +claimed-nist-level: 5 +length-public-key: 64 +length-secret-key: 128 +length-signature: 29792 +testvectors-sha256: 6a85ec1f64d017fc2ffd88aa7d679de7e0554e00bdea62c7fea5c4c403e3eafa +principal-submitter: Andreas Hülsing +auxiliary-submitters: + - Jean-Philippe Aumasson + - Daniel J. Bernstein, + - Christoph Dobraunig + - Maria Eichlseder + - Scott Fluhrer + - Stefan-Lukas Gazdag + - Panos Kampanakis + - Stefan Kölbl + - Tanja Lange + - Martin M. Lauridsen + - Florian Mendel + - Ruben Niederhagen + - Christian Rechberger + - Joost Rijneveld + - Peter Schwabe +implementations: + - name: clean + version: https://github.com/sphincs/sphincsplus/commit/77755c94d0bc744478044d6efbb888dc13156441 diff --git a/crypto_sign/sphincs-sha256-256s-robust/clean/LICENSE b/crypto_sign/sphincs-sha256-256s-robust/clean/LICENSE new file mode 100644 index 00000000..670154e3 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256s-robust/clean/LICENSE @@ -0,0 +1,116 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + diff --git a/crypto_sign/sphincs-sha256-256s-robust/clean/Makefile b/crypto_sign/sphincs-sha256-256s-robust/clean/Makefile new file mode 100644 index 00000000..e06192c4 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256s-robust/clean/Makefile @@ -0,0 +1,20 @@ +# This Makefile can be used with GNU Make or BSD Make + +LIB=libsphincs-sha256-256s-robust_clean.a + +HEADERS = params.h address.h wots.h utils.h fors.h api.h hash.h thash.h sha256.h +OBJECTS = address.o wots.o utils.o fors.o sign.o hash_sha256.o thash_sha256_robust.o sha256.o + +CFLAGS=-O3 -Wall -Wconversion -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) + +all: $(LIB) + +%.o: %.c $(HEADERS) + $(CC) $(CFLAGS) -c -o $@ $< + +$(LIB): $(OBJECTS) + $(AR) -r $@ $(OBJECTS) + +clean: + $(RM) $(OBJECTS) + $(RM) $(LIB) diff --git a/crypto_sign/sphincs-sha256-256s-robust/clean/Makefile.Microsoft_nmake b/crypto_sign/sphincs-sha256-256s-robust/clean/Makefile.Microsoft_nmake new file mode 100644 index 00000000..e4e9ed84 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256s-robust/clean/Makefile.Microsoft_nmake @@ -0,0 +1,19 @@ +# This Makefile can be used with Microsoft Visual Studio's nmake using the command: +# nmake /f Makefile.Microsoft_nmake + +LIBRARY=libsphincs-sha256-256s-robust_clean.lib +OBJECTS=address.obj wots.obj utils.obj fors.obj sign.obj hash_sha256.obj thash_sha256_robust.obj sha256.obj + +CFLAGS=/nologo /I ..\..\..\common /W4 /WX + +all: $(LIBRARY) + +# Make sure objects are recompiled if headers change. +$(OBJECTS): *.h + +$(LIBRARY): $(OBJECTS) + LIB.EXE /NOLOGO /WX /OUT:$@ $** + +clean: + -DEL $(OBJECTS) + -DEL $(LIBRARY) diff --git a/crypto_sign/sphincs-sha256-256s-robust/clean/address.c b/crypto_sign/sphincs-sha256-256s-robust/clean/address.c new file mode 100644 index 00000000..fe922293 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256s-robust/clean/address.c @@ -0,0 +1,78 @@ +#include + +#include "address.h" +#include "params.h" +#include "utils.h" + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]) { + int i; + + for (i = 0; i < 8; i++) { + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_ull_to_bytes( + bytes + i * 4, 4, addr[i]); + } +} + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer) { + addr[0] = layer; +} + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree) { + addr[1] = 0; + addr[2] = (uint32_t) (tree >> 32); + addr[3] = (uint32_t) tree; +} + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_type( + uint32_t addr[8], uint32_t type) { + addr[4] = type; +} + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; +} + +/* These functions are used for OTS addresses. */ + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair) { + addr[5] = keypair; +} + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; + out[5] = in[5]; +} + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain) { + addr[6] = chain; +} + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash) { + addr[7] = hash; +} + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height) { + addr[6] = tree_height; +} + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index) { + addr[7] = tree_index; +} diff --git a/crypto_sign/sphincs-sha256-256s-robust/clean/address.h b/crypto_sign/sphincs-sha256-256s-robust/clean/address.h new file mode 100644 index 00000000..8144aec6 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256s-robust/clean/address.h @@ -0,0 +1,50 @@ +#ifndef SPX_ADDRESS_H +#define SPX_ADDRESS_H + +#include + +#define SPX_ADDR_TYPE_WOTS 0 +#define SPX_ADDR_TYPE_WOTSPK 1 +#define SPX_ADDR_TYPE_HASHTREE 2 +#define SPX_ADDR_TYPE_FORSTREE 3 +#define SPX_ADDR_TYPE_FORSPK 4 + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer); + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree); + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_type( + uint32_t addr[8], uint32_t type); + +/* Copies the layer and tree part of one address into the other */ +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for WOTS and FORS addresses. */ + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair); + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain); + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash); + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height); + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index); + +#endif diff --git a/crypto_sign/sphincs-sha256-256s-robust/clean/api.h b/crypto_sign/sphincs-sha256-256s-robust/clean/api.h new file mode 100644 index 00000000..492ce2d5 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256s-robust/clean/api.h @@ -0,0 +1,78 @@ +#ifndef PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_API_H +#define PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_API_H + +#include +#include + +#define PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_CRYPTO_ALGNAME "SPHINCS+" + +#define PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_CRYPTO_SECRETKEYBYTES 128 +#define PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES 64 +#define PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_CRYPTO_BYTES 29792 +#define PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_CRYPTO_SEEDBYTES 96 + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_crypto_sign_secretkeybytes(void); + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_crypto_sign_publickeybytes(void); + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_crypto_sign_bytes(void); + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_crypto_sign_seedbytes(void); + +/* + * Generates a SPHINCS+ key pair given a seed. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed); + +/* + * Generates a SPHINCS+ key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk); + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk); + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk); + +#endif diff --git a/crypto_sign/sphincs-sha256-256s-robust/clean/fors.c b/crypto_sign/sphincs-sha256-256s-robust/clean/fors.c new file mode 100644 index 00000000..1565994a --- /dev/null +++ b/crypto_sign/sphincs-sha256-256s-robust/clean/fors.c @@ -0,0 +1,164 @@ +#include +#include +#include + +#include "address.h" +#include "fors.h" +#include "hash.h" +#include "thash.h" +#include "utils.h" + +static void fors_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_prf_addr( + sk, sk_seed, fors_leaf_addr); +} + +static void fors_sk_to_leaf(unsigned char *leaf, const unsigned char *sk, + const unsigned char *pub_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_thash_1( + leaf, sk, pub_seed, fors_leaf_addr); +} + +static void fors_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t fors_tree_addr[8]) { + uint32_t fors_leaf_addr[8] = {0}; + + /* Only copy the parts that must be kept in fors_leaf_addr. */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_copy_keypair_addr( + fors_leaf_addr, fors_tree_addr); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_type( + fors_leaf_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_tree_index( + fors_leaf_addr, addr_idx); + + fors_gen_sk(leaf, sk_seed, fors_leaf_addr); + fors_sk_to_leaf(leaf, leaf, pub_seed, fors_leaf_addr); +} + +/** + * Interprets m as SPX_FORS_HEIGHT-bit unsigned integers. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + * Assumes indices has space for SPX_FORS_TREES integers. + */ +static void message_to_indices(uint32_t *indices, const unsigned char *m) { + unsigned int i, j; + unsigned int offset = 0; + + for (i = 0; i < SPX_FORS_TREES; i++) { + indices[i] = 0; + for (j = 0; j < SPX_FORS_HEIGHT; j++) { + indices[i] ^= (((uint32_t)m[offset >> 3] >> (offset & 0x7)) & 0x1) << j; + offset++; + } + } +} + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Include the secret key part that produces the selected leaf node. */ + fors_gen_sk(sig, sk_seed, fors_tree_addr); + sig += SPX_N; + + /* Compute the authentication path for this leaf node. */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_treehash_FORS_HEIGHT( + roots + i * SPX_N, sig, sk_seed, pub_seed, + indices[i], idx_offset, fors_gen_leaf, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + unsigned char leaf[SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Derive the leaf from the included secret key part. */ + fors_sk_to_leaf(leaf, sig, pub_seed, fors_tree_addr); + sig += SPX_N; + + /* Derive the corresponding root node of this tree. */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_compute_root( + roots + i * SPX_N, leaf, indices[i], idx_offset, sig, + SPX_FORS_HEIGHT, pub_seed, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} diff --git a/crypto_sign/sphincs-sha256-256s-robust/clean/fors.h b/crypto_sign/sphincs-sha256-256s-robust/clean/fors.h new file mode 100644 index 00000000..c3e2ce40 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256s-robust/clean/fors.h @@ -0,0 +1,30 @@ +#ifndef SPX_FORS_H +#define SPX_FORS_H + +#include + +#include "params.h" + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]); + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-sha256-256s-robust/clean/hash.h b/crypto_sign/sphincs-sha256-256s-robust/clean/hash.h new file mode 100644 index 00000000..eaa176c5 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256s-robust/clean/hash.h @@ -0,0 +1,22 @@ +#ifndef SPX_HASH_H +#define SPX_HASH_H + +#include + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed); + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen); + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen); + +#endif diff --git a/crypto_sign/sphincs-sha256-256s-robust/clean/hash_sha256.c b/crypto_sign/sphincs-sha256-256s-robust/clean/hash_sha256.c new file mode 100644 index 00000000..da4e2519 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256s-robust/clean/hash_sha256.c @@ -0,0 +1,148 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "utils.h" + +#include "sha2.h" +#include "sha256.h" + +/* For SHA256, there is no immediate reason to initialize at the start, + so this function is an empty operation. */ +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed) { + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_seed_state(pub_seed); + (void)sk_seed; /* Suppress an 'unused parameter' warning. */ +} + +/* + * Computes PRF(key, addr), given a secret key of SPX_N bytes and an address + */ +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]) { + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES]; + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + + memcpy(buf, key, SPX_N); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_compress_address(buf + SPX_N, addr); + + sha256(outbuf, buf, SPX_N + SPX_SHA256_ADDR_BYTES); + memcpy(out, outbuf, SPX_N); +} + +/** + * Computes the message-dependent randomness R, using a secret seed as a key + * for HMAC, and an optional randomization value prefixed to the message. + * This requires m to have at least SPX_SHA256_BLOCK_BYTES + SPX_N space + * available in front of the pointer, i.e. before the message to use for the + * prefix. This is necessary to prevent having to move the message around (and + * allocate memory for it). + */ +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen) { + unsigned char buf[SPX_SHA256_BLOCK_BYTES + SPX_SHA256_OUTPUT_BYTES]; + uint8_t state[40]; + int i; + + /* This implements HMAC-SHA256 */ + for (i = 0; i < SPX_N; i++) { + buf[i] = 0x36 ^ sk_prf[i]; + } + memset(buf + SPX_N, 0x36, SPX_SHA256_BLOCK_BYTES - SPX_N); + + sha256_inc_init(state); + sha256_inc_blocks(state, buf, 1); + + memcpy(buf, optrand, SPX_N); + + /* If optrand + message cannot fill up an entire block */ + if (SPX_N + mlen < SPX_SHA256_BLOCK_BYTES) { + memcpy(buf + SPX_N, m, mlen); + sha256_inc_finalize(buf + SPX_SHA256_BLOCK_BYTES, state, + buf, mlen + SPX_N); + } + /* Otherwise first fill a block, so that finalize only uses the message */ + else { + memcpy(buf + SPX_N, m, SPX_SHA256_BLOCK_BYTES - SPX_N); + sha256_inc_blocks(state, buf, 1); + + m += SPX_SHA256_BLOCK_BYTES - SPX_N; + mlen -= SPX_SHA256_BLOCK_BYTES - SPX_N; + sha256_inc_finalize(buf + SPX_SHA256_BLOCK_BYTES, state, m, mlen); + } + + for (i = 0; i < SPX_N; i++) { + buf[i] = 0x5c ^ sk_prf[i]; + } + memset(buf + SPX_N, 0x5c, SPX_SHA256_BLOCK_BYTES - SPX_N); + + sha256(buf, buf, SPX_SHA256_BLOCK_BYTES + SPX_SHA256_OUTPUT_BYTES); + memcpy(R, buf, SPX_N); +} + +/** + * Computes the message hash using R, the public key, and the message. + * Outputs the message digest and the index of the leaf. The index is split in + * the tree index and the leaf index, for convenient copying to an address. + */ +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen) { +#define SPX_TREE_BITS (SPX_TREE_HEIGHT * (SPX_D - 1)) +#define SPX_TREE_BYTES ((SPX_TREE_BITS + 7) / 8) +#define SPX_LEAF_BITS SPX_TREE_HEIGHT +#define SPX_LEAF_BYTES ((SPX_LEAF_BITS + 7) / 8) +#define SPX_DGST_BYTES (SPX_FORS_MSG_BYTES + SPX_TREE_BYTES + SPX_LEAF_BYTES) + + unsigned char seed[SPX_SHA256_OUTPUT_BYTES + 4]; + + /* Round to nearest multiple of SPX_SHA256_BLOCK_BYTES */ +#define SPX_INBLOCKS (((SPX_N + SPX_PK_BYTES + SPX_SHA256_BLOCK_BYTES - 1) & \ + -SPX_SHA256_BLOCK_BYTES) / SPX_SHA256_BLOCK_BYTES) + unsigned char inbuf[SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES]; + + unsigned char buf[SPX_DGST_BYTES]; + unsigned char *bufp = buf; + uint8_t state[40]; + + sha256_inc_init(state); + + memcpy(inbuf, R, SPX_N); + memcpy(inbuf + SPX_N, pk, SPX_PK_BYTES); + + /* If R + pk + message cannot fill up an entire block */ + if (SPX_N + SPX_PK_BYTES + mlen < SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES) { + memcpy(inbuf + SPX_N + SPX_PK_BYTES, m, mlen); + sha256_inc_finalize(seed, state, inbuf, SPX_N + SPX_PK_BYTES + mlen); + } + /* Otherwise first fill a block, so that finalize only uses the message */ + else { + memcpy(inbuf + SPX_N + SPX_PK_BYTES, m, + SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES); + sha256_inc_blocks(state, inbuf, SPX_INBLOCKS); + + m += SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES; + mlen -= SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES; + sha256_inc_finalize(seed, state, m, mlen); + } + + /* By doing this in two steps, we prevent hashing the message twice; + otherwise each iteration in MGF1 would hash the message again. */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_mgf1(bufp, SPX_DGST_BYTES, seed, SPX_SHA256_OUTPUT_BYTES); + + memcpy(digest, bufp, SPX_FORS_MSG_BYTES); + bufp += SPX_FORS_MSG_BYTES; + + *tree = PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_bytes_to_ull(bufp, SPX_TREE_BYTES); + *tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS); + bufp += SPX_TREE_BYTES; + + *leaf_idx = (uint32_t)PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_bytes_to_ull( + bufp, SPX_LEAF_BYTES); + *leaf_idx &= (~(uint32_t)0) >> (32 - SPX_LEAF_BITS); +} diff --git a/crypto_sign/sphincs-sha256-256s-robust/clean/params.h b/crypto_sign/sphincs-sha256-256s-robust/clean/params.h new file mode 100644 index 00000000..f023d687 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256s-robust/clean/params.h @@ -0,0 +1,53 @@ +#ifndef SPX_PARAMS_H +#define SPX_PARAMS_H + +/* Hash output length in bytes. */ +#define SPX_N 32 +/* Height of the hypertree. */ +#define SPX_FULL_HEIGHT 64 +/* Number of subtree layer. */ +#define SPX_D 8 +/* FORS tree dimensions. */ +#define SPX_FORS_HEIGHT 14 +#define SPX_FORS_TREES 22 +/* Winternitz parameter, */ +#define SPX_WOTS_W 16 + +/* The hash function is defined by linking a different hash.c file, as opposed + to setting a #define constant. */ + +/* For clarity */ +#define SPX_ADDR_BYTES 32 + +/* WOTS parameters. */ +#define SPX_WOTS_LOGW 4 + +#define SPX_WOTS_LEN1 (8 * SPX_N / SPX_WOTS_LOGW) + +/* SPX_WOTS_LEN2 is floor(log(len_1 * (w - 1)) / log(w)) + 1; we precompute */ +#define SPX_WOTS_LEN2 3 + +#define SPX_WOTS_LEN (SPX_WOTS_LEN1 + SPX_WOTS_LEN2) +#define SPX_WOTS_BYTES (SPX_WOTS_LEN * SPX_N) +#define SPX_WOTS_PK_BYTES SPX_WOTS_BYTES + +/* Subtree size. */ +#define SPX_TREE_HEIGHT (SPX_FULL_HEIGHT / SPX_D) + +/* FORS parameters. */ +#define SPX_FORS_MSG_BYTES ((SPX_FORS_HEIGHT * SPX_FORS_TREES + 7) / 8) +#define SPX_FORS_BYTES ((SPX_FORS_HEIGHT + 1) * SPX_FORS_TREES * SPX_N) +#define SPX_FORS_PK_BYTES SPX_N + +/* Resulting SPX sizes. */ +#define SPX_BYTES (SPX_N + SPX_FORS_BYTES + SPX_D * SPX_WOTS_BYTES +\ + SPX_FULL_HEIGHT * SPX_N) +#define SPX_PK_BYTES (2 * SPX_N) +#define SPX_SK_BYTES (2 * SPX_N + SPX_PK_BYTES) + +/* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ +#define SPX_OPTRAND_BYTES 32 + +#endif diff --git a/crypto_sign/sphincs-sha256-256s-robust/clean/sha256.c b/crypto_sign/sphincs-sha256-256s-robust/clean/sha256.c new file mode 100644 index 00000000..181009b8 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256s-robust/clean/sha256.c @@ -0,0 +1,72 @@ +/* Based on the public domain implementation in + * crypto_hash/sha512/ref/ from http://bench.cr.yp.to/supercop.html + * by D. J. Bernstein */ + +#include +#include +#include + +#include "sha2.h" +#include "sha256.h" +#include "utils.h" + +/* + * Compresses an address to a 22-byte sequence. + * This reduces the number of required SHA256 compression calls, as the last + * block of input is padded with at least 65 bits. + */ +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_compress_address(unsigned char *out, const uint32_t addr[8]) { + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_ull_to_bytes(out, 1, addr[0]); /* drop 3 bytes of the layer field */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_ull_to_bytes(out + 1, 4, addr[2]); /* drop the highest tree address word */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_ull_to_bytes(out + 5, 4, addr[3]); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_ull_to_bytes(out + 9, 1, addr[4]); /* drop 3 bytes of the type field */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_ull_to_bytes(out + 10, 4, addr[5]); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_ull_to_bytes(out + 14, 4, addr[6]); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_ull_to_bytes(out + 18, 4, addr[7]); +} + +/** + * Requires 'input_plus_four_bytes' to have 'inlen' + 4 bytes, so that the last + * four bytes can be used for the counter. Typically 'input' is merely a seed. + * Outputs outlen number of bytes + */ +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + unsigned char *input_plus_four_bytes, unsigned long inlen) { + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + unsigned long i; + + /* While we can fit in at least another full block of SHA256 output.. */ + for (i = 0; (i + 1)*SPX_SHA256_OUTPUT_BYTES <= outlen; i++) { + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_ull_to_bytes(input_plus_four_bytes + inlen, 4, i); + sha256(out, input_plus_four_bytes, inlen + 4); + out += SPX_SHA256_OUTPUT_BYTES; + } + /* Until we cannot anymore, and we fill the remainder. */ + if (outlen > i * SPX_SHA256_OUTPUT_BYTES) { + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_ull_to_bytes(input_plus_four_bytes + inlen, 4, i); + sha256(outbuf, input_plus_four_bytes, inlen + 4); + memcpy(out, outbuf, outlen - i * SPX_SHA256_OUTPUT_BYTES); + } +} + +uint8_t PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_state_seeded[40]; + +/** + * Absorb the constant pub_seed using one round of the compression function + * This initializes state_seeded, which can then be reused in thash + **/ +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_seed_state(const unsigned char *pub_seed) { + uint8_t block[SPX_SHA256_BLOCK_BYTES]; + size_t i; + + for (i = 0; i < SPX_N; ++i) { + block[i] = pub_seed[i]; + } + for (i = SPX_N; i < SPX_SHA256_BLOCK_BYTES; ++i) { + block[i] = 0; + } + + sha256_inc_init(PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_state_seeded); + sha256_inc_blocks(PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_state_seeded, block, 1); +} diff --git a/crypto_sign/sphincs-sha256-256s-robust/clean/sha256.h b/crypto_sign/sphincs-sha256-256s-robust/clean/sha256.h new file mode 100644 index 00000000..22a8b5b0 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256s-robust/clean/sha256.h @@ -0,0 +1,21 @@ +#ifndef SPX_SHA256_H +#define SPX_SHA256_H + +#define SPX_SHA256_BLOCK_BYTES 64 +#define SPX_SHA256_OUTPUT_BYTES 32 /* This does not necessarily equal SPX_N */ +#define SPX_SHA256_ADDR_BYTES 22 + +#include +#include + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_compress_address(unsigned char *out, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + unsigned char *input_plus_four_bytes, unsigned long inlen); + +uint8_t PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_state_seeded[40]; + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_seed_state(const unsigned char *pub_seed); + +#endif diff --git a/crypto_sign/sphincs-sha256-256s-robust/clean/sign.c b/crypto_sign/sphincs-sha256-256s-robust/clean/sign.c new file mode 100644 index 00000000..4bc6e951 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256s-robust/clean/sign.c @@ -0,0 +1,344 @@ +#include +#include +#include + +#include "address.h" +#include "api.h" +#include "fors.h" +#include "hash.h" +#include "params.h" +#include "randombytes.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +/** + * Computes the leaf at a given address. First generates the WOTS key pair, + * then computes leaf by hashing horizontally. + */ +static void wots_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t tree_addr[8]) { + unsigned char pk[SPX_WOTS_BYTES]; + uint32_t wots_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_keypair_addr( + wots_addr, addr_idx); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_wots_gen_pk( + pk, sk_seed, pub_seed, wots_addr); + + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_thash_WOTS_LEN( + leaf, pk, pub_seed, wots_pk_addr); +} + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_crypto_sign_secretkeybytes(void) { + return PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_CRYPTO_SECRETKEYBYTES; +} + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_crypto_sign_publickeybytes(void) { + return PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES; +} + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_crypto_sign_bytes(void) { + return PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_CRYPTO_BYTES; +} + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_crypto_sign_seedbytes(void) { + return PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_CRYPTO_SEEDBYTES; +} + +/* + * Generates an SPX key pair given a seed of length + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed) { + /* We do not need the auth path in key generation, but it simplifies the + code to have just one treehash routine that computes both root and path + in one function. */ + unsigned char auth_path[SPX_TREE_HEIGHT * SPX_N]; + uint32_t top_tree_addr[8] = {0}; + + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_layer_addr( + top_tree_addr, SPX_D - 1); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_type( + top_tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */ + memcpy(sk, seed, PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_CRYPTO_SEEDBYTES); + + memcpy(pk, sk + 2 * SPX_N, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_initialize_hash_function(pk, sk); + + /* Compute root node of the top-most subtree. */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_treehash_TREE_HEIGHT( + sk + 3 * SPX_N, auth_path, sk, sk + 2 * SPX_N, 0, 0, + wots_gen_leaf, top_tree_addr); + + memcpy(pk + SPX_N, sk + 3 * SPX_N, SPX_N); + + return 0; +} + +/* + * Generates an SPX key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk) { + unsigned char seed[PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_CRYPTO_SEEDBYTES]; + randombytes(seed, PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_CRYPTO_SEEDBYTES); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_crypto_sign_seed_keypair( + pk, sk, seed); + + return 0; +} + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + const unsigned char *sk_seed = sk; + const unsigned char *sk_prf = sk + SPX_N; + const unsigned char *pk = sk + 2 * SPX_N; + const unsigned char *pub_seed = pk; + + unsigned char optrand[SPX_N]; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char root[SPX_N]; + uint32_t i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_initialize_hash_function( + pub_seed, sk_seed); + + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ + randombytes(optrand, SPX_N); + /* Compute the digest randomization value. */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_gen_message_random( + sig, sk_prf, optrand, m, mlen); + + /* Derive the message digest and leaf index from R, PK and M. */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Sign the message hash using FORS. */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_fors_sign( + sig, root, mhash, sk_seed, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Compute a WOTS signature. */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_wots_sign( + sig, root, sk_seed, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the authentication path for the used WOTS leaf. */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_treehash_TREE_HEIGHT( + root, sig, sk_seed, pub_seed, idx_leaf, 0, + wots_gen_leaf, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + *siglen = SPX_BYTES; + + return 0; +} + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk) { + const unsigned char *pub_seed = pk; + const unsigned char *pub_root = pk + SPX_N; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char wots_pk[SPX_WOTS_BYTES]; + unsigned char root[SPX_N]; + unsigned char leaf[SPX_N]; + unsigned int i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + if (siglen != SPX_BYTES) { + return -1; + } + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_initialize_hash_function( + pub_seed, NULL); + + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + /* Derive the message digest and leaf index from R || PK || M. */ + /* The additional SPX_N is a result of the hash domain separator. */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + /* Layer correctly defaults to 0, so no need to set_layer_addr */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_fors_pk_from_sig( + root, sig, mhash, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + /* For each subtree.. */ + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + + /* The WOTS public key is only correct if the signature was correct. */ + /* Initially, root is the FORS pk, but on subsequent iterations it is + the root of the subtree below the currently processed subtree. */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_wots_pk_from_sig( + wots_pk, sig, root, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the leaf node using the WOTS public key. */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_thash_WOTS_LEN( + leaf, wots_pk, pub_seed, wots_pk_addr); + + /* Compute the root node of this subtree. */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_compute_root( + root, leaf, idx_leaf, 0, sig, SPX_TREE_HEIGHT, + pub_seed, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + /* Check if the root node equals the root node in the public key. */ + if (memcmp(root, pub_root, SPX_N) != 0) { + return -1; + } + + return 0; +} + + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + size_t siglen; + + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_crypto_sign_signature( + sm, &siglen, m, mlen, sk); + + memmove(sm + SPX_BYTES, m, mlen); + *smlen = siglen + mlen; + + return 0; +} + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk) { + /* The API caller does not necessarily know what size a signature should be + but SPHINCS+ signatures are always exactly SPX_BYTES. */ + if (smlen < SPX_BYTES) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + *mlen = smlen - SPX_BYTES; + + if (PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_crypto_sign_verify( + sm, SPX_BYTES, sm + SPX_BYTES, *mlen, pk)) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + /* If verification was successful, move the message to the right place. */ + memmove(m, sm + SPX_BYTES, *mlen); + + return 0; +} diff --git a/crypto_sign/sphincs-sha256-256s-robust/clean/thash.h b/crypto_sign/sphincs-sha256-256s-robust/clean/thash.h new file mode 100644 index 00000000..a0536ea0 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256s-robust/clean/thash.h @@ -0,0 +1,22 @@ +#ifndef SPX_THASH_H +#define SPX_THASH_H + +#include + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-sha256-256s-robust/clean/thash_sha256_robust.c b/crypto_sign/sphincs-sha256-256s-robust/clean/thash_sha256_robust.c new file mode 100644 index 00000000..d7498c60 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256s-robust/clean/thash_sha256_robust.c @@ -0,0 +1,77 @@ +#include +#include + +#include "address.h" +#include "params.h" +#include "thash.h" + +#include "sha2.h" +#include "sha256.h" + +/** + * Takes an array of inblocks concatenated arrays of SPX_N bytes. + */ +static void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_thash( + unsigned char *out, unsigned char *buf, + const unsigned char *in, unsigned int inblocks, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + unsigned char *bitmask = buf + SPX_N + SPX_SHA256_ADDR_BYTES + 4; + uint8_t sha2_state[40]; + unsigned int i; + + memcpy(buf, pub_seed, SPX_N); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_compress_address(buf + SPX_N, addr); + /* MGF1 requires us to have 4 extra bytes in 'buf' */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_mgf1(bitmask, inblocks * SPX_N, buf, SPX_N + SPX_SHA256_ADDR_BYTES); + + /* Retrieve precomputed state containing pub_seed */ + memcpy(sha2_state, PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_state_seeded, 40 * sizeof(uint8_t)); + + for (i = 0; i < inblocks * SPX_N; i++) { + buf[SPX_N + SPX_SHA256_ADDR_BYTES + i] = in[i] ^ bitmask[i]; + } + + sha256_inc_finalize(outbuf, sha2_state, buf + SPX_N, + SPX_SHA256_ADDR_BYTES + inblocks * SPX_N); + memcpy(out, outbuf, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES + 4 + 1 * SPX_N]; + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_thash( + out, buf, in, 1, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES + 4 + 2 * SPX_N]; + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_thash( + out, buf, in, 2, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES + 4 + SPX_WOTS_LEN * SPX_N]; + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_thash( + out, buf, in, SPX_WOTS_LEN, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES + 4 + SPX_FORS_TREES * SPX_N]; + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_thash( + out, buf, in, SPX_FORS_TREES, pub_seed, addr); +} diff --git a/crypto_sign/sphincs-sha256-256s-robust/clean/utils.c b/crypto_sign/sphincs-sha256-256s-robust/clean/utils.c new file mode 100644 index 00000000..32fda6d9 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256s-robust/clean/utils.c @@ -0,0 +1,192 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in) { + + /* Iterate over out in decreasing order, for big-endianness. */ + for (size_t i = outlen; i > 0; i--) { + out[i - 1] = in & 0xff; + in = in >> 8; + } +} + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen) { + unsigned long long retval = 0; + + for (size_t i = 0; i < inlen; i++) { + retval |= ((unsigned long long)in[i]) << (8 * (inlen - 1 - i)); + } + return retval; +} + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + unsigned char buffer[2 * SPX_N]; + + /* If leaf_idx is odd (last bit = 1), current path element is a right child + and auth_path has to go left. Otherwise it is the other way around. */ + if (leaf_idx & 1) { + memcpy(buffer + SPX_N, leaf, SPX_N); + memcpy(buffer, auth_path, SPX_N); + } else { + memcpy(buffer, leaf, SPX_N); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + + for (i = 0; i < tree_height - 1; i++) { + leaf_idx >>= 1; + idx_offset >>= 1; + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_tree_height(addr, i + 1); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + + /* Pick the right or left neighbor, depending on parity of the node. */ + if (leaf_idx & 1) { + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_thash_2( + buffer + SPX_N, buffer, pub_seed, addr); + memcpy(buffer, auth_path, SPX_N); + } else { + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_thash_2( + buffer, buffer, pub_seed, addr); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + } + + /* The last iteration is exceptional; we do not copy an auth_path node. */ + leaf_idx >>= 1; + idx_offset >>= 1; + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_tree_height(addr, tree_height); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_thash_2( + root, buffer, pub_seed, addr); +} + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +static void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_treehash( + unsigned char *root, unsigned char *auth_path, + unsigned char *stack, unsigned int *heights, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned int offset = 0; + uint32_t idx; + uint32_t tree_idx; + + for (idx = 0; idx < (uint32_t)(1 << tree_height); idx++) { + /* Add the next leaf node to the stack. */ + gen_leaf(stack + offset * SPX_N, + sk_seed, pub_seed, idx + idx_offset, tree_addr); + offset++; + heights[offset - 1] = 0; + + /* If this is a node we need for the auth path.. */ + if ((leaf_idx ^ 0x1) == idx) { + memcpy(auth_path, stack + (offset - 1)*SPX_N, SPX_N); + } + + /* While the top-most nodes are of equal height.. */ + while (offset >= 2 && heights[offset - 1] == heights[offset - 2]) { + /* Compute index of the new node, in the next layer. */ + tree_idx = (idx >> (heights[offset - 1] + 1)); + + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_tree_height( + tree_addr, heights[offset - 1] + 1); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_tree_index( + tree_addr, tree_idx + (idx_offset >> (heights[offset - 1] + 1))); + /* Hash the top-most nodes from the stack together. */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_thash_2( + stack + (offset - 2)*SPX_N, stack + (offset - 2)*SPX_N, + pub_seed, tree_addr); + offset--; + /* Note that the top-most node is now one layer higher. */ + heights[offset - 1]++; + + /* If this is a node we need for the auth path.. */ + if (((leaf_idx >> heights[offset - 1]) ^ 0x1) == tree_idx) { + memcpy(auth_path + heights[offset - 1]*SPX_N, + stack + (offset - 1)*SPX_N, SPX_N); + } + } + } + memcpy(root, stack, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_FORS_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_FORS_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_FORS_HEIGHT, gen_leaf, tree_addr); +} + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_TREE_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_TREE_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_TREE_HEIGHT, gen_leaf, tree_addr); +} diff --git a/crypto_sign/sphincs-sha256-256s-robust/clean/utils.h b/crypto_sign/sphincs-sha256-256s-robust/clean/utils.h new file mode 100644 index 00000000..a9ee4d25 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256s-robust/clean/utils.h @@ -0,0 +1,60 @@ +#ifndef SPX_UTILS_H +#define SPX_UTILS_H + +#include "params.h" +#include +#include + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in); + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen); + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-sha256-256s-robust/clean/wots.c b/crypto_sign/sphincs-sha256-256s-robust/clean/wots.c new file mode 100644 index 00000000..36b3fdab --- /dev/null +++ b/crypto_sign/sphincs-sha256-256s-robust/clean/wots.c @@ -0,0 +1,161 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +// TODO clarify address expectations, and make them more uniform. +// TODO i.e. do we expect types to be set already? +// TODO and do we expect modifications or copies? + +/** + * Computes the starting value for a chain, i.e. the secret key. + * Expects the address to be complete up to the chain address. + */ +static void wots_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t wots_addr[8]) { + /* Make sure that the hash address is actually zeroed. */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_hash_addr(wots_addr, 0); + + /* Generate sk element. */ + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_prf_addr(sk, sk_seed, wots_addr); +} + +/** + * Computes the chaining function. + * out and in have to be n-byte arrays. + * + * Interprets in as start-th value of the chain. + * addr has to contain the address of the chain. + */ +static void gen_chain(unsigned char *out, const unsigned char *in, + unsigned int start, unsigned int steps, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + /* Initialize out with the value at position 'start'. */ + memcpy(out, in, SPX_N); + + /* Iterate 'steps' calls to the hash function. */ + for (i = start; i < (start + steps) && i < SPX_WOTS_W; i++) { + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_hash_addr(addr, i); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_thash_1( + out, out, pub_seed, addr); + } +} + +/** + * base_w algorithm as described in draft. + * Interprets an array of bytes as integers in base w. + * This only works when log_w is a divisor of 8. + */ +static void base_w(unsigned int *output, const size_t out_len, + const unsigned char *input) { + size_t in = 0; + size_t out = 0; + unsigned char total = 0; + unsigned int bits = 0; + size_t consumed; + + for (consumed = 0; consumed < out_len; consumed++) { + if (bits == 0) { + total = input[in]; + in++; + bits += 8; + } + bits -= SPX_WOTS_LOGW; + output[out] = (unsigned int)((total >> bits) & (SPX_WOTS_W - 1)); + out++; + } +} + +/* Computes the WOTS+ checksum over a message (in base_w). */ +static void wots_checksum(unsigned int *csum_base_w, + const unsigned int *msg_base_w) { + unsigned int csum = 0; + unsigned char csum_bytes[(SPX_WOTS_LEN2 * SPX_WOTS_LOGW + 7) / 8]; + unsigned int i; + + /* Compute checksum. */ + for (i = 0; i < SPX_WOTS_LEN1; i++) { + csum += SPX_WOTS_W - 1 - msg_base_w[i]; + } + + /* Convert checksum to base_w. */ + /* Make sure expected empty zero bits are the least significant bits. */ + csum = csum << (8 - ((SPX_WOTS_LEN2 * SPX_WOTS_LOGW) % 8)); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_ull_to_bytes( + csum_bytes, sizeof(csum_bytes), csum); + base_w(csum_base_w, SPX_WOTS_LEN2, csum_bytes); +} + +/* Takes a message and derives the matching chain lengths. */ +static void chain_lengths(unsigned int *lengths, const unsigned char *msg) { + base_w(lengths, SPX_WOTS_LEN1, msg); + wots_checksum(lengths + SPX_WOTS_LEN1, lengths); +} + +/** + * WOTS key generation. Takes a 32 byte sk_seed, expands it to WOTS private key + * elements and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(pk + i * SPX_N, sk_seed, addr); + gen_chain(pk + i * SPX_N, pk + i * SPX_N, + 0, SPX_WOTS_W - 1, pub_seed, addr); + } +} + +/** + * Takes a n-byte message and the 32-byte sk_see to compute a signature 'sig'. + */ +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(sig + i * SPX_N, sk_seed, addr); + gen_chain(sig + i * SPX_N, sig + i * SPX_N, 0, lengths[i], pub_seed, addr); + } +} + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_set_chain_addr(addr, i); + gen_chain(pk + i * SPX_N, sig + i * SPX_N, + lengths[i], SPX_WOTS_W - 1 - lengths[i], pub_seed, addr); + } +} diff --git a/crypto_sign/sphincs-sha256-256s-robust/clean/wots.h b/crypto_sign/sphincs-sha256-256s-robust/clean/wots.h new file mode 100644 index 00000000..b5a691fa --- /dev/null +++ b/crypto_sign/sphincs-sha256-256s-robust/clean/wots.h @@ -0,0 +1,38 @@ +#ifndef SPX_WOTS_H +#define SPX_WOTS_H + +#include "params.h" +#include + +/** + * WOTS key generation. Takes a 32 byte seed for the private key, expands it to + * a full WOTS private key and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * Takes a n-byte message and the 32-byte seed for the private key to compute a + * signature that is placed at 'sig'. + */ +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]); + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-sha256-256s-simple/META.yml b/crypto_sign/sphincs-sha256-256s-simple/META.yml new file mode 100644 index 00000000..231b96dc --- /dev/null +++ b/crypto_sign/sphincs-sha256-256s-simple/META.yml @@ -0,0 +1,27 @@ +name: SPHINCS+ +type: signature +claimed-nist-level: 5 +length-public-key: 64 +length-secret-key: 128 +length-signature: 29792 +testvectors-sha256: 796b5101fa5170c92f0186b347716dc0662eac35002a8c4d80ac9283cbef5a02 +principal-submitter: Andreas Hülsing +auxiliary-submitters: + - Jean-Philippe Aumasson + - Daniel J. Bernstein, + - Christoph Dobraunig + - Maria Eichlseder + - Scott Fluhrer + - Stefan-Lukas Gazdag + - Panos Kampanakis + - Stefan Kölbl + - Tanja Lange + - Martin M. Lauridsen + - Florian Mendel + - Ruben Niederhagen + - Christian Rechberger + - Joost Rijneveld + - Peter Schwabe +implementations: + - name: clean + version: https://github.com/sphincs/sphincsplus/commit/77755c94d0bc744478044d6efbb888dc13156441 diff --git a/crypto_sign/sphincs-sha256-256s-simple/clean/LICENSE b/crypto_sign/sphincs-sha256-256s-simple/clean/LICENSE new file mode 100644 index 00000000..670154e3 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256s-simple/clean/LICENSE @@ -0,0 +1,116 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + diff --git a/crypto_sign/sphincs-sha256-256s-simple/clean/Makefile b/crypto_sign/sphincs-sha256-256s-simple/clean/Makefile new file mode 100644 index 00000000..ce925c1b --- /dev/null +++ b/crypto_sign/sphincs-sha256-256s-simple/clean/Makefile @@ -0,0 +1,20 @@ +# This Makefile can be used with GNU Make or BSD Make + +LIB=libsphincs-sha256-256s-simple_clean.a + +HEADERS = params.h address.h wots.h utils.h fors.h api.h hash.h thash.h sha256.h +OBJECTS = address.o wots.o utils.o fors.o sign.o hash_sha256.o thash_sha256_simple.o sha256.o + +CFLAGS=-O3 -Wall -Wconversion -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) + +all: $(LIB) + +%.o: %.c $(HEADERS) + $(CC) $(CFLAGS) -c -o $@ $< + +$(LIB): $(OBJECTS) + $(AR) -r $@ $(OBJECTS) + +clean: + $(RM) $(OBJECTS) + $(RM) $(LIB) diff --git a/crypto_sign/sphincs-sha256-256s-simple/clean/Makefile.Microsoft_nmake b/crypto_sign/sphincs-sha256-256s-simple/clean/Makefile.Microsoft_nmake new file mode 100644 index 00000000..8a997e24 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256s-simple/clean/Makefile.Microsoft_nmake @@ -0,0 +1,19 @@ +# This Makefile can be used with Microsoft Visual Studio's nmake using the command: +# nmake /f Makefile.Microsoft_nmake + +LIBRARY=libsphincs-sha256-256s-simple_clean.lib +OBJECTS=address.obj wots.obj utils.obj fors.obj sign.obj hash_sha256.obj thash_sha256_simple.obj sha256.obj + +CFLAGS=/nologo /I ..\..\..\common /W4 /WX + +all: $(LIBRARY) + +# Make sure objects are recompiled if headers change. +$(OBJECTS): *.h + +$(LIBRARY): $(OBJECTS) + LIB.EXE /NOLOGO /WX /OUT:$@ $** + +clean: + -DEL $(OBJECTS) + -DEL $(LIBRARY) diff --git a/crypto_sign/sphincs-sha256-256s-simple/clean/address.c b/crypto_sign/sphincs-sha256-256s-simple/clean/address.c new file mode 100644 index 00000000..67e83a3d --- /dev/null +++ b/crypto_sign/sphincs-sha256-256s-simple/clean/address.c @@ -0,0 +1,78 @@ +#include + +#include "address.h" +#include "params.h" +#include "utils.h" + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]) { + int i; + + for (i = 0; i < 8; i++) { + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_ull_to_bytes( + bytes + i * 4, 4, addr[i]); + } +} + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer) { + addr[0] = layer; +} + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree) { + addr[1] = 0; + addr[2] = (uint32_t) (tree >> 32); + addr[3] = (uint32_t) tree; +} + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_type( + uint32_t addr[8], uint32_t type) { + addr[4] = type; +} + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; +} + +/* These functions are used for OTS addresses. */ + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair) { + addr[5] = keypair; +} + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; + out[5] = in[5]; +} + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain) { + addr[6] = chain; +} + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash) { + addr[7] = hash; +} + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height) { + addr[6] = tree_height; +} + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index) { + addr[7] = tree_index; +} diff --git a/crypto_sign/sphincs-sha256-256s-simple/clean/address.h b/crypto_sign/sphincs-sha256-256s-simple/clean/address.h new file mode 100644 index 00000000..cb27a5ef --- /dev/null +++ b/crypto_sign/sphincs-sha256-256s-simple/clean/address.h @@ -0,0 +1,50 @@ +#ifndef SPX_ADDRESS_H +#define SPX_ADDRESS_H + +#include + +#define SPX_ADDR_TYPE_WOTS 0 +#define SPX_ADDR_TYPE_WOTSPK 1 +#define SPX_ADDR_TYPE_HASHTREE 2 +#define SPX_ADDR_TYPE_FORSTREE 3 +#define SPX_ADDR_TYPE_FORSPK 4 + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer); + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree); + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_type( + uint32_t addr[8], uint32_t type); + +/* Copies the layer and tree part of one address into the other */ +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for WOTS and FORS addresses. */ + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair); + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain); + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash); + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height); + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index); + +#endif diff --git a/crypto_sign/sphincs-sha256-256s-simple/clean/api.h b/crypto_sign/sphincs-sha256-256s-simple/clean/api.h new file mode 100644 index 00000000..72dff363 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256s-simple/clean/api.h @@ -0,0 +1,78 @@ +#ifndef PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_API_H +#define PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_API_H + +#include +#include + +#define PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_CRYPTO_ALGNAME "SPHINCS+" + +#define PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_CRYPTO_SECRETKEYBYTES 128 +#define PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES 64 +#define PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_CRYPTO_BYTES 29792 +#define PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_CRYPTO_SEEDBYTES 96 + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_crypto_sign_secretkeybytes(void); + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_crypto_sign_publickeybytes(void); + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_crypto_sign_bytes(void); + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_crypto_sign_seedbytes(void); + +/* + * Generates a SPHINCS+ key pair given a seed. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed); + +/* + * Generates a SPHINCS+ key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk); + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk); + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk); + +#endif diff --git a/crypto_sign/sphincs-sha256-256s-simple/clean/fors.c b/crypto_sign/sphincs-sha256-256s-simple/clean/fors.c new file mode 100644 index 00000000..709ba860 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256s-simple/clean/fors.c @@ -0,0 +1,164 @@ +#include +#include +#include + +#include "address.h" +#include "fors.h" +#include "hash.h" +#include "thash.h" +#include "utils.h" + +static void fors_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_prf_addr( + sk, sk_seed, fors_leaf_addr); +} + +static void fors_sk_to_leaf(unsigned char *leaf, const unsigned char *sk, + const unsigned char *pub_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_thash_1( + leaf, sk, pub_seed, fors_leaf_addr); +} + +static void fors_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t fors_tree_addr[8]) { + uint32_t fors_leaf_addr[8] = {0}; + + /* Only copy the parts that must be kept in fors_leaf_addr. */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_copy_keypair_addr( + fors_leaf_addr, fors_tree_addr); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_type( + fors_leaf_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_tree_index( + fors_leaf_addr, addr_idx); + + fors_gen_sk(leaf, sk_seed, fors_leaf_addr); + fors_sk_to_leaf(leaf, leaf, pub_seed, fors_leaf_addr); +} + +/** + * Interprets m as SPX_FORS_HEIGHT-bit unsigned integers. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + * Assumes indices has space for SPX_FORS_TREES integers. + */ +static void message_to_indices(uint32_t *indices, const unsigned char *m) { + unsigned int i, j; + unsigned int offset = 0; + + for (i = 0; i < SPX_FORS_TREES; i++) { + indices[i] = 0; + for (j = 0; j < SPX_FORS_HEIGHT; j++) { + indices[i] ^= (((uint32_t)m[offset >> 3] >> (offset & 0x7)) & 0x1) << j; + offset++; + } + } +} + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Include the secret key part that produces the selected leaf node. */ + fors_gen_sk(sig, sk_seed, fors_tree_addr); + sig += SPX_N; + + /* Compute the authentication path for this leaf node. */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_treehash_FORS_HEIGHT( + roots + i * SPX_N, sig, sk_seed, pub_seed, + indices[i], idx_offset, fors_gen_leaf, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + unsigned char leaf[SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Derive the leaf from the included secret key part. */ + fors_sk_to_leaf(leaf, sig, pub_seed, fors_tree_addr); + sig += SPX_N; + + /* Derive the corresponding root node of this tree. */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_compute_root( + roots + i * SPX_N, leaf, indices[i], idx_offset, sig, + SPX_FORS_HEIGHT, pub_seed, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} diff --git a/crypto_sign/sphincs-sha256-256s-simple/clean/fors.h b/crypto_sign/sphincs-sha256-256s-simple/clean/fors.h new file mode 100644 index 00000000..47c5c1b7 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256s-simple/clean/fors.h @@ -0,0 +1,30 @@ +#ifndef SPX_FORS_H +#define SPX_FORS_H + +#include + +#include "params.h" + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]); + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-sha256-256s-simple/clean/hash.h b/crypto_sign/sphincs-sha256-256s-simple/clean/hash.h new file mode 100644 index 00000000..448a3076 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256s-simple/clean/hash.h @@ -0,0 +1,22 @@ +#ifndef SPX_HASH_H +#define SPX_HASH_H + +#include + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed); + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen); + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen); + +#endif diff --git a/crypto_sign/sphincs-sha256-256s-simple/clean/hash_sha256.c b/crypto_sign/sphincs-sha256-256s-simple/clean/hash_sha256.c new file mode 100644 index 00000000..73cb17d0 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256s-simple/clean/hash_sha256.c @@ -0,0 +1,148 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "utils.h" + +#include "sha2.h" +#include "sha256.h" + +/* For SHA256, there is no immediate reason to initialize at the start, + so this function is an empty operation. */ +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed) { + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_seed_state(pub_seed); + (void)sk_seed; /* Suppress an 'unused parameter' warning. */ +} + +/* + * Computes PRF(key, addr), given a secret key of SPX_N bytes and an address + */ +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]) { + unsigned char buf[SPX_N + SPX_SHA256_ADDR_BYTES]; + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + + memcpy(buf, key, SPX_N); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_compress_address(buf + SPX_N, addr); + + sha256(outbuf, buf, SPX_N + SPX_SHA256_ADDR_BYTES); + memcpy(out, outbuf, SPX_N); +} + +/** + * Computes the message-dependent randomness R, using a secret seed as a key + * for HMAC, and an optional randomization value prefixed to the message. + * This requires m to have at least SPX_SHA256_BLOCK_BYTES + SPX_N space + * available in front of the pointer, i.e. before the message to use for the + * prefix. This is necessary to prevent having to move the message around (and + * allocate memory for it). + */ +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen) { + unsigned char buf[SPX_SHA256_BLOCK_BYTES + SPX_SHA256_OUTPUT_BYTES]; + uint8_t state[40]; + int i; + + /* This implements HMAC-SHA256 */ + for (i = 0; i < SPX_N; i++) { + buf[i] = 0x36 ^ sk_prf[i]; + } + memset(buf + SPX_N, 0x36, SPX_SHA256_BLOCK_BYTES - SPX_N); + + sha256_inc_init(state); + sha256_inc_blocks(state, buf, 1); + + memcpy(buf, optrand, SPX_N); + + /* If optrand + message cannot fill up an entire block */ + if (SPX_N + mlen < SPX_SHA256_BLOCK_BYTES) { + memcpy(buf + SPX_N, m, mlen); + sha256_inc_finalize(buf + SPX_SHA256_BLOCK_BYTES, state, + buf, mlen + SPX_N); + } + /* Otherwise first fill a block, so that finalize only uses the message */ + else { + memcpy(buf + SPX_N, m, SPX_SHA256_BLOCK_BYTES - SPX_N); + sha256_inc_blocks(state, buf, 1); + + m += SPX_SHA256_BLOCK_BYTES - SPX_N; + mlen -= SPX_SHA256_BLOCK_BYTES - SPX_N; + sha256_inc_finalize(buf + SPX_SHA256_BLOCK_BYTES, state, m, mlen); + } + + for (i = 0; i < SPX_N; i++) { + buf[i] = 0x5c ^ sk_prf[i]; + } + memset(buf + SPX_N, 0x5c, SPX_SHA256_BLOCK_BYTES - SPX_N); + + sha256(buf, buf, SPX_SHA256_BLOCK_BYTES + SPX_SHA256_OUTPUT_BYTES); + memcpy(R, buf, SPX_N); +} + +/** + * Computes the message hash using R, the public key, and the message. + * Outputs the message digest and the index of the leaf. The index is split in + * the tree index and the leaf index, for convenient copying to an address. + */ +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen) { +#define SPX_TREE_BITS (SPX_TREE_HEIGHT * (SPX_D - 1)) +#define SPX_TREE_BYTES ((SPX_TREE_BITS + 7) / 8) +#define SPX_LEAF_BITS SPX_TREE_HEIGHT +#define SPX_LEAF_BYTES ((SPX_LEAF_BITS + 7) / 8) +#define SPX_DGST_BYTES (SPX_FORS_MSG_BYTES + SPX_TREE_BYTES + SPX_LEAF_BYTES) + + unsigned char seed[SPX_SHA256_OUTPUT_BYTES + 4]; + + /* Round to nearest multiple of SPX_SHA256_BLOCK_BYTES */ +#define SPX_INBLOCKS (((SPX_N + SPX_PK_BYTES + SPX_SHA256_BLOCK_BYTES - 1) & \ + -SPX_SHA256_BLOCK_BYTES) / SPX_SHA256_BLOCK_BYTES) + unsigned char inbuf[SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES]; + + unsigned char buf[SPX_DGST_BYTES]; + unsigned char *bufp = buf; + uint8_t state[40]; + + sha256_inc_init(state); + + memcpy(inbuf, R, SPX_N); + memcpy(inbuf + SPX_N, pk, SPX_PK_BYTES); + + /* If R + pk + message cannot fill up an entire block */ + if (SPX_N + SPX_PK_BYTES + mlen < SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES) { + memcpy(inbuf + SPX_N + SPX_PK_BYTES, m, mlen); + sha256_inc_finalize(seed, state, inbuf, SPX_N + SPX_PK_BYTES + mlen); + } + /* Otherwise first fill a block, so that finalize only uses the message */ + else { + memcpy(inbuf + SPX_N + SPX_PK_BYTES, m, + SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES); + sha256_inc_blocks(state, inbuf, SPX_INBLOCKS); + + m += SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES; + mlen -= SPX_INBLOCKS * SPX_SHA256_BLOCK_BYTES - SPX_N - SPX_PK_BYTES; + sha256_inc_finalize(seed, state, m, mlen); + } + + /* By doing this in two steps, we prevent hashing the message twice; + otherwise each iteration in MGF1 would hash the message again. */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_mgf1(bufp, SPX_DGST_BYTES, seed, SPX_SHA256_OUTPUT_BYTES); + + memcpy(digest, bufp, SPX_FORS_MSG_BYTES); + bufp += SPX_FORS_MSG_BYTES; + + *tree = PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_bytes_to_ull(bufp, SPX_TREE_BYTES); + *tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS); + bufp += SPX_TREE_BYTES; + + *leaf_idx = (uint32_t)PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_bytes_to_ull( + bufp, SPX_LEAF_BYTES); + *leaf_idx &= (~(uint32_t)0) >> (32 - SPX_LEAF_BITS); +} diff --git a/crypto_sign/sphincs-sha256-256s-simple/clean/params.h b/crypto_sign/sphincs-sha256-256s-simple/clean/params.h new file mode 100644 index 00000000..f023d687 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256s-simple/clean/params.h @@ -0,0 +1,53 @@ +#ifndef SPX_PARAMS_H +#define SPX_PARAMS_H + +/* Hash output length in bytes. */ +#define SPX_N 32 +/* Height of the hypertree. */ +#define SPX_FULL_HEIGHT 64 +/* Number of subtree layer. */ +#define SPX_D 8 +/* FORS tree dimensions. */ +#define SPX_FORS_HEIGHT 14 +#define SPX_FORS_TREES 22 +/* Winternitz parameter, */ +#define SPX_WOTS_W 16 + +/* The hash function is defined by linking a different hash.c file, as opposed + to setting a #define constant. */ + +/* For clarity */ +#define SPX_ADDR_BYTES 32 + +/* WOTS parameters. */ +#define SPX_WOTS_LOGW 4 + +#define SPX_WOTS_LEN1 (8 * SPX_N / SPX_WOTS_LOGW) + +/* SPX_WOTS_LEN2 is floor(log(len_1 * (w - 1)) / log(w)) + 1; we precompute */ +#define SPX_WOTS_LEN2 3 + +#define SPX_WOTS_LEN (SPX_WOTS_LEN1 + SPX_WOTS_LEN2) +#define SPX_WOTS_BYTES (SPX_WOTS_LEN * SPX_N) +#define SPX_WOTS_PK_BYTES SPX_WOTS_BYTES + +/* Subtree size. */ +#define SPX_TREE_HEIGHT (SPX_FULL_HEIGHT / SPX_D) + +/* FORS parameters. */ +#define SPX_FORS_MSG_BYTES ((SPX_FORS_HEIGHT * SPX_FORS_TREES + 7) / 8) +#define SPX_FORS_BYTES ((SPX_FORS_HEIGHT + 1) * SPX_FORS_TREES * SPX_N) +#define SPX_FORS_PK_BYTES SPX_N + +/* Resulting SPX sizes. */ +#define SPX_BYTES (SPX_N + SPX_FORS_BYTES + SPX_D * SPX_WOTS_BYTES +\ + SPX_FULL_HEIGHT * SPX_N) +#define SPX_PK_BYTES (2 * SPX_N) +#define SPX_SK_BYTES (2 * SPX_N + SPX_PK_BYTES) + +/* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ +#define SPX_OPTRAND_BYTES 32 + +#endif diff --git a/crypto_sign/sphincs-sha256-256s-simple/clean/sha256.c b/crypto_sign/sphincs-sha256-256s-simple/clean/sha256.c new file mode 100644 index 00000000..acaa08e0 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256s-simple/clean/sha256.c @@ -0,0 +1,72 @@ +/* Based on the public domain implementation in + * crypto_hash/sha512/ref/ from http://bench.cr.yp.to/supercop.html + * by D. J. Bernstein */ + +#include +#include +#include + +#include "sha2.h" +#include "sha256.h" +#include "utils.h" + +/* + * Compresses an address to a 22-byte sequence. + * This reduces the number of required SHA256 compression calls, as the last + * block of input is padded with at least 65 bits. + */ +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_compress_address(unsigned char *out, const uint32_t addr[8]) { + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_ull_to_bytes(out, 1, addr[0]); /* drop 3 bytes of the layer field */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_ull_to_bytes(out + 1, 4, addr[2]); /* drop the highest tree address word */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_ull_to_bytes(out + 5, 4, addr[3]); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_ull_to_bytes(out + 9, 1, addr[4]); /* drop 3 bytes of the type field */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_ull_to_bytes(out + 10, 4, addr[5]); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_ull_to_bytes(out + 14, 4, addr[6]); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_ull_to_bytes(out + 18, 4, addr[7]); +} + +/** + * Requires 'input_plus_four_bytes' to have 'inlen' + 4 bytes, so that the last + * four bytes can be used for the counter. Typically 'input' is merely a seed. + * Outputs outlen number of bytes + */ +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + unsigned char *input_plus_four_bytes, unsigned long inlen) { + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + unsigned long i; + + /* While we can fit in at least another full block of SHA256 output.. */ + for (i = 0; (i + 1)*SPX_SHA256_OUTPUT_BYTES <= outlen; i++) { + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_ull_to_bytes(input_plus_four_bytes + inlen, 4, i); + sha256(out, input_plus_four_bytes, inlen + 4); + out += SPX_SHA256_OUTPUT_BYTES; + } + /* Until we cannot anymore, and we fill the remainder. */ + if (outlen > i * SPX_SHA256_OUTPUT_BYTES) { + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_ull_to_bytes(input_plus_four_bytes + inlen, 4, i); + sha256(outbuf, input_plus_four_bytes, inlen + 4); + memcpy(out, outbuf, outlen - i * SPX_SHA256_OUTPUT_BYTES); + } +} + +uint8_t PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_state_seeded[40]; + +/** + * Absorb the constant pub_seed using one round of the compression function + * This initializes state_seeded, which can then be reused in thash + **/ +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_seed_state(const unsigned char *pub_seed) { + uint8_t block[SPX_SHA256_BLOCK_BYTES]; + size_t i; + + for (i = 0; i < SPX_N; ++i) { + block[i] = pub_seed[i]; + } + for (i = SPX_N; i < SPX_SHA256_BLOCK_BYTES; ++i) { + block[i] = 0; + } + + sha256_inc_init(PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_state_seeded); + sha256_inc_blocks(PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_state_seeded, block, 1); +} diff --git a/crypto_sign/sphincs-sha256-256s-simple/clean/sha256.h b/crypto_sign/sphincs-sha256-256s-simple/clean/sha256.h new file mode 100644 index 00000000..d3971c06 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256s-simple/clean/sha256.h @@ -0,0 +1,21 @@ +#ifndef SPX_SHA256_H +#define SPX_SHA256_H + +#define SPX_SHA256_BLOCK_BYTES 64 +#define SPX_SHA256_OUTPUT_BYTES 32 /* This does not necessarily equal SPX_N */ +#define SPX_SHA256_ADDR_BYTES 22 + +#include +#include + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_compress_address(unsigned char *out, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + unsigned char *input_plus_four_bytes, unsigned long inlen); + +uint8_t PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_state_seeded[40]; + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_seed_state(const unsigned char *pub_seed); + +#endif diff --git a/crypto_sign/sphincs-sha256-256s-simple/clean/sign.c b/crypto_sign/sphincs-sha256-256s-simple/clean/sign.c new file mode 100644 index 00000000..710bb8b4 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256s-simple/clean/sign.c @@ -0,0 +1,344 @@ +#include +#include +#include + +#include "address.h" +#include "api.h" +#include "fors.h" +#include "hash.h" +#include "params.h" +#include "randombytes.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +/** + * Computes the leaf at a given address. First generates the WOTS key pair, + * then computes leaf by hashing horizontally. + */ +static void wots_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t tree_addr[8]) { + unsigned char pk[SPX_WOTS_BYTES]; + uint32_t wots_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, addr_idx); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_wots_gen_pk( + pk, sk_seed, pub_seed, wots_addr); + + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_thash_WOTS_LEN( + leaf, pk, pub_seed, wots_pk_addr); +} + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_crypto_sign_secretkeybytes(void) { + return PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_CRYPTO_SECRETKEYBYTES; +} + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_crypto_sign_publickeybytes(void) { + return PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES; +} + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_crypto_sign_bytes(void) { + return PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_CRYPTO_BYTES; +} + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_crypto_sign_seedbytes(void) { + return PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_CRYPTO_SEEDBYTES; +} + +/* + * Generates an SPX key pair given a seed of length + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed) { + /* We do not need the auth path in key generation, but it simplifies the + code to have just one treehash routine that computes both root and path + in one function. */ + unsigned char auth_path[SPX_TREE_HEIGHT * SPX_N]; + uint32_t top_tree_addr[8] = {0}; + + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_layer_addr( + top_tree_addr, SPX_D - 1); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_type( + top_tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */ + memcpy(sk, seed, PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_CRYPTO_SEEDBYTES); + + memcpy(pk, sk + 2 * SPX_N, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_initialize_hash_function(pk, sk); + + /* Compute root node of the top-most subtree. */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_treehash_TREE_HEIGHT( + sk + 3 * SPX_N, auth_path, sk, sk + 2 * SPX_N, 0, 0, + wots_gen_leaf, top_tree_addr); + + memcpy(pk + SPX_N, sk + 3 * SPX_N, SPX_N); + + return 0; +} + +/* + * Generates an SPX key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk) { + unsigned char seed[PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_CRYPTO_SEEDBYTES]; + randombytes(seed, PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_CRYPTO_SEEDBYTES); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_crypto_sign_seed_keypair( + pk, sk, seed); + + return 0; +} + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + const unsigned char *sk_seed = sk; + const unsigned char *sk_prf = sk + SPX_N; + const unsigned char *pk = sk + 2 * SPX_N; + const unsigned char *pub_seed = pk; + + unsigned char optrand[SPX_N]; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char root[SPX_N]; + uint32_t i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_initialize_hash_function( + pub_seed, sk_seed); + + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ + randombytes(optrand, SPX_N); + /* Compute the digest randomization value. */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_gen_message_random( + sig, sk_prf, optrand, m, mlen); + + /* Derive the message digest and leaf index from R, PK and M. */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Sign the message hash using FORS. */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_fors_sign( + sig, root, mhash, sk_seed, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Compute a WOTS signature. */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_wots_sign( + sig, root, sk_seed, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the authentication path for the used WOTS leaf. */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_treehash_TREE_HEIGHT( + root, sig, sk_seed, pub_seed, idx_leaf, 0, + wots_gen_leaf, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + *siglen = SPX_BYTES; + + return 0; +} + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk) { + const unsigned char *pub_seed = pk; + const unsigned char *pub_root = pk + SPX_N; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char wots_pk[SPX_WOTS_BYTES]; + unsigned char root[SPX_N]; + unsigned char leaf[SPX_N]; + unsigned int i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + if (siglen != SPX_BYTES) { + return -1; + } + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_initialize_hash_function( + pub_seed, NULL); + + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + /* Derive the message digest and leaf index from R || PK || M. */ + /* The additional SPX_N is a result of the hash domain separator. */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + /* Layer correctly defaults to 0, so no need to set_layer_addr */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_fors_pk_from_sig( + root, sig, mhash, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + /* For each subtree.. */ + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + + /* The WOTS public key is only correct if the signature was correct. */ + /* Initially, root is the FORS pk, but on subsequent iterations it is + the root of the subtree below the currently processed subtree. */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_wots_pk_from_sig( + wots_pk, sig, root, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the leaf node using the WOTS public key. */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_thash_WOTS_LEN( + leaf, wots_pk, pub_seed, wots_pk_addr); + + /* Compute the root node of this subtree. */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_compute_root( + root, leaf, idx_leaf, 0, sig, SPX_TREE_HEIGHT, + pub_seed, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + /* Check if the root node equals the root node in the public key. */ + if (memcmp(root, pub_root, SPX_N) != 0) { + return -1; + } + + return 0; +} + + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + size_t siglen; + + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_crypto_sign_signature( + sm, &siglen, m, mlen, sk); + + memmove(sm + SPX_BYTES, m, mlen); + *smlen = siglen + mlen; + + return 0; +} + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk) { + /* The API caller does not necessarily know what size a signature should be + but SPHINCS+ signatures are always exactly SPX_BYTES. */ + if (smlen < SPX_BYTES) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + *mlen = smlen - SPX_BYTES; + + if (PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_crypto_sign_verify( + sm, SPX_BYTES, sm + SPX_BYTES, *mlen, pk)) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + /* If verification was successful, move the message to the right place. */ + memmove(m, sm + SPX_BYTES, *mlen); + + return 0; +} diff --git a/crypto_sign/sphincs-sha256-256s-simple/clean/thash.h b/crypto_sign/sphincs-sha256-256s-simple/clean/thash.h new file mode 100644 index 00000000..1342406b --- /dev/null +++ b/crypto_sign/sphincs-sha256-256s-simple/clean/thash.h @@ -0,0 +1,22 @@ +#ifndef SPX_THASH_H +#define SPX_THASH_H + +#include + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-sha256-256s-simple/clean/thash_sha256_simple.c b/crypto_sign/sphincs-sha256-256s-simple/clean/thash_sha256_simple.c new file mode 100644 index 00000000..67d008c6 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256s-simple/clean/thash_sha256_simple.c @@ -0,0 +1,70 @@ +#include +#include + +#include "address.h" +#include "params.h" +#include "thash.h" + +#include "sha2.h" +#include "sha256.h" + +/** + * Takes an array of inblocks concatenated arrays of SPX_N bytes. + */ +static void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_thash( + unsigned char *out, unsigned char *buf, + const unsigned char *in, unsigned int inblocks, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + uint8_t sha2_state[40]; + + (void)pub_seed; /* Suppress an 'unused parameter' warning. */ + + /* Retrieve precomputed state containing pub_seed */ + memcpy(sha2_state, PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_state_seeded, 40 * sizeof(uint8_t)); + + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_compress_address(buf, addr); + memcpy(buf + SPX_SHA256_ADDR_BYTES, in, inblocks * SPX_N); + + sha256_inc_finalize(outbuf, sha2_state, buf, SPX_SHA256_ADDR_BYTES + inblocks * SPX_N); + memcpy(out, outbuf, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_SHA256_ADDR_BYTES + 1 * SPX_N]; + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_thash( + out, buf, in, 1, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_SHA256_ADDR_BYTES + 2 * SPX_N]; + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_thash( + out, buf, in, 2, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_SHA256_ADDR_BYTES + SPX_WOTS_LEN * SPX_N]; + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_thash( + out, buf, in, SPX_WOTS_LEN, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_SHA256_ADDR_BYTES + SPX_FORS_TREES * SPX_N]; + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_thash( + out, buf, in, SPX_FORS_TREES, pub_seed, addr); +} diff --git a/crypto_sign/sphincs-sha256-256s-simple/clean/utils.c b/crypto_sign/sphincs-sha256-256s-simple/clean/utils.c new file mode 100644 index 00000000..329d5766 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256s-simple/clean/utils.c @@ -0,0 +1,192 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in) { + + /* Iterate over out in decreasing order, for big-endianness. */ + for (size_t i = outlen; i > 0; i--) { + out[i - 1] = in & 0xff; + in = in >> 8; + } +} + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen) { + unsigned long long retval = 0; + + for (size_t i = 0; i < inlen; i++) { + retval |= ((unsigned long long)in[i]) << (8 * (inlen - 1 - i)); + } + return retval; +} + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + unsigned char buffer[2 * SPX_N]; + + /* If leaf_idx is odd (last bit = 1), current path element is a right child + and auth_path has to go left. Otherwise it is the other way around. */ + if (leaf_idx & 1) { + memcpy(buffer + SPX_N, leaf, SPX_N); + memcpy(buffer, auth_path, SPX_N); + } else { + memcpy(buffer, leaf, SPX_N); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + + for (i = 0; i < tree_height - 1; i++) { + leaf_idx >>= 1; + idx_offset >>= 1; + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_tree_height(addr, i + 1); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + + /* Pick the right or left neighbor, depending on parity of the node. */ + if (leaf_idx & 1) { + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_thash_2( + buffer + SPX_N, buffer, pub_seed, addr); + memcpy(buffer, auth_path, SPX_N); + } else { + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_thash_2( + buffer, buffer, pub_seed, addr); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + } + + /* The last iteration is exceptional; we do not copy an auth_path node. */ + leaf_idx >>= 1; + idx_offset >>= 1; + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_tree_height(addr, tree_height); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_thash_2( + root, buffer, pub_seed, addr); +} + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +static void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_treehash( + unsigned char *root, unsigned char *auth_path, + unsigned char *stack, unsigned int *heights, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned int offset = 0; + uint32_t idx; + uint32_t tree_idx; + + for (idx = 0; idx < (uint32_t)(1 << tree_height); idx++) { + /* Add the next leaf node to the stack. */ + gen_leaf(stack + offset * SPX_N, + sk_seed, pub_seed, idx + idx_offset, tree_addr); + offset++; + heights[offset - 1] = 0; + + /* If this is a node we need for the auth path.. */ + if ((leaf_idx ^ 0x1) == idx) { + memcpy(auth_path, stack + (offset - 1)*SPX_N, SPX_N); + } + + /* While the top-most nodes are of equal height.. */ + while (offset >= 2 && heights[offset - 1] == heights[offset - 2]) { + /* Compute index of the new node, in the next layer. */ + tree_idx = (idx >> (heights[offset - 1] + 1)); + + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_tree_height( + tree_addr, heights[offset - 1] + 1); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_tree_index( + tree_addr, tree_idx + (idx_offset >> (heights[offset - 1] + 1))); + /* Hash the top-most nodes from the stack together. */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_thash_2( + stack + (offset - 2)*SPX_N, stack + (offset - 2)*SPX_N, + pub_seed, tree_addr); + offset--; + /* Note that the top-most node is now one layer higher. */ + heights[offset - 1]++; + + /* If this is a node we need for the auth path.. */ + if (((leaf_idx >> heights[offset - 1]) ^ 0x1) == tree_idx) { + memcpy(auth_path + heights[offset - 1]*SPX_N, + stack + (offset - 1)*SPX_N, SPX_N); + } + } + } + memcpy(root, stack, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_FORS_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_FORS_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_FORS_HEIGHT, gen_leaf, tree_addr); +} + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_TREE_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_TREE_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_TREE_HEIGHT, gen_leaf, tree_addr); +} diff --git a/crypto_sign/sphincs-sha256-256s-simple/clean/utils.h b/crypto_sign/sphincs-sha256-256s-simple/clean/utils.h new file mode 100644 index 00000000..98b404b4 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256s-simple/clean/utils.h @@ -0,0 +1,60 @@ +#ifndef SPX_UTILS_H +#define SPX_UTILS_H + +#include "params.h" +#include +#include + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in); + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen); + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-sha256-256s-simple/clean/wots.c b/crypto_sign/sphincs-sha256-256s-simple/clean/wots.c new file mode 100644 index 00000000..26b06731 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256s-simple/clean/wots.c @@ -0,0 +1,161 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +// TODO clarify address expectations, and make them more uniform. +// TODO i.e. do we expect types to be set already? +// TODO and do we expect modifications or copies? + +/** + * Computes the starting value for a chain, i.e. the secret key. + * Expects the address to be complete up to the chain address. + */ +static void wots_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t wots_addr[8]) { + /* Make sure that the hash address is actually zeroed. */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_hash_addr(wots_addr, 0); + + /* Generate sk element. */ + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_prf_addr(sk, sk_seed, wots_addr); +} + +/** + * Computes the chaining function. + * out and in have to be n-byte arrays. + * + * Interprets in as start-th value of the chain. + * addr has to contain the address of the chain. + */ +static void gen_chain(unsigned char *out, const unsigned char *in, + unsigned int start, unsigned int steps, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + /* Initialize out with the value at position 'start'. */ + memcpy(out, in, SPX_N); + + /* Iterate 'steps' calls to the hash function. */ + for (i = start; i < (start + steps) && i < SPX_WOTS_W; i++) { + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_hash_addr(addr, i); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_thash_1( + out, out, pub_seed, addr); + } +} + +/** + * base_w algorithm as described in draft. + * Interprets an array of bytes as integers in base w. + * This only works when log_w is a divisor of 8. + */ +static void base_w(unsigned int *output, const size_t out_len, + const unsigned char *input) { + size_t in = 0; + size_t out = 0; + unsigned char total = 0; + unsigned int bits = 0; + size_t consumed; + + for (consumed = 0; consumed < out_len; consumed++) { + if (bits == 0) { + total = input[in]; + in++; + bits += 8; + } + bits -= SPX_WOTS_LOGW; + output[out] = (unsigned int)((total >> bits) & (SPX_WOTS_W - 1)); + out++; + } +} + +/* Computes the WOTS+ checksum over a message (in base_w). */ +static void wots_checksum(unsigned int *csum_base_w, + const unsigned int *msg_base_w) { + unsigned int csum = 0; + unsigned char csum_bytes[(SPX_WOTS_LEN2 * SPX_WOTS_LOGW + 7) / 8]; + unsigned int i; + + /* Compute checksum. */ + for (i = 0; i < SPX_WOTS_LEN1; i++) { + csum += SPX_WOTS_W - 1 - msg_base_w[i]; + } + + /* Convert checksum to base_w. */ + /* Make sure expected empty zero bits are the least significant bits. */ + csum = csum << (8 - ((SPX_WOTS_LEN2 * SPX_WOTS_LOGW) % 8)); + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_ull_to_bytes( + csum_bytes, sizeof(csum_bytes), csum); + base_w(csum_base_w, SPX_WOTS_LEN2, csum_bytes); +} + +/* Takes a message and derives the matching chain lengths. */ +static void chain_lengths(unsigned int *lengths, const unsigned char *msg) { + base_w(lengths, SPX_WOTS_LEN1, msg); + wots_checksum(lengths + SPX_WOTS_LEN1, lengths); +} + +/** + * WOTS key generation. Takes a 32 byte sk_seed, expands it to WOTS private key + * elements and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(pk + i * SPX_N, sk_seed, addr); + gen_chain(pk + i * SPX_N, pk + i * SPX_N, + 0, SPX_WOTS_W - 1, pub_seed, addr); + } +} + +/** + * Takes a n-byte message and the 32-byte sk_see to compute a signature 'sig'. + */ +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(sig + i * SPX_N, sk_seed, addr); + gen_chain(sig + i * SPX_N, sig + i * SPX_N, 0, lengths[i], pub_seed, addr); + } +} + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_set_chain_addr(addr, i); + gen_chain(pk + i * SPX_N, sig + i * SPX_N, + lengths[i], SPX_WOTS_W - 1 - lengths[i], pub_seed, addr); + } +} diff --git a/crypto_sign/sphincs-sha256-256s-simple/clean/wots.h b/crypto_sign/sphincs-sha256-256s-simple/clean/wots.h new file mode 100644 index 00000000..8ada7430 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256s-simple/clean/wots.h @@ -0,0 +1,38 @@ +#ifndef SPX_WOTS_H +#define SPX_WOTS_H + +#include "params.h" +#include + +/** + * WOTS key generation. Takes a 32 byte seed for the private key, expands it to + * a full WOTS private key and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * Takes a n-byte message and the 32-byte seed for the private key to compute a + * signature that is placed at 'sig'. + */ +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]); + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-shake256-128f-robust/META.yml b/crypto_sign/sphincs-shake256-128f-robust/META.yml new file mode 100644 index 00000000..9b2474f3 --- /dev/null +++ b/crypto_sign/sphincs-shake256-128f-robust/META.yml @@ -0,0 +1,27 @@ +name: SPHINCS+ +type: signature +claimed-nist-level: 1 +length-public-key: 32 +length-secret-key: 64 +length-signature: 16976 +testvectors-sha256: eea7f59958e732c15110d0d06e3c23005d73df2b15a1e7b4ebc0ca2dcf162bb5 +principal-submitter: Andreas Hülsing +auxiliary-submitters: + - Jean-Philippe Aumasson + - Daniel J. Bernstein, + - Christoph Dobraunig + - Maria Eichlseder + - Scott Fluhrer + - Stefan-Lukas Gazdag + - Panos Kampanakis + - Stefan Kölbl + - Tanja Lange + - Martin M. Lauridsen + - Florian Mendel + - Ruben Niederhagen + - Christian Rechberger + - Joost Rijneveld + - Peter Schwabe +implementations: + - name: clean + version: https://github.com/sphincs/sphincsplus/commit/77755c94d0bc744478044d6efbb888dc13156441 diff --git a/crypto_sign/sphincs-shake256-128f-robust/clean/LICENSE b/crypto_sign/sphincs-shake256-128f-robust/clean/LICENSE new file mode 100644 index 00000000..670154e3 --- /dev/null +++ b/crypto_sign/sphincs-shake256-128f-robust/clean/LICENSE @@ -0,0 +1,116 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + diff --git a/crypto_sign/sphincs-shake256-128f-robust/clean/Makefile b/crypto_sign/sphincs-shake256-128f-robust/clean/Makefile new file mode 100644 index 00000000..86825431 --- /dev/null +++ b/crypto_sign/sphincs-shake256-128f-robust/clean/Makefile @@ -0,0 +1,20 @@ +# This Makefile can be used with GNU Make or BSD Make + +LIB=libsphincs-shake256-128f-robust_clean.a + +HEADERS = params.h address.h wots.h utils.h fors.h api.h hash.h thash.h +OBJECTS = address.o wots.o utils.o fors.o sign.o hash_shake256.o thash_shake256_robust.o + +CFLAGS=-O3 -Wall -Wconversion -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) + +all: $(LIB) + +%.o: %.c $(HEADERS) + $(CC) $(CFLAGS) -c -o $@ $< + +$(LIB): $(OBJECTS) + $(AR) -r $@ $(OBJECTS) + +clean: + $(RM) $(OBJECTS) + $(RM) $(LIB) diff --git a/crypto_sign/sphincs-shake256-128f-robust/clean/Makefile.Microsoft_nmake b/crypto_sign/sphincs-shake256-128f-robust/clean/Makefile.Microsoft_nmake new file mode 100644 index 00000000..e65c9aac --- /dev/null +++ b/crypto_sign/sphincs-shake256-128f-robust/clean/Makefile.Microsoft_nmake @@ -0,0 +1,19 @@ +# This Makefile can be used with Microsoft Visual Studio's nmake using the command: +# nmake /f Makefile.Microsoft_nmake + +LIBRARY=libsphincs-shake256-128f-robust_clean.lib +OBJECTS=address.obj wots.obj utils.obj fors.obj sign.obj hash_shake256.obj thash_shake256_robust.obj + +CFLAGS=/nologo /I ..\..\..\common /W4 /WX + +all: $(LIBRARY) + +# Make sure objects are recompiled if headers change. +$(OBJECTS): *.h + +$(LIBRARY): $(OBJECTS) + LIB.EXE /NOLOGO /WX /OUT:$@ $** + +clean: + -DEL $(OBJECTS) + -DEL $(LIBRARY) diff --git a/crypto_sign/sphincs-shake256-128f-robust/clean/address.c b/crypto_sign/sphincs-shake256-128f-robust/clean/address.c new file mode 100644 index 00000000..dd5d233e --- /dev/null +++ b/crypto_sign/sphincs-shake256-128f-robust/clean/address.c @@ -0,0 +1,78 @@ +#include + +#include "address.h" +#include "params.h" +#include "utils.h" + +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]) { + int i; + + for (i = 0; i < 8; i++) { + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_ull_to_bytes( + bytes + i * 4, 4, addr[i]); + } +} + +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer) { + addr[0] = layer; +} + +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree) { + addr[1] = 0; + addr[2] = (uint32_t) (tree >> 32); + addr[3] = (uint32_t) tree; +} + +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_type( + uint32_t addr[8], uint32_t type) { + addr[4] = type; +} + +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; +} + +/* These functions are used for OTS addresses. */ + +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair) { + addr[5] = keypair; +} + +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; + out[5] = in[5]; +} + +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain) { + addr[6] = chain; +} + +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash) { + addr[7] = hash; +} + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height) { + addr[6] = tree_height; +} + +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index) { + addr[7] = tree_index; +} diff --git a/crypto_sign/sphincs-shake256-128f-robust/clean/address.h b/crypto_sign/sphincs-shake256-128f-robust/clean/address.h new file mode 100644 index 00000000..23b3ed87 --- /dev/null +++ b/crypto_sign/sphincs-shake256-128f-robust/clean/address.h @@ -0,0 +1,50 @@ +#ifndef SPX_ADDRESS_H +#define SPX_ADDRESS_H + +#include + +#define SPX_ADDR_TYPE_WOTS 0 +#define SPX_ADDR_TYPE_WOTSPK 1 +#define SPX_ADDR_TYPE_HASHTREE 2 +#define SPX_ADDR_TYPE_FORSTREE 3 +#define SPX_ADDR_TYPE_FORSPK 4 + +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer); + +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree); + +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_type( + uint32_t addr[8], uint32_t type); + +/* Copies the layer and tree part of one address into the other */ +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for WOTS and FORS addresses. */ + +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair); + +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain); + +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash); + +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height); + +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index); + +#endif diff --git a/crypto_sign/sphincs-shake256-128f-robust/clean/api.h b/crypto_sign/sphincs-shake256-128f-robust/clean/api.h new file mode 100644 index 00000000..9a599525 --- /dev/null +++ b/crypto_sign/sphincs-shake256-128f-robust/clean/api.h @@ -0,0 +1,78 @@ +#ifndef PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_API_H +#define PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_API_H + +#include +#include + +#define PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_CRYPTO_ALGNAME "SPHINCS+" + +#define PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_CRYPTO_SECRETKEYBYTES 64 +#define PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES 32 +#define PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_CRYPTO_BYTES 16976 +#define PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_CRYPTO_SEEDBYTES 48 + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_crypto_sign_secretkeybytes(void); + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_crypto_sign_publickeybytes(void); + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_crypto_sign_bytes(void); + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_crypto_sign_seedbytes(void); + +/* + * Generates a SPHINCS+ key pair given a seed. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed); + +/* + * Generates a SPHINCS+ key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk); + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk); + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk); + +#endif diff --git a/crypto_sign/sphincs-shake256-128f-robust/clean/fors.c b/crypto_sign/sphincs-shake256-128f-robust/clean/fors.c new file mode 100644 index 00000000..9faccedc --- /dev/null +++ b/crypto_sign/sphincs-shake256-128f-robust/clean/fors.c @@ -0,0 +1,164 @@ +#include +#include +#include + +#include "address.h" +#include "fors.h" +#include "hash.h" +#include "thash.h" +#include "utils.h" + +static void fors_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_prf_addr( + sk, sk_seed, fors_leaf_addr); +} + +static void fors_sk_to_leaf(unsigned char *leaf, const unsigned char *sk, + const unsigned char *pub_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_thash_1( + leaf, sk, pub_seed, fors_leaf_addr); +} + +static void fors_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t fors_tree_addr[8]) { + uint32_t fors_leaf_addr[8] = {0}; + + /* Only copy the parts that must be kept in fors_leaf_addr. */ + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_copy_keypair_addr( + fors_leaf_addr, fors_tree_addr); + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_type( + fors_leaf_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_tree_index( + fors_leaf_addr, addr_idx); + + fors_gen_sk(leaf, sk_seed, fors_leaf_addr); + fors_sk_to_leaf(leaf, leaf, pub_seed, fors_leaf_addr); +} + +/** + * Interprets m as SPX_FORS_HEIGHT-bit unsigned integers. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + * Assumes indices has space for SPX_FORS_TREES integers. + */ +static void message_to_indices(uint32_t *indices, const unsigned char *m) { + unsigned int i, j; + unsigned int offset = 0; + + for (i = 0; i < SPX_FORS_TREES; i++) { + indices[i] = 0; + for (j = 0; j < SPX_FORS_HEIGHT; j++) { + indices[i] ^= (((uint32_t)m[offset >> 3] >> (offset & 0x7)) & 0x1) << j; + offset++; + } + } +} + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Include the secret key part that produces the selected leaf node. */ + fors_gen_sk(sig, sk_seed, fors_tree_addr); + sig += SPX_N; + + /* Compute the authentication path for this leaf node. */ + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_treehash_FORS_HEIGHT( + roots + i * SPX_N, sig, sk_seed, pub_seed, + indices[i], idx_offset, fors_gen_leaf, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + unsigned char leaf[SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Derive the leaf from the included secret key part. */ + fors_sk_to_leaf(leaf, sig, pub_seed, fors_tree_addr); + sig += SPX_N; + + /* Derive the corresponding root node of this tree. */ + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_compute_root( + roots + i * SPX_N, leaf, indices[i], idx_offset, sig, + SPX_FORS_HEIGHT, pub_seed, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} diff --git a/crypto_sign/sphincs-shake256-128f-robust/clean/fors.h b/crypto_sign/sphincs-shake256-128f-robust/clean/fors.h new file mode 100644 index 00000000..1d5dbc91 --- /dev/null +++ b/crypto_sign/sphincs-shake256-128f-robust/clean/fors.h @@ -0,0 +1,30 @@ +#ifndef SPX_FORS_H +#define SPX_FORS_H + +#include + +#include "params.h" + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]); + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-shake256-128f-robust/clean/hash.h b/crypto_sign/sphincs-shake256-128f-robust/clean/hash.h new file mode 100644 index 00000000..6c549e5c --- /dev/null +++ b/crypto_sign/sphincs-shake256-128f-robust/clean/hash.h @@ -0,0 +1,22 @@ +#ifndef SPX_HASH_H +#define SPX_HASH_H + +#include + +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed); + +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen); + +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen); + +#endif diff --git a/crypto_sign/sphincs-shake256-128f-robust/clean/hash_shake256.c b/crypto_sign/sphincs-shake256-128f-robust/clean/hash_shake256.c new file mode 100644 index 00000000..9603b8c8 --- /dev/null +++ b/crypto_sign/sphincs-shake256-128f-robust/clean/hash_shake256.c @@ -0,0 +1,87 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "utils.h" + +#include "fips202.h" + +/* For SHAKE256, there is no immediate reason to initialize at the start, + so this function is an empty operation. */ +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed) { + (void)pub_seed; /* Suppress an 'unused parameter' warning. */ + (void)sk_seed; /* Suppress an 'unused parameter' warning. */ +} + +/* + * Computes PRF(key, addr), given a secret key of SPX_N bytes and an address + */ +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]) { + unsigned char buf[SPX_N + SPX_ADDR_BYTES]; + + memcpy(buf, key, SPX_N); + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_addr_to_bytes(buf + SPX_N, addr); + + shake256(out, SPX_N, buf, SPX_N + SPX_ADDR_BYTES); +} + +/** + * Computes the message-dependent randomness R, using a secret seed and an + * optional randomization value as well as the message. + */ +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen) { + uint64_t s_inc[26]; + + shake256_inc_init(s_inc); + shake256_inc_absorb(s_inc, sk_prf, SPX_N); + shake256_inc_absorb(s_inc, optrand, SPX_N); + shake256_inc_absorb(s_inc, m, mlen); + shake256_inc_finalize(s_inc); + shake256_inc_squeeze(R, SPX_N, s_inc); +} + +/** + * Computes the message hash using R, the public key, and the message. + * Outputs the message digest and the index of the leaf. The index is split in + * the tree index and the leaf index, for convenient copying to an address. + */ +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen) { +#define SPX_TREE_BITS (SPX_TREE_HEIGHT * (SPX_D - 1)) +#define SPX_TREE_BYTES ((SPX_TREE_BITS + 7) / 8) +#define SPX_LEAF_BITS SPX_TREE_HEIGHT +#define SPX_LEAF_BYTES ((SPX_LEAF_BITS + 7) / 8) +#define SPX_DGST_BYTES (SPX_FORS_MSG_BYTES + SPX_TREE_BYTES + SPX_LEAF_BYTES) + + unsigned char buf[SPX_DGST_BYTES]; + unsigned char *bufp = buf; + uint64_t s_inc[26]; + + shake256_inc_init(s_inc); + shake256_inc_absorb(s_inc, R, SPX_N); + shake256_inc_absorb(s_inc, pk, SPX_PK_BYTES); + shake256_inc_absorb(s_inc, m, mlen); + shake256_inc_finalize(s_inc); + shake256_inc_squeeze(buf, SPX_DGST_BYTES, s_inc); + + memcpy(digest, bufp, SPX_FORS_MSG_BYTES); + bufp += SPX_FORS_MSG_BYTES; + + *tree = PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_bytes_to_ull( + bufp, SPX_TREE_BYTES); + *tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS); + bufp += SPX_TREE_BYTES; + + *leaf_idx = (uint32_t)PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_bytes_to_ull( + bufp, SPX_LEAF_BYTES); + *leaf_idx &= (~(uint32_t)0) >> (32 - SPX_LEAF_BITS); +} diff --git a/crypto_sign/sphincs-shake256-128f-robust/clean/params.h b/crypto_sign/sphincs-shake256-128f-robust/clean/params.h new file mode 100644 index 00000000..367ef88d --- /dev/null +++ b/crypto_sign/sphincs-shake256-128f-robust/clean/params.h @@ -0,0 +1,53 @@ +#ifndef SPX_PARAMS_H +#define SPX_PARAMS_H + +/* Hash output length in bytes. */ +#define SPX_N 16 +/* Height of the hypertree. */ +#define SPX_FULL_HEIGHT 60 +/* Number of subtree layer. */ +#define SPX_D 20 +/* FORS tree dimensions. */ +#define SPX_FORS_HEIGHT 9 +#define SPX_FORS_TREES 30 +/* Winternitz parameter, */ +#define SPX_WOTS_W 16 + +/* The hash function is defined by linking a different hash.c file, as opposed + to setting a #define constant. */ + +/* For clarity */ +#define SPX_ADDR_BYTES 32 + +/* WOTS parameters. */ +#define SPX_WOTS_LOGW 4 + +#define SPX_WOTS_LEN1 (8 * SPX_N / SPX_WOTS_LOGW) + +/* SPX_WOTS_LEN2 is floor(log(len_1 * (w - 1)) / log(w)) + 1; we precompute */ +#define SPX_WOTS_LEN2 3 + +#define SPX_WOTS_LEN (SPX_WOTS_LEN1 + SPX_WOTS_LEN2) +#define SPX_WOTS_BYTES (SPX_WOTS_LEN * SPX_N) +#define SPX_WOTS_PK_BYTES SPX_WOTS_BYTES + +/* Subtree size. */ +#define SPX_TREE_HEIGHT (SPX_FULL_HEIGHT / SPX_D) + +/* FORS parameters. */ +#define SPX_FORS_MSG_BYTES ((SPX_FORS_HEIGHT * SPX_FORS_TREES + 7) / 8) +#define SPX_FORS_BYTES ((SPX_FORS_HEIGHT + 1) * SPX_FORS_TREES * SPX_N) +#define SPX_FORS_PK_BYTES SPX_N + +/* Resulting SPX sizes. */ +#define SPX_BYTES (SPX_N + SPX_FORS_BYTES + SPX_D * SPX_WOTS_BYTES +\ + SPX_FULL_HEIGHT * SPX_N) +#define SPX_PK_BYTES (2 * SPX_N) +#define SPX_SK_BYTES (2 * SPX_N + SPX_PK_BYTES) + +/* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ +#define SPX_OPTRAND_BYTES 32 + +#endif diff --git a/crypto_sign/sphincs-shake256-128f-robust/clean/sign.c b/crypto_sign/sphincs-shake256-128f-robust/clean/sign.c new file mode 100644 index 00000000..a79527ec --- /dev/null +++ b/crypto_sign/sphincs-shake256-128f-robust/clean/sign.c @@ -0,0 +1,344 @@ +#include +#include +#include + +#include "address.h" +#include "api.h" +#include "fors.h" +#include "hash.h" +#include "params.h" +#include "randombytes.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +/** + * Computes the leaf at a given address. First generates the WOTS key pair, + * then computes leaf by hashing horizontally. + */ +static void wots_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t tree_addr[8]) { + unsigned char pk[SPX_WOTS_BYTES]; + uint32_t wots_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_keypair_addr( + wots_addr, addr_idx); + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_wots_gen_pk( + pk, sk_seed, pub_seed, wots_addr); + + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_thash_WOTS_LEN( + leaf, pk, pub_seed, wots_pk_addr); +} + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_crypto_sign_secretkeybytes(void) { + return PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_CRYPTO_SECRETKEYBYTES; +} + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_crypto_sign_publickeybytes(void) { + return PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES; +} + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_crypto_sign_bytes(void) { + return PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_CRYPTO_BYTES; +} + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_crypto_sign_seedbytes(void) { + return PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_CRYPTO_SEEDBYTES; +} + +/* + * Generates an SPX key pair given a seed of length + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed) { + /* We do not need the auth path in key generation, but it simplifies the + code to have just one treehash routine that computes both root and path + in one function. */ + unsigned char auth_path[SPX_TREE_HEIGHT * SPX_N]; + uint32_t top_tree_addr[8] = {0}; + + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_layer_addr( + top_tree_addr, SPX_D - 1); + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_type( + top_tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */ + memcpy(sk, seed, PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_CRYPTO_SEEDBYTES); + + memcpy(pk, sk + 2 * SPX_N, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_initialize_hash_function(pk, sk); + + /* Compute root node of the top-most subtree. */ + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_treehash_TREE_HEIGHT( + sk + 3 * SPX_N, auth_path, sk, sk + 2 * SPX_N, 0, 0, + wots_gen_leaf, top_tree_addr); + + memcpy(pk + SPX_N, sk + 3 * SPX_N, SPX_N); + + return 0; +} + +/* + * Generates an SPX key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk) { + unsigned char seed[PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_CRYPTO_SEEDBYTES]; + randombytes(seed, PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_CRYPTO_SEEDBYTES); + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_crypto_sign_seed_keypair( + pk, sk, seed); + + return 0; +} + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + const unsigned char *sk_seed = sk; + const unsigned char *sk_prf = sk + SPX_N; + const unsigned char *pk = sk + 2 * SPX_N; + const unsigned char *pub_seed = pk; + + unsigned char optrand[SPX_N]; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char root[SPX_N]; + uint32_t i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_initialize_hash_function( + pub_seed, sk_seed); + + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ + randombytes(optrand, SPX_N); + /* Compute the digest randomization value. */ + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_gen_message_random( + sig, sk_prf, optrand, m, mlen); + + /* Derive the message digest and leaf index from R, PK and M. */ + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Sign the message hash using FORS. */ + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_fors_sign( + sig, root, mhash, sk_seed, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Compute a WOTS signature. */ + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_wots_sign( + sig, root, sk_seed, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the authentication path for the used WOTS leaf. */ + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_treehash_TREE_HEIGHT( + root, sig, sk_seed, pub_seed, idx_leaf, 0, + wots_gen_leaf, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + *siglen = SPX_BYTES; + + return 0; +} + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk) { + const unsigned char *pub_seed = pk; + const unsigned char *pub_root = pk + SPX_N; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char wots_pk[SPX_WOTS_BYTES]; + unsigned char root[SPX_N]; + unsigned char leaf[SPX_N]; + unsigned int i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + if (siglen != SPX_BYTES) { + return -1; + } + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_initialize_hash_function( + pub_seed, NULL); + + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + /* Derive the message digest and leaf index from R || PK || M. */ + /* The additional SPX_N is a result of the hash domain separator. */ + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + /* Layer correctly defaults to 0, so no need to set_layer_addr */ + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_fors_pk_from_sig( + root, sig, mhash, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + /* For each subtree.. */ + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + + /* The WOTS public key is only correct if the signature was correct. */ + /* Initially, root is the FORS pk, but on subsequent iterations it is + the root of the subtree below the currently processed subtree. */ + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_wots_pk_from_sig( + wots_pk, sig, root, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the leaf node using the WOTS public key. */ + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_thash_WOTS_LEN( + leaf, wots_pk, pub_seed, wots_pk_addr); + + /* Compute the root node of this subtree. */ + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_compute_root( + root, leaf, idx_leaf, 0, sig, SPX_TREE_HEIGHT, + pub_seed, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + /* Check if the root node equals the root node in the public key. */ + if (memcmp(root, pub_root, SPX_N) != 0) { + return -1; + } + + return 0; +} + + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + size_t siglen; + + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_crypto_sign_signature( + sm, &siglen, m, mlen, sk); + + memmove(sm + SPX_BYTES, m, mlen); + *smlen = siglen + mlen; + + return 0; +} + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk) { + /* The API caller does not necessarily know what size a signature should be + but SPHINCS+ signatures are always exactly SPX_BYTES. */ + if (smlen < SPX_BYTES) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + *mlen = smlen - SPX_BYTES; + + if (PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_crypto_sign_verify( + sm, SPX_BYTES, sm + SPX_BYTES, *mlen, pk)) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + /* If verification was successful, move the message to the right place. */ + memmove(m, sm + SPX_BYTES, *mlen); + + return 0; +} diff --git a/crypto_sign/sphincs-shake256-128f-robust/clean/thash.h b/crypto_sign/sphincs-shake256-128f-robust/clean/thash.h new file mode 100644 index 00000000..260b1db7 --- /dev/null +++ b/crypto_sign/sphincs-shake256-128f-robust/clean/thash.h @@ -0,0 +1,22 @@ +#ifndef SPX_THASH_H +#define SPX_THASH_H + +#include + +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-shake256-128f-robust/clean/thash_shake256_robust.c b/crypto_sign/sphincs-shake256-128f-robust/clean/thash_shake256_robust.c new file mode 100644 index 00000000..d5321629 --- /dev/null +++ b/crypto_sign/sphincs-shake256-128f-robust/clean/thash_shake256_robust.c @@ -0,0 +1,69 @@ +#include +#include + +#include "address.h" +#include "params.h" +#include "thash.h" + +#include "fips202.h" + +/** + * Takes an array of inblocks concatenated arrays of SPX_N bytes. + */ +static void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_thash( + unsigned char *out, unsigned char *buf, + const unsigned char *in, unsigned int inblocks, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char *bitmask = buf + SPX_N + SPX_ADDR_BYTES; + unsigned int i; + + memcpy(buf, pub_seed, SPX_N); + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_addr_to_bytes(buf + SPX_N, addr); + + shake256(bitmask, inblocks * SPX_N, buf, SPX_N + SPX_ADDR_BYTES); + + for (i = 0; i < inblocks * SPX_N; i++) { + buf[SPX_N + SPX_ADDR_BYTES + i] = in[i] ^ bitmask[i]; + } + + shake256(out, SPX_N, buf, SPX_N + SPX_ADDR_BYTES + inblocks * SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_ADDR_BYTES + 1 * SPX_N]; + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_thash( + out, buf, in, 1, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_ADDR_BYTES + 2 * SPX_N]; + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_thash( + out, buf, in, 2, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_ADDR_BYTES + SPX_WOTS_LEN * SPX_N]; + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_thash( + out, buf, in, SPX_WOTS_LEN, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_ADDR_BYTES + SPX_FORS_TREES * SPX_N]; + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_thash( + out, buf, in, SPX_FORS_TREES, pub_seed, addr); +} diff --git a/crypto_sign/sphincs-shake256-128f-robust/clean/utils.c b/crypto_sign/sphincs-shake256-128f-robust/clean/utils.c new file mode 100644 index 00000000..50dffc84 --- /dev/null +++ b/crypto_sign/sphincs-shake256-128f-robust/clean/utils.c @@ -0,0 +1,192 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in) { + + /* Iterate over out in decreasing order, for big-endianness. */ + for (size_t i = outlen; i > 0; i--) { + out[i - 1] = in & 0xff; + in = in >> 8; + } +} + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen) { + unsigned long long retval = 0; + + for (size_t i = 0; i < inlen; i++) { + retval |= ((unsigned long long)in[i]) << (8 * (inlen - 1 - i)); + } + return retval; +} + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + unsigned char buffer[2 * SPX_N]; + + /* If leaf_idx is odd (last bit = 1), current path element is a right child + and auth_path has to go left. Otherwise it is the other way around. */ + if (leaf_idx & 1) { + memcpy(buffer + SPX_N, leaf, SPX_N); + memcpy(buffer, auth_path, SPX_N); + } else { + memcpy(buffer, leaf, SPX_N); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + + for (i = 0; i < tree_height - 1; i++) { + leaf_idx >>= 1; + idx_offset >>= 1; + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_tree_height(addr, i + 1); + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + + /* Pick the right or left neighbor, depending on parity of the node. */ + if (leaf_idx & 1) { + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_thash_2( + buffer + SPX_N, buffer, pub_seed, addr); + memcpy(buffer, auth_path, SPX_N); + } else { + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_thash_2( + buffer, buffer, pub_seed, addr); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + } + + /* The last iteration is exceptional; we do not copy an auth_path node. */ + leaf_idx >>= 1; + idx_offset >>= 1; + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_tree_height(addr, tree_height); + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_thash_2( + root, buffer, pub_seed, addr); +} + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +static void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_treehash( + unsigned char *root, unsigned char *auth_path, + unsigned char *stack, unsigned int *heights, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned int offset = 0; + uint32_t idx; + uint32_t tree_idx; + + for (idx = 0; idx < (uint32_t)(1 << tree_height); idx++) { + /* Add the next leaf node to the stack. */ + gen_leaf(stack + offset * SPX_N, + sk_seed, pub_seed, idx + idx_offset, tree_addr); + offset++; + heights[offset - 1] = 0; + + /* If this is a node we need for the auth path.. */ + if ((leaf_idx ^ 0x1) == idx) { + memcpy(auth_path, stack + (offset - 1)*SPX_N, SPX_N); + } + + /* While the top-most nodes are of equal height.. */ + while (offset >= 2 && heights[offset - 1] == heights[offset - 2]) { + /* Compute index of the new node, in the next layer. */ + tree_idx = (idx >> (heights[offset - 1] + 1)); + + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_tree_height( + tree_addr, heights[offset - 1] + 1); + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_tree_index( + tree_addr, tree_idx + (idx_offset >> (heights[offset - 1] + 1))); + /* Hash the top-most nodes from the stack together. */ + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_thash_2( + stack + (offset - 2)*SPX_N, stack + (offset - 2)*SPX_N, + pub_seed, tree_addr); + offset--; + /* Note that the top-most node is now one layer higher. */ + heights[offset - 1]++; + + /* If this is a node we need for the auth path.. */ + if (((leaf_idx >> heights[offset - 1]) ^ 0x1) == tree_idx) { + memcpy(auth_path + heights[offset - 1]*SPX_N, + stack + (offset - 1)*SPX_N, SPX_N); + } + } + } + memcpy(root, stack, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_FORS_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_FORS_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_FORS_HEIGHT, gen_leaf, tree_addr); +} + +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_TREE_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_TREE_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_TREE_HEIGHT, gen_leaf, tree_addr); +} diff --git a/crypto_sign/sphincs-shake256-128f-robust/clean/utils.h b/crypto_sign/sphincs-shake256-128f-robust/clean/utils.h new file mode 100644 index 00000000..9a7e1c71 --- /dev/null +++ b/crypto_sign/sphincs-shake256-128f-robust/clean/utils.h @@ -0,0 +1,60 @@ +#ifndef SPX_UTILS_H +#define SPX_UTILS_H + +#include "params.h" +#include +#include + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in); + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen); + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-shake256-128f-robust/clean/wots.c b/crypto_sign/sphincs-shake256-128f-robust/clean/wots.c new file mode 100644 index 00000000..96eb5c4f --- /dev/null +++ b/crypto_sign/sphincs-shake256-128f-robust/clean/wots.c @@ -0,0 +1,161 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +// TODO clarify address expectations, and make them more uniform. +// TODO i.e. do we expect types to be set already? +// TODO and do we expect modifications or copies? + +/** + * Computes the starting value for a chain, i.e. the secret key. + * Expects the address to be complete up to the chain address. + */ +static void wots_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t wots_addr[8]) { + /* Make sure that the hash address is actually zeroed. */ + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_hash_addr(wots_addr, 0); + + /* Generate sk element. */ + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_prf_addr(sk, sk_seed, wots_addr); +} + +/** + * Computes the chaining function. + * out and in have to be n-byte arrays. + * + * Interprets in as start-th value of the chain. + * addr has to contain the address of the chain. + */ +static void gen_chain(unsigned char *out, const unsigned char *in, + unsigned int start, unsigned int steps, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + /* Initialize out with the value at position 'start'. */ + memcpy(out, in, SPX_N); + + /* Iterate 'steps' calls to the hash function. */ + for (i = start; i < (start + steps) && i < SPX_WOTS_W; i++) { + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_hash_addr(addr, i); + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_thash_1( + out, out, pub_seed, addr); + } +} + +/** + * base_w algorithm as described in draft. + * Interprets an array of bytes as integers in base w. + * This only works when log_w is a divisor of 8. + */ +static void base_w(unsigned int *output, const size_t out_len, + const unsigned char *input) { + size_t in = 0; + size_t out = 0; + unsigned char total = 0; + unsigned int bits = 0; + size_t consumed; + + for (consumed = 0; consumed < out_len; consumed++) { + if (bits == 0) { + total = input[in]; + in++; + bits += 8; + } + bits -= SPX_WOTS_LOGW; + output[out] = (unsigned int)((total >> bits) & (SPX_WOTS_W - 1)); + out++; + } +} + +/* Computes the WOTS+ checksum over a message (in base_w). */ +static void wots_checksum(unsigned int *csum_base_w, + const unsigned int *msg_base_w) { + unsigned int csum = 0; + unsigned char csum_bytes[(SPX_WOTS_LEN2 * SPX_WOTS_LOGW + 7) / 8]; + unsigned int i; + + /* Compute checksum. */ + for (i = 0; i < SPX_WOTS_LEN1; i++) { + csum += SPX_WOTS_W - 1 - msg_base_w[i]; + } + + /* Convert checksum to base_w. */ + /* Make sure expected empty zero bits are the least significant bits. */ + csum = csum << (8 - ((SPX_WOTS_LEN2 * SPX_WOTS_LOGW) % 8)); + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_ull_to_bytes( + csum_bytes, sizeof(csum_bytes), csum); + base_w(csum_base_w, SPX_WOTS_LEN2, csum_bytes); +} + +/* Takes a message and derives the matching chain lengths. */ +static void chain_lengths(unsigned int *lengths, const unsigned char *msg) { + base_w(lengths, SPX_WOTS_LEN1, msg); + wots_checksum(lengths + SPX_WOTS_LEN1, lengths); +} + +/** + * WOTS key generation. Takes a 32 byte sk_seed, expands it to WOTS private key + * elements and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(pk + i * SPX_N, sk_seed, addr); + gen_chain(pk + i * SPX_N, pk + i * SPX_N, + 0, SPX_WOTS_W - 1, pub_seed, addr); + } +} + +/** + * Takes a n-byte message and the 32-byte sk_see to compute a signature 'sig'. + */ +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(sig + i * SPX_N, sk_seed, addr); + gen_chain(sig + i * SPX_N, sig + i * SPX_N, 0, lengths[i], pub_seed, addr); + } +} + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_set_chain_addr(addr, i); + gen_chain(pk + i * SPX_N, sig + i * SPX_N, + lengths[i], SPX_WOTS_W - 1 - lengths[i], pub_seed, addr); + } +} diff --git a/crypto_sign/sphincs-shake256-128f-robust/clean/wots.h b/crypto_sign/sphincs-shake256-128f-robust/clean/wots.h new file mode 100644 index 00000000..a6529dfc --- /dev/null +++ b/crypto_sign/sphincs-shake256-128f-robust/clean/wots.h @@ -0,0 +1,38 @@ +#ifndef SPX_WOTS_H +#define SPX_WOTS_H + +#include "params.h" +#include + +/** + * WOTS key generation. Takes a 32 byte seed for the private key, expands it to + * a full WOTS private key and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * Takes a n-byte message and the 32-byte seed for the private key to compute a + * signature that is placed at 'sig'. + */ +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]); + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHAKE256128FROBUST_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-shake256-128f-simple/META.yml b/crypto_sign/sphincs-shake256-128f-simple/META.yml index ecb799d9..5814eaa1 100644 --- a/crypto_sign/sphincs-shake256-128f-simple/META.yml +++ b/crypto_sign/sphincs-shake256-128f-simple/META.yml @@ -24,4 +24,4 @@ auxiliary-submitters: - Peter Schwabe implementations: - name: clean - version: https://github.com/sphincs/sphincsplus/commit/492ec4f1f6d3b3dc4b435783bbaaf4e41cdb6f32 + version: https://github.com/sphincs/sphincsplus/commit/77755c94d0bc744478044d6efbb888dc13156441 diff --git a/crypto_sign/sphincs-shake256-128f-simple/clean/hash_shake256.c b/crypto_sign/sphincs-shake256-128f-simple/clean/hash_shake256.c index a7ab8e15..68103a41 100644 --- a/crypto_sign/sphincs-shake256-128f-simple/clean/hash_shake256.c +++ b/crypto_sign/sphincs-shake256-128f-simple/clean/hash_shake256.c @@ -2,11 +2,12 @@ #include #include "address.h" -#include "fips202.h" #include "hash.h" #include "params.h" #include "utils.h" +#include "fips202.h" + /* For SHAKE256, there is no immediate reason to initialize at the start, so this function is an empty operation. */ void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_initialize_hash_function( diff --git a/crypto_sign/sphincs-shake256-128s-robust/META.yml b/crypto_sign/sphincs-shake256-128s-robust/META.yml new file mode 100644 index 00000000..6302c5c7 --- /dev/null +++ b/crypto_sign/sphincs-shake256-128s-robust/META.yml @@ -0,0 +1,27 @@ +name: SPHINCS+ +type: signature +claimed-nist-level: 1 +length-public-key: 32 +length-secret-key: 64 +length-signature: 8080 +testvectors-sha256: f3f56ddff38a75ee07b44c023b9c9133ffe9538bb4b64f8ec8742b21fcaa6a50 +principal-submitter: Andreas Hülsing +auxiliary-submitters: + - Jean-Philippe Aumasson + - Daniel J. Bernstein, + - Christoph Dobraunig + - Maria Eichlseder + - Scott Fluhrer + - Stefan-Lukas Gazdag + - Panos Kampanakis + - Stefan Kölbl + - Tanja Lange + - Martin M. Lauridsen + - Florian Mendel + - Ruben Niederhagen + - Christian Rechberger + - Joost Rijneveld + - Peter Schwabe +implementations: + - name: clean + version: https://github.com/sphincs/sphincsplus/commit/77755c94d0bc744478044d6efbb888dc13156441 diff --git a/crypto_sign/sphincs-shake256-128s-robust/clean/LICENSE b/crypto_sign/sphincs-shake256-128s-robust/clean/LICENSE new file mode 100644 index 00000000..670154e3 --- /dev/null +++ b/crypto_sign/sphincs-shake256-128s-robust/clean/LICENSE @@ -0,0 +1,116 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + diff --git a/crypto_sign/sphincs-shake256-128s-robust/clean/Makefile b/crypto_sign/sphincs-shake256-128s-robust/clean/Makefile new file mode 100644 index 00000000..dcb37067 --- /dev/null +++ b/crypto_sign/sphincs-shake256-128s-robust/clean/Makefile @@ -0,0 +1,20 @@ +# This Makefile can be used with GNU Make or BSD Make + +LIB=libsphincs-shake256-128s-robust_clean.a + +HEADERS = params.h address.h wots.h utils.h fors.h api.h hash.h thash.h +OBJECTS = address.o wots.o utils.o fors.o sign.o hash_shake256.o thash_shake256_robust.o + +CFLAGS=-O3 -Wall -Wconversion -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) + +all: $(LIB) + +%.o: %.c $(HEADERS) + $(CC) $(CFLAGS) -c -o $@ $< + +$(LIB): $(OBJECTS) + $(AR) -r $@ $(OBJECTS) + +clean: + $(RM) $(OBJECTS) + $(RM) $(LIB) diff --git a/crypto_sign/sphincs-shake256-128s-robust/clean/Makefile.Microsoft_nmake b/crypto_sign/sphincs-shake256-128s-robust/clean/Makefile.Microsoft_nmake new file mode 100644 index 00000000..8e2351a6 --- /dev/null +++ b/crypto_sign/sphincs-shake256-128s-robust/clean/Makefile.Microsoft_nmake @@ -0,0 +1,19 @@ +# This Makefile can be used with Microsoft Visual Studio's nmake using the command: +# nmake /f Makefile.Microsoft_nmake + +LIBRARY=libsphincs-shake256-128s-robust_clean.lib +OBJECTS=address.obj wots.obj utils.obj fors.obj sign.obj hash_shake256.obj thash_shake256_robust.obj + +CFLAGS=/nologo /I ..\..\..\common /W4 /WX + +all: $(LIBRARY) + +# Make sure objects are recompiled if headers change. +$(OBJECTS): *.h + +$(LIBRARY): $(OBJECTS) + LIB.EXE /NOLOGO /WX /OUT:$@ $** + +clean: + -DEL $(OBJECTS) + -DEL $(LIBRARY) diff --git a/crypto_sign/sphincs-shake256-128s-robust/clean/address.c b/crypto_sign/sphincs-shake256-128s-robust/clean/address.c new file mode 100644 index 00000000..c2dc7835 --- /dev/null +++ b/crypto_sign/sphincs-shake256-128s-robust/clean/address.c @@ -0,0 +1,78 @@ +#include + +#include "address.h" +#include "params.h" +#include "utils.h" + +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]) { + int i; + + for (i = 0; i < 8; i++) { + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_ull_to_bytes( + bytes + i * 4, 4, addr[i]); + } +} + +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer) { + addr[0] = layer; +} + +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree) { + addr[1] = 0; + addr[2] = (uint32_t) (tree >> 32); + addr[3] = (uint32_t) tree; +} + +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_type( + uint32_t addr[8], uint32_t type) { + addr[4] = type; +} + +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; +} + +/* These functions are used for OTS addresses. */ + +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair) { + addr[5] = keypair; +} + +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; + out[5] = in[5]; +} + +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain) { + addr[6] = chain; +} + +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash) { + addr[7] = hash; +} + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height) { + addr[6] = tree_height; +} + +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index) { + addr[7] = tree_index; +} diff --git a/crypto_sign/sphincs-shake256-128s-robust/clean/address.h b/crypto_sign/sphincs-shake256-128s-robust/clean/address.h new file mode 100644 index 00000000..d3ce8776 --- /dev/null +++ b/crypto_sign/sphincs-shake256-128s-robust/clean/address.h @@ -0,0 +1,50 @@ +#ifndef SPX_ADDRESS_H +#define SPX_ADDRESS_H + +#include + +#define SPX_ADDR_TYPE_WOTS 0 +#define SPX_ADDR_TYPE_WOTSPK 1 +#define SPX_ADDR_TYPE_HASHTREE 2 +#define SPX_ADDR_TYPE_FORSTREE 3 +#define SPX_ADDR_TYPE_FORSPK 4 + +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer); + +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree); + +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_type( + uint32_t addr[8], uint32_t type); + +/* Copies the layer and tree part of one address into the other */ +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for WOTS and FORS addresses. */ + +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair); + +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain); + +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash); + +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height); + +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index); + +#endif diff --git a/crypto_sign/sphincs-shake256-128s-robust/clean/api.h b/crypto_sign/sphincs-shake256-128s-robust/clean/api.h new file mode 100644 index 00000000..ff705285 --- /dev/null +++ b/crypto_sign/sphincs-shake256-128s-robust/clean/api.h @@ -0,0 +1,78 @@ +#ifndef PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_API_H +#define PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_API_H + +#include +#include + +#define PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_CRYPTO_ALGNAME "SPHINCS+" + +#define PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_CRYPTO_SECRETKEYBYTES 64 +#define PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES 32 +#define PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_CRYPTO_BYTES 8080 +#define PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_CRYPTO_SEEDBYTES 48 + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_crypto_sign_secretkeybytes(void); + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_crypto_sign_publickeybytes(void); + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_crypto_sign_bytes(void); + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_crypto_sign_seedbytes(void); + +/* + * Generates a SPHINCS+ key pair given a seed. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed); + +/* + * Generates a SPHINCS+ key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk); + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk); + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk); + +#endif diff --git a/crypto_sign/sphincs-shake256-128s-robust/clean/fors.c b/crypto_sign/sphincs-shake256-128s-robust/clean/fors.c new file mode 100644 index 00000000..8c2a7b24 --- /dev/null +++ b/crypto_sign/sphincs-shake256-128s-robust/clean/fors.c @@ -0,0 +1,164 @@ +#include +#include +#include + +#include "address.h" +#include "fors.h" +#include "hash.h" +#include "thash.h" +#include "utils.h" + +static void fors_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_prf_addr( + sk, sk_seed, fors_leaf_addr); +} + +static void fors_sk_to_leaf(unsigned char *leaf, const unsigned char *sk, + const unsigned char *pub_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_thash_1( + leaf, sk, pub_seed, fors_leaf_addr); +} + +static void fors_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t fors_tree_addr[8]) { + uint32_t fors_leaf_addr[8] = {0}; + + /* Only copy the parts that must be kept in fors_leaf_addr. */ + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_copy_keypair_addr( + fors_leaf_addr, fors_tree_addr); + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_type( + fors_leaf_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_tree_index( + fors_leaf_addr, addr_idx); + + fors_gen_sk(leaf, sk_seed, fors_leaf_addr); + fors_sk_to_leaf(leaf, leaf, pub_seed, fors_leaf_addr); +} + +/** + * Interprets m as SPX_FORS_HEIGHT-bit unsigned integers. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + * Assumes indices has space for SPX_FORS_TREES integers. + */ +static void message_to_indices(uint32_t *indices, const unsigned char *m) { + unsigned int i, j; + unsigned int offset = 0; + + for (i = 0; i < SPX_FORS_TREES; i++) { + indices[i] = 0; + for (j = 0; j < SPX_FORS_HEIGHT; j++) { + indices[i] ^= (((uint32_t)m[offset >> 3] >> (offset & 0x7)) & 0x1) << j; + offset++; + } + } +} + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Include the secret key part that produces the selected leaf node. */ + fors_gen_sk(sig, sk_seed, fors_tree_addr); + sig += SPX_N; + + /* Compute the authentication path for this leaf node. */ + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_treehash_FORS_HEIGHT( + roots + i * SPX_N, sig, sk_seed, pub_seed, + indices[i], idx_offset, fors_gen_leaf, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + unsigned char leaf[SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Derive the leaf from the included secret key part. */ + fors_sk_to_leaf(leaf, sig, pub_seed, fors_tree_addr); + sig += SPX_N; + + /* Derive the corresponding root node of this tree. */ + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_compute_root( + roots + i * SPX_N, leaf, indices[i], idx_offset, sig, + SPX_FORS_HEIGHT, pub_seed, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} diff --git a/crypto_sign/sphincs-shake256-128s-robust/clean/fors.h b/crypto_sign/sphincs-shake256-128s-robust/clean/fors.h new file mode 100644 index 00000000..6adac4f0 --- /dev/null +++ b/crypto_sign/sphincs-shake256-128s-robust/clean/fors.h @@ -0,0 +1,30 @@ +#ifndef SPX_FORS_H +#define SPX_FORS_H + +#include + +#include "params.h" + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]); + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-shake256-128s-robust/clean/hash.h b/crypto_sign/sphincs-shake256-128s-robust/clean/hash.h new file mode 100644 index 00000000..feb8a9f8 --- /dev/null +++ b/crypto_sign/sphincs-shake256-128s-robust/clean/hash.h @@ -0,0 +1,22 @@ +#ifndef SPX_HASH_H +#define SPX_HASH_H + +#include + +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed); + +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen); + +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen); + +#endif diff --git a/crypto_sign/sphincs-shake256-128s-robust/clean/hash_shake256.c b/crypto_sign/sphincs-shake256-128s-robust/clean/hash_shake256.c new file mode 100644 index 00000000..f636a41f --- /dev/null +++ b/crypto_sign/sphincs-shake256-128s-robust/clean/hash_shake256.c @@ -0,0 +1,87 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "utils.h" + +#include "fips202.h" + +/* For SHAKE256, there is no immediate reason to initialize at the start, + so this function is an empty operation. */ +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed) { + (void)pub_seed; /* Suppress an 'unused parameter' warning. */ + (void)sk_seed; /* Suppress an 'unused parameter' warning. */ +} + +/* + * Computes PRF(key, addr), given a secret key of SPX_N bytes and an address + */ +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]) { + unsigned char buf[SPX_N + SPX_ADDR_BYTES]; + + memcpy(buf, key, SPX_N); + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_addr_to_bytes(buf + SPX_N, addr); + + shake256(out, SPX_N, buf, SPX_N + SPX_ADDR_BYTES); +} + +/** + * Computes the message-dependent randomness R, using a secret seed and an + * optional randomization value as well as the message. + */ +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen) { + uint64_t s_inc[26]; + + shake256_inc_init(s_inc); + shake256_inc_absorb(s_inc, sk_prf, SPX_N); + shake256_inc_absorb(s_inc, optrand, SPX_N); + shake256_inc_absorb(s_inc, m, mlen); + shake256_inc_finalize(s_inc); + shake256_inc_squeeze(R, SPX_N, s_inc); +} + +/** + * Computes the message hash using R, the public key, and the message. + * Outputs the message digest and the index of the leaf. The index is split in + * the tree index and the leaf index, for convenient copying to an address. + */ +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen) { +#define SPX_TREE_BITS (SPX_TREE_HEIGHT * (SPX_D - 1)) +#define SPX_TREE_BYTES ((SPX_TREE_BITS + 7) / 8) +#define SPX_LEAF_BITS SPX_TREE_HEIGHT +#define SPX_LEAF_BYTES ((SPX_LEAF_BITS + 7) / 8) +#define SPX_DGST_BYTES (SPX_FORS_MSG_BYTES + SPX_TREE_BYTES + SPX_LEAF_BYTES) + + unsigned char buf[SPX_DGST_BYTES]; + unsigned char *bufp = buf; + uint64_t s_inc[26]; + + shake256_inc_init(s_inc); + shake256_inc_absorb(s_inc, R, SPX_N); + shake256_inc_absorb(s_inc, pk, SPX_PK_BYTES); + shake256_inc_absorb(s_inc, m, mlen); + shake256_inc_finalize(s_inc); + shake256_inc_squeeze(buf, SPX_DGST_BYTES, s_inc); + + memcpy(digest, bufp, SPX_FORS_MSG_BYTES); + bufp += SPX_FORS_MSG_BYTES; + + *tree = PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_bytes_to_ull( + bufp, SPX_TREE_BYTES); + *tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS); + bufp += SPX_TREE_BYTES; + + *leaf_idx = (uint32_t)PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_bytes_to_ull( + bufp, SPX_LEAF_BYTES); + *leaf_idx &= (~(uint32_t)0) >> (32 - SPX_LEAF_BITS); +} diff --git a/crypto_sign/sphincs-shake256-128s-robust/clean/params.h b/crypto_sign/sphincs-shake256-128s-robust/clean/params.h new file mode 100644 index 00000000..ac30fdea --- /dev/null +++ b/crypto_sign/sphincs-shake256-128s-robust/clean/params.h @@ -0,0 +1,53 @@ +#ifndef SPX_PARAMS_H +#define SPX_PARAMS_H + +/* Hash output length in bytes. */ +#define SPX_N 16 +/* Height of the hypertree. */ +#define SPX_FULL_HEIGHT 64 +/* Number of subtree layer. */ +#define SPX_D 8 +/* FORS tree dimensions. */ +#define SPX_FORS_HEIGHT 15 +#define SPX_FORS_TREES 10 +/* Winternitz parameter, */ +#define SPX_WOTS_W 16 + +/* The hash function is defined by linking a different hash.c file, as opposed + to setting a #define constant. */ + +/* For clarity */ +#define SPX_ADDR_BYTES 32 + +/* WOTS parameters. */ +#define SPX_WOTS_LOGW 4 + +#define SPX_WOTS_LEN1 (8 * SPX_N / SPX_WOTS_LOGW) + +/* SPX_WOTS_LEN2 is floor(log(len_1 * (w - 1)) / log(w)) + 1; we precompute */ +#define SPX_WOTS_LEN2 3 + +#define SPX_WOTS_LEN (SPX_WOTS_LEN1 + SPX_WOTS_LEN2) +#define SPX_WOTS_BYTES (SPX_WOTS_LEN * SPX_N) +#define SPX_WOTS_PK_BYTES SPX_WOTS_BYTES + +/* Subtree size. */ +#define SPX_TREE_HEIGHT (SPX_FULL_HEIGHT / SPX_D) + +/* FORS parameters. */ +#define SPX_FORS_MSG_BYTES ((SPX_FORS_HEIGHT * SPX_FORS_TREES + 7) / 8) +#define SPX_FORS_BYTES ((SPX_FORS_HEIGHT + 1) * SPX_FORS_TREES * SPX_N) +#define SPX_FORS_PK_BYTES SPX_N + +/* Resulting SPX sizes. */ +#define SPX_BYTES (SPX_N + SPX_FORS_BYTES + SPX_D * SPX_WOTS_BYTES +\ + SPX_FULL_HEIGHT * SPX_N) +#define SPX_PK_BYTES (2 * SPX_N) +#define SPX_SK_BYTES (2 * SPX_N + SPX_PK_BYTES) + +/* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ +#define SPX_OPTRAND_BYTES 32 + +#endif diff --git a/crypto_sign/sphincs-shake256-128s-robust/clean/sign.c b/crypto_sign/sphincs-shake256-128s-robust/clean/sign.c new file mode 100644 index 00000000..3b63208c --- /dev/null +++ b/crypto_sign/sphincs-shake256-128s-robust/clean/sign.c @@ -0,0 +1,344 @@ +#include +#include +#include + +#include "address.h" +#include "api.h" +#include "fors.h" +#include "hash.h" +#include "params.h" +#include "randombytes.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +/** + * Computes the leaf at a given address. First generates the WOTS key pair, + * then computes leaf by hashing horizontally. + */ +static void wots_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t tree_addr[8]) { + unsigned char pk[SPX_WOTS_BYTES]; + uint32_t wots_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_keypair_addr( + wots_addr, addr_idx); + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_wots_gen_pk( + pk, sk_seed, pub_seed, wots_addr); + + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_thash_WOTS_LEN( + leaf, pk, pub_seed, wots_pk_addr); +} + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_crypto_sign_secretkeybytes(void) { + return PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_CRYPTO_SECRETKEYBYTES; +} + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_crypto_sign_publickeybytes(void) { + return PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES; +} + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_crypto_sign_bytes(void) { + return PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_CRYPTO_BYTES; +} + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_crypto_sign_seedbytes(void) { + return PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_CRYPTO_SEEDBYTES; +} + +/* + * Generates an SPX key pair given a seed of length + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed) { + /* We do not need the auth path in key generation, but it simplifies the + code to have just one treehash routine that computes both root and path + in one function. */ + unsigned char auth_path[SPX_TREE_HEIGHT * SPX_N]; + uint32_t top_tree_addr[8] = {0}; + + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_layer_addr( + top_tree_addr, SPX_D - 1); + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_type( + top_tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */ + memcpy(sk, seed, PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_CRYPTO_SEEDBYTES); + + memcpy(pk, sk + 2 * SPX_N, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_initialize_hash_function(pk, sk); + + /* Compute root node of the top-most subtree. */ + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_treehash_TREE_HEIGHT( + sk + 3 * SPX_N, auth_path, sk, sk + 2 * SPX_N, 0, 0, + wots_gen_leaf, top_tree_addr); + + memcpy(pk + SPX_N, sk + 3 * SPX_N, SPX_N); + + return 0; +} + +/* + * Generates an SPX key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk) { + unsigned char seed[PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_CRYPTO_SEEDBYTES]; + randombytes(seed, PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_CRYPTO_SEEDBYTES); + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_crypto_sign_seed_keypair( + pk, sk, seed); + + return 0; +} + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + const unsigned char *sk_seed = sk; + const unsigned char *sk_prf = sk + SPX_N; + const unsigned char *pk = sk + 2 * SPX_N; + const unsigned char *pub_seed = pk; + + unsigned char optrand[SPX_N]; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char root[SPX_N]; + uint32_t i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_initialize_hash_function( + pub_seed, sk_seed); + + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ + randombytes(optrand, SPX_N); + /* Compute the digest randomization value. */ + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_gen_message_random( + sig, sk_prf, optrand, m, mlen); + + /* Derive the message digest and leaf index from R, PK and M. */ + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Sign the message hash using FORS. */ + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_fors_sign( + sig, root, mhash, sk_seed, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Compute a WOTS signature. */ + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_wots_sign( + sig, root, sk_seed, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the authentication path for the used WOTS leaf. */ + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_treehash_TREE_HEIGHT( + root, sig, sk_seed, pub_seed, idx_leaf, 0, + wots_gen_leaf, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + *siglen = SPX_BYTES; + + return 0; +} + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk) { + const unsigned char *pub_seed = pk; + const unsigned char *pub_root = pk + SPX_N; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char wots_pk[SPX_WOTS_BYTES]; + unsigned char root[SPX_N]; + unsigned char leaf[SPX_N]; + unsigned int i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + if (siglen != SPX_BYTES) { + return -1; + } + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_initialize_hash_function( + pub_seed, NULL); + + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + /* Derive the message digest and leaf index from R || PK || M. */ + /* The additional SPX_N is a result of the hash domain separator. */ + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + /* Layer correctly defaults to 0, so no need to set_layer_addr */ + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_fors_pk_from_sig( + root, sig, mhash, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + /* For each subtree.. */ + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + + /* The WOTS public key is only correct if the signature was correct. */ + /* Initially, root is the FORS pk, but on subsequent iterations it is + the root of the subtree below the currently processed subtree. */ + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_wots_pk_from_sig( + wots_pk, sig, root, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the leaf node using the WOTS public key. */ + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_thash_WOTS_LEN( + leaf, wots_pk, pub_seed, wots_pk_addr); + + /* Compute the root node of this subtree. */ + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_compute_root( + root, leaf, idx_leaf, 0, sig, SPX_TREE_HEIGHT, + pub_seed, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + /* Check if the root node equals the root node in the public key. */ + if (memcmp(root, pub_root, SPX_N) != 0) { + return -1; + } + + return 0; +} + + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + size_t siglen; + + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_crypto_sign_signature( + sm, &siglen, m, mlen, sk); + + memmove(sm + SPX_BYTES, m, mlen); + *smlen = siglen + mlen; + + return 0; +} + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk) { + /* The API caller does not necessarily know what size a signature should be + but SPHINCS+ signatures are always exactly SPX_BYTES. */ + if (smlen < SPX_BYTES) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + *mlen = smlen - SPX_BYTES; + + if (PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_crypto_sign_verify( + sm, SPX_BYTES, sm + SPX_BYTES, *mlen, pk)) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + /* If verification was successful, move the message to the right place. */ + memmove(m, sm + SPX_BYTES, *mlen); + + return 0; +} diff --git a/crypto_sign/sphincs-shake256-128s-robust/clean/thash.h b/crypto_sign/sphincs-shake256-128s-robust/clean/thash.h new file mode 100644 index 00000000..939ba376 --- /dev/null +++ b/crypto_sign/sphincs-shake256-128s-robust/clean/thash.h @@ -0,0 +1,22 @@ +#ifndef SPX_THASH_H +#define SPX_THASH_H + +#include + +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-shake256-128s-robust/clean/thash_shake256_robust.c b/crypto_sign/sphincs-shake256-128s-robust/clean/thash_shake256_robust.c new file mode 100644 index 00000000..db06c60e --- /dev/null +++ b/crypto_sign/sphincs-shake256-128s-robust/clean/thash_shake256_robust.c @@ -0,0 +1,69 @@ +#include +#include + +#include "address.h" +#include "params.h" +#include "thash.h" + +#include "fips202.h" + +/** + * Takes an array of inblocks concatenated arrays of SPX_N bytes. + */ +static void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_thash( + unsigned char *out, unsigned char *buf, + const unsigned char *in, unsigned int inblocks, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char *bitmask = buf + SPX_N + SPX_ADDR_BYTES; + unsigned int i; + + memcpy(buf, pub_seed, SPX_N); + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_addr_to_bytes(buf + SPX_N, addr); + + shake256(bitmask, inblocks * SPX_N, buf, SPX_N + SPX_ADDR_BYTES); + + for (i = 0; i < inblocks * SPX_N; i++) { + buf[SPX_N + SPX_ADDR_BYTES + i] = in[i] ^ bitmask[i]; + } + + shake256(out, SPX_N, buf, SPX_N + SPX_ADDR_BYTES + inblocks * SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_ADDR_BYTES + 1 * SPX_N]; + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_thash( + out, buf, in, 1, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_ADDR_BYTES + 2 * SPX_N]; + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_thash( + out, buf, in, 2, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_ADDR_BYTES + SPX_WOTS_LEN * SPX_N]; + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_thash( + out, buf, in, SPX_WOTS_LEN, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_ADDR_BYTES + SPX_FORS_TREES * SPX_N]; + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_thash( + out, buf, in, SPX_FORS_TREES, pub_seed, addr); +} diff --git a/crypto_sign/sphincs-shake256-128s-robust/clean/utils.c b/crypto_sign/sphincs-shake256-128s-robust/clean/utils.c new file mode 100644 index 00000000..eddf52c1 --- /dev/null +++ b/crypto_sign/sphincs-shake256-128s-robust/clean/utils.c @@ -0,0 +1,192 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in) { + + /* Iterate over out in decreasing order, for big-endianness. */ + for (size_t i = outlen; i > 0; i--) { + out[i - 1] = in & 0xff; + in = in >> 8; + } +} + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen) { + unsigned long long retval = 0; + + for (size_t i = 0; i < inlen; i++) { + retval |= ((unsigned long long)in[i]) << (8 * (inlen - 1 - i)); + } + return retval; +} + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + unsigned char buffer[2 * SPX_N]; + + /* If leaf_idx is odd (last bit = 1), current path element is a right child + and auth_path has to go left. Otherwise it is the other way around. */ + if (leaf_idx & 1) { + memcpy(buffer + SPX_N, leaf, SPX_N); + memcpy(buffer, auth_path, SPX_N); + } else { + memcpy(buffer, leaf, SPX_N); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + + for (i = 0; i < tree_height - 1; i++) { + leaf_idx >>= 1; + idx_offset >>= 1; + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_tree_height(addr, i + 1); + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + + /* Pick the right or left neighbor, depending on parity of the node. */ + if (leaf_idx & 1) { + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_thash_2( + buffer + SPX_N, buffer, pub_seed, addr); + memcpy(buffer, auth_path, SPX_N); + } else { + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_thash_2( + buffer, buffer, pub_seed, addr); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + } + + /* The last iteration is exceptional; we do not copy an auth_path node. */ + leaf_idx >>= 1; + idx_offset >>= 1; + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_tree_height(addr, tree_height); + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_thash_2( + root, buffer, pub_seed, addr); +} + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +static void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_treehash( + unsigned char *root, unsigned char *auth_path, + unsigned char *stack, unsigned int *heights, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned int offset = 0; + uint32_t idx; + uint32_t tree_idx; + + for (idx = 0; idx < (uint32_t)(1 << tree_height); idx++) { + /* Add the next leaf node to the stack. */ + gen_leaf(stack + offset * SPX_N, + sk_seed, pub_seed, idx + idx_offset, tree_addr); + offset++; + heights[offset - 1] = 0; + + /* If this is a node we need for the auth path.. */ + if ((leaf_idx ^ 0x1) == idx) { + memcpy(auth_path, stack + (offset - 1)*SPX_N, SPX_N); + } + + /* While the top-most nodes are of equal height.. */ + while (offset >= 2 && heights[offset - 1] == heights[offset - 2]) { + /* Compute index of the new node, in the next layer. */ + tree_idx = (idx >> (heights[offset - 1] + 1)); + + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_tree_height( + tree_addr, heights[offset - 1] + 1); + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_tree_index( + tree_addr, tree_idx + (idx_offset >> (heights[offset - 1] + 1))); + /* Hash the top-most nodes from the stack together. */ + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_thash_2( + stack + (offset - 2)*SPX_N, stack + (offset - 2)*SPX_N, + pub_seed, tree_addr); + offset--; + /* Note that the top-most node is now one layer higher. */ + heights[offset - 1]++; + + /* If this is a node we need for the auth path.. */ + if (((leaf_idx >> heights[offset - 1]) ^ 0x1) == tree_idx) { + memcpy(auth_path + heights[offset - 1]*SPX_N, + stack + (offset - 1)*SPX_N, SPX_N); + } + } + } + memcpy(root, stack, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_FORS_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_FORS_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_FORS_HEIGHT, gen_leaf, tree_addr); +} + +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_TREE_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_TREE_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_TREE_HEIGHT, gen_leaf, tree_addr); +} diff --git a/crypto_sign/sphincs-shake256-128s-robust/clean/utils.h b/crypto_sign/sphincs-shake256-128s-robust/clean/utils.h new file mode 100644 index 00000000..eb2c1b08 --- /dev/null +++ b/crypto_sign/sphincs-shake256-128s-robust/clean/utils.h @@ -0,0 +1,60 @@ +#ifndef SPX_UTILS_H +#define SPX_UTILS_H + +#include "params.h" +#include +#include + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in); + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen); + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-shake256-128s-robust/clean/wots.c b/crypto_sign/sphincs-shake256-128s-robust/clean/wots.c new file mode 100644 index 00000000..3993bdbc --- /dev/null +++ b/crypto_sign/sphincs-shake256-128s-robust/clean/wots.c @@ -0,0 +1,161 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +// TODO clarify address expectations, and make them more uniform. +// TODO i.e. do we expect types to be set already? +// TODO and do we expect modifications or copies? + +/** + * Computes the starting value for a chain, i.e. the secret key. + * Expects the address to be complete up to the chain address. + */ +static void wots_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t wots_addr[8]) { + /* Make sure that the hash address is actually zeroed. */ + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_hash_addr(wots_addr, 0); + + /* Generate sk element. */ + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_prf_addr(sk, sk_seed, wots_addr); +} + +/** + * Computes the chaining function. + * out and in have to be n-byte arrays. + * + * Interprets in as start-th value of the chain. + * addr has to contain the address of the chain. + */ +static void gen_chain(unsigned char *out, const unsigned char *in, + unsigned int start, unsigned int steps, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + /* Initialize out with the value at position 'start'. */ + memcpy(out, in, SPX_N); + + /* Iterate 'steps' calls to the hash function. */ + for (i = start; i < (start + steps) && i < SPX_WOTS_W; i++) { + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_hash_addr(addr, i); + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_thash_1( + out, out, pub_seed, addr); + } +} + +/** + * base_w algorithm as described in draft. + * Interprets an array of bytes as integers in base w. + * This only works when log_w is a divisor of 8. + */ +static void base_w(unsigned int *output, const size_t out_len, + const unsigned char *input) { + size_t in = 0; + size_t out = 0; + unsigned char total = 0; + unsigned int bits = 0; + size_t consumed; + + for (consumed = 0; consumed < out_len; consumed++) { + if (bits == 0) { + total = input[in]; + in++; + bits += 8; + } + bits -= SPX_WOTS_LOGW; + output[out] = (unsigned int)((total >> bits) & (SPX_WOTS_W - 1)); + out++; + } +} + +/* Computes the WOTS+ checksum over a message (in base_w). */ +static void wots_checksum(unsigned int *csum_base_w, + const unsigned int *msg_base_w) { + unsigned int csum = 0; + unsigned char csum_bytes[(SPX_WOTS_LEN2 * SPX_WOTS_LOGW + 7) / 8]; + unsigned int i; + + /* Compute checksum. */ + for (i = 0; i < SPX_WOTS_LEN1; i++) { + csum += SPX_WOTS_W - 1 - msg_base_w[i]; + } + + /* Convert checksum to base_w. */ + /* Make sure expected empty zero bits are the least significant bits. */ + csum = csum << (8 - ((SPX_WOTS_LEN2 * SPX_WOTS_LOGW) % 8)); + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_ull_to_bytes( + csum_bytes, sizeof(csum_bytes), csum); + base_w(csum_base_w, SPX_WOTS_LEN2, csum_bytes); +} + +/* Takes a message and derives the matching chain lengths. */ +static void chain_lengths(unsigned int *lengths, const unsigned char *msg) { + base_w(lengths, SPX_WOTS_LEN1, msg); + wots_checksum(lengths + SPX_WOTS_LEN1, lengths); +} + +/** + * WOTS key generation. Takes a 32 byte sk_seed, expands it to WOTS private key + * elements and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(pk + i * SPX_N, sk_seed, addr); + gen_chain(pk + i * SPX_N, pk + i * SPX_N, + 0, SPX_WOTS_W - 1, pub_seed, addr); + } +} + +/** + * Takes a n-byte message and the 32-byte sk_see to compute a signature 'sig'. + */ +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(sig + i * SPX_N, sk_seed, addr); + gen_chain(sig + i * SPX_N, sig + i * SPX_N, 0, lengths[i], pub_seed, addr); + } +} + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_set_chain_addr(addr, i); + gen_chain(pk + i * SPX_N, sig + i * SPX_N, + lengths[i], SPX_WOTS_W - 1 - lengths[i], pub_seed, addr); + } +} diff --git a/crypto_sign/sphincs-shake256-128s-robust/clean/wots.h b/crypto_sign/sphincs-shake256-128s-robust/clean/wots.h new file mode 100644 index 00000000..7c9a7c25 --- /dev/null +++ b/crypto_sign/sphincs-shake256-128s-robust/clean/wots.h @@ -0,0 +1,38 @@ +#ifndef SPX_WOTS_H +#define SPX_WOTS_H + +#include "params.h" +#include + +/** + * WOTS key generation. Takes a 32 byte seed for the private key, expands it to + * a full WOTS private key and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * Takes a n-byte message and the 32-byte seed for the private key to compute a + * signature that is placed at 'sig'. + */ +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]); + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHAKE256128SROBUST_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-shake256-128s-simple/META.yml b/crypto_sign/sphincs-shake256-128s-simple/META.yml new file mode 100644 index 00000000..41237bc4 --- /dev/null +++ b/crypto_sign/sphincs-shake256-128s-simple/META.yml @@ -0,0 +1,27 @@ +name: SPHINCS+ +type: signature +claimed-nist-level: 1 +length-public-key: 32 +length-secret-key: 64 +length-signature: 8080 +testvectors-sha256: ee2af38333f6ba705102ab66689c262b07c1fd9ce1d46180796bcb263bf1a654 +principal-submitter: Andreas Hülsing +auxiliary-submitters: + - Jean-Philippe Aumasson + - Daniel J. Bernstein, + - Christoph Dobraunig + - Maria Eichlseder + - Scott Fluhrer + - Stefan-Lukas Gazdag + - Panos Kampanakis + - Stefan Kölbl + - Tanja Lange + - Martin M. Lauridsen + - Florian Mendel + - Ruben Niederhagen + - Christian Rechberger + - Joost Rijneveld + - Peter Schwabe +implementations: + - name: clean + version: https://github.com/sphincs/sphincsplus/commit/77755c94d0bc744478044d6efbb888dc13156441 diff --git a/crypto_sign/sphincs-shake256-128s-simple/clean/LICENSE b/crypto_sign/sphincs-shake256-128s-simple/clean/LICENSE new file mode 100644 index 00000000..670154e3 --- /dev/null +++ b/crypto_sign/sphincs-shake256-128s-simple/clean/LICENSE @@ -0,0 +1,116 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + diff --git a/crypto_sign/sphincs-shake256-128s-simple/clean/Makefile b/crypto_sign/sphincs-shake256-128s-simple/clean/Makefile new file mode 100644 index 00000000..c3c9de0b --- /dev/null +++ b/crypto_sign/sphincs-shake256-128s-simple/clean/Makefile @@ -0,0 +1,20 @@ +# This Makefile can be used with GNU Make or BSD Make + +LIB=libsphincs-shake256-128s-simple_clean.a + +HEADERS = params.h address.h wots.h utils.h fors.h api.h hash.h thash.h +OBJECTS = address.o wots.o utils.o fors.o sign.o hash_shake256.o thash_shake256_simple.o + +CFLAGS=-O3 -Wall -Wconversion -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) + +all: $(LIB) + +%.o: %.c $(HEADERS) + $(CC) $(CFLAGS) -c -o $@ $< + +$(LIB): $(OBJECTS) + $(AR) -r $@ $(OBJECTS) + +clean: + $(RM) $(OBJECTS) + $(RM) $(LIB) diff --git a/crypto_sign/sphincs-shake256-128s-simple/clean/Makefile.Microsoft_nmake b/crypto_sign/sphincs-shake256-128s-simple/clean/Makefile.Microsoft_nmake new file mode 100644 index 00000000..96326e49 --- /dev/null +++ b/crypto_sign/sphincs-shake256-128s-simple/clean/Makefile.Microsoft_nmake @@ -0,0 +1,19 @@ +# This Makefile can be used with Microsoft Visual Studio's nmake using the command: +# nmake /f Makefile.Microsoft_nmake + +LIBRARY=libsphincs-shake256-128s-simple_clean.lib +OBJECTS=address.obj wots.obj utils.obj fors.obj sign.obj hash_shake256.obj thash_shake256_simple.obj + +CFLAGS=/nologo /I ..\..\..\common /W4 /WX + +all: $(LIBRARY) + +# Make sure objects are recompiled if headers change. +$(OBJECTS): *.h + +$(LIBRARY): $(OBJECTS) + LIB.EXE /NOLOGO /WX /OUT:$@ $** + +clean: + -DEL $(OBJECTS) + -DEL $(LIBRARY) diff --git a/crypto_sign/sphincs-shake256-128s-simple/clean/address.c b/crypto_sign/sphincs-shake256-128s-simple/clean/address.c new file mode 100644 index 00000000..35757e54 --- /dev/null +++ b/crypto_sign/sphincs-shake256-128s-simple/clean/address.c @@ -0,0 +1,78 @@ +#include + +#include "address.h" +#include "params.h" +#include "utils.h" + +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]) { + int i; + + for (i = 0; i < 8; i++) { + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_ull_to_bytes( + bytes + i * 4, 4, addr[i]); + } +} + +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer) { + addr[0] = layer; +} + +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree) { + addr[1] = 0; + addr[2] = (uint32_t) (tree >> 32); + addr[3] = (uint32_t) tree; +} + +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_type( + uint32_t addr[8], uint32_t type) { + addr[4] = type; +} + +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; +} + +/* These functions are used for OTS addresses. */ + +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair) { + addr[5] = keypair; +} + +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; + out[5] = in[5]; +} + +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain) { + addr[6] = chain; +} + +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash) { + addr[7] = hash; +} + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height) { + addr[6] = tree_height; +} + +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index) { + addr[7] = tree_index; +} diff --git a/crypto_sign/sphincs-shake256-128s-simple/clean/address.h b/crypto_sign/sphincs-shake256-128s-simple/clean/address.h new file mode 100644 index 00000000..7f39b551 --- /dev/null +++ b/crypto_sign/sphincs-shake256-128s-simple/clean/address.h @@ -0,0 +1,50 @@ +#ifndef SPX_ADDRESS_H +#define SPX_ADDRESS_H + +#include + +#define SPX_ADDR_TYPE_WOTS 0 +#define SPX_ADDR_TYPE_WOTSPK 1 +#define SPX_ADDR_TYPE_HASHTREE 2 +#define SPX_ADDR_TYPE_FORSTREE 3 +#define SPX_ADDR_TYPE_FORSPK 4 + +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer); + +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree); + +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_type( + uint32_t addr[8], uint32_t type); + +/* Copies the layer and tree part of one address into the other */ +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for WOTS and FORS addresses. */ + +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair); + +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain); + +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash); + +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height); + +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index); + +#endif diff --git a/crypto_sign/sphincs-shake256-128s-simple/clean/api.h b/crypto_sign/sphincs-shake256-128s-simple/clean/api.h new file mode 100644 index 00000000..a59f8525 --- /dev/null +++ b/crypto_sign/sphincs-shake256-128s-simple/clean/api.h @@ -0,0 +1,78 @@ +#ifndef PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_API_H +#define PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_API_H + +#include +#include + +#define PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_CRYPTO_ALGNAME "SPHINCS+" + +#define PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_CRYPTO_SECRETKEYBYTES 64 +#define PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES 32 +#define PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_CRYPTO_BYTES 8080 +#define PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_CRYPTO_SEEDBYTES 48 + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_crypto_sign_secretkeybytes(void); + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_crypto_sign_publickeybytes(void); + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_crypto_sign_bytes(void); + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_crypto_sign_seedbytes(void); + +/* + * Generates a SPHINCS+ key pair given a seed. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed); + +/* + * Generates a SPHINCS+ key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk); + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk); + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk); + +#endif diff --git a/crypto_sign/sphincs-shake256-128s-simple/clean/fors.c b/crypto_sign/sphincs-shake256-128s-simple/clean/fors.c new file mode 100644 index 00000000..81ca5225 --- /dev/null +++ b/crypto_sign/sphincs-shake256-128s-simple/clean/fors.c @@ -0,0 +1,164 @@ +#include +#include +#include + +#include "address.h" +#include "fors.h" +#include "hash.h" +#include "thash.h" +#include "utils.h" + +static void fors_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_prf_addr( + sk, sk_seed, fors_leaf_addr); +} + +static void fors_sk_to_leaf(unsigned char *leaf, const unsigned char *sk, + const unsigned char *pub_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_thash_1( + leaf, sk, pub_seed, fors_leaf_addr); +} + +static void fors_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t fors_tree_addr[8]) { + uint32_t fors_leaf_addr[8] = {0}; + + /* Only copy the parts that must be kept in fors_leaf_addr. */ + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_copy_keypair_addr( + fors_leaf_addr, fors_tree_addr); + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_type( + fors_leaf_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_tree_index( + fors_leaf_addr, addr_idx); + + fors_gen_sk(leaf, sk_seed, fors_leaf_addr); + fors_sk_to_leaf(leaf, leaf, pub_seed, fors_leaf_addr); +} + +/** + * Interprets m as SPX_FORS_HEIGHT-bit unsigned integers. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + * Assumes indices has space for SPX_FORS_TREES integers. + */ +static void message_to_indices(uint32_t *indices, const unsigned char *m) { + unsigned int i, j; + unsigned int offset = 0; + + for (i = 0; i < SPX_FORS_TREES; i++) { + indices[i] = 0; + for (j = 0; j < SPX_FORS_HEIGHT; j++) { + indices[i] ^= (((uint32_t)m[offset >> 3] >> (offset & 0x7)) & 0x1) << j; + offset++; + } + } +} + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Include the secret key part that produces the selected leaf node. */ + fors_gen_sk(sig, sk_seed, fors_tree_addr); + sig += SPX_N; + + /* Compute the authentication path for this leaf node. */ + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_treehash_FORS_HEIGHT( + roots + i * SPX_N, sig, sk_seed, pub_seed, + indices[i], idx_offset, fors_gen_leaf, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + unsigned char leaf[SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Derive the leaf from the included secret key part. */ + fors_sk_to_leaf(leaf, sig, pub_seed, fors_tree_addr); + sig += SPX_N; + + /* Derive the corresponding root node of this tree. */ + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_compute_root( + roots + i * SPX_N, leaf, indices[i], idx_offset, sig, + SPX_FORS_HEIGHT, pub_seed, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} diff --git a/crypto_sign/sphincs-shake256-128s-simple/clean/fors.h b/crypto_sign/sphincs-shake256-128s-simple/clean/fors.h new file mode 100644 index 00000000..3a96a87c --- /dev/null +++ b/crypto_sign/sphincs-shake256-128s-simple/clean/fors.h @@ -0,0 +1,30 @@ +#ifndef SPX_FORS_H +#define SPX_FORS_H + +#include + +#include "params.h" + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]); + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-shake256-128s-simple/clean/hash.h b/crypto_sign/sphincs-shake256-128s-simple/clean/hash.h new file mode 100644 index 00000000..a0af6531 --- /dev/null +++ b/crypto_sign/sphincs-shake256-128s-simple/clean/hash.h @@ -0,0 +1,22 @@ +#ifndef SPX_HASH_H +#define SPX_HASH_H + +#include + +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed); + +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen); + +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen); + +#endif diff --git a/crypto_sign/sphincs-shake256-128s-simple/clean/hash_shake256.c b/crypto_sign/sphincs-shake256-128s-simple/clean/hash_shake256.c new file mode 100644 index 00000000..2408a3e0 --- /dev/null +++ b/crypto_sign/sphincs-shake256-128s-simple/clean/hash_shake256.c @@ -0,0 +1,87 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "utils.h" + +#include "fips202.h" + +/* For SHAKE256, there is no immediate reason to initialize at the start, + so this function is an empty operation. */ +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed) { + (void)pub_seed; /* Suppress an 'unused parameter' warning. */ + (void)sk_seed; /* Suppress an 'unused parameter' warning. */ +} + +/* + * Computes PRF(key, addr), given a secret key of SPX_N bytes and an address + */ +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]) { + unsigned char buf[SPX_N + SPX_ADDR_BYTES]; + + memcpy(buf, key, SPX_N); + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_addr_to_bytes(buf + SPX_N, addr); + + shake256(out, SPX_N, buf, SPX_N + SPX_ADDR_BYTES); +} + +/** + * Computes the message-dependent randomness R, using a secret seed and an + * optional randomization value as well as the message. + */ +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen) { + uint64_t s_inc[26]; + + shake256_inc_init(s_inc); + shake256_inc_absorb(s_inc, sk_prf, SPX_N); + shake256_inc_absorb(s_inc, optrand, SPX_N); + shake256_inc_absorb(s_inc, m, mlen); + shake256_inc_finalize(s_inc); + shake256_inc_squeeze(R, SPX_N, s_inc); +} + +/** + * Computes the message hash using R, the public key, and the message. + * Outputs the message digest and the index of the leaf. The index is split in + * the tree index and the leaf index, for convenient copying to an address. + */ +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen) { +#define SPX_TREE_BITS (SPX_TREE_HEIGHT * (SPX_D - 1)) +#define SPX_TREE_BYTES ((SPX_TREE_BITS + 7) / 8) +#define SPX_LEAF_BITS SPX_TREE_HEIGHT +#define SPX_LEAF_BYTES ((SPX_LEAF_BITS + 7) / 8) +#define SPX_DGST_BYTES (SPX_FORS_MSG_BYTES + SPX_TREE_BYTES + SPX_LEAF_BYTES) + + unsigned char buf[SPX_DGST_BYTES]; + unsigned char *bufp = buf; + uint64_t s_inc[26]; + + shake256_inc_init(s_inc); + shake256_inc_absorb(s_inc, R, SPX_N); + shake256_inc_absorb(s_inc, pk, SPX_PK_BYTES); + shake256_inc_absorb(s_inc, m, mlen); + shake256_inc_finalize(s_inc); + shake256_inc_squeeze(buf, SPX_DGST_BYTES, s_inc); + + memcpy(digest, bufp, SPX_FORS_MSG_BYTES); + bufp += SPX_FORS_MSG_BYTES; + + *tree = PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_bytes_to_ull( + bufp, SPX_TREE_BYTES); + *tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS); + bufp += SPX_TREE_BYTES; + + *leaf_idx = (uint32_t)PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_bytes_to_ull( + bufp, SPX_LEAF_BYTES); + *leaf_idx &= (~(uint32_t)0) >> (32 - SPX_LEAF_BITS); +} diff --git a/crypto_sign/sphincs-shake256-128s-simple/clean/params.h b/crypto_sign/sphincs-shake256-128s-simple/clean/params.h new file mode 100644 index 00000000..ac30fdea --- /dev/null +++ b/crypto_sign/sphincs-shake256-128s-simple/clean/params.h @@ -0,0 +1,53 @@ +#ifndef SPX_PARAMS_H +#define SPX_PARAMS_H + +/* Hash output length in bytes. */ +#define SPX_N 16 +/* Height of the hypertree. */ +#define SPX_FULL_HEIGHT 64 +/* Number of subtree layer. */ +#define SPX_D 8 +/* FORS tree dimensions. */ +#define SPX_FORS_HEIGHT 15 +#define SPX_FORS_TREES 10 +/* Winternitz parameter, */ +#define SPX_WOTS_W 16 + +/* The hash function is defined by linking a different hash.c file, as opposed + to setting a #define constant. */ + +/* For clarity */ +#define SPX_ADDR_BYTES 32 + +/* WOTS parameters. */ +#define SPX_WOTS_LOGW 4 + +#define SPX_WOTS_LEN1 (8 * SPX_N / SPX_WOTS_LOGW) + +/* SPX_WOTS_LEN2 is floor(log(len_1 * (w - 1)) / log(w)) + 1; we precompute */ +#define SPX_WOTS_LEN2 3 + +#define SPX_WOTS_LEN (SPX_WOTS_LEN1 + SPX_WOTS_LEN2) +#define SPX_WOTS_BYTES (SPX_WOTS_LEN * SPX_N) +#define SPX_WOTS_PK_BYTES SPX_WOTS_BYTES + +/* Subtree size. */ +#define SPX_TREE_HEIGHT (SPX_FULL_HEIGHT / SPX_D) + +/* FORS parameters. */ +#define SPX_FORS_MSG_BYTES ((SPX_FORS_HEIGHT * SPX_FORS_TREES + 7) / 8) +#define SPX_FORS_BYTES ((SPX_FORS_HEIGHT + 1) * SPX_FORS_TREES * SPX_N) +#define SPX_FORS_PK_BYTES SPX_N + +/* Resulting SPX sizes. */ +#define SPX_BYTES (SPX_N + SPX_FORS_BYTES + SPX_D * SPX_WOTS_BYTES +\ + SPX_FULL_HEIGHT * SPX_N) +#define SPX_PK_BYTES (2 * SPX_N) +#define SPX_SK_BYTES (2 * SPX_N + SPX_PK_BYTES) + +/* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ +#define SPX_OPTRAND_BYTES 32 + +#endif diff --git a/crypto_sign/sphincs-shake256-128s-simple/clean/sign.c b/crypto_sign/sphincs-shake256-128s-simple/clean/sign.c new file mode 100644 index 00000000..632c24bc --- /dev/null +++ b/crypto_sign/sphincs-shake256-128s-simple/clean/sign.c @@ -0,0 +1,344 @@ +#include +#include +#include + +#include "address.h" +#include "api.h" +#include "fors.h" +#include "hash.h" +#include "params.h" +#include "randombytes.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +/** + * Computes the leaf at a given address. First generates the WOTS key pair, + * then computes leaf by hashing horizontally. + */ +static void wots_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t tree_addr[8]) { + unsigned char pk[SPX_WOTS_BYTES]; + uint32_t wots_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, addr_idx); + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_wots_gen_pk( + pk, sk_seed, pub_seed, wots_addr); + + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_thash_WOTS_LEN( + leaf, pk, pub_seed, wots_pk_addr); +} + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_crypto_sign_secretkeybytes(void) { + return PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_CRYPTO_SECRETKEYBYTES; +} + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_crypto_sign_publickeybytes(void) { + return PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES; +} + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_crypto_sign_bytes(void) { + return PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_CRYPTO_BYTES; +} + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_crypto_sign_seedbytes(void) { + return PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_CRYPTO_SEEDBYTES; +} + +/* + * Generates an SPX key pair given a seed of length + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed) { + /* We do not need the auth path in key generation, but it simplifies the + code to have just one treehash routine that computes both root and path + in one function. */ + unsigned char auth_path[SPX_TREE_HEIGHT * SPX_N]; + uint32_t top_tree_addr[8] = {0}; + + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_layer_addr( + top_tree_addr, SPX_D - 1); + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_type( + top_tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */ + memcpy(sk, seed, PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_CRYPTO_SEEDBYTES); + + memcpy(pk, sk + 2 * SPX_N, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_initialize_hash_function(pk, sk); + + /* Compute root node of the top-most subtree. */ + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_treehash_TREE_HEIGHT( + sk + 3 * SPX_N, auth_path, sk, sk + 2 * SPX_N, 0, 0, + wots_gen_leaf, top_tree_addr); + + memcpy(pk + SPX_N, sk + 3 * SPX_N, SPX_N); + + return 0; +} + +/* + * Generates an SPX key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk) { + unsigned char seed[PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_CRYPTO_SEEDBYTES]; + randombytes(seed, PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_CRYPTO_SEEDBYTES); + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_crypto_sign_seed_keypair( + pk, sk, seed); + + return 0; +} + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + const unsigned char *sk_seed = sk; + const unsigned char *sk_prf = sk + SPX_N; + const unsigned char *pk = sk + 2 * SPX_N; + const unsigned char *pub_seed = pk; + + unsigned char optrand[SPX_N]; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char root[SPX_N]; + uint32_t i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_initialize_hash_function( + pub_seed, sk_seed); + + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ + randombytes(optrand, SPX_N); + /* Compute the digest randomization value. */ + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_gen_message_random( + sig, sk_prf, optrand, m, mlen); + + /* Derive the message digest and leaf index from R, PK and M. */ + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Sign the message hash using FORS. */ + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_fors_sign( + sig, root, mhash, sk_seed, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Compute a WOTS signature. */ + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_wots_sign( + sig, root, sk_seed, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the authentication path for the used WOTS leaf. */ + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_treehash_TREE_HEIGHT( + root, sig, sk_seed, pub_seed, idx_leaf, 0, + wots_gen_leaf, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + *siglen = SPX_BYTES; + + return 0; +} + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk) { + const unsigned char *pub_seed = pk; + const unsigned char *pub_root = pk + SPX_N; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char wots_pk[SPX_WOTS_BYTES]; + unsigned char root[SPX_N]; + unsigned char leaf[SPX_N]; + unsigned int i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + if (siglen != SPX_BYTES) { + return -1; + } + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_initialize_hash_function( + pub_seed, NULL); + + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + /* Derive the message digest and leaf index from R || PK || M. */ + /* The additional SPX_N is a result of the hash domain separator. */ + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + /* Layer correctly defaults to 0, so no need to set_layer_addr */ + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_fors_pk_from_sig( + root, sig, mhash, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + /* For each subtree.. */ + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + + /* The WOTS public key is only correct if the signature was correct. */ + /* Initially, root is the FORS pk, but on subsequent iterations it is + the root of the subtree below the currently processed subtree. */ + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_wots_pk_from_sig( + wots_pk, sig, root, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the leaf node using the WOTS public key. */ + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_thash_WOTS_LEN( + leaf, wots_pk, pub_seed, wots_pk_addr); + + /* Compute the root node of this subtree. */ + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_compute_root( + root, leaf, idx_leaf, 0, sig, SPX_TREE_HEIGHT, + pub_seed, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + /* Check if the root node equals the root node in the public key. */ + if (memcmp(root, pub_root, SPX_N) != 0) { + return -1; + } + + return 0; +} + + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + size_t siglen; + + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_crypto_sign_signature( + sm, &siglen, m, mlen, sk); + + memmove(sm + SPX_BYTES, m, mlen); + *smlen = siglen + mlen; + + return 0; +} + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk) { + /* The API caller does not necessarily know what size a signature should be + but SPHINCS+ signatures are always exactly SPX_BYTES. */ + if (smlen < SPX_BYTES) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + *mlen = smlen - SPX_BYTES; + + if (PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_crypto_sign_verify( + sm, SPX_BYTES, sm + SPX_BYTES, *mlen, pk)) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + /* If verification was successful, move the message to the right place. */ + memmove(m, sm + SPX_BYTES, *mlen); + + return 0; +} diff --git a/crypto_sign/sphincs-shake256-128s-simple/clean/thash.h b/crypto_sign/sphincs-shake256-128s-simple/clean/thash.h new file mode 100644 index 00000000..a4104617 --- /dev/null +++ b/crypto_sign/sphincs-shake256-128s-simple/clean/thash.h @@ -0,0 +1,22 @@ +#ifndef SPX_THASH_H +#define SPX_THASH_H + +#include + +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-shake256-128s-simple/clean/thash_shake256_simple.c b/crypto_sign/sphincs-shake256-128s-simple/clean/thash_shake256_simple.c new file mode 100644 index 00000000..29e55514 --- /dev/null +++ b/crypto_sign/sphincs-shake256-128s-simple/clean/thash_shake256_simple.c @@ -0,0 +1,61 @@ +#include +#include + +#include "address.h" +#include "params.h" +#include "thash.h" + +#include "fips202.h" + +/** + * Takes an array of inblocks concatenated arrays of SPX_N bytes. + */ +static void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_thash( + unsigned char *out, unsigned char *buf, + const unsigned char *in, unsigned int inblocks, + const unsigned char *pub_seed, uint32_t addr[8]) { + + memcpy(buf, pub_seed, SPX_N); + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_addr_to_bytes(buf + SPX_N, addr); + memcpy(buf + SPX_N + SPX_ADDR_BYTES, in, inblocks * SPX_N); + + shake256(out, SPX_N, buf, SPX_N + SPX_ADDR_BYTES + inblocks * SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_ADDR_BYTES + 1 * SPX_N]; + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_thash( + out, buf, in, 1, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_ADDR_BYTES + 2 * SPX_N]; + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_thash( + out, buf, in, 2, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_ADDR_BYTES + SPX_WOTS_LEN * SPX_N]; + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_thash( + out, buf, in, SPX_WOTS_LEN, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_ADDR_BYTES + SPX_FORS_TREES * SPX_N]; + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_thash( + out, buf, in, SPX_FORS_TREES, pub_seed, addr); +} diff --git a/crypto_sign/sphincs-shake256-128s-simple/clean/utils.c b/crypto_sign/sphincs-shake256-128s-simple/clean/utils.c new file mode 100644 index 00000000..7abec8e9 --- /dev/null +++ b/crypto_sign/sphincs-shake256-128s-simple/clean/utils.c @@ -0,0 +1,192 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in) { + + /* Iterate over out in decreasing order, for big-endianness. */ + for (size_t i = outlen; i > 0; i--) { + out[i - 1] = in & 0xff; + in = in >> 8; + } +} + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen) { + unsigned long long retval = 0; + + for (size_t i = 0; i < inlen; i++) { + retval |= ((unsigned long long)in[i]) << (8 * (inlen - 1 - i)); + } + return retval; +} + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + unsigned char buffer[2 * SPX_N]; + + /* If leaf_idx is odd (last bit = 1), current path element is a right child + and auth_path has to go left. Otherwise it is the other way around. */ + if (leaf_idx & 1) { + memcpy(buffer + SPX_N, leaf, SPX_N); + memcpy(buffer, auth_path, SPX_N); + } else { + memcpy(buffer, leaf, SPX_N); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + + for (i = 0; i < tree_height - 1; i++) { + leaf_idx >>= 1; + idx_offset >>= 1; + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_tree_height(addr, i + 1); + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + + /* Pick the right or left neighbor, depending on parity of the node. */ + if (leaf_idx & 1) { + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_thash_2( + buffer + SPX_N, buffer, pub_seed, addr); + memcpy(buffer, auth_path, SPX_N); + } else { + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_thash_2( + buffer, buffer, pub_seed, addr); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + } + + /* The last iteration is exceptional; we do not copy an auth_path node. */ + leaf_idx >>= 1; + idx_offset >>= 1; + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_tree_height(addr, tree_height); + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_thash_2( + root, buffer, pub_seed, addr); +} + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +static void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_treehash( + unsigned char *root, unsigned char *auth_path, + unsigned char *stack, unsigned int *heights, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned int offset = 0; + uint32_t idx; + uint32_t tree_idx; + + for (idx = 0; idx < (uint32_t)(1 << tree_height); idx++) { + /* Add the next leaf node to the stack. */ + gen_leaf(stack + offset * SPX_N, + sk_seed, pub_seed, idx + idx_offset, tree_addr); + offset++; + heights[offset - 1] = 0; + + /* If this is a node we need for the auth path.. */ + if ((leaf_idx ^ 0x1) == idx) { + memcpy(auth_path, stack + (offset - 1)*SPX_N, SPX_N); + } + + /* While the top-most nodes are of equal height.. */ + while (offset >= 2 && heights[offset - 1] == heights[offset - 2]) { + /* Compute index of the new node, in the next layer. */ + tree_idx = (idx >> (heights[offset - 1] + 1)); + + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_tree_height( + tree_addr, heights[offset - 1] + 1); + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_tree_index( + tree_addr, tree_idx + (idx_offset >> (heights[offset - 1] + 1))); + /* Hash the top-most nodes from the stack together. */ + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_thash_2( + stack + (offset - 2)*SPX_N, stack + (offset - 2)*SPX_N, + pub_seed, tree_addr); + offset--; + /* Note that the top-most node is now one layer higher. */ + heights[offset - 1]++; + + /* If this is a node we need for the auth path.. */ + if (((leaf_idx >> heights[offset - 1]) ^ 0x1) == tree_idx) { + memcpy(auth_path + heights[offset - 1]*SPX_N, + stack + (offset - 1)*SPX_N, SPX_N); + } + } + } + memcpy(root, stack, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_FORS_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_FORS_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_FORS_HEIGHT, gen_leaf, tree_addr); +} + +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_TREE_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_TREE_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_TREE_HEIGHT, gen_leaf, tree_addr); +} diff --git a/crypto_sign/sphincs-shake256-128s-simple/clean/utils.h b/crypto_sign/sphincs-shake256-128s-simple/clean/utils.h new file mode 100644 index 00000000..40d514f4 --- /dev/null +++ b/crypto_sign/sphincs-shake256-128s-simple/clean/utils.h @@ -0,0 +1,60 @@ +#ifndef SPX_UTILS_H +#define SPX_UTILS_H + +#include "params.h" +#include +#include + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in); + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen); + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-shake256-128s-simple/clean/wots.c b/crypto_sign/sphincs-shake256-128s-simple/clean/wots.c new file mode 100644 index 00000000..ebdf48fd --- /dev/null +++ b/crypto_sign/sphincs-shake256-128s-simple/clean/wots.c @@ -0,0 +1,161 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +// TODO clarify address expectations, and make them more uniform. +// TODO i.e. do we expect types to be set already? +// TODO and do we expect modifications or copies? + +/** + * Computes the starting value for a chain, i.e. the secret key. + * Expects the address to be complete up to the chain address. + */ +static void wots_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t wots_addr[8]) { + /* Make sure that the hash address is actually zeroed. */ + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_hash_addr(wots_addr, 0); + + /* Generate sk element. */ + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_prf_addr(sk, sk_seed, wots_addr); +} + +/** + * Computes the chaining function. + * out and in have to be n-byte arrays. + * + * Interprets in as start-th value of the chain. + * addr has to contain the address of the chain. + */ +static void gen_chain(unsigned char *out, const unsigned char *in, + unsigned int start, unsigned int steps, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + /* Initialize out with the value at position 'start'. */ + memcpy(out, in, SPX_N); + + /* Iterate 'steps' calls to the hash function. */ + for (i = start; i < (start + steps) && i < SPX_WOTS_W; i++) { + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_hash_addr(addr, i); + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_thash_1( + out, out, pub_seed, addr); + } +} + +/** + * base_w algorithm as described in draft. + * Interprets an array of bytes as integers in base w. + * This only works when log_w is a divisor of 8. + */ +static void base_w(unsigned int *output, const size_t out_len, + const unsigned char *input) { + size_t in = 0; + size_t out = 0; + unsigned char total = 0; + unsigned int bits = 0; + size_t consumed; + + for (consumed = 0; consumed < out_len; consumed++) { + if (bits == 0) { + total = input[in]; + in++; + bits += 8; + } + bits -= SPX_WOTS_LOGW; + output[out] = (unsigned int)((total >> bits) & (SPX_WOTS_W - 1)); + out++; + } +} + +/* Computes the WOTS+ checksum over a message (in base_w). */ +static void wots_checksum(unsigned int *csum_base_w, + const unsigned int *msg_base_w) { + unsigned int csum = 0; + unsigned char csum_bytes[(SPX_WOTS_LEN2 * SPX_WOTS_LOGW + 7) / 8]; + unsigned int i; + + /* Compute checksum. */ + for (i = 0; i < SPX_WOTS_LEN1; i++) { + csum += SPX_WOTS_W - 1 - msg_base_w[i]; + } + + /* Convert checksum to base_w. */ + /* Make sure expected empty zero bits are the least significant bits. */ + csum = csum << (8 - ((SPX_WOTS_LEN2 * SPX_WOTS_LOGW) % 8)); + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_ull_to_bytes( + csum_bytes, sizeof(csum_bytes), csum); + base_w(csum_base_w, SPX_WOTS_LEN2, csum_bytes); +} + +/* Takes a message and derives the matching chain lengths. */ +static void chain_lengths(unsigned int *lengths, const unsigned char *msg) { + base_w(lengths, SPX_WOTS_LEN1, msg); + wots_checksum(lengths + SPX_WOTS_LEN1, lengths); +} + +/** + * WOTS key generation. Takes a 32 byte sk_seed, expands it to WOTS private key + * elements and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(pk + i * SPX_N, sk_seed, addr); + gen_chain(pk + i * SPX_N, pk + i * SPX_N, + 0, SPX_WOTS_W - 1, pub_seed, addr); + } +} + +/** + * Takes a n-byte message and the 32-byte sk_see to compute a signature 'sig'. + */ +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(sig + i * SPX_N, sk_seed, addr); + gen_chain(sig + i * SPX_N, sig + i * SPX_N, 0, lengths[i], pub_seed, addr); + } +} + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_set_chain_addr(addr, i); + gen_chain(pk + i * SPX_N, sig + i * SPX_N, + lengths[i], SPX_WOTS_W - 1 - lengths[i], pub_seed, addr); + } +} diff --git a/crypto_sign/sphincs-shake256-128s-simple/clean/wots.h b/crypto_sign/sphincs-shake256-128s-simple/clean/wots.h new file mode 100644 index 00000000..871fb7f0 --- /dev/null +++ b/crypto_sign/sphincs-shake256-128s-simple/clean/wots.h @@ -0,0 +1,38 @@ +#ifndef SPX_WOTS_H +#define SPX_WOTS_H + +#include "params.h" +#include + +/** + * WOTS key generation. Takes a 32 byte seed for the private key, expands it to + * a full WOTS private key and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * Takes a n-byte message and the 32-byte seed for the private key to compute a + * signature that is placed at 'sig'. + */ +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]); + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHAKE256128SSIMPLE_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-shake256-192f-robust/META.yml b/crypto_sign/sphincs-shake256-192f-robust/META.yml new file mode 100644 index 00000000..95be6a3a --- /dev/null +++ b/crypto_sign/sphincs-shake256-192f-robust/META.yml @@ -0,0 +1,27 @@ +name: SPHINCS+ +type: signature +claimed-nist-level: 3 +length-public-key: 48 +length-secret-key: 96 +length-signature: 35664 +testvectors-sha256: de65b2a7b6d5e819f58b6e1a08ec4ef3308a9c36b7c962450105f82263e35e98 +principal-submitter: Andreas Hülsing +auxiliary-submitters: + - Jean-Philippe Aumasson + - Daniel J. Bernstein, + - Christoph Dobraunig + - Maria Eichlseder + - Scott Fluhrer + - Stefan-Lukas Gazdag + - Panos Kampanakis + - Stefan Kölbl + - Tanja Lange + - Martin M. Lauridsen + - Florian Mendel + - Ruben Niederhagen + - Christian Rechberger + - Joost Rijneveld + - Peter Schwabe +implementations: + - name: clean + version: https://github.com/sphincs/sphincsplus/commit/77755c94d0bc744478044d6efbb888dc13156441 diff --git a/crypto_sign/sphincs-shake256-192f-robust/clean/LICENSE b/crypto_sign/sphincs-shake256-192f-robust/clean/LICENSE new file mode 100644 index 00000000..670154e3 --- /dev/null +++ b/crypto_sign/sphincs-shake256-192f-robust/clean/LICENSE @@ -0,0 +1,116 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + diff --git a/crypto_sign/sphincs-shake256-192f-robust/clean/Makefile b/crypto_sign/sphincs-shake256-192f-robust/clean/Makefile new file mode 100644 index 00000000..1173c5ef --- /dev/null +++ b/crypto_sign/sphincs-shake256-192f-robust/clean/Makefile @@ -0,0 +1,20 @@ +# This Makefile can be used with GNU Make or BSD Make + +LIB=libsphincs-shake256-192f-robust_clean.a + +HEADERS = params.h address.h wots.h utils.h fors.h api.h hash.h thash.h +OBJECTS = address.o wots.o utils.o fors.o sign.o hash_shake256.o thash_shake256_robust.o + +CFLAGS=-O3 -Wall -Wconversion -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) + +all: $(LIB) + +%.o: %.c $(HEADERS) + $(CC) $(CFLAGS) -c -o $@ $< + +$(LIB): $(OBJECTS) + $(AR) -r $@ $(OBJECTS) + +clean: + $(RM) $(OBJECTS) + $(RM) $(LIB) diff --git a/crypto_sign/sphincs-shake256-192f-robust/clean/Makefile.Microsoft_nmake b/crypto_sign/sphincs-shake256-192f-robust/clean/Makefile.Microsoft_nmake new file mode 100644 index 00000000..34927c1a --- /dev/null +++ b/crypto_sign/sphincs-shake256-192f-robust/clean/Makefile.Microsoft_nmake @@ -0,0 +1,19 @@ +# This Makefile can be used with Microsoft Visual Studio's nmake using the command: +# nmake /f Makefile.Microsoft_nmake + +LIBRARY=libsphincs-shake256-192f-robust_clean.lib +OBJECTS=address.obj wots.obj utils.obj fors.obj sign.obj hash_shake256.obj thash_shake256_robust.obj + +CFLAGS=/nologo /I ..\..\..\common /W4 /WX + +all: $(LIBRARY) + +# Make sure objects are recompiled if headers change. +$(OBJECTS): *.h + +$(LIBRARY): $(OBJECTS) + LIB.EXE /NOLOGO /WX /OUT:$@ $** + +clean: + -DEL $(OBJECTS) + -DEL $(LIBRARY) diff --git a/crypto_sign/sphincs-shake256-192f-robust/clean/address.c b/crypto_sign/sphincs-shake256-192f-robust/clean/address.c new file mode 100644 index 00000000..324dd196 --- /dev/null +++ b/crypto_sign/sphincs-shake256-192f-robust/clean/address.c @@ -0,0 +1,78 @@ +#include + +#include "address.h" +#include "params.h" +#include "utils.h" + +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]) { + int i; + + for (i = 0; i < 8; i++) { + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_ull_to_bytes( + bytes + i * 4, 4, addr[i]); + } +} + +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer) { + addr[0] = layer; +} + +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree) { + addr[1] = 0; + addr[2] = (uint32_t) (tree >> 32); + addr[3] = (uint32_t) tree; +} + +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_type( + uint32_t addr[8], uint32_t type) { + addr[4] = type; +} + +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; +} + +/* These functions are used for OTS addresses. */ + +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair) { + addr[5] = keypair; +} + +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; + out[5] = in[5]; +} + +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain) { + addr[6] = chain; +} + +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash) { + addr[7] = hash; +} + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height) { + addr[6] = tree_height; +} + +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index) { + addr[7] = tree_index; +} diff --git a/crypto_sign/sphincs-shake256-192f-robust/clean/address.h b/crypto_sign/sphincs-shake256-192f-robust/clean/address.h new file mode 100644 index 00000000..151a4d7e --- /dev/null +++ b/crypto_sign/sphincs-shake256-192f-robust/clean/address.h @@ -0,0 +1,50 @@ +#ifndef SPX_ADDRESS_H +#define SPX_ADDRESS_H + +#include + +#define SPX_ADDR_TYPE_WOTS 0 +#define SPX_ADDR_TYPE_WOTSPK 1 +#define SPX_ADDR_TYPE_HASHTREE 2 +#define SPX_ADDR_TYPE_FORSTREE 3 +#define SPX_ADDR_TYPE_FORSPK 4 + +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer); + +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree); + +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_type( + uint32_t addr[8], uint32_t type); + +/* Copies the layer and tree part of one address into the other */ +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for WOTS and FORS addresses. */ + +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair); + +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain); + +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash); + +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height); + +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index); + +#endif diff --git a/crypto_sign/sphincs-shake256-192f-robust/clean/api.h b/crypto_sign/sphincs-shake256-192f-robust/clean/api.h new file mode 100644 index 00000000..4b857690 --- /dev/null +++ b/crypto_sign/sphincs-shake256-192f-robust/clean/api.h @@ -0,0 +1,78 @@ +#ifndef PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_API_H +#define PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_API_H + +#include +#include + +#define PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_CRYPTO_ALGNAME "SPHINCS+" + +#define PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_CRYPTO_SECRETKEYBYTES 96 +#define PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES 48 +#define PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_CRYPTO_BYTES 35664 +#define PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_CRYPTO_SEEDBYTES 72 + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_crypto_sign_secretkeybytes(void); + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_crypto_sign_publickeybytes(void); + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_crypto_sign_bytes(void); + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_crypto_sign_seedbytes(void); + +/* + * Generates a SPHINCS+ key pair given a seed. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed); + +/* + * Generates a SPHINCS+ key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk); + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk); + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk); + +#endif diff --git a/crypto_sign/sphincs-shake256-192f-robust/clean/fors.c b/crypto_sign/sphincs-shake256-192f-robust/clean/fors.c new file mode 100644 index 00000000..41a1b6d4 --- /dev/null +++ b/crypto_sign/sphincs-shake256-192f-robust/clean/fors.c @@ -0,0 +1,164 @@ +#include +#include +#include + +#include "address.h" +#include "fors.h" +#include "hash.h" +#include "thash.h" +#include "utils.h" + +static void fors_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_prf_addr( + sk, sk_seed, fors_leaf_addr); +} + +static void fors_sk_to_leaf(unsigned char *leaf, const unsigned char *sk, + const unsigned char *pub_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_thash_1( + leaf, sk, pub_seed, fors_leaf_addr); +} + +static void fors_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t fors_tree_addr[8]) { + uint32_t fors_leaf_addr[8] = {0}; + + /* Only copy the parts that must be kept in fors_leaf_addr. */ + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_copy_keypair_addr( + fors_leaf_addr, fors_tree_addr); + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_type( + fors_leaf_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_tree_index( + fors_leaf_addr, addr_idx); + + fors_gen_sk(leaf, sk_seed, fors_leaf_addr); + fors_sk_to_leaf(leaf, leaf, pub_seed, fors_leaf_addr); +} + +/** + * Interprets m as SPX_FORS_HEIGHT-bit unsigned integers. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + * Assumes indices has space for SPX_FORS_TREES integers. + */ +static void message_to_indices(uint32_t *indices, const unsigned char *m) { + unsigned int i, j; + unsigned int offset = 0; + + for (i = 0; i < SPX_FORS_TREES; i++) { + indices[i] = 0; + for (j = 0; j < SPX_FORS_HEIGHT; j++) { + indices[i] ^= (((uint32_t)m[offset >> 3] >> (offset & 0x7)) & 0x1) << j; + offset++; + } + } +} + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Include the secret key part that produces the selected leaf node. */ + fors_gen_sk(sig, sk_seed, fors_tree_addr); + sig += SPX_N; + + /* Compute the authentication path for this leaf node. */ + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_treehash_FORS_HEIGHT( + roots + i * SPX_N, sig, sk_seed, pub_seed, + indices[i], idx_offset, fors_gen_leaf, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + unsigned char leaf[SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Derive the leaf from the included secret key part. */ + fors_sk_to_leaf(leaf, sig, pub_seed, fors_tree_addr); + sig += SPX_N; + + /* Derive the corresponding root node of this tree. */ + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_compute_root( + roots + i * SPX_N, leaf, indices[i], idx_offset, sig, + SPX_FORS_HEIGHT, pub_seed, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} diff --git a/crypto_sign/sphincs-shake256-192f-robust/clean/fors.h b/crypto_sign/sphincs-shake256-192f-robust/clean/fors.h new file mode 100644 index 00000000..b4634d76 --- /dev/null +++ b/crypto_sign/sphincs-shake256-192f-robust/clean/fors.h @@ -0,0 +1,30 @@ +#ifndef SPX_FORS_H +#define SPX_FORS_H + +#include + +#include "params.h" + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]); + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-shake256-192f-robust/clean/hash.h b/crypto_sign/sphincs-shake256-192f-robust/clean/hash.h new file mode 100644 index 00000000..fa0f6885 --- /dev/null +++ b/crypto_sign/sphincs-shake256-192f-robust/clean/hash.h @@ -0,0 +1,22 @@ +#ifndef SPX_HASH_H +#define SPX_HASH_H + +#include + +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed); + +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen); + +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen); + +#endif diff --git a/crypto_sign/sphincs-shake256-192f-robust/clean/hash_shake256.c b/crypto_sign/sphincs-shake256-192f-robust/clean/hash_shake256.c new file mode 100644 index 00000000..6e5b7b07 --- /dev/null +++ b/crypto_sign/sphincs-shake256-192f-robust/clean/hash_shake256.c @@ -0,0 +1,87 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "utils.h" + +#include "fips202.h" + +/* For SHAKE256, there is no immediate reason to initialize at the start, + so this function is an empty operation. */ +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed) { + (void)pub_seed; /* Suppress an 'unused parameter' warning. */ + (void)sk_seed; /* Suppress an 'unused parameter' warning. */ +} + +/* + * Computes PRF(key, addr), given a secret key of SPX_N bytes and an address + */ +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]) { + unsigned char buf[SPX_N + SPX_ADDR_BYTES]; + + memcpy(buf, key, SPX_N); + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_addr_to_bytes(buf + SPX_N, addr); + + shake256(out, SPX_N, buf, SPX_N + SPX_ADDR_BYTES); +} + +/** + * Computes the message-dependent randomness R, using a secret seed and an + * optional randomization value as well as the message. + */ +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen) { + uint64_t s_inc[26]; + + shake256_inc_init(s_inc); + shake256_inc_absorb(s_inc, sk_prf, SPX_N); + shake256_inc_absorb(s_inc, optrand, SPX_N); + shake256_inc_absorb(s_inc, m, mlen); + shake256_inc_finalize(s_inc); + shake256_inc_squeeze(R, SPX_N, s_inc); +} + +/** + * Computes the message hash using R, the public key, and the message. + * Outputs the message digest and the index of the leaf. The index is split in + * the tree index and the leaf index, for convenient copying to an address. + */ +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen) { +#define SPX_TREE_BITS (SPX_TREE_HEIGHT * (SPX_D - 1)) +#define SPX_TREE_BYTES ((SPX_TREE_BITS + 7) / 8) +#define SPX_LEAF_BITS SPX_TREE_HEIGHT +#define SPX_LEAF_BYTES ((SPX_LEAF_BITS + 7) / 8) +#define SPX_DGST_BYTES (SPX_FORS_MSG_BYTES + SPX_TREE_BYTES + SPX_LEAF_BYTES) + + unsigned char buf[SPX_DGST_BYTES]; + unsigned char *bufp = buf; + uint64_t s_inc[26]; + + shake256_inc_init(s_inc); + shake256_inc_absorb(s_inc, R, SPX_N); + shake256_inc_absorb(s_inc, pk, SPX_PK_BYTES); + shake256_inc_absorb(s_inc, m, mlen); + shake256_inc_finalize(s_inc); + shake256_inc_squeeze(buf, SPX_DGST_BYTES, s_inc); + + memcpy(digest, bufp, SPX_FORS_MSG_BYTES); + bufp += SPX_FORS_MSG_BYTES; + + *tree = PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_bytes_to_ull( + bufp, SPX_TREE_BYTES); + *tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS); + bufp += SPX_TREE_BYTES; + + *leaf_idx = (uint32_t)PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_bytes_to_ull( + bufp, SPX_LEAF_BYTES); + *leaf_idx &= (~(uint32_t)0) >> (32 - SPX_LEAF_BITS); +} diff --git a/crypto_sign/sphincs-shake256-192f-robust/clean/params.h b/crypto_sign/sphincs-shake256-192f-robust/clean/params.h new file mode 100644 index 00000000..898f177f --- /dev/null +++ b/crypto_sign/sphincs-shake256-192f-robust/clean/params.h @@ -0,0 +1,53 @@ +#ifndef SPX_PARAMS_H +#define SPX_PARAMS_H + +/* Hash output length in bytes. */ +#define SPX_N 24 +/* Height of the hypertree. */ +#define SPX_FULL_HEIGHT 66 +/* Number of subtree layer. */ +#define SPX_D 22 +/* FORS tree dimensions. */ +#define SPX_FORS_HEIGHT 8 +#define SPX_FORS_TREES 33 +/* Winternitz parameter, */ +#define SPX_WOTS_W 16 + +/* The hash function is defined by linking a different hash.c file, as opposed + to setting a #define constant. */ + +/* For clarity */ +#define SPX_ADDR_BYTES 32 + +/* WOTS parameters. */ +#define SPX_WOTS_LOGW 4 + +#define SPX_WOTS_LEN1 (8 * SPX_N / SPX_WOTS_LOGW) + +/* SPX_WOTS_LEN2 is floor(log(len_1 * (w - 1)) / log(w)) + 1; we precompute */ +#define SPX_WOTS_LEN2 3 + +#define SPX_WOTS_LEN (SPX_WOTS_LEN1 + SPX_WOTS_LEN2) +#define SPX_WOTS_BYTES (SPX_WOTS_LEN * SPX_N) +#define SPX_WOTS_PK_BYTES SPX_WOTS_BYTES + +/* Subtree size. */ +#define SPX_TREE_HEIGHT (SPX_FULL_HEIGHT / SPX_D) + +/* FORS parameters. */ +#define SPX_FORS_MSG_BYTES ((SPX_FORS_HEIGHT * SPX_FORS_TREES + 7) / 8) +#define SPX_FORS_BYTES ((SPX_FORS_HEIGHT + 1) * SPX_FORS_TREES * SPX_N) +#define SPX_FORS_PK_BYTES SPX_N + +/* Resulting SPX sizes. */ +#define SPX_BYTES (SPX_N + SPX_FORS_BYTES + SPX_D * SPX_WOTS_BYTES +\ + SPX_FULL_HEIGHT * SPX_N) +#define SPX_PK_BYTES (2 * SPX_N) +#define SPX_SK_BYTES (2 * SPX_N + SPX_PK_BYTES) + +/* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ +#define SPX_OPTRAND_BYTES 32 + +#endif diff --git a/crypto_sign/sphincs-shake256-192f-robust/clean/sign.c b/crypto_sign/sphincs-shake256-192f-robust/clean/sign.c new file mode 100644 index 00000000..351a25a9 --- /dev/null +++ b/crypto_sign/sphincs-shake256-192f-robust/clean/sign.c @@ -0,0 +1,344 @@ +#include +#include +#include + +#include "address.h" +#include "api.h" +#include "fors.h" +#include "hash.h" +#include "params.h" +#include "randombytes.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +/** + * Computes the leaf at a given address. First generates the WOTS key pair, + * then computes leaf by hashing horizontally. + */ +static void wots_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t tree_addr[8]) { + unsigned char pk[SPX_WOTS_BYTES]; + uint32_t wots_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_keypair_addr( + wots_addr, addr_idx); + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_wots_gen_pk( + pk, sk_seed, pub_seed, wots_addr); + + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_thash_WOTS_LEN( + leaf, pk, pub_seed, wots_pk_addr); +} + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_crypto_sign_secretkeybytes(void) { + return PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_CRYPTO_SECRETKEYBYTES; +} + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_crypto_sign_publickeybytes(void) { + return PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES; +} + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_crypto_sign_bytes(void) { + return PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_CRYPTO_BYTES; +} + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_crypto_sign_seedbytes(void) { + return PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_CRYPTO_SEEDBYTES; +} + +/* + * Generates an SPX key pair given a seed of length + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed) { + /* We do not need the auth path in key generation, but it simplifies the + code to have just one treehash routine that computes both root and path + in one function. */ + unsigned char auth_path[SPX_TREE_HEIGHT * SPX_N]; + uint32_t top_tree_addr[8] = {0}; + + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_layer_addr( + top_tree_addr, SPX_D - 1); + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_type( + top_tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */ + memcpy(sk, seed, PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_CRYPTO_SEEDBYTES); + + memcpy(pk, sk + 2 * SPX_N, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_initialize_hash_function(pk, sk); + + /* Compute root node of the top-most subtree. */ + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_treehash_TREE_HEIGHT( + sk + 3 * SPX_N, auth_path, sk, sk + 2 * SPX_N, 0, 0, + wots_gen_leaf, top_tree_addr); + + memcpy(pk + SPX_N, sk + 3 * SPX_N, SPX_N); + + return 0; +} + +/* + * Generates an SPX key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk) { + unsigned char seed[PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_CRYPTO_SEEDBYTES]; + randombytes(seed, PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_CRYPTO_SEEDBYTES); + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_crypto_sign_seed_keypair( + pk, sk, seed); + + return 0; +} + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + const unsigned char *sk_seed = sk; + const unsigned char *sk_prf = sk + SPX_N; + const unsigned char *pk = sk + 2 * SPX_N; + const unsigned char *pub_seed = pk; + + unsigned char optrand[SPX_N]; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char root[SPX_N]; + uint32_t i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_initialize_hash_function( + pub_seed, sk_seed); + + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ + randombytes(optrand, SPX_N); + /* Compute the digest randomization value. */ + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_gen_message_random( + sig, sk_prf, optrand, m, mlen); + + /* Derive the message digest and leaf index from R, PK and M. */ + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Sign the message hash using FORS. */ + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_fors_sign( + sig, root, mhash, sk_seed, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Compute a WOTS signature. */ + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_wots_sign( + sig, root, sk_seed, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the authentication path for the used WOTS leaf. */ + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_treehash_TREE_HEIGHT( + root, sig, sk_seed, pub_seed, idx_leaf, 0, + wots_gen_leaf, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + *siglen = SPX_BYTES; + + return 0; +} + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk) { + const unsigned char *pub_seed = pk; + const unsigned char *pub_root = pk + SPX_N; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char wots_pk[SPX_WOTS_BYTES]; + unsigned char root[SPX_N]; + unsigned char leaf[SPX_N]; + unsigned int i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + if (siglen != SPX_BYTES) { + return -1; + } + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_initialize_hash_function( + pub_seed, NULL); + + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + /* Derive the message digest and leaf index from R || PK || M. */ + /* The additional SPX_N is a result of the hash domain separator. */ + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + /* Layer correctly defaults to 0, so no need to set_layer_addr */ + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_fors_pk_from_sig( + root, sig, mhash, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + /* For each subtree.. */ + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + + /* The WOTS public key is only correct if the signature was correct. */ + /* Initially, root is the FORS pk, but on subsequent iterations it is + the root of the subtree below the currently processed subtree. */ + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_wots_pk_from_sig( + wots_pk, sig, root, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the leaf node using the WOTS public key. */ + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_thash_WOTS_LEN( + leaf, wots_pk, pub_seed, wots_pk_addr); + + /* Compute the root node of this subtree. */ + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_compute_root( + root, leaf, idx_leaf, 0, sig, SPX_TREE_HEIGHT, + pub_seed, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + /* Check if the root node equals the root node in the public key. */ + if (memcmp(root, pub_root, SPX_N) != 0) { + return -1; + } + + return 0; +} + + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + size_t siglen; + + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_crypto_sign_signature( + sm, &siglen, m, mlen, sk); + + memmove(sm + SPX_BYTES, m, mlen); + *smlen = siglen + mlen; + + return 0; +} + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk) { + /* The API caller does not necessarily know what size a signature should be + but SPHINCS+ signatures are always exactly SPX_BYTES. */ + if (smlen < SPX_BYTES) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + *mlen = smlen - SPX_BYTES; + + if (PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_crypto_sign_verify( + sm, SPX_BYTES, sm + SPX_BYTES, *mlen, pk)) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + /* If verification was successful, move the message to the right place. */ + memmove(m, sm + SPX_BYTES, *mlen); + + return 0; +} diff --git a/crypto_sign/sphincs-shake256-192f-robust/clean/thash.h b/crypto_sign/sphincs-shake256-192f-robust/clean/thash.h new file mode 100644 index 00000000..c9711f5a --- /dev/null +++ b/crypto_sign/sphincs-shake256-192f-robust/clean/thash.h @@ -0,0 +1,22 @@ +#ifndef SPX_THASH_H +#define SPX_THASH_H + +#include + +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-shake256-192f-robust/clean/thash_shake256_robust.c b/crypto_sign/sphincs-shake256-192f-robust/clean/thash_shake256_robust.c new file mode 100644 index 00000000..81db67b3 --- /dev/null +++ b/crypto_sign/sphincs-shake256-192f-robust/clean/thash_shake256_robust.c @@ -0,0 +1,69 @@ +#include +#include + +#include "address.h" +#include "params.h" +#include "thash.h" + +#include "fips202.h" + +/** + * Takes an array of inblocks concatenated arrays of SPX_N bytes. + */ +static void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_thash( + unsigned char *out, unsigned char *buf, + const unsigned char *in, unsigned int inblocks, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char *bitmask = buf + SPX_N + SPX_ADDR_BYTES; + unsigned int i; + + memcpy(buf, pub_seed, SPX_N); + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_addr_to_bytes(buf + SPX_N, addr); + + shake256(bitmask, inblocks * SPX_N, buf, SPX_N + SPX_ADDR_BYTES); + + for (i = 0; i < inblocks * SPX_N; i++) { + buf[SPX_N + SPX_ADDR_BYTES + i] = in[i] ^ bitmask[i]; + } + + shake256(out, SPX_N, buf, SPX_N + SPX_ADDR_BYTES + inblocks * SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_ADDR_BYTES + 1 * SPX_N]; + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_thash( + out, buf, in, 1, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_ADDR_BYTES + 2 * SPX_N]; + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_thash( + out, buf, in, 2, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_ADDR_BYTES + SPX_WOTS_LEN * SPX_N]; + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_thash( + out, buf, in, SPX_WOTS_LEN, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_ADDR_BYTES + SPX_FORS_TREES * SPX_N]; + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_thash( + out, buf, in, SPX_FORS_TREES, pub_seed, addr); +} diff --git a/crypto_sign/sphincs-shake256-192f-robust/clean/utils.c b/crypto_sign/sphincs-shake256-192f-robust/clean/utils.c new file mode 100644 index 00000000..351f17e2 --- /dev/null +++ b/crypto_sign/sphincs-shake256-192f-robust/clean/utils.c @@ -0,0 +1,192 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in) { + + /* Iterate over out in decreasing order, for big-endianness. */ + for (size_t i = outlen; i > 0; i--) { + out[i - 1] = in & 0xff; + in = in >> 8; + } +} + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen) { + unsigned long long retval = 0; + + for (size_t i = 0; i < inlen; i++) { + retval |= ((unsigned long long)in[i]) << (8 * (inlen - 1 - i)); + } + return retval; +} + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + unsigned char buffer[2 * SPX_N]; + + /* If leaf_idx is odd (last bit = 1), current path element is a right child + and auth_path has to go left. Otherwise it is the other way around. */ + if (leaf_idx & 1) { + memcpy(buffer + SPX_N, leaf, SPX_N); + memcpy(buffer, auth_path, SPX_N); + } else { + memcpy(buffer, leaf, SPX_N); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + + for (i = 0; i < tree_height - 1; i++) { + leaf_idx >>= 1; + idx_offset >>= 1; + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_tree_height(addr, i + 1); + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + + /* Pick the right or left neighbor, depending on parity of the node. */ + if (leaf_idx & 1) { + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_thash_2( + buffer + SPX_N, buffer, pub_seed, addr); + memcpy(buffer, auth_path, SPX_N); + } else { + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_thash_2( + buffer, buffer, pub_seed, addr); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + } + + /* The last iteration is exceptional; we do not copy an auth_path node. */ + leaf_idx >>= 1; + idx_offset >>= 1; + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_tree_height(addr, tree_height); + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_thash_2( + root, buffer, pub_seed, addr); +} + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +static void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_treehash( + unsigned char *root, unsigned char *auth_path, + unsigned char *stack, unsigned int *heights, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned int offset = 0; + uint32_t idx; + uint32_t tree_idx; + + for (idx = 0; idx < (uint32_t)(1 << tree_height); idx++) { + /* Add the next leaf node to the stack. */ + gen_leaf(stack + offset * SPX_N, + sk_seed, pub_seed, idx + idx_offset, tree_addr); + offset++; + heights[offset - 1] = 0; + + /* If this is a node we need for the auth path.. */ + if ((leaf_idx ^ 0x1) == idx) { + memcpy(auth_path, stack + (offset - 1)*SPX_N, SPX_N); + } + + /* While the top-most nodes are of equal height.. */ + while (offset >= 2 && heights[offset - 1] == heights[offset - 2]) { + /* Compute index of the new node, in the next layer. */ + tree_idx = (idx >> (heights[offset - 1] + 1)); + + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_tree_height( + tree_addr, heights[offset - 1] + 1); + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_tree_index( + tree_addr, tree_idx + (idx_offset >> (heights[offset - 1] + 1))); + /* Hash the top-most nodes from the stack together. */ + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_thash_2( + stack + (offset - 2)*SPX_N, stack + (offset - 2)*SPX_N, + pub_seed, tree_addr); + offset--; + /* Note that the top-most node is now one layer higher. */ + heights[offset - 1]++; + + /* If this is a node we need for the auth path.. */ + if (((leaf_idx >> heights[offset - 1]) ^ 0x1) == tree_idx) { + memcpy(auth_path + heights[offset - 1]*SPX_N, + stack + (offset - 1)*SPX_N, SPX_N); + } + } + } + memcpy(root, stack, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_FORS_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_FORS_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_FORS_HEIGHT, gen_leaf, tree_addr); +} + +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_TREE_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_TREE_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_TREE_HEIGHT, gen_leaf, tree_addr); +} diff --git a/crypto_sign/sphincs-shake256-192f-robust/clean/utils.h b/crypto_sign/sphincs-shake256-192f-robust/clean/utils.h new file mode 100644 index 00000000..0b0bcf01 --- /dev/null +++ b/crypto_sign/sphincs-shake256-192f-robust/clean/utils.h @@ -0,0 +1,60 @@ +#ifndef SPX_UTILS_H +#define SPX_UTILS_H + +#include "params.h" +#include +#include + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in); + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen); + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-shake256-192f-robust/clean/wots.c b/crypto_sign/sphincs-shake256-192f-robust/clean/wots.c new file mode 100644 index 00000000..1c1628f6 --- /dev/null +++ b/crypto_sign/sphincs-shake256-192f-robust/clean/wots.c @@ -0,0 +1,161 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +// TODO clarify address expectations, and make them more uniform. +// TODO i.e. do we expect types to be set already? +// TODO and do we expect modifications or copies? + +/** + * Computes the starting value for a chain, i.e. the secret key. + * Expects the address to be complete up to the chain address. + */ +static void wots_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t wots_addr[8]) { + /* Make sure that the hash address is actually zeroed. */ + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_hash_addr(wots_addr, 0); + + /* Generate sk element. */ + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_prf_addr(sk, sk_seed, wots_addr); +} + +/** + * Computes the chaining function. + * out and in have to be n-byte arrays. + * + * Interprets in as start-th value of the chain. + * addr has to contain the address of the chain. + */ +static void gen_chain(unsigned char *out, const unsigned char *in, + unsigned int start, unsigned int steps, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + /* Initialize out with the value at position 'start'. */ + memcpy(out, in, SPX_N); + + /* Iterate 'steps' calls to the hash function. */ + for (i = start; i < (start + steps) && i < SPX_WOTS_W; i++) { + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_hash_addr(addr, i); + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_thash_1( + out, out, pub_seed, addr); + } +} + +/** + * base_w algorithm as described in draft. + * Interprets an array of bytes as integers in base w. + * This only works when log_w is a divisor of 8. + */ +static void base_w(unsigned int *output, const size_t out_len, + const unsigned char *input) { + size_t in = 0; + size_t out = 0; + unsigned char total = 0; + unsigned int bits = 0; + size_t consumed; + + for (consumed = 0; consumed < out_len; consumed++) { + if (bits == 0) { + total = input[in]; + in++; + bits += 8; + } + bits -= SPX_WOTS_LOGW; + output[out] = (unsigned int)((total >> bits) & (SPX_WOTS_W - 1)); + out++; + } +} + +/* Computes the WOTS+ checksum over a message (in base_w). */ +static void wots_checksum(unsigned int *csum_base_w, + const unsigned int *msg_base_w) { + unsigned int csum = 0; + unsigned char csum_bytes[(SPX_WOTS_LEN2 * SPX_WOTS_LOGW + 7) / 8]; + unsigned int i; + + /* Compute checksum. */ + for (i = 0; i < SPX_WOTS_LEN1; i++) { + csum += SPX_WOTS_W - 1 - msg_base_w[i]; + } + + /* Convert checksum to base_w. */ + /* Make sure expected empty zero bits are the least significant bits. */ + csum = csum << (8 - ((SPX_WOTS_LEN2 * SPX_WOTS_LOGW) % 8)); + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_ull_to_bytes( + csum_bytes, sizeof(csum_bytes), csum); + base_w(csum_base_w, SPX_WOTS_LEN2, csum_bytes); +} + +/* Takes a message and derives the matching chain lengths. */ +static void chain_lengths(unsigned int *lengths, const unsigned char *msg) { + base_w(lengths, SPX_WOTS_LEN1, msg); + wots_checksum(lengths + SPX_WOTS_LEN1, lengths); +} + +/** + * WOTS key generation. Takes a 32 byte sk_seed, expands it to WOTS private key + * elements and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(pk + i * SPX_N, sk_seed, addr); + gen_chain(pk + i * SPX_N, pk + i * SPX_N, + 0, SPX_WOTS_W - 1, pub_seed, addr); + } +} + +/** + * Takes a n-byte message and the 32-byte sk_see to compute a signature 'sig'. + */ +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(sig + i * SPX_N, sk_seed, addr); + gen_chain(sig + i * SPX_N, sig + i * SPX_N, 0, lengths[i], pub_seed, addr); + } +} + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_set_chain_addr(addr, i); + gen_chain(pk + i * SPX_N, sig + i * SPX_N, + lengths[i], SPX_WOTS_W - 1 - lengths[i], pub_seed, addr); + } +} diff --git a/crypto_sign/sphincs-shake256-192f-robust/clean/wots.h b/crypto_sign/sphincs-shake256-192f-robust/clean/wots.h new file mode 100644 index 00000000..baf2b14f --- /dev/null +++ b/crypto_sign/sphincs-shake256-192f-robust/clean/wots.h @@ -0,0 +1,38 @@ +#ifndef SPX_WOTS_H +#define SPX_WOTS_H + +#include "params.h" +#include + +/** + * WOTS key generation. Takes a 32 byte seed for the private key, expands it to + * a full WOTS private key and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * Takes a n-byte message and the 32-byte seed for the private key to compute a + * signature that is placed at 'sig'. + */ +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]); + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-shake256-192f-simple/META.yml b/crypto_sign/sphincs-shake256-192f-simple/META.yml new file mode 100644 index 00000000..4e9fe612 --- /dev/null +++ b/crypto_sign/sphincs-shake256-192f-simple/META.yml @@ -0,0 +1,27 @@ +name: SPHINCS+ +type: signature +claimed-nist-level: 3 +length-public-key: 48 +length-secret-key: 96 +length-signature: 35664 +testvectors-sha256: 14f60a3099cfddf30c46491a98a5f3508739df108425b2eaa5c19383f0ca4b22 +principal-submitter: Andreas Hülsing +auxiliary-submitters: + - Jean-Philippe Aumasson + - Daniel J. Bernstein, + - Christoph Dobraunig + - Maria Eichlseder + - Scott Fluhrer + - Stefan-Lukas Gazdag + - Panos Kampanakis + - Stefan Kölbl + - Tanja Lange + - Martin M. Lauridsen + - Florian Mendel + - Ruben Niederhagen + - Christian Rechberger + - Joost Rijneveld + - Peter Schwabe +implementations: + - name: clean + version: https://github.com/sphincs/sphincsplus/commit/77755c94d0bc744478044d6efbb888dc13156441 diff --git a/crypto_sign/sphincs-shake256-192f-simple/clean/LICENSE b/crypto_sign/sphincs-shake256-192f-simple/clean/LICENSE new file mode 100644 index 00000000..670154e3 --- /dev/null +++ b/crypto_sign/sphincs-shake256-192f-simple/clean/LICENSE @@ -0,0 +1,116 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + diff --git a/crypto_sign/sphincs-shake256-192f-simple/clean/Makefile b/crypto_sign/sphincs-shake256-192f-simple/clean/Makefile new file mode 100644 index 00000000..895dd95b --- /dev/null +++ b/crypto_sign/sphincs-shake256-192f-simple/clean/Makefile @@ -0,0 +1,20 @@ +# This Makefile can be used with GNU Make or BSD Make + +LIB=libsphincs-shake256-192f-simple_clean.a + +HEADERS = params.h address.h wots.h utils.h fors.h api.h hash.h thash.h +OBJECTS = address.o wots.o utils.o fors.o sign.o hash_shake256.o thash_shake256_simple.o + +CFLAGS=-O3 -Wall -Wconversion -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) + +all: $(LIB) + +%.o: %.c $(HEADERS) + $(CC) $(CFLAGS) -c -o $@ $< + +$(LIB): $(OBJECTS) + $(AR) -r $@ $(OBJECTS) + +clean: + $(RM) $(OBJECTS) + $(RM) $(LIB) diff --git a/crypto_sign/sphincs-shake256-192f-simple/clean/Makefile.Microsoft_nmake b/crypto_sign/sphincs-shake256-192f-simple/clean/Makefile.Microsoft_nmake new file mode 100644 index 00000000..d1a90648 --- /dev/null +++ b/crypto_sign/sphincs-shake256-192f-simple/clean/Makefile.Microsoft_nmake @@ -0,0 +1,19 @@ +# This Makefile can be used with Microsoft Visual Studio's nmake using the command: +# nmake /f Makefile.Microsoft_nmake + +LIBRARY=libsphincs-shake256-192f-simple_clean.lib +OBJECTS=address.obj wots.obj utils.obj fors.obj sign.obj hash_shake256.obj thash_shake256_simple.obj + +CFLAGS=/nologo /I ..\..\..\common /W4 /WX + +all: $(LIBRARY) + +# Make sure objects are recompiled if headers change. +$(OBJECTS): *.h + +$(LIBRARY): $(OBJECTS) + LIB.EXE /NOLOGO /WX /OUT:$@ $** + +clean: + -DEL $(OBJECTS) + -DEL $(LIBRARY) diff --git a/crypto_sign/sphincs-shake256-192f-simple/clean/address.c b/crypto_sign/sphincs-shake256-192f-simple/clean/address.c new file mode 100644 index 00000000..cce899fe --- /dev/null +++ b/crypto_sign/sphincs-shake256-192f-simple/clean/address.c @@ -0,0 +1,78 @@ +#include + +#include "address.h" +#include "params.h" +#include "utils.h" + +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]) { + int i; + + for (i = 0; i < 8; i++) { + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_ull_to_bytes( + bytes + i * 4, 4, addr[i]); + } +} + +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer) { + addr[0] = layer; +} + +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree) { + addr[1] = 0; + addr[2] = (uint32_t) (tree >> 32); + addr[3] = (uint32_t) tree; +} + +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_type( + uint32_t addr[8], uint32_t type) { + addr[4] = type; +} + +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; +} + +/* These functions are used for OTS addresses. */ + +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair) { + addr[5] = keypair; +} + +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; + out[5] = in[5]; +} + +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain) { + addr[6] = chain; +} + +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash) { + addr[7] = hash; +} + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height) { + addr[6] = tree_height; +} + +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index) { + addr[7] = tree_index; +} diff --git a/crypto_sign/sphincs-shake256-192f-simple/clean/address.h b/crypto_sign/sphincs-shake256-192f-simple/clean/address.h new file mode 100644 index 00000000..39ca3db0 --- /dev/null +++ b/crypto_sign/sphincs-shake256-192f-simple/clean/address.h @@ -0,0 +1,50 @@ +#ifndef SPX_ADDRESS_H +#define SPX_ADDRESS_H + +#include + +#define SPX_ADDR_TYPE_WOTS 0 +#define SPX_ADDR_TYPE_WOTSPK 1 +#define SPX_ADDR_TYPE_HASHTREE 2 +#define SPX_ADDR_TYPE_FORSTREE 3 +#define SPX_ADDR_TYPE_FORSPK 4 + +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer); + +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree); + +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_type( + uint32_t addr[8], uint32_t type); + +/* Copies the layer and tree part of one address into the other */ +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for WOTS and FORS addresses. */ + +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair); + +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain); + +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash); + +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height); + +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index); + +#endif diff --git a/crypto_sign/sphincs-shake256-192f-simple/clean/api.h b/crypto_sign/sphincs-shake256-192f-simple/clean/api.h new file mode 100644 index 00000000..64edd677 --- /dev/null +++ b/crypto_sign/sphincs-shake256-192f-simple/clean/api.h @@ -0,0 +1,78 @@ +#ifndef PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_API_H +#define PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_API_H + +#include +#include + +#define PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_CRYPTO_ALGNAME "SPHINCS+" + +#define PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_CRYPTO_SECRETKEYBYTES 96 +#define PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES 48 +#define PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_CRYPTO_BYTES 35664 +#define PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_CRYPTO_SEEDBYTES 72 + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_crypto_sign_secretkeybytes(void); + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_crypto_sign_publickeybytes(void); + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_crypto_sign_bytes(void); + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_crypto_sign_seedbytes(void); + +/* + * Generates a SPHINCS+ key pair given a seed. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed); + +/* + * Generates a SPHINCS+ key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk); + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk); + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk); + +#endif diff --git a/crypto_sign/sphincs-shake256-192f-simple/clean/fors.c b/crypto_sign/sphincs-shake256-192f-simple/clean/fors.c new file mode 100644 index 00000000..427fa7a0 --- /dev/null +++ b/crypto_sign/sphincs-shake256-192f-simple/clean/fors.c @@ -0,0 +1,164 @@ +#include +#include +#include + +#include "address.h" +#include "fors.h" +#include "hash.h" +#include "thash.h" +#include "utils.h" + +static void fors_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_prf_addr( + sk, sk_seed, fors_leaf_addr); +} + +static void fors_sk_to_leaf(unsigned char *leaf, const unsigned char *sk, + const unsigned char *pub_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_thash_1( + leaf, sk, pub_seed, fors_leaf_addr); +} + +static void fors_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t fors_tree_addr[8]) { + uint32_t fors_leaf_addr[8] = {0}; + + /* Only copy the parts that must be kept in fors_leaf_addr. */ + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_copy_keypair_addr( + fors_leaf_addr, fors_tree_addr); + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_type( + fors_leaf_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_tree_index( + fors_leaf_addr, addr_idx); + + fors_gen_sk(leaf, sk_seed, fors_leaf_addr); + fors_sk_to_leaf(leaf, leaf, pub_seed, fors_leaf_addr); +} + +/** + * Interprets m as SPX_FORS_HEIGHT-bit unsigned integers. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + * Assumes indices has space for SPX_FORS_TREES integers. + */ +static void message_to_indices(uint32_t *indices, const unsigned char *m) { + unsigned int i, j; + unsigned int offset = 0; + + for (i = 0; i < SPX_FORS_TREES; i++) { + indices[i] = 0; + for (j = 0; j < SPX_FORS_HEIGHT; j++) { + indices[i] ^= (((uint32_t)m[offset >> 3] >> (offset & 0x7)) & 0x1) << j; + offset++; + } + } +} + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Include the secret key part that produces the selected leaf node. */ + fors_gen_sk(sig, sk_seed, fors_tree_addr); + sig += SPX_N; + + /* Compute the authentication path for this leaf node. */ + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_treehash_FORS_HEIGHT( + roots + i * SPX_N, sig, sk_seed, pub_seed, + indices[i], idx_offset, fors_gen_leaf, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + unsigned char leaf[SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Derive the leaf from the included secret key part. */ + fors_sk_to_leaf(leaf, sig, pub_seed, fors_tree_addr); + sig += SPX_N; + + /* Derive the corresponding root node of this tree. */ + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_compute_root( + roots + i * SPX_N, leaf, indices[i], idx_offset, sig, + SPX_FORS_HEIGHT, pub_seed, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} diff --git a/crypto_sign/sphincs-shake256-192f-simple/clean/fors.h b/crypto_sign/sphincs-shake256-192f-simple/clean/fors.h new file mode 100644 index 00000000..30ba47b5 --- /dev/null +++ b/crypto_sign/sphincs-shake256-192f-simple/clean/fors.h @@ -0,0 +1,30 @@ +#ifndef SPX_FORS_H +#define SPX_FORS_H + +#include + +#include "params.h" + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]); + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-shake256-192f-simple/clean/hash.h b/crypto_sign/sphincs-shake256-192f-simple/clean/hash.h new file mode 100644 index 00000000..46935457 --- /dev/null +++ b/crypto_sign/sphincs-shake256-192f-simple/clean/hash.h @@ -0,0 +1,22 @@ +#ifndef SPX_HASH_H +#define SPX_HASH_H + +#include + +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed); + +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen); + +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen); + +#endif diff --git a/crypto_sign/sphincs-shake256-192f-simple/clean/hash_shake256.c b/crypto_sign/sphincs-shake256-192f-simple/clean/hash_shake256.c new file mode 100644 index 00000000..d970155e --- /dev/null +++ b/crypto_sign/sphincs-shake256-192f-simple/clean/hash_shake256.c @@ -0,0 +1,87 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "utils.h" + +#include "fips202.h" + +/* For SHAKE256, there is no immediate reason to initialize at the start, + so this function is an empty operation. */ +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed) { + (void)pub_seed; /* Suppress an 'unused parameter' warning. */ + (void)sk_seed; /* Suppress an 'unused parameter' warning. */ +} + +/* + * Computes PRF(key, addr), given a secret key of SPX_N bytes and an address + */ +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]) { + unsigned char buf[SPX_N + SPX_ADDR_BYTES]; + + memcpy(buf, key, SPX_N); + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_addr_to_bytes(buf + SPX_N, addr); + + shake256(out, SPX_N, buf, SPX_N + SPX_ADDR_BYTES); +} + +/** + * Computes the message-dependent randomness R, using a secret seed and an + * optional randomization value as well as the message. + */ +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen) { + uint64_t s_inc[26]; + + shake256_inc_init(s_inc); + shake256_inc_absorb(s_inc, sk_prf, SPX_N); + shake256_inc_absorb(s_inc, optrand, SPX_N); + shake256_inc_absorb(s_inc, m, mlen); + shake256_inc_finalize(s_inc); + shake256_inc_squeeze(R, SPX_N, s_inc); +} + +/** + * Computes the message hash using R, the public key, and the message. + * Outputs the message digest and the index of the leaf. The index is split in + * the tree index and the leaf index, for convenient copying to an address. + */ +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen) { +#define SPX_TREE_BITS (SPX_TREE_HEIGHT * (SPX_D - 1)) +#define SPX_TREE_BYTES ((SPX_TREE_BITS + 7) / 8) +#define SPX_LEAF_BITS SPX_TREE_HEIGHT +#define SPX_LEAF_BYTES ((SPX_LEAF_BITS + 7) / 8) +#define SPX_DGST_BYTES (SPX_FORS_MSG_BYTES + SPX_TREE_BYTES + SPX_LEAF_BYTES) + + unsigned char buf[SPX_DGST_BYTES]; + unsigned char *bufp = buf; + uint64_t s_inc[26]; + + shake256_inc_init(s_inc); + shake256_inc_absorb(s_inc, R, SPX_N); + shake256_inc_absorb(s_inc, pk, SPX_PK_BYTES); + shake256_inc_absorb(s_inc, m, mlen); + shake256_inc_finalize(s_inc); + shake256_inc_squeeze(buf, SPX_DGST_BYTES, s_inc); + + memcpy(digest, bufp, SPX_FORS_MSG_BYTES); + bufp += SPX_FORS_MSG_BYTES; + + *tree = PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_bytes_to_ull( + bufp, SPX_TREE_BYTES); + *tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS); + bufp += SPX_TREE_BYTES; + + *leaf_idx = (uint32_t)PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_bytes_to_ull( + bufp, SPX_LEAF_BYTES); + *leaf_idx &= (~(uint32_t)0) >> (32 - SPX_LEAF_BITS); +} diff --git a/crypto_sign/sphincs-shake256-192f-simple/clean/params.h b/crypto_sign/sphincs-shake256-192f-simple/clean/params.h new file mode 100644 index 00000000..898f177f --- /dev/null +++ b/crypto_sign/sphincs-shake256-192f-simple/clean/params.h @@ -0,0 +1,53 @@ +#ifndef SPX_PARAMS_H +#define SPX_PARAMS_H + +/* Hash output length in bytes. */ +#define SPX_N 24 +/* Height of the hypertree. */ +#define SPX_FULL_HEIGHT 66 +/* Number of subtree layer. */ +#define SPX_D 22 +/* FORS tree dimensions. */ +#define SPX_FORS_HEIGHT 8 +#define SPX_FORS_TREES 33 +/* Winternitz parameter, */ +#define SPX_WOTS_W 16 + +/* The hash function is defined by linking a different hash.c file, as opposed + to setting a #define constant. */ + +/* For clarity */ +#define SPX_ADDR_BYTES 32 + +/* WOTS parameters. */ +#define SPX_WOTS_LOGW 4 + +#define SPX_WOTS_LEN1 (8 * SPX_N / SPX_WOTS_LOGW) + +/* SPX_WOTS_LEN2 is floor(log(len_1 * (w - 1)) / log(w)) + 1; we precompute */ +#define SPX_WOTS_LEN2 3 + +#define SPX_WOTS_LEN (SPX_WOTS_LEN1 + SPX_WOTS_LEN2) +#define SPX_WOTS_BYTES (SPX_WOTS_LEN * SPX_N) +#define SPX_WOTS_PK_BYTES SPX_WOTS_BYTES + +/* Subtree size. */ +#define SPX_TREE_HEIGHT (SPX_FULL_HEIGHT / SPX_D) + +/* FORS parameters. */ +#define SPX_FORS_MSG_BYTES ((SPX_FORS_HEIGHT * SPX_FORS_TREES + 7) / 8) +#define SPX_FORS_BYTES ((SPX_FORS_HEIGHT + 1) * SPX_FORS_TREES * SPX_N) +#define SPX_FORS_PK_BYTES SPX_N + +/* Resulting SPX sizes. */ +#define SPX_BYTES (SPX_N + SPX_FORS_BYTES + SPX_D * SPX_WOTS_BYTES +\ + SPX_FULL_HEIGHT * SPX_N) +#define SPX_PK_BYTES (2 * SPX_N) +#define SPX_SK_BYTES (2 * SPX_N + SPX_PK_BYTES) + +/* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ +#define SPX_OPTRAND_BYTES 32 + +#endif diff --git a/crypto_sign/sphincs-shake256-192f-simple/clean/sign.c b/crypto_sign/sphincs-shake256-192f-simple/clean/sign.c new file mode 100644 index 00000000..5e9c8cbb --- /dev/null +++ b/crypto_sign/sphincs-shake256-192f-simple/clean/sign.c @@ -0,0 +1,344 @@ +#include +#include +#include + +#include "address.h" +#include "api.h" +#include "fors.h" +#include "hash.h" +#include "params.h" +#include "randombytes.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +/** + * Computes the leaf at a given address. First generates the WOTS key pair, + * then computes leaf by hashing horizontally. + */ +static void wots_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t tree_addr[8]) { + unsigned char pk[SPX_WOTS_BYTES]; + uint32_t wots_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_keypair_addr( + wots_addr, addr_idx); + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_wots_gen_pk( + pk, sk_seed, pub_seed, wots_addr); + + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_thash_WOTS_LEN( + leaf, pk, pub_seed, wots_pk_addr); +} + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_crypto_sign_secretkeybytes(void) { + return PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_CRYPTO_SECRETKEYBYTES; +} + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_crypto_sign_publickeybytes(void) { + return PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES; +} + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_crypto_sign_bytes(void) { + return PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_CRYPTO_BYTES; +} + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_crypto_sign_seedbytes(void) { + return PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_CRYPTO_SEEDBYTES; +} + +/* + * Generates an SPX key pair given a seed of length + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed) { + /* We do not need the auth path in key generation, but it simplifies the + code to have just one treehash routine that computes both root and path + in one function. */ + unsigned char auth_path[SPX_TREE_HEIGHT * SPX_N]; + uint32_t top_tree_addr[8] = {0}; + + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_layer_addr( + top_tree_addr, SPX_D - 1); + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_type( + top_tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */ + memcpy(sk, seed, PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_CRYPTO_SEEDBYTES); + + memcpy(pk, sk + 2 * SPX_N, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_initialize_hash_function(pk, sk); + + /* Compute root node of the top-most subtree. */ + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_treehash_TREE_HEIGHT( + sk + 3 * SPX_N, auth_path, sk, sk + 2 * SPX_N, 0, 0, + wots_gen_leaf, top_tree_addr); + + memcpy(pk + SPX_N, sk + 3 * SPX_N, SPX_N); + + return 0; +} + +/* + * Generates an SPX key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk) { + unsigned char seed[PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_CRYPTO_SEEDBYTES]; + randombytes(seed, PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_CRYPTO_SEEDBYTES); + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_crypto_sign_seed_keypair( + pk, sk, seed); + + return 0; +} + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + const unsigned char *sk_seed = sk; + const unsigned char *sk_prf = sk + SPX_N; + const unsigned char *pk = sk + 2 * SPX_N; + const unsigned char *pub_seed = pk; + + unsigned char optrand[SPX_N]; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char root[SPX_N]; + uint32_t i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_initialize_hash_function( + pub_seed, sk_seed); + + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ + randombytes(optrand, SPX_N); + /* Compute the digest randomization value. */ + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_gen_message_random( + sig, sk_prf, optrand, m, mlen); + + /* Derive the message digest and leaf index from R, PK and M. */ + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Sign the message hash using FORS. */ + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_fors_sign( + sig, root, mhash, sk_seed, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Compute a WOTS signature. */ + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_wots_sign( + sig, root, sk_seed, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the authentication path for the used WOTS leaf. */ + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_treehash_TREE_HEIGHT( + root, sig, sk_seed, pub_seed, idx_leaf, 0, + wots_gen_leaf, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + *siglen = SPX_BYTES; + + return 0; +} + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk) { + const unsigned char *pub_seed = pk; + const unsigned char *pub_root = pk + SPX_N; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char wots_pk[SPX_WOTS_BYTES]; + unsigned char root[SPX_N]; + unsigned char leaf[SPX_N]; + unsigned int i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + if (siglen != SPX_BYTES) { + return -1; + } + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_initialize_hash_function( + pub_seed, NULL); + + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + /* Derive the message digest and leaf index from R || PK || M. */ + /* The additional SPX_N is a result of the hash domain separator. */ + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + /* Layer correctly defaults to 0, so no need to set_layer_addr */ + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_fors_pk_from_sig( + root, sig, mhash, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + /* For each subtree.. */ + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + + /* The WOTS public key is only correct if the signature was correct. */ + /* Initially, root is the FORS pk, but on subsequent iterations it is + the root of the subtree below the currently processed subtree. */ + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_wots_pk_from_sig( + wots_pk, sig, root, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the leaf node using the WOTS public key. */ + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_thash_WOTS_LEN( + leaf, wots_pk, pub_seed, wots_pk_addr); + + /* Compute the root node of this subtree. */ + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_compute_root( + root, leaf, idx_leaf, 0, sig, SPX_TREE_HEIGHT, + pub_seed, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + /* Check if the root node equals the root node in the public key. */ + if (memcmp(root, pub_root, SPX_N) != 0) { + return -1; + } + + return 0; +} + + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + size_t siglen; + + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_crypto_sign_signature( + sm, &siglen, m, mlen, sk); + + memmove(sm + SPX_BYTES, m, mlen); + *smlen = siglen + mlen; + + return 0; +} + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk) { + /* The API caller does not necessarily know what size a signature should be + but SPHINCS+ signatures are always exactly SPX_BYTES. */ + if (smlen < SPX_BYTES) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + *mlen = smlen - SPX_BYTES; + + if (PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_crypto_sign_verify( + sm, SPX_BYTES, sm + SPX_BYTES, *mlen, pk)) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + /* If verification was successful, move the message to the right place. */ + memmove(m, sm + SPX_BYTES, *mlen); + + return 0; +} diff --git a/crypto_sign/sphincs-shake256-192f-simple/clean/thash.h b/crypto_sign/sphincs-shake256-192f-simple/clean/thash.h new file mode 100644 index 00000000..89e98190 --- /dev/null +++ b/crypto_sign/sphincs-shake256-192f-simple/clean/thash.h @@ -0,0 +1,22 @@ +#ifndef SPX_THASH_H +#define SPX_THASH_H + +#include + +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-shake256-192f-simple/clean/thash_shake256_simple.c b/crypto_sign/sphincs-shake256-192f-simple/clean/thash_shake256_simple.c new file mode 100644 index 00000000..50c47fbf --- /dev/null +++ b/crypto_sign/sphincs-shake256-192f-simple/clean/thash_shake256_simple.c @@ -0,0 +1,61 @@ +#include +#include + +#include "address.h" +#include "params.h" +#include "thash.h" + +#include "fips202.h" + +/** + * Takes an array of inblocks concatenated arrays of SPX_N bytes. + */ +static void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_thash( + unsigned char *out, unsigned char *buf, + const unsigned char *in, unsigned int inblocks, + const unsigned char *pub_seed, uint32_t addr[8]) { + + memcpy(buf, pub_seed, SPX_N); + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_addr_to_bytes(buf + SPX_N, addr); + memcpy(buf + SPX_N + SPX_ADDR_BYTES, in, inblocks * SPX_N); + + shake256(out, SPX_N, buf, SPX_N + SPX_ADDR_BYTES + inblocks * SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_ADDR_BYTES + 1 * SPX_N]; + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_thash( + out, buf, in, 1, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_ADDR_BYTES + 2 * SPX_N]; + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_thash( + out, buf, in, 2, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_ADDR_BYTES + SPX_WOTS_LEN * SPX_N]; + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_thash( + out, buf, in, SPX_WOTS_LEN, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_ADDR_BYTES + SPX_FORS_TREES * SPX_N]; + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_thash( + out, buf, in, SPX_FORS_TREES, pub_seed, addr); +} diff --git a/crypto_sign/sphincs-shake256-192f-simple/clean/utils.c b/crypto_sign/sphincs-shake256-192f-simple/clean/utils.c new file mode 100644 index 00000000..0ed7ddc0 --- /dev/null +++ b/crypto_sign/sphincs-shake256-192f-simple/clean/utils.c @@ -0,0 +1,192 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in) { + + /* Iterate over out in decreasing order, for big-endianness. */ + for (size_t i = outlen; i > 0; i--) { + out[i - 1] = in & 0xff; + in = in >> 8; + } +} + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen) { + unsigned long long retval = 0; + + for (size_t i = 0; i < inlen; i++) { + retval |= ((unsigned long long)in[i]) << (8 * (inlen - 1 - i)); + } + return retval; +} + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + unsigned char buffer[2 * SPX_N]; + + /* If leaf_idx is odd (last bit = 1), current path element is a right child + and auth_path has to go left. Otherwise it is the other way around. */ + if (leaf_idx & 1) { + memcpy(buffer + SPX_N, leaf, SPX_N); + memcpy(buffer, auth_path, SPX_N); + } else { + memcpy(buffer, leaf, SPX_N); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + + for (i = 0; i < tree_height - 1; i++) { + leaf_idx >>= 1; + idx_offset >>= 1; + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_tree_height(addr, i + 1); + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + + /* Pick the right or left neighbor, depending on parity of the node. */ + if (leaf_idx & 1) { + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_thash_2( + buffer + SPX_N, buffer, pub_seed, addr); + memcpy(buffer, auth_path, SPX_N); + } else { + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_thash_2( + buffer, buffer, pub_seed, addr); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + } + + /* The last iteration is exceptional; we do not copy an auth_path node. */ + leaf_idx >>= 1; + idx_offset >>= 1; + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_tree_height(addr, tree_height); + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_thash_2( + root, buffer, pub_seed, addr); +} + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +static void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_treehash( + unsigned char *root, unsigned char *auth_path, + unsigned char *stack, unsigned int *heights, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned int offset = 0; + uint32_t idx; + uint32_t tree_idx; + + for (idx = 0; idx < (uint32_t)(1 << tree_height); idx++) { + /* Add the next leaf node to the stack. */ + gen_leaf(stack + offset * SPX_N, + sk_seed, pub_seed, idx + idx_offset, tree_addr); + offset++; + heights[offset - 1] = 0; + + /* If this is a node we need for the auth path.. */ + if ((leaf_idx ^ 0x1) == idx) { + memcpy(auth_path, stack + (offset - 1)*SPX_N, SPX_N); + } + + /* While the top-most nodes are of equal height.. */ + while (offset >= 2 && heights[offset - 1] == heights[offset - 2]) { + /* Compute index of the new node, in the next layer. */ + tree_idx = (idx >> (heights[offset - 1] + 1)); + + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_tree_height( + tree_addr, heights[offset - 1] + 1); + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_tree_index( + tree_addr, tree_idx + (idx_offset >> (heights[offset - 1] + 1))); + /* Hash the top-most nodes from the stack together. */ + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_thash_2( + stack + (offset - 2)*SPX_N, stack + (offset - 2)*SPX_N, + pub_seed, tree_addr); + offset--; + /* Note that the top-most node is now one layer higher. */ + heights[offset - 1]++; + + /* If this is a node we need for the auth path.. */ + if (((leaf_idx >> heights[offset - 1]) ^ 0x1) == tree_idx) { + memcpy(auth_path + heights[offset - 1]*SPX_N, + stack + (offset - 1)*SPX_N, SPX_N); + } + } + } + memcpy(root, stack, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_FORS_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_FORS_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_FORS_HEIGHT, gen_leaf, tree_addr); +} + +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_TREE_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_TREE_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_TREE_HEIGHT, gen_leaf, tree_addr); +} diff --git a/crypto_sign/sphincs-shake256-192f-simple/clean/utils.h b/crypto_sign/sphincs-shake256-192f-simple/clean/utils.h new file mode 100644 index 00000000..33cc7943 --- /dev/null +++ b/crypto_sign/sphincs-shake256-192f-simple/clean/utils.h @@ -0,0 +1,60 @@ +#ifndef SPX_UTILS_H +#define SPX_UTILS_H + +#include "params.h" +#include +#include + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in); + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen); + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-shake256-192f-simple/clean/wots.c b/crypto_sign/sphincs-shake256-192f-simple/clean/wots.c new file mode 100644 index 00000000..b95e55ea --- /dev/null +++ b/crypto_sign/sphincs-shake256-192f-simple/clean/wots.c @@ -0,0 +1,161 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +// TODO clarify address expectations, and make them more uniform. +// TODO i.e. do we expect types to be set already? +// TODO and do we expect modifications or copies? + +/** + * Computes the starting value for a chain, i.e. the secret key. + * Expects the address to be complete up to the chain address. + */ +static void wots_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t wots_addr[8]) { + /* Make sure that the hash address is actually zeroed. */ + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_hash_addr(wots_addr, 0); + + /* Generate sk element. */ + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_prf_addr(sk, sk_seed, wots_addr); +} + +/** + * Computes the chaining function. + * out and in have to be n-byte arrays. + * + * Interprets in as start-th value of the chain. + * addr has to contain the address of the chain. + */ +static void gen_chain(unsigned char *out, const unsigned char *in, + unsigned int start, unsigned int steps, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + /* Initialize out with the value at position 'start'. */ + memcpy(out, in, SPX_N); + + /* Iterate 'steps' calls to the hash function. */ + for (i = start; i < (start + steps) && i < SPX_WOTS_W; i++) { + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_hash_addr(addr, i); + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_thash_1( + out, out, pub_seed, addr); + } +} + +/** + * base_w algorithm as described in draft. + * Interprets an array of bytes as integers in base w. + * This only works when log_w is a divisor of 8. + */ +static void base_w(unsigned int *output, const size_t out_len, + const unsigned char *input) { + size_t in = 0; + size_t out = 0; + unsigned char total = 0; + unsigned int bits = 0; + size_t consumed; + + for (consumed = 0; consumed < out_len; consumed++) { + if (bits == 0) { + total = input[in]; + in++; + bits += 8; + } + bits -= SPX_WOTS_LOGW; + output[out] = (unsigned int)((total >> bits) & (SPX_WOTS_W - 1)); + out++; + } +} + +/* Computes the WOTS+ checksum over a message (in base_w). */ +static void wots_checksum(unsigned int *csum_base_w, + const unsigned int *msg_base_w) { + unsigned int csum = 0; + unsigned char csum_bytes[(SPX_WOTS_LEN2 * SPX_WOTS_LOGW + 7) / 8]; + unsigned int i; + + /* Compute checksum. */ + for (i = 0; i < SPX_WOTS_LEN1; i++) { + csum += SPX_WOTS_W - 1 - msg_base_w[i]; + } + + /* Convert checksum to base_w. */ + /* Make sure expected empty zero bits are the least significant bits. */ + csum = csum << (8 - ((SPX_WOTS_LEN2 * SPX_WOTS_LOGW) % 8)); + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_ull_to_bytes( + csum_bytes, sizeof(csum_bytes), csum); + base_w(csum_base_w, SPX_WOTS_LEN2, csum_bytes); +} + +/* Takes a message and derives the matching chain lengths. */ +static void chain_lengths(unsigned int *lengths, const unsigned char *msg) { + base_w(lengths, SPX_WOTS_LEN1, msg); + wots_checksum(lengths + SPX_WOTS_LEN1, lengths); +} + +/** + * WOTS key generation. Takes a 32 byte sk_seed, expands it to WOTS private key + * elements and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(pk + i * SPX_N, sk_seed, addr); + gen_chain(pk + i * SPX_N, pk + i * SPX_N, + 0, SPX_WOTS_W - 1, pub_seed, addr); + } +} + +/** + * Takes a n-byte message and the 32-byte sk_see to compute a signature 'sig'. + */ +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(sig + i * SPX_N, sk_seed, addr); + gen_chain(sig + i * SPX_N, sig + i * SPX_N, 0, lengths[i], pub_seed, addr); + } +} + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_set_chain_addr(addr, i); + gen_chain(pk + i * SPX_N, sig + i * SPX_N, + lengths[i], SPX_WOTS_W - 1 - lengths[i], pub_seed, addr); + } +} diff --git a/crypto_sign/sphincs-shake256-192f-simple/clean/wots.h b/crypto_sign/sphincs-shake256-192f-simple/clean/wots.h new file mode 100644 index 00000000..ba46fd7a --- /dev/null +++ b/crypto_sign/sphincs-shake256-192f-simple/clean/wots.h @@ -0,0 +1,38 @@ +#ifndef SPX_WOTS_H +#define SPX_WOTS_H + +#include "params.h" +#include + +/** + * WOTS key generation. Takes a 32 byte seed for the private key, expands it to + * a full WOTS private key and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * Takes a n-byte message and the 32-byte seed for the private key to compute a + * signature that is placed at 'sig'. + */ +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]); + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-shake256-192s-robust/META.yml b/crypto_sign/sphincs-shake256-192s-robust/META.yml new file mode 100644 index 00000000..ccc5bc5c --- /dev/null +++ b/crypto_sign/sphincs-shake256-192s-robust/META.yml @@ -0,0 +1,27 @@ +name: SPHINCS+ +type: signature +claimed-nist-level: 3 +length-public-key: 48 +length-secret-key: 96 +length-signature: 17064 +testvectors-sha256: 4f80c9cf98c017293c7543f96170f18655e6ef65675300aa302de42562b21f5a +principal-submitter: Andreas Hülsing +auxiliary-submitters: + - Jean-Philippe Aumasson + - Daniel J. Bernstein, + - Christoph Dobraunig + - Maria Eichlseder + - Scott Fluhrer + - Stefan-Lukas Gazdag + - Panos Kampanakis + - Stefan Kölbl + - Tanja Lange + - Martin M. Lauridsen + - Florian Mendel + - Ruben Niederhagen + - Christian Rechberger + - Joost Rijneveld + - Peter Schwabe +implementations: + - name: clean + version: https://github.com/sphincs/sphincsplus/commit/77755c94d0bc744478044d6efbb888dc13156441 diff --git a/crypto_sign/sphincs-shake256-192s-robust/clean/LICENSE b/crypto_sign/sphincs-shake256-192s-robust/clean/LICENSE new file mode 100644 index 00000000..670154e3 --- /dev/null +++ b/crypto_sign/sphincs-shake256-192s-robust/clean/LICENSE @@ -0,0 +1,116 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + diff --git a/crypto_sign/sphincs-shake256-192s-robust/clean/Makefile b/crypto_sign/sphincs-shake256-192s-robust/clean/Makefile new file mode 100644 index 00000000..3a777491 --- /dev/null +++ b/crypto_sign/sphincs-shake256-192s-robust/clean/Makefile @@ -0,0 +1,20 @@ +# This Makefile can be used with GNU Make or BSD Make + +LIB=libsphincs-shake256-192s-robust_clean.a + +HEADERS = params.h address.h wots.h utils.h fors.h api.h hash.h thash.h +OBJECTS = address.o wots.o utils.o fors.o sign.o hash_shake256.o thash_shake256_robust.o + +CFLAGS=-O3 -Wall -Wconversion -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) + +all: $(LIB) + +%.o: %.c $(HEADERS) + $(CC) $(CFLAGS) -c -o $@ $< + +$(LIB): $(OBJECTS) + $(AR) -r $@ $(OBJECTS) + +clean: + $(RM) $(OBJECTS) + $(RM) $(LIB) diff --git a/crypto_sign/sphincs-shake256-192s-robust/clean/Makefile.Microsoft_nmake b/crypto_sign/sphincs-shake256-192s-robust/clean/Makefile.Microsoft_nmake new file mode 100644 index 00000000..86bca31d --- /dev/null +++ b/crypto_sign/sphincs-shake256-192s-robust/clean/Makefile.Microsoft_nmake @@ -0,0 +1,19 @@ +# This Makefile can be used with Microsoft Visual Studio's nmake using the command: +# nmake /f Makefile.Microsoft_nmake + +LIBRARY=libsphincs-shake256-192s-robust_clean.lib +OBJECTS=address.obj wots.obj utils.obj fors.obj sign.obj hash_shake256.obj thash_shake256_robust.obj + +CFLAGS=/nologo /I ..\..\..\common /W4 /WX + +all: $(LIBRARY) + +# Make sure objects are recompiled if headers change. +$(OBJECTS): *.h + +$(LIBRARY): $(OBJECTS) + LIB.EXE /NOLOGO /WX /OUT:$@ $** + +clean: + -DEL $(OBJECTS) + -DEL $(LIBRARY) diff --git a/crypto_sign/sphincs-shake256-192s-robust/clean/address.c b/crypto_sign/sphincs-shake256-192s-robust/clean/address.c new file mode 100644 index 00000000..1d6afdd8 --- /dev/null +++ b/crypto_sign/sphincs-shake256-192s-robust/clean/address.c @@ -0,0 +1,78 @@ +#include + +#include "address.h" +#include "params.h" +#include "utils.h" + +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]) { + int i; + + for (i = 0; i < 8; i++) { + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_ull_to_bytes( + bytes + i * 4, 4, addr[i]); + } +} + +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer) { + addr[0] = layer; +} + +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree) { + addr[1] = 0; + addr[2] = (uint32_t) (tree >> 32); + addr[3] = (uint32_t) tree; +} + +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_type( + uint32_t addr[8], uint32_t type) { + addr[4] = type; +} + +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; +} + +/* These functions are used for OTS addresses. */ + +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair) { + addr[5] = keypair; +} + +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; + out[5] = in[5]; +} + +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain) { + addr[6] = chain; +} + +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash) { + addr[7] = hash; +} + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height) { + addr[6] = tree_height; +} + +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index) { + addr[7] = tree_index; +} diff --git a/crypto_sign/sphincs-shake256-192s-robust/clean/address.h b/crypto_sign/sphincs-shake256-192s-robust/clean/address.h new file mode 100644 index 00000000..919f4b20 --- /dev/null +++ b/crypto_sign/sphincs-shake256-192s-robust/clean/address.h @@ -0,0 +1,50 @@ +#ifndef SPX_ADDRESS_H +#define SPX_ADDRESS_H + +#include + +#define SPX_ADDR_TYPE_WOTS 0 +#define SPX_ADDR_TYPE_WOTSPK 1 +#define SPX_ADDR_TYPE_HASHTREE 2 +#define SPX_ADDR_TYPE_FORSTREE 3 +#define SPX_ADDR_TYPE_FORSPK 4 + +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer); + +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree); + +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_type( + uint32_t addr[8], uint32_t type); + +/* Copies the layer and tree part of one address into the other */ +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for WOTS and FORS addresses. */ + +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair); + +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain); + +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash); + +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height); + +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index); + +#endif diff --git a/crypto_sign/sphincs-shake256-192s-robust/clean/api.h b/crypto_sign/sphincs-shake256-192s-robust/clean/api.h new file mode 100644 index 00000000..10992c2a --- /dev/null +++ b/crypto_sign/sphincs-shake256-192s-robust/clean/api.h @@ -0,0 +1,78 @@ +#ifndef PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_API_H +#define PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_API_H + +#include +#include + +#define PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_CRYPTO_ALGNAME "SPHINCS+" + +#define PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_CRYPTO_SECRETKEYBYTES 96 +#define PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES 48 +#define PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_CRYPTO_BYTES 17064 +#define PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_CRYPTO_SEEDBYTES 72 + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_crypto_sign_secretkeybytes(void); + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_crypto_sign_publickeybytes(void); + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_crypto_sign_bytes(void); + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_crypto_sign_seedbytes(void); + +/* + * Generates a SPHINCS+ key pair given a seed. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed); + +/* + * Generates a SPHINCS+ key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk); + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk); + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk); + +#endif diff --git a/crypto_sign/sphincs-shake256-192s-robust/clean/fors.c b/crypto_sign/sphincs-shake256-192s-robust/clean/fors.c new file mode 100644 index 00000000..9170171c --- /dev/null +++ b/crypto_sign/sphincs-shake256-192s-robust/clean/fors.c @@ -0,0 +1,164 @@ +#include +#include +#include + +#include "address.h" +#include "fors.h" +#include "hash.h" +#include "thash.h" +#include "utils.h" + +static void fors_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_prf_addr( + sk, sk_seed, fors_leaf_addr); +} + +static void fors_sk_to_leaf(unsigned char *leaf, const unsigned char *sk, + const unsigned char *pub_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_thash_1( + leaf, sk, pub_seed, fors_leaf_addr); +} + +static void fors_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t fors_tree_addr[8]) { + uint32_t fors_leaf_addr[8] = {0}; + + /* Only copy the parts that must be kept in fors_leaf_addr. */ + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_copy_keypair_addr( + fors_leaf_addr, fors_tree_addr); + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_type( + fors_leaf_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_tree_index( + fors_leaf_addr, addr_idx); + + fors_gen_sk(leaf, sk_seed, fors_leaf_addr); + fors_sk_to_leaf(leaf, leaf, pub_seed, fors_leaf_addr); +} + +/** + * Interprets m as SPX_FORS_HEIGHT-bit unsigned integers. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + * Assumes indices has space for SPX_FORS_TREES integers. + */ +static void message_to_indices(uint32_t *indices, const unsigned char *m) { + unsigned int i, j; + unsigned int offset = 0; + + for (i = 0; i < SPX_FORS_TREES; i++) { + indices[i] = 0; + for (j = 0; j < SPX_FORS_HEIGHT; j++) { + indices[i] ^= (((uint32_t)m[offset >> 3] >> (offset & 0x7)) & 0x1) << j; + offset++; + } + } +} + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Include the secret key part that produces the selected leaf node. */ + fors_gen_sk(sig, sk_seed, fors_tree_addr); + sig += SPX_N; + + /* Compute the authentication path for this leaf node. */ + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_treehash_FORS_HEIGHT( + roots + i * SPX_N, sig, sk_seed, pub_seed, + indices[i], idx_offset, fors_gen_leaf, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + unsigned char leaf[SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Derive the leaf from the included secret key part. */ + fors_sk_to_leaf(leaf, sig, pub_seed, fors_tree_addr); + sig += SPX_N; + + /* Derive the corresponding root node of this tree. */ + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_compute_root( + roots + i * SPX_N, leaf, indices[i], idx_offset, sig, + SPX_FORS_HEIGHT, pub_seed, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} diff --git a/crypto_sign/sphincs-shake256-192s-robust/clean/fors.h b/crypto_sign/sphincs-shake256-192s-robust/clean/fors.h new file mode 100644 index 00000000..751ab52a --- /dev/null +++ b/crypto_sign/sphincs-shake256-192s-robust/clean/fors.h @@ -0,0 +1,30 @@ +#ifndef SPX_FORS_H +#define SPX_FORS_H + +#include + +#include "params.h" + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]); + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-shake256-192s-robust/clean/hash.h b/crypto_sign/sphincs-shake256-192s-robust/clean/hash.h new file mode 100644 index 00000000..23763952 --- /dev/null +++ b/crypto_sign/sphincs-shake256-192s-robust/clean/hash.h @@ -0,0 +1,22 @@ +#ifndef SPX_HASH_H +#define SPX_HASH_H + +#include + +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed); + +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen); + +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen); + +#endif diff --git a/crypto_sign/sphincs-shake256-192s-robust/clean/hash_shake256.c b/crypto_sign/sphincs-shake256-192s-robust/clean/hash_shake256.c new file mode 100644 index 00000000..6b4e0656 --- /dev/null +++ b/crypto_sign/sphincs-shake256-192s-robust/clean/hash_shake256.c @@ -0,0 +1,87 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "utils.h" + +#include "fips202.h" + +/* For SHAKE256, there is no immediate reason to initialize at the start, + so this function is an empty operation. */ +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed) { + (void)pub_seed; /* Suppress an 'unused parameter' warning. */ + (void)sk_seed; /* Suppress an 'unused parameter' warning. */ +} + +/* + * Computes PRF(key, addr), given a secret key of SPX_N bytes and an address + */ +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]) { + unsigned char buf[SPX_N + SPX_ADDR_BYTES]; + + memcpy(buf, key, SPX_N); + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_addr_to_bytes(buf + SPX_N, addr); + + shake256(out, SPX_N, buf, SPX_N + SPX_ADDR_BYTES); +} + +/** + * Computes the message-dependent randomness R, using a secret seed and an + * optional randomization value as well as the message. + */ +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen) { + uint64_t s_inc[26]; + + shake256_inc_init(s_inc); + shake256_inc_absorb(s_inc, sk_prf, SPX_N); + shake256_inc_absorb(s_inc, optrand, SPX_N); + shake256_inc_absorb(s_inc, m, mlen); + shake256_inc_finalize(s_inc); + shake256_inc_squeeze(R, SPX_N, s_inc); +} + +/** + * Computes the message hash using R, the public key, and the message. + * Outputs the message digest and the index of the leaf. The index is split in + * the tree index and the leaf index, for convenient copying to an address. + */ +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen) { +#define SPX_TREE_BITS (SPX_TREE_HEIGHT * (SPX_D - 1)) +#define SPX_TREE_BYTES ((SPX_TREE_BITS + 7) / 8) +#define SPX_LEAF_BITS SPX_TREE_HEIGHT +#define SPX_LEAF_BYTES ((SPX_LEAF_BITS + 7) / 8) +#define SPX_DGST_BYTES (SPX_FORS_MSG_BYTES + SPX_TREE_BYTES + SPX_LEAF_BYTES) + + unsigned char buf[SPX_DGST_BYTES]; + unsigned char *bufp = buf; + uint64_t s_inc[26]; + + shake256_inc_init(s_inc); + shake256_inc_absorb(s_inc, R, SPX_N); + shake256_inc_absorb(s_inc, pk, SPX_PK_BYTES); + shake256_inc_absorb(s_inc, m, mlen); + shake256_inc_finalize(s_inc); + shake256_inc_squeeze(buf, SPX_DGST_BYTES, s_inc); + + memcpy(digest, bufp, SPX_FORS_MSG_BYTES); + bufp += SPX_FORS_MSG_BYTES; + + *tree = PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_bytes_to_ull( + bufp, SPX_TREE_BYTES); + *tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS); + bufp += SPX_TREE_BYTES; + + *leaf_idx = (uint32_t)PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_bytes_to_ull( + bufp, SPX_LEAF_BYTES); + *leaf_idx &= (~(uint32_t)0) >> (32 - SPX_LEAF_BITS); +} diff --git a/crypto_sign/sphincs-shake256-192s-robust/clean/params.h b/crypto_sign/sphincs-shake256-192s-robust/clean/params.h new file mode 100644 index 00000000..483b5374 --- /dev/null +++ b/crypto_sign/sphincs-shake256-192s-robust/clean/params.h @@ -0,0 +1,53 @@ +#ifndef SPX_PARAMS_H +#define SPX_PARAMS_H + +/* Hash output length in bytes. */ +#define SPX_N 24 +/* Height of the hypertree. */ +#define SPX_FULL_HEIGHT 64 +/* Number of subtree layer. */ +#define SPX_D 8 +/* FORS tree dimensions. */ +#define SPX_FORS_HEIGHT 16 +#define SPX_FORS_TREES 14 +/* Winternitz parameter, */ +#define SPX_WOTS_W 16 + +/* The hash function is defined by linking a different hash.c file, as opposed + to setting a #define constant. */ + +/* For clarity */ +#define SPX_ADDR_BYTES 32 + +/* WOTS parameters. */ +#define SPX_WOTS_LOGW 4 + +#define SPX_WOTS_LEN1 (8 * SPX_N / SPX_WOTS_LOGW) + +/* SPX_WOTS_LEN2 is floor(log(len_1 * (w - 1)) / log(w)) + 1; we precompute */ +#define SPX_WOTS_LEN2 3 + +#define SPX_WOTS_LEN (SPX_WOTS_LEN1 + SPX_WOTS_LEN2) +#define SPX_WOTS_BYTES (SPX_WOTS_LEN * SPX_N) +#define SPX_WOTS_PK_BYTES SPX_WOTS_BYTES + +/* Subtree size. */ +#define SPX_TREE_HEIGHT (SPX_FULL_HEIGHT / SPX_D) + +/* FORS parameters. */ +#define SPX_FORS_MSG_BYTES ((SPX_FORS_HEIGHT * SPX_FORS_TREES + 7) / 8) +#define SPX_FORS_BYTES ((SPX_FORS_HEIGHT + 1) * SPX_FORS_TREES * SPX_N) +#define SPX_FORS_PK_BYTES SPX_N + +/* Resulting SPX sizes. */ +#define SPX_BYTES (SPX_N + SPX_FORS_BYTES + SPX_D * SPX_WOTS_BYTES +\ + SPX_FULL_HEIGHT * SPX_N) +#define SPX_PK_BYTES (2 * SPX_N) +#define SPX_SK_BYTES (2 * SPX_N + SPX_PK_BYTES) + +/* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ +#define SPX_OPTRAND_BYTES 32 + +#endif diff --git a/crypto_sign/sphincs-shake256-192s-robust/clean/sign.c b/crypto_sign/sphincs-shake256-192s-robust/clean/sign.c new file mode 100644 index 00000000..2244da28 --- /dev/null +++ b/crypto_sign/sphincs-shake256-192s-robust/clean/sign.c @@ -0,0 +1,344 @@ +#include +#include +#include + +#include "address.h" +#include "api.h" +#include "fors.h" +#include "hash.h" +#include "params.h" +#include "randombytes.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +/** + * Computes the leaf at a given address. First generates the WOTS key pair, + * then computes leaf by hashing horizontally. + */ +static void wots_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t tree_addr[8]) { + unsigned char pk[SPX_WOTS_BYTES]; + uint32_t wots_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_keypair_addr( + wots_addr, addr_idx); + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_wots_gen_pk( + pk, sk_seed, pub_seed, wots_addr); + + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_thash_WOTS_LEN( + leaf, pk, pub_seed, wots_pk_addr); +} + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_crypto_sign_secretkeybytes(void) { + return PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_CRYPTO_SECRETKEYBYTES; +} + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_crypto_sign_publickeybytes(void) { + return PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES; +} + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_crypto_sign_bytes(void) { + return PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_CRYPTO_BYTES; +} + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_crypto_sign_seedbytes(void) { + return PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_CRYPTO_SEEDBYTES; +} + +/* + * Generates an SPX key pair given a seed of length + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed) { + /* We do not need the auth path in key generation, but it simplifies the + code to have just one treehash routine that computes both root and path + in one function. */ + unsigned char auth_path[SPX_TREE_HEIGHT * SPX_N]; + uint32_t top_tree_addr[8] = {0}; + + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_layer_addr( + top_tree_addr, SPX_D - 1); + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_type( + top_tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */ + memcpy(sk, seed, PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_CRYPTO_SEEDBYTES); + + memcpy(pk, sk + 2 * SPX_N, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_initialize_hash_function(pk, sk); + + /* Compute root node of the top-most subtree. */ + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_treehash_TREE_HEIGHT( + sk + 3 * SPX_N, auth_path, sk, sk + 2 * SPX_N, 0, 0, + wots_gen_leaf, top_tree_addr); + + memcpy(pk + SPX_N, sk + 3 * SPX_N, SPX_N); + + return 0; +} + +/* + * Generates an SPX key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk) { + unsigned char seed[PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_CRYPTO_SEEDBYTES]; + randombytes(seed, PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_CRYPTO_SEEDBYTES); + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_crypto_sign_seed_keypair( + pk, sk, seed); + + return 0; +} + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + const unsigned char *sk_seed = sk; + const unsigned char *sk_prf = sk + SPX_N; + const unsigned char *pk = sk + 2 * SPX_N; + const unsigned char *pub_seed = pk; + + unsigned char optrand[SPX_N]; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char root[SPX_N]; + uint32_t i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_initialize_hash_function( + pub_seed, sk_seed); + + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ + randombytes(optrand, SPX_N); + /* Compute the digest randomization value. */ + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_gen_message_random( + sig, sk_prf, optrand, m, mlen); + + /* Derive the message digest and leaf index from R, PK and M. */ + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Sign the message hash using FORS. */ + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_fors_sign( + sig, root, mhash, sk_seed, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Compute a WOTS signature. */ + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_wots_sign( + sig, root, sk_seed, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the authentication path for the used WOTS leaf. */ + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_treehash_TREE_HEIGHT( + root, sig, sk_seed, pub_seed, idx_leaf, 0, + wots_gen_leaf, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + *siglen = SPX_BYTES; + + return 0; +} + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk) { + const unsigned char *pub_seed = pk; + const unsigned char *pub_root = pk + SPX_N; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char wots_pk[SPX_WOTS_BYTES]; + unsigned char root[SPX_N]; + unsigned char leaf[SPX_N]; + unsigned int i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + if (siglen != SPX_BYTES) { + return -1; + } + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_initialize_hash_function( + pub_seed, NULL); + + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + /* Derive the message digest and leaf index from R || PK || M. */ + /* The additional SPX_N is a result of the hash domain separator. */ + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + /* Layer correctly defaults to 0, so no need to set_layer_addr */ + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_fors_pk_from_sig( + root, sig, mhash, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + /* For each subtree.. */ + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + + /* The WOTS public key is only correct if the signature was correct. */ + /* Initially, root is the FORS pk, but on subsequent iterations it is + the root of the subtree below the currently processed subtree. */ + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_wots_pk_from_sig( + wots_pk, sig, root, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the leaf node using the WOTS public key. */ + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_thash_WOTS_LEN( + leaf, wots_pk, pub_seed, wots_pk_addr); + + /* Compute the root node of this subtree. */ + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_compute_root( + root, leaf, idx_leaf, 0, sig, SPX_TREE_HEIGHT, + pub_seed, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + /* Check if the root node equals the root node in the public key. */ + if (memcmp(root, pub_root, SPX_N) != 0) { + return -1; + } + + return 0; +} + + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + size_t siglen; + + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_crypto_sign_signature( + sm, &siglen, m, mlen, sk); + + memmove(sm + SPX_BYTES, m, mlen); + *smlen = siglen + mlen; + + return 0; +} + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk) { + /* The API caller does not necessarily know what size a signature should be + but SPHINCS+ signatures are always exactly SPX_BYTES. */ + if (smlen < SPX_BYTES) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + *mlen = smlen - SPX_BYTES; + + if (PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_crypto_sign_verify( + sm, SPX_BYTES, sm + SPX_BYTES, *mlen, pk)) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + /* If verification was successful, move the message to the right place. */ + memmove(m, sm + SPX_BYTES, *mlen); + + return 0; +} diff --git a/crypto_sign/sphincs-shake256-192s-robust/clean/thash.h b/crypto_sign/sphincs-shake256-192s-robust/clean/thash.h new file mode 100644 index 00000000..7a4774de --- /dev/null +++ b/crypto_sign/sphincs-shake256-192s-robust/clean/thash.h @@ -0,0 +1,22 @@ +#ifndef SPX_THASH_H +#define SPX_THASH_H + +#include + +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-shake256-192s-robust/clean/thash_shake256_robust.c b/crypto_sign/sphincs-shake256-192s-robust/clean/thash_shake256_robust.c new file mode 100644 index 00000000..aae2cfed --- /dev/null +++ b/crypto_sign/sphincs-shake256-192s-robust/clean/thash_shake256_robust.c @@ -0,0 +1,69 @@ +#include +#include + +#include "address.h" +#include "params.h" +#include "thash.h" + +#include "fips202.h" + +/** + * Takes an array of inblocks concatenated arrays of SPX_N bytes. + */ +static void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_thash( + unsigned char *out, unsigned char *buf, + const unsigned char *in, unsigned int inblocks, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char *bitmask = buf + SPX_N + SPX_ADDR_BYTES; + unsigned int i; + + memcpy(buf, pub_seed, SPX_N); + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_addr_to_bytes(buf + SPX_N, addr); + + shake256(bitmask, inblocks * SPX_N, buf, SPX_N + SPX_ADDR_BYTES); + + for (i = 0; i < inblocks * SPX_N; i++) { + buf[SPX_N + SPX_ADDR_BYTES + i] = in[i] ^ bitmask[i]; + } + + shake256(out, SPX_N, buf, SPX_N + SPX_ADDR_BYTES + inblocks * SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_ADDR_BYTES + 1 * SPX_N]; + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_thash( + out, buf, in, 1, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_ADDR_BYTES + 2 * SPX_N]; + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_thash( + out, buf, in, 2, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_ADDR_BYTES + SPX_WOTS_LEN * SPX_N]; + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_thash( + out, buf, in, SPX_WOTS_LEN, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_ADDR_BYTES + SPX_FORS_TREES * SPX_N]; + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_thash( + out, buf, in, SPX_FORS_TREES, pub_seed, addr); +} diff --git a/crypto_sign/sphincs-shake256-192s-robust/clean/utils.c b/crypto_sign/sphincs-shake256-192s-robust/clean/utils.c new file mode 100644 index 00000000..3c43ba7f --- /dev/null +++ b/crypto_sign/sphincs-shake256-192s-robust/clean/utils.c @@ -0,0 +1,192 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in) { + + /* Iterate over out in decreasing order, for big-endianness. */ + for (size_t i = outlen; i > 0; i--) { + out[i - 1] = in & 0xff; + in = in >> 8; + } +} + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen) { + unsigned long long retval = 0; + + for (size_t i = 0; i < inlen; i++) { + retval |= ((unsigned long long)in[i]) << (8 * (inlen - 1 - i)); + } + return retval; +} + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + unsigned char buffer[2 * SPX_N]; + + /* If leaf_idx is odd (last bit = 1), current path element is a right child + and auth_path has to go left. Otherwise it is the other way around. */ + if (leaf_idx & 1) { + memcpy(buffer + SPX_N, leaf, SPX_N); + memcpy(buffer, auth_path, SPX_N); + } else { + memcpy(buffer, leaf, SPX_N); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + + for (i = 0; i < tree_height - 1; i++) { + leaf_idx >>= 1; + idx_offset >>= 1; + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_tree_height(addr, i + 1); + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + + /* Pick the right or left neighbor, depending on parity of the node. */ + if (leaf_idx & 1) { + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_thash_2( + buffer + SPX_N, buffer, pub_seed, addr); + memcpy(buffer, auth_path, SPX_N); + } else { + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_thash_2( + buffer, buffer, pub_seed, addr); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + } + + /* The last iteration is exceptional; we do not copy an auth_path node. */ + leaf_idx >>= 1; + idx_offset >>= 1; + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_tree_height(addr, tree_height); + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_thash_2( + root, buffer, pub_seed, addr); +} + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +static void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_treehash( + unsigned char *root, unsigned char *auth_path, + unsigned char *stack, unsigned int *heights, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned int offset = 0; + uint32_t idx; + uint32_t tree_idx; + + for (idx = 0; idx < (uint32_t)(1 << tree_height); idx++) { + /* Add the next leaf node to the stack. */ + gen_leaf(stack + offset * SPX_N, + sk_seed, pub_seed, idx + idx_offset, tree_addr); + offset++; + heights[offset - 1] = 0; + + /* If this is a node we need for the auth path.. */ + if ((leaf_idx ^ 0x1) == idx) { + memcpy(auth_path, stack + (offset - 1)*SPX_N, SPX_N); + } + + /* While the top-most nodes are of equal height.. */ + while (offset >= 2 && heights[offset - 1] == heights[offset - 2]) { + /* Compute index of the new node, in the next layer. */ + tree_idx = (idx >> (heights[offset - 1] + 1)); + + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_tree_height( + tree_addr, heights[offset - 1] + 1); + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_tree_index( + tree_addr, tree_idx + (idx_offset >> (heights[offset - 1] + 1))); + /* Hash the top-most nodes from the stack together. */ + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_thash_2( + stack + (offset - 2)*SPX_N, stack + (offset - 2)*SPX_N, + pub_seed, tree_addr); + offset--; + /* Note that the top-most node is now one layer higher. */ + heights[offset - 1]++; + + /* If this is a node we need for the auth path.. */ + if (((leaf_idx >> heights[offset - 1]) ^ 0x1) == tree_idx) { + memcpy(auth_path + heights[offset - 1]*SPX_N, + stack + (offset - 1)*SPX_N, SPX_N); + } + } + } + memcpy(root, stack, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_FORS_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_FORS_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_FORS_HEIGHT, gen_leaf, tree_addr); +} + +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_TREE_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_TREE_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_TREE_HEIGHT, gen_leaf, tree_addr); +} diff --git a/crypto_sign/sphincs-shake256-192s-robust/clean/utils.h b/crypto_sign/sphincs-shake256-192s-robust/clean/utils.h new file mode 100644 index 00000000..f8ea9adf --- /dev/null +++ b/crypto_sign/sphincs-shake256-192s-robust/clean/utils.h @@ -0,0 +1,60 @@ +#ifndef SPX_UTILS_H +#define SPX_UTILS_H + +#include "params.h" +#include +#include + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in); + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen); + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-shake256-192s-robust/clean/wots.c b/crypto_sign/sphincs-shake256-192s-robust/clean/wots.c new file mode 100644 index 00000000..f5cd5577 --- /dev/null +++ b/crypto_sign/sphincs-shake256-192s-robust/clean/wots.c @@ -0,0 +1,161 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +// TODO clarify address expectations, and make them more uniform. +// TODO i.e. do we expect types to be set already? +// TODO and do we expect modifications or copies? + +/** + * Computes the starting value for a chain, i.e. the secret key. + * Expects the address to be complete up to the chain address. + */ +static void wots_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t wots_addr[8]) { + /* Make sure that the hash address is actually zeroed. */ + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_hash_addr(wots_addr, 0); + + /* Generate sk element. */ + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_prf_addr(sk, sk_seed, wots_addr); +} + +/** + * Computes the chaining function. + * out and in have to be n-byte arrays. + * + * Interprets in as start-th value of the chain. + * addr has to contain the address of the chain. + */ +static void gen_chain(unsigned char *out, const unsigned char *in, + unsigned int start, unsigned int steps, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + /* Initialize out with the value at position 'start'. */ + memcpy(out, in, SPX_N); + + /* Iterate 'steps' calls to the hash function. */ + for (i = start; i < (start + steps) && i < SPX_WOTS_W; i++) { + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_hash_addr(addr, i); + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_thash_1( + out, out, pub_seed, addr); + } +} + +/** + * base_w algorithm as described in draft. + * Interprets an array of bytes as integers in base w. + * This only works when log_w is a divisor of 8. + */ +static void base_w(unsigned int *output, const size_t out_len, + const unsigned char *input) { + size_t in = 0; + size_t out = 0; + unsigned char total = 0; + unsigned int bits = 0; + size_t consumed; + + for (consumed = 0; consumed < out_len; consumed++) { + if (bits == 0) { + total = input[in]; + in++; + bits += 8; + } + bits -= SPX_WOTS_LOGW; + output[out] = (unsigned int)((total >> bits) & (SPX_WOTS_W - 1)); + out++; + } +} + +/* Computes the WOTS+ checksum over a message (in base_w). */ +static void wots_checksum(unsigned int *csum_base_w, + const unsigned int *msg_base_w) { + unsigned int csum = 0; + unsigned char csum_bytes[(SPX_WOTS_LEN2 * SPX_WOTS_LOGW + 7) / 8]; + unsigned int i; + + /* Compute checksum. */ + for (i = 0; i < SPX_WOTS_LEN1; i++) { + csum += SPX_WOTS_W - 1 - msg_base_w[i]; + } + + /* Convert checksum to base_w. */ + /* Make sure expected empty zero bits are the least significant bits. */ + csum = csum << (8 - ((SPX_WOTS_LEN2 * SPX_WOTS_LOGW) % 8)); + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_ull_to_bytes( + csum_bytes, sizeof(csum_bytes), csum); + base_w(csum_base_w, SPX_WOTS_LEN2, csum_bytes); +} + +/* Takes a message and derives the matching chain lengths. */ +static void chain_lengths(unsigned int *lengths, const unsigned char *msg) { + base_w(lengths, SPX_WOTS_LEN1, msg); + wots_checksum(lengths + SPX_WOTS_LEN1, lengths); +} + +/** + * WOTS key generation. Takes a 32 byte sk_seed, expands it to WOTS private key + * elements and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(pk + i * SPX_N, sk_seed, addr); + gen_chain(pk + i * SPX_N, pk + i * SPX_N, + 0, SPX_WOTS_W - 1, pub_seed, addr); + } +} + +/** + * Takes a n-byte message and the 32-byte sk_see to compute a signature 'sig'. + */ +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(sig + i * SPX_N, sk_seed, addr); + gen_chain(sig + i * SPX_N, sig + i * SPX_N, 0, lengths[i], pub_seed, addr); + } +} + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_set_chain_addr(addr, i); + gen_chain(pk + i * SPX_N, sig + i * SPX_N, + lengths[i], SPX_WOTS_W - 1 - lengths[i], pub_seed, addr); + } +} diff --git a/crypto_sign/sphincs-shake256-192s-robust/clean/wots.h b/crypto_sign/sphincs-shake256-192s-robust/clean/wots.h new file mode 100644 index 00000000..da5d8ea4 --- /dev/null +++ b/crypto_sign/sphincs-shake256-192s-robust/clean/wots.h @@ -0,0 +1,38 @@ +#ifndef SPX_WOTS_H +#define SPX_WOTS_H + +#include "params.h" +#include + +/** + * WOTS key generation. Takes a 32 byte seed for the private key, expands it to + * a full WOTS private key and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * Takes a n-byte message and the 32-byte seed for the private key to compute a + * signature that is placed at 'sig'. + */ +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]); + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-shake256-192s-simple/META.yml b/crypto_sign/sphincs-shake256-192s-simple/META.yml new file mode 100644 index 00000000..3bf7fa16 --- /dev/null +++ b/crypto_sign/sphincs-shake256-192s-simple/META.yml @@ -0,0 +1,27 @@ +name: SPHINCS+ +type: signature +claimed-nist-level: 3 +length-public-key: 48 +length-secret-key: 96 +length-signature: 17064 +testvectors-sha256: ea1c38dafdeec8bd6b5a844955b1edffbb1d16f392a647fdae8e6dd148c6396c +principal-submitter: Andreas Hülsing +auxiliary-submitters: + - Jean-Philippe Aumasson + - Daniel J. Bernstein, + - Christoph Dobraunig + - Maria Eichlseder + - Scott Fluhrer + - Stefan-Lukas Gazdag + - Panos Kampanakis + - Stefan Kölbl + - Tanja Lange + - Martin M. Lauridsen + - Florian Mendel + - Ruben Niederhagen + - Christian Rechberger + - Joost Rijneveld + - Peter Schwabe +implementations: + - name: clean + version: https://github.com/sphincs/sphincsplus/commit/77755c94d0bc744478044d6efbb888dc13156441 diff --git a/crypto_sign/sphincs-shake256-192s-simple/clean/LICENSE b/crypto_sign/sphincs-shake256-192s-simple/clean/LICENSE new file mode 100644 index 00000000..670154e3 --- /dev/null +++ b/crypto_sign/sphincs-shake256-192s-simple/clean/LICENSE @@ -0,0 +1,116 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + diff --git a/crypto_sign/sphincs-shake256-192s-simple/clean/Makefile b/crypto_sign/sphincs-shake256-192s-simple/clean/Makefile new file mode 100644 index 00000000..49763008 --- /dev/null +++ b/crypto_sign/sphincs-shake256-192s-simple/clean/Makefile @@ -0,0 +1,20 @@ +# This Makefile can be used with GNU Make or BSD Make + +LIB=libsphincs-shake256-192s-simple_clean.a + +HEADERS = params.h address.h wots.h utils.h fors.h api.h hash.h thash.h +OBJECTS = address.o wots.o utils.o fors.o sign.o hash_shake256.o thash_shake256_simple.o + +CFLAGS=-O3 -Wall -Wconversion -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) + +all: $(LIB) + +%.o: %.c $(HEADERS) + $(CC) $(CFLAGS) -c -o $@ $< + +$(LIB): $(OBJECTS) + $(AR) -r $@ $(OBJECTS) + +clean: + $(RM) $(OBJECTS) + $(RM) $(LIB) diff --git a/crypto_sign/sphincs-shake256-192s-simple/clean/Makefile.Microsoft_nmake b/crypto_sign/sphincs-shake256-192s-simple/clean/Makefile.Microsoft_nmake new file mode 100644 index 00000000..3257c45c --- /dev/null +++ b/crypto_sign/sphincs-shake256-192s-simple/clean/Makefile.Microsoft_nmake @@ -0,0 +1,19 @@ +# This Makefile can be used with Microsoft Visual Studio's nmake using the command: +# nmake /f Makefile.Microsoft_nmake + +LIBRARY=libsphincs-shake256-192s-simple_clean.lib +OBJECTS=address.obj wots.obj utils.obj fors.obj sign.obj hash_shake256.obj thash_shake256_simple.obj + +CFLAGS=/nologo /I ..\..\..\common /W4 /WX + +all: $(LIBRARY) + +# Make sure objects are recompiled if headers change. +$(OBJECTS): *.h + +$(LIBRARY): $(OBJECTS) + LIB.EXE /NOLOGO /WX /OUT:$@ $** + +clean: + -DEL $(OBJECTS) + -DEL $(LIBRARY) diff --git a/crypto_sign/sphincs-shake256-192s-simple/clean/address.c b/crypto_sign/sphincs-shake256-192s-simple/clean/address.c new file mode 100644 index 00000000..1a3092c0 --- /dev/null +++ b/crypto_sign/sphincs-shake256-192s-simple/clean/address.c @@ -0,0 +1,78 @@ +#include + +#include "address.h" +#include "params.h" +#include "utils.h" + +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]) { + int i; + + for (i = 0; i < 8; i++) { + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_ull_to_bytes( + bytes + i * 4, 4, addr[i]); + } +} + +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer) { + addr[0] = layer; +} + +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree) { + addr[1] = 0; + addr[2] = (uint32_t) (tree >> 32); + addr[3] = (uint32_t) tree; +} + +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_type( + uint32_t addr[8], uint32_t type) { + addr[4] = type; +} + +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; +} + +/* These functions are used for OTS addresses. */ + +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair) { + addr[5] = keypair; +} + +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; + out[5] = in[5]; +} + +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain) { + addr[6] = chain; +} + +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash) { + addr[7] = hash; +} + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height) { + addr[6] = tree_height; +} + +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index) { + addr[7] = tree_index; +} diff --git a/crypto_sign/sphincs-shake256-192s-simple/clean/address.h b/crypto_sign/sphincs-shake256-192s-simple/clean/address.h new file mode 100644 index 00000000..05c86db2 --- /dev/null +++ b/crypto_sign/sphincs-shake256-192s-simple/clean/address.h @@ -0,0 +1,50 @@ +#ifndef SPX_ADDRESS_H +#define SPX_ADDRESS_H + +#include + +#define SPX_ADDR_TYPE_WOTS 0 +#define SPX_ADDR_TYPE_WOTSPK 1 +#define SPX_ADDR_TYPE_HASHTREE 2 +#define SPX_ADDR_TYPE_FORSTREE 3 +#define SPX_ADDR_TYPE_FORSPK 4 + +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer); + +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree); + +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_type( + uint32_t addr[8], uint32_t type); + +/* Copies the layer and tree part of one address into the other */ +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for WOTS and FORS addresses. */ + +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair); + +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain); + +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash); + +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height); + +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index); + +#endif diff --git a/crypto_sign/sphincs-shake256-192s-simple/clean/api.h b/crypto_sign/sphincs-shake256-192s-simple/clean/api.h new file mode 100644 index 00000000..5f2a74f5 --- /dev/null +++ b/crypto_sign/sphincs-shake256-192s-simple/clean/api.h @@ -0,0 +1,78 @@ +#ifndef PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_API_H +#define PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_API_H + +#include +#include + +#define PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_CRYPTO_ALGNAME "SPHINCS+" + +#define PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_CRYPTO_SECRETKEYBYTES 96 +#define PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES 48 +#define PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_CRYPTO_BYTES 17064 +#define PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_CRYPTO_SEEDBYTES 72 + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_crypto_sign_secretkeybytes(void); + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_crypto_sign_publickeybytes(void); + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_crypto_sign_bytes(void); + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_crypto_sign_seedbytes(void); + +/* + * Generates a SPHINCS+ key pair given a seed. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed); + +/* + * Generates a SPHINCS+ key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk); + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk); + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk); + +#endif diff --git a/crypto_sign/sphincs-shake256-192s-simple/clean/fors.c b/crypto_sign/sphincs-shake256-192s-simple/clean/fors.c new file mode 100644 index 00000000..3d5d4a7c --- /dev/null +++ b/crypto_sign/sphincs-shake256-192s-simple/clean/fors.c @@ -0,0 +1,164 @@ +#include +#include +#include + +#include "address.h" +#include "fors.h" +#include "hash.h" +#include "thash.h" +#include "utils.h" + +static void fors_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_prf_addr( + sk, sk_seed, fors_leaf_addr); +} + +static void fors_sk_to_leaf(unsigned char *leaf, const unsigned char *sk, + const unsigned char *pub_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_thash_1( + leaf, sk, pub_seed, fors_leaf_addr); +} + +static void fors_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t fors_tree_addr[8]) { + uint32_t fors_leaf_addr[8] = {0}; + + /* Only copy the parts that must be kept in fors_leaf_addr. */ + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_copy_keypair_addr( + fors_leaf_addr, fors_tree_addr); + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_type( + fors_leaf_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_tree_index( + fors_leaf_addr, addr_idx); + + fors_gen_sk(leaf, sk_seed, fors_leaf_addr); + fors_sk_to_leaf(leaf, leaf, pub_seed, fors_leaf_addr); +} + +/** + * Interprets m as SPX_FORS_HEIGHT-bit unsigned integers. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + * Assumes indices has space for SPX_FORS_TREES integers. + */ +static void message_to_indices(uint32_t *indices, const unsigned char *m) { + unsigned int i, j; + unsigned int offset = 0; + + for (i = 0; i < SPX_FORS_TREES; i++) { + indices[i] = 0; + for (j = 0; j < SPX_FORS_HEIGHT; j++) { + indices[i] ^= (((uint32_t)m[offset >> 3] >> (offset & 0x7)) & 0x1) << j; + offset++; + } + } +} + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Include the secret key part that produces the selected leaf node. */ + fors_gen_sk(sig, sk_seed, fors_tree_addr); + sig += SPX_N; + + /* Compute the authentication path for this leaf node. */ + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_treehash_FORS_HEIGHT( + roots + i * SPX_N, sig, sk_seed, pub_seed, + indices[i], idx_offset, fors_gen_leaf, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + unsigned char leaf[SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Derive the leaf from the included secret key part. */ + fors_sk_to_leaf(leaf, sig, pub_seed, fors_tree_addr); + sig += SPX_N; + + /* Derive the corresponding root node of this tree. */ + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_compute_root( + roots + i * SPX_N, leaf, indices[i], idx_offset, sig, + SPX_FORS_HEIGHT, pub_seed, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} diff --git a/crypto_sign/sphincs-shake256-192s-simple/clean/fors.h b/crypto_sign/sphincs-shake256-192s-simple/clean/fors.h new file mode 100644 index 00000000..1c0c3f59 --- /dev/null +++ b/crypto_sign/sphincs-shake256-192s-simple/clean/fors.h @@ -0,0 +1,30 @@ +#ifndef SPX_FORS_H +#define SPX_FORS_H + +#include + +#include "params.h" + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]); + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-shake256-192s-simple/clean/hash.h b/crypto_sign/sphincs-shake256-192s-simple/clean/hash.h new file mode 100644 index 00000000..96460328 --- /dev/null +++ b/crypto_sign/sphincs-shake256-192s-simple/clean/hash.h @@ -0,0 +1,22 @@ +#ifndef SPX_HASH_H +#define SPX_HASH_H + +#include + +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed); + +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen); + +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen); + +#endif diff --git a/crypto_sign/sphincs-shake256-192s-simple/clean/hash_shake256.c b/crypto_sign/sphincs-shake256-192s-simple/clean/hash_shake256.c new file mode 100644 index 00000000..5c79d478 --- /dev/null +++ b/crypto_sign/sphincs-shake256-192s-simple/clean/hash_shake256.c @@ -0,0 +1,87 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "utils.h" + +#include "fips202.h" + +/* For SHAKE256, there is no immediate reason to initialize at the start, + so this function is an empty operation. */ +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed) { + (void)pub_seed; /* Suppress an 'unused parameter' warning. */ + (void)sk_seed; /* Suppress an 'unused parameter' warning. */ +} + +/* + * Computes PRF(key, addr), given a secret key of SPX_N bytes and an address + */ +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]) { + unsigned char buf[SPX_N + SPX_ADDR_BYTES]; + + memcpy(buf, key, SPX_N); + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_addr_to_bytes(buf + SPX_N, addr); + + shake256(out, SPX_N, buf, SPX_N + SPX_ADDR_BYTES); +} + +/** + * Computes the message-dependent randomness R, using a secret seed and an + * optional randomization value as well as the message. + */ +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen) { + uint64_t s_inc[26]; + + shake256_inc_init(s_inc); + shake256_inc_absorb(s_inc, sk_prf, SPX_N); + shake256_inc_absorb(s_inc, optrand, SPX_N); + shake256_inc_absorb(s_inc, m, mlen); + shake256_inc_finalize(s_inc); + shake256_inc_squeeze(R, SPX_N, s_inc); +} + +/** + * Computes the message hash using R, the public key, and the message. + * Outputs the message digest and the index of the leaf. The index is split in + * the tree index and the leaf index, for convenient copying to an address. + */ +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen) { +#define SPX_TREE_BITS (SPX_TREE_HEIGHT * (SPX_D - 1)) +#define SPX_TREE_BYTES ((SPX_TREE_BITS + 7) / 8) +#define SPX_LEAF_BITS SPX_TREE_HEIGHT +#define SPX_LEAF_BYTES ((SPX_LEAF_BITS + 7) / 8) +#define SPX_DGST_BYTES (SPX_FORS_MSG_BYTES + SPX_TREE_BYTES + SPX_LEAF_BYTES) + + unsigned char buf[SPX_DGST_BYTES]; + unsigned char *bufp = buf; + uint64_t s_inc[26]; + + shake256_inc_init(s_inc); + shake256_inc_absorb(s_inc, R, SPX_N); + shake256_inc_absorb(s_inc, pk, SPX_PK_BYTES); + shake256_inc_absorb(s_inc, m, mlen); + shake256_inc_finalize(s_inc); + shake256_inc_squeeze(buf, SPX_DGST_BYTES, s_inc); + + memcpy(digest, bufp, SPX_FORS_MSG_BYTES); + bufp += SPX_FORS_MSG_BYTES; + + *tree = PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_bytes_to_ull( + bufp, SPX_TREE_BYTES); + *tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS); + bufp += SPX_TREE_BYTES; + + *leaf_idx = (uint32_t)PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_bytes_to_ull( + bufp, SPX_LEAF_BYTES); + *leaf_idx &= (~(uint32_t)0) >> (32 - SPX_LEAF_BITS); +} diff --git a/crypto_sign/sphincs-shake256-192s-simple/clean/params.h b/crypto_sign/sphincs-shake256-192s-simple/clean/params.h new file mode 100644 index 00000000..483b5374 --- /dev/null +++ b/crypto_sign/sphincs-shake256-192s-simple/clean/params.h @@ -0,0 +1,53 @@ +#ifndef SPX_PARAMS_H +#define SPX_PARAMS_H + +/* Hash output length in bytes. */ +#define SPX_N 24 +/* Height of the hypertree. */ +#define SPX_FULL_HEIGHT 64 +/* Number of subtree layer. */ +#define SPX_D 8 +/* FORS tree dimensions. */ +#define SPX_FORS_HEIGHT 16 +#define SPX_FORS_TREES 14 +/* Winternitz parameter, */ +#define SPX_WOTS_W 16 + +/* The hash function is defined by linking a different hash.c file, as opposed + to setting a #define constant. */ + +/* For clarity */ +#define SPX_ADDR_BYTES 32 + +/* WOTS parameters. */ +#define SPX_WOTS_LOGW 4 + +#define SPX_WOTS_LEN1 (8 * SPX_N / SPX_WOTS_LOGW) + +/* SPX_WOTS_LEN2 is floor(log(len_1 * (w - 1)) / log(w)) + 1; we precompute */ +#define SPX_WOTS_LEN2 3 + +#define SPX_WOTS_LEN (SPX_WOTS_LEN1 + SPX_WOTS_LEN2) +#define SPX_WOTS_BYTES (SPX_WOTS_LEN * SPX_N) +#define SPX_WOTS_PK_BYTES SPX_WOTS_BYTES + +/* Subtree size. */ +#define SPX_TREE_HEIGHT (SPX_FULL_HEIGHT / SPX_D) + +/* FORS parameters. */ +#define SPX_FORS_MSG_BYTES ((SPX_FORS_HEIGHT * SPX_FORS_TREES + 7) / 8) +#define SPX_FORS_BYTES ((SPX_FORS_HEIGHT + 1) * SPX_FORS_TREES * SPX_N) +#define SPX_FORS_PK_BYTES SPX_N + +/* Resulting SPX sizes. */ +#define SPX_BYTES (SPX_N + SPX_FORS_BYTES + SPX_D * SPX_WOTS_BYTES +\ + SPX_FULL_HEIGHT * SPX_N) +#define SPX_PK_BYTES (2 * SPX_N) +#define SPX_SK_BYTES (2 * SPX_N + SPX_PK_BYTES) + +/* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ +#define SPX_OPTRAND_BYTES 32 + +#endif diff --git a/crypto_sign/sphincs-shake256-192s-simple/clean/sign.c b/crypto_sign/sphincs-shake256-192s-simple/clean/sign.c new file mode 100644 index 00000000..d48c33ec --- /dev/null +++ b/crypto_sign/sphincs-shake256-192s-simple/clean/sign.c @@ -0,0 +1,344 @@ +#include +#include +#include + +#include "address.h" +#include "api.h" +#include "fors.h" +#include "hash.h" +#include "params.h" +#include "randombytes.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +/** + * Computes the leaf at a given address. First generates the WOTS key pair, + * then computes leaf by hashing horizontally. + */ +static void wots_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t tree_addr[8]) { + unsigned char pk[SPX_WOTS_BYTES]; + uint32_t wots_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, addr_idx); + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_wots_gen_pk( + pk, sk_seed, pub_seed, wots_addr); + + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_thash_WOTS_LEN( + leaf, pk, pub_seed, wots_pk_addr); +} + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_crypto_sign_secretkeybytes(void) { + return PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_CRYPTO_SECRETKEYBYTES; +} + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_crypto_sign_publickeybytes(void) { + return PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES; +} + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_crypto_sign_bytes(void) { + return PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_CRYPTO_BYTES; +} + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_crypto_sign_seedbytes(void) { + return PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_CRYPTO_SEEDBYTES; +} + +/* + * Generates an SPX key pair given a seed of length + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed) { + /* We do not need the auth path in key generation, but it simplifies the + code to have just one treehash routine that computes both root and path + in one function. */ + unsigned char auth_path[SPX_TREE_HEIGHT * SPX_N]; + uint32_t top_tree_addr[8] = {0}; + + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_layer_addr( + top_tree_addr, SPX_D - 1); + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_type( + top_tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */ + memcpy(sk, seed, PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_CRYPTO_SEEDBYTES); + + memcpy(pk, sk + 2 * SPX_N, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_initialize_hash_function(pk, sk); + + /* Compute root node of the top-most subtree. */ + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_treehash_TREE_HEIGHT( + sk + 3 * SPX_N, auth_path, sk, sk + 2 * SPX_N, 0, 0, + wots_gen_leaf, top_tree_addr); + + memcpy(pk + SPX_N, sk + 3 * SPX_N, SPX_N); + + return 0; +} + +/* + * Generates an SPX key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk) { + unsigned char seed[PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_CRYPTO_SEEDBYTES]; + randombytes(seed, PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_CRYPTO_SEEDBYTES); + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_crypto_sign_seed_keypair( + pk, sk, seed); + + return 0; +} + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + const unsigned char *sk_seed = sk; + const unsigned char *sk_prf = sk + SPX_N; + const unsigned char *pk = sk + 2 * SPX_N; + const unsigned char *pub_seed = pk; + + unsigned char optrand[SPX_N]; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char root[SPX_N]; + uint32_t i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_initialize_hash_function( + pub_seed, sk_seed); + + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ + randombytes(optrand, SPX_N); + /* Compute the digest randomization value. */ + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_gen_message_random( + sig, sk_prf, optrand, m, mlen); + + /* Derive the message digest and leaf index from R, PK and M. */ + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Sign the message hash using FORS. */ + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_fors_sign( + sig, root, mhash, sk_seed, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Compute a WOTS signature. */ + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_wots_sign( + sig, root, sk_seed, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the authentication path for the used WOTS leaf. */ + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_treehash_TREE_HEIGHT( + root, sig, sk_seed, pub_seed, idx_leaf, 0, + wots_gen_leaf, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + *siglen = SPX_BYTES; + + return 0; +} + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk) { + const unsigned char *pub_seed = pk; + const unsigned char *pub_root = pk + SPX_N; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char wots_pk[SPX_WOTS_BYTES]; + unsigned char root[SPX_N]; + unsigned char leaf[SPX_N]; + unsigned int i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + if (siglen != SPX_BYTES) { + return -1; + } + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_initialize_hash_function( + pub_seed, NULL); + + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + /* Derive the message digest and leaf index from R || PK || M. */ + /* The additional SPX_N is a result of the hash domain separator. */ + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + /* Layer correctly defaults to 0, so no need to set_layer_addr */ + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_fors_pk_from_sig( + root, sig, mhash, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + /* For each subtree.. */ + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + + /* The WOTS public key is only correct if the signature was correct. */ + /* Initially, root is the FORS pk, but on subsequent iterations it is + the root of the subtree below the currently processed subtree. */ + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_wots_pk_from_sig( + wots_pk, sig, root, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the leaf node using the WOTS public key. */ + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_thash_WOTS_LEN( + leaf, wots_pk, pub_seed, wots_pk_addr); + + /* Compute the root node of this subtree. */ + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_compute_root( + root, leaf, idx_leaf, 0, sig, SPX_TREE_HEIGHT, + pub_seed, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + /* Check if the root node equals the root node in the public key. */ + if (memcmp(root, pub_root, SPX_N) != 0) { + return -1; + } + + return 0; +} + + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + size_t siglen; + + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_crypto_sign_signature( + sm, &siglen, m, mlen, sk); + + memmove(sm + SPX_BYTES, m, mlen); + *smlen = siglen + mlen; + + return 0; +} + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk) { + /* The API caller does not necessarily know what size a signature should be + but SPHINCS+ signatures are always exactly SPX_BYTES. */ + if (smlen < SPX_BYTES) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + *mlen = smlen - SPX_BYTES; + + if (PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_crypto_sign_verify( + sm, SPX_BYTES, sm + SPX_BYTES, *mlen, pk)) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + /* If verification was successful, move the message to the right place. */ + memmove(m, sm + SPX_BYTES, *mlen); + + return 0; +} diff --git a/crypto_sign/sphincs-shake256-192s-simple/clean/thash.h b/crypto_sign/sphincs-shake256-192s-simple/clean/thash.h new file mode 100644 index 00000000..41097226 --- /dev/null +++ b/crypto_sign/sphincs-shake256-192s-simple/clean/thash.h @@ -0,0 +1,22 @@ +#ifndef SPX_THASH_H +#define SPX_THASH_H + +#include + +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-shake256-192s-simple/clean/thash_shake256_simple.c b/crypto_sign/sphincs-shake256-192s-simple/clean/thash_shake256_simple.c new file mode 100644 index 00000000..5c5aabf3 --- /dev/null +++ b/crypto_sign/sphincs-shake256-192s-simple/clean/thash_shake256_simple.c @@ -0,0 +1,61 @@ +#include +#include + +#include "address.h" +#include "params.h" +#include "thash.h" + +#include "fips202.h" + +/** + * Takes an array of inblocks concatenated arrays of SPX_N bytes. + */ +static void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_thash( + unsigned char *out, unsigned char *buf, + const unsigned char *in, unsigned int inblocks, + const unsigned char *pub_seed, uint32_t addr[8]) { + + memcpy(buf, pub_seed, SPX_N); + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_addr_to_bytes(buf + SPX_N, addr); + memcpy(buf + SPX_N + SPX_ADDR_BYTES, in, inblocks * SPX_N); + + shake256(out, SPX_N, buf, SPX_N + SPX_ADDR_BYTES + inblocks * SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_ADDR_BYTES + 1 * SPX_N]; + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_thash( + out, buf, in, 1, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_ADDR_BYTES + 2 * SPX_N]; + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_thash( + out, buf, in, 2, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_ADDR_BYTES + SPX_WOTS_LEN * SPX_N]; + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_thash( + out, buf, in, SPX_WOTS_LEN, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_ADDR_BYTES + SPX_FORS_TREES * SPX_N]; + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_thash( + out, buf, in, SPX_FORS_TREES, pub_seed, addr); +} diff --git a/crypto_sign/sphincs-shake256-192s-simple/clean/utils.c b/crypto_sign/sphincs-shake256-192s-simple/clean/utils.c new file mode 100644 index 00000000..0f5337cd --- /dev/null +++ b/crypto_sign/sphincs-shake256-192s-simple/clean/utils.c @@ -0,0 +1,192 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in) { + + /* Iterate over out in decreasing order, for big-endianness. */ + for (size_t i = outlen; i > 0; i--) { + out[i - 1] = in & 0xff; + in = in >> 8; + } +} + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen) { + unsigned long long retval = 0; + + for (size_t i = 0; i < inlen; i++) { + retval |= ((unsigned long long)in[i]) << (8 * (inlen - 1 - i)); + } + return retval; +} + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + unsigned char buffer[2 * SPX_N]; + + /* If leaf_idx is odd (last bit = 1), current path element is a right child + and auth_path has to go left. Otherwise it is the other way around. */ + if (leaf_idx & 1) { + memcpy(buffer + SPX_N, leaf, SPX_N); + memcpy(buffer, auth_path, SPX_N); + } else { + memcpy(buffer, leaf, SPX_N); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + + for (i = 0; i < tree_height - 1; i++) { + leaf_idx >>= 1; + idx_offset >>= 1; + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_tree_height(addr, i + 1); + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + + /* Pick the right or left neighbor, depending on parity of the node. */ + if (leaf_idx & 1) { + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_thash_2( + buffer + SPX_N, buffer, pub_seed, addr); + memcpy(buffer, auth_path, SPX_N); + } else { + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_thash_2( + buffer, buffer, pub_seed, addr); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + } + + /* The last iteration is exceptional; we do not copy an auth_path node. */ + leaf_idx >>= 1; + idx_offset >>= 1; + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_tree_height(addr, tree_height); + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_thash_2( + root, buffer, pub_seed, addr); +} + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +static void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_treehash( + unsigned char *root, unsigned char *auth_path, + unsigned char *stack, unsigned int *heights, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned int offset = 0; + uint32_t idx; + uint32_t tree_idx; + + for (idx = 0; idx < (uint32_t)(1 << tree_height); idx++) { + /* Add the next leaf node to the stack. */ + gen_leaf(stack + offset * SPX_N, + sk_seed, pub_seed, idx + idx_offset, tree_addr); + offset++; + heights[offset - 1] = 0; + + /* If this is a node we need for the auth path.. */ + if ((leaf_idx ^ 0x1) == idx) { + memcpy(auth_path, stack + (offset - 1)*SPX_N, SPX_N); + } + + /* While the top-most nodes are of equal height.. */ + while (offset >= 2 && heights[offset - 1] == heights[offset - 2]) { + /* Compute index of the new node, in the next layer. */ + tree_idx = (idx >> (heights[offset - 1] + 1)); + + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_tree_height( + tree_addr, heights[offset - 1] + 1); + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_tree_index( + tree_addr, tree_idx + (idx_offset >> (heights[offset - 1] + 1))); + /* Hash the top-most nodes from the stack together. */ + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_thash_2( + stack + (offset - 2)*SPX_N, stack + (offset - 2)*SPX_N, + pub_seed, tree_addr); + offset--; + /* Note that the top-most node is now one layer higher. */ + heights[offset - 1]++; + + /* If this is a node we need for the auth path.. */ + if (((leaf_idx >> heights[offset - 1]) ^ 0x1) == tree_idx) { + memcpy(auth_path + heights[offset - 1]*SPX_N, + stack + (offset - 1)*SPX_N, SPX_N); + } + } + } + memcpy(root, stack, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_FORS_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_FORS_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_FORS_HEIGHT, gen_leaf, tree_addr); +} + +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_TREE_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_TREE_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_TREE_HEIGHT, gen_leaf, tree_addr); +} diff --git a/crypto_sign/sphincs-shake256-192s-simple/clean/utils.h b/crypto_sign/sphincs-shake256-192s-simple/clean/utils.h new file mode 100644 index 00000000..f9b55eb8 --- /dev/null +++ b/crypto_sign/sphincs-shake256-192s-simple/clean/utils.h @@ -0,0 +1,60 @@ +#ifndef SPX_UTILS_H +#define SPX_UTILS_H + +#include "params.h" +#include +#include + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in); + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen); + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-shake256-192s-simple/clean/wots.c b/crypto_sign/sphincs-shake256-192s-simple/clean/wots.c new file mode 100644 index 00000000..aad30ffd --- /dev/null +++ b/crypto_sign/sphincs-shake256-192s-simple/clean/wots.c @@ -0,0 +1,161 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +// TODO clarify address expectations, and make them more uniform. +// TODO i.e. do we expect types to be set already? +// TODO and do we expect modifications or copies? + +/** + * Computes the starting value for a chain, i.e. the secret key. + * Expects the address to be complete up to the chain address. + */ +static void wots_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t wots_addr[8]) { + /* Make sure that the hash address is actually zeroed. */ + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_hash_addr(wots_addr, 0); + + /* Generate sk element. */ + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_prf_addr(sk, sk_seed, wots_addr); +} + +/** + * Computes the chaining function. + * out and in have to be n-byte arrays. + * + * Interprets in as start-th value of the chain. + * addr has to contain the address of the chain. + */ +static void gen_chain(unsigned char *out, const unsigned char *in, + unsigned int start, unsigned int steps, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + /* Initialize out with the value at position 'start'. */ + memcpy(out, in, SPX_N); + + /* Iterate 'steps' calls to the hash function. */ + for (i = start; i < (start + steps) && i < SPX_WOTS_W; i++) { + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_hash_addr(addr, i); + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_thash_1( + out, out, pub_seed, addr); + } +} + +/** + * base_w algorithm as described in draft. + * Interprets an array of bytes as integers in base w. + * This only works when log_w is a divisor of 8. + */ +static void base_w(unsigned int *output, const size_t out_len, + const unsigned char *input) { + size_t in = 0; + size_t out = 0; + unsigned char total = 0; + unsigned int bits = 0; + size_t consumed; + + for (consumed = 0; consumed < out_len; consumed++) { + if (bits == 0) { + total = input[in]; + in++; + bits += 8; + } + bits -= SPX_WOTS_LOGW; + output[out] = (unsigned int)((total >> bits) & (SPX_WOTS_W - 1)); + out++; + } +} + +/* Computes the WOTS+ checksum over a message (in base_w). */ +static void wots_checksum(unsigned int *csum_base_w, + const unsigned int *msg_base_w) { + unsigned int csum = 0; + unsigned char csum_bytes[(SPX_WOTS_LEN2 * SPX_WOTS_LOGW + 7) / 8]; + unsigned int i; + + /* Compute checksum. */ + for (i = 0; i < SPX_WOTS_LEN1; i++) { + csum += SPX_WOTS_W - 1 - msg_base_w[i]; + } + + /* Convert checksum to base_w. */ + /* Make sure expected empty zero bits are the least significant bits. */ + csum = csum << (8 - ((SPX_WOTS_LEN2 * SPX_WOTS_LOGW) % 8)); + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_ull_to_bytes( + csum_bytes, sizeof(csum_bytes), csum); + base_w(csum_base_w, SPX_WOTS_LEN2, csum_bytes); +} + +/* Takes a message and derives the matching chain lengths. */ +static void chain_lengths(unsigned int *lengths, const unsigned char *msg) { + base_w(lengths, SPX_WOTS_LEN1, msg); + wots_checksum(lengths + SPX_WOTS_LEN1, lengths); +} + +/** + * WOTS key generation. Takes a 32 byte sk_seed, expands it to WOTS private key + * elements and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(pk + i * SPX_N, sk_seed, addr); + gen_chain(pk + i * SPX_N, pk + i * SPX_N, + 0, SPX_WOTS_W - 1, pub_seed, addr); + } +} + +/** + * Takes a n-byte message and the 32-byte sk_see to compute a signature 'sig'. + */ +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(sig + i * SPX_N, sk_seed, addr); + gen_chain(sig + i * SPX_N, sig + i * SPX_N, 0, lengths[i], pub_seed, addr); + } +} + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_set_chain_addr(addr, i); + gen_chain(pk + i * SPX_N, sig + i * SPX_N, + lengths[i], SPX_WOTS_W - 1 - lengths[i], pub_seed, addr); + } +} diff --git a/crypto_sign/sphincs-shake256-192s-simple/clean/wots.h b/crypto_sign/sphincs-shake256-192s-simple/clean/wots.h new file mode 100644 index 00000000..ab39be0f --- /dev/null +++ b/crypto_sign/sphincs-shake256-192s-simple/clean/wots.h @@ -0,0 +1,38 @@ +#ifndef SPX_WOTS_H +#define SPX_WOTS_H + +#include "params.h" +#include + +/** + * WOTS key generation. Takes a 32 byte seed for the private key, expands it to + * a full WOTS private key and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * Takes a n-byte message and the 32-byte seed for the private key to compute a + * signature that is placed at 'sig'. + */ +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]); + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-shake256-256f-robust/META.yml b/crypto_sign/sphincs-shake256-256f-robust/META.yml new file mode 100644 index 00000000..67eb8355 --- /dev/null +++ b/crypto_sign/sphincs-shake256-256f-robust/META.yml @@ -0,0 +1,27 @@ +name: SPHINCS+ +type: signature +claimed-nist-level: 5 +length-public-key: 64 +length-secret-key: 128 +length-signature: 49216 +testvectors-sha256: 4757a2ce7aec6daac4ab894336586949f7919c63d55200ec6325eb395efcf1ef +principal-submitter: Andreas Hülsing +auxiliary-submitters: + - Jean-Philippe Aumasson + - Daniel J. Bernstein, + - Christoph Dobraunig + - Maria Eichlseder + - Scott Fluhrer + - Stefan-Lukas Gazdag + - Panos Kampanakis + - Stefan Kölbl + - Tanja Lange + - Martin M. Lauridsen + - Florian Mendel + - Ruben Niederhagen + - Christian Rechberger + - Joost Rijneveld + - Peter Schwabe +implementations: + - name: clean + version: https://github.com/sphincs/sphincsplus/commit/77755c94d0bc744478044d6efbb888dc13156441 diff --git a/crypto_sign/sphincs-shake256-256f-robust/clean/LICENSE b/crypto_sign/sphincs-shake256-256f-robust/clean/LICENSE new file mode 100644 index 00000000..670154e3 --- /dev/null +++ b/crypto_sign/sphincs-shake256-256f-robust/clean/LICENSE @@ -0,0 +1,116 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + diff --git a/crypto_sign/sphincs-shake256-256f-robust/clean/Makefile b/crypto_sign/sphincs-shake256-256f-robust/clean/Makefile new file mode 100644 index 00000000..7a30f008 --- /dev/null +++ b/crypto_sign/sphincs-shake256-256f-robust/clean/Makefile @@ -0,0 +1,20 @@ +# This Makefile can be used with GNU Make or BSD Make + +LIB=libsphincs-shake256-256f-robust_clean.a + +HEADERS = params.h address.h wots.h utils.h fors.h api.h hash.h thash.h +OBJECTS = address.o wots.o utils.o fors.o sign.o hash_shake256.o thash_shake256_robust.o + +CFLAGS=-O3 -Wall -Wconversion -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) + +all: $(LIB) + +%.o: %.c $(HEADERS) + $(CC) $(CFLAGS) -c -o $@ $< + +$(LIB): $(OBJECTS) + $(AR) -r $@ $(OBJECTS) + +clean: + $(RM) $(OBJECTS) + $(RM) $(LIB) diff --git a/crypto_sign/sphincs-shake256-256f-robust/clean/Makefile.Microsoft_nmake b/crypto_sign/sphincs-shake256-256f-robust/clean/Makefile.Microsoft_nmake new file mode 100644 index 00000000..0087257f --- /dev/null +++ b/crypto_sign/sphincs-shake256-256f-robust/clean/Makefile.Microsoft_nmake @@ -0,0 +1,19 @@ +# This Makefile can be used with Microsoft Visual Studio's nmake using the command: +# nmake /f Makefile.Microsoft_nmake + +LIBRARY=libsphincs-shake256-256f-robust_clean.lib +OBJECTS=address.obj wots.obj utils.obj fors.obj sign.obj hash_shake256.obj thash_shake256_robust.obj + +CFLAGS=/nologo /I ..\..\..\common /W4 /WX + +all: $(LIBRARY) + +# Make sure objects are recompiled if headers change. +$(OBJECTS): *.h + +$(LIBRARY): $(OBJECTS) + LIB.EXE /NOLOGO /WX /OUT:$@ $** + +clean: + -DEL $(OBJECTS) + -DEL $(LIBRARY) diff --git a/crypto_sign/sphincs-shake256-256f-robust/clean/address.c b/crypto_sign/sphincs-shake256-256f-robust/clean/address.c new file mode 100644 index 00000000..1cda8cdb --- /dev/null +++ b/crypto_sign/sphincs-shake256-256f-robust/clean/address.c @@ -0,0 +1,78 @@ +#include + +#include "address.h" +#include "params.h" +#include "utils.h" + +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]) { + int i; + + for (i = 0; i < 8; i++) { + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_ull_to_bytes( + bytes + i * 4, 4, addr[i]); + } +} + +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer) { + addr[0] = layer; +} + +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree) { + addr[1] = 0; + addr[2] = (uint32_t) (tree >> 32); + addr[3] = (uint32_t) tree; +} + +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_type( + uint32_t addr[8], uint32_t type) { + addr[4] = type; +} + +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; +} + +/* These functions are used for OTS addresses. */ + +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair) { + addr[5] = keypair; +} + +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; + out[5] = in[5]; +} + +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain) { + addr[6] = chain; +} + +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash) { + addr[7] = hash; +} + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height) { + addr[6] = tree_height; +} + +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index) { + addr[7] = tree_index; +} diff --git a/crypto_sign/sphincs-shake256-256f-robust/clean/address.h b/crypto_sign/sphincs-shake256-256f-robust/clean/address.h new file mode 100644 index 00000000..ceb1d176 --- /dev/null +++ b/crypto_sign/sphincs-shake256-256f-robust/clean/address.h @@ -0,0 +1,50 @@ +#ifndef SPX_ADDRESS_H +#define SPX_ADDRESS_H + +#include + +#define SPX_ADDR_TYPE_WOTS 0 +#define SPX_ADDR_TYPE_WOTSPK 1 +#define SPX_ADDR_TYPE_HASHTREE 2 +#define SPX_ADDR_TYPE_FORSTREE 3 +#define SPX_ADDR_TYPE_FORSPK 4 + +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer); + +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree); + +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_type( + uint32_t addr[8], uint32_t type); + +/* Copies the layer and tree part of one address into the other */ +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for WOTS and FORS addresses. */ + +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair); + +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain); + +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash); + +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height); + +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index); + +#endif diff --git a/crypto_sign/sphincs-shake256-256f-robust/clean/api.h b/crypto_sign/sphincs-shake256-256f-robust/clean/api.h new file mode 100644 index 00000000..9dbc77fb --- /dev/null +++ b/crypto_sign/sphincs-shake256-256f-robust/clean/api.h @@ -0,0 +1,78 @@ +#ifndef PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_API_H +#define PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_API_H + +#include +#include + +#define PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_CRYPTO_ALGNAME "SPHINCS+" + +#define PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_CRYPTO_SECRETKEYBYTES 128 +#define PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES 64 +#define PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_CRYPTO_BYTES 49216 +#define PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_CRYPTO_SEEDBYTES 96 + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_crypto_sign_secretkeybytes(void); + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_crypto_sign_publickeybytes(void); + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_crypto_sign_bytes(void); + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_crypto_sign_seedbytes(void); + +/* + * Generates a SPHINCS+ key pair given a seed. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed); + +/* + * Generates a SPHINCS+ key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk); + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk); + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk); + +#endif diff --git a/crypto_sign/sphincs-shake256-256f-robust/clean/fors.c b/crypto_sign/sphincs-shake256-256f-robust/clean/fors.c new file mode 100644 index 00000000..1e9b4a82 --- /dev/null +++ b/crypto_sign/sphincs-shake256-256f-robust/clean/fors.c @@ -0,0 +1,164 @@ +#include +#include +#include + +#include "address.h" +#include "fors.h" +#include "hash.h" +#include "thash.h" +#include "utils.h" + +static void fors_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_prf_addr( + sk, sk_seed, fors_leaf_addr); +} + +static void fors_sk_to_leaf(unsigned char *leaf, const unsigned char *sk, + const unsigned char *pub_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_thash_1( + leaf, sk, pub_seed, fors_leaf_addr); +} + +static void fors_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t fors_tree_addr[8]) { + uint32_t fors_leaf_addr[8] = {0}; + + /* Only copy the parts that must be kept in fors_leaf_addr. */ + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_copy_keypair_addr( + fors_leaf_addr, fors_tree_addr); + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_type( + fors_leaf_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_tree_index( + fors_leaf_addr, addr_idx); + + fors_gen_sk(leaf, sk_seed, fors_leaf_addr); + fors_sk_to_leaf(leaf, leaf, pub_seed, fors_leaf_addr); +} + +/** + * Interprets m as SPX_FORS_HEIGHT-bit unsigned integers. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + * Assumes indices has space for SPX_FORS_TREES integers. + */ +static void message_to_indices(uint32_t *indices, const unsigned char *m) { + unsigned int i, j; + unsigned int offset = 0; + + for (i = 0; i < SPX_FORS_TREES; i++) { + indices[i] = 0; + for (j = 0; j < SPX_FORS_HEIGHT; j++) { + indices[i] ^= (((uint32_t)m[offset >> 3] >> (offset & 0x7)) & 0x1) << j; + offset++; + } + } +} + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Include the secret key part that produces the selected leaf node. */ + fors_gen_sk(sig, sk_seed, fors_tree_addr); + sig += SPX_N; + + /* Compute the authentication path for this leaf node. */ + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_treehash_FORS_HEIGHT( + roots + i * SPX_N, sig, sk_seed, pub_seed, + indices[i], idx_offset, fors_gen_leaf, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + unsigned char leaf[SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Derive the leaf from the included secret key part. */ + fors_sk_to_leaf(leaf, sig, pub_seed, fors_tree_addr); + sig += SPX_N; + + /* Derive the corresponding root node of this tree. */ + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_compute_root( + roots + i * SPX_N, leaf, indices[i], idx_offset, sig, + SPX_FORS_HEIGHT, pub_seed, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} diff --git a/crypto_sign/sphincs-shake256-256f-robust/clean/fors.h b/crypto_sign/sphincs-shake256-256f-robust/clean/fors.h new file mode 100644 index 00000000..c672b984 --- /dev/null +++ b/crypto_sign/sphincs-shake256-256f-robust/clean/fors.h @@ -0,0 +1,30 @@ +#ifndef SPX_FORS_H +#define SPX_FORS_H + +#include + +#include "params.h" + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]); + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-shake256-256f-robust/clean/hash.h b/crypto_sign/sphincs-shake256-256f-robust/clean/hash.h new file mode 100644 index 00000000..6ba0da31 --- /dev/null +++ b/crypto_sign/sphincs-shake256-256f-robust/clean/hash.h @@ -0,0 +1,22 @@ +#ifndef SPX_HASH_H +#define SPX_HASH_H + +#include + +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed); + +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen); + +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen); + +#endif diff --git a/crypto_sign/sphincs-shake256-256f-robust/clean/hash_shake256.c b/crypto_sign/sphincs-shake256-256f-robust/clean/hash_shake256.c new file mode 100644 index 00000000..308338b4 --- /dev/null +++ b/crypto_sign/sphincs-shake256-256f-robust/clean/hash_shake256.c @@ -0,0 +1,87 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "utils.h" + +#include "fips202.h" + +/* For SHAKE256, there is no immediate reason to initialize at the start, + so this function is an empty operation. */ +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed) { + (void)pub_seed; /* Suppress an 'unused parameter' warning. */ + (void)sk_seed; /* Suppress an 'unused parameter' warning. */ +} + +/* + * Computes PRF(key, addr), given a secret key of SPX_N bytes and an address + */ +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]) { + unsigned char buf[SPX_N + SPX_ADDR_BYTES]; + + memcpy(buf, key, SPX_N); + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_addr_to_bytes(buf + SPX_N, addr); + + shake256(out, SPX_N, buf, SPX_N + SPX_ADDR_BYTES); +} + +/** + * Computes the message-dependent randomness R, using a secret seed and an + * optional randomization value as well as the message. + */ +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen) { + uint64_t s_inc[26]; + + shake256_inc_init(s_inc); + shake256_inc_absorb(s_inc, sk_prf, SPX_N); + shake256_inc_absorb(s_inc, optrand, SPX_N); + shake256_inc_absorb(s_inc, m, mlen); + shake256_inc_finalize(s_inc); + shake256_inc_squeeze(R, SPX_N, s_inc); +} + +/** + * Computes the message hash using R, the public key, and the message. + * Outputs the message digest and the index of the leaf. The index is split in + * the tree index and the leaf index, for convenient copying to an address. + */ +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen) { +#define SPX_TREE_BITS (SPX_TREE_HEIGHT * (SPX_D - 1)) +#define SPX_TREE_BYTES ((SPX_TREE_BITS + 7) / 8) +#define SPX_LEAF_BITS SPX_TREE_HEIGHT +#define SPX_LEAF_BYTES ((SPX_LEAF_BITS + 7) / 8) +#define SPX_DGST_BYTES (SPX_FORS_MSG_BYTES + SPX_TREE_BYTES + SPX_LEAF_BYTES) + + unsigned char buf[SPX_DGST_BYTES]; + unsigned char *bufp = buf; + uint64_t s_inc[26]; + + shake256_inc_init(s_inc); + shake256_inc_absorb(s_inc, R, SPX_N); + shake256_inc_absorb(s_inc, pk, SPX_PK_BYTES); + shake256_inc_absorb(s_inc, m, mlen); + shake256_inc_finalize(s_inc); + shake256_inc_squeeze(buf, SPX_DGST_BYTES, s_inc); + + memcpy(digest, bufp, SPX_FORS_MSG_BYTES); + bufp += SPX_FORS_MSG_BYTES; + + *tree = PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_bytes_to_ull( + bufp, SPX_TREE_BYTES); + *tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS); + bufp += SPX_TREE_BYTES; + + *leaf_idx = (uint32_t)PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_bytes_to_ull( + bufp, SPX_LEAF_BYTES); + *leaf_idx &= (~(uint32_t)0) >> (32 - SPX_LEAF_BITS); +} diff --git a/crypto_sign/sphincs-shake256-256f-robust/clean/params.h b/crypto_sign/sphincs-shake256-256f-robust/clean/params.h new file mode 100644 index 00000000..c2ad0531 --- /dev/null +++ b/crypto_sign/sphincs-shake256-256f-robust/clean/params.h @@ -0,0 +1,53 @@ +#ifndef SPX_PARAMS_H +#define SPX_PARAMS_H + +/* Hash output length in bytes. */ +#define SPX_N 32 +/* Height of the hypertree. */ +#define SPX_FULL_HEIGHT 68 +/* Number of subtree layer. */ +#define SPX_D 17 +/* FORS tree dimensions. */ +#define SPX_FORS_HEIGHT 10 +#define SPX_FORS_TREES 30 +/* Winternitz parameter, */ +#define SPX_WOTS_W 16 + +/* The hash function is defined by linking a different hash.c file, as opposed + to setting a #define constant. */ + +/* For clarity */ +#define SPX_ADDR_BYTES 32 + +/* WOTS parameters. */ +#define SPX_WOTS_LOGW 4 + +#define SPX_WOTS_LEN1 (8 * SPX_N / SPX_WOTS_LOGW) + +/* SPX_WOTS_LEN2 is floor(log(len_1 * (w - 1)) / log(w)) + 1; we precompute */ +#define SPX_WOTS_LEN2 3 + +#define SPX_WOTS_LEN (SPX_WOTS_LEN1 + SPX_WOTS_LEN2) +#define SPX_WOTS_BYTES (SPX_WOTS_LEN * SPX_N) +#define SPX_WOTS_PK_BYTES SPX_WOTS_BYTES + +/* Subtree size. */ +#define SPX_TREE_HEIGHT (SPX_FULL_HEIGHT / SPX_D) + +/* FORS parameters. */ +#define SPX_FORS_MSG_BYTES ((SPX_FORS_HEIGHT * SPX_FORS_TREES + 7) / 8) +#define SPX_FORS_BYTES ((SPX_FORS_HEIGHT + 1) * SPX_FORS_TREES * SPX_N) +#define SPX_FORS_PK_BYTES SPX_N + +/* Resulting SPX sizes. */ +#define SPX_BYTES (SPX_N + SPX_FORS_BYTES + SPX_D * SPX_WOTS_BYTES +\ + SPX_FULL_HEIGHT * SPX_N) +#define SPX_PK_BYTES (2 * SPX_N) +#define SPX_SK_BYTES (2 * SPX_N + SPX_PK_BYTES) + +/* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ +#define SPX_OPTRAND_BYTES 32 + +#endif diff --git a/crypto_sign/sphincs-shake256-256f-robust/clean/sign.c b/crypto_sign/sphincs-shake256-256f-robust/clean/sign.c new file mode 100644 index 00000000..731c0fc3 --- /dev/null +++ b/crypto_sign/sphincs-shake256-256f-robust/clean/sign.c @@ -0,0 +1,344 @@ +#include +#include +#include + +#include "address.h" +#include "api.h" +#include "fors.h" +#include "hash.h" +#include "params.h" +#include "randombytes.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +/** + * Computes the leaf at a given address. First generates the WOTS key pair, + * then computes leaf by hashing horizontally. + */ +static void wots_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t tree_addr[8]) { + unsigned char pk[SPX_WOTS_BYTES]; + uint32_t wots_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_keypair_addr( + wots_addr, addr_idx); + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_wots_gen_pk( + pk, sk_seed, pub_seed, wots_addr); + + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_thash_WOTS_LEN( + leaf, pk, pub_seed, wots_pk_addr); +} + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_crypto_sign_secretkeybytes(void) { + return PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_CRYPTO_SECRETKEYBYTES; +} + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_crypto_sign_publickeybytes(void) { + return PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES; +} + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_crypto_sign_bytes(void) { + return PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_CRYPTO_BYTES; +} + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_crypto_sign_seedbytes(void) { + return PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_CRYPTO_SEEDBYTES; +} + +/* + * Generates an SPX key pair given a seed of length + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed) { + /* We do not need the auth path in key generation, but it simplifies the + code to have just one treehash routine that computes both root and path + in one function. */ + unsigned char auth_path[SPX_TREE_HEIGHT * SPX_N]; + uint32_t top_tree_addr[8] = {0}; + + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_layer_addr( + top_tree_addr, SPX_D - 1); + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_type( + top_tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */ + memcpy(sk, seed, PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_CRYPTO_SEEDBYTES); + + memcpy(pk, sk + 2 * SPX_N, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_initialize_hash_function(pk, sk); + + /* Compute root node of the top-most subtree. */ + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_treehash_TREE_HEIGHT( + sk + 3 * SPX_N, auth_path, sk, sk + 2 * SPX_N, 0, 0, + wots_gen_leaf, top_tree_addr); + + memcpy(pk + SPX_N, sk + 3 * SPX_N, SPX_N); + + return 0; +} + +/* + * Generates an SPX key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk) { + unsigned char seed[PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_CRYPTO_SEEDBYTES]; + randombytes(seed, PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_CRYPTO_SEEDBYTES); + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_crypto_sign_seed_keypair( + pk, sk, seed); + + return 0; +} + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + const unsigned char *sk_seed = sk; + const unsigned char *sk_prf = sk + SPX_N; + const unsigned char *pk = sk + 2 * SPX_N; + const unsigned char *pub_seed = pk; + + unsigned char optrand[SPX_N]; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char root[SPX_N]; + uint32_t i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_initialize_hash_function( + pub_seed, sk_seed); + + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ + randombytes(optrand, SPX_N); + /* Compute the digest randomization value. */ + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_gen_message_random( + sig, sk_prf, optrand, m, mlen); + + /* Derive the message digest and leaf index from R, PK and M. */ + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Sign the message hash using FORS. */ + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_fors_sign( + sig, root, mhash, sk_seed, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Compute a WOTS signature. */ + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_wots_sign( + sig, root, sk_seed, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the authentication path for the used WOTS leaf. */ + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_treehash_TREE_HEIGHT( + root, sig, sk_seed, pub_seed, idx_leaf, 0, + wots_gen_leaf, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + *siglen = SPX_BYTES; + + return 0; +} + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk) { + const unsigned char *pub_seed = pk; + const unsigned char *pub_root = pk + SPX_N; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char wots_pk[SPX_WOTS_BYTES]; + unsigned char root[SPX_N]; + unsigned char leaf[SPX_N]; + unsigned int i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + if (siglen != SPX_BYTES) { + return -1; + } + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_initialize_hash_function( + pub_seed, NULL); + + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + /* Derive the message digest and leaf index from R || PK || M. */ + /* The additional SPX_N is a result of the hash domain separator. */ + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + /* Layer correctly defaults to 0, so no need to set_layer_addr */ + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_fors_pk_from_sig( + root, sig, mhash, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + /* For each subtree.. */ + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + + /* The WOTS public key is only correct if the signature was correct. */ + /* Initially, root is the FORS pk, but on subsequent iterations it is + the root of the subtree below the currently processed subtree. */ + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_wots_pk_from_sig( + wots_pk, sig, root, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the leaf node using the WOTS public key. */ + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_thash_WOTS_LEN( + leaf, wots_pk, pub_seed, wots_pk_addr); + + /* Compute the root node of this subtree. */ + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_compute_root( + root, leaf, idx_leaf, 0, sig, SPX_TREE_HEIGHT, + pub_seed, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + /* Check if the root node equals the root node in the public key. */ + if (memcmp(root, pub_root, SPX_N) != 0) { + return -1; + } + + return 0; +} + + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + size_t siglen; + + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_crypto_sign_signature( + sm, &siglen, m, mlen, sk); + + memmove(sm + SPX_BYTES, m, mlen); + *smlen = siglen + mlen; + + return 0; +} + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk) { + /* The API caller does not necessarily know what size a signature should be + but SPHINCS+ signatures are always exactly SPX_BYTES. */ + if (smlen < SPX_BYTES) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + *mlen = smlen - SPX_BYTES; + + if (PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_crypto_sign_verify( + sm, SPX_BYTES, sm + SPX_BYTES, *mlen, pk)) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + /* If verification was successful, move the message to the right place. */ + memmove(m, sm + SPX_BYTES, *mlen); + + return 0; +} diff --git a/crypto_sign/sphincs-shake256-256f-robust/clean/thash.h b/crypto_sign/sphincs-shake256-256f-robust/clean/thash.h new file mode 100644 index 00000000..d65a034d --- /dev/null +++ b/crypto_sign/sphincs-shake256-256f-robust/clean/thash.h @@ -0,0 +1,22 @@ +#ifndef SPX_THASH_H +#define SPX_THASH_H + +#include + +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-shake256-256f-robust/clean/thash_shake256_robust.c b/crypto_sign/sphincs-shake256-256f-robust/clean/thash_shake256_robust.c new file mode 100644 index 00000000..41351d90 --- /dev/null +++ b/crypto_sign/sphincs-shake256-256f-robust/clean/thash_shake256_robust.c @@ -0,0 +1,69 @@ +#include +#include + +#include "address.h" +#include "params.h" +#include "thash.h" + +#include "fips202.h" + +/** + * Takes an array of inblocks concatenated arrays of SPX_N bytes. + */ +static void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_thash( + unsigned char *out, unsigned char *buf, + const unsigned char *in, unsigned int inblocks, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char *bitmask = buf + SPX_N + SPX_ADDR_BYTES; + unsigned int i; + + memcpy(buf, pub_seed, SPX_N); + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_addr_to_bytes(buf + SPX_N, addr); + + shake256(bitmask, inblocks * SPX_N, buf, SPX_N + SPX_ADDR_BYTES); + + for (i = 0; i < inblocks * SPX_N; i++) { + buf[SPX_N + SPX_ADDR_BYTES + i] = in[i] ^ bitmask[i]; + } + + shake256(out, SPX_N, buf, SPX_N + SPX_ADDR_BYTES + inblocks * SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_ADDR_BYTES + 1 * SPX_N]; + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_thash( + out, buf, in, 1, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_ADDR_BYTES + 2 * SPX_N]; + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_thash( + out, buf, in, 2, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_ADDR_BYTES + SPX_WOTS_LEN * SPX_N]; + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_thash( + out, buf, in, SPX_WOTS_LEN, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_ADDR_BYTES + SPX_FORS_TREES * SPX_N]; + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_thash( + out, buf, in, SPX_FORS_TREES, pub_seed, addr); +} diff --git a/crypto_sign/sphincs-shake256-256f-robust/clean/utils.c b/crypto_sign/sphincs-shake256-256f-robust/clean/utils.c new file mode 100644 index 00000000..88f04db3 --- /dev/null +++ b/crypto_sign/sphincs-shake256-256f-robust/clean/utils.c @@ -0,0 +1,192 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in) { + + /* Iterate over out in decreasing order, for big-endianness. */ + for (size_t i = outlen; i > 0; i--) { + out[i - 1] = in & 0xff; + in = in >> 8; + } +} + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen) { + unsigned long long retval = 0; + + for (size_t i = 0; i < inlen; i++) { + retval |= ((unsigned long long)in[i]) << (8 * (inlen - 1 - i)); + } + return retval; +} + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + unsigned char buffer[2 * SPX_N]; + + /* If leaf_idx is odd (last bit = 1), current path element is a right child + and auth_path has to go left. Otherwise it is the other way around. */ + if (leaf_idx & 1) { + memcpy(buffer + SPX_N, leaf, SPX_N); + memcpy(buffer, auth_path, SPX_N); + } else { + memcpy(buffer, leaf, SPX_N); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + + for (i = 0; i < tree_height - 1; i++) { + leaf_idx >>= 1; + idx_offset >>= 1; + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_tree_height(addr, i + 1); + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + + /* Pick the right or left neighbor, depending on parity of the node. */ + if (leaf_idx & 1) { + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_thash_2( + buffer + SPX_N, buffer, pub_seed, addr); + memcpy(buffer, auth_path, SPX_N); + } else { + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_thash_2( + buffer, buffer, pub_seed, addr); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + } + + /* The last iteration is exceptional; we do not copy an auth_path node. */ + leaf_idx >>= 1; + idx_offset >>= 1; + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_tree_height(addr, tree_height); + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_thash_2( + root, buffer, pub_seed, addr); +} + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +static void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_treehash( + unsigned char *root, unsigned char *auth_path, + unsigned char *stack, unsigned int *heights, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned int offset = 0; + uint32_t idx; + uint32_t tree_idx; + + for (idx = 0; idx < (uint32_t)(1 << tree_height); idx++) { + /* Add the next leaf node to the stack. */ + gen_leaf(stack + offset * SPX_N, + sk_seed, pub_seed, idx + idx_offset, tree_addr); + offset++; + heights[offset - 1] = 0; + + /* If this is a node we need for the auth path.. */ + if ((leaf_idx ^ 0x1) == idx) { + memcpy(auth_path, stack + (offset - 1)*SPX_N, SPX_N); + } + + /* While the top-most nodes are of equal height.. */ + while (offset >= 2 && heights[offset - 1] == heights[offset - 2]) { + /* Compute index of the new node, in the next layer. */ + tree_idx = (idx >> (heights[offset - 1] + 1)); + + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_tree_height( + tree_addr, heights[offset - 1] + 1); + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_tree_index( + tree_addr, tree_idx + (idx_offset >> (heights[offset - 1] + 1))); + /* Hash the top-most nodes from the stack together. */ + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_thash_2( + stack + (offset - 2)*SPX_N, stack + (offset - 2)*SPX_N, + pub_seed, tree_addr); + offset--; + /* Note that the top-most node is now one layer higher. */ + heights[offset - 1]++; + + /* If this is a node we need for the auth path.. */ + if (((leaf_idx >> heights[offset - 1]) ^ 0x1) == tree_idx) { + memcpy(auth_path + heights[offset - 1]*SPX_N, + stack + (offset - 1)*SPX_N, SPX_N); + } + } + } + memcpy(root, stack, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_FORS_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_FORS_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_FORS_HEIGHT, gen_leaf, tree_addr); +} + +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_TREE_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_TREE_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_TREE_HEIGHT, gen_leaf, tree_addr); +} diff --git a/crypto_sign/sphincs-shake256-256f-robust/clean/utils.h b/crypto_sign/sphincs-shake256-256f-robust/clean/utils.h new file mode 100644 index 00000000..0d420c54 --- /dev/null +++ b/crypto_sign/sphincs-shake256-256f-robust/clean/utils.h @@ -0,0 +1,60 @@ +#ifndef SPX_UTILS_H +#define SPX_UTILS_H + +#include "params.h" +#include +#include + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in); + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen); + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-shake256-256f-robust/clean/wots.c b/crypto_sign/sphincs-shake256-256f-robust/clean/wots.c new file mode 100644 index 00000000..27c5c98b --- /dev/null +++ b/crypto_sign/sphincs-shake256-256f-robust/clean/wots.c @@ -0,0 +1,161 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +// TODO clarify address expectations, and make them more uniform. +// TODO i.e. do we expect types to be set already? +// TODO and do we expect modifications or copies? + +/** + * Computes the starting value for a chain, i.e. the secret key. + * Expects the address to be complete up to the chain address. + */ +static void wots_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t wots_addr[8]) { + /* Make sure that the hash address is actually zeroed. */ + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_hash_addr(wots_addr, 0); + + /* Generate sk element. */ + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_prf_addr(sk, sk_seed, wots_addr); +} + +/** + * Computes the chaining function. + * out and in have to be n-byte arrays. + * + * Interprets in as start-th value of the chain. + * addr has to contain the address of the chain. + */ +static void gen_chain(unsigned char *out, const unsigned char *in, + unsigned int start, unsigned int steps, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + /* Initialize out with the value at position 'start'. */ + memcpy(out, in, SPX_N); + + /* Iterate 'steps' calls to the hash function. */ + for (i = start; i < (start + steps) && i < SPX_WOTS_W; i++) { + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_hash_addr(addr, i); + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_thash_1( + out, out, pub_seed, addr); + } +} + +/** + * base_w algorithm as described in draft. + * Interprets an array of bytes as integers in base w. + * This only works when log_w is a divisor of 8. + */ +static void base_w(unsigned int *output, const size_t out_len, + const unsigned char *input) { + size_t in = 0; + size_t out = 0; + unsigned char total = 0; + unsigned int bits = 0; + size_t consumed; + + for (consumed = 0; consumed < out_len; consumed++) { + if (bits == 0) { + total = input[in]; + in++; + bits += 8; + } + bits -= SPX_WOTS_LOGW; + output[out] = (unsigned int)((total >> bits) & (SPX_WOTS_W - 1)); + out++; + } +} + +/* Computes the WOTS+ checksum over a message (in base_w). */ +static void wots_checksum(unsigned int *csum_base_w, + const unsigned int *msg_base_w) { + unsigned int csum = 0; + unsigned char csum_bytes[(SPX_WOTS_LEN2 * SPX_WOTS_LOGW + 7) / 8]; + unsigned int i; + + /* Compute checksum. */ + for (i = 0; i < SPX_WOTS_LEN1; i++) { + csum += SPX_WOTS_W - 1 - msg_base_w[i]; + } + + /* Convert checksum to base_w. */ + /* Make sure expected empty zero bits are the least significant bits. */ + csum = csum << (8 - ((SPX_WOTS_LEN2 * SPX_WOTS_LOGW) % 8)); + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_ull_to_bytes( + csum_bytes, sizeof(csum_bytes), csum); + base_w(csum_base_w, SPX_WOTS_LEN2, csum_bytes); +} + +/* Takes a message and derives the matching chain lengths. */ +static void chain_lengths(unsigned int *lengths, const unsigned char *msg) { + base_w(lengths, SPX_WOTS_LEN1, msg); + wots_checksum(lengths + SPX_WOTS_LEN1, lengths); +} + +/** + * WOTS key generation. Takes a 32 byte sk_seed, expands it to WOTS private key + * elements and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(pk + i * SPX_N, sk_seed, addr); + gen_chain(pk + i * SPX_N, pk + i * SPX_N, + 0, SPX_WOTS_W - 1, pub_seed, addr); + } +} + +/** + * Takes a n-byte message and the 32-byte sk_see to compute a signature 'sig'. + */ +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(sig + i * SPX_N, sk_seed, addr); + gen_chain(sig + i * SPX_N, sig + i * SPX_N, 0, lengths[i], pub_seed, addr); + } +} + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_set_chain_addr(addr, i); + gen_chain(pk + i * SPX_N, sig + i * SPX_N, + lengths[i], SPX_WOTS_W - 1 - lengths[i], pub_seed, addr); + } +} diff --git a/crypto_sign/sphincs-shake256-256f-robust/clean/wots.h b/crypto_sign/sphincs-shake256-256f-robust/clean/wots.h new file mode 100644 index 00000000..ee5f83a4 --- /dev/null +++ b/crypto_sign/sphincs-shake256-256f-robust/clean/wots.h @@ -0,0 +1,38 @@ +#ifndef SPX_WOTS_H +#define SPX_WOTS_H + +#include "params.h" +#include + +/** + * WOTS key generation. Takes a 32 byte seed for the private key, expands it to + * a full WOTS private key and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * Takes a n-byte message and the 32-byte seed for the private key to compute a + * signature that is placed at 'sig'. + */ +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]); + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-shake256-256f-simple/META.yml b/crypto_sign/sphincs-shake256-256f-simple/META.yml new file mode 100644 index 00000000..37d2134b --- /dev/null +++ b/crypto_sign/sphincs-shake256-256f-simple/META.yml @@ -0,0 +1,27 @@ +name: SPHINCS+ +type: signature +claimed-nist-level: 5 +length-public-key: 64 +length-secret-key: 128 +length-signature: 49216 +testvectors-sha256: 1b261fc7394dc847349c07bde922ac028aad94c534f51341f8202670558ed27a +principal-submitter: Andreas Hülsing +auxiliary-submitters: + - Jean-Philippe Aumasson + - Daniel J. Bernstein, + - Christoph Dobraunig + - Maria Eichlseder + - Scott Fluhrer + - Stefan-Lukas Gazdag + - Panos Kampanakis + - Stefan Kölbl + - Tanja Lange + - Martin M. Lauridsen + - Florian Mendel + - Ruben Niederhagen + - Christian Rechberger + - Joost Rijneveld + - Peter Schwabe +implementations: + - name: clean + version: https://github.com/sphincs/sphincsplus/commit/77755c94d0bc744478044d6efbb888dc13156441 diff --git a/crypto_sign/sphincs-shake256-256f-simple/clean/LICENSE b/crypto_sign/sphincs-shake256-256f-simple/clean/LICENSE new file mode 100644 index 00000000..670154e3 --- /dev/null +++ b/crypto_sign/sphincs-shake256-256f-simple/clean/LICENSE @@ -0,0 +1,116 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + diff --git a/crypto_sign/sphincs-shake256-256f-simple/clean/Makefile b/crypto_sign/sphincs-shake256-256f-simple/clean/Makefile new file mode 100644 index 00000000..2d90b6a9 --- /dev/null +++ b/crypto_sign/sphincs-shake256-256f-simple/clean/Makefile @@ -0,0 +1,20 @@ +# This Makefile can be used with GNU Make or BSD Make + +LIB=libsphincs-shake256-256f-simple_clean.a + +HEADERS = params.h address.h wots.h utils.h fors.h api.h hash.h thash.h +OBJECTS = address.o wots.o utils.o fors.o sign.o hash_shake256.o thash_shake256_simple.o + +CFLAGS=-O3 -Wall -Wconversion -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) + +all: $(LIB) + +%.o: %.c $(HEADERS) + $(CC) $(CFLAGS) -c -o $@ $< + +$(LIB): $(OBJECTS) + $(AR) -r $@ $(OBJECTS) + +clean: + $(RM) $(OBJECTS) + $(RM) $(LIB) diff --git a/crypto_sign/sphincs-shake256-256f-simple/clean/Makefile.Microsoft_nmake b/crypto_sign/sphincs-shake256-256f-simple/clean/Makefile.Microsoft_nmake new file mode 100644 index 00000000..bc28e8e4 --- /dev/null +++ b/crypto_sign/sphincs-shake256-256f-simple/clean/Makefile.Microsoft_nmake @@ -0,0 +1,19 @@ +# This Makefile can be used with Microsoft Visual Studio's nmake using the command: +# nmake /f Makefile.Microsoft_nmake + +LIBRARY=libsphincs-shake256-256f-simple_clean.lib +OBJECTS=address.obj wots.obj utils.obj fors.obj sign.obj hash_shake256.obj thash_shake256_simple.obj + +CFLAGS=/nologo /I ..\..\..\common /W4 /WX + +all: $(LIBRARY) + +# Make sure objects are recompiled if headers change. +$(OBJECTS): *.h + +$(LIBRARY): $(OBJECTS) + LIB.EXE /NOLOGO /WX /OUT:$@ $** + +clean: + -DEL $(OBJECTS) + -DEL $(LIBRARY) diff --git a/crypto_sign/sphincs-shake256-256f-simple/clean/address.c b/crypto_sign/sphincs-shake256-256f-simple/clean/address.c new file mode 100644 index 00000000..9153447f --- /dev/null +++ b/crypto_sign/sphincs-shake256-256f-simple/clean/address.c @@ -0,0 +1,78 @@ +#include + +#include "address.h" +#include "params.h" +#include "utils.h" + +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]) { + int i; + + for (i = 0; i < 8; i++) { + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_ull_to_bytes( + bytes + i * 4, 4, addr[i]); + } +} + +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer) { + addr[0] = layer; +} + +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree) { + addr[1] = 0; + addr[2] = (uint32_t) (tree >> 32); + addr[3] = (uint32_t) tree; +} + +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_type( + uint32_t addr[8], uint32_t type) { + addr[4] = type; +} + +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; +} + +/* These functions are used for OTS addresses. */ + +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair) { + addr[5] = keypair; +} + +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; + out[5] = in[5]; +} + +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain) { + addr[6] = chain; +} + +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash) { + addr[7] = hash; +} + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height) { + addr[6] = tree_height; +} + +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index) { + addr[7] = tree_index; +} diff --git a/crypto_sign/sphincs-shake256-256f-simple/clean/address.h b/crypto_sign/sphincs-shake256-256f-simple/clean/address.h new file mode 100644 index 00000000..ca717d1d --- /dev/null +++ b/crypto_sign/sphincs-shake256-256f-simple/clean/address.h @@ -0,0 +1,50 @@ +#ifndef SPX_ADDRESS_H +#define SPX_ADDRESS_H + +#include + +#define SPX_ADDR_TYPE_WOTS 0 +#define SPX_ADDR_TYPE_WOTSPK 1 +#define SPX_ADDR_TYPE_HASHTREE 2 +#define SPX_ADDR_TYPE_FORSTREE 3 +#define SPX_ADDR_TYPE_FORSPK 4 + +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer); + +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree); + +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_type( + uint32_t addr[8], uint32_t type); + +/* Copies the layer and tree part of one address into the other */ +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for WOTS and FORS addresses. */ + +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair); + +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain); + +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash); + +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height); + +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index); + +#endif diff --git a/crypto_sign/sphincs-shake256-256f-simple/clean/api.h b/crypto_sign/sphincs-shake256-256f-simple/clean/api.h new file mode 100644 index 00000000..92d192b3 --- /dev/null +++ b/crypto_sign/sphincs-shake256-256f-simple/clean/api.h @@ -0,0 +1,78 @@ +#ifndef PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_API_H +#define PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_API_H + +#include +#include + +#define PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_CRYPTO_ALGNAME "SPHINCS+" + +#define PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_CRYPTO_SECRETKEYBYTES 128 +#define PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES 64 +#define PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_CRYPTO_BYTES 49216 +#define PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_CRYPTO_SEEDBYTES 96 + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_crypto_sign_secretkeybytes(void); + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_crypto_sign_publickeybytes(void); + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_crypto_sign_bytes(void); + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_crypto_sign_seedbytes(void); + +/* + * Generates a SPHINCS+ key pair given a seed. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed); + +/* + * Generates a SPHINCS+ key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk); + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk); + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk); + +#endif diff --git a/crypto_sign/sphincs-shake256-256f-simple/clean/fors.c b/crypto_sign/sphincs-shake256-256f-simple/clean/fors.c new file mode 100644 index 00000000..b4c11d37 --- /dev/null +++ b/crypto_sign/sphincs-shake256-256f-simple/clean/fors.c @@ -0,0 +1,164 @@ +#include +#include +#include + +#include "address.h" +#include "fors.h" +#include "hash.h" +#include "thash.h" +#include "utils.h" + +static void fors_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_prf_addr( + sk, sk_seed, fors_leaf_addr); +} + +static void fors_sk_to_leaf(unsigned char *leaf, const unsigned char *sk, + const unsigned char *pub_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_thash_1( + leaf, sk, pub_seed, fors_leaf_addr); +} + +static void fors_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t fors_tree_addr[8]) { + uint32_t fors_leaf_addr[8] = {0}; + + /* Only copy the parts that must be kept in fors_leaf_addr. */ + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_copy_keypair_addr( + fors_leaf_addr, fors_tree_addr); + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_type( + fors_leaf_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_tree_index( + fors_leaf_addr, addr_idx); + + fors_gen_sk(leaf, sk_seed, fors_leaf_addr); + fors_sk_to_leaf(leaf, leaf, pub_seed, fors_leaf_addr); +} + +/** + * Interprets m as SPX_FORS_HEIGHT-bit unsigned integers. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + * Assumes indices has space for SPX_FORS_TREES integers. + */ +static void message_to_indices(uint32_t *indices, const unsigned char *m) { + unsigned int i, j; + unsigned int offset = 0; + + for (i = 0; i < SPX_FORS_TREES; i++) { + indices[i] = 0; + for (j = 0; j < SPX_FORS_HEIGHT; j++) { + indices[i] ^= (((uint32_t)m[offset >> 3] >> (offset & 0x7)) & 0x1) << j; + offset++; + } + } +} + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Include the secret key part that produces the selected leaf node. */ + fors_gen_sk(sig, sk_seed, fors_tree_addr); + sig += SPX_N; + + /* Compute the authentication path for this leaf node. */ + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_treehash_FORS_HEIGHT( + roots + i * SPX_N, sig, sk_seed, pub_seed, + indices[i], idx_offset, fors_gen_leaf, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + unsigned char leaf[SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Derive the leaf from the included secret key part. */ + fors_sk_to_leaf(leaf, sig, pub_seed, fors_tree_addr); + sig += SPX_N; + + /* Derive the corresponding root node of this tree. */ + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_compute_root( + roots + i * SPX_N, leaf, indices[i], idx_offset, sig, + SPX_FORS_HEIGHT, pub_seed, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} diff --git a/crypto_sign/sphincs-shake256-256f-simple/clean/fors.h b/crypto_sign/sphincs-shake256-256f-simple/clean/fors.h new file mode 100644 index 00000000..c1e8a3f4 --- /dev/null +++ b/crypto_sign/sphincs-shake256-256f-simple/clean/fors.h @@ -0,0 +1,30 @@ +#ifndef SPX_FORS_H +#define SPX_FORS_H + +#include + +#include "params.h" + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]); + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-shake256-256f-simple/clean/hash.h b/crypto_sign/sphincs-shake256-256f-simple/clean/hash.h new file mode 100644 index 00000000..fce39a07 --- /dev/null +++ b/crypto_sign/sphincs-shake256-256f-simple/clean/hash.h @@ -0,0 +1,22 @@ +#ifndef SPX_HASH_H +#define SPX_HASH_H + +#include + +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed); + +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen); + +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen); + +#endif diff --git a/crypto_sign/sphincs-shake256-256f-simple/clean/hash_shake256.c b/crypto_sign/sphincs-shake256-256f-simple/clean/hash_shake256.c new file mode 100644 index 00000000..db8875d7 --- /dev/null +++ b/crypto_sign/sphincs-shake256-256f-simple/clean/hash_shake256.c @@ -0,0 +1,87 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "utils.h" + +#include "fips202.h" + +/* For SHAKE256, there is no immediate reason to initialize at the start, + so this function is an empty operation. */ +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed) { + (void)pub_seed; /* Suppress an 'unused parameter' warning. */ + (void)sk_seed; /* Suppress an 'unused parameter' warning. */ +} + +/* + * Computes PRF(key, addr), given a secret key of SPX_N bytes and an address + */ +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]) { + unsigned char buf[SPX_N + SPX_ADDR_BYTES]; + + memcpy(buf, key, SPX_N); + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_addr_to_bytes(buf + SPX_N, addr); + + shake256(out, SPX_N, buf, SPX_N + SPX_ADDR_BYTES); +} + +/** + * Computes the message-dependent randomness R, using a secret seed and an + * optional randomization value as well as the message. + */ +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen) { + uint64_t s_inc[26]; + + shake256_inc_init(s_inc); + shake256_inc_absorb(s_inc, sk_prf, SPX_N); + shake256_inc_absorb(s_inc, optrand, SPX_N); + shake256_inc_absorb(s_inc, m, mlen); + shake256_inc_finalize(s_inc); + shake256_inc_squeeze(R, SPX_N, s_inc); +} + +/** + * Computes the message hash using R, the public key, and the message. + * Outputs the message digest and the index of the leaf. The index is split in + * the tree index and the leaf index, for convenient copying to an address. + */ +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen) { +#define SPX_TREE_BITS (SPX_TREE_HEIGHT * (SPX_D - 1)) +#define SPX_TREE_BYTES ((SPX_TREE_BITS + 7) / 8) +#define SPX_LEAF_BITS SPX_TREE_HEIGHT +#define SPX_LEAF_BYTES ((SPX_LEAF_BITS + 7) / 8) +#define SPX_DGST_BYTES (SPX_FORS_MSG_BYTES + SPX_TREE_BYTES + SPX_LEAF_BYTES) + + unsigned char buf[SPX_DGST_BYTES]; + unsigned char *bufp = buf; + uint64_t s_inc[26]; + + shake256_inc_init(s_inc); + shake256_inc_absorb(s_inc, R, SPX_N); + shake256_inc_absorb(s_inc, pk, SPX_PK_BYTES); + shake256_inc_absorb(s_inc, m, mlen); + shake256_inc_finalize(s_inc); + shake256_inc_squeeze(buf, SPX_DGST_BYTES, s_inc); + + memcpy(digest, bufp, SPX_FORS_MSG_BYTES); + bufp += SPX_FORS_MSG_BYTES; + + *tree = PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_bytes_to_ull( + bufp, SPX_TREE_BYTES); + *tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS); + bufp += SPX_TREE_BYTES; + + *leaf_idx = (uint32_t)PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_bytes_to_ull( + bufp, SPX_LEAF_BYTES); + *leaf_idx &= (~(uint32_t)0) >> (32 - SPX_LEAF_BITS); +} diff --git a/crypto_sign/sphincs-shake256-256f-simple/clean/params.h b/crypto_sign/sphincs-shake256-256f-simple/clean/params.h new file mode 100644 index 00000000..c2ad0531 --- /dev/null +++ b/crypto_sign/sphincs-shake256-256f-simple/clean/params.h @@ -0,0 +1,53 @@ +#ifndef SPX_PARAMS_H +#define SPX_PARAMS_H + +/* Hash output length in bytes. */ +#define SPX_N 32 +/* Height of the hypertree. */ +#define SPX_FULL_HEIGHT 68 +/* Number of subtree layer. */ +#define SPX_D 17 +/* FORS tree dimensions. */ +#define SPX_FORS_HEIGHT 10 +#define SPX_FORS_TREES 30 +/* Winternitz parameter, */ +#define SPX_WOTS_W 16 + +/* The hash function is defined by linking a different hash.c file, as opposed + to setting a #define constant. */ + +/* For clarity */ +#define SPX_ADDR_BYTES 32 + +/* WOTS parameters. */ +#define SPX_WOTS_LOGW 4 + +#define SPX_WOTS_LEN1 (8 * SPX_N / SPX_WOTS_LOGW) + +/* SPX_WOTS_LEN2 is floor(log(len_1 * (w - 1)) / log(w)) + 1; we precompute */ +#define SPX_WOTS_LEN2 3 + +#define SPX_WOTS_LEN (SPX_WOTS_LEN1 + SPX_WOTS_LEN2) +#define SPX_WOTS_BYTES (SPX_WOTS_LEN * SPX_N) +#define SPX_WOTS_PK_BYTES SPX_WOTS_BYTES + +/* Subtree size. */ +#define SPX_TREE_HEIGHT (SPX_FULL_HEIGHT / SPX_D) + +/* FORS parameters. */ +#define SPX_FORS_MSG_BYTES ((SPX_FORS_HEIGHT * SPX_FORS_TREES + 7) / 8) +#define SPX_FORS_BYTES ((SPX_FORS_HEIGHT + 1) * SPX_FORS_TREES * SPX_N) +#define SPX_FORS_PK_BYTES SPX_N + +/* Resulting SPX sizes. */ +#define SPX_BYTES (SPX_N + SPX_FORS_BYTES + SPX_D * SPX_WOTS_BYTES +\ + SPX_FULL_HEIGHT * SPX_N) +#define SPX_PK_BYTES (2 * SPX_N) +#define SPX_SK_BYTES (2 * SPX_N + SPX_PK_BYTES) + +/* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ +#define SPX_OPTRAND_BYTES 32 + +#endif diff --git a/crypto_sign/sphincs-shake256-256f-simple/clean/sign.c b/crypto_sign/sphincs-shake256-256f-simple/clean/sign.c new file mode 100644 index 00000000..ae53ae2b --- /dev/null +++ b/crypto_sign/sphincs-shake256-256f-simple/clean/sign.c @@ -0,0 +1,344 @@ +#include +#include +#include + +#include "address.h" +#include "api.h" +#include "fors.h" +#include "hash.h" +#include "params.h" +#include "randombytes.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +/** + * Computes the leaf at a given address. First generates the WOTS key pair, + * then computes leaf by hashing horizontally. + */ +static void wots_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t tree_addr[8]) { + unsigned char pk[SPX_WOTS_BYTES]; + uint32_t wots_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_keypair_addr( + wots_addr, addr_idx); + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_wots_gen_pk( + pk, sk_seed, pub_seed, wots_addr); + + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_thash_WOTS_LEN( + leaf, pk, pub_seed, wots_pk_addr); +} + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_crypto_sign_secretkeybytes(void) { + return PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_CRYPTO_SECRETKEYBYTES; +} + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_crypto_sign_publickeybytes(void) { + return PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES; +} + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_crypto_sign_bytes(void) { + return PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_CRYPTO_BYTES; +} + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_crypto_sign_seedbytes(void) { + return PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_CRYPTO_SEEDBYTES; +} + +/* + * Generates an SPX key pair given a seed of length + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed) { + /* We do not need the auth path in key generation, but it simplifies the + code to have just one treehash routine that computes both root and path + in one function. */ + unsigned char auth_path[SPX_TREE_HEIGHT * SPX_N]; + uint32_t top_tree_addr[8] = {0}; + + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_layer_addr( + top_tree_addr, SPX_D - 1); + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_type( + top_tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */ + memcpy(sk, seed, PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_CRYPTO_SEEDBYTES); + + memcpy(pk, sk + 2 * SPX_N, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_initialize_hash_function(pk, sk); + + /* Compute root node of the top-most subtree. */ + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_treehash_TREE_HEIGHT( + sk + 3 * SPX_N, auth_path, sk, sk + 2 * SPX_N, 0, 0, + wots_gen_leaf, top_tree_addr); + + memcpy(pk + SPX_N, sk + 3 * SPX_N, SPX_N); + + return 0; +} + +/* + * Generates an SPX key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk) { + unsigned char seed[PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_CRYPTO_SEEDBYTES]; + randombytes(seed, PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_CRYPTO_SEEDBYTES); + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_crypto_sign_seed_keypair( + pk, sk, seed); + + return 0; +} + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + const unsigned char *sk_seed = sk; + const unsigned char *sk_prf = sk + SPX_N; + const unsigned char *pk = sk + 2 * SPX_N; + const unsigned char *pub_seed = pk; + + unsigned char optrand[SPX_N]; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char root[SPX_N]; + uint32_t i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_initialize_hash_function( + pub_seed, sk_seed); + + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ + randombytes(optrand, SPX_N); + /* Compute the digest randomization value. */ + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_gen_message_random( + sig, sk_prf, optrand, m, mlen); + + /* Derive the message digest and leaf index from R, PK and M. */ + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Sign the message hash using FORS. */ + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_fors_sign( + sig, root, mhash, sk_seed, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Compute a WOTS signature. */ + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_wots_sign( + sig, root, sk_seed, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the authentication path for the used WOTS leaf. */ + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_treehash_TREE_HEIGHT( + root, sig, sk_seed, pub_seed, idx_leaf, 0, + wots_gen_leaf, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + *siglen = SPX_BYTES; + + return 0; +} + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk) { + const unsigned char *pub_seed = pk; + const unsigned char *pub_root = pk + SPX_N; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char wots_pk[SPX_WOTS_BYTES]; + unsigned char root[SPX_N]; + unsigned char leaf[SPX_N]; + unsigned int i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + if (siglen != SPX_BYTES) { + return -1; + } + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_initialize_hash_function( + pub_seed, NULL); + + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + /* Derive the message digest and leaf index from R || PK || M. */ + /* The additional SPX_N is a result of the hash domain separator. */ + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + /* Layer correctly defaults to 0, so no need to set_layer_addr */ + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_fors_pk_from_sig( + root, sig, mhash, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + /* For each subtree.. */ + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + + /* The WOTS public key is only correct if the signature was correct. */ + /* Initially, root is the FORS pk, but on subsequent iterations it is + the root of the subtree below the currently processed subtree. */ + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_wots_pk_from_sig( + wots_pk, sig, root, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the leaf node using the WOTS public key. */ + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_thash_WOTS_LEN( + leaf, wots_pk, pub_seed, wots_pk_addr); + + /* Compute the root node of this subtree. */ + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_compute_root( + root, leaf, idx_leaf, 0, sig, SPX_TREE_HEIGHT, + pub_seed, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + /* Check if the root node equals the root node in the public key. */ + if (memcmp(root, pub_root, SPX_N) != 0) { + return -1; + } + + return 0; +} + + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + size_t siglen; + + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_crypto_sign_signature( + sm, &siglen, m, mlen, sk); + + memmove(sm + SPX_BYTES, m, mlen); + *smlen = siglen + mlen; + + return 0; +} + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk) { + /* The API caller does not necessarily know what size a signature should be + but SPHINCS+ signatures are always exactly SPX_BYTES. */ + if (smlen < SPX_BYTES) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + *mlen = smlen - SPX_BYTES; + + if (PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_crypto_sign_verify( + sm, SPX_BYTES, sm + SPX_BYTES, *mlen, pk)) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + /* If verification was successful, move the message to the right place. */ + memmove(m, sm + SPX_BYTES, *mlen); + + return 0; +} diff --git a/crypto_sign/sphincs-shake256-256f-simple/clean/thash.h b/crypto_sign/sphincs-shake256-256f-simple/clean/thash.h new file mode 100644 index 00000000..949de93a --- /dev/null +++ b/crypto_sign/sphincs-shake256-256f-simple/clean/thash.h @@ -0,0 +1,22 @@ +#ifndef SPX_THASH_H +#define SPX_THASH_H + +#include + +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-shake256-256f-simple/clean/thash_shake256_simple.c b/crypto_sign/sphincs-shake256-256f-simple/clean/thash_shake256_simple.c new file mode 100644 index 00000000..f66a4d3d --- /dev/null +++ b/crypto_sign/sphincs-shake256-256f-simple/clean/thash_shake256_simple.c @@ -0,0 +1,61 @@ +#include +#include + +#include "address.h" +#include "params.h" +#include "thash.h" + +#include "fips202.h" + +/** + * Takes an array of inblocks concatenated arrays of SPX_N bytes. + */ +static void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_thash( + unsigned char *out, unsigned char *buf, + const unsigned char *in, unsigned int inblocks, + const unsigned char *pub_seed, uint32_t addr[8]) { + + memcpy(buf, pub_seed, SPX_N); + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_addr_to_bytes(buf + SPX_N, addr); + memcpy(buf + SPX_N + SPX_ADDR_BYTES, in, inblocks * SPX_N); + + shake256(out, SPX_N, buf, SPX_N + SPX_ADDR_BYTES + inblocks * SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_ADDR_BYTES + 1 * SPX_N]; + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_thash( + out, buf, in, 1, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_ADDR_BYTES + 2 * SPX_N]; + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_thash( + out, buf, in, 2, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_ADDR_BYTES + SPX_WOTS_LEN * SPX_N]; + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_thash( + out, buf, in, SPX_WOTS_LEN, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_ADDR_BYTES + SPX_FORS_TREES * SPX_N]; + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_thash( + out, buf, in, SPX_FORS_TREES, pub_seed, addr); +} diff --git a/crypto_sign/sphincs-shake256-256f-simple/clean/utils.c b/crypto_sign/sphincs-shake256-256f-simple/clean/utils.c new file mode 100644 index 00000000..1791df9e --- /dev/null +++ b/crypto_sign/sphincs-shake256-256f-simple/clean/utils.c @@ -0,0 +1,192 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in) { + + /* Iterate over out in decreasing order, for big-endianness. */ + for (size_t i = outlen; i > 0; i--) { + out[i - 1] = in & 0xff; + in = in >> 8; + } +} + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen) { + unsigned long long retval = 0; + + for (size_t i = 0; i < inlen; i++) { + retval |= ((unsigned long long)in[i]) << (8 * (inlen - 1 - i)); + } + return retval; +} + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + unsigned char buffer[2 * SPX_N]; + + /* If leaf_idx is odd (last bit = 1), current path element is a right child + and auth_path has to go left. Otherwise it is the other way around. */ + if (leaf_idx & 1) { + memcpy(buffer + SPX_N, leaf, SPX_N); + memcpy(buffer, auth_path, SPX_N); + } else { + memcpy(buffer, leaf, SPX_N); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + + for (i = 0; i < tree_height - 1; i++) { + leaf_idx >>= 1; + idx_offset >>= 1; + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_tree_height(addr, i + 1); + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + + /* Pick the right or left neighbor, depending on parity of the node. */ + if (leaf_idx & 1) { + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_thash_2( + buffer + SPX_N, buffer, pub_seed, addr); + memcpy(buffer, auth_path, SPX_N); + } else { + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_thash_2( + buffer, buffer, pub_seed, addr); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + } + + /* The last iteration is exceptional; we do not copy an auth_path node. */ + leaf_idx >>= 1; + idx_offset >>= 1; + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_tree_height(addr, tree_height); + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_thash_2( + root, buffer, pub_seed, addr); +} + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +static void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_treehash( + unsigned char *root, unsigned char *auth_path, + unsigned char *stack, unsigned int *heights, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned int offset = 0; + uint32_t idx; + uint32_t tree_idx; + + for (idx = 0; idx < (uint32_t)(1 << tree_height); idx++) { + /* Add the next leaf node to the stack. */ + gen_leaf(stack + offset * SPX_N, + sk_seed, pub_seed, idx + idx_offset, tree_addr); + offset++; + heights[offset - 1] = 0; + + /* If this is a node we need for the auth path.. */ + if ((leaf_idx ^ 0x1) == idx) { + memcpy(auth_path, stack + (offset - 1)*SPX_N, SPX_N); + } + + /* While the top-most nodes are of equal height.. */ + while (offset >= 2 && heights[offset - 1] == heights[offset - 2]) { + /* Compute index of the new node, in the next layer. */ + tree_idx = (idx >> (heights[offset - 1] + 1)); + + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_tree_height( + tree_addr, heights[offset - 1] + 1); + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_tree_index( + tree_addr, tree_idx + (idx_offset >> (heights[offset - 1] + 1))); + /* Hash the top-most nodes from the stack together. */ + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_thash_2( + stack + (offset - 2)*SPX_N, stack + (offset - 2)*SPX_N, + pub_seed, tree_addr); + offset--; + /* Note that the top-most node is now one layer higher. */ + heights[offset - 1]++; + + /* If this is a node we need for the auth path.. */ + if (((leaf_idx >> heights[offset - 1]) ^ 0x1) == tree_idx) { + memcpy(auth_path + heights[offset - 1]*SPX_N, + stack + (offset - 1)*SPX_N, SPX_N); + } + } + } + memcpy(root, stack, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_FORS_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_FORS_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_FORS_HEIGHT, gen_leaf, tree_addr); +} + +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_TREE_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_TREE_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_TREE_HEIGHT, gen_leaf, tree_addr); +} diff --git a/crypto_sign/sphincs-shake256-256f-simple/clean/utils.h b/crypto_sign/sphincs-shake256-256f-simple/clean/utils.h new file mode 100644 index 00000000..9e0ecec0 --- /dev/null +++ b/crypto_sign/sphincs-shake256-256f-simple/clean/utils.h @@ -0,0 +1,60 @@ +#ifndef SPX_UTILS_H +#define SPX_UTILS_H + +#include "params.h" +#include +#include + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in); + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen); + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-shake256-256f-simple/clean/wots.c b/crypto_sign/sphincs-shake256-256f-simple/clean/wots.c new file mode 100644 index 00000000..fc6019f3 --- /dev/null +++ b/crypto_sign/sphincs-shake256-256f-simple/clean/wots.c @@ -0,0 +1,161 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +// TODO clarify address expectations, and make them more uniform. +// TODO i.e. do we expect types to be set already? +// TODO and do we expect modifications or copies? + +/** + * Computes the starting value for a chain, i.e. the secret key. + * Expects the address to be complete up to the chain address. + */ +static void wots_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t wots_addr[8]) { + /* Make sure that the hash address is actually zeroed. */ + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_hash_addr(wots_addr, 0); + + /* Generate sk element. */ + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_prf_addr(sk, sk_seed, wots_addr); +} + +/** + * Computes the chaining function. + * out and in have to be n-byte arrays. + * + * Interprets in as start-th value of the chain. + * addr has to contain the address of the chain. + */ +static void gen_chain(unsigned char *out, const unsigned char *in, + unsigned int start, unsigned int steps, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + /* Initialize out with the value at position 'start'. */ + memcpy(out, in, SPX_N); + + /* Iterate 'steps' calls to the hash function. */ + for (i = start; i < (start + steps) && i < SPX_WOTS_W; i++) { + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_hash_addr(addr, i); + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_thash_1( + out, out, pub_seed, addr); + } +} + +/** + * base_w algorithm as described in draft. + * Interprets an array of bytes as integers in base w. + * This only works when log_w is a divisor of 8. + */ +static void base_w(unsigned int *output, const size_t out_len, + const unsigned char *input) { + size_t in = 0; + size_t out = 0; + unsigned char total = 0; + unsigned int bits = 0; + size_t consumed; + + for (consumed = 0; consumed < out_len; consumed++) { + if (bits == 0) { + total = input[in]; + in++; + bits += 8; + } + bits -= SPX_WOTS_LOGW; + output[out] = (unsigned int)((total >> bits) & (SPX_WOTS_W - 1)); + out++; + } +} + +/* Computes the WOTS+ checksum over a message (in base_w). */ +static void wots_checksum(unsigned int *csum_base_w, + const unsigned int *msg_base_w) { + unsigned int csum = 0; + unsigned char csum_bytes[(SPX_WOTS_LEN2 * SPX_WOTS_LOGW + 7) / 8]; + unsigned int i; + + /* Compute checksum. */ + for (i = 0; i < SPX_WOTS_LEN1; i++) { + csum += SPX_WOTS_W - 1 - msg_base_w[i]; + } + + /* Convert checksum to base_w. */ + /* Make sure expected empty zero bits are the least significant bits. */ + csum = csum << (8 - ((SPX_WOTS_LEN2 * SPX_WOTS_LOGW) % 8)); + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_ull_to_bytes( + csum_bytes, sizeof(csum_bytes), csum); + base_w(csum_base_w, SPX_WOTS_LEN2, csum_bytes); +} + +/* Takes a message and derives the matching chain lengths. */ +static void chain_lengths(unsigned int *lengths, const unsigned char *msg) { + base_w(lengths, SPX_WOTS_LEN1, msg); + wots_checksum(lengths + SPX_WOTS_LEN1, lengths); +} + +/** + * WOTS key generation. Takes a 32 byte sk_seed, expands it to WOTS private key + * elements and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(pk + i * SPX_N, sk_seed, addr); + gen_chain(pk + i * SPX_N, pk + i * SPX_N, + 0, SPX_WOTS_W - 1, pub_seed, addr); + } +} + +/** + * Takes a n-byte message and the 32-byte sk_see to compute a signature 'sig'. + */ +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(sig + i * SPX_N, sk_seed, addr); + gen_chain(sig + i * SPX_N, sig + i * SPX_N, 0, lengths[i], pub_seed, addr); + } +} + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_set_chain_addr(addr, i); + gen_chain(pk + i * SPX_N, sig + i * SPX_N, + lengths[i], SPX_WOTS_W - 1 - lengths[i], pub_seed, addr); + } +} diff --git a/crypto_sign/sphincs-shake256-256f-simple/clean/wots.h b/crypto_sign/sphincs-shake256-256f-simple/clean/wots.h new file mode 100644 index 00000000..f82afe5f --- /dev/null +++ b/crypto_sign/sphincs-shake256-256f-simple/clean/wots.h @@ -0,0 +1,38 @@ +#ifndef SPX_WOTS_H +#define SPX_WOTS_H + +#include "params.h" +#include + +/** + * WOTS key generation. Takes a 32 byte seed for the private key, expands it to + * a full WOTS private key and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * Takes a n-byte message and the 32-byte seed for the private key to compute a + * signature that is placed at 'sig'. + */ +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]); + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-shake256-256s-robust/META.yml b/crypto_sign/sphincs-shake256-256s-robust/META.yml new file mode 100644 index 00000000..a04440fd --- /dev/null +++ b/crypto_sign/sphincs-shake256-256s-robust/META.yml @@ -0,0 +1,27 @@ +name: SPHINCS+ +type: signature +claimed-nist-level: 5 +length-public-key: 64 +length-secret-key: 128 +length-signature: 29792 +testvectors-sha256: eea62308d71394a888e05128f078c4663dc83e128c34e0300bb16cb839d8698b +principal-submitter: Andreas Hülsing +auxiliary-submitters: + - Jean-Philippe Aumasson + - Daniel J. Bernstein, + - Christoph Dobraunig + - Maria Eichlseder + - Scott Fluhrer + - Stefan-Lukas Gazdag + - Panos Kampanakis + - Stefan Kölbl + - Tanja Lange + - Martin M. Lauridsen + - Florian Mendel + - Ruben Niederhagen + - Christian Rechberger + - Joost Rijneveld + - Peter Schwabe +implementations: + - name: clean + version: https://github.com/sphincs/sphincsplus/commit/77755c94d0bc744478044d6efbb888dc13156441 diff --git a/crypto_sign/sphincs-shake256-256s-robust/clean/LICENSE b/crypto_sign/sphincs-shake256-256s-robust/clean/LICENSE new file mode 100644 index 00000000..670154e3 --- /dev/null +++ b/crypto_sign/sphincs-shake256-256s-robust/clean/LICENSE @@ -0,0 +1,116 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + diff --git a/crypto_sign/sphincs-shake256-256s-robust/clean/Makefile b/crypto_sign/sphincs-shake256-256s-robust/clean/Makefile new file mode 100644 index 00000000..66d22b40 --- /dev/null +++ b/crypto_sign/sphincs-shake256-256s-robust/clean/Makefile @@ -0,0 +1,20 @@ +# This Makefile can be used with GNU Make or BSD Make + +LIB=libsphincs-shake256-256s-robust_clean.a + +HEADERS = params.h address.h wots.h utils.h fors.h api.h hash.h thash.h +OBJECTS = address.o wots.o utils.o fors.o sign.o hash_shake256.o thash_shake256_robust.o + +CFLAGS=-O3 -Wall -Wconversion -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) + +all: $(LIB) + +%.o: %.c $(HEADERS) + $(CC) $(CFLAGS) -c -o $@ $< + +$(LIB): $(OBJECTS) + $(AR) -r $@ $(OBJECTS) + +clean: + $(RM) $(OBJECTS) + $(RM) $(LIB) diff --git a/crypto_sign/sphincs-shake256-256s-robust/clean/Makefile.Microsoft_nmake b/crypto_sign/sphincs-shake256-256s-robust/clean/Makefile.Microsoft_nmake new file mode 100644 index 00000000..906ed730 --- /dev/null +++ b/crypto_sign/sphincs-shake256-256s-robust/clean/Makefile.Microsoft_nmake @@ -0,0 +1,19 @@ +# This Makefile can be used with Microsoft Visual Studio's nmake using the command: +# nmake /f Makefile.Microsoft_nmake + +LIBRARY=libsphincs-shake256-256s-robust_clean.lib +OBJECTS=address.obj wots.obj utils.obj fors.obj sign.obj hash_shake256.obj thash_shake256_robust.obj + +CFLAGS=/nologo /I ..\..\..\common /W4 /WX + +all: $(LIBRARY) + +# Make sure objects are recompiled if headers change. +$(OBJECTS): *.h + +$(LIBRARY): $(OBJECTS) + LIB.EXE /NOLOGO /WX /OUT:$@ $** + +clean: + -DEL $(OBJECTS) + -DEL $(LIBRARY) diff --git a/crypto_sign/sphincs-shake256-256s-robust/clean/address.c b/crypto_sign/sphincs-shake256-256s-robust/clean/address.c new file mode 100644 index 00000000..87b3586c --- /dev/null +++ b/crypto_sign/sphincs-shake256-256s-robust/clean/address.c @@ -0,0 +1,78 @@ +#include + +#include "address.h" +#include "params.h" +#include "utils.h" + +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]) { + int i; + + for (i = 0; i < 8; i++) { + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_ull_to_bytes( + bytes + i * 4, 4, addr[i]); + } +} + +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer) { + addr[0] = layer; +} + +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree) { + addr[1] = 0; + addr[2] = (uint32_t) (tree >> 32); + addr[3] = (uint32_t) tree; +} + +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_type( + uint32_t addr[8], uint32_t type) { + addr[4] = type; +} + +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; +} + +/* These functions are used for OTS addresses. */ + +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair) { + addr[5] = keypair; +} + +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; + out[5] = in[5]; +} + +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain) { + addr[6] = chain; +} + +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash) { + addr[7] = hash; +} + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height) { + addr[6] = tree_height; +} + +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index) { + addr[7] = tree_index; +} diff --git a/crypto_sign/sphincs-shake256-256s-robust/clean/address.h b/crypto_sign/sphincs-shake256-256s-robust/clean/address.h new file mode 100644 index 00000000..f66b6d7e --- /dev/null +++ b/crypto_sign/sphincs-shake256-256s-robust/clean/address.h @@ -0,0 +1,50 @@ +#ifndef SPX_ADDRESS_H +#define SPX_ADDRESS_H + +#include + +#define SPX_ADDR_TYPE_WOTS 0 +#define SPX_ADDR_TYPE_WOTSPK 1 +#define SPX_ADDR_TYPE_HASHTREE 2 +#define SPX_ADDR_TYPE_FORSTREE 3 +#define SPX_ADDR_TYPE_FORSPK 4 + +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer); + +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree); + +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_type( + uint32_t addr[8], uint32_t type); + +/* Copies the layer and tree part of one address into the other */ +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for WOTS and FORS addresses. */ + +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair); + +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain); + +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash); + +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height); + +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index); + +#endif diff --git a/crypto_sign/sphincs-shake256-256s-robust/clean/api.h b/crypto_sign/sphincs-shake256-256s-robust/clean/api.h new file mode 100644 index 00000000..b88c59cb --- /dev/null +++ b/crypto_sign/sphincs-shake256-256s-robust/clean/api.h @@ -0,0 +1,78 @@ +#ifndef PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_API_H +#define PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_API_H + +#include +#include + +#define PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_CRYPTO_ALGNAME "SPHINCS+" + +#define PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_CRYPTO_SECRETKEYBYTES 128 +#define PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES 64 +#define PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_CRYPTO_BYTES 29792 +#define PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_CRYPTO_SEEDBYTES 96 + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_crypto_sign_secretkeybytes(void); + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_crypto_sign_publickeybytes(void); + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_crypto_sign_bytes(void); + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_crypto_sign_seedbytes(void); + +/* + * Generates a SPHINCS+ key pair given a seed. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed); + +/* + * Generates a SPHINCS+ key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk); + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk); + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk); + +#endif diff --git a/crypto_sign/sphincs-shake256-256s-robust/clean/fors.c b/crypto_sign/sphincs-shake256-256s-robust/clean/fors.c new file mode 100644 index 00000000..976d5675 --- /dev/null +++ b/crypto_sign/sphincs-shake256-256s-robust/clean/fors.c @@ -0,0 +1,164 @@ +#include +#include +#include + +#include "address.h" +#include "fors.h" +#include "hash.h" +#include "thash.h" +#include "utils.h" + +static void fors_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_prf_addr( + sk, sk_seed, fors_leaf_addr); +} + +static void fors_sk_to_leaf(unsigned char *leaf, const unsigned char *sk, + const unsigned char *pub_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_thash_1( + leaf, sk, pub_seed, fors_leaf_addr); +} + +static void fors_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t fors_tree_addr[8]) { + uint32_t fors_leaf_addr[8] = {0}; + + /* Only copy the parts that must be kept in fors_leaf_addr. */ + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_copy_keypair_addr( + fors_leaf_addr, fors_tree_addr); + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_type( + fors_leaf_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_tree_index( + fors_leaf_addr, addr_idx); + + fors_gen_sk(leaf, sk_seed, fors_leaf_addr); + fors_sk_to_leaf(leaf, leaf, pub_seed, fors_leaf_addr); +} + +/** + * Interprets m as SPX_FORS_HEIGHT-bit unsigned integers. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + * Assumes indices has space for SPX_FORS_TREES integers. + */ +static void message_to_indices(uint32_t *indices, const unsigned char *m) { + unsigned int i, j; + unsigned int offset = 0; + + for (i = 0; i < SPX_FORS_TREES; i++) { + indices[i] = 0; + for (j = 0; j < SPX_FORS_HEIGHT; j++) { + indices[i] ^= (((uint32_t)m[offset >> 3] >> (offset & 0x7)) & 0x1) << j; + offset++; + } + } +} + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Include the secret key part that produces the selected leaf node. */ + fors_gen_sk(sig, sk_seed, fors_tree_addr); + sig += SPX_N; + + /* Compute the authentication path for this leaf node. */ + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_treehash_FORS_HEIGHT( + roots + i * SPX_N, sig, sk_seed, pub_seed, + indices[i], idx_offset, fors_gen_leaf, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + unsigned char leaf[SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Derive the leaf from the included secret key part. */ + fors_sk_to_leaf(leaf, sig, pub_seed, fors_tree_addr); + sig += SPX_N; + + /* Derive the corresponding root node of this tree. */ + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_compute_root( + roots + i * SPX_N, leaf, indices[i], idx_offset, sig, + SPX_FORS_HEIGHT, pub_seed, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} diff --git a/crypto_sign/sphincs-shake256-256s-robust/clean/fors.h b/crypto_sign/sphincs-shake256-256s-robust/clean/fors.h new file mode 100644 index 00000000..31fef0ee --- /dev/null +++ b/crypto_sign/sphincs-shake256-256s-robust/clean/fors.h @@ -0,0 +1,30 @@ +#ifndef SPX_FORS_H +#define SPX_FORS_H + +#include + +#include "params.h" + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]); + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-shake256-256s-robust/clean/hash.h b/crypto_sign/sphincs-shake256-256s-robust/clean/hash.h new file mode 100644 index 00000000..96cd5c58 --- /dev/null +++ b/crypto_sign/sphincs-shake256-256s-robust/clean/hash.h @@ -0,0 +1,22 @@ +#ifndef SPX_HASH_H +#define SPX_HASH_H + +#include + +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed); + +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen); + +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen); + +#endif diff --git a/crypto_sign/sphincs-shake256-256s-robust/clean/hash_shake256.c b/crypto_sign/sphincs-shake256-256s-robust/clean/hash_shake256.c new file mode 100644 index 00000000..41146c98 --- /dev/null +++ b/crypto_sign/sphincs-shake256-256s-robust/clean/hash_shake256.c @@ -0,0 +1,87 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "utils.h" + +#include "fips202.h" + +/* For SHAKE256, there is no immediate reason to initialize at the start, + so this function is an empty operation. */ +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed) { + (void)pub_seed; /* Suppress an 'unused parameter' warning. */ + (void)sk_seed; /* Suppress an 'unused parameter' warning. */ +} + +/* + * Computes PRF(key, addr), given a secret key of SPX_N bytes and an address + */ +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]) { + unsigned char buf[SPX_N + SPX_ADDR_BYTES]; + + memcpy(buf, key, SPX_N); + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_addr_to_bytes(buf + SPX_N, addr); + + shake256(out, SPX_N, buf, SPX_N + SPX_ADDR_BYTES); +} + +/** + * Computes the message-dependent randomness R, using a secret seed and an + * optional randomization value as well as the message. + */ +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen) { + uint64_t s_inc[26]; + + shake256_inc_init(s_inc); + shake256_inc_absorb(s_inc, sk_prf, SPX_N); + shake256_inc_absorb(s_inc, optrand, SPX_N); + shake256_inc_absorb(s_inc, m, mlen); + shake256_inc_finalize(s_inc); + shake256_inc_squeeze(R, SPX_N, s_inc); +} + +/** + * Computes the message hash using R, the public key, and the message. + * Outputs the message digest and the index of the leaf. The index is split in + * the tree index and the leaf index, for convenient copying to an address. + */ +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen) { +#define SPX_TREE_BITS (SPX_TREE_HEIGHT * (SPX_D - 1)) +#define SPX_TREE_BYTES ((SPX_TREE_BITS + 7) / 8) +#define SPX_LEAF_BITS SPX_TREE_HEIGHT +#define SPX_LEAF_BYTES ((SPX_LEAF_BITS + 7) / 8) +#define SPX_DGST_BYTES (SPX_FORS_MSG_BYTES + SPX_TREE_BYTES + SPX_LEAF_BYTES) + + unsigned char buf[SPX_DGST_BYTES]; + unsigned char *bufp = buf; + uint64_t s_inc[26]; + + shake256_inc_init(s_inc); + shake256_inc_absorb(s_inc, R, SPX_N); + shake256_inc_absorb(s_inc, pk, SPX_PK_BYTES); + shake256_inc_absorb(s_inc, m, mlen); + shake256_inc_finalize(s_inc); + shake256_inc_squeeze(buf, SPX_DGST_BYTES, s_inc); + + memcpy(digest, bufp, SPX_FORS_MSG_BYTES); + bufp += SPX_FORS_MSG_BYTES; + + *tree = PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_bytes_to_ull( + bufp, SPX_TREE_BYTES); + *tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS); + bufp += SPX_TREE_BYTES; + + *leaf_idx = (uint32_t)PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_bytes_to_ull( + bufp, SPX_LEAF_BYTES); + *leaf_idx &= (~(uint32_t)0) >> (32 - SPX_LEAF_BITS); +} diff --git a/crypto_sign/sphincs-shake256-256s-robust/clean/params.h b/crypto_sign/sphincs-shake256-256s-robust/clean/params.h new file mode 100644 index 00000000..f023d687 --- /dev/null +++ b/crypto_sign/sphincs-shake256-256s-robust/clean/params.h @@ -0,0 +1,53 @@ +#ifndef SPX_PARAMS_H +#define SPX_PARAMS_H + +/* Hash output length in bytes. */ +#define SPX_N 32 +/* Height of the hypertree. */ +#define SPX_FULL_HEIGHT 64 +/* Number of subtree layer. */ +#define SPX_D 8 +/* FORS tree dimensions. */ +#define SPX_FORS_HEIGHT 14 +#define SPX_FORS_TREES 22 +/* Winternitz parameter, */ +#define SPX_WOTS_W 16 + +/* The hash function is defined by linking a different hash.c file, as opposed + to setting a #define constant. */ + +/* For clarity */ +#define SPX_ADDR_BYTES 32 + +/* WOTS parameters. */ +#define SPX_WOTS_LOGW 4 + +#define SPX_WOTS_LEN1 (8 * SPX_N / SPX_WOTS_LOGW) + +/* SPX_WOTS_LEN2 is floor(log(len_1 * (w - 1)) / log(w)) + 1; we precompute */ +#define SPX_WOTS_LEN2 3 + +#define SPX_WOTS_LEN (SPX_WOTS_LEN1 + SPX_WOTS_LEN2) +#define SPX_WOTS_BYTES (SPX_WOTS_LEN * SPX_N) +#define SPX_WOTS_PK_BYTES SPX_WOTS_BYTES + +/* Subtree size. */ +#define SPX_TREE_HEIGHT (SPX_FULL_HEIGHT / SPX_D) + +/* FORS parameters. */ +#define SPX_FORS_MSG_BYTES ((SPX_FORS_HEIGHT * SPX_FORS_TREES + 7) / 8) +#define SPX_FORS_BYTES ((SPX_FORS_HEIGHT + 1) * SPX_FORS_TREES * SPX_N) +#define SPX_FORS_PK_BYTES SPX_N + +/* Resulting SPX sizes. */ +#define SPX_BYTES (SPX_N + SPX_FORS_BYTES + SPX_D * SPX_WOTS_BYTES +\ + SPX_FULL_HEIGHT * SPX_N) +#define SPX_PK_BYTES (2 * SPX_N) +#define SPX_SK_BYTES (2 * SPX_N + SPX_PK_BYTES) + +/* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ +#define SPX_OPTRAND_BYTES 32 + +#endif diff --git a/crypto_sign/sphincs-shake256-256s-robust/clean/sign.c b/crypto_sign/sphincs-shake256-256s-robust/clean/sign.c new file mode 100644 index 00000000..f3b08274 --- /dev/null +++ b/crypto_sign/sphincs-shake256-256s-robust/clean/sign.c @@ -0,0 +1,344 @@ +#include +#include +#include + +#include "address.h" +#include "api.h" +#include "fors.h" +#include "hash.h" +#include "params.h" +#include "randombytes.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +/** + * Computes the leaf at a given address. First generates the WOTS key pair, + * then computes leaf by hashing horizontally. + */ +static void wots_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t tree_addr[8]) { + unsigned char pk[SPX_WOTS_BYTES]; + uint32_t wots_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_keypair_addr( + wots_addr, addr_idx); + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_wots_gen_pk( + pk, sk_seed, pub_seed, wots_addr); + + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_thash_WOTS_LEN( + leaf, pk, pub_seed, wots_pk_addr); +} + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_crypto_sign_secretkeybytes(void) { + return PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_CRYPTO_SECRETKEYBYTES; +} + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_crypto_sign_publickeybytes(void) { + return PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES; +} + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_crypto_sign_bytes(void) { + return PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_CRYPTO_BYTES; +} + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_crypto_sign_seedbytes(void) { + return PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_CRYPTO_SEEDBYTES; +} + +/* + * Generates an SPX key pair given a seed of length + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed) { + /* We do not need the auth path in key generation, but it simplifies the + code to have just one treehash routine that computes both root and path + in one function. */ + unsigned char auth_path[SPX_TREE_HEIGHT * SPX_N]; + uint32_t top_tree_addr[8] = {0}; + + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_layer_addr( + top_tree_addr, SPX_D - 1); + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_type( + top_tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */ + memcpy(sk, seed, PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_CRYPTO_SEEDBYTES); + + memcpy(pk, sk + 2 * SPX_N, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_initialize_hash_function(pk, sk); + + /* Compute root node of the top-most subtree. */ + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_treehash_TREE_HEIGHT( + sk + 3 * SPX_N, auth_path, sk, sk + 2 * SPX_N, 0, 0, + wots_gen_leaf, top_tree_addr); + + memcpy(pk + SPX_N, sk + 3 * SPX_N, SPX_N); + + return 0; +} + +/* + * Generates an SPX key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk) { + unsigned char seed[PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_CRYPTO_SEEDBYTES]; + randombytes(seed, PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_CRYPTO_SEEDBYTES); + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_crypto_sign_seed_keypair( + pk, sk, seed); + + return 0; +} + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + const unsigned char *sk_seed = sk; + const unsigned char *sk_prf = sk + SPX_N; + const unsigned char *pk = sk + 2 * SPX_N; + const unsigned char *pub_seed = pk; + + unsigned char optrand[SPX_N]; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char root[SPX_N]; + uint32_t i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_initialize_hash_function( + pub_seed, sk_seed); + + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ + randombytes(optrand, SPX_N); + /* Compute the digest randomization value. */ + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_gen_message_random( + sig, sk_prf, optrand, m, mlen); + + /* Derive the message digest and leaf index from R, PK and M. */ + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Sign the message hash using FORS. */ + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_fors_sign( + sig, root, mhash, sk_seed, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Compute a WOTS signature. */ + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_wots_sign( + sig, root, sk_seed, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the authentication path for the used WOTS leaf. */ + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_treehash_TREE_HEIGHT( + root, sig, sk_seed, pub_seed, idx_leaf, 0, + wots_gen_leaf, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + *siglen = SPX_BYTES; + + return 0; +} + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk) { + const unsigned char *pub_seed = pk; + const unsigned char *pub_root = pk + SPX_N; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char wots_pk[SPX_WOTS_BYTES]; + unsigned char root[SPX_N]; + unsigned char leaf[SPX_N]; + unsigned int i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + if (siglen != SPX_BYTES) { + return -1; + } + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_initialize_hash_function( + pub_seed, NULL); + + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + /* Derive the message digest and leaf index from R || PK || M. */ + /* The additional SPX_N is a result of the hash domain separator. */ + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + /* Layer correctly defaults to 0, so no need to set_layer_addr */ + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_fors_pk_from_sig( + root, sig, mhash, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + /* For each subtree.. */ + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + + /* The WOTS public key is only correct if the signature was correct. */ + /* Initially, root is the FORS pk, but on subsequent iterations it is + the root of the subtree below the currently processed subtree. */ + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_wots_pk_from_sig( + wots_pk, sig, root, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the leaf node using the WOTS public key. */ + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_thash_WOTS_LEN( + leaf, wots_pk, pub_seed, wots_pk_addr); + + /* Compute the root node of this subtree. */ + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_compute_root( + root, leaf, idx_leaf, 0, sig, SPX_TREE_HEIGHT, + pub_seed, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + /* Check if the root node equals the root node in the public key. */ + if (memcmp(root, pub_root, SPX_N) != 0) { + return -1; + } + + return 0; +} + + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + size_t siglen; + + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_crypto_sign_signature( + sm, &siglen, m, mlen, sk); + + memmove(sm + SPX_BYTES, m, mlen); + *smlen = siglen + mlen; + + return 0; +} + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk) { + /* The API caller does not necessarily know what size a signature should be + but SPHINCS+ signatures are always exactly SPX_BYTES. */ + if (smlen < SPX_BYTES) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + *mlen = smlen - SPX_BYTES; + + if (PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_crypto_sign_verify( + sm, SPX_BYTES, sm + SPX_BYTES, *mlen, pk)) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + /* If verification was successful, move the message to the right place. */ + memmove(m, sm + SPX_BYTES, *mlen); + + return 0; +} diff --git a/crypto_sign/sphincs-shake256-256s-robust/clean/thash.h b/crypto_sign/sphincs-shake256-256s-robust/clean/thash.h new file mode 100644 index 00000000..0f498c3f --- /dev/null +++ b/crypto_sign/sphincs-shake256-256s-robust/clean/thash.h @@ -0,0 +1,22 @@ +#ifndef SPX_THASH_H +#define SPX_THASH_H + +#include + +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-shake256-256s-robust/clean/thash_shake256_robust.c b/crypto_sign/sphincs-shake256-256s-robust/clean/thash_shake256_robust.c new file mode 100644 index 00000000..cec70db8 --- /dev/null +++ b/crypto_sign/sphincs-shake256-256s-robust/clean/thash_shake256_robust.c @@ -0,0 +1,69 @@ +#include +#include + +#include "address.h" +#include "params.h" +#include "thash.h" + +#include "fips202.h" + +/** + * Takes an array of inblocks concatenated arrays of SPX_N bytes. + */ +static void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_thash( + unsigned char *out, unsigned char *buf, + const unsigned char *in, unsigned int inblocks, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char *bitmask = buf + SPX_N + SPX_ADDR_BYTES; + unsigned int i; + + memcpy(buf, pub_seed, SPX_N); + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_addr_to_bytes(buf + SPX_N, addr); + + shake256(bitmask, inblocks * SPX_N, buf, SPX_N + SPX_ADDR_BYTES); + + for (i = 0; i < inblocks * SPX_N; i++) { + buf[SPX_N + SPX_ADDR_BYTES + i] = in[i] ^ bitmask[i]; + } + + shake256(out, SPX_N, buf, SPX_N + SPX_ADDR_BYTES + inblocks * SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_ADDR_BYTES + 1 * SPX_N]; + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_thash( + out, buf, in, 1, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_ADDR_BYTES + 2 * SPX_N]; + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_thash( + out, buf, in, 2, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_ADDR_BYTES + SPX_WOTS_LEN * SPX_N]; + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_thash( + out, buf, in, SPX_WOTS_LEN, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_ADDR_BYTES + SPX_FORS_TREES * SPX_N]; + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_thash( + out, buf, in, SPX_FORS_TREES, pub_seed, addr); +} diff --git a/crypto_sign/sphincs-shake256-256s-robust/clean/utils.c b/crypto_sign/sphincs-shake256-256s-robust/clean/utils.c new file mode 100644 index 00000000..ef99a3fa --- /dev/null +++ b/crypto_sign/sphincs-shake256-256s-robust/clean/utils.c @@ -0,0 +1,192 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in) { + + /* Iterate over out in decreasing order, for big-endianness. */ + for (size_t i = outlen; i > 0; i--) { + out[i - 1] = in & 0xff; + in = in >> 8; + } +} + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen) { + unsigned long long retval = 0; + + for (size_t i = 0; i < inlen; i++) { + retval |= ((unsigned long long)in[i]) << (8 * (inlen - 1 - i)); + } + return retval; +} + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + unsigned char buffer[2 * SPX_N]; + + /* If leaf_idx is odd (last bit = 1), current path element is a right child + and auth_path has to go left. Otherwise it is the other way around. */ + if (leaf_idx & 1) { + memcpy(buffer + SPX_N, leaf, SPX_N); + memcpy(buffer, auth_path, SPX_N); + } else { + memcpy(buffer, leaf, SPX_N); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + + for (i = 0; i < tree_height - 1; i++) { + leaf_idx >>= 1; + idx_offset >>= 1; + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_tree_height(addr, i + 1); + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + + /* Pick the right or left neighbor, depending on parity of the node. */ + if (leaf_idx & 1) { + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_thash_2( + buffer + SPX_N, buffer, pub_seed, addr); + memcpy(buffer, auth_path, SPX_N); + } else { + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_thash_2( + buffer, buffer, pub_seed, addr); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + } + + /* The last iteration is exceptional; we do not copy an auth_path node. */ + leaf_idx >>= 1; + idx_offset >>= 1; + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_tree_height(addr, tree_height); + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_thash_2( + root, buffer, pub_seed, addr); +} + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +static void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_treehash( + unsigned char *root, unsigned char *auth_path, + unsigned char *stack, unsigned int *heights, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned int offset = 0; + uint32_t idx; + uint32_t tree_idx; + + for (idx = 0; idx < (uint32_t)(1 << tree_height); idx++) { + /* Add the next leaf node to the stack. */ + gen_leaf(stack + offset * SPX_N, + sk_seed, pub_seed, idx + idx_offset, tree_addr); + offset++; + heights[offset - 1] = 0; + + /* If this is a node we need for the auth path.. */ + if ((leaf_idx ^ 0x1) == idx) { + memcpy(auth_path, stack + (offset - 1)*SPX_N, SPX_N); + } + + /* While the top-most nodes are of equal height.. */ + while (offset >= 2 && heights[offset - 1] == heights[offset - 2]) { + /* Compute index of the new node, in the next layer. */ + tree_idx = (idx >> (heights[offset - 1] + 1)); + + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_tree_height( + tree_addr, heights[offset - 1] + 1); + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_tree_index( + tree_addr, tree_idx + (idx_offset >> (heights[offset - 1] + 1))); + /* Hash the top-most nodes from the stack together. */ + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_thash_2( + stack + (offset - 2)*SPX_N, stack + (offset - 2)*SPX_N, + pub_seed, tree_addr); + offset--; + /* Note that the top-most node is now one layer higher. */ + heights[offset - 1]++; + + /* If this is a node we need for the auth path.. */ + if (((leaf_idx >> heights[offset - 1]) ^ 0x1) == tree_idx) { + memcpy(auth_path + heights[offset - 1]*SPX_N, + stack + (offset - 1)*SPX_N, SPX_N); + } + } + } + memcpy(root, stack, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_FORS_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_FORS_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_FORS_HEIGHT, gen_leaf, tree_addr); +} + +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_TREE_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_TREE_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_TREE_HEIGHT, gen_leaf, tree_addr); +} diff --git a/crypto_sign/sphincs-shake256-256s-robust/clean/utils.h b/crypto_sign/sphincs-shake256-256s-robust/clean/utils.h new file mode 100644 index 00000000..5f48a979 --- /dev/null +++ b/crypto_sign/sphincs-shake256-256s-robust/clean/utils.h @@ -0,0 +1,60 @@ +#ifndef SPX_UTILS_H +#define SPX_UTILS_H + +#include "params.h" +#include +#include + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in); + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen); + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-shake256-256s-robust/clean/wots.c b/crypto_sign/sphincs-shake256-256s-robust/clean/wots.c new file mode 100644 index 00000000..173115b8 --- /dev/null +++ b/crypto_sign/sphincs-shake256-256s-robust/clean/wots.c @@ -0,0 +1,161 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +// TODO clarify address expectations, and make them more uniform. +// TODO i.e. do we expect types to be set already? +// TODO and do we expect modifications or copies? + +/** + * Computes the starting value for a chain, i.e. the secret key. + * Expects the address to be complete up to the chain address. + */ +static void wots_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t wots_addr[8]) { + /* Make sure that the hash address is actually zeroed. */ + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_hash_addr(wots_addr, 0); + + /* Generate sk element. */ + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_prf_addr(sk, sk_seed, wots_addr); +} + +/** + * Computes the chaining function. + * out and in have to be n-byte arrays. + * + * Interprets in as start-th value of the chain. + * addr has to contain the address of the chain. + */ +static void gen_chain(unsigned char *out, const unsigned char *in, + unsigned int start, unsigned int steps, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + /* Initialize out with the value at position 'start'. */ + memcpy(out, in, SPX_N); + + /* Iterate 'steps' calls to the hash function. */ + for (i = start; i < (start + steps) && i < SPX_WOTS_W; i++) { + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_hash_addr(addr, i); + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_thash_1( + out, out, pub_seed, addr); + } +} + +/** + * base_w algorithm as described in draft. + * Interprets an array of bytes as integers in base w. + * This only works when log_w is a divisor of 8. + */ +static void base_w(unsigned int *output, const size_t out_len, + const unsigned char *input) { + size_t in = 0; + size_t out = 0; + unsigned char total = 0; + unsigned int bits = 0; + size_t consumed; + + for (consumed = 0; consumed < out_len; consumed++) { + if (bits == 0) { + total = input[in]; + in++; + bits += 8; + } + bits -= SPX_WOTS_LOGW; + output[out] = (unsigned int)((total >> bits) & (SPX_WOTS_W - 1)); + out++; + } +} + +/* Computes the WOTS+ checksum over a message (in base_w). */ +static void wots_checksum(unsigned int *csum_base_w, + const unsigned int *msg_base_w) { + unsigned int csum = 0; + unsigned char csum_bytes[(SPX_WOTS_LEN2 * SPX_WOTS_LOGW + 7) / 8]; + unsigned int i; + + /* Compute checksum. */ + for (i = 0; i < SPX_WOTS_LEN1; i++) { + csum += SPX_WOTS_W - 1 - msg_base_w[i]; + } + + /* Convert checksum to base_w. */ + /* Make sure expected empty zero bits are the least significant bits. */ + csum = csum << (8 - ((SPX_WOTS_LEN2 * SPX_WOTS_LOGW) % 8)); + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_ull_to_bytes( + csum_bytes, sizeof(csum_bytes), csum); + base_w(csum_base_w, SPX_WOTS_LEN2, csum_bytes); +} + +/* Takes a message and derives the matching chain lengths. */ +static void chain_lengths(unsigned int *lengths, const unsigned char *msg) { + base_w(lengths, SPX_WOTS_LEN1, msg); + wots_checksum(lengths + SPX_WOTS_LEN1, lengths); +} + +/** + * WOTS key generation. Takes a 32 byte sk_seed, expands it to WOTS private key + * elements and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(pk + i * SPX_N, sk_seed, addr); + gen_chain(pk + i * SPX_N, pk + i * SPX_N, + 0, SPX_WOTS_W - 1, pub_seed, addr); + } +} + +/** + * Takes a n-byte message and the 32-byte sk_see to compute a signature 'sig'. + */ +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(sig + i * SPX_N, sk_seed, addr); + gen_chain(sig + i * SPX_N, sig + i * SPX_N, 0, lengths[i], pub_seed, addr); + } +} + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_set_chain_addr(addr, i); + gen_chain(pk + i * SPX_N, sig + i * SPX_N, + lengths[i], SPX_WOTS_W - 1 - lengths[i], pub_seed, addr); + } +} diff --git a/crypto_sign/sphincs-shake256-256s-robust/clean/wots.h b/crypto_sign/sphincs-shake256-256s-robust/clean/wots.h new file mode 100644 index 00000000..5892430d --- /dev/null +++ b/crypto_sign/sphincs-shake256-256s-robust/clean/wots.h @@ -0,0 +1,38 @@ +#ifndef SPX_WOTS_H +#define SPX_WOTS_H + +#include "params.h" +#include + +/** + * WOTS key generation. Takes a 32 byte seed for the private key, expands it to + * a full WOTS private key and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * Takes a n-byte message and the 32-byte seed for the private key to compute a + * signature that is placed at 'sig'. + */ +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]); + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-shake256-256s-simple/META.yml b/crypto_sign/sphincs-shake256-256s-simple/META.yml new file mode 100644 index 00000000..33732662 --- /dev/null +++ b/crypto_sign/sphincs-shake256-256s-simple/META.yml @@ -0,0 +1,27 @@ +name: SPHINCS+ +type: signature +claimed-nist-level: 5 +length-public-key: 64 +length-secret-key: 128 +length-signature: 29792 +testvectors-sha256: fc518be7778d0363f17a30c50efbe28841f5a795e7375e94d206f115967f30df +principal-submitter: Andreas Hülsing +auxiliary-submitters: + - Jean-Philippe Aumasson + - Daniel J. Bernstein, + - Christoph Dobraunig + - Maria Eichlseder + - Scott Fluhrer + - Stefan-Lukas Gazdag + - Panos Kampanakis + - Stefan Kölbl + - Tanja Lange + - Martin M. Lauridsen + - Florian Mendel + - Ruben Niederhagen + - Christian Rechberger + - Joost Rijneveld + - Peter Schwabe +implementations: + - name: clean + version: https://github.com/sphincs/sphincsplus/commit/77755c94d0bc744478044d6efbb888dc13156441 diff --git a/crypto_sign/sphincs-shake256-256s-simple/clean/LICENSE b/crypto_sign/sphincs-shake256-256s-simple/clean/LICENSE new file mode 100644 index 00000000..670154e3 --- /dev/null +++ b/crypto_sign/sphincs-shake256-256s-simple/clean/LICENSE @@ -0,0 +1,116 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + diff --git a/crypto_sign/sphincs-shake256-256s-simple/clean/Makefile b/crypto_sign/sphincs-shake256-256s-simple/clean/Makefile new file mode 100644 index 00000000..f764bc17 --- /dev/null +++ b/crypto_sign/sphincs-shake256-256s-simple/clean/Makefile @@ -0,0 +1,20 @@ +# This Makefile can be used with GNU Make or BSD Make + +LIB=libsphincs-shake256-256s-simple_clean.a + +HEADERS = params.h address.h wots.h utils.h fors.h api.h hash.h thash.h +OBJECTS = address.o wots.o utils.o fors.o sign.o hash_shake256.o thash_shake256_simple.o + +CFLAGS=-O3 -Wall -Wconversion -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) + +all: $(LIB) + +%.o: %.c $(HEADERS) + $(CC) $(CFLAGS) -c -o $@ $< + +$(LIB): $(OBJECTS) + $(AR) -r $@ $(OBJECTS) + +clean: + $(RM) $(OBJECTS) + $(RM) $(LIB) diff --git a/crypto_sign/sphincs-shake256-256s-simple/clean/Makefile.Microsoft_nmake b/crypto_sign/sphincs-shake256-256s-simple/clean/Makefile.Microsoft_nmake new file mode 100644 index 00000000..89437f30 --- /dev/null +++ b/crypto_sign/sphincs-shake256-256s-simple/clean/Makefile.Microsoft_nmake @@ -0,0 +1,19 @@ +# This Makefile can be used with Microsoft Visual Studio's nmake using the command: +# nmake /f Makefile.Microsoft_nmake + +LIBRARY=libsphincs-shake256-256s-simple_clean.lib +OBJECTS=address.obj wots.obj utils.obj fors.obj sign.obj hash_shake256.obj thash_shake256_simple.obj + +CFLAGS=/nologo /I ..\..\..\common /W4 /WX + +all: $(LIBRARY) + +# Make sure objects are recompiled if headers change. +$(OBJECTS): *.h + +$(LIBRARY): $(OBJECTS) + LIB.EXE /NOLOGO /WX /OUT:$@ $** + +clean: + -DEL $(OBJECTS) + -DEL $(LIBRARY) diff --git a/crypto_sign/sphincs-shake256-256s-simple/clean/address.c b/crypto_sign/sphincs-shake256-256s-simple/clean/address.c new file mode 100644 index 00000000..5e16da61 --- /dev/null +++ b/crypto_sign/sphincs-shake256-256s-simple/clean/address.c @@ -0,0 +1,78 @@ +#include + +#include "address.h" +#include "params.h" +#include "utils.h" + +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]) { + int i; + + for (i = 0; i < 8; i++) { + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_ull_to_bytes( + bytes + i * 4, 4, addr[i]); + } +} + +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer) { + addr[0] = layer; +} + +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree) { + addr[1] = 0; + addr[2] = (uint32_t) (tree >> 32); + addr[3] = (uint32_t) tree; +} + +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_type( + uint32_t addr[8], uint32_t type) { + addr[4] = type; +} + +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; +} + +/* These functions are used for OTS addresses. */ + +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair) { + addr[5] = keypair; +} + +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]) { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; + out[5] = in[5]; +} + +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain) { + addr[6] = chain; +} + +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash) { + addr[7] = hash; +} + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height) { + addr[6] = tree_height; +} + +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index) { + addr[7] = tree_index; +} diff --git a/crypto_sign/sphincs-shake256-256s-simple/clean/address.h b/crypto_sign/sphincs-shake256-256s-simple/clean/address.h new file mode 100644 index 00000000..6a4eeef3 --- /dev/null +++ b/crypto_sign/sphincs-shake256-256s-simple/clean/address.h @@ -0,0 +1,50 @@ +#ifndef SPX_ADDRESS_H +#define SPX_ADDRESS_H + +#include + +#define SPX_ADDR_TYPE_WOTS 0 +#define SPX_ADDR_TYPE_WOTSPK 1 +#define SPX_ADDR_TYPE_HASHTREE 2 +#define SPX_ADDR_TYPE_FORSTREE 3 +#define SPX_ADDR_TYPE_FORSPK 4 + +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_addr_to_bytes( + unsigned char *bytes, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_layer_addr( + uint32_t addr[8], uint32_t layer); + +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_tree_addr( + uint32_t addr[8], uint64_t tree); + +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_type( + uint32_t addr[8], uint32_t type); + +/* Copies the layer and tree part of one address into the other */ +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_copy_subtree_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for WOTS and FORS addresses. */ + +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_keypair_addr( + uint32_t addr[8], uint32_t keypair); + +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_chain_addr( + uint32_t addr[8], uint32_t chain); + +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_hash_addr( + uint32_t addr[8], uint32_t hash); + +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_copy_keypair_addr( + uint32_t out[8], const uint32_t in[8]); + +/* These functions are used for all hash tree addresses (including FORS). */ + +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_tree_height( + uint32_t addr[8], uint32_t tree_height); + +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_tree_index( + uint32_t addr[8], uint32_t tree_index); + +#endif diff --git a/crypto_sign/sphincs-shake256-256s-simple/clean/api.h b/crypto_sign/sphincs-shake256-256s-simple/clean/api.h new file mode 100644 index 00000000..bd043140 --- /dev/null +++ b/crypto_sign/sphincs-shake256-256s-simple/clean/api.h @@ -0,0 +1,78 @@ +#ifndef PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_API_H +#define PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_API_H + +#include +#include + +#define PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_CRYPTO_ALGNAME "SPHINCS+" + +#define PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_CRYPTO_SECRETKEYBYTES 128 +#define PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES 64 +#define PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_CRYPTO_BYTES 29792 +#define PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_CRYPTO_SEEDBYTES 96 + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_crypto_sign_secretkeybytes(void); + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_crypto_sign_publickeybytes(void); + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_crypto_sign_bytes(void); + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_crypto_sign_seedbytes(void); + +/* + * Generates a SPHINCS+ key pair given a seed. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed); + +/* + * Generates a SPHINCS+ key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [root || PUB_SEED] + */ +int PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk); + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk); + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk); + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk); + +#endif diff --git a/crypto_sign/sphincs-shake256-256s-simple/clean/fors.c b/crypto_sign/sphincs-shake256-256s-simple/clean/fors.c new file mode 100644 index 00000000..363cb10d --- /dev/null +++ b/crypto_sign/sphincs-shake256-256s-simple/clean/fors.c @@ -0,0 +1,164 @@ +#include +#include +#include + +#include "address.h" +#include "fors.h" +#include "hash.h" +#include "thash.h" +#include "utils.h" + +static void fors_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_prf_addr( + sk, sk_seed, fors_leaf_addr); +} + +static void fors_sk_to_leaf(unsigned char *leaf, const unsigned char *sk, + const unsigned char *pub_seed, + uint32_t fors_leaf_addr[8]) { + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_thash_1( + leaf, sk, pub_seed, fors_leaf_addr); +} + +static void fors_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t fors_tree_addr[8]) { + uint32_t fors_leaf_addr[8] = {0}; + + /* Only copy the parts that must be kept in fors_leaf_addr. */ + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_copy_keypair_addr( + fors_leaf_addr, fors_tree_addr); + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_type( + fors_leaf_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_tree_index( + fors_leaf_addr, addr_idx); + + fors_gen_sk(leaf, sk_seed, fors_leaf_addr); + fors_sk_to_leaf(leaf, leaf, pub_seed, fors_leaf_addr); +} + +/** + * Interprets m as SPX_FORS_HEIGHT-bit unsigned integers. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + * Assumes indices has space for SPX_FORS_TREES integers. + */ +static void message_to_indices(uint32_t *indices, const unsigned char *m) { + unsigned int i, j; + unsigned int offset = 0; + + for (i = 0; i < SPX_FORS_TREES; i++) { + indices[i] = 0; + for (j = 0; j < SPX_FORS_HEIGHT; j++) { + indices[i] ^= (((uint32_t)m[offset >> 3] >> (offset & 0x7)) & 0x1) << j; + offset++; + } + } +} + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Include the secret key part that produces the selected leaf node. */ + fors_gen_sk(sig, sk_seed, fors_tree_addr); + sig += SPX_N; + + /* Compute the authentication path for this leaf node. */ + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_treehash_FORS_HEIGHT( + roots + i * SPX_N, sig, sk_seed, pub_seed, + indices[i], idx_offset, fors_gen_leaf, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]) { + uint32_t indices[SPX_FORS_TREES]; + unsigned char roots[SPX_FORS_TREES * SPX_N]; + unsigned char leaf[SPX_N]; + uint32_t fors_tree_addr[8] = {0}; + uint32_t fors_pk_addr[8] = {0}; + uint32_t idx_offset; + unsigned int i; + + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_copy_keypair_addr( + fors_tree_addr, fors_addr); + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_copy_keypair_addr( + fors_pk_addr, fors_addr); + + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_type( + fors_tree_addr, SPX_ADDR_TYPE_FORSTREE); + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_type( + fors_pk_addr, SPX_ADDR_TYPE_FORSPK); + + message_to_indices(indices, m); + + for (i = 0; i < SPX_FORS_TREES; i++) { + idx_offset = i * (1 << SPX_FORS_HEIGHT); + + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_tree_height( + fors_tree_addr, 0); + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_tree_index( + fors_tree_addr, indices[i] + idx_offset); + + /* Derive the leaf from the included secret key part. */ + fors_sk_to_leaf(leaf, sig, pub_seed, fors_tree_addr); + sig += SPX_N; + + /* Derive the corresponding root node of this tree. */ + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_compute_root( + roots + i * SPX_N, leaf, indices[i], idx_offset, sig, + SPX_FORS_HEIGHT, pub_seed, fors_tree_addr); + sig += SPX_N * SPX_FORS_HEIGHT; + } + + /* Hash horizontally across all tree roots to derive the public key. */ + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_thash_FORS_TREES( + pk, roots, pub_seed, fors_pk_addr); +} diff --git a/crypto_sign/sphincs-shake256-256s-simple/clean/fors.h b/crypto_sign/sphincs-shake256-256s-simple/clean/fors.h new file mode 100644 index 00000000..d2cb06a5 --- /dev/null +++ b/crypto_sign/sphincs-shake256-256s-simple/clean/fors.h @@ -0,0 +1,30 @@ +#ifndef SPX_FORS_H +#define SPX_FORS_H + +#include + +#include "params.h" + +/** + * Signs a message m, deriving the secret key from sk_seed and the FTS address. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_fors_sign( + unsigned char *sig, unsigned char *pk, + const unsigned char *m, + const unsigned char *sk_seed, const unsigned char *pub_seed, + const uint32_t fors_addr[8]); + +/** + * Derives the FORS public key from a signature. + * This can be used for verification by comparing to a known public key, or to + * subsequently verify a signature on the derived public key. The latter is the + * typical use-case when used as an FTS below an OTS in a hypertree. + * Assumes m contains at least SPX_FORS_HEIGHT * SPX_FORS_TREES bits. + */ +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_fors_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *m, + const unsigned char *pub_seed, const uint32_t fors_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-shake256-256s-simple/clean/hash.h b/crypto_sign/sphincs-shake256-256s-simple/clean/hash.h new file mode 100644 index 00000000..7d7f9a39 --- /dev/null +++ b/crypto_sign/sphincs-shake256-256s-simple/clean/hash.h @@ -0,0 +1,22 @@ +#ifndef SPX_HASH_H +#define SPX_HASH_H + +#include + +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed); + +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen); + +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen); + +#endif diff --git a/crypto_sign/sphincs-shake256-256s-simple/clean/hash_shake256.c b/crypto_sign/sphincs-shake256-256s-simple/clean/hash_shake256.c new file mode 100644 index 00000000..a0982585 --- /dev/null +++ b/crypto_sign/sphincs-shake256-256s-simple/clean/hash_shake256.c @@ -0,0 +1,87 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "utils.h" + +#include "fips202.h" + +/* For SHAKE256, there is no immediate reason to initialize at the start, + so this function is an empty operation. */ +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_initialize_hash_function( + const unsigned char *pub_seed, const unsigned char *sk_seed) { + (void)pub_seed; /* Suppress an 'unused parameter' warning. */ + (void)sk_seed; /* Suppress an 'unused parameter' warning. */ +} + +/* + * Computes PRF(key, addr), given a secret key of SPX_N bytes and an address + */ +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_prf_addr( + unsigned char *out, const unsigned char *key, const uint32_t addr[8]) { + unsigned char buf[SPX_N + SPX_ADDR_BYTES]; + + memcpy(buf, key, SPX_N); + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_addr_to_bytes(buf + SPX_N, addr); + + shake256(out, SPX_N, buf, SPX_N + SPX_ADDR_BYTES); +} + +/** + * Computes the message-dependent randomness R, using a secret seed and an + * optional randomization value as well as the message. + */ +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_gen_message_random( + unsigned char *R, + const unsigned char *sk_prf, const unsigned char *optrand, + const unsigned char *m, size_t mlen) { + uint64_t s_inc[26]; + + shake256_inc_init(s_inc); + shake256_inc_absorb(s_inc, sk_prf, SPX_N); + shake256_inc_absorb(s_inc, optrand, SPX_N); + shake256_inc_absorb(s_inc, m, mlen); + shake256_inc_finalize(s_inc); + shake256_inc_squeeze(R, SPX_N, s_inc); +} + +/** + * Computes the message hash using R, the public key, and the message. + * Outputs the message digest and the index of the leaf. The index is split in + * the tree index and the leaf index, for convenient copying to an address. + */ +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_hash_message( + unsigned char *digest, uint64_t *tree, uint32_t *leaf_idx, + const unsigned char *R, const unsigned char *pk, + const unsigned char *m, size_t mlen) { +#define SPX_TREE_BITS (SPX_TREE_HEIGHT * (SPX_D - 1)) +#define SPX_TREE_BYTES ((SPX_TREE_BITS + 7) / 8) +#define SPX_LEAF_BITS SPX_TREE_HEIGHT +#define SPX_LEAF_BYTES ((SPX_LEAF_BITS + 7) / 8) +#define SPX_DGST_BYTES (SPX_FORS_MSG_BYTES + SPX_TREE_BYTES + SPX_LEAF_BYTES) + + unsigned char buf[SPX_DGST_BYTES]; + unsigned char *bufp = buf; + uint64_t s_inc[26]; + + shake256_inc_init(s_inc); + shake256_inc_absorb(s_inc, R, SPX_N); + shake256_inc_absorb(s_inc, pk, SPX_PK_BYTES); + shake256_inc_absorb(s_inc, m, mlen); + shake256_inc_finalize(s_inc); + shake256_inc_squeeze(buf, SPX_DGST_BYTES, s_inc); + + memcpy(digest, bufp, SPX_FORS_MSG_BYTES); + bufp += SPX_FORS_MSG_BYTES; + + *tree = PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_bytes_to_ull( + bufp, SPX_TREE_BYTES); + *tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS); + bufp += SPX_TREE_BYTES; + + *leaf_idx = (uint32_t)PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_bytes_to_ull( + bufp, SPX_LEAF_BYTES); + *leaf_idx &= (~(uint32_t)0) >> (32 - SPX_LEAF_BITS); +} diff --git a/crypto_sign/sphincs-shake256-256s-simple/clean/params.h b/crypto_sign/sphincs-shake256-256s-simple/clean/params.h new file mode 100644 index 00000000..f023d687 --- /dev/null +++ b/crypto_sign/sphincs-shake256-256s-simple/clean/params.h @@ -0,0 +1,53 @@ +#ifndef SPX_PARAMS_H +#define SPX_PARAMS_H + +/* Hash output length in bytes. */ +#define SPX_N 32 +/* Height of the hypertree. */ +#define SPX_FULL_HEIGHT 64 +/* Number of subtree layer. */ +#define SPX_D 8 +/* FORS tree dimensions. */ +#define SPX_FORS_HEIGHT 14 +#define SPX_FORS_TREES 22 +/* Winternitz parameter, */ +#define SPX_WOTS_W 16 + +/* The hash function is defined by linking a different hash.c file, as opposed + to setting a #define constant. */ + +/* For clarity */ +#define SPX_ADDR_BYTES 32 + +/* WOTS parameters. */ +#define SPX_WOTS_LOGW 4 + +#define SPX_WOTS_LEN1 (8 * SPX_N / SPX_WOTS_LOGW) + +/* SPX_WOTS_LEN2 is floor(log(len_1 * (w - 1)) / log(w)) + 1; we precompute */ +#define SPX_WOTS_LEN2 3 + +#define SPX_WOTS_LEN (SPX_WOTS_LEN1 + SPX_WOTS_LEN2) +#define SPX_WOTS_BYTES (SPX_WOTS_LEN * SPX_N) +#define SPX_WOTS_PK_BYTES SPX_WOTS_BYTES + +/* Subtree size. */ +#define SPX_TREE_HEIGHT (SPX_FULL_HEIGHT / SPX_D) + +/* FORS parameters. */ +#define SPX_FORS_MSG_BYTES ((SPX_FORS_HEIGHT * SPX_FORS_TREES + 7) / 8) +#define SPX_FORS_BYTES ((SPX_FORS_HEIGHT + 1) * SPX_FORS_TREES * SPX_N) +#define SPX_FORS_PK_BYTES SPX_N + +/* Resulting SPX sizes. */ +#define SPX_BYTES (SPX_N + SPX_FORS_BYTES + SPX_D * SPX_WOTS_BYTES +\ + SPX_FULL_HEIGHT * SPX_N) +#define SPX_PK_BYTES (2 * SPX_N) +#define SPX_SK_BYTES (2 * SPX_N + SPX_PK_BYTES) + +/* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ +#define SPX_OPTRAND_BYTES 32 + +#endif diff --git a/crypto_sign/sphincs-shake256-256s-simple/clean/sign.c b/crypto_sign/sphincs-shake256-256s-simple/clean/sign.c new file mode 100644 index 00000000..d5422c48 --- /dev/null +++ b/crypto_sign/sphincs-shake256-256s-simple/clean/sign.c @@ -0,0 +1,344 @@ +#include +#include +#include + +#include "address.h" +#include "api.h" +#include "fors.h" +#include "hash.h" +#include "params.h" +#include "randombytes.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +/** + * Computes the leaf at a given address. First generates the WOTS key pair, + * then computes leaf by hashing horizontally. + */ +static void wots_gen_leaf(unsigned char *leaf, const unsigned char *sk_seed, + const unsigned char *pub_seed, + uint32_t addr_idx, const uint32_t tree_addr[8]) { + unsigned char pk[SPX_WOTS_BYTES]; + uint32_t wots_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, addr_idx); + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_wots_gen_pk( + pk, sk_seed, pub_seed, wots_addr); + + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_thash_WOTS_LEN( + leaf, pk, pub_seed, wots_pk_addr); +} + +/* + * Returns the length of a secret key, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_crypto_sign_secretkeybytes(void) { + return PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_CRYPTO_SECRETKEYBYTES; +} + +/* + * Returns the length of a public key, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_crypto_sign_publickeybytes(void) { + return PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES; +} + +/* + * Returns the length of a signature, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_crypto_sign_bytes(void) { + return PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_CRYPTO_BYTES; +} + +/* + * Returns the length of the seed required to generate a key pair, in bytes + */ +size_t PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_crypto_sign_seedbytes(void) { + return PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_CRYPTO_SEEDBYTES; +} + +/* + * Generates an SPX key pair given a seed of length + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_crypto_sign_seed_keypair( + uint8_t *pk, uint8_t *sk, const uint8_t *seed) { + /* We do not need the auth path in key generation, but it simplifies the + code to have just one treehash routine that computes both root and path + in one function. */ + unsigned char auth_path[SPX_TREE_HEIGHT * SPX_N]; + uint32_t top_tree_addr[8] = {0}; + + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_layer_addr( + top_tree_addr, SPX_D - 1); + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_type( + top_tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */ + memcpy(sk, seed, PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_CRYPTO_SEEDBYTES); + + memcpy(pk, sk + 2 * SPX_N, SPX_N); + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_initialize_hash_function(pk, sk); + + /* Compute root node of the top-most subtree. */ + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_treehash_TREE_HEIGHT( + sk + 3 * SPX_N, auth_path, sk, sk + 2 * SPX_N, 0, 0, + wots_gen_leaf, top_tree_addr); + + memcpy(pk + SPX_N, sk + 3 * SPX_N, SPX_N); + + return 0; +} + +/* + * Generates an SPX key pair. + * Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] + * Format pk: [PUB_SEED || root] + */ +int PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_crypto_sign_keypair( + uint8_t *pk, uint8_t *sk) { + unsigned char seed[PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_CRYPTO_SEEDBYTES]; + randombytes(seed, PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_CRYPTO_SEEDBYTES); + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_crypto_sign_seed_keypair( + pk, sk, seed); + + return 0; +} + +/** + * Returns an array containing a detached signature. + */ +int PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_crypto_sign_signature( + uint8_t *sig, size_t *siglen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + const unsigned char *sk_seed = sk; + const unsigned char *sk_prf = sk + SPX_N; + const unsigned char *pk = sk + 2 * SPX_N; + const unsigned char *pub_seed = pk; + + unsigned char optrand[SPX_N]; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char root[SPX_N]; + uint32_t i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_initialize_hash_function( + pub_seed, sk_seed); + + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + + /* Optionally, signing can be made non-deterministic using optrand. + This can help counter side-channel attacks that would benefit from + getting a large number of traces when the signer uses the same nodes. */ + randombytes(optrand, SPX_N); + /* Compute the digest randomization value. */ + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_gen_message_random( + sig, sk_prf, optrand, m, mlen); + + /* Derive the message digest and leaf index from R, PK and M. */ + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Sign the message hash using FORS. */ + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_fors_sign( + sig, root, mhash, sk_seed, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + /* Compute a WOTS signature. */ + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_wots_sign( + sig, root, sk_seed, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the authentication path for the used WOTS leaf. */ + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_treehash_TREE_HEIGHT( + root, sig, sk_seed, pub_seed, idx_leaf, 0, + wots_gen_leaf, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + *siglen = SPX_BYTES; + + return 0; +} + +/** + * Verifies a detached signature and message under a given public key. + */ +int PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_crypto_sign_verify( + const uint8_t *sig, size_t siglen, + const uint8_t *m, size_t mlen, const uint8_t *pk) { + const unsigned char *pub_seed = pk; + const unsigned char *pub_root = pk + SPX_N; + unsigned char mhash[SPX_FORS_MSG_BYTES]; + unsigned char wots_pk[SPX_WOTS_BYTES]; + unsigned char root[SPX_N]; + unsigned char leaf[SPX_N]; + unsigned int i; + uint64_t tree; + uint32_t idx_leaf; + uint32_t wots_addr[8] = {0}; + uint32_t tree_addr[8] = {0}; + uint32_t wots_pk_addr[8] = {0}; + + if (siglen != SPX_BYTES) { + return -1; + } + + /* This hook allows the hash function instantiation to do whatever + preparation or computation it needs, based on the public seed. */ + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_initialize_hash_function( + pub_seed, NULL); + + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_type( + wots_addr, SPX_ADDR_TYPE_WOTS); + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_type( + tree_addr, SPX_ADDR_TYPE_HASHTREE); + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_type( + wots_pk_addr, SPX_ADDR_TYPE_WOTSPK); + + /* Derive the message digest and leaf index from R || PK || M. */ + /* The additional SPX_N is a result of the hash domain separator. */ + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_hash_message( + mhash, &tree, &idx_leaf, sig, pk, m, mlen); + sig += SPX_N; + + /* Layer correctly defaults to 0, so no need to set_layer_addr */ + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_tree_addr(wots_addr, tree); + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_fors_pk_from_sig( + root, sig, mhash, pub_seed, wots_addr); + sig += SPX_FORS_BYTES; + + /* For each subtree.. */ + for (i = 0; i < SPX_D; i++) { + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_layer_addr(tree_addr, i); + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_tree_addr(tree_addr, tree); + + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_copy_subtree_addr( + wots_addr, tree_addr); + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_keypair_addr( + wots_addr, idx_leaf); + + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_copy_keypair_addr( + wots_pk_addr, wots_addr); + + /* The WOTS public key is only correct if the signature was correct. */ + /* Initially, root is the FORS pk, but on subsequent iterations it is + the root of the subtree below the currently processed subtree. */ + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_wots_pk_from_sig( + wots_pk, sig, root, pub_seed, wots_addr); + sig += SPX_WOTS_BYTES; + + /* Compute the leaf node using the WOTS public key. */ + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_thash_WOTS_LEN( + leaf, wots_pk, pub_seed, wots_pk_addr); + + /* Compute the root node of this subtree. */ + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_compute_root( + root, leaf, idx_leaf, 0, sig, SPX_TREE_HEIGHT, + pub_seed, tree_addr); + sig += SPX_TREE_HEIGHT * SPX_N; + + /* Update the indices for the next layer. */ + idx_leaf = (tree & ((1 << SPX_TREE_HEIGHT) - 1)); + tree = tree >> SPX_TREE_HEIGHT; + } + + /* Check if the root node equals the root node in the public key. */ + if (memcmp(root, pub_root, SPX_N) != 0) { + return -1; + } + + return 0; +} + + +/** + * Returns an array containing the signature followed by the message. + */ +int PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_crypto_sign( + uint8_t *sm, size_t *smlen, + const uint8_t *m, size_t mlen, const uint8_t *sk) { + size_t siglen; + + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_crypto_sign_signature( + sm, &siglen, m, mlen, sk); + + memmove(sm + SPX_BYTES, m, mlen); + *smlen = siglen + mlen; + + return 0; +} + +/** + * Verifies a given signature-message pair under a given public key. + */ +int PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_crypto_sign_open( + uint8_t *m, size_t *mlen, + const uint8_t *sm, size_t smlen, const uint8_t *pk) { + /* The API caller does not necessarily know what size a signature should be + but SPHINCS+ signatures are always exactly SPX_BYTES. */ + if (smlen < SPX_BYTES) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + *mlen = smlen - SPX_BYTES; + + if (PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_crypto_sign_verify( + sm, SPX_BYTES, sm + SPX_BYTES, *mlen, pk)) { + memset(m, 0, smlen); + *mlen = 0; + return -1; + } + + /* If verification was successful, move the message to the right place. */ + memmove(m, sm + SPX_BYTES, *mlen); + + return 0; +} diff --git a/crypto_sign/sphincs-shake256-256s-simple/clean/thash.h b/crypto_sign/sphincs-shake256-256s-simple/clean/thash.h new file mode 100644 index 00000000..fa5b7632 --- /dev/null +++ b/crypto_sign/sphincs-shake256-256s-simple/clean/thash.h @@ -0,0 +1,22 @@ +#ifndef SPX_THASH_H +#define SPX_THASH_H + +#include + +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/crypto_sign/sphincs-shake256-256s-simple/clean/thash_shake256_simple.c b/crypto_sign/sphincs-shake256-256s-simple/clean/thash_shake256_simple.c new file mode 100644 index 00000000..d8a1b80c --- /dev/null +++ b/crypto_sign/sphincs-shake256-256s-simple/clean/thash_shake256_simple.c @@ -0,0 +1,61 @@ +#include +#include + +#include "address.h" +#include "params.h" +#include "thash.h" + +#include "fips202.h" + +/** + * Takes an array of inblocks concatenated arrays of SPX_N bytes. + */ +static void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_thash( + unsigned char *out, unsigned char *buf, + const unsigned char *in, unsigned int inblocks, + const unsigned char *pub_seed, uint32_t addr[8]) { + + memcpy(buf, pub_seed, SPX_N); + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_addr_to_bytes(buf + SPX_N, addr); + memcpy(buf + SPX_N + SPX_ADDR_BYTES, in, inblocks * SPX_N); + + shake256(out, SPX_N, buf, SPX_N + SPX_ADDR_BYTES + inblocks * SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_thash_1( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_ADDR_BYTES + 1 * SPX_N]; + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_thash( + out, buf, in, 1, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_thash_2( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_ADDR_BYTES + 2 * SPX_N]; + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_thash( + out, buf, in, 2, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_thash_WOTS_LEN( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_ADDR_BYTES + SPX_WOTS_LEN * SPX_N]; + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_thash( + out, buf, in, SPX_WOTS_LEN, pub_seed, addr); +} + +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_thash_FORS_TREES( + unsigned char *out, const unsigned char *in, + const unsigned char *pub_seed, uint32_t addr[8]) { + + unsigned char buf[SPX_N + SPX_ADDR_BYTES + SPX_FORS_TREES * SPX_N]; + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_thash( + out, buf, in, SPX_FORS_TREES, pub_seed, addr); +} diff --git a/crypto_sign/sphincs-shake256-256s-simple/clean/utils.c b/crypto_sign/sphincs-shake256-256s-simple/clean/utils.c new file mode 100644 index 00000000..8a22842a --- /dev/null +++ b/crypto_sign/sphincs-shake256-256s-simple/clean/utils.c @@ -0,0 +1,192 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in) { + + /* Iterate over out in decreasing order, for big-endianness. */ + for (size_t i = outlen; i > 0; i--) { + out[i - 1] = in & 0xff; + in = in >> 8; + } +} + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen) { + unsigned long long retval = 0; + + for (size_t i = 0; i < inlen; i++) { + retval |= ((unsigned long long)in[i]) << (8 * (inlen - 1 - i)); + } + return retval; +} + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + unsigned char buffer[2 * SPX_N]; + + /* If leaf_idx is odd (last bit = 1), current path element is a right child + and auth_path has to go left. Otherwise it is the other way around. */ + if (leaf_idx & 1) { + memcpy(buffer + SPX_N, leaf, SPX_N); + memcpy(buffer, auth_path, SPX_N); + } else { + memcpy(buffer, leaf, SPX_N); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + + for (i = 0; i < tree_height - 1; i++) { + leaf_idx >>= 1; + idx_offset >>= 1; + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_tree_height(addr, i + 1); + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + + /* Pick the right or left neighbor, depending on parity of the node. */ + if (leaf_idx & 1) { + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_thash_2( + buffer + SPX_N, buffer, pub_seed, addr); + memcpy(buffer, auth_path, SPX_N); + } else { + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_thash_2( + buffer, buffer, pub_seed, addr); + memcpy(buffer + SPX_N, auth_path, SPX_N); + } + auth_path += SPX_N; + } + + /* The last iteration is exceptional; we do not copy an auth_path node. */ + leaf_idx >>= 1; + idx_offset >>= 1; + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_tree_height(addr, tree_height); + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_tree_index( + addr, leaf_idx + idx_offset); + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_thash_2( + root, buffer, pub_seed, addr); +} + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +static void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_treehash( + unsigned char *root, unsigned char *auth_path, + unsigned char *stack, unsigned int *heights, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, uint32_t tree_height, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned int offset = 0; + uint32_t idx; + uint32_t tree_idx; + + for (idx = 0; idx < (uint32_t)(1 << tree_height); idx++) { + /* Add the next leaf node to the stack. */ + gen_leaf(stack + offset * SPX_N, + sk_seed, pub_seed, idx + idx_offset, tree_addr); + offset++; + heights[offset - 1] = 0; + + /* If this is a node we need for the auth path.. */ + if ((leaf_idx ^ 0x1) == idx) { + memcpy(auth_path, stack + (offset - 1)*SPX_N, SPX_N); + } + + /* While the top-most nodes are of equal height.. */ + while (offset >= 2 && heights[offset - 1] == heights[offset - 2]) { + /* Compute index of the new node, in the next layer. */ + tree_idx = (idx >> (heights[offset - 1] + 1)); + + /* Set the address of the node we're creating. */ + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_tree_height( + tree_addr, heights[offset - 1] + 1); + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_tree_index( + tree_addr, tree_idx + (idx_offset >> (heights[offset - 1] + 1))); + /* Hash the top-most nodes from the stack together. */ + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_thash_2( + stack + (offset - 2)*SPX_N, stack + (offset - 2)*SPX_N, + pub_seed, tree_addr); + offset--; + /* Note that the top-most node is now one layer higher. */ + heights[offset - 1]++; + + /* If this is a node we need for the auth path.. */ + if (((leaf_idx >> heights[offset - 1]) ^ 0x1) == tree_idx) { + memcpy(auth_path + heights[offset - 1]*SPX_N, + stack + (offset - 1)*SPX_N, SPX_N); + } + } + } + memcpy(root, stack, SPX_N); +} + +/* The wrappers below ensure that we use fixed-size buffers on the stack */ + +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_FORS_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_FORS_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_FORS_HEIGHT, gen_leaf, tree_addr); +} + +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]) { + + unsigned char stack[(SPX_TREE_HEIGHT + 1)*SPX_N]; + unsigned int heights[SPX_TREE_HEIGHT + 1]; + + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_treehash( + root, auth_path, stack, heights, sk_seed, pub_seed, + leaf_idx, idx_offset, SPX_TREE_HEIGHT, gen_leaf, tree_addr); +} diff --git a/crypto_sign/sphincs-shake256-256s-simple/clean/utils.h b/crypto_sign/sphincs-shake256-256s-simple/clean/utils.h new file mode 100644 index 00000000..aea40865 --- /dev/null +++ b/crypto_sign/sphincs-shake256-256s-simple/clean/utils.h @@ -0,0 +1,60 @@ +#ifndef SPX_UTILS_H +#define SPX_UTILS_H + +#include "params.h" +#include +#include + +/** + * Converts the value of 'in' to 'outlen' bytes in big-endian byte order. + */ +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_ull_to_bytes( + unsigned char *out, size_t outlen, unsigned long long in); + +/** + * Converts the inlen bytes in 'in' from big-endian byte order to an integer. + */ +unsigned long long PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_bytes_to_ull( + const unsigned char *in, size_t inlen); + +/** + * Computes a root node given a leaf and an auth path. + * Expects address to be complete other than the tree_height and tree_index. + */ +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_compute_root( + unsigned char *root, const unsigned char *leaf, + uint32_t leaf_idx, uint32_t idx_offset, + const unsigned char *auth_path, uint32_t tree_height, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * For a given leaf index, computes the authentication path and the resulting + * root node using Merkle's TreeHash algorithm. + * Expects the layer and tree parts of the tree_addr to be set, as well as the + * tree type (i.e. SPX_ADDR_TYPE_HASHTREE or SPX_ADDR_TYPE_FORSTREE). + * Applies the offset idx_offset to indices before building addresses, so that + * it is possible to continue counting indices across trees. + */ +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_treehash_FORS_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_treehash_TREE_HEIGHT( + unsigned char *root, unsigned char *auth_path, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t leaf_idx, uint32_t idx_offset, + void (*gen_leaf)( + unsigned char * /* leaf */, + const unsigned char * /* sk_seed */, + const unsigned char * /* pub_seed */, + uint32_t /* addr_idx */, const uint32_t[8] /* tree_addr */), + uint32_t tree_addr[8]); + +#endif diff --git a/crypto_sign/sphincs-shake256-256s-simple/clean/wots.c b/crypto_sign/sphincs-shake256-256s-simple/clean/wots.c new file mode 100644 index 00000000..56d2f265 --- /dev/null +++ b/crypto_sign/sphincs-shake256-256s-simple/clean/wots.c @@ -0,0 +1,161 @@ +#include +#include + +#include "address.h" +#include "hash.h" +#include "params.h" +#include "thash.h" +#include "utils.h" +#include "wots.h" + +// TODO clarify address expectations, and make them more uniform. +// TODO i.e. do we expect types to be set already? +// TODO and do we expect modifications or copies? + +/** + * Computes the starting value for a chain, i.e. the secret key. + * Expects the address to be complete up to the chain address. + */ +static void wots_gen_sk(unsigned char *sk, const unsigned char *sk_seed, + uint32_t wots_addr[8]) { + /* Make sure that the hash address is actually zeroed. */ + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_hash_addr(wots_addr, 0); + + /* Generate sk element. */ + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_prf_addr(sk, sk_seed, wots_addr); +} + +/** + * Computes the chaining function. + * out and in have to be n-byte arrays. + * + * Interprets in as start-th value of the chain. + * addr has to contain the address of the chain. + */ +static void gen_chain(unsigned char *out, const unsigned char *in, + unsigned int start, unsigned int steps, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + /* Initialize out with the value at position 'start'. */ + memcpy(out, in, SPX_N); + + /* Iterate 'steps' calls to the hash function. */ + for (i = start; i < (start + steps) && i < SPX_WOTS_W; i++) { + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_hash_addr(addr, i); + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_thash_1( + out, out, pub_seed, addr); + } +} + +/** + * base_w algorithm as described in draft. + * Interprets an array of bytes as integers in base w. + * This only works when log_w is a divisor of 8. + */ +static void base_w(unsigned int *output, const size_t out_len, + const unsigned char *input) { + size_t in = 0; + size_t out = 0; + unsigned char total = 0; + unsigned int bits = 0; + size_t consumed; + + for (consumed = 0; consumed < out_len; consumed++) { + if (bits == 0) { + total = input[in]; + in++; + bits += 8; + } + bits -= SPX_WOTS_LOGW; + output[out] = (unsigned int)((total >> bits) & (SPX_WOTS_W - 1)); + out++; + } +} + +/* Computes the WOTS+ checksum over a message (in base_w). */ +static void wots_checksum(unsigned int *csum_base_w, + const unsigned int *msg_base_w) { + unsigned int csum = 0; + unsigned char csum_bytes[(SPX_WOTS_LEN2 * SPX_WOTS_LOGW + 7) / 8]; + unsigned int i; + + /* Compute checksum. */ + for (i = 0; i < SPX_WOTS_LEN1; i++) { + csum += SPX_WOTS_W - 1 - msg_base_w[i]; + } + + /* Convert checksum to base_w. */ + /* Make sure expected empty zero bits are the least significant bits. */ + csum = csum << (8 - ((SPX_WOTS_LEN2 * SPX_WOTS_LOGW) % 8)); + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_ull_to_bytes( + csum_bytes, sizeof(csum_bytes), csum); + base_w(csum_base_w, SPX_WOTS_LEN2, csum_bytes); +} + +/* Takes a message and derives the matching chain lengths. */ +static void chain_lengths(unsigned int *lengths, const unsigned char *msg) { + base_w(lengths, SPX_WOTS_LEN1, msg); + wots_checksum(lengths + SPX_WOTS_LEN1, lengths); +} + +/** + * WOTS key generation. Takes a 32 byte sk_seed, expands it to WOTS private key + * elements and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]) { + uint32_t i; + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(pk + i * SPX_N, sk_seed, addr); + gen_chain(pk + i * SPX_N, pk + i * SPX_N, + 0, SPX_WOTS_W - 1, pub_seed, addr); + } +} + +/** + * Takes a n-byte message and the 32-byte sk_see to compute a signature 'sig'. + */ +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_chain_addr(addr, i); + wots_gen_sk(sig + i * SPX_N, sk_seed, addr); + gen_chain(sig + i * SPX_N, sig + i * SPX_N, 0, lengths[i], pub_seed, addr); + } +} + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]) { + unsigned int lengths[SPX_WOTS_LEN]; + uint32_t i; + + chain_lengths(lengths, msg); + + for (i = 0; i < SPX_WOTS_LEN; i++) { + PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_set_chain_addr(addr, i); + gen_chain(pk + i * SPX_N, sig + i * SPX_N, + lengths[i], SPX_WOTS_W - 1 - lengths[i], pub_seed, addr); + } +} diff --git a/crypto_sign/sphincs-shake256-256s-simple/clean/wots.h b/crypto_sign/sphincs-shake256-256s-simple/clean/wots.h new file mode 100644 index 00000000..3357f3a9 --- /dev/null +++ b/crypto_sign/sphincs-shake256-256s-simple/clean/wots.h @@ -0,0 +1,38 @@ +#ifndef SPX_WOTS_H +#define SPX_WOTS_H + +#include "params.h" +#include + +/** + * WOTS key generation. Takes a 32 byte seed for the private key, expands it to + * a full WOTS private key and computes the corresponding public key. + * It requires the seed pub_seed (used to generate bitmasks and hash keys) + * and the address of this WOTS key pair. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_wots_gen_pk( + unsigned char *pk, const unsigned char *sk_seed, + const unsigned char *pub_seed, uint32_t addr[8]); + +/** + * Takes a n-byte message and the 32-byte seed for the private key to compute a + * signature that is placed at 'sig'. + */ +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_wots_sign( + unsigned char *sig, const unsigned char *msg, + const unsigned char *sk_seed, const unsigned char *pub_seed, + uint32_t addr[8]); + +/** + * Takes a WOTS signature and an n-byte message, computes a WOTS public key. + * + * Writes the computed public key to 'pk'. + */ +void PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_wots_pk_from_sig( + unsigned char *pk, + const unsigned char *sig, const unsigned char *msg, + const unsigned char *pub_seed, uint32_t addr[8]); + +#endif diff --git a/test/duplicate_consistency/sphincs-haraka-128f-robust_clean.yml b/test/duplicate_consistency/sphincs-haraka-128f-robust_clean.yml new file mode 100644 index 00000000..2e1392df --- /dev/null +++ b/test/duplicate_consistency/sphincs-haraka-128f-robust_clean.yml @@ -0,0 +1,31 @@ +consistency_checks: +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - address.c + - address.h + - fors.c + - fors.h + - hash.h + - sign.c + - thash.h + - utils.c + - utils.h + - wots.c + - wots.h +- source: + scheme: sphincs-shake256-128f-robust + implementation: clean + files: + - params.h +- source: + scheme: sphincs-haraka-256s-robust + implementation: clean + files: + - hash_haraka.c +- source: + scheme: sphincs-haraka-256s-robust + implementation: clean + files: + - thash_haraka_robust.c diff --git a/test/duplicate_consistency/sphincs-haraka-128f-simple_clean.yml b/test/duplicate_consistency/sphincs-haraka-128f-simple_clean.yml new file mode 100644 index 00000000..d23ff294 --- /dev/null +++ b/test/duplicate_consistency/sphincs-haraka-128f-simple_clean.yml @@ -0,0 +1,31 @@ +consistency_checks: +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - address.c + - address.h + - fors.c + - fors.h + - hash.h + - sign.c + - thash.h + - utils.c + - utils.h + - wots.c + - wots.h +- source: + scheme: sphincs-shake256-128f-robust + implementation: clean + files: + - params.h +- source: + scheme: sphincs-haraka-256s-robust + implementation: clean + files: + - hash_haraka.c +- source: + scheme: sphincs-haraka-256s-simple + implementation: clean + files: + - thash_haraka_simple.c diff --git a/test/duplicate_consistency/sphincs-haraka-128s-robust_clean.yml b/test/duplicate_consistency/sphincs-haraka-128s-robust_clean.yml new file mode 100644 index 00000000..c15be28b --- /dev/null +++ b/test/duplicate_consistency/sphincs-haraka-128s-robust_clean.yml @@ -0,0 +1,31 @@ +consistency_checks: +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - address.c + - address.h + - fors.c + - fors.h + - hash.h + - sign.c + - thash.h + - utils.c + - utils.h + - wots.c + - wots.h +- source: + scheme: sphincs-shake256-128s-robust + implementation: clean + files: + - params.h +- source: + scheme: sphincs-haraka-256s-robust + implementation: clean + files: + - hash_haraka.c +- source: + scheme: sphincs-haraka-256s-robust + implementation: clean + files: + - thash_haraka_robust.c diff --git a/test/duplicate_consistency/sphincs-haraka-128s-simple_clean.yml b/test/duplicate_consistency/sphincs-haraka-128s-simple_clean.yml new file mode 100644 index 00000000..6a2193f2 --- /dev/null +++ b/test/duplicate_consistency/sphincs-haraka-128s-simple_clean.yml @@ -0,0 +1,31 @@ +consistency_checks: +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - address.c + - address.h + - fors.c + - fors.h + - hash.h + - sign.c + - thash.h + - utils.c + - utils.h + - wots.c + - wots.h +- source: + scheme: sphincs-shake256-128s-robust + implementation: clean + files: + - params.h +- source: + scheme: sphincs-haraka-256s-robust + implementation: clean + files: + - hash_haraka.c +- source: + scheme: sphincs-haraka-256s-simple + implementation: clean + files: + - thash_haraka_simple.c diff --git a/test/duplicate_consistency/sphincs-haraka-192f-robust_clean.yml b/test/duplicate_consistency/sphincs-haraka-192f-robust_clean.yml new file mode 100644 index 00000000..3faf9810 --- /dev/null +++ b/test/duplicate_consistency/sphincs-haraka-192f-robust_clean.yml @@ -0,0 +1,31 @@ +consistency_checks: +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - address.c + - address.h + - fors.c + - fors.h + - hash.h + - sign.c + - thash.h + - utils.c + - utils.h + - wots.c + - wots.h +- source: + scheme: sphincs-shake256-192f-robust + implementation: clean + files: + - params.h +- source: + scheme: sphincs-haraka-256s-robust + implementation: clean + files: + - hash_haraka.c +- source: + scheme: sphincs-haraka-256s-robust + implementation: clean + files: + - thash_haraka_robust.c diff --git a/test/duplicate_consistency/sphincs-haraka-192f-simple_clean.yml b/test/duplicate_consistency/sphincs-haraka-192f-simple_clean.yml new file mode 100644 index 00000000..9905ef9b --- /dev/null +++ b/test/duplicate_consistency/sphincs-haraka-192f-simple_clean.yml @@ -0,0 +1,31 @@ +consistency_checks: +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - address.c + - address.h + - fors.c + - fors.h + - hash.h + - sign.c + - thash.h + - utils.c + - utils.h + - wots.c + - wots.h +- source: + scheme: sphincs-shake256-192f-robust + implementation: clean + files: + - params.h +- source: + scheme: sphincs-haraka-256s-robust + implementation: clean + files: + - hash_haraka.c +- source: + scheme: sphincs-haraka-256s-simple + implementation: clean + files: + - thash_haraka_simple.c diff --git a/test/duplicate_consistency/sphincs-haraka-192s-robust_clean.yml b/test/duplicate_consistency/sphincs-haraka-192s-robust_clean.yml new file mode 100644 index 00000000..91928a5c --- /dev/null +++ b/test/duplicate_consistency/sphincs-haraka-192s-robust_clean.yml @@ -0,0 +1,31 @@ +consistency_checks: +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - address.c + - address.h + - fors.c + - fors.h + - hash.h + - sign.c + - thash.h + - utils.c + - utils.h + - wots.c + - wots.h +- source: + scheme: sphincs-shake256-192s-robust + implementation: clean + files: + - params.h +- source: + scheme: sphincs-haraka-256s-robust + implementation: clean + files: + - hash_haraka.c +- source: + scheme: sphincs-haraka-256s-robust + implementation: clean + files: + - thash_haraka_robust.c diff --git a/test/duplicate_consistency/sphincs-haraka-192s-simple_clean.yml b/test/duplicate_consistency/sphincs-haraka-192s-simple_clean.yml new file mode 100644 index 00000000..fa531d46 --- /dev/null +++ b/test/duplicate_consistency/sphincs-haraka-192s-simple_clean.yml @@ -0,0 +1,31 @@ +consistency_checks: +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - address.c + - address.h + - fors.c + - fors.h + - hash.h + - sign.c + - thash.h + - utils.c + - utils.h + - wots.c + - wots.h +- source: + scheme: sphincs-shake256-192s-robust + implementation: clean + files: + - params.h +- source: + scheme: sphincs-haraka-256s-robust + implementation: clean + files: + - hash_haraka.c +- source: + scheme: sphincs-haraka-256s-simple + implementation: clean + files: + - thash_haraka_simple.c diff --git a/test/duplicate_consistency/sphincs-haraka-256f-robust_clean.yml b/test/duplicate_consistency/sphincs-haraka-256f-robust_clean.yml new file mode 100644 index 00000000..36cea0fe --- /dev/null +++ b/test/duplicate_consistency/sphincs-haraka-256f-robust_clean.yml @@ -0,0 +1,31 @@ +consistency_checks: +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - address.c + - address.h + - fors.c + - fors.h + - hash.h + - sign.c + - thash.h + - utils.c + - utils.h + - wots.c + - wots.h +- source: + scheme: sphincs-shake256-256f-robust + implementation: clean + files: + - params.h +- source: + scheme: sphincs-haraka-256s-robust + implementation: clean + files: + - hash_haraka.c +- source: + scheme: sphincs-haraka-256s-robust + implementation: clean + files: + - thash_haraka_robust.c diff --git a/test/duplicate_consistency/sphincs-haraka-256f-simple_clean.yml b/test/duplicate_consistency/sphincs-haraka-256f-simple_clean.yml new file mode 100644 index 00000000..a378d25a --- /dev/null +++ b/test/duplicate_consistency/sphincs-haraka-256f-simple_clean.yml @@ -0,0 +1,31 @@ +consistency_checks: +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - address.c + - address.h + - fors.c + - fors.h + - hash.h + - sign.c + - thash.h + - utils.c + - utils.h + - wots.c + - wots.h +- source: + scheme: sphincs-shake256-256f-robust + implementation: clean + files: + - params.h +- source: + scheme: sphincs-haraka-256s-robust + implementation: clean + files: + - hash_haraka.c +- source: + scheme: sphincs-haraka-256s-simple + implementation: clean + files: + - thash_haraka_simple.c diff --git a/test/duplicate_consistency/sphincs-haraka-256s-robust_clean.yml b/test/duplicate_consistency/sphincs-haraka-256s-robust_clean.yml new file mode 100644 index 00000000..0865fa3d --- /dev/null +++ b/test/duplicate_consistency/sphincs-haraka-256s-robust_clean.yml @@ -0,0 +1,31 @@ +consistency_checks: +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - address.c + - address.h + - fors.c + - fors.h + - hash.h + - sign.c + - thash.h + - utils.c + - utils.h + - wots.c + - wots.h +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - params.h +- source: + scheme: sphincs-haraka-256s-robust + implementation: clean + files: + - hash_haraka.c +- source: + scheme: sphincs-haraka-256s-robust + implementation: clean + files: + - thash_haraka_robust.c diff --git a/test/duplicate_consistency/sphincs-haraka-256s-simple_clean.yml b/test/duplicate_consistency/sphincs-haraka-256s-simple_clean.yml new file mode 100644 index 00000000..50b13b7e --- /dev/null +++ b/test/duplicate_consistency/sphincs-haraka-256s-simple_clean.yml @@ -0,0 +1,31 @@ +consistency_checks: +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - address.c + - address.h + - fors.c + - fors.h + - hash.h + - sign.c + - thash.h + - utils.c + - utils.h + - wots.c + - wots.h +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - params.h +- source: + scheme: sphincs-haraka-256s-robust + implementation: clean + files: + - hash_haraka.c +- source: + scheme: sphincs-haraka-256s-simple + implementation: clean + files: + - thash_haraka_simple.c diff --git a/test/duplicate_consistency/sphincs-sha256-128f-robust_clean.yml b/test/duplicate_consistency/sphincs-sha256-128f-robust_clean.yml new file mode 100644 index 00000000..3569c280 --- /dev/null +++ b/test/duplicate_consistency/sphincs-sha256-128f-robust_clean.yml @@ -0,0 +1,31 @@ +consistency_checks: +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - address.c + - address.h + - fors.c + - fors.h + - hash.h + - sign.c + - thash.h + - utils.c + - utils.h + - wots.c + - wots.h +- source: + scheme: sphincs-shake256-128f-robust + implementation: clean + files: + - params.h +- source: + scheme: sphincs-sha256-256s-robust + implementation: clean + files: + - hash_sha256.c +- source: + scheme: sphincs-sha256-256s-robust + implementation: clean + files: + - thash_sha256_robust.c diff --git a/test/duplicate_consistency/sphincs-sha256-128f-simple_clean.yml b/test/duplicate_consistency/sphincs-sha256-128f-simple_clean.yml new file mode 100644 index 00000000..e11d6ef0 --- /dev/null +++ b/test/duplicate_consistency/sphincs-sha256-128f-simple_clean.yml @@ -0,0 +1,31 @@ +consistency_checks: +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - address.c + - address.h + - fors.c + - fors.h + - hash.h + - sign.c + - thash.h + - utils.c + - utils.h + - wots.c + - wots.h +- source: + scheme: sphincs-shake256-128f-robust + implementation: clean + files: + - params.h +- source: + scheme: sphincs-sha256-256s-robust + implementation: clean + files: + - hash_sha256.c +- source: + scheme: sphincs-sha256-256s-simple + implementation: clean + files: + - thash_sha256_simple.c diff --git a/test/duplicate_consistency/sphincs-sha256-128s-robust_clean.yml b/test/duplicate_consistency/sphincs-sha256-128s-robust_clean.yml new file mode 100644 index 00000000..3f6ee84f --- /dev/null +++ b/test/duplicate_consistency/sphincs-sha256-128s-robust_clean.yml @@ -0,0 +1,31 @@ +consistency_checks: +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - address.c + - address.h + - fors.c + - fors.h + - hash.h + - sign.c + - thash.h + - utils.c + - utils.h + - wots.c + - wots.h +- source: + scheme: sphincs-shake256-128s-robust + implementation: clean + files: + - params.h +- source: + scheme: sphincs-sha256-256s-robust + implementation: clean + files: + - hash_sha256.c +- source: + scheme: sphincs-sha256-256s-robust + implementation: clean + files: + - thash_sha256_robust.c diff --git a/test/duplicate_consistency/sphincs-sha256-128s-simple_clean.yml b/test/duplicate_consistency/sphincs-sha256-128s-simple_clean.yml new file mode 100644 index 00000000..fba64e51 --- /dev/null +++ b/test/duplicate_consistency/sphincs-sha256-128s-simple_clean.yml @@ -0,0 +1,31 @@ +consistency_checks: +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - address.c + - address.h + - fors.c + - fors.h + - hash.h + - sign.c + - thash.h + - utils.c + - utils.h + - wots.c + - wots.h +- source: + scheme: sphincs-shake256-128s-robust + implementation: clean + files: + - params.h +- source: + scheme: sphincs-sha256-256s-robust + implementation: clean + files: + - hash_sha256.c +- source: + scheme: sphincs-sha256-256s-simple + implementation: clean + files: + - thash_sha256_simple.c diff --git a/test/duplicate_consistency/sphincs-sha256-192f-robust_clean.yml b/test/duplicate_consistency/sphincs-sha256-192f-robust_clean.yml new file mode 100644 index 00000000..0b803e12 --- /dev/null +++ b/test/duplicate_consistency/sphincs-sha256-192f-robust_clean.yml @@ -0,0 +1,31 @@ +consistency_checks: +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - address.c + - address.h + - fors.c + - fors.h + - hash.h + - sign.c + - thash.h + - utils.c + - utils.h + - wots.c + - wots.h +- source: + scheme: sphincs-shake256-192f-robust + implementation: clean + files: + - params.h +- source: + scheme: sphincs-sha256-256s-robust + implementation: clean + files: + - hash_sha256.c +- source: + scheme: sphincs-sha256-256s-robust + implementation: clean + files: + - thash_sha256_robust.c diff --git a/test/duplicate_consistency/sphincs-sha256-192f-simple_clean.yml b/test/duplicate_consistency/sphincs-sha256-192f-simple_clean.yml new file mode 100644 index 00000000..0cc91c41 --- /dev/null +++ b/test/duplicate_consistency/sphincs-sha256-192f-simple_clean.yml @@ -0,0 +1,31 @@ +consistency_checks: +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - address.c + - address.h + - fors.c + - fors.h + - hash.h + - sign.c + - thash.h + - utils.c + - utils.h + - wots.c + - wots.h +- source: + scheme: sphincs-shake256-192f-robust + implementation: clean + files: + - params.h +- source: + scheme: sphincs-sha256-256s-robust + implementation: clean + files: + - hash_sha256.c +- source: + scheme: sphincs-sha256-256s-simple + implementation: clean + files: + - thash_sha256_simple.c diff --git a/test/duplicate_consistency/sphincs-sha256-192s-robust_clean.yml b/test/duplicate_consistency/sphincs-sha256-192s-robust_clean.yml new file mode 100644 index 00000000..09074d4d --- /dev/null +++ b/test/duplicate_consistency/sphincs-sha256-192s-robust_clean.yml @@ -0,0 +1,31 @@ +consistency_checks: +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - address.c + - address.h + - fors.c + - fors.h + - hash.h + - sign.c + - thash.h + - utils.c + - utils.h + - wots.c + - wots.h +- source: + scheme: sphincs-shake256-192s-robust + implementation: clean + files: + - params.h +- source: + scheme: sphincs-sha256-256s-robust + implementation: clean + files: + - hash_sha256.c +- source: + scheme: sphincs-sha256-256s-robust + implementation: clean + files: + - thash_sha256_robust.c diff --git a/test/duplicate_consistency/sphincs-sha256-192s-simple_clean.yml b/test/duplicate_consistency/sphincs-sha256-192s-simple_clean.yml new file mode 100644 index 00000000..f9997876 --- /dev/null +++ b/test/duplicate_consistency/sphincs-sha256-192s-simple_clean.yml @@ -0,0 +1,31 @@ +consistency_checks: +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - address.c + - address.h + - fors.c + - fors.h + - hash.h + - sign.c + - thash.h + - utils.c + - utils.h + - wots.c + - wots.h +- source: + scheme: sphincs-shake256-192s-robust + implementation: clean + files: + - params.h +- source: + scheme: sphincs-sha256-256s-robust + implementation: clean + files: + - hash_sha256.c +- source: + scheme: sphincs-sha256-256s-simple + implementation: clean + files: + - thash_sha256_simple.c diff --git a/test/duplicate_consistency/sphincs-sha256-256f-robust_clean.yml b/test/duplicate_consistency/sphincs-sha256-256f-robust_clean.yml new file mode 100644 index 00000000..95bbb714 --- /dev/null +++ b/test/duplicate_consistency/sphincs-sha256-256f-robust_clean.yml @@ -0,0 +1,31 @@ +consistency_checks: +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - address.c + - address.h + - fors.c + - fors.h + - hash.h + - sign.c + - thash.h + - utils.c + - utils.h + - wots.c + - wots.h +- source: + scheme: sphincs-shake256-256f-robust + implementation: clean + files: + - params.h +- source: + scheme: sphincs-sha256-256s-robust + implementation: clean + files: + - hash_sha256.c +- source: + scheme: sphincs-sha256-256s-robust + implementation: clean + files: + - thash_sha256_robust.c diff --git a/test/duplicate_consistency/sphincs-sha256-256f-simple_clean.yml b/test/duplicate_consistency/sphincs-sha256-256f-simple_clean.yml new file mode 100644 index 00000000..d3e30957 --- /dev/null +++ b/test/duplicate_consistency/sphincs-sha256-256f-simple_clean.yml @@ -0,0 +1,31 @@ +consistency_checks: +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - address.c + - address.h + - fors.c + - fors.h + - hash.h + - sign.c + - thash.h + - utils.c + - utils.h + - wots.c + - wots.h +- source: + scheme: sphincs-shake256-256f-robust + implementation: clean + files: + - params.h +- source: + scheme: sphincs-sha256-256s-robust + implementation: clean + files: + - hash_sha256.c +- source: + scheme: sphincs-sha256-256s-simple + implementation: clean + files: + - thash_sha256_simple.c diff --git a/test/duplicate_consistency/sphincs-sha256-256s-robust_clean.yml b/test/duplicate_consistency/sphincs-sha256-256s-robust_clean.yml new file mode 100644 index 00000000..68df69a6 --- /dev/null +++ b/test/duplicate_consistency/sphincs-sha256-256s-robust_clean.yml @@ -0,0 +1,31 @@ +consistency_checks: +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - address.c + - address.h + - fors.c + - fors.h + - hash.h + - sign.c + - thash.h + - utils.c + - utils.h + - wots.c + - wots.h +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - params.h +- source: + scheme: sphincs-sha256-256s-robust + implementation: clean + files: + - hash_sha256.c +- source: + scheme: sphincs-sha256-256s-robust + implementation: clean + files: + - thash_sha256_robust.c diff --git a/test/duplicate_consistency/sphincs-sha256-256s-simple_clean.yml b/test/duplicate_consistency/sphincs-sha256-256s-simple_clean.yml new file mode 100644 index 00000000..d5da9791 --- /dev/null +++ b/test/duplicate_consistency/sphincs-sha256-256s-simple_clean.yml @@ -0,0 +1,31 @@ +consistency_checks: +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - address.c + - address.h + - fors.c + - fors.h + - hash.h + - sign.c + - thash.h + - utils.c + - utils.h + - wots.c + - wots.h +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - params.h +- source: + scheme: sphincs-sha256-256s-robust + implementation: clean + files: + - hash_sha256.c +- source: + scheme: sphincs-sha256-256s-simple + implementation: clean + files: + - thash_sha256_simple.c diff --git a/test/duplicate_consistency/sphincs-shake256-128f-robust_clean.yml b/test/duplicate_consistency/sphincs-shake256-128f-robust_clean.yml new file mode 100644 index 00000000..022c0c43 --- /dev/null +++ b/test/duplicate_consistency/sphincs-shake256-128f-robust_clean.yml @@ -0,0 +1,31 @@ +consistency_checks: +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - address.c + - address.h + - fors.c + - fors.h + - hash.h + - sign.c + - thash.h + - utils.c + - utils.h + - wots.c + - wots.h +- source: + scheme: sphincs-shake256-128f-robust + implementation: clean + files: + - params.h +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - hash_shake256.c +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - thash_shake256_robust.c diff --git a/test/duplicate_consistency/sphincs-shake256-128f-simple_clean.yml b/test/duplicate_consistency/sphincs-shake256-128f-simple_clean.yml new file mode 100644 index 00000000..16d118bd --- /dev/null +++ b/test/duplicate_consistency/sphincs-shake256-128f-simple_clean.yml @@ -0,0 +1,31 @@ +consistency_checks: +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - address.c + - address.h + - fors.c + - fors.h + - hash.h + - sign.c + - thash.h + - utils.c + - utils.h + - wots.c + - wots.h +- source: + scheme: sphincs-shake256-128f-robust + implementation: clean + files: + - params.h +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - hash_shake256.c +- source: + scheme: sphincs-shake256-256s-simple + implementation: clean + files: + - thash_shake256_simple.c diff --git a/test/duplicate_consistency/sphincs-shake256-128s-robust_clean.yml b/test/duplicate_consistency/sphincs-shake256-128s-robust_clean.yml new file mode 100644 index 00000000..45e99938 --- /dev/null +++ b/test/duplicate_consistency/sphincs-shake256-128s-robust_clean.yml @@ -0,0 +1,31 @@ +consistency_checks: +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - address.c + - address.h + - fors.c + - fors.h + - hash.h + - sign.c + - thash.h + - utils.c + - utils.h + - wots.c + - wots.h +- source: + scheme: sphincs-shake256-128s-robust + implementation: clean + files: + - params.h +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - hash_shake256.c +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - thash_shake256_robust.c diff --git a/test/duplicate_consistency/sphincs-shake256-128s-simple_clean.yml b/test/duplicate_consistency/sphincs-shake256-128s-simple_clean.yml new file mode 100644 index 00000000..202d02b2 --- /dev/null +++ b/test/duplicate_consistency/sphincs-shake256-128s-simple_clean.yml @@ -0,0 +1,31 @@ +consistency_checks: +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - address.c + - address.h + - fors.c + - fors.h + - hash.h + - sign.c + - thash.h + - utils.c + - utils.h + - wots.c + - wots.h +- source: + scheme: sphincs-shake256-128s-robust + implementation: clean + files: + - params.h +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - hash_shake256.c +- source: + scheme: sphincs-shake256-256s-simple + implementation: clean + files: + - thash_shake256_simple.c diff --git a/test/duplicate_consistency/sphincs-shake256-192f-robust_clean.yml b/test/duplicate_consistency/sphincs-shake256-192f-robust_clean.yml new file mode 100644 index 00000000..5691560d --- /dev/null +++ b/test/duplicate_consistency/sphincs-shake256-192f-robust_clean.yml @@ -0,0 +1,31 @@ +consistency_checks: +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - address.c + - address.h + - fors.c + - fors.h + - hash.h + - sign.c + - thash.h + - utils.c + - utils.h + - wots.c + - wots.h +- source: + scheme: sphincs-shake256-192f-robust + implementation: clean + files: + - params.h +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - hash_shake256.c +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - thash_shake256_robust.c diff --git a/test/duplicate_consistency/sphincs-shake256-192f-simple_clean.yml b/test/duplicate_consistency/sphincs-shake256-192f-simple_clean.yml new file mode 100644 index 00000000..c90ae314 --- /dev/null +++ b/test/duplicate_consistency/sphincs-shake256-192f-simple_clean.yml @@ -0,0 +1,31 @@ +consistency_checks: +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - address.c + - address.h + - fors.c + - fors.h + - hash.h + - sign.c + - thash.h + - utils.c + - utils.h + - wots.c + - wots.h +- source: + scheme: sphincs-shake256-192f-robust + implementation: clean + files: + - params.h +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - hash_shake256.c +- source: + scheme: sphincs-shake256-256s-simple + implementation: clean + files: + - thash_shake256_simple.c diff --git a/test/duplicate_consistency/sphincs-shake256-192s-robust_clean.yml b/test/duplicate_consistency/sphincs-shake256-192s-robust_clean.yml new file mode 100644 index 00000000..c9729217 --- /dev/null +++ b/test/duplicate_consistency/sphincs-shake256-192s-robust_clean.yml @@ -0,0 +1,31 @@ +consistency_checks: +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - address.c + - address.h + - fors.c + - fors.h + - hash.h + - sign.c + - thash.h + - utils.c + - utils.h + - wots.c + - wots.h +- source: + scheme: sphincs-shake256-192s-robust + implementation: clean + files: + - params.h +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - hash_shake256.c +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - thash_shake256_robust.c diff --git a/test/duplicate_consistency/sphincs-shake256-192s-simple_clean.yml b/test/duplicate_consistency/sphincs-shake256-192s-simple_clean.yml new file mode 100644 index 00000000..28790a2a --- /dev/null +++ b/test/duplicate_consistency/sphincs-shake256-192s-simple_clean.yml @@ -0,0 +1,31 @@ +consistency_checks: +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - address.c + - address.h + - fors.c + - fors.h + - hash.h + - sign.c + - thash.h + - utils.c + - utils.h + - wots.c + - wots.h +- source: + scheme: sphincs-shake256-192s-robust + implementation: clean + files: + - params.h +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - hash_shake256.c +- source: + scheme: sphincs-shake256-256s-simple + implementation: clean + files: + - thash_shake256_simple.c diff --git a/test/duplicate_consistency/sphincs-shake256-256f-robust_clean.yml b/test/duplicate_consistency/sphincs-shake256-256f-robust_clean.yml new file mode 100644 index 00000000..7e8ef0d7 --- /dev/null +++ b/test/duplicate_consistency/sphincs-shake256-256f-robust_clean.yml @@ -0,0 +1,31 @@ +consistency_checks: +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - address.c + - address.h + - fors.c + - fors.h + - hash.h + - sign.c + - thash.h + - utils.c + - utils.h + - wots.c + - wots.h +- source: + scheme: sphincs-shake256-256f-robust + implementation: clean + files: + - params.h +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - hash_shake256.c +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - thash_shake256_robust.c diff --git a/test/duplicate_consistency/sphincs-shake256-256f-simple_clean.yml b/test/duplicate_consistency/sphincs-shake256-256f-simple_clean.yml new file mode 100644 index 00000000..69789347 --- /dev/null +++ b/test/duplicate_consistency/sphincs-shake256-256f-simple_clean.yml @@ -0,0 +1,31 @@ +consistency_checks: +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - address.c + - address.h + - fors.c + - fors.h + - hash.h + - sign.c + - thash.h + - utils.c + - utils.h + - wots.c + - wots.h +- source: + scheme: sphincs-shake256-256f-robust + implementation: clean + files: + - params.h +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - hash_shake256.c +- source: + scheme: sphincs-shake256-256s-simple + implementation: clean + files: + - thash_shake256_simple.c diff --git a/test/duplicate_consistency/sphincs-shake256-256s-robust_clean.yml b/test/duplicate_consistency/sphincs-shake256-256s-robust_clean.yml new file mode 100644 index 00000000..636dd828 --- /dev/null +++ b/test/duplicate_consistency/sphincs-shake256-256s-robust_clean.yml @@ -0,0 +1,31 @@ +consistency_checks: +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - address.c + - address.h + - fors.c + - fors.h + - hash.h + - sign.c + - thash.h + - utils.c + - utils.h + - wots.c + - wots.h +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - params.h +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - hash_shake256.c +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - thash_shake256_robust.c diff --git a/test/duplicate_consistency/sphincs-shake256-256s-simple_clean.yml b/test/duplicate_consistency/sphincs-shake256-256s-simple_clean.yml new file mode 100644 index 00000000..153ee6fb --- /dev/null +++ b/test/duplicate_consistency/sphincs-shake256-256s-simple_clean.yml @@ -0,0 +1,31 @@ +consistency_checks: +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - address.c + - address.h + - fors.c + - fors.h + - hash.h + - sign.c + - thash.h + - utils.c + - utils.h + - wots.c + - wots.h +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - params.h +- source: + scheme: sphincs-shake256-256s-robust + implementation: clean + files: + - hash_shake256.c +- source: + scheme: sphincs-shake256-256s-simple + implementation: clean + files: + - thash_shake256_simple.c