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..95b0df3f --- /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-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/492ec4f1f6d3b3dc4b435783bbaaf4e41cdb6f32 + length-secret-key: 64 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..9716e43e --- /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=-Wall -Wconversion -Wextra -Wpedantic -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..24858deb --- /dev/null +++ b/crypto_sign/sphincs-haraka-128f-robust/clean/haraka.c @@ -0,0 +1,373 @@ +/* +Plain C implementation of the Haraka256 and Haraka512 permutations. +*/ +#include +#include +#include + +#include "haraka.h" + +#define HARAKAS_RATE 32 + +static const unsigned char haraka_rc[40][16] = { + {0x9d, 0x7b, 0x81, 0x75, 0xf0, 0xfe, 0xc5, 0xb2, 0x0a, 0xc0, 0x20, 0xe6, 0x4c, 0x70, 0x84, 0x06}, + {0x17, 0xf7, 0x08, 0x2f, 0xa4, 0x6b, 0x0f, 0x64, 0x6b, 0xa0, 0xf3, 0x88, 0xe1, 0xb4, 0x66, 0x8b}, + {0x14, 0x91, 0x02, 0x9f, 0x60, 0x9d, 0x02, 0xcf, 0x98, 0x84, 0xf2, 0x53, 0x2d, 0xde, 0x02, 0x34}, + {0x79, 0x4f, 0x5b, 0xfd, 0xaf, 0xbc, 0xf3, 0xbb, 0x08, 0x4f, 0x7b, 0x2e, 0xe6, 0xea, 0xd6, 0x0e}, + {0x44, 0x70, 0x39, 0xbe, 0x1c, 0xcd, 0xee, 0x79, 0x8b, 0x44, 0x72, 0x48, 0xcb, 0xb0, 0xcf, 0xcb}, + {0x7b, 0x05, 0x8a, 0x2b, 0xed, 0x35, 0x53, 0x8d, 0xb7, 0x32, 0x90, 0x6e, 0xee, 0xcd, 0xea, 0x7e}, + {0x1b, 0xef, 0x4f, 0xda, 0x61, 0x27, 0x41, 0xe2, 0xd0, 0x7c, 0x2e, 0x5e, 0x43, 0x8f, 0xc2, 0x67}, + {0x3b, 0x0b, 0xc7, 0x1f, 0xe2, 0xfd, 0x5f, 0x67, 0x07, 0xcc, 0xca, 0xaf, 0xb0, 0xd9, 0x24, 0x29}, + {0xee, 0x65, 0xd4, 0xb9, 0xca, 0x8f, 0xdb, 0xec, 0xe9, 0x7f, 0x86, 0xe6, 0xf1, 0x63, 0x4d, 0xab}, + {0x33, 0x7e, 0x03, 0xad, 0x4f, 0x40, 0x2a, 0x5b, 0x64, 0xcd, 0xb7, 0xd4, 0x84, 0xbf, 0x30, 0x1c}, + {0x00, 0x98, 0xf6, 0x8d, 0x2e, 0x8b, 0x02, 0x69, 0xbf, 0x23, 0x17, 0x94, 0xb9, 0x0b, 0xcc, 0xb2}, + {0x8a, 0x2d, 0x9d, 0x5c, 0xc8, 0x9e, 0xaa, 0x4a, 0x72, 0x55, 0x6f, 0xde, 0xa6, 0x78, 0x04, 0xfa}, + {0xd4, 0x9f, 0x12, 0x29, 0x2e, 0x4f, 0xfa, 0x0e, 0x12, 0x2a, 0x77, 0x6b, 0x2b, 0x9f, 0xb4, 0xdf}, + {0xee, 0x12, 0x6a, 0xbb, 0xae, 0x11, 0xd6, 0x32, 0x36, 0xa2, 0x49, 0xf4, 0x44, 0x03, 0xa1, 0x1e}, + {0xa6, 0xec, 0xa8, 0x9c, 0xc9, 0x00, 0x96, 0x5f, 0x84, 0x00, 0x05, 0x4b, 0x88, 0x49, 0x04, 0xaf}, + {0xec, 0x93, 0xe5, 0x27, 0xe3, 0xc7, 0xa2, 0x78, 0x4f, 0x9c, 0x19, 0x9d, 0xd8, 0x5e, 0x02, 0x21}, + {0x73, 0x01, 0xd4, 0x82, 0xcd, 0x2e, 0x28, 0xb9, 0xb7, 0xc9, 0x59, 0xa7, 0xf8, 0xaa, 0x3a, 0xbf}, + {0x6b, 0x7d, 0x30, 0x10, 0xd9, 0xef, 0xf2, 0x37, 0x17, 0xb0, 0x86, 0x61, 0x0d, 0x70, 0x60, 0x62}, + {0xc6, 0x9a, 0xfc, 0xf6, 0x53, 0x91, 0xc2, 0x81, 0x43, 0x04, 0x30, 0x21, 0xc2, 0x45, 0xca, 0x5a}, + {0x3a, 0x94, 0xd1, 0x36, 0xe8, 0x92, 0xaf, 0x2c, 0xbb, 0x68, 0x6b, 0x22, 0x3c, 0x97, 0x23, 0x92}, + {0xb4, 0x71, 0x10, 0xe5, 0x58, 0xb9, 0xba, 0x6c, 0xeb, 0x86, 0x58, 0x22, 0x38, 0x92, 0xbf, 0xd3}, + {0x8d, 0x12, 0xe1, 0x24, 0xdd, 0xfd, 0x3d, 0x93, 0x77, 0xc6, 0xf0, 0xae, 0xe5, 0x3c, 0x86, 0xdb}, + {0xb1, 0x12, 0x22, 0xcb, 0xe3, 0x8d, 0xe4, 0x83, 0x9c, 0xa0, 0xeb, 0xff, 0x68, 0x62, 0x60, 0xbb}, + {0x7d, 0xf7, 0x2b, 0xc7, 0x4e, 0x1a, 0xb9, 0x2d, 0x9c, 0xd1, 0xe4, 0xe2, 0xdc, 0xd3, 0x4b, 0x73}, + {0x4e, 0x92, 0xb3, 0x2c, 0xc4, 0x15, 0x14, 0x4b, 0x43, 0x1b, 0x30, 0x61, 0xc3, 0x47, 0xbb, 0x43}, + {0x99, 0x68, 0xeb, 0x16, 0xdd, 0x31, 0xb2, 0x03, 0xf6, 0xef, 0x07, 0xe7, 0xa8, 0x75, 0xa7, 0xdb}, + {0x2c, 0x47, 0xca, 0x7e, 0x02, 0x23, 0x5e, 0x8e, 0x77, 0x59, 0x75, 0x3c, 0x4b, 0x61, 0xf3, 0x6d}, + {0xf9, 0x17, 0x86, 0xb8, 0xb9, 0xe5, 0x1b, 0x6d, 0x77, 0x7d, 0xde, 0xd6, 0x17, 0x5a, 0xa7, 0xcd}, + {0x5d, 0xee, 0x46, 0xa9, 0x9d, 0x06, 0x6c, 0x9d, 0xaa, 0xe9, 0xa8, 0x6b, 0xf0, 0x43, 0x6b, 0xec}, + {0xc1, 0x27, 0xf3, 0x3b, 0x59, 0x11, 0x53, 0xa2, 0x2b, 0x33, 0x57, 0xf9, 0x50, 0x69, 0x1e, 0xcb}, + {0xd9, 0xd0, 0x0e, 0x60, 0x53, 0x03, 0xed, 0xe4, 0x9c, 0x61, 0xda, 0x00, 0x75, 0x0c, 0xee, 0x2c}, + {0x50, 0xa3, 0xa4, 0x63, 0xbc, 0xba, 0xbb, 0x80, 0xab, 0x0c, 0xe9, 0x96, 0xa1, 0xa5, 0xb1, 0xf0}, + {0x39, 0xca, 0x8d, 0x93, 0x30, 0xde, 0x0d, 0xab, 0x88, 0x29, 0x96, 0x5e, 0x02, 0xb1, 0x3d, 0xae}, + {0x42, 0xb4, 0x75, 0x2e, 0xa8, 0xf3, 0x14, 0x88, 0x0b, 0xa4, 0x54, 0xd5, 0x38, 0x8f, 0xbb, 0x17}, + {0xf6, 0x16, 0x0a, 0x36, 0x79, 0xb7, 0xb6, 0xae, 0xd7, 0x7f, 0x42, 0x5f, 0x5b, 0x8a, 0xbb, 0x34}, + {0xde, 0xaf, 0xba, 0xff, 0x18, 0x59, 0xce, 0x43, 0x38, 0x54, 0xe5, 0xcb, 0x41, 0x52, 0xf6, 0x26}, + {0x78, 0xc9, 0x9e, 0x83, 0xf7, 0x9c, 0xca, 0xa2, 0x6a, 0x02, 0xf3, 0xb9, 0x54, 0x9a, 0xe9, 0x4c}, + {0x35, 0x12, 0x90, 0x22, 0x28, 0x6e, 0xc0, 0x40, 0xbe, 0xf7, 0xdf, 0x1b, 0x1a, 0xa5, 0x51, 0xae}, + {0xcf, 0x59, 0xa6, 0x48, 0x0f, 0xbc, 0x73, 0xc1, 0x2b, 0xd2, 0x7e, 0xba, 0x3c, 0x61, 0xc1, 0xa0}, + {0xa1, 0x9d, 0xc5, 0xe9, 0xfd, 0xbd, 0xd6, 0x4a, 0x88, 0x82, 0x28, 0x02, 0x03, 0xcc, 0x6a, 0x75} +}; + +static unsigned char rc[40][16]; +static unsigned char rc_sseed[40][16]; + +static const unsigned char sbox[256] = { + 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, + 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, + 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, + 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, + 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, + 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, + 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, + 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, + 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, + 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, + 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, + 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, + 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, + 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, + 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, + 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, + 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, + 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, + 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, + 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 +}; + +#define XT(x) (((x) << 1) ^ ((((x) >> 7) & 1) * 0x1b)) + +// Simulate _mm_aesenc_si128 instructions from AESNI +static void aesenc(unsigned char *s, const unsigned char *rk) { + uint8_t i, t, u, v[4][4]; + for (i = 0; i < 16; ++i) { + v[((i / 4) + 4 - (i % 4) ) % 4][i % 4] = sbox[s[i]]; + } + for (i = 0; i < 4; ++i) { + t = v[i][0]; + u = v[i][0] ^ v[i][1] ^ v[i][2] ^ v[i][3]; + v[i][0] ^= (uint8_t)(u ^ XT(v[i][0] ^ v[i][1])); + v[i][1] ^= (uint8_t)(u ^ XT(v[i][1] ^ v[i][2])); + v[i][2] ^= (uint8_t)(u ^ XT(v[i][2] ^ v[i][3])); + v[i][3] ^= (uint8_t)(u ^ XT(v[i][3] ^ t)); + } + for (i = 0; i < 16; ++i) { + s[i] = v[i / 4][i % 4] ^ rk[i]; + } +} + +// Simulate _mm_unpacklo_epi32 +static void unpacklo32(unsigned char *t, unsigned char *a, unsigned char *b) { + unsigned char tmp[16]; + memcpy(tmp, a, 4); + memcpy(tmp + 4, b, 4); + memcpy(tmp + 8, a + 4, 4); + memcpy(tmp + 12, b + 4, 4); + memcpy(t, tmp, 16); +} + +// Simulate _mm_unpackhi_epi32 +static void unpackhi32(unsigned char *t, unsigned char *a, unsigned char *b) { + unsigned char tmp[16]; + memcpy(tmp, a + 8, 4); + memcpy(tmp + 4, b + 8, 4); + memcpy(tmp + 8, a + 12, 4); + memcpy(tmp + 12, b + 12, 4); + memcpy(t, tmp, 16); +} + +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]; + + /* Use the standard constants to generate tweaked ones. */ + memcpy(rc, haraka_rc, 40 * 16); + + /* Constants for sk.seed */ + if (sk_seed != NULL) { + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka_S(buf, 40 * 16, sk_seed, seed_length); + memcpy(rc_sseed, buf, 40 * 16); + } + + /* Constants for pk.seed */ + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka_S(buf, 40 * 16, pk_seed, seed_length); + memcpy(rc, buf, 40 * 16); +} + +static void haraka_S_absorb(unsigned char *s, unsigned int r, + const unsigned char *m, unsigned long long mlen, + unsigned char p) { + unsigned long long i; + unsigned char t[r]; + + while (mlen >= r) { + // XOR block to state + for (i = 0; i < r; ++i) { + s[i] ^= m[i]; + } + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka512_perm(s, s); + mlen -= r; + m += r; + } + + for (i = 0; i < r; ++i) { + t[i] = 0; + } + for (i = 0; i < mlen; ++i) { + t[i] = m[i]; + } + t[i] = p; + t[r - 1] |= 128; + for (i = 0; i < r; ++i) { + s[i] ^= t[i]; + } +} + +static void haraka_S_squeezeblocks(unsigned char *h, unsigned long long nblocks, + unsigned char *s, unsigned int r) { + while (nblocks > 0) { + PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka512_perm(s, s); + memcpy(h, s, HARAKAS_RATE); + h += r; + 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, 32, in, inlen, 0x1F); + + haraka_S_squeezeblocks(out, outlen / 32, s, 32); + out += (outlen / 32) * 32; + + if (outlen % 32) { + haraka_S_squeezeblocks(d, 1, s, 32); + for (i = 0; i < outlen % 32; i++) { + out[i] = d[i]; + } + } +} + +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka512_perm(unsigned char *out, const unsigned char *in) { + int i, j; + + unsigned char s[64], tmp[16]; + + memcpy(s, in, 16); + memcpy(s + 16, in + 16, 16); + memcpy(s + 32, in + 32, 16); + memcpy(s + 48, in + 48, 16); + + for (i = 0; i < 5; ++i) { + // aes round(s) + for (j = 0; j < 2; ++j) { + aesenc(s, rc[4 * 2 * i + 4 * j]); + aesenc(s + 16, rc[4 * 2 * i + 4 * j + 1]); + aesenc(s + 32, rc[4 * 2 * i + 4 * j + 2]); + aesenc(s + 48, rc[4 * 2 * i + 4 * j + 3]); + } + + // mixing + unpacklo32(tmp, s, s + 16); + unpackhi32(s, s, s + 16); + unpacklo32(s + 16, s + 32, s + 48); + unpackhi32(s + 32, s + 32, s + 48); + unpacklo32(s + 48, s, s + 32); + unpackhi32(s, s, s + 32); + unpackhi32(s + 32, s + 16, tmp); + unpacklo32(s + 16, s + 16, tmp); + } + + memcpy(out, s, 64); +} + +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) { + int i, j; + + unsigned char s[32], tmp[16]; + + memcpy(s, in, 16); + memcpy(s + 16, in + 16, 16); + + for (i = 0; i < 5; ++i) { + // aes round(s) + for (j = 0; j < 2; ++j) { + aesenc(s, rc[2 * 2 * i + 2 * j]); + aesenc(s + 16, rc[2 * 2 * i + 2 * j + 1]); + } + + // mixing + unpacklo32(tmp, s, s + 16); + unpackhi32(s + 16, s, s + 16); + memcpy(s, tmp, 16); + } + + /* Feed-forward */ + for (i = 0; i < 32; i++) { + out[i] = in[i] ^ s[i]; + } +} + +void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka256_sk(unsigned char *out, const unsigned char *in) { + int i, j; + + unsigned char s[32], tmp[16]; + + memcpy(s, in, 16); + memcpy(s + 16, in + 16, 16); + + for (i = 0; i < 5; ++i) { + // aes round(s) + for (j = 0; j < 2; ++j) { + aesenc(s, rc_sseed[2 * 2 * i + 2 * j]); + aesenc(s + 16, rc_sseed[2 * 2 * i + 2 * j + 1]); + } + + // mixing + unpacklo32(tmp, s, s + 16); + unpackhi32(s + 16, s, s + 16); + memcpy(s, tmp, 16); + } + + /* Feed-forward */ + for (i = 0; i < 32; i++) { + out[i] = in[i] ^ s[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..de673b17 --- /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-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/492ec4f1f6d3b3dc4b435783bbaaf4e41cdb6f32 + length-secret-key: 64 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..502a4585 --- /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=-Wall -Wconversion -Wextra -Wpedantic -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..d53b16a8 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128f-simple/clean/haraka.c @@ -0,0 +1,373 @@ +/* +Plain C implementation of the Haraka256 and Haraka512 permutations. +*/ +#include +#include +#include + +#include "haraka.h" + +#define HARAKAS_RATE 32 + +static const unsigned char haraka_rc[40][16] = { + {0x9d, 0x7b, 0x81, 0x75, 0xf0, 0xfe, 0xc5, 0xb2, 0x0a, 0xc0, 0x20, 0xe6, 0x4c, 0x70, 0x84, 0x06}, + {0x17, 0xf7, 0x08, 0x2f, 0xa4, 0x6b, 0x0f, 0x64, 0x6b, 0xa0, 0xf3, 0x88, 0xe1, 0xb4, 0x66, 0x8b}, + {0x14, 0x91, 0x02, 0x9f, 0x60, 0x9d, 0x02, 0xcf, 0x98, 0x84, 0xf2, 0x53, 0x2d, 0xde, 0x02, 0x34}, + {0x79, 0x4f, 0x5b, 0xfd, 0xaf, 0xbc, 0xf3, 0xbb, 0x08, 0x4f, 0x7b, 0x2e, 0xe6, 0xea, 0xd6, 0x0e}, + {0x44, 0x70, 0x39, 0xbe, 0x1c, 0xcd, 0xee, 0x79, 0x8b, 0x44, 0x72, 0x48, 0xcb, 0xb0, 0xcf, 0xcb}, + {0x7b, 0x05, 0x8a, 0x2b, 0xed, 0x35, 0x53, 0x8d, 0xb7, 0x32, 0x90, 0x6e, 0xee, 0xcd, 0xea, 0x7e}, + {0x1b, 0xef, 0x4f, 0xda, 0x61, 0x27, 0x41, 0xe2, 0xd0, 0x7c, 0x2e, 0x5e, 0x43, 0x8f, 0xc2, 0x67}, + {0x3b, 0x0b, 0xc7, 0x1f, 0xe2, 0xfd, 0x5f, 0x67, 0x07, 0xcc, 0xca, 0xaf, 0xb0, 0xd9, 0x24, 0x29}, + {0xee, 0x65, 0xd4, 0xb9, 0xca, 0x8f, 0xdb, 0xec, 0xe9, 0x7f, 0x86, 0xe6, 0xf1, 0x63, 0x4d, 0xab}, + {0x33, 0x7e, 0x03, 0xad, 0x4f, 0x40, 0x2a, 0x5b, 0x64, 0xcd, 0xb7, 0xd4, 0x84, 0xbf, 0x30, 0x1c}, + {0x00, 0x98, 0xf6, 0x8d, 0x2e, 0x8b, 0x02, 0x69, 0xbf, 0x23, 0x17, 0x94, 0xb9, 0x0b, 0xcc, 0xb2}, + {0x8a, 0x2d, 0x9d, 0x5c, 0xc8, 0x9e, 0xaa, 0x4a, 0x72, 0x55, 0x6f, 0xde, 0xa6, 0x78, 0x04, 0xfa}, + {0xd4, 0x9f, 0x12, 0x29, 0x2e, 0x4f, 0xfa, 0x0e, 0x12, 0x2a, 0x77, 0x6b, 0x2b, 0x9f, 0xb4, 0xdf}, + {0xee, 0x12, 0x6a, 0xbb, 0xae, 0x11, 0xd6, 0x32, 0x36, 0xa2, 0x49, 0xf4, 0x44, 0x03, 0xa1, 0x1e}, + {0xa6, 0xec, 0xa8, 0x9c, 0xc9, 0x00, 0x96, 0x5f, 0x84, 0x00, 0x05, 0x4b, 0x88, 0x49, 0x04, 0xaf}, + {0xec, 0x93, 0xe5, 0x27, 0xe3, 0xc7, 0xa2, 0x78, 0x4f, 0x9c, 0x19, 0x9d, 0xd8, 0x5e, 0x02, 0x21}, + {0x73, 0x01, 0xd4, 0x82, 0xcd, 0x2e, 0x28, 0xb9, 0xb7, 0xc9, 0x59, 0xa7, 0xf8, 0xaa, 0x3a, 0xbf}, + {0x6b, 0x7d, 0x30, 0x10, 0xd9, 0xef, 0xf2, 0x37, 0x17, 0xb0, 0x86, 0x61, 0x0d, 0x70, 0x60, 0x62}, + {0xc6, 0x9a, 0xfc, 0xf6, 0x53, 0x91, 0xc2, 0x81, 0x43, 0x04, 0x30, 0x21, 0xc2, 0x45, 0xca, 0x5a}, + {0x3a, 0x94, 0xd1, 0x36, 0xe8, 0x92, 0xaf, 0x2c, 0xbb, 0x68, 0x6b, 0x22, 0x3c, 0x97, 0x23, 0x92}, + {0xb4, 0x71, 0x10, 0xe5, 0x58, 0xb9, 0xba, 0x6c, 0xeb, 0x86, 0x58, 0x22, 0x38, 0x92, 0xbf, 0xd3}, + {0x8d, 0x12, 0xe1, 0x24, 0xdd, 0xfd, 0x3d, 0x93, 0x77, 0xc6, 0xf0, 0xae, 0xe5, 0x3c, 0x86, 0xdb}, + {0xb1, 0x12, 0x22, 0xcb, 0xe3, 0x8d, 0xe4, 0x83, 0x9c, 0xa0, 0xeb, 0xff, 0x68, 0x62, 0x60, 0xbb}, + {0x7d, 0xf7, 0x2b, 0xc7, 0x4e, 0x1a, 0xb9, 0x2d, 0x9c, 0xd1, 0xe4, 0xe2, 0xdc, 0xd3, 0x4b, 0x73}, + {0x4e, 0x92, 0xb3, 0x2c, 0xc4, 0x15, 0x14, 0x4b, 0x43, 0x1b, 0x30, 0x61, 0xc3, 0x47, 0xbb, 0x43}, + {0x99, 0x68, 0xeb, 0x16, 0xdd, 0x31, 0xb2, 0x03, 0xf6, 0xef, 0x07, 0xe7, 0xa8, 0x75, 0xa7, 0xdb}, + {0x2c, 0x47, 0xca, 0x7e, 0x02, 0x23, 0x5e, 0x8e, 0x77, 0x59, 0x75, 0x3c, 0x4b, 0x61, 0xf3, 0x6d}, + {0xf9, 0x17, 0x86, 0xb8, 0xb9, 0xe5, 0x1b, 0x6d, 0x77, 0x7d, 0xde, 0xd6, 0x17, 0x5a, 0xa7, 0xcd}, + {0x5d, 0xee, 0x46, 0xa9, 0x9d, 0x06, 0x6c, 0x9d, 0xaa, 0xe9, 0xa8, 0x6b, 0xf0, 0x43, 0x6b, 0xec}, + {0xc1, 0x27, 0xf3, 0x3b, 0x59, 0x11, 0x53, 0xa2, 0x2b, 0x33, 0x57, 0xf9, 0x50, 0x69, 0x1e, 0xcb}, + {0xd9, 0xd0, 0x0e, 0x60, 0x53, 0x03, 0xed, 0xe4, 0x9c, 0x61, 0xda, 0x00, 0x75, 0x0c, 0xee, 0x2c}, + {0x50, 0xa3, 0xa4, 0x63, 0xbc, 0xba, 0xbb, 0x80, 0xab, 0x0c, 0xe9, 0x96, 0xa1, 0xa5, 0xb1, 0xf0}, + {0x39, 0xca, 0x8d, 0x93, 0x30, 0xde, 0x0d, 0xab, 0x88, 0x29, 0x96, 0x5e, 0x02, 0xb1, 0x3d, 0xae}, + {0x42, 0xb4, 0x75, 0x2e, 0xa8, 0xf3, 0x14, 0x88, 0x0b, 0xa4, 0x54, 0xd5, 0x38, 0x8f, 0xbb, 0x17}, + {0xf6, 0x16, 0x0a, 0x36, 0x79, 0xb7, 0xb6, 0xae, 0xd7, 0x7f, 0x42, 0x5f, 0x5b, 0x8a, 0xbb, 0x34}, + {0xde, 0xaf, 0xba, 0xff, 0x18, 0x59, 0xce, 0x43, 0x38, 0x54, 0xe5, 0xcb, 0x41, 0x52, 0xf6, 0x26}, + {0x78, 0xc9, 0x9e, 0x83, 0xf7, 0x9c, 0xca, 0xa2, 0x6a, 0x02, 0xf3, 0xb9, 0x54, 0x9a, 0xe9, 0x4c}, + {0x35, 0x12, 0x90, 0x22, 0x28, 0x6e, 0xc0, 0x40, 0xbe, 0xf7, 0xdf, 0x1b, 0x1a, 0xa5, 0x51, 0xae}, + {0xcf, 0x59, 0xa6, 0x48, 0x0f, 0xbc, 0x73, 0xc1, 0x2b, 0xd2, 0x7e, 0xba, 0x3c, 0x61, 0xc1, 0xa0}, + {0xa1, 0x9d, 0xc5, 0xe9, 0xfd, 0xbd, 0xd6, 0x4a, 0x88, 0x82, 0x28, 0x02, 0x03, 0xcc, 0x6a, 0x75} +}; + +static unsigned char rc[40][16]; +static unsigned char rc_sseed[40][16]; + +static const unsigned char sbox[256] = { + 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, + 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, + 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, + 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, + 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, + 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, + 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, + 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, + 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, + 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, + 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, + 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, + 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, + 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, + 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, + 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, + 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, + 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, + 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, + 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 +}; + +#define XT(x) (((x) << 1) ^ ((((x) >> 7) & 1) * 0x1b)) + +// Simulate _mm_aesenc_si128 instructions from AESNI +static void aesenc(unsigned char *s, const unsigned char *rk) { + uint8_t i, t, u, v[4][4]; + for (i = 0; i < 16; ++i) { + v[((i / 4) + 4 - (i % 4) ) % 4][i % 4] = sbox[s[i]]; + } + for (i = 0; i < 4; ++i) { + t = v[i][0]; + u = v[i][0] ^ v[i][1] ^ v[i][2] ^ v[i][3]; + v[i][0] ^= (uint8_t)(u ^ XT(v[i][0] ^ v[i][1])); + v[i][1] ^= (uint8_t)(u ^ XT(v[i][1] ^ v[i][2])); + v[i][2] ^= (uint8_t)(u ^ XT(v[i][2] ^ v[i][3])); + v[i][3] ^= (uint8_t)(u ^ XT(v[i][3] ^ t)); + } + for (i = 0; i < 16; ++i) { + s[i] = v[i / 4][i % 4] ^ rk[i]; + } +} + +// Simulate _mm_unpacklo_epi32 +static void unpacklo32(unsigned char *t, unsigned char *a, unsigned char *b) { + unsigned char tmp[16]; + memcpy(tmp, a, 4); + memcpy(tmp + 4, b, 4); + memcpy(tmp + 8, a + 4, 4); + memcpy(tmp + 12, b + 4, 4); + memcpy(t, tmp, 16); +} + +// Simulate _mm_unpackhi_epi32 +static void unpackhi32(unsigned char *t, unsigned char *a, unsigned char *b) { + unsigned char tmp[16]; + memcpy(tmp, a + 8, 4); + memcpy(tmp + 4, b + 8, 4); + memcpy(tmp + 8, a + 12, 4); + memcpy(tmp + 12, b + 12, 4); + memcpy(t, tmp, 16); +} + +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]; + + /* Use the standard constants to generate tweaked ones. */ + memcpy(rc, haraka_rc, 40 * 16); + + /* Constants for sk.seed */ + if (sk_seed != NULL) { + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka_S(buf, 40 * 16, sk_seed, seed_length); + memcpy(rc_sseed, buf, 40 * 16); + } + + /* Constants for pk.seed */ + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka_S(buf, 40 * 16, pk_seed, seed_length); + memcpy(rc, buf, 40 * 16); +} + +static void haraka_S_absorb(unsigned char *s, unsigned int r, + const unsigned char *m, unsigned long long mlen, + unsigned char p) { + unsigned long long i; + unsigned char t[r]; + + while (mlen >= r) { + // XOR block to state + for (i = 0; i < r; ++i) { + s[i] ^= m[i]; + } + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka512_perm(s, s); + mlen -= r; + m += r; + } + + for (i = 0; i < r; ++i) { + t[i] = 0; + } + for (i = 0; i < mlen; ++i) { + t[i] = m[i]; + } + t[i] = p; + t[r - 1] |= 128; + for (i = 0; i < r; ++i) { + s[i] ^= t[i]; + } +} + +static void haraka_S_squeezeblocks(unsigned char *h, unsigned long long nblocks, + unsigned char *s, unsigned int r) { + while (nblocks > 0) { + PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka512_perm(s, s); + memcpy(h, s, HARAKAS_RATE); + h += r; + 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, 32, in, inlen, 0x1F); + + haraka_S_squeezeblocks(out, outlen / 32, s, 32); + out += (outlen / 32) * 32; + + if (outlen % 32) { + haraka_S_squeezeblocks(d, 1, s, 32); + for (i = 0; i < outlen % 32; i++) { + out[i] = d[i]; + } + } +} + +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka512_perm(unsigned char *out, const unsigned char *in) { + int i, j; + + unsigned char s[64], tmp[16]; + + memcpy(s, in, 16); + memcpy(s + 16, in + 16, 16); + memcpy(s + 32, in + 32, 16); + memcpy(s + 48, in + 48, 16); + + for (i = 0; i < 5; ++i) { + // aes round(s) + for (j = 0; j < 2; ++j) { + aesenc(s, rc[4 * 2 * i + 4 * j]); + aesenc(s + 16, rc[4 * 2 * i + 4 * j + 1]); + aesenc(s + 32, rc[4 * 2 * i + 4 * j + 2]); + aesenc(s + 48, rc[4 * 2 * i + 4 * j + 3]); + } + + // mixing + unpacklo32(tmp, s, s + 16); + unpackhi32(s, s, s + 16); + unpacklo32(s + 16, s + 32, s + 48); + unpackhi32(s + 32, s + 32, s + 48); + unpacklo32(s + 48, s, s + 32); + unpackhi32(s, s, s + 32); + unpackhi32(s + 32, s + 16, tmp); + unpacklo32(s + 16, s + 16, tmp); + } + + memcpy(out, s, 64); +} + +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) { + int i, j; + + unsigned char s[32], tmp[16]; + + memcpy(s, in, 16); + memcpy(s + 16, in + 16, 16); + + for (i = 0; i < 5; ++i) { + // aes round(s) + for (j = 0; j < 2; ++j) { + aesenc(s, rc[2 * 2 * i + 2 * j]); + aesenc(s + 16, rc[2 * 2 * i + 2 * j + 1]); + } + + // mixing + unpacklo32(tmp, s, s + 16); + unpackhi32(s + 16, s, s + 16); + memcpy(s, tmp, 16); + } + + /* Feed-forward */ + for (i = 0; i < 32; i++) { + out[i] = in[i] ^ s[i]; + } +} + +void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka256_sk(unsigned char *out, const unsigned char *in) { + int i, j; + + unsigned char s[32], tmp[16]; + + memcpy(s, in, 16); + memcpy(s + 16, in + 16, 16); + + for (i = 0; i < 5; ++i) { + // aes round(s) + for (j = 0; j < 2; ++j) { + aesenc(s, rc_sseed[2 * 2 * i + 2 * j]); + aesenc(s + 16, rc_sseed[2 * 2 * i + 2 * j + 1]); + } + + // mixing + unpacklo32(tmp, s, s + 16); + unpackhi32(s + 16, s, s + 16); + memcpy(s, tmp, 16); + } + + /* Feed-forward */ + for (i = 0; i < 32; i++) { + out[i] = in[i] ^ s[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..7eb67b73 --- /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-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/492ec4f1f6d3b3dc4b435783bbaaf4e41cdb6f32 + length-secret-key: 64 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..6ab2d592 --- /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=-Wall -Wconversion -Wextra -Wpedantic -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..33e6a2b1 --- /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 16976 +#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..6b8f4030 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128s-robust/clean/haraka.c @@ -0,0 +1,373 @@ +/* +Plain C implementation of the Haraka256 and Haraka512 permutations. +*/ +#include +#include +#include + +#include "haraka.h" + +#define HARAKAS_RATE 32 + +static const unsigned char haraka_rc[40][16] = { + {0x9d, 0x7b, 0x81, 0x75, 0xf0, 0xfe, 0xc5, 0xb2, 0x0a, 0xc0, 0x20, 0xe6, 0x4c, 0x70, 0x84, 0x06}, + {0x17, 0xf7, 0x08, 0x2f, 0xa4, 0x6b, 0x0f, 0x64, 0x6b, 0xa0, 0xf3, 0x88, 0xe1, 0xb4, 0x66, 0x8b}, + {0x14, 0x91, 0x02, 0x9f, 0x60, 0x9d, 0x02, 0xcf, 0x98, 0x84, 0xf2, 0x53, 0x2d, 0xde, 0x02, 0x34}, + {0x79, 0x4f, 0x5b, 0xfd, 0xaf, 0xbc, 0xf3, 0xbb, 0x08, 0x4f, 0x7b, 0x2e, 0xe6, 0xea, 0xd6, 0x0e}, + {0x44, 0x70, 0x39, 0xbe, 0x1c, 0xcd, 0xee, 0x79, 0x8b, 0x44, 0x72, 0x48, 0xcb, 0xb0, 0xcf, 0xcb}, + {0x7b, 0x05, 0x8a, 0x2b, 0xed, 0x35, 0x53, 0x8d, 0xb7, 0x32, 0x90, 0x6e, 0xee, 0xcd, 0xea, 0x7e}, + {0x1b, 0xef, 0x4f, 0xda, 0x61, 0x27, 0x41, 0xe2, 0xd0, 0x7c, 0x2e, 0x5e, 0x43, 0x8f, 0xc2, 0x67}, + {0x3b, 0x0b, 0xc7, 0x1f, 0xe2, 0xfd, 0x5f, 0x67, 0x07, 0xcc, 0xca, 0xaf, 0xb0, 0xd9, 0x24, 0x29}, + {0xee, 0x65, 0xd4, 0xb9, 0xca, 0x8f, 0xdb, 0xec, 0xe9, 0x7f, 0x86, 0xe6, 0xf1, 0x63, 0x4d, 0xab}, + {0x33, 0x7e, 0x03, 0xad, 0x4f, 0x40, 0x2a, 0x5b, 0x64, 0xcd, 0xb7, 0xd4, 0x84, 0xbf, 0x30, 0x1c}, + {0x00, 0x98, 0xf6, 0x8d, 0x2e, 0x8b, 0x02, 0x69, 0xbf, 0x23, 0x17, 0x94, 0xb9, 0x0b, 0xcc, 0xb2}, + {0x8a, 0x2d, 0x9d, 0x5c, 0xc8, 0x9e, 0xaa, 0x4a, 0x72, 0x55, 0x6f, 0xde, 0xa6, 0x78, 0x04, 0xfa}, + {0xd4, 0x9f, 0x12, 0x29, 0x2e, 0x4f, 0xfa, 0x0e, 0x12, 0x2a, 0x77, 0x6b, 0x2b, 0x9f, 0xb4, 0xdf}, + {0xee, 0x12, 0x6a, 0xbb, 0xae, 0x11, 0xd6, 0x32, 0x36, 0xa2, 0x49, 0xf4, 0x44, 0x03, 0xa1, 0x1e}, + {0xa6, 0xec, 0xa8, 0x9c, 0xc9, 0x00, 0x96, 0x5f, 0x84, 0x00, 0x05, 0x4b, 0x88, 0x49, 0x04, 0xaf}, + {0xec, 0x93, 0xe5, 0x27, 0xe3, 0xc7, 0xa2, 0x78, 0x4f, 0x9c, 0x19, 0x9d, 0xd8, 0x5e, 0x02, 0x21}, + {0x73, 0x01, 0xd4, 0x82, 0xcd, 0x2e, 0x28, 0xb9, 0xb7, 0xc9, 0x59, 0xa7, 0xf8, 0xaa, 0x3a, 0xbf}, + {0x6b, 0x7d, 0x30, 0x10, 0xd9, 0xef, 0xf2, 0x37, 0x17, 0xb0, 0x86, 0x61, 0x0d, 0x70, 0x60, 0x62}, + {0xc6, 0x9a, 0xfc, 0xf6, 0x53, 0x91, 0xc2, 0x81, 0x43, 0x04, 0x30, 0x21, 0xc2, 0x45, 0xca, 0x5a}, + {0x3a, 0x94, 0xd1, 0x36, 0xe8, 0x92, 0xaf, 0x2c, 0xbb, 0x68, 0x6b, 0x22, 0x3c, 0x97, 0x23, 0x92}, + {0xb4, 0x71, 0x10, 0xe5, 0x58, 0xb9, 0xba, 0x6c, 0xeb, 0x86, 0x58, 0x22, 0x38, 0x92, 0xbf, 0xd3}, + {0x8d, 0x12, 0xe1, 0x24, 0xdd, 0xfd, 0x3d, 0x93, 0x77, 0xc6, 0xf0, 0xae, 0xe5, 0x3c, 0x86, 0xdb}, + {0xb1, 0x12, 0x22, 0xcb, 0xe3, 0x8d, 0xe4, 0x83, 0x9c, 0xa0, 0xeb, 0xff, 0x68, 0x62, 0x60, 0xbb}, + {0x7d, 0xf7, 0x2b, 0xc7, 0x4e, 0x1a, 0xb9, 0x2d, 0x9c, 0xd1, 0xe4, 0xe2, 0xdc, 0xd3, 0x4b, 0x73}, + {0x4e, 0x92, 0xb3, 0x2c, 0xc4, 0x15, 0x14, 0x4b, 0x43, 0x1b, 0x30, 0x61, 0xc3, 0x47, 0xbb, 0x43}, + {0x99, 0x68, 0xeb, 0x16, 0xdd, 0x31, 0xb2, 0x03, 0xf6, 0xef, 0x07, 0xe7, 0xa8, 0x75, 0xa7, 0xdb}, + {0x2c, 0x47, 0xca, 0x7e, 0x02, 0x23, 0x5e, 0x8e, 0x77, 0x59, 0x75, 0x3c, 0x4b, 0x61, 0xf3, 0x6d}, + {0xf9, 0x17, 0x86, 0xb8, 0xb9, 0xe5, 0x1b, 0x6d, 0x77, 0x7d, 0xde, 0xd6, 0x17, 0x5a, 0xa7, 0xcd}, + {0x5d, 0xee, 0x46, 0xa9, 0x9d, 0x06, 0x6c, 0x9d, 0xaa, 0xe9, 0xa8, 0x6b, 0xf0, 0x43, 0x6b, 0xec}, + {0xc1, 0x27, 0xf3, 0x3b, 0x59, 0x11, 0x53, 0xa2, 0x2b, 0x33, 0x57, 0xf9, 0x50, 0x69, 0x1e, 0xcb}, + {0xd9, 0xd0, 0x0e, 0x60, 0x53, 0x03, 0xed, 0xe4, 0x9c, 0x61, 0xda, 0x00, 0x75, 0x0c, 0xee, 0x2c}, + {0x50, 0xa3, 0xa4, 0x63, 0xbc, 0xba, 0xbb, 0x80, 0xab, 0x0c, 0xe9, 0x96, 0xa1, 0xa5, 0xb1, 0xf0}, + {0x39, 0xca, 0x8d, 0x93, 0x30, 0xde, 0x0d, 0xab, 0x88, 0x29, 0x96, 0x5e, 0x02, 0xb1, 0x3d, 0xae}, + {0x42, 0xb4, 0x75, 0x2e, 0xa8, 0xf3, 0x14, 0x88, 0x0b, 0xa4, 0x54, 0xd5, 0x38, 0x8f, 0xbb, 0x17}, + {0xf6, 0x16, 0x0a, 0x36, 0x79, 0xb7, 0xb6, 0xae, 0xd7, 0x7f, 0x42, 0x5f, 0x5b, 0x8a, 0xbb, 0x34}, + {0xde, 0xaf, 0xba, 0xff, 0x18, 0x59, 0xce, 0x43, 0x38, 0x54, 0xe5, 0xcb, 0x41, 0x52, 0xf6, 0x26}, + {0x78, 0xc9, 0x9e, 0x83, 0xf7, 0x9c, 0xca, 0xa2, 0x6a, 0x02, 0xf3, 0xb9, 0x54, 0x9a, 0xe9, 0x4c}, + {0x35, 0x12, 0x90, 0x22, 0x28, 0x6e, 0xc0, 0x40, 0xbe, 0xf7, 0xdf, 0x1b, 0x1a, 0xa5, 0x51, 0xae}, + {0xcf, 0x59, 0xa6, 0x48, 0x0f, 0xbc, 0x73, 0xc1, 0x2b, 0xd2, 0x7e, 0xba, 0x3c, 0x61, 0xc1, 0xa0}, + {0xa1, 0x9d, 0xc5, 0xe9, 0xfd, 0xbd, 0xd6, 0x4a, 0x88, 0x82, 0x28, 0x02, 0x03, 0xcc, 0x6a, 0x75} +}; + +static unsigned char rc[40][16]; +static unsigned char rc_sseed[40][16]; + +static const unsigned char sbox[256] = { + 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, + 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, + 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, + 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, + 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, + 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, + 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, + 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, + 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, + 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, + 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, + 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, + 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, + 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, + 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, + 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, + 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, + 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, + 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, + 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 +}; + +#define XT(x) (((x) << 1) ^ ((((x) >> 7) & 1) * 0x1b)) + +// Simulate _mm_aesenc_si128 instructions from AESNI +static void aesenc(unsigned char *s, const unsigned char *rk) { + uint8_t i, t, u, v[4][4]; + for (i = 0; i < 16; ++i) { + v[((i / 4) + 4 - (i % 4) ) % 4][i % 4] = sbox[s[i]]; + } + for (i = 0; i < 4; ++i) { + t = v[i][0]; + u = v[i][0] ^ v[i][1] ^ v[i][2] ^ v[i][3]; + v[i][0] ^= (uint8_t)(u ^ XT(v[i][0] ^ v[i][1])); + v[i][1] ^= (uint8_t)(u ^ XT(v[i][1] ^ v[i][2])); + v[i][2] ^= (uint8_t)(u ^ XT(v[i][2] ^ v[i][3])); + v[i][3] ^= (uint8_t)(u ^ XT(v[i][3] ^ t)); + } + for (i = 0; i < 16; ++i) { + s[i] = v[i / 4][i % 4] ^ rk[i]; + } +} + +// Simulate _mm_unpacklo_epi32 +static void unpacklo32(unsigned char *t, unsigned char *a, unsigned char *b) { + unsigned char tmp[16]; + memcpy(tmp, a, 4); + memcpy(tmp + 4, b, 4); + memcpy(tmp + 8, a + 4, 4); + memcpy(tmp + 12, b + 4, 4); + memcpy(t, tmp, 16); +} + +// Simulate _mm_unpackhi_epi32 +static void unpackhi32(unsigned char *t, unsigned char *a, unsigned char *b) { + unsigned char tmp[16]; + memcpy(tmp, a + 8, 4); + memcpy(tmp + 4, b + 8, 4); + memcpy(tmp + 8, a + 12, 4); + memcpy(tmp + 12, b + 12, 4); + memcpy(t, tmp, 16); +} + +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]; + + /* Use the standard constants to generate tweaked ones. */ + memcpy(rc, haraka_rc, 40 * 16); + + /* Constants for sk.seed */ + if (sk_seed != NULL) { + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka_S(buf, 40 * 16, sk_seed, seed_length); + memcpy(rc_sseed, buf, 40 * 16); + } + + /* Constants for pk.seed */ + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka_S(buf, 40 * 16, pk_seed, seed_length); + memcpy(rc, buf, 40 * 16); +} + +static void haraka_S_absorb(unsigned char *s, unsigned int r, + const unsigned char *m, unsigned long long mlen, + unsigned char p) { + unsigned long long i; + unsigned char t[r]; + + while (mlen >= r) { + // XOR block to state + for (i = 0; i < r; ++i) { + s[i] ^= m[i]; + } + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka512_perm(s, s); + mlen -= r; + m += r; + } + + for (i = 0; i < r; ++i) { + t[i] = 0; + } + for (i = 0; i < mlen; ++i) { + t[i] = m[i]; + } + t[i] = p; + t[r - 1] |= 128; + for (i = 0; i < r; ++i) { + s[i] ^= t[i]; + } +} + +static void haraka_S_squeezeblocks(unsigned char *h, unsigned long long nblocks, + unsigned char *s, unsigned int r) { + while (nblocks > 0) { + PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka512_perm(s, s); + memcpy(h, s, HARAKAS_RATE); + h += r; + 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, 32, in, inlen, 0x1F); + + haraka_S_squeezeblocks(out, outlen / 32, s, 32); + out += (outlen / 32) * 32; + + if (outlen % 32) { + haraka_S_squeezeblocks(d, 1, s, 32); + for (i = 0; i < outlen % 32; i++) { + out[i] = d[i]; + } + } +} + +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka512_perm(unsigned char *out, const unsigned char *in) { + int i, j; + + unsigned char s[64], tmp[16]; + + memcpy(s, in, 16); + memcpy(s + 16, in + 16, 16); + memcpy(s + 32, in + 32, 16); + memcpy(s + 48, in + 48, 16); + + for (i = 0; i < 5; ++i) { + // aes round(s) + for (j = 0; j < 2; ++j) { + aesenc(s, rc[4 * 2 * i + 4 * j]); + aesenc(s + 16, rc[4 * 2 * i + 4 * j + 1]); + aesenc(s + 32, rc[4 * 2 * i + 4 * j + 2]); + aesenc(s + 48, rc[4 * 2 * i + 4 * j + 3]); + } + + // mixing + unpacklo32(tmp, s, s + 16); + unpackhi32(s, s, s + 16); + unpacklo32(s + 16, s + 32, s + 48); + unpackhi32(s + 32, s + 32, s + 48); + unpacklo32(s + 48, s, s + 32); + unpackhi32(s, s, s + 32); + unpackhi32(s + 32, s + 16, tmp); + unpacklo32(s + 16, s + 16, tmp); + } + + memcpy(out, s, 64); +} + +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) { + int i, j; + + unsigned char s[32], tmp[16]; + + memcpy(s, in, 16); + memcpy(s + 16, in + 16, 16); + + for (i = 0; i < 5; ++i) { + // aes round(s) + for (j = 0; j < 2; ++j) { + aesenc(s, rc[2 * 2 * i + 2 * j]); + aesenc(s + 16, rc[2 * 2 * i + 2 * j + 1]); + } + + // mixing + unpacklo32(tmp, s, s + 16); + unpackhi32(s + 16, s, s + 16); + memcpy(s, tmp, 16); + } + + /* Feed-forward */ + for (i = 0; i < 32; i++) { + out[i] = in[i] ^ s[i]; + } +} + +void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka256_sk(unsigned char *out, const unsigned char *in) { + int i, j; + + unsigned char s[32], tmp[16]; + + memcpy(s, in, 16); + memcpy(s + 16, in + 16, 16); + + for (i = 0; i < 5; ++i) { + // aes round(s) + for (j = 0; j < 2; ++j) { + aesenc(s, rc_sseed[2 * 2 * i + 2 * j]); + aesenc(s + 16, rc_sseed[2 * 2 * i + 2 * j + 1]); + } + + // mixing + unpacklo32(tmp, s, s + 16); + unpackhi32(s + 16, s, s + 16); + memcpy(s, tmp, 16); + } + + /* Feed-forward */ + for (i = 0; i < 32; i++) { + out[i] = in[i] ^ s[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..ff5d1e71 --- /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-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/492ec4f1f6d3b3dc4b435783bbaaf4e41cdb6f32 + length-secret-key: 64 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..f6dfd16c --- /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=-Wall -Wconversion -Wextra -Wpedantic -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..7b3561ca --- /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 16976 +#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..78ac75d0 --- /dev/null +++ b/crypto_sign/sphincs-haraka-128s-simple/clean/haraka.c @@ -0,0 +1,373 @@ +/* +Plain C implementation of the Haraka256 and Haraka512 permutations. +*/ +#include +#include +#include + +#include "haraka.h" + +#define HARAKAS_RATE 32 + +static const unsigned char haraka_rc[40][16] = { + {0x9d, 0x7b, 0x81, 0x75, 0xf0, 0xfe, 0xc5, 0xb2, 0x0a, 0xc0, 0x20, 0xe6, 0x4c, 0x70, 0x84, 0x06}, + {0x17, 0xf7, 0x08, 0x2f, 0xa4, 0x6b, 0x0f, 0x64, 0x6b, 0xa0, 0xf3, 0x88, 0xe1, 0xb4, 0x66, 0x8b}, + {0x14, 0x91, 0x02, 0x9f, 0x60, 0x9d, 0x02, 0xcf, 0x98, 0x84, 0xf2, 0x53, 0x2d, 0xde, 0x02, 0x34}, + {0x79, 0x4f, 0x5b, 0xfd, 0xaf, 0xbc, 0xf3, 0xbb, 0x08, 0x4f, 0x7b, 0x2e, 0xe6, 0xea, 0xd6, 0x0e}, + {0x44, 0x70, 0x39, 0xbe, 0x1c, 0xcd, 0xee, 0x79, 0x8b, 0x44, 0x72, 0x48, 0xcb, 0xb0, 0xcf, 0xcb}, + {0x7b, 0x05, 0x8a, 0x2b, 0xed, 0x35, 0x53, 0x8d, 0xb7, 0x32, 0x90, 0x6e, 0xee, 0xcd, 0xea, 0x7e}, + {0x1b, 0xef, 0x4f, 0xda, 0x61, 0x27, 0x41, 0xe2, 0xd0, 0x7c, 0x2e, 0x5e, 0x43, 0x8f, 0xc2, 0x67}, + {0x3b, 0x0b, 0xc7, 0x1f, 0xe2, 0xfd, 0x5f, 0x67, 0x07, 0xcc, 0xca, 0xaf, 0xb0, 0xd9, 0x24, 0x29}, + {0xee, 0x65, 0xd4, 0xb9, 0xca, 0x8f, 0xdb, 0xec, 0xe9, 0x7f, 0x86, 0xe6, 0xf1, 0x63, 0x4d, 0xab}, + {0x33, 0x7e, 0x03, 0xad, 0x4f, 0x40, 0x2a, 0x5b, 0x64, 0xcd, 0xb7, 0xd4, 0x84, 0xbf, 0x30, 0x1c}, + {0x00, 0x98, 0xf6, 0x8d, 0x2e, 0x8b, 0x02, 0x69, 0xbf, 0x23, 0x17, 0x94, 0xb9, 0x0b, 0xcc, 0xb2}, + {0x8a, 0x2d, 0x9d, 0x5c, 0xc8, 0x9e, 0xaa, 0x4a, 0x72, 0x55, 0x6f, 0xde, 0xa6, 0x78, 0x04, 0xfa}, + {0xd4, 0x9f, 0x12, 0x29, 0x2e, 0x4f, 0xfa, 0x0e, 0x12, 0x2a, 0x77, 0x6b, 0x2b, 0x9f, 0xb4, 0xdf}, + {0xee, 0x12, 0x6a, 0xbb, 0xae, 0x11, 0xd6, 0x32, 0x36, 0xa2, 0x49, 0xf4, 0x44, 0x03, 0xa1, 0x1e}, + {0xa6, 0xec, 0xa8, 0x9c, 0xc9, 0x00, 0x96, 0x5f, 0x84, 0x00, 0x05, 0x4b, 0x88, 0x49, 0x04, 0xaf}, + {0xec, 0x93, 0xe5, 0x27, 0xe3, 0xc7, 0xa2, 0x78, 0x4f, 0x9c, 0x19, 0x9d, 0xd8, 0x5e, 0x02, 0x21}, + {0x73, 0x01, 0xd4, 0x82, 0xcd, 0x2e, 0x28, 0xb9, 0xb7, 0xc9, 0x59, 0xa7, 0xf8, 0xaa, 0x3a, 0xbf}, + {0x6b, 0x7d, 0x30, 0x10, 0xd9, 0xef, 0xf2, 0x37, 0x17, 0xb0, 0x86, 0x61, 0x0d, 0x70, 0x60, 0x62}, + {0xc6, 0x9a, 0xfc, 0xf6, 0x53, 0x91, 0xc2, 0x81, 0x43, 0x04, 0x30, 0x21, 0xc2, 0x45, 0xca, 0x5a}, + {0x3a, 0x94, 0xd1, 0x36, 0xe8, 0x92, 0xaf, 0x2c, 0xbb, 0x68, 0x6b, 0x22, 0x3c, 0x97, 0x23, 0x92}, + {0xb4, 0x71, 0x10, 0xe5, 0x58, 0xb9, 0xba, 0x6c, 0xeb, 0x86, 0x58, 0x22, 0x38, 0x92, 0xbf, 0xd3}, + {0x8d, 0x12, 0xe1, 0x24, 0xdd, 0xfd, 0x3d, 0x93, 0x77, 0xc6, 0xf0, 0xae, 0xe5, 0x3c, 0x86, 0xdb}, + {0xb1, 0x12, 0x22, 0xcb, 0xe3, 0x8d, 0xe4, 0x83, 0x9c, 0xa0, 0xeb, 0xff, 0x68, 0x62, 0x60, 0xbb}, + {0x7d, 0xf7, 0x2b, 0xc7, 0x4e, 0x1a, 0xb9, 0x2d, 0x9c, 0xd1, 0xe4, 0xe2, 0xdc, 0xd3, 0x4b, 0x73}, + {0x4e, 0x92, 0xb3, 0x2c, 0xc4, 0x15, 0x14, 0x4b, 0x43, 0x1b, 0x30, 0x61, 0xc3, 0x47, 0xbb, 0x43}, + {0x99, 0x68, 0xeb, 0x16, 0xdd, 0x31, 0xb2, 0x03, 0xf6, 0xef, 0x07, 0xe7, 0xa8, 0x75, 0xa7, 0xdb}, + {0x2c, 0x47, 0xca, 0x7e, 0x02, 0x23, 0x5e, 0x8e, 0x77, 0x59, 0x75, 0x3c, 0x4b, 0x61, 0xf3, 0x6d}, + {0xf9, 0x17, 0x86, 0xb8, 0xb9, 0xe5, 0x1b, 0x6d, 0x77, 0x7d, 0xde, 0xd6, 0x17, 0x5a, 0xa7, 0xcd}, + {0x5d, 0xee, 0x46, 0xa9, 0x9d, 0x06, 0x6c, 0x9d, 0xaa, 0xe9, 0xa8, 0x6b, 0xf0, 0x43, 0x6b, 0xec}, + {0xc1, 0x27, 0xf3, 0x3b, 0x59, 0x11, 0x53, 0xa2, 0x2b, 0x33, 0x57, 0xf9, 0x50, 0x69, 0x1e, 0xcb}, + {0xd9, 0xd0, 0x0e, 0x60, 0x53, 0x03, 0xed, 0xe4, 0x9c, 0x61, 0xda, 0x00, 0x75, 0x0c, 0xee, 0x2c}, + {0x50, 0xa3, 0xa4, 0x63, 0xbc, 0xba, 0xbb, 0x80, 0xab, 0x0c, 0xe9, 0x96, 0xa1, 0xa5, 0xb1, 0xf0}, + {0x39, 0xca, 0x8d, 0x93, 0x30, 0xde, 0x0d, 0xab, 0x88, 0x29, 0x96, 0x5e, 0x02, 0xb1, 0x3d, 0xae}, + {0x42, 0xb4, 0x75, 0x2e, 0xa8, 0xf3, 0x14, 0x88, 0x0b, 0xa4, 0x54, 0xd5, 0x38, 0x8f, 0xbb, 0x17}, + {0xf6, 0x16, 0x0a, 0x36, 0x79, 0xb7, 0xb6, 0xae, 0xd7, 0x7f, 0x42, 0x5f, 0x5b, 0x8a, 0xbb, 0x34}, + {0xde, 0xaf, 0xba, 0xff, 0x18, 0x59, 0xce, 0x43, 0x38, 0x54, 0xe5, 0xcb, 0x41, 0x52, 0xf6, 0x26}, + {0x78, 0xc9, 0x9e, 0x83, 0xf7, 0x9c, 0xca, 0xa2, 0x6a, 0x02, 0xf3, 0xb9, 0x54, 0x9a, 0xe9, 0x4c}, + {0x35, 0x12, 0x90, 0x22, 0x28, 0x6e, 0xc0, 0x40, 0xbe, 0xf7, 0xdf, 0x1b, 0x1a, 0xa5, 0x51, 0xae}, + {0xcf, 0x59, 0xa6, 0x48, 0x0f, 0xbc, 0x73, 0xc1, 0x2b, 0xd2, 0x7e, 0xba, 0x3c, 0x61, 0xc1, 0xa0}, + {0xa1, 0x9d, 0xc5, 0xe9, 0xfd, 0xbd, 0xd6, 0x4a, 0x88, 0x82, 0x28, 0x02, 0x03, 0xcc, 0x6a, 0x75} +}; + +static unsigned char rc[40][16]; +static unsigned char rc_sseed[40][16]; + +static const unsigned char sbox[256] = { + 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, + 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, + 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, + 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, + 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, + 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, + 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, + 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, + 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, + 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, + 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, + 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, + 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, + 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, + 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, + 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, + 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, + 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, + 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, + 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 +}; + +#define XT(x) (((x) << 1) ^ ((((x) >> 7) & 1) * 0x1b)) + +// Simulate _mm_aesenc_si128 instructions from AESNI +static void aesenc(unsigned char *s, const unsigned char *rk) { + uint8_t i, t, u, v[4][4]; + for (i = 0; i < 16; ++i) { + v[((i / 4) + 4 - (i % 4) ) % 4][i % 4] = sbox[s[i]]; + } + for (i = 0; i < 4; ++i) { + t = v[i][0]; + u = v[i][0] ^ v[i][1] ^ v[i][2] ^ v[i][3]; + v[i][0] ^= (uint8_t)(u ^ XT(v[i][0] ^ v[i][1])); + v[i][1] ^= (uint8_t)(u ^ XT(v[i][1] ^ v[i][2])); + v[i][2] ^= (uint8_t)(u ^ XT(v[i][2] ^ v[i][3])); + v[i][3] ^= (uint8_t)(u ^ XT(v[i][3] ^ t)); + } + for (i = 0; i < 16; ++i) { + s[i] = v[i / 4][i % 4] ^ rk[i]; + } +} + +// Simulate _mm_unpacklo_epi32 +static void unpacklo32(unsigned char *t, unsigned char *a, unsigned char *b) { + unsigned char tmp[16]; + memcpy(tmp, a, 4); + memcpy(tmp + 4, b, 4); + memcpy(tmp + 8, a + 4, 4); + memcpy(tmp + 12, b + 4, 4); + memcpy(t, tmp, 16); +} + +// Simulate _mm_unpackhi_epi32 +static void unpackhi32(unsigned char *t, unsigned char *a, unsigned char *b) { + unsigned char tmp[16]; + memcpy(tmp, a + 8, 4); + memcpy(tmp + 4, b + 8, 4); + memcpy(tmp + 8, a + 12, 4); + memcpy(tmp + 12, b + 12, 4); + memcpy(t, tmp, 16); +} + +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]; + + /* Use the standard constants to generate tweaked ones. */ + memcpy(rc, haraka_rc, 40 * 16); + + /* Constants for sk.seed */ + if (sk_seed != NULL) { + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka_S(buf, 40 * 16, sk_seed, seed_length); + memcpy(rc_sseed, buf, 40 * 16); + } + + /* Constants for pk.seed */ + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka_S(buf, 40 * 16, pk_seed, seed_length); + memcpy(rc, buf, 40 * 16); +} + +static void haraka_S_absorb(unsigned char *s, unsigned int r, + const unsigned char *m, unsigned long long mlen, + unsigned char p) { + unsigned long long i; + unsigned char t[r]; + + while (mlen >= r) { + // XOR block to state + for (i = 0; i < r; ++i) { + s[i] ^= m[i]; + } + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka512_perm(s, s); + mlen -= r; + m += r; + } + + for (i = 0; i < r; ++i) { + t[i] = 0; + } + for (i = 0; i < mlen; ++i) { + t[i] = m[i]; + } + t[i] = p; + t[r - 1] |= 128; + for (i = 0; i < r; ++i) { + s[i] ^= t[i]; + } +} + +static void haraka_S_squeezeblocks(unsigned char *h, unsigned long long nblocks, + unsigned char *s, unsigned int r) { + while (nblocks > 0) { + PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka512_perm(s, s); + memcpy(h, s, HARAKAS_RATE); + h += r; + 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, 32, in, inlen, 0x1F); + + haraka_S_squeezeblocks(out, outlen / 32, s, 32); + out += (outlen / 32) * 32; + + if (outlen % 32) { + haraka_S_squeezeblocks(d, 1, s, 32); + for (i = 0; i < outlen % 32; i++) { + out[i] = d[i]; + } + } +} + +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka512_perm(unsigned char *out, const unsigned char *in) { + int i, j; + + unsigned char s[64], tmp[16]; + + memcpy(s, in, 16); + memcpy(s + 16, in + 16, 16); + memcpy(s + 32, in + 32, 16); + memcpy(s + 48, in + 48, 16); + + for (i = 0; i < 5; ++i) { + // aes round(s) + for (j = 0; j < 2; ++j) { + aesenc(s, rc[4 * 2 * i + 4 * j]); + aesenc(s + 16, rc[4 * 2 * i + 4 * j + 1]); + aesenc(s + 32, rc[4 * 2 * i + 4 * j + 2]); + aesenc(s + 48, rc[4 * 2 * i + 4 * j + 3]); + } + + // mixing + unpacklo32(tmp, s, s + 16); + unpackhi32(s, s, s + 16); + unpacklo32(s + 16, s + 32, s + 48); + unpackhi32(s + 32, s + 32, s + 48); + unpacklo32(s + 48, s, s + 32); + unpackhi32(s, s, s + 32); + unpackhi32(s + 32, s + 16, tmp); + unpacklo32(s + 16, s + 16, tmp); + } + + memcpy(out, s, 64); +} + +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) { + int i, j; + + unsigned char s[32], tmp[16]; + + memcpy(s, in, 16); + memcpy(s + 16, in + 16, 16); + + for (i = 0; i < 5; ++i) { + // aes round(s) + for (j = 0; j < 2; ++j) { + aesenc(s, rc[2 * 2 * i + 2 * j]); + aesenc(s + 16, rc[2 * 2 * i + 2 * j + 1]); + } + + // mixing + unpacklo32(tmp, s, s + 16); + unpackhi32(s + 16, s, s + 16); + memcpy(s, tmp, 16); + } + + /* Feed-forward */ + for (i = 0; i < 32; i++) { + out[i] = in[i] ^ s[i]; + } +} + +void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka256_sk(unsigned char *out, const unsigned char *in) { + int i, j; + + unsigned char s[32], tmp[16]; + + memcpy(s, in, 16); + memcpy(s + 16, in + 16, 16); + + for (i = 0; i < 5; ++i) { + // aes round(s) + for (j = 0; j < 2; ++j) { + aesenc(s, rc_sseed[2 * 2 * i + 2 * j]); + aesenc(s + 16, rc_sseed[2 * 2 * i + 2 * j + 1]); + } + + // mixing + unpacklo32(tmp, s, s + 16); + unpackhi32(s + 16, s, s + 16); + memcpy(s, tmp, 16); + } + + /* Feed-forward */ + for (i = 0; i < 32; i++) { + out[i] = in[i] ^ s[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..2e4b98ff --- /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-signature: 35664 +testvectors-sha256: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +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/492ec4f1f6d3b3dc4b435783bbaaf4e41cdb6f32 + length-secret-key: 96 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..f599064e --- /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=-Wall -Wconversion -Wextra -Wpedantic -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..8a85092c --- /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 64 +#define PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES 32 +#define PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_CRYPTO_BYTES 16976 +#define PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_CRYPTO_SEEDBYTES 48 + +/* + * 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..ab61e613 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192f-robust/clean/haraka.c @@ -0,0 +1,373 @@ +/* +Plain C implementation of the Haraka256 and Haraka512 permutations. +*/ +#include +#include +#include + +#include "haraka.h" + +#define HARAKAS_RATE 32 + +static const unsigned char haraka_rc[40][16] = { + {0x9d, 0x7b, 0x81, 0x75, 0xf0, 0xfe, 0xc5, 0xb2, 0x0a, 0xc0, 0x20, 0xe6, 0x4c, 0x70, 0x84, 0x06}, + {0x17, 0xf7, 0x08, 0x2f, 0xa4, 0x6b, 0x0f, 0x64, 0x6b, 0xa0, 0xf3, 0x88, 0xe1, 0xb4, 0x66, 0x8b}, + {0x14, 0x91, 0x02, 0x9f, 0x60, 0x9d, 0x02, 0xcf, 0x98, 0x84, 0xf2, 0x53, 0x2d, 0xde, 0x02, 0x34}, + {0x79, 0x4f, 0x5b, 0xfd, 0xaf, 0xbc, 0xf3, 0xbb, 0x08, 0x4f, 0x7b, 0x2e, 0xe6, 0xea, 0xd6, 0x0e}, + {0x44, 0x70, 0x39, 0xbe, 0x1c, 0xcd, 0xee, 0x79, 0x8b, 0x44, 0x72, 0x48, 0xcb, 0xb0, 0xcf, 0xcb}, + {0x7b, 0x05, 0x8a, 0x2b, 0xed, 0x35, 0x53, 0x8d, 0xb7, 0x32, 0x90, 0x6e, 0xee, 0xcd, 0xea, 0x7e}, + {0x1b, 0xef, 0x4f, 0xda, 0x61, 0x27, 0x41, 0xe2, 0xd0, 0x7c, 0x2e, 0x5e, 0x43, 0x8f, 0xc2, 0x67}, + {0x3b, 0x0b, 0xc7, 0x1f, 0xe2, 0xfd, 0x5f, 0x67, 0x07, 0xcc, 0xca, 0xaf, 0xb0, 0xd9, 0x24, 0x29}, + {0xee, 0x65, 0xd4, 0xb9, 0xca, 0x8f, 0xdb, 0xec, 0xe9, 0x7f, 0x86, 0xe6, 0xf1, 0x63, 0x4d, 0xab}, + {0x33, 0x7e, 0x03, 0xad, 0x4f, 0x40, 0x2a, 0x5b, 0x64, 0xcd, 0xb7, 0xd4, 0x84, 0xbf, 0x30, 0x1c}, + {0x00, 0x98, 0xf6, 0x8d, 0x2e, 0x8b, 0x02, 0x69, 0xbf, 0x23, 0x17, 0x94, 0xb9, 0x0b, 0xcc, 0xb2}, + {0x8a, 0x2d, 0x9d, 0x5c, 0xc8, 0x9e, 0xaa, 0x4a, 0x72, 0x55, 0x6f, 0xde, 0xa6, 0x78, 0x04, 0xfa}, + {0xd4, 0x9f, 0x12, 0x29, 0x2e, 0x4f, 0xfa, 0x0e, 0x12, 0x2a, 0x77, 0x6b, 0x2b, 0x9f, 0xb4, 0xdf}, + {0xee, 0x12, 0x6a, 0xbb, 0xae, 0x11, 0xd6, 0x32, 0x36, 0xa2, 0x49, 0xf4, 0x44, 0x03, 0xa1, 0x1e}, + {0xa6, 0xec, 0xa8, 0x9c, 0xc9, 0x00, 0x96, 0x5f, 0x84, 0x00, 0x05, 0x4b, 0x88, 0x49, 0x04, 0xaf}, + {0xec, 0x93, 0xe5, 0x27, 0xe3, 0xc7, 0xa2, 0x78, 0x4f, 0x9c, 0x19, 0x9d, 0xd8, 0x5e, 0x02, 0x21}, + {0x73, 0x01, 0xd4, 0x82, 0xcd, 0x2e, 0x28, 0xb9, 0xb7, 0xc9, 0x59, 0xa7, 0xf8, 0xaa, 0x3a, 0xbf}, + {0x6b, 0x7d, 0x30, 0x10, 0xd9, 0xef, 0xf2, 0x37, 0x17, 0xb0, 0x86, 0x61, 0x0d, 0x70, 0x60, 0x62}, + {0xc6, 0x9a, 0xfc, 0xf6, 0x53, 0x91, 0xc2, 0x81, 0x43, 0x04, 0x30, 0x21, 0xc2, 0x45, 0xca, 0x5a}, + {0x3a, 0x94, 0xd1, 0x36, 0xe8, 0x92, 0xaf, 0x2c, 0xbb, 0x68, 0x6b, 0x22, 0x3c, 0x97, 0x23, 0x92}, + {0xb4, 0x71, 0x10, 0xe5, 0x58, 0xb9, 0xba, 0x6c, 0xeb, 0x86, 0x58, 0x22, 0x38, 0x92, 0xbf, 0xd3}, + {0x8d, 0x12, 0xe1, 0x24, 0xdd, 0xfd, 0x3d, 0x93, 0x77, 0xc6, 0xf0, 0xae, 0xe5, 0x3c, 0x86, 0xdb}, + {0xb1, 0x12, 0x22, 0xcb, 0xe3, 0x8d, 0xe4, 0x83, 0x9c, 0xa0, 0xeb, 0xff, 0x68, 0x62, 0x60, 0xbb}, + {0x7d, 0xf7, 0x2b, 0xc7, 0x4e, 0x1a, 0xb9, 0x2d, 0x9c, 0xd1, 0xe4, 0xe2, 0xdc, 0xd3, 0x4b, 0x73}, + {0x4e, 0x92, 0xb3, 0x2c, 0xc4, 0x15, 0x14, 0x4b, 0x43, 0x1b, 0x30, 0x61, 0xc3, 0x47, 0xbb, 0x43}, + {0x99, 0x68, 0xeb, 0x16, 0xdd, 0x31, 0xb2, 0x03, 0xf6, 0xef, 0x07, 0xe7, 0xa8, 0x75, 0xa7, 0xdb}, + {0x2c, 0x47, 0xca, 0x7e, 0x02, 0x23, 0x5e, 0x8e, 0x77, 0x59, 0x75, 0x3c, 0x4b, 0x61, 0xf3, 0x6d}, + {0xf9, 0x17, 0x86, 0xb8, 0xb9, 0xe5, 0x1b, 0x6d, 0x77, 0x7d, 0xde, 0xd6, 0x17, 0x5a, 0xa7, 0xcd}, + {0x5d, 0xee, 0x46, 0xa9, 0x9d, 0x06, 0x6c, 0x9d, 0xaa, 0xe9, 0xa8, 0x6b, 0xf0, 0x43, 0x6b, 0xec}, + {0xc1, 0x27, 0xf3, 0x3b, 0x59, 0x11, 0x53, 0xa2, 0x2b, 0x33, 0x57, 0xf9, 0x50, 0x69, 0x1e, 0xcb}, + {0xd9, 0xd0, 0x0e, 0x60, 0x53, 0x03, 0xed, 0xe4, 0x9c, 0x61, 0xda, 0x00, 0x75, 0x0c, 0xee, 0x2c}, + {0x50, 0xa3, 0xa4, 0x63, 0xbc, 0xba, 0xbb, 0x80, 0xab, 0x0c, 0xe9, 0x96, 0xa1, 0xa5, 0xb1, 0xf0}, + {0x39, 0xca, 0x8d, 0x93, 0x30, 0xde, 0x0d, 0xab, 0x88, 0x29, 0x96, 0x5e, 0x02, 0xb1, 0x3d, 0xae}, + {0x42, 0xb4, 0x75, 0x2e, 0xa8, 0xf3, 0x14, 0x88, 0x0b, 0xa4, 0x54, 0xd5, 0x38, 0x8f, 0xbb, 0x17}, + {0xf6, 0x16, 0x0a, 0x36, 0x79, 0xb7, 0xb6, 0xae, 0xd7, 0x7f, 0x42, 0x5f, 0x5b, 0x8a, 0xbb, 0x34}, + {0xde, 0xaf, 0xba, 0xff, 0x18, 0x59, 0xce, 0x43, 0x38, 0x54, 0xe5, 0xcb, 0x41, 0x52, 0xf6, 0x26}, + {0x78, 0xc9, 0x9e, 0x83, 0xf7, 0x9c, 0xca, 0xa2, 0x6a, 0x02, 0xf3, 0xb9, 0x54, 0x9a, 0xe9, 0x4c}, + {0x35, 0x12, 0x90, 0x22, 0x28, 0x6e, 0xc0, 0x40, 0xbe, 0xf7, 0xdf, 0x1b, 0x1a, 0xa5, 0x51, 0xae}, + {0xcf, 0x59, 0xa6, 0x48, 0x0f, 0xbc, 0x73, 0xc1, 0x2b, 0xd2, 0x7e, 0xba, 0x3c, 0x61, 0xc1, 0xa0}, + {0xa1, 0x9d, 0xc5, 0xe9, 0xfd, 0xbd, 0xd6, 0x4a, 0x88, 0x82, 0x28, 0x02, 0x03, 0xcc, 0x6a, 0x75} +}; + +static unsigned char rc[40][16]; +static unsigned char rc_sseed[40][16]; + +static const unsigned char sbox[256] = { + 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, + 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, + 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, + 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, + 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, + 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, + 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, + 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, + 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, + 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, + 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, + 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, + 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, + 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, + 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, + 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, + 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, + 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, + 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, + 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 +}; + +#define XT(x) (((x) << 1) ^ ((((x) >> 7) & 1) * 0x1b)) + +// Simulate _mm_aesenc_si128 instructions from AESNI +static void aesenc(unsigned char *s, const unsigned char *rk) { + uint8_t i, t, u, v[4][4]; + for (i = 0; i < 16; ++i) { + v[((i / 4) + 4 - (i % 4) ) % 4][i % 4] = sbox[s[i]]; + } + for (i = 0; i < 4; ++i) { + t = v[i][0]; + u = v[i][0] ^ v[i][1] ^ v[i][2] ^ v[i][3]; + v[i][0] ^= (uint8_t)(u ^ XT(v[i][0] ^ v[i][1])); + v[i][1] ^= (uint8_t)(u ^ XT(v[i][1] ^ v[i][2])); + v[i][2] ^= (uint8_t)(u ^ XT(v[i][2] ^ v[i][3])); + v[i][3] ^= (uint8_t)(u ^ XT(v[i][3] ^ t)); + } + for (i = 0; i < 16; ++i) { + s[i] = v[i / 4][i % 4] ^ rk[i]; + } +} + +// Simulate _mm_unpacklo_epi32 +static void unpacklo32(unsigned char *t, unsigned char *a, unsigned char *b) { + unsigned char tmp[16]; + memcpy(tmp, a, 4); + memcpy(tmp + 4, b, 4); + memcpy(tmp + 8, a + 4, 4); + memcpy(tmp + 12, b + 4, 4); + memcpy(t, tmp, 16); +} + +// Simulate _mm_unpackhi_epi32 +static void unpackhi32(unsigned char *t, unsigned char *a, unsigned char *b) { + unsigned char tmp[16]; + memcpy(tmp, a + 8, 4); + memcpy(tmp + 4, b + 8, 4); + memcpy(tmp + 8, a + 12, 4); + memcpy(tmp + 12, b + 12, 4); + memcpy(t, tmp, 16); +} + +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]; + + /* Use the standard constants to generate tweaked ones. */ + memcpy(rc, haraka_rc, 40 * 16); + + /* Constants for sk.seed */ + if (sk_seed != NULL) { + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka_S(buf, 40 * 16, sk_seed, seed_length); + memcpy(rc_sseed, buf, 40 * 16); + } + + /* Constants for pk.seed */ + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka_S(buf, 40 * 16, pk_seed, seed_length); + memcpy(rc, buf, 40 * 16); +} + +static void haraka_S_absorb(unsigned char *s, unsigned int r, + const unsigned char *m, unsigned long long mlen, + unsigned char p) { + unsigned long long i; + unsigned char t[r]; + + while (mlen >= r) { + // XOR block to state + for (i = 0; i < r; ++i) { + s[i] ^= m[i]; + } + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka512_perm(s, s); + mlen -= r; + m += r; + } + + for (i = 0; i < r; ++i) { + t[i] = 0; + } + for (i = 0; i < mlen; ++i) { + t[i] = m[i]; + } + t[i] = p; + t[r - 1] |= 128; + for (i = 0; i < r; ++i) { + s[i] ^= t[i]; + } +} + +static void haraka_S_squeezeblocks(unsigned char *h, unsigned long long nblocks, + unsigned char *s, unsigned int r) { + while (nblocks > 0) { + PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka512_perm(s, s); + memcpy(h, s, HARAKAS_RATE); + h += r; + 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, 32, in, inlen, 0x1F); + + haraka_S_squeezeblocks(out, outlen / 32, s, 32); + out += (outlen / 32) * 32; + + if (outlen % 32) { + haraka_S_squeezeblocks(d, 1, s, 32); + for (i = 0; i < outlen % 32; i++) { + out[i] = d[i]; + } + } +} + +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka512_perm(unsigned char *out, const unsigned char *in) { + int i, j; + + unsigned char s[64], tmp[16]; + + memcpy(s, in, 16); + memcpy(s + 16, in + 16, 16); + memcpy(s + 32, in + 32, 16); + memcpy(s + 48, in + 48, 16); + + for (i = 0; i < 5; ++i) { + // aes round(s) + for (j = 0; j < 2; ++j) { + aesenc(s, rc[4 * 2 * i + 4 * j]); + aesenc(s + 16, rc[4 * 2 * i + 4 * j + 1]); + aesenc(s + 32, rc[4 * 2 * i + 4 * j + 2]); + aesenc(s + 48, rc[4 * 2 * i + 4 * j + 3]); + } + + // mixing + unpacklo32(tmp, s, s + 16); + unpackhi32(s, s, s + 16); + unpacklo32(s + 16, s + 32, s + 48); + unpackhi32(s + 32, s + 32, s + 48); + unpacklo32(s + 48, s, s + 32); + unpackhi32(s, s, s + 32); + unpackhi32(s + 32, s + 16, tmp); + unpacklo32(s + 16, s + 16, tmp); + } + + memcpy(out, s, 64); +} + +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) { + int i, j; + + unsigned char s[32], tmp[16]; + + memcpy(s, in, 16); + memcpy(s + 16, in + 16, 16); + + for (i = 0; i < 5; ++i) { + // aes round(s) + for (j = 0; j < 2; ++j) { + aesenc(s, rc[2 * 2 * i + 2 * j]); + aesenc(s + 16, rc[2 * 2 * i + 2 * j + 1]); + } + + // mixing + unpacklo32(tmp, s, s + 16); + unpackhi32(s + 16, s, s + 16); + memcpy(s, tmp, 16); + } + + /* Feed-forward */ + for (i = 0; i < 32; i++) { + out[i] = in[i] ^ s[i]; + } +} + +void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka256_sk(unsigned char *out, const unsigned char *in) { + int i, j; + + unsigned char s[32], tmp[16]; + + memcpy(s, in, 16); + memcpy(s + 16, in + 16, 16); + + for (i = 0; i < 5; ++i) { + // aes round(s) + for (j = 0; j < 2; ++j) { + aesenc(s, rc_sseed[2 * 2 * i + 2 * j]); + aesenc(s + 16, rc_sseed[2 * 2 * i + 2 * j + 1]); + } + + // mixing + unpacklo32(tmp, s, s + 16); + unpackhi32(s + 16, s, s + 16); + memcpy(s, tmp, 16); + } + + /* Feed-forward */ + for (i = 0; i < 32; i++) { + out[i] = in[i] ^ s[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..2e4b98ff --- /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-signature: 35664 +testvectors-sha256: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +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/492ec4f1f6d3b3dc4b435783bbaaf4e41cdb6f32 + length-secret-key: 96 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..1e2c792f --- /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=-Wall -Wconversion -Wextra -Wpedantic -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..584aaf05 --- /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 64 +#define PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES 32 +#define PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_CRYPTO_BYTES 16976 +#define PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_CRYPTO_SEEDBYTES 48 + +/* + * 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..c0397350 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192f-simple/clean/haraka.c @@ -0,0 +1,373 @@ +/* +Plain C implementation of the Haraka256 and Haraka512 permutations. +*/ +#include +#include +#include + +#include "haraka.h" + +#define HARAKAS_RATE 32 + +static const unsigned char haraka_rc[40][16] = { + {0x9d, 0x7b, 0x81, 0x75, 0xf0, 0xfe, 0xc5, 0xb2, 0x0a, 0xc0, 0x20, 0xe6, 0x4c, 0x70, 0x84, 0x06}, + {0x17, 0xf7, 0x08, 0x2f, 0xa4, 0x6b, 0x0f, 0x64, 0x6b, 0xa0, 0xf3, 0x88, 0xe1, 0xb4, 0x66, 0x8b}, + {0x14, 0x91, 0x02, 0x9f, 0x60, 0x9d, 0x02, 0xcf, 0x98, 0x84, 0xf2, 0x53, 0x2d, 0xde, 0x02, 0x34}, + {0x79, 0x4f, 0x5b, 0xfd, 0xaf, 0xbc, 0xf3, 0xbb, 0x08, 0x4f, 0x7b, 0x2e, 0xe6, 0xea, 0xd6, 0x0e}, + {0x44, 0x70, 0x39, 0xbe, 0x1c, 0xcd, 0xee, 0x79, 0x8b, 0x44, 0x72, 0x48, 0xcb, 0xb0, 0xcf, 0xcb}, + {0x7b, 0x05, 0x8a, 0x2b, 0xed, 0x35, 0x53, 0x8d, 0xb7, 0x32, 0x90, 0x6e, 0xee, 0xcd, 0xea, 0x7e}, + {0x1b, 0xef, 0x4f, 0xda, 0x61, 0x27, 0x41, 0xe2, 0xd0, 0x7c, 0x2e, 0x5e, 0x43, 0x8f, 0xc2, 0x67}, + {0x3b, 0x0b, 0xc7, 0x1f, 0xe2, 0xfd, 0x5f, 0x67, 0x07, 0xcc, 0xca, 0xaf, 0xb0, 0xd9, 0x24, 0x29}, + {0xee, 0x65, 0xd4, 0xb9, 0xca, 0x8f, 0xdb, 0xec, 0xe9, 0x7f, 0x86, 0xe6, 0xf1, 0x63, 0x4d, 0xab}, + {0x33, 0x7e, 0x03, 0xad, 0x4f, 0x40, 0x2a, 0x5b, 0x64, 0xcd, 0xb7, 0xd4, 0x84, 0xbf, 0x30, 0x1c}, + {0x00, 0x98, 0xf6, 0x8d, 0x2e, 0x8b, 0x02, 0x69, 0xbf, 0x23, 0x17, 0x94, 0xb9, 0x0b, 0xcc, 0xb2}, + {0x8a, 0x2d, 0x9d, 0x5c, 0xc8, 0x9e, 0xaa, 0x4a, 0x72, 0x55, 0x6f, 0xde, 0xa6, 0x78, 0x04, 0xfa}, + {0xd4, 0x9f, 0x12, 0x29, 0x2e, 0x4f, 0xfa, 0x0e, 0x12, 0x2a, 0x77, 0x6b, 0x2b, 0x9f, 0xb4, 0xdf}, + {0xee, 0x12, 0x6a, 0xbb, 0xae, 0x11, 0xd6, 0x32, 0x36, 0xa2, 0x49, 0xf4, 0x44, 0x03, 0xa1, 0x1e}, + {0xa6, 0xec, 0xa8, 0x9c, 0xc9, 0x00, 0x96, 0x5f, 0x84, 0x00, 0x05, 0x4b, 0x88, 0x49, 0x04, 0xaf}, + {0xec, 0x93, 0xe5, 0x27, 0xe3, 0xc7, 0xa2, 0x78, 0x4f, 0x9c, 0x19, 0x9d, 0xd8, 0x5e, 0x02, 0x21}, + {0x73, 0x01, 0xd4, 0x82, 0xcd, 0x2e, 0x28, 0xb9, 0xb7, 0xc9, 0x59, 0xa7, 0xf8, 0xaa, 0x3a, 0xbf}, + {0x6b, 0x7d, 0x30, 0x10, 0xd9, 0xef, 0xf2, 0x37, 0x17, 0xb0, 0x86, 0x61, 0x0d, 0x70, 0x60, 0x62}, + {0xc6, 0x9a, 0xfc, 0xf6, 0x53, 0x91, 0xc2, 0x81, 0x43, 0x04, 0x30, 0x21, 0xc2, 0x45, 0xca, 0x5a}, + {0x3a, 0x94, 0xd1, 0x36, 0xe8, 0x92, 0xaf, 0x2c, 0xbb, 0x68, 0x6b, 0x22, 0x3c, 0x97, 0x23, 0x92}, + {0xb4, 0x71, 0x10, 0xe5, 0x58, 0xb9, 0xba, 0x6c, 0xeb, 0x86, 0x58, 0x22, 0x38, 0x92, 0xbf, 0xd3}, + {0x8d, 0x12, 0xe1, 0x24, 0xdd, 0xfd, 0x3d, 0x93, 0x77, 0xc6, 0xf0, 0xae, 0xe5, 0x3c, 0x86, 0xdb}, + {0xb1, 0x12, 0x22, 0xcb, 0xe3, 0x8d, 0xe4, 0x83, 0x9c, 0xa0, 0xeb, 0xff, 0x68, 0x62, 0x60, 0xbb}, + {0x7d, 0xf7, 0x2b, 0xc7, 0x4e, 0x1a, 0xb9, 0x2d, 0x9c, 0xd1, 0xe4, 0xe2, 0xdc, 0xd3, 0x4b, 0x73}, + {0x4e, 0x92, 0xb3, 0x2c, 0xc4, 0x15, 0x14, 0x4b, 0x43, 0x1b, 0x30, 0x61, 0xc3, 0x47, 0xbb, 0x43}, + {0x99, 0x68, 0xeb, 0x16, 0xdd, 0x31, 0xb2, 0x03, 0xf6, 0xef, 0x07, 0xe7, 0xa8, 0x75, 0xa7, 0xdb}, + {0x2c, 0x47, 0xca, 0x7e, 0x02, 0x23, 0x5e, 0x8e, 0x77, 0x59, 0x75, 0x3c, 0x4b, 0x61, 0xf3, 0x6d}, + {0xf9, 0x17, 0x86, 0xb8, 0xb9, 0xe5, 0x1b, 0x6d, 0x77, 0x7d, 0xde, 0xd6, 0x17, 0x5a, 0xa7, 0xcd}, + {0x5d, 0xee, 0x46, 0xa9, 0x9d, 0x06, 0x6c, 0x9d, 0xaa, 0xe9, 0xa8, 0x6b, 0xf0, 0x43, 0x6b, 0xec}, + {0xc1, 0x27, 0xf3, 0x3b, 0x59, 0x11, 0x53, 0xa2, 0x2b, 0x33, 0x57, 0xf9, 0x50, 0x69, 0x1e, 0xcb}, + {0xd9, 0xd0, 0x0e, 0x60, 0x53, 0x03, 0xed, 0xe4, 0x9c, 0x61, 0xda, 0x00, 0x75, 0x0c, 0xee, 0x2c}, + {0x50, 0xa3, 0xa4, 0x63, 0xbc, 0xba, 0xbb, 0x80, 0xab, 0x0c, 0xe9, 0x96, 0xa1, 0xa5, 0xb1, 0xf0}, + {0x39, 0xca, 0x8d, 0x93, 0x30, 0xde, 0x0d, 0xab, 0x88, 0x29, 0x96, 0x5e, 0x02, 0xb1, 0x3d, 0xae}, + {0x42, 0xb4, 0x75, 0x2e, 0xa8, 0xf3, 0x14, 0x88, 0x0b, 0xa4, 0x54, 0xd5, 0x38, 0x8f, 0xbb, 0x17}, + {0xf6, 0x16, 0x0a, 0x36, 0x79, 0xb7, 0xb6, 0xae, 0xd7, 0x7f, 0x42, 0x5f, 0x5b, 0x8a, 0xbb, 0x34}, + {0xde, 0xaf, 0xba, 0xff, 0x18, 0x59, 0xce, 0x43, 0x38, 0x54, 0xe5, 0xcb, 0x41, 0x52, 0xf6, 0x26}, + {0x78, 0xc9, 0x9e, 0x83, 0xf7, 0x9c, 0xca, 0xa2, 0x6a, 0x02, 0xf3, 0xb9, 0x54, 0x9a, 0xe9, 0x4c}, + {0x35, 0x12, 0x90, 0x22, 0x28, 0x6e, 0xc0, 0x40, 0xbe, 0xf7, 0xdf, 0x1b, 0x1a, 0xa5, 0x51, 0xae}, + {0xcf, 0x59, 0xa6, 0x48, 0x0f, 0xbc, 0x73, 0xc1, 0x2b, 0xd2, 0x7e, 0xba, 0x3c, 0x61, 0xc1, 0xa0}, + {0xa1, 0x9d, 0xc5, 0xe9, 0xfd, 0xbd, 0xd6, 0x4a, 0x88, 0x82, 0x28, 0x02, 0x03, 0xcc, 0x6a, 0x75} +}; + +static unsigned char rc[40][16]; +static unsigned char rc_sseed[40][16]; + +static const unsigned char sbox[256] = { + 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, + 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, + 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, + 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, + 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, + 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, + 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, + 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, + 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, + 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, + 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, + 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, + 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, + 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, + 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, + 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, + 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, + 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, + 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, + 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 +}; + +#define XT(x) (((x) << 1) ^ ((((x) >> 7) & 1) * 0x1b)) + +// Simulate _mm_aesenc_si128 instructions from AESNI +static void aesenc(unsigned char *s, const unsigned char *rk) { + uint8_t i, t, u, v[4][4]; + for (i = 0; i < 16; ++i) { + v[((i / 4) + 4 - (i % 4) ) % 4][i % 4] = sbox[s[i]]; + } + for (i = 0; i < 4; ++i) { + t = v[i][0]; + u = v[i][0] ^ v[i][1] ^ v[i][2] ^ v[i][3]; + v[i][0] ^= (uint8_t)(u ^ XT(v[i][0] ^ v[i][1])); + v[i][1] ^= (uint8_t)(u ^ XT(v[i][1] ^ v[i][2])); + v[i][2] ^= (uint8_t)(u ^ XT(v[i][2] ^ v[i][3])); + v[i][3] ^= (uint8_t)(u ^ XT(v[i][3] ^ t)); + } + for (i = 0; i < 16; ++i) { + s[i] = v[i / 4][i % 4] ^ rk[i]; + } +} + +// Simulate _mm_unpacklo_epi32 +static void unpacklo32(unsigned char *t, unsigned char *a, unsigned char *b) { + unsigned char tmp[16]; + memcpy(tmp, a, 4); + memcpy(tmp + 4, b, 4); + memcpy(tmp + 8, a + 4, 4); + memcpy(tmp + 12, b + 4, 4); + memcpy(t, tmp, 16); +} + +// Simulate _mm_unpackhi_epi32 +static void unpackhi32(unsigned char *t, unsigned char *a, unsigned char *b) { + unsigned char tmp[16]; + memcpy(tmp, a + 8, 4); + memcpy(tmp + 4, b + 8, 4); + memcpy(tmp + 8, a + 12, 4); + memcpy(tmp + 12, b + 12, 4); + memcpy(t, tmp, 16); +} + +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]; + + /* Use the standard constants to generate tweaked ones. */ + memcpy(rc, haraka_rc, 40 * 16); + + /* Constants for sk.seed */ + if (sk_seed != NULL) { + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_haraka_S(buf, 40 * 16, sk_seed, seed_length); + memcpy(rc_sseed, buf, 40 * 16); + } + + /* Constants for pk.seed */ + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_haraka_S(buf, 40 * 16, pk_seed, seed_length); + memcpy(rc, buf, 40 * 16); +} + +static void haraka_S_absorb(unsigned char *s, unsigned int r, + const unsigned char *m, unsigned long long mlen, + unsigned char p) { + unsigned long long i; + unsigned char t[r]; + + while (mlen >= r) { + // XOR block to state + for (i = 0; i < r; ++i) { + s[i] ^= m[i]; + } + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_haraka512_perm(s, s); + mlen -= r; + m += r; + } + + for (i = 0; i < r; ++i) { + t[i] = 0; + } + for (i = 0; i < mlen; ++i) { + t[i] = m[i]; + } + t[i] = p; + t[r - 1] |= 128; + for (i = 0; i < r; ++i) { + s[i] ^= t[i]; + } +} + +static void haraka_S_squeezeblocks(unsigned char *h, unsigned long long nblocks, + unsigned char *s, unsigned int r) { + while (nblocks > 0) { + PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_haraka512_perm(s, s); + memcpy(h, s, HARAKAS_RATE); + h += r; + 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, 32, in, inlen, 0x1F); + + haraka_S_squeezeblocks(out, outlen / 32, s, 32); + out += (outlen / 32) * 32; + + if (outlen % 32) { + haraka_S_squeezeblocks(d, 1, s, 32); + for (i = 0; i < outlen % 32; i++) { + out[i] = d[i]; + } + } +} + +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_haraka512_perm(unsigned char *out, const unsigned char *in) { + int i, j; + + unsigned char s[64], tmp[16]; + + memcpy(s, in, 16); + memcpy(s + 16, in + 16, 16); + memcpy(s + 32, in + 32, 16); + memcpy(s + 48, in + 48, 16); + + for (i = 0; i < 5; ++i) { + // aes round(s) + for (j = 0; j < 2; ++j) { + aesenc(s, rc[4 * 2 * i + 4 * j]); + aesenc(s + 16, rc[4 * 2 * i + 4 * j + 1]); + aesenc(s + 32, rc[4 * 2 * i + 4 * j + 2]); + aesenc(s + 48, rc[4 * 2 * i + 4 * j + 3]); + } + + // mixing + unpacklo32(tmp, s, s + 16); + unpackhi32(s, s, s + 16); + unpacklo32(s + 16, s + 32, s + 48); + unpackhi32(s + 32, s + 32, s + 48); + unpacklo32(s + 48, s, s + 32); + unpackhi32(s, s, s + 32); + unpackhi32(s + 32, s + 16, tmp); + unpacklo32(s + 16, s + 16, tmp); + } + + memcpy(out, s, 64); +} + +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) { + int i, j; + + unsigned char s[32], tmp[16]; + + memcpy(s, in, 16); + memcpy(s + 16, in + 16, 16); + + for (i = 0; i < 5; ++i) { + // aes round(s) + for (j = 0; j < 2; ++j) { + aesenc(s, rc[2 * 2 * i + 2 * j]); + aesenc(s + 16, rc[2 * 2 * i + 2 * j + 1]); + } + + // mixing + unpacklo32(tmp, s, s + 16); + unpackhi32(s + 16, s, s + 16); + memcpy(s, tmp, 16); + } + + /* Feed-forward */ + for (i = 0; i < 32; i++) { + out[i] = in[i] ^ s[i]; + } +} + +void PQCLEAN_SPHINCSHARAKA192FSIMPLE_CLEAN_haraka256_sk(unsigned char *out, const unsigned char *in) { + int i, j; + + unsigned char s[32], tmp[16]; + + memcpy(s, in, 16); + memcpy(s + 16, in + 16, 16); + + for (i = 0; i < 5; ++i) { + // aes round(s) + for (j = 0; j < 2; ++j) { + aesenc(s, rc_sseed[2 * 2 * i + 2 * j]); + aesenc(s + 16, rc_sseed[2 * 2 * i + 2 * j + 1]); + } + + // mixing + unpacklo32(tmp, s, s + 16); + unpackhi32(s + 16, s, s + 16); + memcpy(s, tmp, 16); + } + + /* Feed-forward */ + for (i = 0; i < 32; i++) { + out[i] = in[i] ^ s[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..1574e002 --- /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-signature: 17064 +testvectors-sha256: a5964b23e6d86db3534501606d80a11c528b4eb3a846bd76505dc23ec4412148 +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/492ec4f1f6d3b3dc4b435783bbaaf4e41cdb6f32 + length-secret-key: 96 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..a4e20d14 --- /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=-Wall -Wconversion -Wextra -Wpedantic -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..15840d8d --- /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 64 +#define PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES 32 +#define PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_CRYPTO_BYTES 16976 +#define PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_CRYPTO_SEEDBYTES 48 + +/* + * 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..5d0b5607 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192s-robust/clean/haraka.c @@ -0,0 +1,373 @@ +/* +Plain C implementation of the Haraka256 and Haraka512 permutations. +*/ +#include +#include +#include + +#include "haraka.h" + +#define HARAKAS_RATE 32 + +static const unsigned char haraka_rc[40][16] = { + {0x9d, 0x7b, 0x81, 0x75, 0xf0, 0xfe, 0xc5, 0xb2, 0x0a, 0xc0, 0x20, 0xe6, 0x4c, 0x70, 0x84, 0x06}, + {0x17, 0xf7, 0x08, 0x2f, 0xa4, 0x6b, 0x0f, 0x64, 0x6b, 0xa0, 0xf3, 0x88, 0xe1, 0xb4, 0x66, 0x8b}, + {0x14, 0x91, 0x02, 0x9f, 0x60, 0x9d, 0x02, 0xcf, 0x98, 0x84, 0xf2, 0x53, 0x2d, 0xde, 0x02, 0x34}, + {0x79, 0x4f, 0x5b, 0xfd, 0xaf, 0xbc, 0xf3, 0xbb, 0x08, 0x4f, 0x7b, 0x2e, 0xe6, 0xea, 0xd6, 0x0e}, + {0x44, 0x70, 0x39, 0xbe, 0x1c, 0xcd, 0xee, 0x79, 0x8b, 0x44, 0x72, 0x48, 0xcb, 0xb0, 0xcf, 0xcb}, + {0x7b, 0x05, 0x8a, 0x2b, 0xed, 0x35, 0x53, 0x8d, 0xb7, 0x32, 0x90, 0x6e, 0xee, 0xcd, 0xea, 0x7e}, + {0x1b, 0xef, 0x4f, 0xda, 0x61, 0x27, 0x41, 0xe2, 0xd0, 0x7c, 0x2e, 0x5e, 0x43, 0x8f, 0xc2, 0x67}, + {0x3b, 0x0b, 0xc7, 0x1f, 0xe2, 0xfd, 0x5f, 0x67, 0x07, 0xcc, 0xca, 0xaf, 0xb0, 0xd9, 0x24, 0x29}, + {0xee, 0x65, 0xd4, 0xb9, 0xca, 0x8f, 0xdb, 0xec, 0xe9, 0x7f, 0x86, 0xe6, 0xf1, 0x63, 0x4d, 0xab}, + {0x33, 0x7e, 0x03, 0xad, 0x4f, 0x40, 0x2a, 0x5b, 0x64, 0xcd, 0xb7, 0xd4, 0x84, 0xbf, 0x30, 0x1c}, + {0x00, 0x98, 0xf6, 0x8d, 0x2e, 0x8b, 0x02, 0x69, 0xbf, 0x23, 0x17, 0x94, 0xb9, 0x0b, 0xcc, 0xb2}, + {0x8a, 0x2d, 0x9d, 0x5c, 0xc8, 0x9e, 0xaa, 0x4a, 0x72, 0x55, 0x6f, 0xde, 0xa6, 0x78, 0x04, 0xfa}, + {0xd4, 0x9f, 0x12, 0x29, 0x2e, 0x4f, 0xfa, 0x0e, 0x12, 0x2a, 0x77, 0x6b, 0x2b, 0x9f, 0xb4, 0xdf}, + {0xee, 0x12, 0x6a, 0xbb, 0xae, 0x11, 0xd6, 0x32, 0x36, 0xa2, 0x49, 0xf4, 0x44, 0x03, 0xa1, 0x1e}, + {0xa6, 0xec, 0xa8, 0x9c, 0xc9, 0x00, 0x96, 0x5f, 0x84, 0x00, 0x05, 0x4b, 0x88, 0x49, 0x04, 0xaf}, + {0xec, 0x93, 0xe5, 0x27, 0xe3, 0xc7, 0xa2, 0x78, 0x4f, 0x9c, 0x19, 0x9d, 0xd8, 0x5e, 0x02, 0x21}, + {0x73, 0x01, 0xd4, 0x82, 0xcd, 0x2e, 0x28, 0xb9, 0xb7, 0xc9, 0x59, 0xa7, 0xf8, 0xaa, 0x3a, 0xbf}, + {0x6b, 0x7d, 0x30, 0x10, 0xd9, 0xef, 0xf2, 0x37, 0x17, 0xb0, 0x86, 0x61, 0x0d, 0x70, 0x60, 0x62}, + {0xc6, 0x9a, 0xfc, 0xf6, 0x53, 0x91, 0xc2, 0x81, 0x43, 0x04, 0x30, 0x21, 0xc2, 0x45, 0xca, 0x5a}, + {0x3a, 0x94, 0xd1, 0x36, 0xe8, 0x92, 0xaf, 0x2c, 0xbb, 0x68, 0x6b, 0x22, 0x3c, 0x97, 0x23, 0x92}, + {0xb4, 0x71, 0x10, 0xe5, 0x58, 0xb9, 0xba, 0x6c, 0xeb, 0x86, 0x58, 0x22, 0x38, 0x92, 0xbf, 0xd3}, + {0x8d, 0x12, 0xe1, 0x24, 0xdd, 0xfd, 0x3d, 0x93, 0x77, 0xc6, 0xf0, 0xae, 0xe5, 0x3c, 0x86, 0xdb}, + {0xb1, 0x12, 0x22, 0xcb, 0xe3, 0x8d, 0xe4, 0x83, 0x9c, 0xa0, 0xeb, 0xff, 0x68, 0x62, 0x60, 0xbb}, + {0x7d, 0xf7, 0x2b, 0xc7, 0x4e, 0x1a, 0xb9, 0x2d, 0x9c, 0xd1, 0xe4, 0xe2, 0xdc, 0xd3, 0x4b, 0x73}, + {0x4e, 0x92, 0xb3, 0x2c, 0xc4, 0x15, 0x14, 0x4b, 0x43, 0x1b, 0x30, 0x61, 0xc3, 0x47, 0xbb, 0x43}, + {0x99, 0x68, 0xeb, 0x16, 0xdd, 0x31, 0xb2, 0x03, 0xf6, 0xef, 0x07, 0xe7, 0xa8, 0x75, 0xa7, 0xdb}, + {0x2c, 0x47, 0xca, 0x7e, 0x02, 0x23, 0x5e, 0x8e, 0x77, 0x59, 0x75, 0x3c, 0x4b, 0x61, 0xf3, 0x6d}, + {0xf9, 0x17, 0x86, 0xb8, 0xb9, 0xe5, 0x1b, 0x6d, 0x77, 0x7d, 0xde, 0xd6, 0x17, 0x5a, 0xa7, 0xcd}, + {0x5d, 0xee, 0x46, 0xa9, 0x9d, 0x06, 0x6c, 0x9d, 0xaa, 0xe9, 0xa8, 0x6b, 0xf0, 0x43, 0x6b, 0xec}, + {0xc1, 0x27, 0xf3, 0x3b, 0x59, 0x11, 0x53, 0xa2, 0x2b, 0x33, 0x57, 0xf9, 0x50, 0x69, 0x1e, 0xcb}, + {0xd9, 0xd0, 0x0e, 0x60, 0x53, 0x03, 0xed, 0xe4, 0x9c, 0x61, 0xda, 0x00, 0x75, 0x0c, 0xee, 0x2c}, + {0x50, 0xa3, 0xa4, 0x63, 0xbc, 0xba, 0xbb, 0x80, 0xab, 0x0c, 0xe9, 0x96, 0xa1, 0xa5, 0xb1, 0xf0}, + {0x39, 0xca, 0x8d, 0x93, 0x30, 0xde, 0x0d, 0xab, 0x88, 0x29, 0x96, 0x5e, 0x02, 0xb1, 0x3d, 0xae}, + {0x42, 0xb4, 0x75, 0x2e, 0xa8, 0xf3, 0x14, 0x88, 0x0b, 0xa4, 0x54, 0xd5, 0x38, 0x8f, 0xbb, 0x17}, + {0xf6, 0x16, 0x0a, 0x36, 0x79, 0xb7, 0xb6, 0xae, 0xd7, 0x7f, 0x42, 0x5f, 0x5b, 0x8a, 0xbb, 0x34}, + {0xde, 0xaf, 0xba, 0xff, 0x18, 0x59, 0xce, 0x43, 0x38, 0x54, 0xe5, 0xcb, 0x41, 0x52, 0xf6, 0x26}, + {0x78, 0xc9, 0x9e, 0x83, 0xf7, 0x9c, 0xca, 0xa2, 0x6a, 0x02, 0xf3, 0xb9, 0x54, 0x9a, 0xe9, 0x4c}, + {0x35, 0x12, 0x90, 0x22, 0x28, 0x6e, 0xc0, 0x40, 0xbe, 0xf7, 0xdf, 0x1b, 0x1a, 0xa5, 0x51, 0xae}, + {0xcf, 0x59, 0xa6, 0x48, 0x0f, 0xbc, 0x73, 0xc1, 0x2b, 0xd2, 0x7e, 0xba, 0x3c, 0x61, 0xc1, 0xa0}, + {0xa1, 0x9d, 0xc5, 0xe9, 0xfd, 0xbd, 0xd6, 0x4a, 0x88, 0x82, 0x28, 0x02, 0x03, 0xcc, 0x6a, 0x75} +}; + +static unsigned char rc[40][16]; +static unsigned char rc_sseed[40][16]; + +static const unsigned char sbox[256] = { + 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, + 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, + 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, + 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, + 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, + 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, + 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, + 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, + 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, + 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, + 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, + 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, + 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, + 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, + 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, + 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, + 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, + 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, + 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, + 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 +}; + +#define XT(x) (((x) << 1) ^ ((((x) >> 7) & 1) * 0x1b)) + +// Simulate _mm_aesenc_si128 instructions from AESNI +static void aesenc(unsigned char *s, const unsigned char *rk) { + uint8_t i, t, u, v[4][4]; + for (i = 0; i < 16; ++i) { + v[((i / 4) + 4 - (i % 4) ) % 4][i % 4] = sbox[s[i]]; + } + for (i = 0; i < 4; ++i) { + t = v[i][0]; + u = v[i][0] ^ v[i][1] ^ v[i][2] ^ v[i][3]; + v[i][0] ^= (uint8_t)(u ^ XT(v[i][0] ^ v[i][1])); + v[i][1] ^= (uint8_t)(u ^ XT(v[i][1] ^ v[i][2])); + v[i][2] ^= (uint8_t)(u ^ XT(v[i][2] ^ v[i][3])); + v[i][3] ^= (uint8_t)(u ^ XT(v[i][3] ^ t)); + } + for (i = 0; i < 16; ++i) { + s[i] = v[i / 4][i % 4] ^ rk[i]; + } +} + +// Simulate _mm_unpacklo_epi32 +static void unpacklo32(unsigned char *t, unsigned char *a, unsigned char *b) { + unsigned char tmp[16]; + memcpy(tmp, a, 4); + memcpy(tmp + 4, b, 4); + memcpy(tmp + 8, a + 4, 4); + memcpy(tmp + 12, b + 4, 4); + memcpy(t, tmp, 16); +} + +// Simulate _mm_unpackhi_epi32 +static void unpackhi32(unsigned char *t, unsigned char *a, unsigned char *b) { + unsigned char tmp[16]; + memcpy(tmp, a + 8, 4); + memcpy(tmp + 4, b + 8, 4); + memcpy(tmp + 8, a + 12, 4); + memcpy(tmp + 12, b + 12, 4); + memcpy(t, tmp, 16); +} + +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]; + + /* Use the standard constants to generate tweaked ones. */ + memcpy(rc, haraka_rc, 40 * 16); + + /* Constants for sk.seed */ + if (sk_seed != NULL) { + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_haraka_S(buf, 40 * 16, sk_seed, seed_length); + memcpy(rc_sseed, buf, 40 * 16); + } + + /* Constants for pk.seed */ + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_haraka_S(buf, 40 * 16, pk_seed, seed_length); + memcpy(rc, buf, 40 * 16); +} + +static void haraka_S_absorb(unsigned char *s, unsigned int r, + const unsigned char *m, unsigned long long mlen, + unsigned char p) { + unsigned long long i; + unsigned char t[r]; + + while (mlen >= r) { + // XOR block to state + for (i = 0; i < r; ++i) { + s[i] ^= m[i]; + } + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_haraka512_perm(s, s); + mlen -= r; + m += r; + } + + for (i = 0; i < r; ++i) { + t[i] = 0; + } + for (i = 0; i < mlen; ++i) { + t[i] = m[i]; + } + t[i] = p; + t[r - 1] |= 128; + for (i = 0; i < r; ++i) { + s[i] ^= t[i]; + } +} + +static void haraka_S_squeezeblocks(unsigned char *h, unsigned long long nblocks, + unsigned char *s, unsigned int r) { + while (nblocks > 0) { + PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_haraka512_perm(s, s); + memcpy(h, s, HARAKAS_RATE); + h += r; + 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, 32, in, inlen, 0x1F); + + haraka_S_squeezeblocks(out, outlen / 32, s, 32); + out += (outlen / 32) * 32; + + if (outlen % 32) { + haraka_S_squeezeblocks(d, 1, s, 32); + for (i = 0; i < outlen % 32; i++) { + out[i] = d[i]; + } + } +} + +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_haraka512_perm(unsigned char *out, const unsigned char *in) { + int i, j; + + unsigned char s[64], tmp[16]; + + memcpy(s, in, 16); + memcpy(s + 16, in + 16, 16); + memcpy(s + 32, in + 32, 16); + memcpy(s + 48, in + 48, 16); + + for (i = 0; i < 5; ++i) { + // aes round(s) + for (j = 0; j < 2; ++j) { + aesenc(s, rc[4 * 2 * i + 4 * j]); + aesenc(s + 16, rc[4 * 2 * i + 4 * j + 1]); + aesenc(s + 32, rc[4 * 2 * i + 4 * j + 2]); + aesenc(s + 48, rc[4 * 2 * i + 4 * j + 3]); + } + + // mixing + unpacklo32(tmp, s, s + 16); + unpackhi32(s, s, s + 16); + unpacklo32(s + 16, s + 32, s + 48); + unpackhi32(s + 32, s + 32, s + 48); + unpacklo32(s + 48, s, s + 32); + unpackhi32(s, s, s + 32); + unpackhi32(s + 32, s + 16, tmp); + unpacklo32(s + 16, s + 16, tmp); + } + + memcpy(out, s, 64); +} + +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) { + int i, j; + + unsigned char s[32], tmp[16]; + + memcpy(s, in, 16); + memcpy(s + 16, in + 16, 16); + + for (i = 0; i < 5; ++i) { + // aes round(s) + for (j = 0; j < 2; ++j) { + aesenc(s, rc[2 * 2 * i + 2 * j]); + aesenc(s + 16, rc[2 * 2 * i + 2 * j + 1]); + } + + // mixing + unpacklo32(tmp, s, s + 16); + unpackhi32(s + 16, s, s + 16); + memcpy(s, tmp, 16); + } + + /* Feed-forward */ + for (i = 0; i < 32; i++) { + out[i] = in[i] ^ s[i]; + } +} + +void PQCLEAN_SPHINCSHARAKA192SROBUST_CLEAN_haraka256_sk(unsigned char *out, const unsigned char *in) { + int i, j; + + unsigned char s[32], tmp[16]; + + memcpy(s, in, 16); + memcpy(s + 16, in + 16, 16); + + for (i = 0; i < 5; ++i) { + // aes round(s) + for (j = 0; j < 2; ++j) { + aesenc(s, rc_sseed[2 * 2 * i + 2 * j]); + aesenc(s + 16, rc_sseed[2 * 2 * i + 2 * j + 1]); + } + + // mixing + unpacklo32(tmp, s, s + 16); + unpackhi32(s + 16, s, s + 16); + memcpy(s, tmp, 16); + } + + /* Feed-forward */ + for (i = 0; i < 32; i++) { + out[i] = in[i] ^ s[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..ab7a86c7 --- /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-signature: 17064 +testvectors-sha256: 6f3297d5c455729edfb6603229b76e0a64c0f3fe12528099a4bceaccab6b9f10 +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/492ec4f1f6d3b3dc4b435783bbaaf4e41cdb6f32 + length-secret-key: 96 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..1c6a1a4a --- /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=-Wall -Wconversion -Wextra -Wpedantic -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..be13cef2 --- /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 64 +#define PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES 32 +#define PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_CRYPTO_BYTES 16976 +#define PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_CRYPTO_SEEDBYTES 48 + +/* + * 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..38a62689 --- /dev/null +++ b/crypto_sign/sphincs-haraka-192s-simple/clean/haraka.c @@ -0,0 +1,373 @@ +/* +Plain C implementation of the Haraka256 and Haraka512 permutations. +*/ +#include +#include +#include + +#include "haraka.h" + +#define HARAKAS_RATE 32 + +static const unsigned char haraka_rc[40][16] = { + {0x9d, 0x7b, 0x81, 0x75, 0xf0, 0xfe, 0xc5, 0xb2, 0x0a, 0xc0, 0x20, 0xe6, 0x4c, 0x70, 0x84, 0x06}, + {0x17, 0xf7, 0x08, 0x2f, 0xa4, 0x6b, 0x0f, 0x64, 0x6b, 0xa0, 0xf3, 0x88, 0xe1, 0xb4, 0x66, 0x8b}, + {0x14, 0x91, 0x02, 0x9f, 0x60, 0x9d, 0x02, 0xcf, 0x98, 0x84, 0xf2, 0x53, 0x2d, 0xde, 0x02, 0x34}, + {0x79, 0x4f, 0x5b, 0xfd, 0xaf, 0xbc, 0xf3, 0xbb, 0x08, 0x4f, 0x7b, 0x2e, 0xe6, 0xea, 0xd6, 0x0e}, + {0x44, 0x70, 0x39, 0xbe, 0x1c, 0xcd, 0xee, 0x79, 0x8b, 0x44, 0x72, 0x48, 0xcb, 0xb0, 0xcf, 0xcb}, + {0x7b, 0x05, 0x8a, 0x2b, 0xed, 0x35, 0x53, 0x8d, 0xb7, 0x32, 0x90, 0x6e, 0xee, 0xcd, 0xea, 0x7e}, + {0x1b, 0xef, 0x4f, 0xda, 0x61, 0x27, 0x41, 0xe2, 0xd0, 0x7c, 0x2e, 0x5e, 0x43, 0x8f, 0xc2, 0x67}, + {0x3b, 0x0b, 0xc7, 0x1f, 0xe2, 0xfd, 0x5f, 0x67, 0x07, 0xcc, 0xca, 0xaf, 0xb0, 0xd9, 0x24, 0x29}, + {0xee, 0x65, 0xd4, 0xb9, 0xca, 0x8f, 0xdb, 0xec, 0xe9, 0x7f, 0x86, 0xe6, 0xf1, 0x63, 0x4d, 0xab}, + {0x33, 0x7e, 0x03, 0xad, 0x4f, 0x40, 0x2a, 0x5b, 0x64, 0xcd, 0xb7, 0xd4, 0x84, 0xbf, 0x30, 0x1c}, + {0x00, 0x98, 0xf6, 0x8d, 0x2e, 0x8b, 0x02, 0x69, 0xbf, 0x23, 0x17, 0x94, 0xb9, 0x0b, 0xcc, 0xb2}, + {0x8a, 0x2d, 0x9d, 0x5c, 0xc8, 0x9e, 0xaa, 0x4a, 0x72, 0x55, 0x6f, 0xde, 0xa6, 0x78, 0x04, 0xfa}, + {0xd4, 0x9f, 0x12, 0x29, 0x2e, 0x4f, 0xfa, 0x0e, 0x12, 0x2a, 0x77, 0x6b, 0x2b, 0x9f, 0xb4, 0xdf}, + {0xee, 0x12, 0x6a, 0xbb, 0xae, 0x11, 0xd6, 0x32, 0x36, 0xa2, 0x49, 0xf4, 0x44, 0x03, 0xa1, 0x1e}, + {0xa6, 0xec, 0xa8, 0x9c, 0xc9, 0x00, 0x96, 0x5f, 0x84, 0x00, 0x05, 0x4b, 0x88, 0x49, 0x04, 0xaf}, + {0xec, 0x93, 0xe5, 0x27, 0xe3, 0xc7, 0xa2, 0x78, 0x4f, 0x9c, 0x19, 0x9d, 0xd8, 0x5e, 0x02, 0x21}, + {0x73, 0x01, 0xd4, 0x82, 0xcd, 0x2e, 0x28, 0xb9, 0xb7, 0xc9, 0x59, 0xa7, 0xf8, 0xaa, 0x3a, 0xbf}, + {0x6b, 0x7d, 0x30, 0x10, 0xd9, 0xef, 0xf2, 0x37, 0x17, 0xb0, 0x86, 0x61, 0x0d, 0x70, 0x60, 0x62}, + {0xc6, 0x9a, 0xfc, 0xf6, 0x53, 0x91, 0xc2, 0x81, 0x43, 0x04, 0x30, 0x21, 0xc2, 0x45, 0xca, 0x5a}, + {0x3a, 0x94, 0xd1, 0x36, 0xe8, 0x92, 0xaf, 0x2c, 0xbb, 0x68, 0x6b, 0x22, 0x3c, 0x97, 0x23, 0x92}, + {0xb4, 0x71, 0x10, 0xe5, 0x58, 0xb9, 0xba, 0x6c, 0xeb, 0x86, 0x58, 0x22, 0x38, 0x92, 0xbf, 0xd3}, + {0x8d, 0x12, 0xe1, 0x24, 0xdd, 0xfd, 0x3d, 0x93, 0x77, 0xc6, 0xf0, 0xae, 0xe5, 0x3c, 0x86, 0xdb}, + {0xb1, 0x12, 0x22, 0xcb, 0xe3, 0x8d, 0xe4, 0x83, 0x9c, 0xa0, 0xeb, 0xff, 0x68, 0x62, 0x60, 0xbb}, + {0x7d, 0xf7, 0x2b, 0xc7, 0x4e, 0x1a, 0xb9, 0x2d, 0x9c, 0xd1, 0xe4, 0xe2, 0xdc, 0xd3, 0x4b, 0x73}, + {0x4e, 0x92, 0xb3, 0x2c, 0xc4, 0x15, 0x14, 0x4b, 0x43, 0x1b, 0x30, 0x61, 0xc3, 0x47, 0xbb, 0x43}, + {0x99, 0x68, 0xeb, 0x16, 0xdd, 0x31, 0xb2, 0x03, 0xf6, 0xef, 0x07, 0xe7, 0xa8, 0x75, 0xa7, 0xdb}, + {0x2c, 0x47, 0xca, 0x7e, 0x02, 0x23, 0x5e, 0x8e, 0x77, 0x59, 0x75, 0x3c, 0x4b, 0x61, 0xf3, 0x6d}, + {0xf9, 0x17, 0x86, 0xb8, 0xb9, 0xe5, 0x1b, 0x6d, 0x77, 0x7d, 0xde, 0xd6, 0x17, 0x5a, 0xa7, 0xcd}, + {0x5d, 0xee, 0x46, 0xa9, 0x9d, 0x06, 0x6c, 0x9d, 0xaa, 0xe9, 0xa8, 0x6b, 0xf0, 0x43, 0x6b, 0xec}, + {0xc1, 0x27, 0xf3, 0x3b, 0x59, 0x11, 0x53, 0xa2, 0x2b, 0x33, 0x57, 0xf9, 0x50, 0x69, 0x1e, 0xcb}, + {0xd9, 0xd0, 0x0e, 0x60, 0x53, 0x03, 0xed, 0xe4, 0x9c, 0x61, 0xda, 0x00, 0x75, 0x0c, 0xee, 0x2c}, + {0x50, 0xa3, 0xa4, 0x63, 0xbc, 0xba, 0xbb, 0x80, 0xab, 0x0c, 0xe9, 0x96, 0xa1, 0xa5, 0xb1, 0xf0}, + {0x39, 0xca, 0x8d, 0x93, 0x30, 0xde, 0x0d, 0xab, 0x88, 0x29, 0x96, 0x5e, 0x02, 0xb1, 0x3d, 0xae}, + {0x42, 0xb4, 0x75, 0x2e, 0xa8, 0xf3, 0x14, 0x88, 0x0b, 0xa4, 0x54, 0xd5, 0x38, 0x8f, 0xbb, 0x17}, + {0xf6, 0x16, 0x0a, 0x36, 0x79, 0xb7, 0xb6, 0xae, 0xd7, 0x7f, 0x42, 0x5f, 0x5b, 0x8a, 0xbb, 0x34}, + {0xde, 0xaf, 0xba, 0xff, 0x18, 0x59, 0xce, 0x43, 0x38, 0x54, 0xe5, 0xcb, 0x41, 0x52, 0xf6, 0x26}, + {0x78, 0xc9, 0x9e, 0x83, 0xf7, 0x9c, 0xca, 0xa2, 0x6a, 0x02, 0xf3, 0xb9, 0x54, 0x9a, 0xe9, 0x4c}, + {0x35, 0x12, 0x90, 0x22, 0x28, 0x6e, 0xc0, 0x40, 0xbe, 0xf7, 0xdf, 0x1b, 0x1a, 0xa5, 0x51, 0xae}, + {0xcf, 0x59, 0xa6, 0x48, 0x0f, 0xbc, 0x73, 0xc1, 0x2b, 0xd2, 0x7e, 0xba, 0x3c, 0x61, 0xc1, 0xa0}, + {0xa1, 0x9d, 0xc5, 0xe9, 0xfd, 0xbd, 0xd6, 0x4a, 0x88, 0x82, 0x28, 0x02, 0x03, 0xcc, 0x6a, 0x75} +}; + +static unsigned char rc[40][16]; +static unsigned char rc_sseed[40][16]; + +static const unsigned char sbox[256] = { + 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, + 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, + 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, + 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, + 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, + 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, + 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, + 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, + 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, + 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, + 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, + 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, + 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, + 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, + 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, + 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, + 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, + 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, + 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, + 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 +}; + +#define XT(x) (((x) << 1) ^ ((((x) >> 7) & 1) * 0x1b)) + +// Simulate _mm_aesenc_si128 instructions from AESNI +static void aesenc(unsigned char *s, const unsigned char *rk) { + uint8_t i, t, u, v[4][4]; + for (i = 0; i < 16; ++i) { + v[((i / 4) + 4 - (i % 4) ) % 4][i % 4] = sbox[s[i]]; + } + for (i = 0; i < 4; ++i) { + t = v[i][0]; + u = v[i][0] ^ v[i][1] ^ v[i][2] ^ v[i][3]; + v[i][0] ^= (uint8_t)(u ^ XT(v[i][0] ^ v[i][1])); + v[i][1] ^= (uint8_t)(u ^ XT(v[i][1] ^ v[i][2])); + v[i][2] ^= (uint8_t)(u ^ XT(v[i][2] ^ v[i][3])); + v[i][3] ^= (uint8_t)(u ^ XT(v[i][3] ^ t)); + } + for (i = 0; i < 16; ++i) { + s[i] = v[i / 4][i % 4] ^ rk[i]; + } +} + +// Simulate _mm_unpacklo_epi32 +static void unpacklo32(unsigned char *t, unsigned char *a, unsigned char *b) { + unsigned char tmp[16]; + memcpy(tmp, a, 4); + memcpy(tmp + 4, b, 4); + memcpy(tmp + 8, a + 4, 4); + memcpy(tmp + 12, b + 4, 4); + memcpy(t, tmp, 16); +} + +// Simulate _mm_unpackhi_epi32 +static void unpackhi32(unsigned char *t, unsigned char *a, unsigned char *b) { + unsigned char tmp[16]; + memcpy(tmp, a + 8, 4); + memcpy(tmp + 4, b + 8, 4); + memcpy(tmp + 8, a + 12, 4); + memcpy(tmp + 12, b + 12, 4); + memcpy(t, tmp, 16); +} + +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]; + + /* Use the standard constants to generate tweaked ones. */ + memcpy(rc, haraka_rc, 40 * 16); + + /* Constants for sk.seed */ + if (sk_seed != NULL) { + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_haraka_S(buf, 40 * 16, sk_seed, seed_length); + memcpy(rc_sseed, buf, 40 * 16); + } + + /* Constants for pk.seed */ + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_haraka_S(buf, 40 * 16, pk_seed, seed_length); + memcpy(rc, buf, 40 * 16); +} + +static void haraka_S_absorb(unsigned char *s, unsigned int r, + const unsigned char *m, unsigned long long mlen, + unsigned char p) { + unsigned long long i; + unsigned char t[r]; + + while (mlen >= r) { + // XOR block to state + for (i = 0; i < r; ++i) { + s[i] ^= m[i]; + } + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_haraka512_perm(s, s); + mlen -= r; + m += r; + } + + for (i = 0; i < r; ++i) { + t[i] = 0; + } + for (i = 0; i < mlen; ++i) { + t[i] = m[i]; + } + t[i] = p; + t[r - 1] |= 128; + for (i = 0; i < r; ++i) { + s[i] ^= t[i]; + } +} + +static void haraka_S_squeezeblocks(unsigned char *h, unsigned long long nblocks, + unsigned char *s, unsigned int r) { + while (nblocks > 0) { + PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_haraka512_perm(s, s); + memcpy(h, s, HARAKAS_RATE); + h += r; + 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, 32, in, inlen, 0x1F); + + haraka_S_squeezeblocks(out, outlen / 32, s, 32); + out += (outlen / 32) * 32; + + if (outlen % 32) { + haraka_S_squeezeblocks(d, 1, s, 32); + for (i = 0; i < outlen % 32; i++) { + out[i] = d[i]; + } + } +} + +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_haraka512_perm(unsigned char *out, const unsigned char *in) { + int i, j; + + unsigned char s[64], tmp[16]; + + memcpy(s, in, 16); + memcpy(s + 16, in + 16, 16); + memcpy(s + 32, in + 32, 16); + memcpy(s + 48, in + 48, 16); + + for (i = 0; i < 5; ++i) { + // aes round(s) + for (j = 0; j < 2; ++j) { + aesenc(s, rc[4 * 2 * i + 4 * j]); + aesenc(s + 16, rc[4 * 2 * i + 4 * j + 1]); + aesenc(s + 32, rc[4 * 2 * i + 4 * j + 2]); + aesenc(s + 48, rc[4 * 2 * i + 4 * j + 3]); + } + + // mixing + unpacklo32(tmp, s, s + 16); + unpackhi32(s, s, s + 16); + unpacklo32(s + 16, s + 32, s + 48); + unpackhi32(s + 32, s + 32, s + 48); + unpacklo32(s + 48, s, s + 32); + unpackhi32(s, s, s + 32); + unpackhi32(s + 32, s + 16, tmp); + unpacklo32(s + 16, s + 16, tmp); + } + + memcpy(out, s, 64); +} + +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) { + int i, j; + + unsigned char s[32], tmp[16]; + + memcpy(s, in, 16); + memcpy(s + 16, in + 16, 16); + + for (i = 0; i < 5; ++i) { + // aes round(s) + for (j = 0; j < 2; ++j) { + aesenc(s, rc[2 * 2 * i + 2 * j]); + aesenc(s + 16, rc[2 * 2 * i + 2 * j + 1]); + } + + // mixing + unpacklo32(tmp, s, s + 16); + unpackhi32(s + 16, s, s + 16); + memcpy(s, tmp, 16); + } + + /* Feed-forward */ + for (i = 0; i < 32; i++) { + out[i] = in[i] ^ s[i]; + } +} + +void PQCLEAN_SPHINCSHARAKA192SSIMPLE_CLEAN_haraka256_sk(unsigned char *out, const unsigned char *in) { + int i, j; + + unsigned char s[32], tmp[16]; + + memcpy(s, in, 16); + memcpy(s + 16, in + 16, 16); + + for (i = 0; i < 5; ++i) { + // aes round(s) + for (j = 0; j < 2; ++j) { + aesenc(s, rc_sseed[2 * 2 * i + 2 * j]); + aesenc(s + 16, rc_sseed[2 * 2 * i + 2 * j + 1]); + } + + // mixing + unpacklo32(tmp, s, s + 16); + unpackhi32(s + 16, s, s + 16); + memcpy(s, tmp, 16); + } + + /* Feed-forward */ + for (i = 0; i < 32; i++) { + out[i] = in[i] ^ s[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..af9dcc0c --- /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-signature: 49216 +testvectors-sha256: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +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/492ec4f1f6d3b3dc4b435783bbaaf4e41cdb6f32 + length-secret-key: 128 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..65645c8b --- /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=-Wall -Wconversion -Wextra -Wpedantic -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..85527e7f --- /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 64 +#define PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES 32 +#define PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_CRYPTO_BYTES 16976 +#define PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_CRYPTO_SEEDBYTES 48 + +/* + * 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..358d9ecb --- /dev/null +++ b/crypto_sign/sphincs-haraka-256f-robust/clean/haraka.c @@ -0,0 +1,373 @@ +/* +Plain C implementation of the Haraka256 and Haraka512 permutations. +*/ +#include +#include +#include + +#include "haraka.h" + +#define HARAKAS_RATE 32 + +static const unsigned char haraka_rc[40][16] = { + {0x9d, 0x7b, 0x81, 0x75, 0xf0, 0xfe, 0xc5, 0xb2, 0x0a, 0xc0, 0x20, 0xe6, 0x4c, 0x70, 0x84, 0x06}, + {0x17, 0xf7, 0x08, 0x2f, 0xa4, 0x6b, 0x0f, 0x64, 0x6b, 0xa0, 0xf3, 0x88, 0xe1, 0xb4, 0x66, 0x8b}, + {0x14, 0x91, 0x02, 0x9f, 0x60, 0x9d, 0x02, 0xcf, 0x98, 0x84, 0xf2, 0x53, 0x2d, 0xde, 0x02, 0x34}, + {0x79, 0x4f, 0x5b, 0xfd, 0xaf, 0xbc, 0xf3, 0xbb, 0x08, 0x4f, 0x7b, 0x2e, 0xe6, 0xea, 0xd6, 0x0e}, + {0x44, 0x70, 0x39, 0xbe, 0x1c, 0xcd, 0xee, 0x79, 0x8b, 0x44, 0x72, 0x48, 0xcb, 0xb0, 0xcf, 0xcb}, + {0x7b, 0x05, 0x8a, 0x2b, 0xed, 0x35, 0x53, 0x8d, 0xb7, 0x32, 0x90, 0x6e, 0xee, 0xcd, 0xea, 0x7e}, + {0x1b, 0xef, 0x4f, 0xda, 0x61, 0x27, 0x41, 0xe2, 0xd0, 0x7c, 0x2e, 0x5e, 0x43, 0x8f, 0xc2, 0x67}, + {0x3b, 0x0b, 0xc7, 0x1f, 0xe2, 0xfd, 0x5f, 0x67, 0x07, 0xcc, 0xca, 0xaf, 0xb0, 0xd9, 0x24, 0x29}, + {0xee, 0x65, 0xd4, 0xb9, 0xca, 0x8f, 0xdb, 0xec, 0xe9, 0x7f, 0x86, 0xe6, 0xf1, 0x63, 0x4d, 0xab}, + {0x33, 0x7e, 0x03, 0xad, 0x4f, 0x40, 0x2a, 0x5b, 0x64, 0xcd, 0xb7, 0xd4, 0x84, 0xbf, 0x30, 0x1c}, + {0x00, 0x98, 0xf6, 0x8d, 0x2e, 0x8b, 0x02, 0x69, 0xbf, 0x23, 0x17, 0x94, 0xb9, 0x0b, 0xcc, 0xb2}, + {0x8a, 0x2d, 0x9d, 0x5c, 0xc8, 0x9e, 0xaa, 0x4a, 0x72, 0x55, 0x6f, 0xde, 0xa6, 0x78, 0x04, 0xfa}, + {0xd4, 0x9f, 0x12, 0x29, 0x2e, 0x4f, 0xfa, 0x0e, 0x12, 0x2a, 0x77, 0x6b, 0x2b, 0x9f, 0xb4, 0xdf}, + {0xee, 0x12, 0x6a, 0xbb, 0xae, 0x11, 0xd6, 0x32, 0x36, 0xa2, 0x49, 0xf4, 0x44, 0x03, 0xa1, 0x1e}, + {0xa6, 0xec, 0xa8, 0x9c, 0xc9, 0x00, 0x96, 0x5f, 0x84, 0x00, 0x05, 0x4b, 0x88, 0x49, 0x04, 0xaf}, + {0xec, 0x93, 0xe5, 0x27, 0xe3, 0xc7, 0xa2, 0x78, 0x4f, 0x9c, 0x19, 0x9d, 0xd8, 0x5e, 0x02, 0x21}, + {0x73, 0x01, 0xd4, 0x82, 0xcd, 0x2e, 0x28, 0xb9, 0xb7, 0xc9, 0x59, 0xa7, 0xf8, 0xaa, 0x3a, 0xbf}, + {0x6b, 0x7d, 0x30, 0x10, 0xd9, 0xef, 0xf2, 0x37, 0x17, 0xb0, 0x86, 0x61, 0x0d, 0x70, 0x60, 0x62}, + {0xc6, 0x9a, 0xfc, 0xf6, 0x53, 0x91, 0xc2, 0x81, 0x43, 0x04, 0x30, 0x21, 0xc2, 0x45, 0xca, 0x5a}, + {0x3a, 0x94, 0xd1, 0x36, 0xe8, 0x92, 0xaf, 0x2c, 0xbb, 0x68, 0x6b, 0x22, 0x3c, 0x97, 0x23, 0x92}, + {0xb4, 0x71, 0x10, 0xe5, 0x58, 0xb9, 0xba, 0x6c, 0xeb, 0x86, 0x58, 0x22, 0x38, 0x92, 0xbf, 0xd3}, + {0x8d, 0x12, 0xe1, 0x24, 0xdd, 0xfd, 0x3d, 0x93, 0x77, 0xc6, 0xf0, 0xae, 0xe5, 0x3c, 0x86, 0xdb}, + {0xb1, 0x12, 0x22, 0xcb, 0xe3, 0x8d, 0xe4, 0x83, 0x9c, 0xa0, 0xeb, 0xff, 0x68, 0x62, 0x60, 0xbb}, + {0x7d, 0xf7, 0x2b, 0xc7, 0x4e, 0x1a, 0xb9, 0x2d, 0x9c, 0xd1, 0xe4, 0xe2, 0xdc, 0xd3, 0x4b, 0x73}, + {0x4e, 0x92, 0xb3, 0x2c, 0xc4, 0x15, 0x14, 0x4b, 0x43, 0x1b, 0x30, 0x61, 0xc3, 0x47, 0xbb, 0x43}, + {0x99, 0x68, 0xeb, 0x16, 0xdd, 0x31, 0xb2, 0x03, 0xf6, 0xef, 0x07, 0xe7, 0xa8, 0x75, 0xa7, 0xdb}, + {0x2c, 0x47, 0xca, 0x7e, 0x02, 0x23, 0x5e, 0x8e, 0x77, 0x59, 0x75, 0x3c, 0x4b, 0x61, 0xf3, 0x6d}, + {0xf9, 0x17, 0x86, 0xb8, 0xb9, 0xe5, 0x1b, 0x6d, 0x77, 0x7d, 0xde, 0xd6, 0x17, 0x5a, 0xa7, 0xcd}, + {0x5d, 0xee, 0x46, 0xa9, 0x9d, 0x06, 0x6c, 0x9d, 0xaa, 0xe9, 0xa8, 0x6b, 0xf0, 0x43, 0x6b, 0xec}, + {0xc1, 0x27, 0xf3, 0x3b, 0x59, 0x11, 0x53, 0xa2, 0x2b, 0x33, 0x57, 0xf9, 0x50, 0x69, 0x1e, 0xcb}, + {0xd9, 0xd0, 0x0e, 0x60, 0x53, 0x03, 0xed, 0xe4, 0x9c, 0x61, 0xda, 0x00, 0x75, 0x0c, 0xee, 0x2c}, + {0x50, 0xa3, 0xa4, 0x63, 0xbc, 0xba, 0xbb, 0x80, 0xab, 0x0c, 0xe9, 0x96, 0xa1, 0xa5, 0xb1, 0xf0}, + {0x39, 0xca, 0x8d, 0x93, 0x30, 0xde, 0x0d, 0xab, 0x88, 0x29, 0x96, 0x5e, 0x02, 0xb1, 0x3d, 0xae}, + {0x42, 0xb4, 0x75, 0x2e, 0xa8, 0xf3, 0x14, 0x88, 0x0b, 0xa4, 0x54, 0xd5, 0x38, 0x8f, 0xbb, 0x17}, + {0xf6, 0x16, 0x0a, 0x36, 0x79, 0xb7, 0xb6, 0xae, 0xd7, 0x7f, 0x42, 0x5f, 0x5b, 0x8a, 0xbb, 0x34}, + {0xde, 0xaf, 0xba, 0xff, 0x18, 0x59, 0xce, 0x43, 0x38, 0x54, 0xe5, 0xcb, 0x41, 0x52, 0xf6, 0x26}, + {0x78, 0xc9, 0x9e, 0x83, 0xf7, 0x9c, 0xca, 0xa2, 0x6a, 0x02, 0xf3, 0xb9, 0x54, 0x9a, 0xe9, 0x4c}, + {0x35, 0x12, 0x90, 0x22, 0x28, 0x6e, 0xc0, 0x40, 0xbe, 0xf7, 0xdf, 0x1b, 0x1a, 0xa5, 0x51, 0xae}, + {0xcf, 0x59, 0xa6, 0x48, 0x0f, 0xbc, 0x73, 0xc1, 0x2b, 0xd2, 0x7e, 0xba, 0x3c, 0x61, 0xc1, 0xa0}, + {0xa1, 0x9d, 0xc5, 0xe9, 0xfd, 0xbd, 0xd6, 0x4a, 0x88, 0x82, 0x28, 0x02, 0x03, 0xcc, 0x6a, 0x75} +}; + +static unsigned char rc[40][16]; +static unsigned char rc_sseed[40][16]; + +static const unsigned char sbox[256] = { + 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, + 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, + 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, + 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, + 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, + 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, + 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, + 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, + 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, + 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, + 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, + 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, + 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, + 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, + 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, + 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, + 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, + 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, + 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, + 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 +}; + +#define XT(x) (((x) << 1) ^ ((((x) >> 7) & 1) * 0x1b)) + +// Simulate _mm_aesenc_si128 instructions from AESNI +static void aesenc(unsigned char *s, const unsigned char *rk) { + uint8_t i, t, u, v[4][4]; + for (i = 0; i < 16; ++i) { + v[((i / 4) + 4 - (i % 4) ) % 4][i % 4] = sbox[s[i]]; + } + for (i = 0; i < 4; ++i) { + t = v[i][0]; + u = v[i][0] ^ v[i][1] ^ v[i][2] ^ v[i][3]; + v[i][0] ^= (uint8_t)(u ^ XT(v[i][0] ^ v[i][1])); + v[i][1] ^= (uint8_t)(u ^ XT(v[i][1] ^ v[i][2])); + v[i][2] ^= (uint8_t)(u ^ XT(v[i][2] ^ v[i][3])); + v[i][3] ^= (uint8_t)(u ^ XT(v[i][3] ^ t)); + } + for (i = 0; i < 16; ++i) { + s[i] = v[i / 4][i % 4] ^ rk[i]; + } +} + +// Simulate _mm_unpacklo_epi32 +static void unpacklo32(unsigned char *t, unsigned char *a, unsigned char *b) { + unsigned char tmp[16]; + memcpy(tmp, a, 4); + memcpy(tmp + 4, b, 4); + memcpy(tmp + 8, a + 4, 4); + memcpy(tmp + 12, b + 4, 4); + memcpy(t, tmp, 16); +} + +// Simulate _mm_unpackhi_epi32 +static void unpackhi32(unsigned char *t, unsigned char *a, unsigned char *b) { + unsigned char tmp[16]; + memcpy(tmp, a + 8, 4); + memcpy(tmp + 4, b + 8, 4); + memcpy(tmp + 8, a + 12, 4); + memcpy(tmp + 12, b + 12, 4); + memcpy(t, tmp, 16); +} + +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]; + + /* Use the standard constants to generate tweaked ones. */ + memcpy(rc, haraka_rc, 40 * 16); + + /* Constants for sk.seed */ + if (sk_seed != NULL) { + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_haraka_S(buf, 40 * 16, sk_seed, seed_length); + memcpy(rc_sseed, buf, 40 * 16); + } + + /* Constants for pk.seed */ + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_haraka_S(buf, 40 * 16, pk_seed, seed_length); + memcpy(rc, buf, 40 * 16); +} + +static void haraka_S_absorb(unsigned char *s, unsigned int r, + const unsigned char *m, unsigned long long mlen, + unsigned char p) { + unsigned long long i; + unsigned char t[r]; + + while (mlen >= r) { + // XOR block to state + for (i = 0; i < r; ++i) { + s[i] ^= m[i]; + } + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_haraka512_perm(s, s); + mlen -= r; + m += r; + } + + for (i = 0; i < r; ++i) { + t[i] = 0; + } + for (i = 0; i < mlen; ++i) { + t[i] = m[i]; + } + t[i] = p; + t[r - 1] |= 128; + for (i = 0; i < r; ++i) { + s[i] ^= t[i]; + } +} + +static void haraka_S_squeezeblocks(unsigned char *h, unsigned long long nblocks, + unsigned char *s, unsigned int r) { + while (nblocks > 0) { + PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_haraka512_perm(s, s); + memcpy(h, s, HARAKAS_RATE); + h += r; + 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, 32, in, inlen, 0x1F); + + haraka_S_squeezeblocks(out, outlen / 32, s, 32); + out += (outlen / 32) * 32; + + if (outlen % 32) { + haraka_S_squeezeblocks(d, 1, s, 32); + for (i = 0; i < outlen % 32; i++) { + out[i] = d[i]; + } + } +} + +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_haraka512_perm(unsigned char *out, const unsigned char *in) { + int i, j; + + unsigned char s[64], tmp[16]; + + memcpy(s, in, 16); + memcpy(s + 16, in + 16, 16); + memcpy(s + 32, in + 32, 16); + memcpy(s + 48, in + 48, 16); + + for (i = 0; i < 5; ++i) { + // aes round(s) + for (j = 0; j < 2; ++j) { + aesenc(s, rc[4 * 2 * i + 4 * j]); + aesenc(s + 16, rc[4 * 2 * i + 4 * j + 1]); + aesenc(s + 32, rc[4 * 2 * i + 4 * j + 2]); + aesenc(s + 48, rc[4 * 2 * i + 4 * j + 3]); + } + + // mixing + unpacklo32(tmp, s, s + 16); + unpackhi32(s, s, s + 16); + unpacklo32(s + 16, s + 32, s + 48); + unpackhi32(s + 32, s + 32, s + 48); + unpacklo32(s + 48, s, s + 32); + unpackhi32(s, s, s + 32); + unpackhi32(s + 32, s + 16, tmp); + unpacklo32(s + 16, s + 16, tmp); + } + + memcpy(out, s, 64); +} + +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) { + int i, j; + + unsigned char s[32], tmp[16]; + + memcpy(s, in, 16); + memcpy(s + 16, in + 16, 16); + + for (i = 0; i < 5; ++i) { + // aes round(s) + for (j = 0; j < 2; ++j) { + aesenc(s, rc[2 * 2 * i + 2 * j]); + aesenc(s + 16, rc[2 * 2 * i + 2 * j + 1]); + } + + // mixing + unpacklo32(tmp, s, s + 16); + unpackhi32(s + 16, s, s + 16); + memcpy(s, tmp, 16); + } + + /* Feed-forward */ + for (i = 0; i < 32; i++) { + out[i] = in[i] ^ s[i]; + } +} + +void PQCLEAN_SPHINCSHARAKA256FROBUST_CLEAN_haraka256_sk(unsigned char *out, const unsigned char *in) { + int i, j; + + unsigned char s[32], tmp[16]; + + memcpy(s, in, 16); + memcpy(s + 16, in + 16, 16); + + for (i = 0; i < 5; ++i) { + // aes round(s) + for (j = 0; j < 2; ++j) { + aesenc(s, rc_sseed[2 * 2 * i + 2 * j]); + aesenc(s + 16, rc_sseed[2 * 2 * i + 2 * j + 1]); + } + + // mixing + unpacklo32(tmp, s, s + 16); + unpackhi32(s + 16, s, s + 16); + memcpy(s, tmp, 16); + } + + /* Feed-forward */ + for (i = 0; i < 32; i++) { + out[i] = in[i] ^ s[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..af9dcc0c --- /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-signature: 49216 +testvectors-sha256: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +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/492ec4f1f6d3b3dc4b435783bbaaf4e41cdb6f32 + length-secret-key: 128 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..a573a3c1 --- /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=-Wall -Wconversion -Wextra -Wpedantic -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..08b4ca36 --- /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 64 +#define PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES 32 +#define PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_CRYPTO_BYTES 16976 +#define PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_CRYPTO_SEEDBYTES 48 + +/* + * 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..be939df5 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256f-simple/clean/haraka.c @@ -0,0 +1,373 @@ +/* +Plain C implementation of the Haraka256 and Haraka512 permutations. +*/ +#include +#include +#include + +#include "haraka.h" + +#define HARAKAS_RATE 32 + +static const unsigned char haraka_rc[40][16] = { + {0x9d, 0x7b, 0x81, 0x75, 0xf0, 0xfe, 0xc5, 0xb2, 0x0a, 0xc0, 0x20, 0xe6, 0x4c, 0x70, 0x84, 0x06}, + {0x17, 0xf7, 0x08, 0x2f, 0xa4, 0x6b, 0x0f, 0x64, 0x6b, 0xa0, 0xf3, 0x88, 0xe1, 0xb4, 0x66, 0x8b}, + {0x14, 0x91, 0x02, 0x9f, 0x60, 0x9d, 0x02, 0xcf, 0x98, 0x84, 0xf2, 0x53, 0x2d, 0xde, 0x02, 0x34}, + {0x79, 0x4f, 0x5b, 0xfd, 0xaf, 0xbc, 0xf3, 0xbb, 0x08, 0x4f, 0x7b, 0x2e, 0xe6, 0xea, 0xd6, 0x0e}, + {0x44, 0x70, 0x39, 0xbe, 0x1c, 0xcd, 0xee, 0x79, 0x8b, 0x44, 0x72, 0x48, 0xcb, 0xb0, 0xcf, 0xcb}, + {0x7b, 0x05, 0x8a, 0x2b, 0xed, 0x35, 0x53, 0x8d, 0xb7, 0x32, 0x90, 0x6e, 0xee, 0xcd, 0xea, 0x7e}, + {0x1b, 0xef, 0x4f, 0xda, 0x61, 0x27, 0x41, 0xe2, 0xd0, 0x7c, 0x2e, 0x5e, 0x43, 0x8f, 0xc2, 0x67}, + {0x3b, 0x0b, 0xc7, 0x1f, 0xe2, 0xfd, 0x5f, 0x67, 0x07, 0xcc, 0xca, 0xaf, 0xb0, 0xd9, 0x24, 0x29}, + {0xee, 0x65, 0xd4, 0xb9, 0xca, 0x8f, 0xdb, 0xec, 0xe9, 0x7f, 0x86, 0xe6, 0xf1, 0x63, 0x4d, 0xab}, + {0x33, 0x7e, 0x03, 0xad, 0x4f, 0x40, 0x2a, 0x5b, 0x64, 0xcd, 0xb7, 0xd4, 0x84, 0xbf, 0x30, 0x1c}, + {0x00, 0x98, 0xf6, 0x8d, 0x2e, 0x8b, 0x02, 0x69, 0xbf, 0x23, 0x17, 0x94, 0xb9, 0x0b, 0xcc, 0xb2}, + {0x8a, 0x2d, 0x9d, 0x5c, 0xc8, 0x9e, 0xaa, 0x4a, 0x72, 0x55, 0x6f, 0xde, 0xa6, 0x78, 0x04, 0xfa}, + {0xd4, 0x9f, 0x12, 0x29, 0x2e, 0x4f, 0xfa, 0x0e, 0x12, 0x2a, 0x77, 0x6b, 0x2b, 0x9f, 0xb4, 0xdf}, + {0xee, 0x12, 0x6a, 0xbb, 0xae, 0x11, 0xd6, 0x32, 0x36, 0xa2, 0x49, 0xf4, 0x44, 0x03, 0xa1, 0x1e}, + {0xa6, 0xec, 0xa8, 0x9c, 0xc9, 0x00, 0x96, 0x5f, 0x84, 0x00, 0x05, 0x4b, 0x88, 0x49, 0x04, 0xaf}, + {0xec, 0x93, 0xe5, 0x27, 0xe3, 0xc7, 0xa2, 0x78, 0x4f, 0x9c, 0x19, 0x9d, 0xd8, 0x5e, 0x02, 0x21}, + {0x73, 0x01, 0xd4, 0x82, 0xcd, 0x2e, 0x28, 0xb9, 0xb7, 0xc9, 0x59, 0xa7, 0xf8, 0xaa, 0x3a, 0xbf}, + {0x6b, 0x7d, 0x30, 0x10, 0xd9, 0xef, 0xf2, 0x37, 0x17, 0xb0, 0x86, 0x61, 0x0d, 0x70, 0x60, 0x62}, + {0xc6, 0x9a, 0xfc, 0xf6, 0x53, 0x91, 0xc2, 0x81, 0x43, 0x04, 0x30, 0x21, 0xc2, 0x45, 0xca, 0x5a}, + {0x3a, 0x94, 0xd1, 0x36, 0xe8, 0x92, 0xaf, 0x2c, 0xbb, 0x68, 0x6b, 0x22, 0x3c, 0x97, 0x23, 0x92}, + {0xb4, 0x71, 0x10, 0xe5, 0x58, 0xb9, 0xba, 0x6c, 0xeb, 0x86, 0x58, 0x22, 0x38, 0x92, 0xbf, 0xd3}, + {0x8d, 0x12, 0xe1, 0x24, 0xdd, 0xfd, 0x3d, 0x93, 0x77, 0xc6, 0xf0, 0xae, 0xe5, 0x3c, 0x86, 0xdb}, + {0xb1, 0x12, 0x22, 0xcb, 0xe3, 0x8d, 0xe4, 0x83, 0x9c, 0xa0, 0xeb, 0xff, 0x68, 0x62, 0x60, 0xbb}, + {0x7d, 0xf7, 0x2b, 0xc7, 0x4e, 0x1a, 0xb9, 0x2d, 0x9c, 0xd1, 0xe4, 0xe2, 0xdc, 0xd3, 0x4b, 0x73}, + {0x4e, 0x92, 0xb3, 0x2c, 0xc4, 0x15, 0x14, 0x4b, 0x43, 0x1b, 0x30, 0x61, 0xc3, 0x47, 0xbb, 0x43}, + {0x99, 0x68, 0xeb, 0x16, 0xdd, 0x31, 0xb2, 0x03, 0xf6, 0xef, 0x07, 0xe7, 0xa8, 0x75, 0xa7, 0xdb}, + {0x2c, 0x47, 0xca, 0x7e, 0x02, 0x23, 0x5e, 0x8e, 0x77, 0x59, 0x75, 0x3c, 0x4b, 0x61, 0xf3, 0x6d}, + {0xf9, 0x17, 0x86, 0xb8, 0xb9, 0xe5, 0x1b, 0x6d, 0x77, 0x7d, 0xde, 0xd6, 0x17, 0x5a, 0xa7, 0xcd}, + {0x5d, 0xee, 0x46, 0xa9, 0x9d, 0x06, 0x6c, 0x9d, 0xaa, 0xe9, 0xa8, 0x6b, 0xf0, 0x43, 0x6b, 0xec}, + {0xc1, 0x27, 0xf3, 0x3b, 0x59, 0x11, 0x53, 0xa2, 0x2b, 0x33, 0x57, 0xf9, 0x50, 0x69, 0x1e, 0xcb}, + {0xd9, 0xd0, 0x0e, 0x60, 0x53, 0x03, 0xed, 0xe4, 0x9c, 0x61, 0xda, 0x00, 0x75, 0x0c, 0xee, 0x2c}, + {0x50, 0xa3, 0xa4, 0x63, 0xbc, 0xba, 0xbb, 0x80, 0xab, 0x0c, 0xe9, 0x96, 0xa1, 0xa5, 0xb1, 0xf0}, + {0x39, 0xca, 0x8d, 0x93, 0x30, 0xde, 0x0d, 0xab, 0x88, 0x29, 0x96, 0x5e, 0x02, 0xb1, 0x3d, 0xae}, + {0x42, 0xb4, 0x75, 0x2e, 0xa8, 0xf3, 0x14, 0x88, 0x0b, 0xa4, 0x54, 0xd5, 0x38, 0x8f, 0xbb, 0x17}, + {0xf6, 0x16, 0x0a, 0x36, 0x79, 0xb7, 0xb6, 0xae, 0xd7, 0x7f, 0x42, 0x5f, 0x5b, 0x8a, 0xbb, 0x34}, + {0xde, 0xaf, 0xba, 0xff, 0x18, 0x59, 0xce, 0x43, 0x38, 0x54, 0xe5, 0xcb, 0x41, 0x52, 0xf6, 0x26}, + {0x78, 0xc9, 0x9e, 0x83, 0xf7, 0x9c, 0xca, 0xa2, 0x6a, 0x02, 0xf3, 0xb9, 0x54, 0x9a, 0xe9, 0x4c}, + {0x35, 0x12, 0x90, 0x22, 0x28, 0x6e, 0xc0, 0x40, 0xbe, 0xf7, 0xdf, 0x1b, 0x1a, 0xa5, 0x51, 0xae}, + {0xcf, 0x59, 0xa6, 0x48, 0x0f, 0xbc, 0x73, 0xc1, 0x2b, 0xd2, 0x7e, 0xba, 0x3c, 0x61, 0xc1, 0xa0}, + {0xa1, 0x9d, 0xc5, 0xe9, 0xfd, 0xbd, 0xd6, 0x4a, 0x88, 0x82, 0x28, 0x02, 0x03, 0xcc, 0x6a, 0x75} +}; + +static unsigned char rc[40][16]; +static unsigned char rc_sseed[40][16]; + +static const unsigned char sbox[256] = { + 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, + 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, + 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, + 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, + 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, + 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, + 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, + 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, + 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, + 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, + 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, + 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, + 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, + 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, + 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, + 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, + 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, + 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, + 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, + 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 +}; + +#define XT(x) (((x) << 1) ^ ((((x) >> 7) & 1) * 0x1b)) + +// Simulate _mm_aesenc_si128 instructions from AESNI +static void aesenc(unsigned char *s, const unsigned char *rk) { + uint8_t i, t, u, v[4][4]; + for (i = 0; i < 16; ++i) { + v[((i / 4) + 4 - (i % 4) ) % 4][i % 4] = sbox[s[i]]; + } + for (i = 0; i < 4; ++i) { + t = v[i][0]; + u = v[i][0] ^ v[i][1] ^ v[i][2] ^ v[i][3]; + v[i][0] ^= (uint8_t)(u ^ XT(v[i][0] ^ v[i][1])); + v[i][1] ^= (uint8_t)(u ^ XT(v[i][1] ^ v[i][2])); + v[i][2] ^= (uint8_t)(u ^ XT(v[i][2] ^ v[i][3])); + v[i][3] ^= (uint8_t)(u ^ XT(v[i][3] ^ t)); + } + for (i = 0; i < 16; ++i) { + s[i] = v[i / 4][i % 4] ^ rk[i]; + } +} + +// Simulate _mm_unpacklo_epi32 +static void unpacklo32(unsigned char *t, unsigned char *a, unsigned char *b) { + unsigned char tmp[16]; + memcpy(tmp, a, 4); + memcpy(tmp + 4, b, 4); + memcpy(tmp + 8, a + 4, 4); + memcpy(tmp + 12, b + 4, 4); + memcpy(t, tmp, 16); +} + +// Simulate _mm_unpackhi_epi32 +static void unpackhi32(unsigned char *t, unsigned char *a, unsigned char *b) { + unsigned char tmp[16]; + memcpy(tmp, a + 8, 4); + memcpy(tmp + 4, b + 8, 4); + memcpy(tmp + 8, a + 12, 4); + memcpy(tmp + 12, b + 12, 4); + memcpy(t, tmp, 16); +} + +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]; + + /* Use the standard constants to generate tweaked ones. */ + memcpy(rc, haraka_rc, 40 * 16); + + /* Constants for sk.seed */ + if (sk_seed != NULL) { + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_haraka_S(buf, 40 * 16, sk_seed, seed_length); + memcpy(rc_sseed, buf, 40 * 16); + } + + /* Constants for pk.seed */ + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_haraka_S(buf, 40 * 16, pk_seed, seed_length); + memcpy(rc, buf, 40 * 16); +} + +static void haraka_S_absorb(unsigned char *s, unsigned int r, + const unsigned char *m, unsigned long long mlen, + unsigned char p) { + unsigned long long i; + unsigned char t[r]; + + while (mlen >= r) { + // XOR block to state + for (i = 0; i < r; ++i) { + s[i] ^= m[i]; + } + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_haraka512_perm(s, s); + mlen -= r; + m += r; + } + + for (i = 0; i < r; ++i) { + t[i] = 0; + } + for (i = 0; i < mlen; ++i) { + t[i] = m[i]; + } + t[i] = p; + t[r - 1] |= 128; + for (i = 0; i < r; ++i) { + s[i] ^= t[i]; + } +} + +static void haraka_S_squeezeblocks(unsigned char *h, unsigned long long nblocks, + unsigned char *s, unsigned int r) { + while (nblocks > 0) { + PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_haraka512_perm(s, s); + memcpy(h, s, HARAKAS_RATE); + h += r; + 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, 32, in, inlen, 0x1F); + + haraka_S_squeezeblocks(out, outlen / 32, s, 32); + out += (outlen / 32) * 32; + + if (outlen % 32) { + haraka_S_squeezeblocks(d, 1, s, 32); + for (i = 0; i < outlen % 32; i++) { + out[i] = d[i]; + } + } +} + +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_haraka512_perm(unsigned char *out, const unsigned char *in) { + int i, j; + + unsigned char s[64], tmp[16]; + + memcpy(s, in, 16); + memcpy(s + 16, in + 16, 16); + memcpy(s + 32, in + 32, 16); + memcpy(s + 48, in + 48, 16); + + for (i = 0; i < 5; ++i) { + // aes round(s) + for (j = 0; j < 2; ++j) { + aesenc(s, rc[4 * 2 * i + 4 * j]); + aesenc(s + 16, rc[4 * 2 * i + 4 * j + 1]); + aesenc(s + 32, rc[4 * 2 * i + 4 * j + 2]); + aesenc(s + 48, rc[4 * 2 * i + 4 * j + 3]); + } + + // mixing + unpacklo32(tmp, s, s + 16); + unpackhi32(s, s, s + 16); + unpacklo32(s + 16, s + 32, s + 48); + unpackhi32(s + 32, s + 32, s + 48); + unpacklo32(s + 48, s, s + 32); + unpackhi32(s, s, s + 32); + unpackhi32(s + 32, s + 16, tmp); + unpacklo32(s + 16, s + 16, tmp); + } + + memcpy(out, s, 64); +} + +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) { + int i, j; + + unsigned char s[32], tmp[16]; + + memcpy(s, in, 16); + memcpy(s + 16, in + 16, 16); + + for (i = 0; i < 5; ++i) { + // aes round(s) + for (j = 0; j < 2; ++j) { + aesenc(s, rc[2 * 2 * i + 2 * j]); + aesenc(s + 16, rc[2 * 2 * i + 2 * j + 1]); + } + + // mixing + unpacklo32(tmp, s, s + 16); + unpackhi32(s + 16, s, s + 16); + memcpy(s, tmp, 16); + } + + /* Feed-forward */ + for (i = 0; i < 32; i++) { + out[i] = in[i] ^ s[i]; + } +} + +void PQCLEAN_SPHINCSHARAKA256FSIMPLE_CLEAN_haraka256_sk(unsigned char *out, const unsigned char *in) { + int i, j; + + unsigned char s[32], tmp[16]; + + memcpy(s, in, 16); + memcpy(s + 16, in + 16, 16); + + for (i = 0; i < 5; ++i) { + // aes round(s) + for (j = 0; j < 2; ++j) { + aesenc(s, rc_sseed[2 * 2 * i + 2 * j]); + aesenc(s + 16, rc_sseed[2 * 2 * i + 2 * j + 1]); + } + + // mixing + unpacklo32(tmp, s, s + 16); + unpackhi32(s + 16, s, s + 16); + memcpy(s, tmp, 16); + } + + /* Feed-forward */ + for (i = 0; i < 32; i++) { + out[i] = in[i] ^ s[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..2c968bc3 --- /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-signature: 29792 +testvectors-sha256: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +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/492ec4f1f6d3b3dc4b435783bbaaf4e41cdb6f32 + length-secret-key: 128 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..ba0cb5f0 --- /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=-Wall -Wconversion -Wextra -Wpedantic -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..5fb33615 --- /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 64 +#define PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES 32 +#define PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_CRYPTO_BYTES 16976 +#define PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_CRYPTO_SEEDBYTES 48 + +/* + * 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..32c08d75 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256s-robust/clean/haraka.c @@ -0,0 +1,373 @@ +/* +Plain C implementation of the Haraka256 and Haraka512 permutations. +*/ +#include +#include +#include + +#include "haraka.h" + +#define HARAKAS_RATE 32 + +static const unsigned char haraka_rc[40][16] = { + {0x9d, 0x7b, 0x81, 0x75, 0xf0, 0xfe, 0xc5, 0xb2, 0x0a, 0xc0, 0x20, 0xe6, 0x4c, 0x70, 0x84, 0x06}, + {0x17, 0xf7, 0x08, 0x2f, 0xa4, 0x6b, 0x0f, 0x64, 0x6b, 0xa0, 0xf3, 0x88, 0xe1, 0xb4, 0x66, 0x8b}, + {0x14, 0x91, 0x02, 0x9f, 0x60, 0x9d, 0x02, 0xcf, 0x98, 0x84, 0xf2, 0x53, 0x2d, 0xde, 0x02, 0x34}, + {0x79, 0x4f, 0x5b, 0xfd, 0xaf, 0xbc, 0xf3, 0xbb, 0x08, 0x4f, 0x7b, 0x2e, 0xe6, 0xea, 0xd6, 0x0e}, + {0x44, 0x70, 0x39, 0xbe, 0x1c, 0xcd, 0xee, 0x79, 0x8b, 0x44, 0x72, 0x48, 0xcb, 0xb0, 0xcf, 0xcb}, + {0x7b, 0x05, 0x8a, 0x2b, 0xed, 0x35, 0x53, 0x8d, 0xb7, 0x32, 0x90, 0x6e, 0xee, 0xcd, 0xea, 0x7e}, + {0x1b, 0xef, 0x4f, 0xda, 0x61, 0x27, 0x41, 0xe2, 0xd0, 0x7c, 0x2e, 0x5e, 0x43, 0x8f, 0xc2, 0x67}, + {0x3b, 0x0b, 0xc7, 0x1f, 0xe2, 0xfd, 0x5f, 0x67, 0x07, 0xcc, 0xca, 0xaf, 0xb0, 0xd9, 0x24, 0x29}, + {0xee, 0x65, 0xd4, 0xb9, 0xca, 0x8f, 0xdb, 0xec, 0xe9, 0x7f, 0x86, 0xe6, 0xf1, 0x63, 0x4d, 0xab}, + {0x33, 0x7e, 0x03, 0xad, 0x4f, 0x40, 0x2a, 0x5b, 0x64, 0xcd, 0xb7, 0xd4, 0x84, 0xbf, 0x30, 0x1c}, + {0x00, 0x98, 0xf6, 0x8d, 0x2e, 0x8b, 0x02, 0x69, 0xbf, 0x23, 0x17, 0x94, 0xb9, 0x0b, 0xcc, 0xb2}, + {0x8a, 0x2d, 0x9d, 0x5c, 0xc8, 0x9e, 0xaa, 0x4a, 0x72, 0x55, 0x6f, 0xde, 0xa6, 0x78, 0x04, 0xfa}, + {0xd4, 0x9f, 0x12, 0x29, 0x2e, 0x4f, 0xfa, 0x0e, 0x12, 0x2a, 0x77, 0x6b, 0x2b, 0x9f, 0xb4, 0xdf}, + {0xee, 0x12, 0x6a, 0xbb, 0xae, 0x11, 0xd6, 0x32, 0x36, 0xa2, 0x49, 0xf4, 0x44, 0x03, 0xa1, 0x1e}, + {0xa6, 0xec, 0xa8, 0x9c, 0xc9, 0x00, 0x96, 0x5f, 0x84, 0x00, 0x05, 0x4b, 0x88, 0x49, 0x04, 0xaf}, + {0xec, 0x93, 0xe5, 0x27, 0xe3, 0xc7, 0xa2, 0x78, 0x4f, 0x9c, 0x19, 0x9d, 0xd8, 0x5e, 0x02, 0x21}, + {0x73, 0x01, 0xd4, 0x82, 0xcd, 0x2e, 0x28, 0xb9, 0xb7, 0xc9, 0x59, 0xa7, 0xf8, 0xaa, 0x3a, 0xbf}, + {0x6b, 0x7d, 0x30, 0x10, 0xd9, 0xef, 0xf2, 0x37, 0x17, 0xb0, 0x86, 0x61, 0x0d, 0x70, 0x60, 0x62}, + {0xc6, 0x9a, 0xfc, 0xf6, 0x53, 0x91, 0xc2, 0x81, 0x43, 0x04, 0x30, 0x21, 0xc2, 0x45, 0xca, 0x5a}, + {0x3a, 0x94, 0xd1, 0x36, 0xe8, 0x92, 0xaf, 0x2c, 0xbb, 0x68, 0x6b, 0x22, 0x3c, 0x97, 0x23, 0x92}, + {0xb4, 0x71, 0x10, 0xe5, 0x58, 0xb9, 0xba, 0x6c, 0xeb, 0x86, 0x58, 0x22, 0x38, 0x92, 0xbf, 0xd3}, + {0x8d, 0x12, 0xe1, 0x24, 0xdd, 0xfd, 0x3d, 0x93, 0x77, 0xc6, 0xf0, 0xae, 0xe5, 0x3c, 0x86, 0xdb}, + {0xb1, 0x12, 0x22, 0xcb, 0xe3, 0x8d, 0xe4, 0x83, 0x9c, 0xa0, 0xeb, 0xff, 0x68, 0x62, 0x60, 0xbb}, + {0x7d, 0xf7, 0x2b, 0xc7, 0x4e, 0x1a, 0xb9, 0x2d, 0x9c, 0xd1, 0xe4, 0xe2, 0xdc, 0xd3, 0x4b, 0x73}, + {0x4e, 0x92, 0xb3, 0x2c, 0xc4, 0x15, 0x14, 0x4b, 0x43, 0x1b, 0x30, 0x61, 0xc3, 0x47, 0xbb, 0x43}, + {0x99, 0x68, 0xeb, 0x16, 0xdd, 0x31, 0xb2, 0x03, 0xf6, 0xef, 0x07, 0xe7, 0xa8, 0x75, 0xa7, 0xdb}, + {0x2c, 0x47, 0xca, 0x7e, 0x02, 0x23, 0x5e, 0x8e, 0x77, 0x59, 0x75, 0x3c, 0x4b, 0x61, 0xf3, 0x6d}, + {0xf9, 0x17, 0x86, 0xb8, 0xb9, 0xe5, 0x1b, 0x6d, 0x77, 0x7d, 0xde, 0xd6, 0x17, 0x5a, 0xa7, 0xcd}, + {0x5d, 0xee, 0x46, 0xa9, 0x9d, 0x06, 0x6c, 0x9d, 0xaa, 0xe9, 0xa8, 0x6b, 0xf0, 0x43, 0x6b, 0xec}, + {0xc1, 0x27, 0xf3, 0x3b, 0x59, 0x11, 0x53, 0xa2, 0x2b, 0x33, 0x57, 0xf9, 0x50, 0x69, 0x1e, 0xcb}, + {0xd9, 0xd0, 0x0e, 0x60, 0x53, 0x03, 0xed, 0xe4, 0x9c, 0x61, 0xda, 0x00, 0x75, 0x0c, 0xee, 0x2c}, + {0x50, 0xa3, 0xa4, 0x63, 0xbc, 0xba, 0xbb, 0x80, 0xab, 0x0c, 0xe9, 0x96, 0xa1, 0xa5, 0xb1, 0xf0}, + {0x39, 0xca, 0x8d, 0x93, 0x30, 0xde, 0x0d, 0xab, 0x88, 0x29, 0x96, 0x5e, 0x02, 0xb1, 0x3d, 0xae}, + {0x42, 0xb4, 0x75, 0x2e, 0xa8, 0xf3, 0x14, 0x88, 0x0b, 0xa4, 0x54, 0xd5, 0x38, 0x8f, 0xbb, 0x17}, + {0xf6, 0x16, 0x0a, 0x36, 0x79, 0xb7, 0xb6, 0xae, 0xd7, 0x7f, 0x42, 0x5f, 0x5b, 0x8a, 0xbb, 0x34}, + {0xde, 0xaf, 0xba, 0xff, 0x18, 0x59, 0xce, 0x43, 0x38, 0x54, 0xe5, 0xcb, 0x41, 0x52, 0xf6, 0x26}, + {0x78, 0xc9, 0x9e, 0x83, 0xf7, 0x9c, 0xca, 0xa2, 0x6a, 0x02, 0xf3, 0xb9, 0x54, 0x9a, 0xe9, 0x4c}, + {0x35, 0x12, 0x90, 0x22, 0x28, 0x6e, 0xc0, 0x40, 0xbe, 0xf7, 0xdf, 0x1b, 0x1a, 0xa5, 0x51, 0xae}, + {0xcf, 0x59, 0xa6, 0x48, 0x0f, 0xbc, 0x73, 0xc1, 0x2b, 0xd2, 0x7e, 0xba, 0x3c, 0x61, 0xc1, 0xa0}, + {0xa1, 0x9d, 0xc5, 0xe9, 0xfd, 0xbd, 0xd6, 0x4a, 0x88, 0x82, 0x28, 0x02, 0x03, 0xcc, 0x6a, 0x75} +}; + +static unsigned char rc[40][16]; +static unsigned char rc_sseed[40][16]; + +static const unsigned char sbox[256] = { + 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, + 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, + 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, + 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, + 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, + 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, + 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, + 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, + 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, + 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, + 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, + 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, + 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, + 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, + 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, + 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, + 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, + 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, + 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, + 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 +}; + +#define XT(x) (((x) << 1) ^ ((((x) >> 7) & 1) * 0x1b)) + +// Simulate _mm_aesenc_si128 instructions from AESNI +static void aesenc(unsigned char *s, const unsigned char *rk) { + uint8_t i, t, u, v[4][4]; + for (i = 0; i < 16; ++i) { + v[((i / 4) + 4 - (i % 4) ) % 4][i % 4] = sbox[s[i]]; + } + for (i = 0; i < 4; ++i) { + t = v[i][0]; + u = v[i][0] ^ v[i][1] ^ v[i][2] ^ v[i][3]; + v[i][0] ^= (uint8_t)(u ^ XT(v[i][0] ^ v[i][1])); + v[i][1] ^= (uint8_t)(u ^ XT(v[i][1] ^ v[i][2])); + v[i][2] ^= (uint8_t)(u ^ XT(v[i][2] ^ v[i][3])); + v[i][3] ^= (uint8_t)(u ^ XT(v[i][3] ^ t)); + } + for (i = 0; i < 16; ++i) { + s[i] = v[i / 4][i % 4] ^ rk[i]; + } +} + +// Simulate _mm_unpacklo_epi32 +static void unpacklo32(unsigned char *t, unsigned char *a, unsigned char *b) { + unsigned char tmp[16]; + memcpy(tmp, a, 4); + memcpy(tmp + 4, b, 4); + memcpy(tmp + 8, a + 4, 4); + memcpy(tmp + 12, b + 4, 4); + memcpy(t, tmp, 16); +} + +// Simulate _mm_unpackhi_epi32 +static void unpackhi32(unsigned char *t, unsigned char *a, unsigned char *b) { + unsigned char tmp[16]; + memcpy(tmp, a + 8, 4); + memcpy(tmp + 4, b + 8, 4); + memcpy(tmp + 8, a + 12, 4); + memcpy(tmp + 12, b + 12, 4); + memcpy(t, tmp, 16); +} + +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]; + + /* Use the standard constants to generate tweaked ones. */ + memcpy(rc, haraka_rc, 40 * 16); + + /* Constants for sk.seed */ + if (sk_seed != NULL) { + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_haraka_S(buf, 40 * 16, sk_seed, seed_length); + memcpy(rc_sseed, buf, 40 * 16); + } + + /* Constants for pk.seed */ + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_haraka_S(buf, 40 * 16, pk_seed, seed_length); + memcpy(rc, buf, 40 * 16); +} + +static void haraka_S_absorb(unsigned char *s, unsigned int r, + const unsigned char *m, unsigned long long mlen, + unsigned char p) { + unsigned long long i; + unsigned char t[r]; + + while (mlen >= r) { + // XOR block to state + for (i = 0; i < r; ++i) { + s[i] ^= m[i]; + } + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_haraka512_perm(s, s); + mlen -= r; + m += r; + } + + for (i = 0; i < r; ++i) { + t[i] = 0; + } + for (i = 0; i < mlen; ++i) { + t[i] = m[i]; + } + t[i] = p; + t[r - 1] |= 128; + for (i = 0; i < r; ++i) { + s[i] ^= t[i]; + } +} + +static void haraka_S_squeezeblocks(unsigned char *h, unsigned long long nblocks, + unsigned char *s, unsigned int r) { + while (nblocks > 0) { + PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_haraka512_perm(s, s); + memcpy(h, s, HARAKAS_RATE); + h += r; + 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, 32, in, inlen, 0x1F); + + haraka_S_squeezeblocks(out, outlen / 32, s, 32); + out += (outlen / 32) * 32; + + if (outlen % 32) { + haraka_S_squeezeblocks(d, 1, s, 32); + for (i = 0; i < outlen % 32; i++) { + out[i] = d[i]; + } + } +} + +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_haraka512_perm(unsigned char *out, const unsigned char *in) { + int i, j; + + unsigned char s[64], tmp[16]; + + memcpy(s, in, 16); + memcpy(s + 16, in + 16, 16); + memcpy(s + 32, in + 32, 16); + memcpy(s + 48, in + 48, 16); + + for (i = 0; i < 5; ++i) { + // aes round(s) + for (j = 0; j < 2; ++j) { + aesenc(s, rc[4 * 2 * i + 4 * j]); + aesenc(s + 16, rc[4 * 2 * i + 4 * j + 1]); + aesenc(s + 32, rc[4 * 2 * i + 4 * j + 2]); + aesenc(s + 48, rc[4 * 2 * i + 4 * j + 3]); + } + + // mixing + unpacklo32(tmp, s, s + 16); + unpackhi32(s, s, s + 16); + unpacklo32(s + 16, s + 32, s + 48); + unpackhi32(s + 32, s + 32, s + 48); + unpacklo32(s + 48, s, s + 32); + unpackhi32(s, s, s + 32); + unpackhi32(s + 32, s + 16, tmp); + unpacklo32(s + 16, s + 16, tmp); + } + + memcpy(out, s, 64); +} + +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) { + int i, j; + + unsigned char s[32], tmp[16]; + + memcpy(s, in, 16); + memcpy(s + 16, in + 16, 16); + + for (i = 0; i < 5; ++i) { + // aes round(s) + for (j = 0; j < 2; ++j) { + aesenc(s, rc[2 * 2 * i + 2 * j]); + aesenc(s + 16, rc[2 * 2 * i + 2 * j + 1]); + } + + // mixing + unpacklo32(tmp, s, s + 16); + unpackhi32(s + 16, s, s + 16); + memcpy(s, tmp, 16); + } + + /* Feed-forward */ + for (i = 0; i < 32; i++) { + out[i] = in[i] ^ s[i]; + } +} + +void PQCLEAN_SPHINCSHARAKA256SROBUST_CLEAN_haraka256_sk(unsigned char *out, const unsigned char *in) { + int i, j; + + unsigned char s[32], tmp[16]; + + memcpy(s, in, 16); + memcpy(s + 16, in + 16, 16); + + for (i = 0; i < 5; ++i) { + // aes round(s) + for (j = 0; j < 2; ++j) { + aesenc(s, rc_sseed[2 * 2 * i + 2 * j]); + aesenc(s + 16, rc_sseed[2 * 2 * i + 2 * j + 1]); + } + + // mixing + unpacklo32(tmp, s, s + 16); + unpackhi32(s + 16, s, s + 16); + memcpy(s, tmp, 16); + } + + /* Feed-forward */ + for (i = 0; i < 32; i++) { + out[i] = in[i] ^ s[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..2c968bc3 --- /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-signature: 29792 +testvectors-sha256: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +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/492ec4f1f6d3b3dc4b435783bbaaf4e41cdb6f32 + length-secret-key: 128 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..e0a67389 --- /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=-Wall -Wconversion -Wextra -Wpedantic -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..0213bd06 --- /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 64 +#define PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES 32 +#define PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_CRYPTO_BYTES 16976 +#define PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_CRYPTO_SEEDBYTES 48 + +/* + * 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..0ed3d012 --- /dev/null +++ b/crypto_sign/sphincs-haraka-256s-simple/clean/haraka.c @@ -0,0 +1,373 @@ +/* +Plain C implementation of the Haraka256 and Haraka512 permutations. +*/ +#include +#include +#include + +#include "haraka.h" + +#define HARAKAS_RATE 32 + +static const unsigned char haraka_rc[40][16] = { + {0x9d, 0x7b, 0x81, 0x75, 0xf0, 0xfe, 0xc5, 0xb2, 0x0a, 0xc0, 0x20, 0xe6, 0x4c, 0x70, 0x84, 0x06}, + {0x17, 0xf7, 0x08, 0x2f, 0xa4, 0x6b, 0x0f, 0x64, 0x6b, 0xa0, 0xf3, 0x88, 0xe1, 0xb4, 0x66, 0x8b}, + {0x14, 0x91, 0x02, 0x9f, 0x60, 0x9d, 0x02, 0xcf, 0x98, 0x84, 0xf2, 0x53, 0x2d, 0xde, 0x02, 0x34}, + {0x79, 0x4f, 0x5b, 0xfd, 0xaf, 0xbc, 0xf3, 0xbb, 0x08, 0x4f, 0x7b, 0x2e, 0xe6, 0xea, 0xd6, 0x0e}, + {0x44, 0x70, 0x39, 0xbe, 0x1c, 0xcd, 0xee, 0x79, 0x8b, 0x44, 0x72, 0x48, 0xcb, 0xb0, 0xcf, 0xcb}, + {0x7b, 0x05, 0x8a, 0x2b, 0xed, 0x35, 0x53, 0x8d, 0xb7, 0x32, 0x90, 0x6e, 0xee, 0xcd, 0xea, 0x7e}, + {0x1b, 0xef, 0x4f, 0xda, 0x61, 0x27, 0x41, 0xe2, 0xd0, 0x7c, 0x2e, 0x5e, 0x43, 0x8f, 0xc2, 0x67}, + {0x3b, 0x0b, 0xc7, 0x1f, 0xe2, 0xfd, 0x5f, 0x67, 0x07, 0xcc, 0xca, 0xaf, 0xb0, 0xd9, 0x24, 0x29}, + {0xee, 0x65, 0xd4, 0xb9, 0xca, 0x8f, 0xdb, 0xec, 0xe9, 0x7f, 0x86, 0xe6, 0xf1, 0x63, 0x4d, 0xab}, + {0x33, 0x7e, 0x03, 0xad, 0x4f, 0x40, 0x2a, 0x5b, 0x64, 0xcd, 0xb7, 0xd4, 0x84, 0xbf, 0x30, 0x1c}, + {0x00, 0x98, 0xf6, 0x8d, 0x2e, 0x8b, 0x02, 0x69, 0xbf, 0x23, 0x17, 0x94, 0xb9, 0x0b, 0xcc, 0xb2}, + {0x8a, 0x2d, 0x9d, 0x5c, 0xc8, 0x9e, 0xaa, 0x4a, 0x72, 0x55, 0x6f, 0xde, 0xa6, 0x78, 0x04, 0xfa}, + {0xd4, 0x9f, 0x12, 0x29, 0x2e, 0x4f, 0xfa, 0x0e, 0x12, 0x2a, 0x77, 0x6b, 0x2b, 0x9f, 0xb4, 0xdf}, + {0xee, 0x12, 0x6a, 0xbb, 0xae, 0x11, 0xd6, 0x32, 0x36, 0xa2, 0x49, 0xf4, 0x44, 0x03, 0xa1, 0x1e}, + {0xa6, 0xec, 0xa8, 0x9c, 0xc9, 0x00, 0x96, 0x5f, 0x84, 0x00, 0x05, 0x4b, 0x88, 0x49, 0x04, 0xaf}, + {0xec, 0x93, 0xe5, 0x27, 0xe3, 0xc7, 0xa2, 0x78, 0x4f, 0x9c, 0x19, 0x9d, 0xd8, 0x5e, 0x02, 0x21}, + {0x73, 0x01, 0xd4, 0x82, 0xcd, 0x2e, 0x28, 0xb9, 0xb7, 0xc9, 0x59, 0xa7, 0xf8, 0xaa, 0x3a, 0xbf}, + {0x6b, 0x7d, 0x30, 0x10, 0xd9, 0xef, 0xf2, 0x37, 0x17, 0xb0, 0x86, 0x61, 0x0d, 0x70, 0x60, 0x62}, + {0xc6, 0x9a, 0xfc, 0xf6, 0x53, 0x91, 0xc2, 0x81, 0x43, 0x04, 0x30, 0x21, 0xc2, 0x45, 0xca, 0x5a}, + {0x3a, 0x94, 0xd1, 0x36, 0xe8, 0x92, 0xaf, 0x2c, 0xbb, 0x68, 0x6b, 0x22, 0x3c, 0x97, 0x23, 0x92}, + {0xb4, 0x71, 0x10, 0xe5, 0x58, 0xb9, 0xba, 0x6c, 0xeb, 0x86, 0x58, 0x22, 0x38, 0x92, 0xbf, 0xd3}, + {0x8d, 0x12, 0xe1, 0x24, 0xdd, 0xfd, 0x3d, 0x93, 0x77, 0xc6, 0xf0, 0xae, 0xe5, 0x3c, 0x86, 0xdb}, + {0xb1, 0x12, 0x22, 0xcb, 0xe3, 0x8d, 0xe4, 0x83, 0x9c, 0xa0, 0xeb, 0xff, 0x68, 0x62, 0x60, 0xbb}, + {0x7d, 0xf7, 0x2b, 0xc7, 0x4e, 0x1a, 0xb9, 0x2d, 0x9c, 0xd1, 0xe4, 0xe2, 0xdc, 0xd3, 0x4b, 0x73}, + {0x4e, 0x92, 0xb3, 0x2c, 0xc4, 0x15, 0x14, 0x4b, 0x43, 0x1b, 0x30, 0x61, 0xc3, 0x47, 0xbb, 0x43}, + {0x99, 0x68, 0xeb, 0x16, 0xdd, 0x31, 0xb2, 0x03, 0xf6, 0xef, 0x07, 0xe7, 0xa8, 0x75, 0xa7, 0xdb}, + {0x2c, 0x47, 0xca, 0x7e, 0x02, 0x23, 0x5e, 0x8e, 0x77, 0x59, 0x75, 0x3c, 0x4b, 0x61, 0xf3, 0x6d}, + {0xf9, 0x17, 0x86, 0xb8, 0xb9, 0xe5, 0x1b, 0x6d, 0x77, 0x7d, 0xde, 0xd6, 0x17, 0x5a, 0xa7, 0xcd}, + {0x5d, 0xee, 0x46, 0xa9, 0x9d, 0x06, 0x6c, 0x9d, 0xaa, 0xe9, 0xa8, 0x6b, 0xf0, 0x43, 0x6b, 0xec}, + {0xc1, 0x27, 0xf3, 0x3b, 0x59, 0x11, 0x53, 0xa2, 0x2b, 0x33, 0x57, 0xf9, 0x50, 0x69, 0x1e, 0xcb}, + {0xd9, 0xd0, 0x0e, 0x60, 0x53, 0x03, 0xed, 0xe4, 0x9c, 0x61, 0xda, 0x00, 0x75, 0x0c, 0xee, 0x2c}, + {0x50, 0xa3, 0xa4, 0x63, 0xbc, 0xba, 0xbb, 0x80, 0xab, 0x0c, 0xe9, 0x96, 0xa1, 0xa5, 0xb1, 0xf0}, + {0x39, 0xca, 0x8d, 0x93, 0x30, 0xde, 0x0d, 0xab, 0x88, 0x29, 0x96, 0x5e, 0x02, 0xb1, 0x3d, 0xae}, + {0x42, 0xb4, 0x75, 0x2e, 0xa8, 0xf3, 0x14, 0x88, 0x0b, 0xa4, 0x54, 0xd5, 0x38, 0x8f, 0xbb, 0x17}, + {0xf6, 0x16, 0x0a, 0x36, 0x79, 0xb7, 0xb6, 0xae, 0xd7, 0x7f, 0x42, 0x5f, 0x5b, 0x8a, 0xbb, 0x34}, + {0xde, 0xaf, 0xba, 0xff, 0x18, 0x59, 0xce, 0x43, 0x38, 0x54, 0xe5, 0xcb, 0x41, 0x52, 0xf6, 0x26}, + {0x78, 0xc9, 0x9e, 0x83, 0xf7, 0x9c, 0xca, 0xa2, 0x6a, 0x02, 0xf3, 0xb9, 0x54, 0x9a, 0xe9, 0x4c}, + {0x35, 0x12, 0x90, 0x22, 0x28, 0x6e, 0xc0, 0x40, 0xbe, 0xf7, 0xdf, 0x1b, 0x1a, 0xa5, 0x51, 0xae}, + {0xcf, 0x59, 0xa6, 0x48, 0x0f, 0xbc, 0x73, 0xc1, 0x2b, 0xd2, 0x7e, 0xba, 0x3c, 0x61, 0xc1, 0xa0}, + {0xa1, 0x9d, 0xc5, 0xe9, 0xfd, 0xbd, 0xd6, 0x4a, 0x88, 0x82, 0x28, 0x02, 0x03, 0xcc, 0x6a, 0x75} +}; + +static unsigned char rc[40][16]; +static unsigned char rc_sseed[40][16]; + +static const unsigned char sbox[256] = { + 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, + 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, + 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, + 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, + 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, + 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, + 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, + 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, + 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, + 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, + 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, + 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, + 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, + 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, + 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, + 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, + 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, + 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, + 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, + 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 +}; + +#define XT(x) (((x) << 1) ^ ((((x) >> 7) & 1) * 0x1b)) + +// Simulate _mm_aesenc_si128 instructions from AESNI +static void aesenc(unsigned char *s, const unsigned char *rk) { + uint8_t i, t, u, v[4][4]; + for (i = 0; i < 16; ++i) { + v[((i / 4) + 4 - (i % 4) ) % 4][i % 4] = sbox[s[i]]; + } + for (i = 0; i < 4; ++i) { + t = v[i][0]; + u = v[i][0] ^ v[i][1] ^ v[i][2] ^ v[i][3]; + v[i][0] ^= (uint8_t)(u ^ XT(v[i][0] ^ v[i][1])); + v[i][1] ^= (uint8_t)(u ^ XT(v[i][1] ^ v[i][2])); + v[i][2] ^= (uint8_t)(u ^ XT(v[i][2] ^ v[i][3])); + v[i][3] ^= (uint8_t)(u ^ XT(v[i][3] ^ t)); + } + for (i = 0; i < 16; ++i) { + s[i] = v[i / 4][i % 4] ^ rk[i]; + } +} + +// Simulate _mm_unpacklo_epi32 +static void unpacklo32(unsigned char *t, unsigned char *a, unsigned char *b) { + unsigned char tmp[16]; + memcpy(tmp, a, 4); + memcpy(tmp + 4, b, 4); + memcpy(tmp + 8, a + 4, 4); + memcpy(tmp + 12, b + 4, 4); + memcpy(t, tmp, 16); +} + +// Simulate _mm_unpackhi_epi32 +static void unpackhi32(unsigned char *t, unsigned char *a, unsigned char *b) { + unsigned char tmp[16]; + memcpy(tmp, a + 8, 4); + memcpy(tmp + 4, b + 8, 4); + memcpy(tmp + 8, a + 12, 4); + memcpy(tmp + 12, b + 12, 4); + memcpy(t, tmp, 16); +} + +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]; + + /* Use the standard constants to generate tweaked ones. */ + memcpy(rc, haraka_rc, 40 * 16); + + /* Constants for sk.seed */ + if (sk_seed != NULL) { + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_haraka_S(buf, 40 * 16, sk_seed, seed_length); + memcpy(rc_sseed, buf, 40 * 16); + } + + /* Constants for pk.seed */ + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_haraka_S(buf, 40 * 16, pk_seed, seed_length); + memcpy(rc, buf, 40 * 16); +} + +static void haraka_S_absorb(unsigned char *s, unsigned int r, + const unsigned char *m, unsigned long long mlen, + unsigned char p) { + unsigned long long i; + unsigned char t[r]; + + while (mlen >= r) { + // XOR block to state + for (i = 0; i < r; ++i) { + s[i] ^= m[i]; + } + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_haraka512_perm(s, s); + mlen -= r; + m += r; + } + + for (i = 0; i < r; ++i) { + t[i] = 0; + } + for (i = 0; i < mlen; ++i) { + t[i] = m[i]; + } + t[i] = p; + t[r - 1] |= 128; + for (i = 0; i < r; ++i) { + s[i] ^= t[i]; + } +} + +static void haraka_S_squeezeblocks(unsigned char *h, unsigned long long nblocks, + unsigned char *s, unsigned int r) { + while (nblocks > 0) { + PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_haraka512_perm(s, s); + memcpy(h, s, HARAKAS_RATE); + h += r; + 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, 32, in, inlen, 0x1F); + + haraka_S_squeezeblocks(out, outlen / 32, s, 32); + out += (outlen / 32) * 32; + + if (outlen % 32) { + haraka_S_squeezeblocks(d, 1, s, 32); + for (i = 0; i < outlen % 32; i++) { + out[i] = d[i]; + } + } +} + +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_haraka512_perm(unsigned char *out, const unsigned char *in) { + int i, j; + + unsigned char s[64], tmp[16]; + + memcpy(s, in, 16); + memcpy(s + 16, in + 16, 16); + memcpy(s + 32, in + 32, 16); + memcpy(s + 48, in + 48, 16); + + for (i = 0; i < 5; ++i) { + // aes round(s) + for (j = 0; j < 2; ++j) { + aesenc(s, rc[4 * 2 * i + 4 * j]); + aesenc(s + 16, rc[4 * 2 * i + 4 * j + 1]); + aesenc(s + 32, rc[4 * 2 * i + 4 * j + 2]); + aesenc(s + 48, rc[4 * 2 * i + 4 * j + 3]); + } + + // mixing + unpacklo32(tmp, s, s + 16); + unpackhi32(s, s, s + 16); + unpacklo32(s + 16, s + 32, s + 48); + unpackhi32(s + 32, s + 32, s + 48); + unpacklo32(s + 48, s, s + 32); + unpackhi32(s, s, s + 32); + unpackhi32(s + 32, s + 16, tmp); + unpacklo32(s + 16, s + 16, tmp); + } + + memcpy(out, s, 64); +} + +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) { + int i, j; + + unsigned char s[32], tmp[16]; + + memcpy(s, in, 16); + memcpy(s + 16, in + 16, 16); + + for (i = 0; i < 5; ++i) { + // aes round(s) + for (j = 0; j < 2; ++j) { + aesenc(s, rc[2 * 2 * i + 2 * j]); + aesenc(s + 16, rc[2 * 2 * i + 2 * j + 1]); + } + + // mixing + unpacklo32(tmp, s, s + 16); + unpackhi32(s + 16, s, s + 16); + memcpy(s, tmp, 16); + } + + /* Feed-forward */ + for (i = 0; i < 32; i++) { + out[i] = in[i] ^ s[i]; + } +} + +void PQCLEAN_SPHINCSHARAKA256SSIMPLE_CLEAN_haraka256_sk(unsigned char *out, const unsigned char *in) { + int i, j; + + unsigned char s[32], tmp[16]; + + memcpy(s, in, 16); + memcpy(s + 16, in + 16, 16); + + for (i = 0; i < 5; ++i) { + // aes round(s) + for (j = 0; j < 2; ++j) { + aesenc(s, rc_sseed[2 * 2 * i + 2 * j]); + aesenc(s + 16, rc_sseed[2 * 2 * i + 2 * j + 1]); + } + + // mixing + unpacklo32(tmp, s, s + 16); + unpackhi32(s + 16, s, s + 16); + memcpy(s, tmp, 16); + } + + /* Feed-forward */ + for (i = 0; i < 32; i++) { + out[i] = in[i] ^ s[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..164e0592 --- /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-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/492ec4f1f6d3b3dc4b435783bbaaf4e41cdb6f32 + length-secret-key: 64 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..20685ac9 --- /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=-Wall -Wconversion -Wextra -Wpedantic -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..10c2f5d3 --- /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]; + + /* 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..7a734e6e --- /dev/null +++ b/crypto_sign/sphincs-sha256-128f-robust/clean/sha256.c @@ -0,0 +1,75 @@ +/* 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]); +} + +/** + * Note that inlen should be sufficiently small that it still allows for + * an array to be allocated on the stack. Typically 'in' is merely a seed. + * Outputs outlen number of bytes + */ +void PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + const unsigned char *in, unsigned long inlen) { + unsigned char inbuf[inlen + 4]; + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + unsigned long i; + + memcpy(inbuf, in, inlen); + + /* 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(inbuf + inlen, 4, i); + sha256(out, inbuf, 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(inbuf + inlen, 4, i); + sha256(outbuf, inbuf, 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..cb578dfc --- /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, + const unsigned char *in, 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..7bd25f4a --- /dev/null +++ b/crypto_sign/sphincs-sha256-128f-robust/clean/thash_sha256_robust.c @@ -0,0 +1,76 @@ +#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; + uint8_t sha2_state[40]; + unsigned int i; + + memcpy(buf, pub_seed, SPX_N); + PQCLEAN_SPHINCSSHA256128FROBUST_CLEAN_compress_address(buf + SPX_N, addr); + 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 + 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 + 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 + 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 + 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..2361cc77 --- /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-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/492ec4f1f6d3b3dc4b435783bbaaf4e41cdb6f32 + length-secret-key: 64 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..338c3f55 --- /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=-Wall -Wconversion -Wextra -Wpedantic -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..27591f62 --- /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]; + + /* 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..0a04e7db --- /dev/null +++ b/crypto_sign/sphincs-sha256-128f-simple/clean/sha256.c @@ -0,0 +1,75 @@ +/* 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]); +} + +/** + * Note that inlen should be sufficiently small that it still allows for + * an array to be allocated on the stack. Typically 'in' is merely a seed. + * Outputs outlen number of bytes + */ +void PQCLEAN_SPHINCSSHA256128FSIMPLE_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + const unsigned char *in, unsigned long inlen) { + unsigned char inbuf[inlen + 4]; + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + unsigned long i; + + memcpy(inbuf, in, inlen); + + /* 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(inbuf + inlen, 4, i); + sha256(out, inbuf, 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(inbuf + inlen, 4, i); + sha256(outbuf, inbuf, 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..718e37ba --- /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, + const unsigned char *in, 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..25b3a526 --- /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-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/492ec4f1f6d3b3dc4b435783bbaaf4e41cdb6f32 + length-secret-key: 64 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..88775b78 --- /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=-Wall -Wconversion -Wextra -Wpedantic -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..66d524d3 --- /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 16976 +#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..49c179b4 --- /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]; + + /* 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..d3c262a5 --- /dev/null +++ b/crypto_sign/sphincs-sha256-128s-robust/clean/sha256.c @@ -0,0 +1,75 @@ +/* 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]); +} + +/** + * Note that inlen should be sufficiently small that it still allows for + * an array to be allocated on the stack. Typically 'in' is merely a seed. + * Outputs outlen number of bytes + */ +void PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + const unsigned char *in, unsigned long inlen) { + unsigned char inbuf[inlen + 4]; + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + unsigned long i; + + memcpy(inbuf, in, inlen); + + /* 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(inbuf + inlen, 4, i); + sha256(out, inbuf, 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(inbuf + inlen, 4, i); + sha256(outbuf, inbuf, 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..4b47bede --- /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, + const unsigned char *in, 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..e421eaec --- /dev/null +++ b/crypto_sign/sphincs-sha256-128s-robust/clean/thash_sha256_robust.c @@ -0,0 +1,76 @@ +#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; + uint8_t sha2_state[40]; + unsigned int i; + + memcpy(buf, pub_seed, SPX_N); + PQCLEAN_SPHINCSSHA256128SROBUST_CLEAN_compress_address(buf + SPX_N, addr); + 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 + 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 + 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 + 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 + 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..2d695eb4 --- /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-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/492ec4f1f6d3b3dc4b435783bbaaf4e41cdb6f32 + length-secret-key: 64 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..990988b6 --- /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=-Wall -Wconversion -Wextra -Wpedantic -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..c35104f6 --- /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 16976 +#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..e42c7496 --- /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]; + + /* 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..3b71a5ba --- /dev/null +++ b/crypto_sign/sphincs-sha256-128s-simple/clean/sha256.c @@ -0,0 +1,75 @@ +/* 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]); +} + +/** + * Note that inlen should be sufficiently small that it still allows for + * an array to be allocated on the stack. Typically 'in' is merely a seed. + * Outputs outlen number of bytes + */ +void PQCLEAN_SPHINCSSHA256128SSIMPLE_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + const unsigned char *in, unsigned long inlen) { + unsigned char inbuf[inlen + 4]; + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + unsigned long i; + + memcpy(inbuf, in, inlen); + + /* 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(inbuf + inlen, 4, i); + sha256(out, inbuf, 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(inbuf + inlen, 4, i); + sha256(outbuf, inbuf, 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..994d51f9 --- /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, + const unsigned char *in, 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..2e4b98ff --- /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-signature: 35664 +testvectors-sha256: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +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/492ec4f1f6d3b3dc4b435783bbaaf4e41cdb6f32 + length-secret-key: 96 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..9f3af076 --- /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=-Wall -Wconversion -Wextra -Wpedantic -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..c6fc3fc5 --- /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 64 +#define PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES 32 +#define PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_CRYPTO_BYTES 16976 +#define PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_CRYPTO_SEEDBYTES 48 + +/* + * 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..83b1e3bc --- /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]; + + /* 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..25cf1205 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192f-robust/clean/sha256.c @@ -0,0 +1,75 @@ +/* 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]); +} + +/** + * Note that inlen should be sufficiently small that it still allows for + * an array to be allocated on the stack. Typically 'in' is merely a seed. + * Outputs outlen number of bytes + */ +void PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + const unsigned char *in, unsigned long inlen) { + unsigned char inbuf[inlen + 4]; + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + unsigned long i; + + memcpy(inbuf, in, inlen); + + /* 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(inbuf + inlen, 4, i); + sha256(out, inbuf, 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(inbuf + inlen, 4, i); + sha256(outbuf, inbuf, 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..73f86d33 --- /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, + const unsigned char *in, 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..450a42a0 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192f-robust/clean/thash_sha256_robust.c @@ -0,0 +1,76 @@ +#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; + uint8_t sha2_state[40]; + unsigned int i; + + memcpy(buf, pub_seed, SPX_N); + PQCLEAN_SPHINCSSHA256192FROBUST_CLEAN_compress_address(buf + SPX_N, addr); + 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 + 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 + 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 + 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 + 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..2e4b98ff --- /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-signature: 35664 +testvectors-sha256: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +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/492ec4f1f6d3b3dc4b435783bbaaf4e41cdb6f32 + length-secret-key: 96 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..d15dfb6c --- /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=-Wall -Wconversion -Wextra -Wpedantic -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..da2d1e6b --- /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 64 +#define PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES 32 +#define PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_CRYPTO_BYTES 16976 +#define PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_CRYPTO_SEEDBYTES 48 + +/* + * 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..caf52843 --- /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]; + + /* 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..1dd34d56 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192f-simple/clean/sha256.c @@ -0,0 +1,75 @@ +/* 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]); +} + +/** + * Note that inlen should be sufficiently small that it still allows for + * an array to be allocated on the stack. Typically 'in' is merely a seed. + * Outputs outlen number of bytes + */ +void PQCLEAN_SPHINCSSHA256192FSIMPLE_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + const unsigned char *in, unsigned long inlen) { + unsigned char inbuf[inlen + 4]; + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + unsigned long i; + + memcpy(inbuf, in, inlen); + + /* 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(inbuf + inlen, 4, i); + sha256(out, inbuf, 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(inbuf + inlen, 4, i); + sha256(outbuf, inbuf, 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..6041fb99 --- /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, + const unsigned char *in, 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..c7f0ef3a --- /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-signature: 17064 +testvectors-sha256: 2857d1498ab0242746518973fd8a2367a69edf0bfc09caebfc8ad9fb2278471c +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/492ec4f1f6d3b3dc4b435783bbaaf4e41cdb6f32 + length-secret-key: 96 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..c8fd7215 --- /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=-Wall -Wconversion -Wextra -Wpedantic -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..1f5b3999 --- /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 64 +#define PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES 32 +#define PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_CRYPTO_BYTES 16976 +#define PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_CRYPTO_SEEDBYTES 48 + +/* + * 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..4cf815c5 --- /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]; + + /* 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..9b8e7f1c --- /dev/null +++ b/crypto_sign/sphincs-sha256-192s-robust/clean/sha256.c @@ -0,0 +1,75 @@ +/* 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]); +} + +/** + * Note that inlen should be sufficiently small that it still allows for + * an array to be allocated on the stack. Typically 'in' is merely a seed. + * Outputs outlen number of bytes + */ +void PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + const unsigned char *in, unsigned long inlen) { + unsigned char inbuf[inlen + 4]; + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + unsigned long i; + + memcpy(inbuf, in, inlen); + + /* 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(inbuf + inlen, 4, i); + sha256(out, inbuf, 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(inbuf + inlen, 4, i); + sha256(outbuf, inbuf, 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..875a524e --- /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, + const unsigned char *in, 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..9591cec9 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192s-robust/clean/thash_sha256_robust.c @@ -0,0 +1,76 @@ +#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; + uint8_t sha2_state[40]; + unsigned int i; + + memcpy(buf, pub_seed, SPX_N); + PQCLEAN_SPHINCSSHA256192SROBUST_CLEAN_compress_address(buf + SPX_N, addr); + 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 + 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 + 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 + 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 + 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..241da1be --- /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-signature: 17064 +testvectors-sha256: f8aa367561c521bc2b1590f6d3d676c7ba2d1efff3c81c9e6ee0a7ab7cebd9d1 +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/492ec4f1f6d3b3dc4b435783bbaaf4e41cdb6f32 + length-secret-key: 96 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..b4964631 --- /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=-Wall -Wconversion -Wextra -Wpedantic -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..b1337658 --- /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 64 +#define PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES 32 +#define PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_CRYPTO_BYTES 16976 +#define PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_CRYPTO_SEEDBYTES 48 + +/* + * 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..3bfae4cc --- /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]; + + /* 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..3734daa4 --- /dev/null +++ b/crypto_sign/sphincs-sha256-192s-simple/clean/sha256.c @@ -0,0 +1,75 @@ +/* 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]); +} + +/** + * Note that inlen should be sufficiently small that it still allows for + * an array to be allocated on the stack. Typically 'in' is merely a seed. + * Outputs outlen number of bytes + */ +void PQCLEAN_SPHINCSSHA256192SSIMPLE_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + const unsigned char *in, unsigned long inlen) { + unsigned char inbuf[inlen + 4]; + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + unsigned long i; + + memcpy(inbuf, in, inlen); + + /* 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(inbuf + inlen, 4, i); + sha256(out, inbuf, 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(inbuf + inlen, 4, i); + sha256(outbuf, inbuf, 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..a4cedda0 --- /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, + const unsigned char *in, 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..af9dcc0c --- /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-signature: 49216 +testvectors-sha256: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +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/492ec4f1f6d3b3dc4b435783bbaaf4e41cdb6f32 + length-secret-key: 128 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..f05ea23c --- /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=-Wall -Wconversion -Wextra -Wpedantic -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..6d1f2c6c --- /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 64 +#define PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES 32 +#define PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_CRYPTO_BYTES 16976 +#define PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_CRYPTO_SEEDBYTES 48 + +/* + * 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..06be9ae2 --- /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]; + + /* 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..ba42ca5c --- /dev/null +++ b/crypto_sign/sphincs-sha256-256f-robust/clean/sha256.c @@ -0,0 +1,75 @@ +/* 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]); +} + +/** + * Note that inlen should be sufficiently small that it still allows for + * an array to be allocated on the stack. Typically 'in' is merely a seed. + * Outputs outlen number of bytes + */ +void PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + const unsigned char *in, unsigned long inlen) { + unsigned char inbuf[inlen + 4]; + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + unsigned long i; + + memcpy(inbuf, in, inlen); + + /* 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(inbuf + inlen, 4, i); + sha256(out, inbuf, 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(inbuf + inlen, 4, i); + sha256(outbuf, inbuf, 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..a6ab9d20 --- /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, + const unsigned char *in, 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..8399f0cf --- /dev/null +++ b/crypto_sign/sphincs-sha256-256f-robust/clean/thash_sha256_robust.c @@ -0,0 +1,76 @@ +#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; + uint8_t sha2_state[40]; + unsigned int i; + + memcpy(buf, pub_seed, SPX_N); + PQCLEAN_SPHINCSSHA256256FROBUST_CLEAN_compress_address(buf + SPX_N, addr); + 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 + 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 + 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 + 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 + 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..af9dcc0c --- /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-signature: 49216 +testvectors-sha256: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +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/492ec4f1f6d3b3dc4b435783bbaaf4e41cdb6f32 + length-secret-key: 128 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..717c9d99 --- /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=-Wall -Wconversion -Wextra -Wpedantic -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..c0e985a3 --- /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 64 +#define PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES 32 +#define PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_CRYPTO_BYTES 16976 +#define PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_CRYPTO_SEEDBYTES 48 + +/* + * 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..dc12e192 --- /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]; + + /* 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..052924e2 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256f-simple/clean/sha256.c @@ -0,0 +1,75 @@ +/* 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]); +} + +/** + * Note that inlen should be sufficiently small that it still allows for + * an array to be allocated on the stack. Typically 'in' is merely a seed. + * Outputs outlen number of bytes + */ +void PQCLEAN_SPHINCSSHA256256FSIMPLE_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + const unsigned char *in, unsigned long inlen) { + unsigned char inbuf[inlen + 4]; + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + unsigned long i; + + memcpy(inbuf, in, inlen); + + /* 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(inbuf + inlen, 4, i); + sha256(out, inbuf, 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(inbuf + inlen, 4, i); + sha256(outbuf, inbuf, 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..54fc991a --- /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, + const unsigned char *in, 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..2c968bc3 --- /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-signature: 29792 +testvectors-sha256: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +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/492ec4f1f6d3b3dc4b435783bbaaf4e41cdb6f32 + length-secret-key: 128 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..8061bea0 --- /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=-Wall -Wconversion -Wextra -Wpedantic -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..8dbfa5ed --- /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 64 +#define PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES 32 +#define PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_CRYPTO_BYTES 16976 +#define PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_CRYPTO_SEEDBYTES 48 + +/* + * 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..bf547d63 --- /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]; + + /* 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..32f553d8 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256s-robust/clean/sha256.c @@ -0,0 +1,75 @@ +/* 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]); +} + +/** + * Note that inlen should be sufficiently small that it still allows for + * an array to be allocated on the stack. Typically 'in' is merely a seed. + * Outputs outlen number of bytes + */ +void PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + const unsigned char *in, unsigned long inlen) { + unsigned char inbuf[inlen + 4]; + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + unsigned long i; + + memcpy(inbuf, in, inlen); + + /* 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(inbuf + inlen, 4, i); + sha256(out, inbuf, 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(inbuf + inlen, 4, i); + sha256(outbuf, inbuf, 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..52d4b1da --- /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, + const unsigned char *in, 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..ee4f3a05 --- /dev/null +++ b/crypto_sign/sphincs-sha256-256s-robust/clean/thash_sha256_robust.c @@ -0,0 +1,76 @@ +#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; + uint8_t sha2_state[40]; + unsigned int i; + + memcpy(buf, pub_seed, SPX_N); + PQCLEAN_SPHINCSSHA256256SROBUST_CLEAN_compress_address(buf + SPX_N, addr); + 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 + 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 + 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 + 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 + 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..2c968bc3 --- /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-signature: 29792 +testvectors-sha256: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +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/492ec4f1f6d3b3dc4b435783bbaaf4e41cdb6f32 + length-secret-key: 128 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..5e23595f --- /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=-Wall -Wconversion -Wextra -Wpedantic -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..267b00d2 --- /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 64 +#define PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES 32 +#define PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_CRYPTO_BYTES 16976 +#define PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_CRYPTO_SEEDBYTES 48 + +/* + * 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..c4663c68 --- /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]; + + /* 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..9c92fa0c --- /dev/null +++ b/crypto_sign/sphincs-sha256-256s-simple/clean/sha256.c @@ -0,0 +1,75 @@ +/* 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]); +} + +/** + * Note that inlen should be sufficiently small that it still allows for + * an array to be allocated on the stack. Typically 'in' is merely a seed. + * Outputs outlen number of bytes + */ +void PQCLEAN_SPHINCSSHA256256SSIMPLE_CLEAN_mgf1( + unsigned char *out, unsigned long outlen, + const unsigned char *in, unsigned long inlen) { + unsigned char inbuf[inlen + 4]; + unsigned char outbuf[SPX_SHA256_OUTPUT_BYTES]; + unsigned long i; + + memcpy(inbuf, in, inlen); + + /* 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(inbuf + inlen, 4, i); + sha256(out, inbuf, 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(inbuf + inlen, 4, i); + sha256(outbuf, inbuf, 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..df0f1d80 --- /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, + const unsigned char *in, 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..af34d5a0 --- /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-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/492ec4f1f6d3b3dc4b435783bbaaf4e41cdb6f32 + length-secret-key: 64 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..d667c89c --- /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=-Wall -Wconversion -Wextra -Wpedantic -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/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..0d29a18d --- /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-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/492ec4f1f6d3b3dc4b435783bbaaf4e41cdb6f32 + length-secret-key: 64 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..c40f8325 --- /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=-Wall -Wconversion -Wextra -Wpedantic -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..f2754382 --- /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 16976 +#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..b8b5e6a1 --- /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-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/492ec4f1f6d3b3dc4b435783bbaaf4e41cdb6f32 + length-secret-key: 64 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..3d31edc7 --- /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=-Wall -Wconversion -Wextra -Wpedantic -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..dc961f8c --- /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 16976 +#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..2e4b98ff --- /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-signature: 35664 +testvectors-sha256: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +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/492ec4f1f6d3b3dc4b435783bbaaf4e41cdb6f32 + length-secret-key: 96 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..04364baf --- /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=-Wall -Wconversion -Wextra -Wpedantic -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..e6e3c157 --- /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 64 +#define PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES 32 +#define PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_CRYPTO_BYTES 16976 +#define PQCLEAN_SPHINCSSHAKE256192FROBUST_CLEAN_CRYPTO_SEEDBYTES 48 + +/* + * 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..2e4b98ff --- /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-signature: 35664 +testvectors-sha256: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +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/492ec4f1f6d3b3dc4b435783bbaaf4e41cdb6f32 + length-secret-key: 96 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..b1c5dddf --- /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=-Wall -Wconversion -Wextra -Wpedantic -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..e4f3e527 --- /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 64 +#define PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES 32 +#define PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_CRYPTO_BYTES 16976 +#define PQCLEAN_SPHINCSSHAKE256192FSIMPLE_CLEAN_CRYPTO_SEEDBYTES 48 + +/* + * 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..a0158dd0 --- /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-signature: 17064 +testvectors-sha256: fcbf73935a7089e45c79d5ac427647b1d8287821e91fc50fa80e5f03b05f6270 +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/492ec4f1f6d3b3dc4b435783bbaaf4e41cdb6f32 + length-secret-key: 96 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..8d178e39 --- /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=-Wall -Wconversion -Wextra -Wpedantic -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..de4b0a54 --- /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 64 +#define PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES 32 +#define PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_CRYPTO_BYTES 16976 +#define PQCLEAN_SPHINCSSHAKE256192SROBUST_CLEAN_CRYPTO_SEEDBYTES 48 + +/* + * 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..bc65a2dc --- /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-signature: 17064 +testvectors-sha256: 23edafc2af902ab5f76384cd855de979ed0a4ac7c0bbc9371958b361d8119d55 +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/492ec4f1f6d3b3dc4b435783bbaaf4e41cdb6f32 + length-secret-key: 96 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..fb0bdddd --- /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=-Wall -Wconversion -Wextra -Wpedantic -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..b53ce788 --- /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 64 +#define PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES 32 +#define PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_CRYPTO_BYTES 16976 +#define PQCLEAN_SPHINCSSHAKE256192SSIMPLE_CLEAN_CRYPTO_SEEDBYTES 48 + +/* + * 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..af9dcc0c --- /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-signature: 49216 +testvectors-sha256: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +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/492ec4f1f6d3b3dc4b435783bbaaf4e41cdb6f32 + length-secret-key: 128 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..b43911a4 --- /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=-Wall -Wconversion -Wextra -Wpedantic -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..9f2bd486 --- /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 64 +#define PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES 32 +#define PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_CRYPTO_BYTES 16976 +#define PQCLEAN_SPHINCSSHAKE256256FROBUST_CLEAN_CRYPTO_SEEDBYTES 48 + +/* + * 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..af9dcc0c --- /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-signature: 49216 +testvectors-sha256: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +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/492ec4f1f6d3b3dc4b435783bbaaf4e41cdb6f32 + length-secret-key: 128 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..b585d808 --- /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=-Wall -Wconversion -Wextra -Wpedantic -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..fd05811b --- /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 64 +#define PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES 32 +#define PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_CRYPTO_BYTES 16976 +#define PQCLEAN_SPHINCSSHAKE256256FSIMPLE_CLEAN_CRYPTO_SEEDBYTES 48 + +/* + * 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..4e002607 --- /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-signature: 29792 +testvectors-sha256: 6b0bf2fb0f2db01be38accb262e0d23a47332f26d765f74f58a7cdd929872c6b +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/492ec4f1f6d3b3dc4b435783bbaaf4e41cdb6f32 + length-secret-key: 128 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..60bf6c95 --- /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=-Wall -Wconversion -Wextra -Wpedantic -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..0a2b83e9 --- /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 64 +#define PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES 32 +#define PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_CRYPTO_BYTES 16976 +#define PQCLEAN_SPHINCSSHAKE256256SROBUST_CLEAN_CRYPTO_SEEDBYTES 48 + +/* + * 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..2c968bc3 --- /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-signature: 29792 +testvectors-sha256: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +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/492ec4f1f6d3b3dc4b435783bbaaf4e41cdb6f32 + length-secret-key: 128 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..fcc20c94 --- /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=-Wall -Wconversion -Wextra -Wpedantic -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..98cb83a8 --- /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 64 +#define PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES 32 +#define PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_CRYPTO_BYTES 16976 +#define PQCLEAN_SPHINCSSHAKE256256SSIMPLE_CLEAN_CRYPTO_SEEDBYTES 48 + +/* + * 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