@@ -17,7 +17,7 @@ version: 2.1 | |||
- run: | |||
name: Run the tests in a container | |||
command: | | |||
docker run -e CI=true -e PQCLEAN_ONLY_DIFF=1 --rm -v `pwd`:`pwd` -w `pwd` "pqclean/ci-container:$ARCH" /bin/bash -c " | |||
docker run -e CI=true -e PQCLEAN_ONLY_DIFF=1 -e PQCLEAN_SKIP_SCHEMES=sphincs-haraka-128f-robust,sphincs-haraka-192s-robust,sphincs-sha256-128f-robust,sphincs-sha256-192s-robust,sphincs-shake256-128f-robust,sphincs-shake256-192s-robust,sphincs-haraka-128f-simple,sphincs-haraka-192s-simple,sphincs-sha256-128f-simple,sphincs-sha256-192s-simple,sphincs-shake256-128f-simple,sphincs-shake256-192s-simple,sphincs-haraka-128s-robust,sphincs-haraka-256f-robust,sphincs-sha256-128s-robust,sphincs-sha256-256f-robust,sphincs-shake256-128s-robust,sphincs-shake256-256f-robust,sphincs-haraka-128s-simple,sphincs-haraka-256f-simple,sphincs-sha256-128s-simple,sphincs-sha256-256f-simple,sphincs-shake256-128s-simple,sphincs-shake256-256f-simple,sphincs-haraka-192f-robust,sphincs-haraka-256s-robust,sphincs-sha256-192f-robust,sphincs-sha256-256s-robust,sphincs-shake256-192f-robust,sphincs-shake256-256s-robust,sphincs-haraka-192f-simple,sphincs-haraka-256s-simple,sphincs-sha256-192f-simple,sphincs-sha256-256s-simple,sphincs-shake256-192f-simple,sphincs-shake256-256s-simple --rm -v `pwd`:`pwd` -w `pwd` "pqclean/ci-container:$ARCH" /bin/bash -c " | |||
uname -a && | |||
export CC=${CC} && | |||
pip3 install -r requirements.txt && | |||
@@ -38,6 +38,7 @@ version: 2.1 | |||
command: | | |||
export CC=${CC} | |||
export PQCLEAN_ONLY_DIFF=1 | |||
export PQCLEAN_SKIP_SCHEMES=sphincs-haraka-128f-robust,sphincs-haraka-192s-robust,sphincs-sha256-128f-robust,sphincs-sha256-192s-robust,sphincs-shake256-128f-robust,sphincs-shake256-192s-robust,sphincs-haraka-128f-simple,sphincs-haraka-192s-simple,sphincs-sha256-128f-simple,sphincs-sha256-192s-simple,sphincs-shake256-128f-simple,sphincs-shake256-192s-simple,sphincs-haraka-128s-robust,sphincs-haraka-256f-robust,sphincs-sha256-128s-robust,sphincs-sha256-256f-robust,sphincs-shake256-128s-robust,sphincs-shake256-256f-robust,sphincs-haraka-128s-simple,sphincs-haraka-256f-simple,sphincs-sha256-128s-simple,sphincs-sha256-256f-simple,sphincs-shake256-128s-simple,sphincs-shake256-256f-simple,sphincs-haraka-192f-robust,sphincs-haraka-256s-robust,sphincs-sha256-192f-robust,sphincs-sha256-256s-robust,sphincs-shake256-192f-robust,sphincs-shake256-256s-robust,sphincs-haraka-192f-simple,sphincs-haraka-256s-simple,sphincs-sha256-192f-simple,sphincs-sha256-256s-simple,sphincs-shake256-192f-simple,sphincs-shake256-256s-simple | |||
pip3 install -r requirements.txt | |||
mkdir test-results | |||
cd test | |||
@@ -13,7 +13,7 @@ matrix: | |||
- git reset --hard $TRAVIS_COMMIT | |||
script: | |||
# Use travis-wait to allow slower tests to run | |||
- "cd test && travis_wait 30 python3 -m nose --rednose --verbose" | |||
- "cd test && travis_wait 60 python3 -m nose --rednose --verbose" | |||
env: | |||
PQCLEAN_ONLY_DIFF: 1 | |||
addons: | |||
@@ -43,7 +43,7 @@ matrix: | |||
- gcc --version | |||
script: | |||
# Use travis-wait to allow slower tests to run | |||
- "cd test && travis_wait 30 python3 -m nose --rednose --verbose" | |||
- "cd test && travis_wait 60 python3 -m nose --rednose --verbose" | |||
cache: pip | |||
@@ -9,6 +9,7 @@ shallow_clone: false | |||
environment: | |||
PQCLEAN_ONLY_DIFF: 1 | |||
PQCLEAN_SKIP_SCHEMES: sphincs-haraka-128f-robust,sphincs-haraka-192s-robust,sphincs-sha256-128f-robust,sphincs-sha256-192s-robust,sphincs-shake256-128f-robust,sphincs-shake256-192s-robust,sphincs-haraka-128f-simple,sphincs-haraka-192s-simple,sphincs-sha256-128f-simple,sphincs-sha256-192s-simple,sphincs-shake256-128f-simple,sphincs-shake256-192s-simple,sphincs-haraka-128s-robust,sphincs-haraka-256f-robust,sphincs-sha256-128s-robust,sphincs-sha256-256f-robust,sphincs-shake256-128s-robust,sphincs-shake256-256f-robust,sphincs-haraka-128s-simple,sphincs-haraka-256f-simple,sphincs-sha256-128s-simple,sphincs-sha256-256f-simple,sphincs-shake256-128s-simple,sphincs-shake256-256f-simple,sphincs-haraka-192f-robust,sphincs-haraka-256s-robust,sphincs-sha256-192f-robust,sphincs-sha256-256s-robust,sphincs-shake256-192f-robust,sphincs-shake256-256s-robust,sphincs-haraka-192f-simple,sphincs-haraka-256s-simple,sphincs-sha256-192f-simple,sphincs-sha256-256s-simple,sphincs-shake256-192f-simple,sphincs-shake256-256s-simple | |||
matrix: | |||
- BITS: 64 | |||
- BITS: 32 | |||
@@ -0,0 +1,27 @@ | |||
name: SPHINCS+ | |||
type: signature | |||
claimed-nist-level: 1 | |||
length-public-key: 32 | |||
length-secret-key: 64 | |||
length-signature: 16976 | |||
testvectors-sha256: f0f84722cf529a108006d84b52966cbebd92146ee33cacdd7d1bba2cdc1944fd | |||
principal-submitter: Andreas Hülsing | |||
auxiliary-submitters: | |||
- Jean-Philippe Aumasson | |||
- Daniel J. Bernstein, | |||
- Christoph Dobraunig | |||
- Maria Eichlseder | |||
- Scott Fluhrer | |||
- Stefan-Lukas Gazdag | |||
- Panos Kampanakis | |||
- Stefan Kölbl | |||
- Tanja Lange | |||
- Martin M. Lauridsen | |||
- Florian Mendel | |||
- Ruben Niederhagen | |||
- Christian Rechberger | |||
- Joost Rijneveld | |||
- Peter Schwabe | |||
implementations: | |||
- name: clean | |||
version: https://github.com/sphincs/sphincsplus/commit/77755c94d0bc744478044d6efbb888dc13156441 |
@@ -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 | |||
<http://creativecommons.org/publicdomain/zero/1.0/> |
@@ -0,0 +1,20 @@ | |||
# This Makefile can be used with GNU Make or BSD Make | |||
LIB=libsphincs-haraka-128f-robust_clean.a | |||
HEADERS = params.h address.h wots.h utils.h fors.h api.h hash.h thash.h haraka.h | |||
OBJECTS = address.o wots.o utils.o fors.o sign.o hash_haraka.o thash_haraka_robust.o haraka.o | |||
CFLAGS=-O3 -Wall -Wconversion -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) | |||
all: $(LIB) | |||
%.o: %.c $(HEADERS) | |||
$(CC) $(CFLAGS) -c -o $@ $< | |||
$(LIB): $(OBJECTS) | |||
$(AR) -r $@ $(OBJECTS) | |||
clean: | |||
$(RM) $(OBJECTS) | |||
$(RM) $(LIB) |
@@ -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) |
@@ -0,0 +1,78 @@ | |||
#include <stdint.h> | |||
#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; | |||
} |
@@ -0,0 +1,50 @@ | |||
#ifndef SPX_ADDRESS_H | |||
#define SPX_ADDRESS_H | |||
#include <stdint.h> | |||
#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 |
@@ -0,0 +1,78 @@ | |||
#ifndef PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_API_H | |||
#define PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_API_H | |||
#include <stddef.h> | |||
#include <stdint.h> | |||
#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 |
@@ -0,0 +1,164 @@ | |||
#include <stdint.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#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); | |||
} |
@@ -0,0 +1,30 @@ | |||
#ifndef SPX_FORS_H | |||
#define SPX_FORS_H | |||
#include <stdint.h> | |||
#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 |
@@ -0,0 +1,965 @@ | |||
/* | |||
* Constant time implementation of the Haraka hash function. | |||
* | |||
* The bit-sliced implementation of the AES round functions are | |||
* based on the AES implementation in BearSSL written | |||
* by Thomas Pornin <pornin@bolet.org> | |||
*/ | |||
#include <stdint.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include "haraka.h" | |||
#define HARAKAS_RATE 32 | |||
static const uint64_t haraka512_rc64[10][8] = { | |||
{0x24cf0ab9086f628b, 0xbdd6eeecc83b8382, 0xd96fb0306cdad0a7, 0xaace082ac8f95f89, 0x449d8e8870d7041f, 0x49bb2f80b2b3e2f8, 0x0569ae98d93bb258, 0x23dc9691e7d6a4b1}, | |||
{0xd8ba10ede0fe5b6e, 0x7ecf7dbe424c7b8e, 0x6ea9949c6df62a31, 0xbf3f3c97ec9c313e, 0x241d03a196a1861e, 0xead3a51116e5a2ea, 0x77d479fcad9574e3, 0x18657a1af894b7a0}, | |||
{0x10671e1a7f595522, 0xd9a00ff675d28c7b, 0x2f1edf0d2b9ba661, 0xb8ff58b8e3de45f9, 0xee29261da9865c02, 0xd1532aa4b50bdf43, 0x8bf858159b231bb1, 0xdf17439d22d4f599}, | |||
{0xdd4b2f0870b918c0, 0x757a81f3b39b1bb6, 0x7a5c556898952e3f, 0x7dd70a16d915d87a, 0x3ae61971982b8301, 0xc3ab319e030412be, 0x17c0033ac094a8cb, 0x5a0630fc1a8dc4ef}, | |||
{0x17708988c1632f73, 0xf92ddae090b44f4f, 0x11ac0285c43aa314, 0x509059941936b8ba, 0xd03e152fa2ce9b69, 0x3fbcbcb63a32998b, 0x6204696d692254f7, 0x915542ed93ec59b4}, | |||
{0xf4ed94aa8879236e, 0xff6cb41cd38e03c0, 0x069b38602368aeab, 0x669495b820f0ddba, 0xf42013b1b8bf9e3d, 0xcf935efe6439734d, 0xbc1dcf42ca29e3f8, 0x7e6d3ed29f78ad67}, | |||
{0xf3b0f6837ffcddaa, 0x3a76faef934ddf41, 0xcec7ae583a9c8e35, 0xe4dd18c68f0260af, 0x2c0e5df1ad398eaa, 0x478df5236ae22e8c, 0xfb944c46fe865f39, 0xaa48f82f028132ba}, | |||
{0x231b9ae2b76aca77, 0x292a76a712db0b40, 0x5850625dc8134491, 0x73137dd469810fb5, 0x8a12a6a202a474fd, 0xd36fd9daa78bdb80, 0xb34c5e733505706f, 0xbaf1cdca818d9d96}, | |||
{0x2e99781335e8c641, 0xbddfe5cce47d560e, 0xf74e9bf32e5e040c, 0x1d7a709d65996be9, 0x670df36a9cf66cdd, 0xd05ef84a176a2875, 0x0f888e828cb1c44e, 0x1a79e9c9727b052c}, | |||
{0x83497348628d84de, 0x2e9387d51f22a754, 0xb000068da2f852d6, 0x378c9e1190fd6fe5, 0x870027c316de7293, 0xe51a9d4462e047bb, 0x90ecf7f8c6251195, 0x655953bfbed90a9c}, | |||
}; | |||
static uint64_t tweaked512_rc64[10][8]; | |||
static uint32_t tweaked256_rc32[10][8]; | |||
static uint32_t tweaked256_rc32_sseed[10][8]; | |||
static inline uint32_t br_dec32le(const unsigned char *src) { | |||
return (uint32_t)src[0] | |||
| ((uint32_t)src[1] << 8) | |||
| ((uint32_t)src[2] << 16) | |||
| ((uint32_t)src[3] << 24); | |||
} | |||
static void br_range_dec32le(uint32_t *v, size_t num, const unsigned char *src) { | |||
while (num-- > 0) { | |||
*v ++ = br_dec32le(src); | |||
src += 4; | |||
} | |||
} | |||
static inline void br_enc32le(unsigned char *dst, uint32_t x) { | |||
dst[0] = (unsigned char)x; | |||
dst[1] = (unsigned char)(x >> 8); | |||
dst[2] = (unsigned char)(x >> 16); | |||
dst[3] = (unsigned char)(x >> 24); | |||
} | |||
static void br_range_enc32le(unsigned char *dst, const uint32_t *v, size_t num) { | |||
while (num-- > 0) { | |||
br_enc32le(dst, *v ++); | |||
dst += 4; | |||
} | |||
} | |||
static void br_aes_ct64_bitslice_Sbox(uint64_t *q) { | |||
/* | |||
* This S-box implementation is a straightforward translation of | |||
* the circuit described by Boyar and Peralta in "A new | |||
* combinational logic minimization technique with applications | |||
* to cryptology" (https://eprint.iacr.org/2009/191.pdf). | |||
* | |||
* Note that variables x* (input) and s* (output) are numbered | |||
* in "reverse" order (x0 is the high bit, x7 is the low bit). | |||
*/ | |||
uint64_t x0, x1, x2, x3, x4, x5, x6, x7; | |||
uint64_t y1, y2, y3, y4, y5, y6, y7, y8, y9; | |||
uint64_t y10, y11, y12, y13, y14, y15, y16, y17, y18, y19; | |||
uint64_t y20, y21; | |||
uint64_t z0, z1, z2, z3, z4, z5, z6, z7, z8, z9; | |||
uint64_t z10, z11, z12, z13, z14, z15, z16, z17; | |||
uint64_t t0, t1, t2, t3, t4, t5, t6, t7, t8, t9; | |||
uint64_t t10, t11, t12, t13, t14, t15, t16, t17, t18, t19; | |||
uint64_t t20, t21, t22, t23, t24, t25, t26, t27, t28, t29; | |||
uint64_t t30, t31, t32, t33, t34, t35, t36, t37, t38, t39; | |||
uint64_t t40, t41, t42, t43, t44, t45, t46, t47, t48, t49; | |||
uint64_t t50, t51, t52, t53, t54, t55, t56, t57, t58, t59; | |||
uint64_t t60, t61, t62, t63, t64, t65, t66, t67; | |||
uint64_t s0, s1, s2, s3, s4, s5, s6, s7; | |||
x0 = q[7]; | |||
x1 = q[6]; | |||
x2 = q[5]; | |||
x3 = q[4]; | |||
x4 = q[3]; | |||
x5 = q[2]; | |||
x6 = q[1]; | |||
x7 = q[0]; | |||
/* | |||
* Top linear transformation. | |||
*/ | |||
y14 = x3 ^ x5; | |||
y13 = x0 ^ x6; | |||
y9 = x0 ^ x3; | |||
y8 = x0 ^ x5; | |||
t0 = x1 ^ x2; | |||
y1 = t0 ^ x7; | |||
y4 = y1 ^ x3; | |||
y12 = y13 ^ y14; | |||
y2 = y1 ^ x0; | |||
y5 = y1 ^ x6; | |||
y3 = y5 ^ y8; | |||
t1 = x4 ^ y12; | |||
y15 = t1 ^ x5; | |||
y20 = t1 ^ x1; | |||
y6 = y15 ^ x7; | |||
y10 = y15 ^ t0; | |||
y11 = y20 ^ y9; | |||
y7 = x7 ^ y11; | |||
y17 = y10 ^ y11; | |||
y19 = y10 ^ y8; | |||
y16 = t0 ^ y11; | |||
y21 = y13 ^ y16; | |||
y18 = x0 ^ y16; | |||
/* | |||
* Non-linear section. | |||
*/ | |||
t2 = y12 & y15; | |||
t3 = y3 & y6; | |||
t4 = t3 ^ t2; | |||
t5 = y4 & x7; | |||
t6 = t5 ^ t2; | |||
t7 = y13 & y16; | |||
t8 = y5 & y1; | |||
t9 = t8 ^ t7; | |||
t10 = y2 & y7; | |||
t11 = t10 ^ t7; | |||
t12 = y9 & y11; | |||
t13 = y14 & y17; | |||
t14 = t13 ^ t12; | |||
t15 = y8 & y10; | |||
t16 = t15 ^ t12; | |||
t17 = t4 ^ t14; | |||
t18 = t6 ^ t16; | |||
t19 = t9 ^ t14; | |||
t20 = t11 ^ t16; | |||
t21 = t17 ^ y20; | |||
t22 = t18 ^ y19; | |||
t23 = t19 ^ y21; | |||
t24 = t20 ^ y18; | |||
t25 = t21 ^ t22; | |||
t26 = t21 & t23; | |||
t27 = t24 ^ t26; | |||
t28 = t25 & t27; | |||
t29 = t28 ^ t22; | |||
t30 = t23 ^ t24; | |||
t31 = t22 ^ t26; | |||
t32 = t31 & t30; | |||
t33 = t32 ^ t24; | |||
t34 = t23 ^ t33; | |||
t35 = t27 ^ t33; | |||
t36 = t24 & t35; | |||
t37 = t36 ^ t34; | |||
t38 = t27 ^ t36; | |||
t39 = t29 & t38; | |||
t40 = t25 ^ t39; | |||
t41 = t40 ^ t37; | |||
t42 = t29 ^ t33; | |||
t43 = t29 ^ t40; | |||
t44 = t33 ^ t37; | |||
t45 = t42 ^ t41; | |||
z0 = t44 & y15; | |||
z1 = t37 & y6; | |||
z2 = t33 & x7; | |||
z3 = t43 & y16; | |||
z4 = t40 & y1; | |||
z5 = t29 & y7; | |||
z6 = t42 & y11; | |||
z7 = t45 & y17; | |||
z8 = t41 & y10; | |||
z9 = t44 & y12; | |||
z10 = t37 & y3; | |||
z11 = t33 & y4; | |||
z12 = t43 & y13; | |||
z13 = t40 & y5; | |||
z14 = t29 & y2; | |||
z15 = t42 & y9; | |||
z16 = t45 & y14; | |||
z17 = t41 & y8; | |||
/* | |||
* Bottom linear transformation. | |||
*/ | |||
t46 = z15 ^ z16; | |||
t47 = z10 ^ z11; | |||
t48 = z5 ^ z13; | |||
t49 = z9 ^ z10; | |||
t50 = z2 ^ z12; | |||
t51 = z2 ^ z5; | |||
t52 = z7 ^ z8; | |||
t53 = z0 ^ z3; | |||
t54 = z6 ^ z7; | |||
t55 = z16 ^ z17; | |||
t56 = z12 ^ t48; | |||
t57 = t50 ^ t53; | |||
t58 = z4 ^ t46; | |||
t59 = z3 ^ t54; | |||
t60 = t46 ^ t57; | |||
t61 = z14 ^ t57; | |||
t62 = t52 ^ t58; | |||
t63 = t49 ^ t58; | |||
t64 = z4 ^ t59; | |||
t65 = t61 ^ t62; | |||
t66 = z1 ^ t63; | |||
s0 = t59 ^ t63; | |||
s6 = t56 ^ ~t62; | |||
s7 = t48 ^ ~t60; | |||
t67 = t64 ^ t65; | |||
s3 = t53 ^ t66; | |||
s4 = t51 ^ t66; | |||
s5 = t47 ^ t65; | |||
s1 = t64 ^ ~s3; | |||
s2 = t55 ^ ~t67; | |||
q[7] = s0; | |||
q[6] = s1; | |||
q[5] = s2; | |||
q[4] = s3; | |||
q[3] = s4; | |||
q[2] = s5; | |||
q[1] = s6; | |||
q[0] = s7; | |||
} | |||
static void br_aes_ct_bitslice_Sbox(uint32_t *q) { | |||
/* | |||
* This S-box implementation is a straightforward translation of | |||
* the circuit described by Boyar and Peralta in "A new | |||
* combinational logic minimization technique with applications | |||
* to cryptology" (https://eprint.iacr.org/2009/191.pdf). | |||
* | |||
* Note that variables x* (input) and s* (output) are numbered | |||
* in "reverse" order (x0 is the high bit, x7 is the low bit). | |||
*/ | |||
uint32_t x0, x1, x2, x3, x4, x5, x6, x7; | |||
uint32_t y1, y2, y3, y4, y5, y6, y7, y8, y9; | |||
uint32_t y10, y11, y12, y13, y14, y15, y16, y17, y18, y19; | |||
uint32_t y20, y21; | |||
uint32_t z0, z1, z2, z3, z4, z5, z6, z7, z8, z9; | |||
uint32_t z10, z11, z12, z13, z14, z15, z16, z17; | |||
uint32_t t0, t1, t2, t3, t4, t5, t6, t7, t8, t9; | |||
uint32_t t10, t11, t12, t13, t14, t15, t16, t17, t18, t19; | |||
uint32_t t20, t21, t22, t23, t24, t25, t26, t27, t28, t29; | |||
uint32_t t30, t31, t32, t33, t34, t35, t36, t37, t38, t39; | |||
uint32_t t40, t41, t42, t43, t44, t45, t46, t47, t48, t49; | |||
uint32_t t50, t51, t52, t53, t54, t55, t56, t57, t58, t59; | |||
uint32_t t60, t61, t62, t63, t64, t65, t66, t67; | |||
uint32_t s0, s1, s2, s3, s4, s5, s6, s7; | |||
x0 = q[7]; | |||
x1 = q[6]; | |||
x2 = q[5]; | |||
x3 = q[4]; | |||
x4 = q[3]; | |||
x5 = q[2]; | |||
x6 = q[1]; | |||
x7 = q[0]; | |||
/* | |||
* Top linear transformation. | |||
*/ | |||
y14 = x3 ^ x5; | |||
y13 = x0 ^ x6; | |||
y9 = x0 ^ x3; | |||
y8 = x0 ^ x5; | |||
t0 = x1 ^ x2; | |||
y1 = t0 ^ x7; | |||
y4 = y1 ^ x3; | |||
y12 = y13 ^ y14; | |||
y2 = y1 ^ x0; | |||
y5 = y1 ^ x6; | |||
y3 = y5 ^ y8; | |||
t1 = x4 ^ y12; | |||
y15 = t1 ^ x5; | |||
y20 = t1 ^ x1; | |||
y6 = y15 ^ x7; | |||
y10 = y15 ^ t0; | |||
y11 = y20 ^ y9; | |||
y7 = x7 ^ y11; | |||
y17 = y10 ^ y11; | |||
y19 = y10 ^ y8; | |||
y16 = t0 ^ y11; | |||
y21 = y13 ^ y16; | |||
y18 = x0 ^ y16; | |||
/* | |||
* Non-linear section. | |||
*/ | |||
t2 = y12 & y15; | |||
t3 = y3 & y6; | |||
t4 = t3 ^ t2; | |||
t5 = y4 & x7; | |||
t6 = t5 ^ t2; | |||
t7 = y13 & y16; | |||
t8 = y5 & y1; | |||
t9 = t8 ^ t7; | |||
t10 = y2 & y7; | |||
t11 = t10 ^ t7; | |||
t12 = y9 & y11; | |||
t13 = y14 & y17; | |||
t14 = t13 ^ t12; | |||
t15 = y8 & y10; | |||
t16 = t15 ^ t12; | |||
t17 = t4 ^ t14; | |||
t18 = t6 ^ t16; | |||
t19 = t9 ^ t14; | |||
t20 = t11 ^ t16; | |||
t21 = t17 ^ y20; | |||
t22 = t18 ^ y19; | |||
t23 = t19 ^ y21; | |||
t24 = t20 ^ y18; | |||
t25 = t21 ^ t22; | |||
t26 = t21 & t23; | |||
t27 = t24 ^ t26; | |||
t28 = t25 & t27; | |||
t29 = t28 ^ t22; | |||
t30 = t23 ^ t24; | |||
t31 = t22 ^ t26; | |||
t32 = t31 & t30; | |||
t33 = t32 ^ t24; | |||
t34 = t23 ^ t33; | |||
t35 = t27 ^ t33; | |||
t36 = t24 & t35; | |||
t37 = t36 ^ t34; | |||
t38 = t27 ^ t36; | |||
t39 = t29 & t38; | |||
t40 = t25 ^ t39; | |||
t41 = t40 ^ t37; | |||
t42 = t29 ^ t33; | |||
t43 = t29 ^ t40; | |||
t44 = t33 ^ t37; | |||
t45 = t42 ^ t41; | |||
z0 = t44 & y15; | |||
z1 = t37 & y6; | |||
z2 = t33 & x7; | |||
z3 = t43 & y16; | |||
z4 = t40 & y1; | |||
z5 = t29 & y7; | |||
z6 = t42 & y11; | |||
z7 = t45 & y17; | |||
z8 = t41 & y10; | |||
z9 = t44 & y12; | |||
z10 = t37 & y3; | |||
z11 = t33 & y4; | |||
z12 = t43 & y13; | |||
z13 = t40 & y5; | |||
z14 = t29 & y2; | |||
z15 = t42 & y9; | |||
z16 = t45 & y14; | |||
z17 = t41 & y8; | |||
/* | |||
* Bottom linear transformation. | |||
*/ | |||
t46 = z15 ^ z16; | |||
t47 = z10 ^ z11; | |||
t48 = z5 ^ z13; | |||
t49 = z9 ^ z10; | |||
t50 = z2 ^ z12; | |||
t51 = z2 ^ z5; | |||
t52 = z7 ^ z8; | |||
t53 = z0 ^ z3; | |||
t54 = z6 ^ z7; | |||
t55 = z16 ^ z17; | |||
t56 = z12 ^ t48; | |||
t57 = t50 ^ t53; | |||
t58 = z4 ^ t46; | |||
t59 = z3 ^ t54; | |||
t60 = t46 ^ t57; | |||
t61 = z14 ^ t57; | |||
t62 = t52 ^ t58; | |||
t63 = t49 ^ t58; | |||
t64 = z4 ^ t59; | |||
t65 = t61 ^ t62; | |||
t66 = z1 ^ t63; | |||
s0 = t59 ^ t63; | |||
s6 = t56 ^ ~t62; | |||
s7 = t48 ^ ~t60; | |||
t67 = t64 ^ t65; | |||
s3 = t53 ^ t66; | |||
s4 = t51 ^ t66; | |||
s5 = t47 ^ t65; | |||
s1 = t64 ^ ~s3; | |||
s2 = t55 ^ ~t67; | |||
q[7] = s0; | |||
q[6] = s1; | |||
q[5] = s2; | |||
q[4] = s3; | |||
q[3] = s4; | |||
q[2] = s5; | |||
q[1] = s6; | |||
q[0] = s7; | |||
} | |||
static void br_aes_ct_ortho(uint32_t *q) { | |||
#define SWAPN_32(cl, ch, s, x, y) do { \ | |||
uint32_t a, b; \ | |||
a = (x); \ | |||
b = (y); \ | |||
(x) = (a & (uint32_t)(cl)) | ((b & (uint32_t)(cl)) << (s)); \ | |||
(y) = ((a & (uint32_t)(ch)) >> (s)) | (b & (uint32_t)(ch)); \ | |||
} while (0) | |||
#define SWAP2_32(x, y) SWAPN_32(0x55555555, 0xAAAAAAAA, 1, x, y) | |||
#define SWAP4_32(x, y) SWAPN_32(0x33333333, 0xCCCCCCCC, 2, x, y) | |||
#define SWAP8_32(x, y) SWAPN_32(0x0F0F0F0F, 0xF0F0F0F0, 4, x, y) | |||
SWAP2_32(q[0], q[1]); | |||
SWAP2_32(q[2], q[3]); | |||
SWAP2_32(q[4], q[5]); | |||
SWAP2_32(q[6], q[7]); | |||
SWAP4_32(q[0], q[2]); | |||
SWAP4_32(q[1], q[3]); | |||
SWAP4_32(q[4], q[6]); | |||
SWAP4_32(q[5], q[7]); | |||
SWAP8_32(q[0], q[4]); | |||
SWAP8_32(q[1], q[5]); | |||
SWAP8_32(q[2], q[6]); | |||
SWAP8_32(q[3], q[7]); | |||
} | |||
static inline void add_round_key32(uint32_t *q, const uint32_t *sk) { | |||
q[0] ^= sk[0]; | |||
q[1] ^= sk[1]; | |||
q[2] ^= sk[2]; | |||
q[3] ^= sk[3]; | |||
q[4] ^= sk[4]; | |||
q[5] ^= sk[5]; | |||
q[6] ^= sk[6]; | |||
q[7] ^= sk[7]; | |||
} | |||
static inline void shift_rows32(uint32_t *q) { | |||
int i; | |||
for (i = 0; i < 8; i++) { | |||
uint32_t x; | |||
x = q[i]; | |||
q[i] = (x & 0x000000FF) | |||
| ((x & 0x0000FC00) >> 2) | ((x & 0x00000300) << 6) | |||
| ((x & 0x00F00000) >> 4) | ((x & 0x000F0000) << 4) | |||
| ((x & 0xC0000000) >> 6) | ((x & 0x3F000000) << 2); | |||
} | |||
} | |||
static inline uint32_t rotr16(uint32_t x) { | |||
return (x << 16) | (x >> 16); | |||
} | |||
static inline void mix_columns32(uint32_t *q) { | |||
uint32_t q0, q1, q2, q3, q4, q5, q6, q7; | |||
uint32_t r0, r1, r2, r3, r4, r5, r6, r7; | |||
q0 = q[0]; | |||
q1 = q[1]; | |||
q2 = q[2]; | |||
q3 = q[3]; | |||
q4 = q[4]; | |||
q5 = q[5]; | |||
q6 = q[6]; | |||
q7 = q[7]; | |||
r0 = (q0 >> 8) | (q0 << 24); | |||
r1 = (q1 >> 8) | (q1 << 24); | |||
r2 = (q2 >> 8) | (q2 << 24); | |||
r3 = (q3 >> 8) | (q3 << 24); | |||
r4 = (q4 >> 8) | (q4 << 24); | |||
r5 = (q5 >> 8) | (q5 << 24); | |||
r6 = (q6 >> 8) | (q6 << 24); | |||
r7 = (q7 >> 8) | (q7 << 24); | |||
q[0] = q7 ^ r7 ^ r0 ^ rotr16(q0 ^ r0); | |||
q[1] = q0 ^ r0 ^ q7 ^ r7 ^ r1 ^ rotr16(q1 ^ r1); | |||
q[2] = q1 ^ r1 ^ r2 ^ rotr16(q2 ^ r2); | |||
q[3] = q2 ^ r2 ^ q7 ^ r7 ^ r3 ^ rotr16(q3 ^ r3); | |||
q[4] = q3 ^ r3 ^ q7 ^ r7 ^ r4 ^ rotr16(q4 ^ r4); | |||
q[5] = q4 ^ r4 ^ r5 ^ rotr16(q5 ^ r5); | |||
q[6] = q5 ^ r5 ^ r6 ^ rotr16(q6 ^ r6); | |||
q[7] = q6 ^ r6 ^ r7 ^ rotr16(q7 ^ r7); | |||
} | |||
static void br_aes_ct64_ortho(uint64_t *q) { | |||
#define SWAPN(cl, ch, s, x, y) do { \ | |||
uint64_t a, b; \ | |||
a = (x); \ | |||
b = (y); \ | |||
(x) = (a & (uint64_t)(cl)) | ((b & (uint64_t)(cl)) << (s)); \ | |||
(y) = ((a & (uint64_t)(ch)) >> (s)) | (b & (uint64_t)(ch)); \ | |||
} while (0) | |||
#define SWAP2(x, y) SWAPN(0x5555555555555555, 0xAAAAAAAAAAAAAAAA, 1, x, y) | |||
#define SWAP4(x, y) SWAPN(0x3333333333333333, 0xCCCCCCCCCCCCCCCC, 2, x, y) | |||
#define SWAP8(x, y) SWAPN(0x0F0F0F0F0F0F0F0F, 0xF0F0F0F0F0F0F0F0, 4, x, y) | |||
SWAP2(q[0], q[1]); | |||
SWAP2(q[2], q[3]); | |||
SWAP2(q[4], q[5]); | |||
SWAP2(q[6], q[7]); | |||
SWAP4(q[0], q[2]); | |||
SWAP4(q[1], q[3]); | |||
SWAP4(q[4], q[6]); | |||
SWAP4(q[5], q[7]); | |||
SWAP8(q[0], q[4]); | |||
SWAP8(q[1], q[5]); | |||
SWAP8(q[2], q[6]); | |||
SWAP8(q[3], q[7]); | |||
} | |||
static void br_aes_ct64_interleave_in(uint64_t *q0, uint64_t *q1, const uint32_t *w) { | |||
uint64_t x0, x1, x2, x3; | |||
x0 = w[0]; | |||
x1 = w[1]; | |||
x2 = w[2]; | |||
x3 = w[3]; | |||
x0 |= (x0 << 16); | |||
x1 |= (x1 << 16); | |||
x2 |= (x2 << 16); | |||
x3 |= (x3 << 16); | |||
x0 &= (uint64_t)0x0000FFFF0000FFFF; | |||
x1 &= (uint64_t)0x0000FFFF0000FFFF; | |||
x2 &= (uint64_t)0x0000FFFF0000FFFF; | |||
x3 &= (uint64_t)0x0000FFFF0000FFFF; | |||
x0 |= (x0 << 8); | |||
x1 |= (x1 << 8); | |||
x2 |= (x2 << 8); | |||
x3 |= (x3 << 8); | |||
x0 &= (uint64_t)0x00FF00FF00FF00FF; | |||
x1 &= (uint64_t)0x00FF00FF00FF00FF; | |||
x2 &= (uint64_t)0x00FF00FF00FF00FF; | |||
x3 &= (uint64_t)0x00FF00FF00FF00FF; | |||
*q0 = x0 | (x2 << 8); | |||
*q1 = x1 | (x3 << 8); | |||
} | |||
static void br_aes_ct64_interleave_out(uint32_t *w, uint64_t q0, uint64_t q1) { | |||
uint64_t x0, x1, x2, x3; | |||
x0 = q0 & (uint64_t)0x00FF00FF00FF00FF; | |||
x1 = q1 & (uint64_t)0x00FF00FF00FF00FF; | |||
x2 = (q0 >> 8) & (uint64_t)0x00FF00FF00FF00FF; | |||
x3 = (q1 >> 8) & (uint64_t)0x00FF00FF00FF00FF; | |||
x0 |= (x0 >> 8); | |||
x1 |= (x1 >> 8); | |||
x2 |= (x2 >> 8); | |||
x3 |= (x3 >> 8); | |||
x0 &= (uint64_t)0x0000FFFF0000FFFF; | |||
x1 &= (uint64_t)0x0000FFFF0000FFFF; | |||
x2 &= (uint64_t)0x0000FFFF0000FFFF; | |||
x3 &= (uint64_t)0x0000FFFF0000FFFF; | |||
w[0] = (uint32_t)x0 | (uint32_t)(x0 >> 16); | |||
w[1] = (uint32_t)x1 | (uint32_t)(x1 >> 16); | |||
w[2] = (uint32_t)x2 | (uint32_t)(x2 >> 16); | |||
w[3] = (uint32_t)x3 | (uint32_t)(x3 >> 16); | |||
} | |||
static inline void add_round_key(uint64_t *q, const uint64_t *sk) { | |||
q[0] ^= sk[0]; | |||
q[1] ^= sk[1]; | |||
q[2] ^= sk[2]; | |||
q[3] ^= sk[3]; | |||
q[4] ^= sk[4]; | |||
q[5] ^= sk[5]; | |||
q[6] ^= sk[6]; | |||
q[7] ^= sk[7]; | |||
} | |||
static inline void shift_rows(uint64_t *q) { | |||
int i; | |||
for (i = 0; i < 8; i++) { | |||
uint64_t x; | |||
x = q[i]; | |||
q[i] = (x & (uint64_t)0x000000000000FFFF) | |||
| ((x & (uint64_t)0x00000000FFF00000) >> 4) | |||
| ((x & (uint64_t)0x00000000000F0000) << 12) | |||
| ((x & (uint64_t)0x0000FF0000000000) >> 8) | |||
| ((x & (uint64_t)0x000000FF00000000) << 8) | |||
| ((x & (uint64_t)0xF000000000000000) >> 12) | |||
| ((x & (uint64_t)0x0FFF000000000000) << 4); | |||
} | |||
} | |||
static inline uint64_t rotr32(uint64_t x) { | |||
return (x << 32) | (x >> 32); | |||
} | |||
static inline void mix_columns(uint64_t *q) { | |||
uint64_t q0, q1, q2, q3, q4, q5, q6, q7; | |||
uint64_t r0, r1, r2, r3, r4, r5, r6, r7; | |||
q0 = q[0]; | |||
q1 = q[1]; | |||
q2 = q[2]; | |||
q3 = q[3]; | |||
q4 = q[4]; | |||
q5 = q[5]; | |||
q6 = q[6]; | |||
q7 = q[7]; | |||
r0 = (q0 >> 16) | (q0 << 48); | |||
r1 = (q1 >> 16) | (q1 << 48); | |||
r2 = (q2 >> 16) | (q2 << 48); | |||
r3 = (q3 >> 16) | (q3 << 48); | |||
r4 = (q4 >> 16) | (q4 << 48); | |||
r5 = (q5 >> 16) | (q5 << 48); | |||
r6 = (q6 >> 16) | (q6 << 48); | |||
r7 = (q7 >> 16) | (q7 << 48); | |||
q[0] = q7 ^ r7 ^ r0 ^ rotr32(q0 ^ r0); | |||
q[1] = q0 ^ r0 ^ q7 ^ r7 ^ r1 ^ rotr32(q1 ^ r1); | |||
q[2] = q1 ^ r1 ^ r2 ^ rotr32(q2 ^ r2); | |||
q[3] = q2 ^ r2 ^ q7 ^ r7 ^ r3 ^ rotr32(q3 ^ r3); | |||
q[4] = q3 ^ r3 ^ q7 ^ r7 ^ r4 ^ rotr32(q4 ^ r4); | |||
q[5] = q4 ^ r4 ^ r5 ^ rotr32(q5 ^ r5); | |||
q[6] = q5 ^ r5 ^ r6 ^ rotr32(q6 ^ r6); | |||
q[7] = q6 ^ r6 ^ r7 ^ rotr32(q7 ^ r7); | |||
} | |||
static void interleave_constant(uint64_t *out, const unsigned char *in) { | |||
uint32_t tmp_32_constant[16]; | |||
int i; | |||
br_range_dec32le(tmp_32_constant, 16, in); | |||
for (i = 0; i < 4; i++) { | |||
br_aes_ct64_interleave_in(&out[i], &out[i + 4], tmp_32_constant + (i << 2)); | |||
} | |||
br_aes_ct64_ortho(out); | |||
} | |||
static void interleave_constant32(uint32_t *out, const unsigned char *in) { | |||
int i; | |||
for (i = 0; i < 4; i++) { | |||
out[2 * i] = br_dec32le(in + 4 * i); | |||
out[2 * i + 1] = br_dec32le(in + 4 * i + 16); | |||
} | |||
br_aes_ct_ortho(out); | |||
} | |||
void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_tweak_constants( | |||
const unsigned char *pk_seed, const unsigned char *sk_seed, | |||
unsigned long long seed_length) { | |||
unsigned char buf[40 * 16]; | |||
int i; | |||
/* Use the standard constants to generate tweaked ones. */ | |||
memcpy((uint8_t *)tweaked512_rc64, (uint8_t *)haraka512_rc64, 40 * 16); | |||
/* Constants for sk.seed */ | |||
if (sk_seed != NULL) { | |||
PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka_S( | |||
buf, 40 * 16, sk_seed, seed_length); | |||
/* Interleave constants */ | |||
for (i = 0; i < 10; i++) { | |||
interleave_constant32(tweaked256_rc32_sseed[i], buf + 32 * i); | |||
} | |||
} | |||
/* Constants for pk.seed */ | |||
PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka_S( | |||
buf, 40 * 16, pk_seed, seed_length); | |||
for (i = 0; i < 10; i++) { | |||
interleave_constant32(tweaked256_rc32[i], buf + 32 * i); | |||
interleave_constant(tweaked512_rc64[i], buf + 64 * i); | |||
} | |||
} | |||
static void haraka_S_absorb(unsigned char *s, | |||
const unsigned char *m, unsigned long long mlen, | |||
unsigned char p) { | |||
unsigned long long i; | |||
unsigned char t[HARAKAS_RATE]; | |||
while (mlen >= HARAKAS_RATE) { | |||
/* XOR block to state */ | |||
for (i = 0; i < HARAKAS_RATE; ++i) { | |||
s[i] ^= m[i]; | |||
} | |||
PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka512_perm(s, s); | |||
mlen -= HARAKAS_RATE; | |||
m += HARAKAS_RATE; | |||
} | |||
for (i = 0; i < HARAKAS_RATE; ++i) { | |||
t[i] = 0; | |||
} | |||
for (i = 0; i < mlen; ++i) { | |||
t[i] = m[i]; | |||
} | |||
t[i] = p; | |||
t[HARAKAS_RATE - 1] |= 128; | |||
for (i = 0; i < HARAKAS_RATE; ++i) { | |||
s[i] ^= t[i]; | |||
} | |||
} | |||
static void haraka_S_squeezeblocks(unsigned char *h, unsigned long long nblocks, | |||
unsigned char *s) { | |||
while (nblocks > 0) { | |||
PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka512_perm(s, s); | |||
memcpy(h, s, HARAKAS_RATE); | |||
h += HARAKAS_RATE; | |||
nblocks--; | |||
} | |||
} | |||
void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka_S_inc_init(uint8_t *s_inc) { | |||
size_t i; | |||
for (i = 0; i < 64; i++) { | |||
s_inc[i] = 0; | |||
} | |||
s_inc[64] = 0; | |||
} | |||
void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka_S_inc_absorb(uint8_t *s_inc, const uint8_t *m, size_t mlen) { | |||
size_t i; | |||
/* Recall that s_inc[64] is the non-absorbed bytes xored into the state */ | |||
while (mlen + s_inc[64] >= HARAKAS_RATE) { | |||
for (i = 0; i < (size_t)(HARAKAS_RATE - s_inc[64]); i++) { | |||
/* Take the i'th byte from message | |||
xor with the s_inc[64] + i'th byte of the state */ | |||
s_inc[s_inc[64] + i] ^= m[i]; | |||
} | |||
mlen -= (size_t)(HARAKAS_RATE - s_inc[64]); | |||
m += HARAKAS_RATE - s_inc[64]; | |||
s_inc[64] = 0; | |||
PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka512_perm(s_inc, s_inc); | |||
} | |||
for (i = 0; i < mlen; i++) { | |||
s_inc[s_inc[64] + i] ^= m[i]; | |||
} | |||
s_inc[64] = (uint8_t)(mlen + s_inc[64]); | |||
} | |||
void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka_S_inc_finalize(uint8_t *s_inc) { | |||
/* After haraka_S_inc_absorb, we are guaranteed that s_inc[64] < HARAKAS_RATE, | |||
so we can always use one more byte for p in the current state. */ | |||
s_inc[s_inc[64]] ^= 0x1F; | |||
s_inc[HARAKAS_RATE - 1] ^= 128; | |||
s_inc[64] = 0; | |||
} | |||
void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka_S_inc_squeeze(uint8_t *out, size_t outlen, uint8_t *s_inc) { | |||
uint8_t i; | |||
/* First consume any bytes we still have sitting around */ | |||
for (i = 0; i < outlen && i < s_inc[64]; i++) { | |||
/* There are s_inc[64] bytes left, so r - s_inc[64] is the first | |||
available byte. We consume from there, i.e., up to r. */ | |||
out[i] = s_inc[(HARAKAS_RATE - s_inc[64] + i)]; | |||
} | |||
out += i; | |||
outlen -= i; | |||
s_inc[64] = (uint8_t)(s_inc[64] - i); | |||
/* Then squeeze the remaining necessary blocks */ | |||
while (outlen > 0) { | |||
PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka512_perm(s_inc, s_inc); | |||
for (i = 0; i < outlen && i < HARAKAS_RATE; i++) { | |||
out[i] = s_inc[i]; | |||
} | |||
out += i; | |||
outlen -= i; | |||
s_inc[64] = (uint8_t)(HARAKAS_RATE - i); | |||
} | |||
} | |||
void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka_S(unsigned char *out, unsigned long long outlen, const unsigned char *in, unsigned long long inlen) { | |||
unsigned long long i; | |||
unsigned char s[64]; | |||
unsigned char d[32]; | |||
for (i = 0; i < 64; i++) { | |||
s[i] = 0; | |||
} | |||
haraka_S_absorb(s, in, inlen, 0x1F); | |||
haraka_S_squeezeblocks(out, outlen / 32, s); | |||
out += (outlen / 32) * 32; | |||
if (outlen % 32) { | |||
haraka_S_squeezeblocks(d, 1, s); | |||
for (i = 0; i < outlen % 32; i++) { | |||
out[i] = d[i]; | |||
} | |||
} | |||
} | |||
void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka512_perm(unsigned char *out, const unsigned char *in) { | |||
uint32_t w[16]; | |||
uint64_t q[8], tmp_q; | |||
unsigned int i, j; | |||
br_range_dec32le(w, 16, in); | |||
for (i = 0; i < 4; i++) { | |||
br_aes_ct64_interleave_in(&q[i], &q[i + 4], w + (i << 2)); | |||
} | |||
br_aes_ct64_ortho(q); | |||
/* AES rounds */ | |||
for (i = 0; i < 5; i++) { | |||
for (j = 0; j < 2; j++) { | |||
br_aes_ct64_bitslice_Sbox(q); | |||
shift_rows(q); | |||
mix_columns(q); | |||
add_round_key(q, tweaked512_rc64[2 * i + j]); | |||
} | |||
/* Mix states */ | |||
for (j = 0; j < 8; j++) { | |||
tmp_q = q[j]; | |||
q[j] = (tmp_q & 0x0001000100010001) << 5 | | |||
(tmp_q & 0x0002000200020002) << 12 | | |||
(tmp_q & 0x0004000400040004) >> 1 | | |||
(tmp_q & 0x0008000800080008) << 6 | | |||
(tmp_q & 0x0020002000200020) << 9 | | |||
(tmp_q & 0x0040004000400040) >> 4 | | |||
(tmp_q & 0x0080008000800080) << 3 | | |||
(tmp_q & 0x2100210021002100) >> 5 | | |||
(tmp_q & 0x0210021002100210) << 2 | | |||
(tmp_q & 0x0800080008000800) << 4 | | |||
(tmp_q & 0x1000100010001000) >> 12 | | |||
(tmp_q & 0x4000400040004000) >> 10 | | |||
(tmp_q & 0x8400840084008400) >> 3; | |||
} | |||
} | |||
br_aes_ct64_ortho(q); | |||
for (i = 0; i < 4; i ++) { | |||
br_aes_ct64_interleave_out(w + (i << 2), q[i], q[i + 4]); | |||
} | |||
br_range_enc32le(out, w, 16); | |||
} | |||
void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka512(unsigned char *out, const unsigned char *in) { | |||
int i; | |||
unsigned char buf[64]; | |||
PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka512_perm(buf, in); | |||
/* Feed-forward */ | |||
for (i = 0; i < 64; i++) { | |||
buf[i] = buf[i] ^ in[i]; | |||
} | |||
/* Truncated */ | |||
memcpy(out, buf + 8, 8); | |||
memcpy(out + 8, buf + 24, 8); | |||
memcpy(out + 16, buf + 32, 8); | |||
memcpy(out + 24, buf + 48, 8); | |||
} | |||
void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka256(unsigned char *out, const unsigned char *in) { | |||
uint32_t q[8], tmp_q; | |||
int i, j; | |||
for (i = 0; i < 4; i++) { | |||
q[2 * i] = br_dec32le(in + 4 * i); | |||
q[2 * i + 1] = br_dec32le(in + 4 * i + 16); | |||
} | |||
br_aes_ct_ortho(q); | |||
/* AES rounds */ | |||
for (i = 0; i < 5; i++) { | |||
for (j = 0; j < 2; j++) { | |||
br_aes_ct_bitslice_Sbox(q); | |||
shift_rows32(q); | |||
mix_columns32(q); | |||
add_round_key32(q, tweaked256_rc32[2 * i + j]); | |||
} | |||
/* Mix states */ | |||
for (j = 0; j < 8; j++) { | |||
tmp_q = q[j]; | |||
q[j] = (tmp_q & 0x81818181) | | |||
(tmp_q & 0x02020202) << 1 | | |||
(tmp_q & 0x04040404) << 2 | | |||
(tmp_q & 0x08080808) << 3 | | |||
(tmp_q & 0x10101010) >> 3 | | |||
(tmp_q & 0x20202020) >> 2 | | |||
(tmp_q & 0x40404040) >> 1; | |||
} | |||
} | |||
br_aes_ct_ortho(q); | |||
for (i = 0; i < 4; i++) { | |||
br_enc32le(out + 4 * i, q[2 * i]); | |||
br_enc32le(out + 4 * i + 16, q[2 * i + 1]); | |||
} | |||
for (i = 0; i < 32; i++) { | |||
out[i] ^= in[i]; | |||
} | |||
} | |||
void PQCLEAN_SPHINCSHARAKA128FROBUST_CLEAN_haraka256_sk(unsigned char *out, const unsigned char *in) { | |||
uint32_t q[8], tmp_q; | |||
int i, j; | |||
for (i = 0; i < 4; i++) { | |||
q[2 * i] = br_dec32le(in + 4 * i); | |||
q[2 * i + 1] = br_dec32le(in + 4 * i + 16); | |||
} | |||
br_aes_ct_ortho(q); | |||
/* AES rounds */ | |||
for (i = 0; i < 5; i++) { | |||
for (j = 0; j < 2; j++) { | |||
br_aes_ct_bitslice_Sbox(q); | |||
shift_rows32(q); | |||
mix_columns32(q); | |||
add_round_key32(q, tweaked256_rc32_sseed[2 * i + j]); | |||
} | |||
/* Mix states */ | |||
for (j = 0; j < 8; j++) { | |||
tmp_q = q[j]; | |||
q[j] = (tmp_q & 0x81818181) | | |||
(tmp_q & 0x02020202) << 1 | | |||
(tmp_q & 0x04040404) << 2 | | |||
(tmp_q & 0x08080808) << 3 | | |||
(tmp_q & 0x10101010) >> 3 | | |||
(tmp_q & 0x20202020) >> 2 | | |||
(tmp_q & 0x40404040) >> 1; | |||
} | |||
} | |||
br_aes_ct_ortho(q); | |||
for (i = 0; i < 4; i++) { | |||
br_enc32le(out + 4 * i, q[2 * i]); | |||
br_enc32le(out + 4 * i + 16, q[2 * i + 1]); | |||
} | |||
for (i = 0; i < 32; i++) { | |||
out[i] ^= in[i]; | |||
} | |||
} |
@@ -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 |
@@ -0,0 +1,22 @@ | |||
#ifndef SPX_HASH_H | |||
#define SPX_HASH_H | |||
#include <stdint.h> | |||
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 |
@@ -0,0 +1,86 @@ | |||
#include <stdint.h> | |||
#include <string.h> | |||
#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); | |||
} |
@@ -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 |
@@ -0,0 +1,344 @@ | |||
#include <stddef.h> | |||
#include <stdint.h> | |||
#include <string.h> | |||
#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; | |||
} |
@@ -0,0 +1,22 @@ | |||
#ifndef SPX_THASH_H | |||
#define SPX_THASH_H | |||
#include <stdint.h> | |||
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 |
@@ -0,0 +1,88 @@ | |||
#include <stdint.h> | |||
#include <string.h> | |||
#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); | |||
} |
@@ -0,0 +1,192 @@ | |||
#include <stddef.h> | |||
#include <string.h> | |||
#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); | |||
} |
@@ -0,0 +1,60 @@ | |||
#ifndef SPX_UTILS_H | |||
#define SPX_UTILS_H | |||
#include "params.h" | |||
#include <stddef.h> | |||
#include <stdint.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); | |||
/** | |||
* 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 |
@@ -0,0 +1,161 @@ | |||
#include <stdint.h> | |||
#include <string.h> | |||
#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); | |||
} | |||
} |
@@ -0,0 +1,38 @@ | |||
#ifndef SPX_WOTS_H | |||
#define SPX_WOTS_H | |||
#include "params.h" | |||
#include <stdint.h> | |||
/** | |||
* 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 |
@@ -0,0 +1,27 @@ | |||
name: SPHINCS+ | |||
type: signature | |||
claimed-nist-level: 1 | |||
length-public-key: 32 | |||
length-secret-key: 64 | |||
length-signature: 16976 | |||
testvectors-sha256: b9ea5703411a79c215a2643862bf4924ff62eeec08a0d1e328e39f47417fec8f | |||
principal-submitter: Andreas Hülsing | |||
auxiliary-submitters: | |||
- Jean-Philippe Aumasson | |||
- Daniel J. Bernstein, | |||
- Christoph Dobraunig | |||
- Maria Eichlseder | |||
- Scott Fluhrer | |||
- Stefan-Lukas Gazdag | |||
- Panos Kampanakis | |||
- Stefan Kölbl | |||
- Tanja Lange | |||
- Martin M. Lauridsen | |||
- Florian Mendel | |||
- Ruben Niederhagen | |||
- Christian Rechberger | |||
- Joost Rijneveld | |||
- Peter Schwabe | |||
implementations: | |||
- name: clean | |||
version: https://github.com/sphincs/sphincsplus/commit/77755c94d0bc744478044d6efbb888dc13156441 |
@@ -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 | |||
<http://creativecommons.org/publicdomain/zero/1.0/> |
@@ -0,0 +1,20 @@ | |||
# This Makefile can be used with GNU Make or BSD Make | |||
LIB=libsphincs-haraka-128f-simple_clean.a | |||
HEADERS = params.h address.h wots.h utils.h fors.h api.h hash.h thash.h haraka.h | |||
OBJECTS = address.o wots.o utils.o fors.o sign.o hash_haraka.o thash_haraka_simple.o haraka.o | |||
CFLAGS=-O3 -Wall -Wconversion -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) | |||
all: $(LIB) | |||
%.o: %.c $(HEADERS) | |||
$(CC) $(CFLAGS) -c -o $@ $< | |||
$(LIB): $(OBJECTS) | |||
$(AR) -r $@ $(OBJECTS) | |||
clean: | |||
$(RM) $(OBJECTS) | |||
$(RM) $(LIB) |
@@ -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) |
@@ -0,0 +1,78 @@ | |||
#include <stdint.h> | |||
#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; | |||
} |
@@ -0,0 +1,50 @@ | |||
#ifndef SPX_ADDRESS_H | |||
#define SPX_ADDRESS_H | |||
#include <stdint.h> | |||
#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 |
@@ -0,0 +1,78 @@ | |||
#ifndef PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_API_H | |||
#define PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_API_H | |||
#include <stddef.h> | |||
#include <stdint.h> | |||
#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 |
@@ -0,0 +1,164 @@ | |||
#include <stdint.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#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); | |||
} |
@@ -0,0 +1,30 @@ | |||
#ifndef SPX_FORS_H | |||
#define SPX_FORS_H | |||
#include <stdint.h> | |||
#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 |
@@ -0,0 +1,965 @@ | |||
/* | |||
* Constant time implementation of the Haraka hash function. | |||
* | |||
* The bit-sliced implementation of the AES round functions are | |||
* based on the AES implementation in BearSSL written | |||
* by Thomas Pornin <pornin@bolet.org> | |||
*/ | |||
#include <stdint.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include "haraka.h" | |||
#define HARAKAS_RATE 32 | |||
static const uint64_t haraka512_rc64[10][8] = { | |||
{0x24cf0ab9086f628b, 0xbdd6eeecc83b8382, 0xd96fb0306cdad0a7, 0xaace082ac8f95f89, 0x449d8e8870d7041f, 0x49bb2f80b2b3e2f8, 0x0569ae98d93bb258, 0x23dc9691e7d6a4b1}, | |||
{0xd8ba10ede0fe5b6e, 0x7ecf7dbe424c7b8e, 0x6ea9949c6df62a31, 0xbf3f3c97ec9c313e, 0x241d03a196a1861e, 0xead3a51116e5a2ea, 0x77d479fcad9574e3, 0x18657a1af894b7a0}, | |||
{0x10671e1a7f595522, 0xd9a00ff675d28c7b, 0x2f1edf0d2b9ba661, 0xb8ff58b8e3de45f9, 0xee29261da9865c02, 0xd1532aa4b50bdf43, 0x8bf858159b231bb1, 0xdf17439d22d4f599}, | |||
{0xdd4b2f0870b918c0, 0x757a81f3b39b1bb6, 0x7a5c556898952e3f, 0x7dd70a16d915d87a, 0x3ae61971982b8301, 0xc3ab319e030412be, 0x17c0033ac094a8cb, 0x5a0630fc1a8dc4ef}, | |||
{0x17708988c1632f73, 0xf92ddae090b44f4f, 0x11ac0285c43aa314, 0x509059941936b8ba, 0xd03e152fa2ce9b69, 0x3fbcbcb63a32998b, 0x6204696d692254f7, 0x915542ed93ec59b4}, | |||
{0xf4ed94aa8879236e, 0xff6cb41cd38e03c0, 0x069b38602368aeab, 0x669495b820f0ddba, 0xf42013b1b8bf9e3d, 0xcf935efe6439734d, 0xbc1dcf42ca29e3f8, 0x7e6d3ed29f78ad67}, | |||
{0xf3b0f6837ffcddaa, 0x3a76faef934ddf41, 0xcec7ae583a9c8e35, 0xe4dd18c68f0260af, 0x2c0e5df1ad398eaa, 0x478df5236ae22e8c, 0xfb944c46fe865f39, 0xaa48f82f028132ba}, | |||
{0x231b9ae2b76aca77, 0x292a76a712db0b40, 0x5850625dc8134491, 0x73137dd469810fb5, 0x8a12a6a202a474fd, 0xd36fd9daa78bdb80, 0xb34c5e733505706f, 0xbaf1cdca818d9d96}, | |||
{0x2e99781335e8c641, 0xbddfe5cce47d560e, 0xf74e9bf32e5e040c, 0x1d7a709d65996be9, 0x670df36a9cf66cdd, 0xd05ef84a176a2875, 0x0f888e828cb1c44e, 0x1a79e9c9727b052c}, | |||
{0x83497348628d84de, 0x2e9387d51f22a754, 0xb000068da2f852d6, 0x378c9e1190fd6fe5, 0x870027c316de7293, 0xe51a9d4462e047bb, 0x90ecf7f8c6251195, 0x655953bfbed90a9c}, | |||
}; | |||
static uint64_t tweaked512_rc64[10][8]; | |||
static uint32_t tweaked256_rc32[10][8]; | |||
static uint32_t tweaked256_rc32_sseed[10][8]; | |||
static inline uint32_t br_dec32le(const unsigned char *src) { | |||
return (uint32_t)src[0] | |||
| ((uint32_t)src[1] << 8) | |||
| ((uint32_t)src[2] << 16) | |||
| ((uint32_t)src[3] << 24); | |||
} | |||
static void br_range_dec32le(uint32_t *v, size_t num, const unsigned char *src) { | |||
while (num-- > 0) { | |||
*v ++ = br_dec32le(src); | |||
src += 4; | |||
} | |||
} | |||
static inline void br_enc32le(unsigned char *dst, uint32_t x) { | |||
dst[0] = (unsigned char)x; | |||
dst[1] = (unsigned char)(x >> 8); | |||
dst[2] = (unsigned char)(x >> 16); | |||
dst[3] = (unsigned char)(x >> 24); | |||
} | |||
static void br_range_enc32le(unsigned char *dst, const uint32_t *v, size_t num) { | |||
while (num-- > 0) { | |||
br_enc32le(dst, *v ++); | |||
dst += 4; | |||
} | |||
} | |||
static void br_aes_ct64_bitslice_Sbox(uint64_t *q) { | |||
/* | |||
* This S-box implementation is a straightforward translation of | |||
* the circuit described by Boyar and Peralta in "A new | |||
* combinational logic minimization technique with applications | |||
* to cryptology" (https://eprint.iacr.org/2009/191.pdf). | |||
* | |||
* Note that variables x* (input) and s* (output) are numbered | |||
* in "reverse" order (x0 is the high bit, x7 is the low bit). | |||
*/ | |||
uint64_t x0, x1, x2, x3, x4, x5, x6, x7; | |||
uint64_t y1, y2, y3, y4, y5, y6, y7, y8, y9; | |||
uint64_t y10, y11, y12, y13, y14, y15, y16, y17, y18, y19; | |||
uint64_t y20, y21; | |||
uint64_t z0, z1, z2, z3, z4, z5, z6, z7, z8, z9; | |||
uint64_t z10, z11, z12, z13, z14, z15, z16, z17; | |||
uint64_t t0, t1, t2, t3, t4, t5, t6, t7, t8, t9; | |||
uint64_t t10, t11, t12, t13, t14, t15, t16, t17, t18, t19; | |||
uint64_t t20, t21, t22, t23, t24, t25, t26, t27, t28, t29; | |||
uint64_t t30, t31, t32, t33, t34, t35, t36, t37, t38, t39; | |||
uint64_t t40, t41, t42, t43, t44, t45, t46, t47, t48, t49; | |||
uint64_t t50, t51, t52, t53, t54, t55, t56, t57, t58, t59; | |||
uint64_t t60, t61, t62, t63, t64, t65, t66, t67; | |||
uint64_t s0, s1, s2, s3, s4, s5, s6, s7; | |||
x0 = q[7]; | |||
x1 = q[6]; | |||
x2 = q[5]; | |||
x3 = q[4]; | |||
x4 = q[3]; | |||
x5 = q[2]; | |||
x6 = q[1]; | |||
x7 = q[0]; | |||
/* | |||
* Top linear transformation. | |||
*/ | |||
y14 = x3 ^ x5; | |||
y13 = x0 ^ x6; | |||
y9 = x0 ^ x3; | |||
y8 = x0 ^ x5; | |||
t0 = x1 ^ x2; | |||
y1 = t0 ^ x7; | |||
y4 = y1 ^ x3; | |||
y12 = y13 ^ y14; | |||
y2 = y1 ^ x0; | |||
y5 = y1 ^ x6; | |||
y3 = y5 ^ y8; | |||
t1 = x4 ^ y12; | |||
y15 = t1 ^ x5; | |||
y20 = t1 ^ x1; | |||
y6 = y15 ^ x7; | |||
y10 = y15 ^ t0; | |||
y11 = y20 ^ y9; | |||
y7 = x7 ^ y11; | |||
y17 = y10 ^ y11; | |||
y19 = y10 ^ y8; | |||
y16 = t0 ^ y11; | |||
y21 = y13 ^ y16; | |||
y18 = x0 ^ y16; | |||
/* | |||
* Non-linear section. | |||
*/ | |||
t2 = y12 & y15; | |||
t3 = y3 & y6; | |||
t4 = t3 ^ t2; | |||
t5 = y4 & x7; | |||
t6 = t5 ^ t2; | |||
t7 = y13 & y16; | |||
t8 = y5 & y1; | |||
t9 = t8 ^ t7; | |||
t10 = y2 & y7; | |||
t11 = t10 ^ t7; | |||
t12 = y9 & y11; | |||
t13 = y14 & y17; | |||
t14 = t13 ^ t12; | |||
t15 = y8 & y10; | |||
t16 = t15 ^ t12; | |||
t17 = t4 ^ t14; | |||
t18 = t6 ^ t16; | |||
t19 = t9 ^ t14; | |||
t20 = t11 ^ t16; | |||
t21 = t17 ^ y20; | |||
t22 = t18 ^ y19; | |||
t23 = t19 ^ y21; | |||
t24 = t20 ^ y18; | |||
t25 = t21 ^ t22; | |||
t26 = t21 & t23; | |||
t27 = t24 ^ t26; | |||
t28 = t25 & t27; | |||
t29 = t28 ^ t22; | |||
t30 = t23 ^ t24; | |||
t31 = t22 ^ t26; | |||
t32 = t31 & t30; | |||
t33 = t32 ^ t24; | |||
t34 = t23 ^ t33; | |||
t35 = t27 ^ t33; | |||
t36 = t24 & t35; | |||
t37 = t36 ^ t34; | |||
t38 = t27 ^ t36; | |||
t39 = t29 & t38; | |||
t40 = t25 ^ t39; | |||
t41 = t40 ^ t37; | |||
t42 = t29 ^ t33; | |||
t43 = t29 ^ t40; | |||
t44 = t33 ^ t37; | |||
t45 = t42 ^ t41; | |||
z0 = t44 & y15; | |||
z1 = t37 & y6; | |||
z2 = t33 & x7; | |||
z3 = t43 & y16; | |||
z4 = t40 & y1; | |||
z5 = t29 & y7; | |||
z6 = t42 & y11; | |||
z7 = t45 & y17; | |||
z8 = t41 & y10; | |||
z9 = t44 & y12; | |||
z10 = t37 & y3; | |||
z11 = t33 & y4; | |||
z12 = t43 & y13; | |||
z13 = t40 & y5; | |||
z14 = t29 & y2; | |||
z15 = t42 & y9; | |||
z16 = t45 & y14; | |||
z17 = t41 & y8; | |||
/* | |||
* Bottom linear transformation. | |||
*/ | |||
t46 = z15 ^ z16; | |||
t47 = z10 ^ z11; | |||
t48 = z5 ^ z13; | |||
t49 = z9 ^ z10; | |||
t50 = z2 ^ z12; | |||
t51 = z2 ^ z5; | |||
t52 = z7 ^ z8; | |||
t53 = z0 ^ z3; | |||
t54 = z6 ^ z7; | |||
t55 = z16 ^ z17; | |||
t56 = z12 ^ t48; | |||
t57 = t50 ^ t53; | |||
t58 = z4 ^ t46; | |||
t59 = z3 ^ t54; | |||
t60 = t46 ^ t57; | |||
t61 = z14 ^ t57; | |||
t62 = t52 ^ t58; | |||
t63 = t49 ^ t58; | |||
t64 = z4 ^ t59; | |||
t65 = t61 ^ t62; | |||
t66 = z1 ^ t63; | |||
s0 = t59 ^ t63; | |||
s6 = t56 ^ ~t62; | |||
s7 = t48 ^ ~t60; | |||
t67 = t64 ^ t65; | |||
s3 = t53 ^ t66; | |||
s4 = t51 ^ t66; | |||
s5 = t47 ^ t65; | |||
s1 = t64 ^ ~s3; | |||
s2 = t55 ^ ~t67; | |||
q[7] = s0; | |||
q[6] = s1; | |||
q[5] = s2; | |||
q[4] = s3; | |||
q[3] = s4; | |||
q[2] = s5; | |||
q[1] = s6; | |||
q[0] = s7; | |||
} | |||
static void br_aes_ct_bitslice_Sbox(uint32_t *q) { | |||
/* | |||
* This S-box implementation is a straightforward translation of | |||
* the circuit described by Boyar and Peralta in "A new | |||
* combinational logic minimization technique with applications | |||
* to cryptology" (https://eprint.iacr.org/2009/191.pdf). | |||
* | |||
* Note that variables x* (input) and s* (output) are numbered | |||
* in "reverse" order (x0 is the high bit, x7 is the low bit). | |||
*/ | |||
uint32_t x0, x1, x2, x3, x4, x5, x6, x7; | |||
uint32_t y1, y2, y3, y4, y5, y6, y7, y8, y9; | |||
uint32_t y10, y11, y12, y13, y14, y15, y16, y17, y18, y19; | |||
uint32_t y20, y21; | |||
uint32_t z0, z1, z2, z3, z4, z5, z6, z7, z8, z9; | |||
uint32_t z10, z11, z12, z13, z14, z15, z16, z17; | |||
uint32_t t0, t1, t2, t3, t4, t5, t6, t7, t8, t9; | |||
uint32_t t10, t11, t12, t13, t14, t15, t16, t17, t18, t19; | |||
uint32_t t20, t21, t22, t23, t24, t25, t26, t27, t28, t29; | |||
uint32_t t30, t31, t32, t33, t34, t35, t36, t37, t38, t39; | |||
uint32_t t40, t41, t42, t43, t44, t45, t46, t47, t48, t49; | |||
uint32_t t50, t51, t52, t53, t54, t55, t56, t57, t58, t59; | |||
uint32_t t60, t61, t62, t63, t64, t65, t66, t67; | |||
uint32_t s0, s1, s2, s3, s4, s5, s6, s7; | |||
x0 = q[7]; | |||
x1 = q[6]; | |||
x2 = q[5]; | |||
x3 = q[4]; | |||
x4 = q[3]; | |||
x5 = q[2]; | |||
x6 = q[1]; | |||
x7 = q[0]; | |||
/* | |||
* Top linear transformation. | |||
*/ | |||
y14 = x3 ^ x5; | |||
y13 = x0 ^ x6; | |||
y9 = x0 ^ x3; | |||
y8 = x0 ^ x5; | |||
t0 = x1 ^ x2; | |||
y1 = t0 ^ x7; | |||
y4 = y1 ^ x3; | |||
y12 = y13 ^ y14; | |||
y2 = y1 ^ x0; | |||
y5 = y1 ^ x6; | |||
y3 = y5 ^ y8; | |||
t1 = x4 ^ y12; | |||
y15 = t1 ^ x5; | |||
y20 = t1 ^ x1; | |||
y6 = y15 ^ x7; | |||
y10 = y15 ^ t0; | |||
y11 = y20 ^ y9; | |||
y7 = x7 ^ y11; | |||
y17 = y10 ^ y11; | |||
y19 = y10 ^ y8; | |||
y16 = t0 ^ y11; | |||
y21 = y13 ^ y16; | |||
y18 = x0 ^ y16; | |||
/* | |||
* Non-linear section. | |||
*/ | |||
t2 = y12 & y15; | |||
t3 = y3 & y6; | |||
t4 = t3 ^ t2; | |||
t5 = y4 & x7; | |||
t6 = t5 ^ t2; | |||
t7 = y13 & y16; | |||
t8 = y5 & y1; | |||
t9 = t8 ^ t7; | |||
t10 = y2 & y7; | |||
t11 = t10 ^ t7; | |||
t12 = y9 & y11; | |||
t13 = y14 & y17; | |||
t14 = t13 ^ t12; | |||
t15 = y8 & y10; | |||
t16 = t15 ^ t12; | |||
t17 = t4 ^ t14; | |||
t18 = t6 ^ t16; | |||
t19 = t9 ^ t14; | |||
t20 = t11 ^ t16; | |||
t21 = t17 ^ y20; | |||
t22 = t18 ^ y19; | |||
t23 = t19 ^ y21; | |||
t24 = t20 ^ y18; | |||
t25 = t21 ^ t22; | |||
t26 = t21 & t23; | |||
t27 = t24 ^ t26; | |||
t28 = t25 & t27; | |||
t29 = t28 ^ t22; | |||
t30 = t23 ^ t24; | |||
t31 = t22 ^ t26; | |||
t32 = t31 & t30; | |||
t33 = t32 ^ t24; | |||
t34 = t23 ^ t33; | |||
t35 = t27 ^ t33; | |||
t36 = t24 & t35; | |||
t37 = t36 ^ t34; | |||
t38 = t27 ^ t36; | |||
t39 = t29 & t38; | |||
t40 = t25 ^ t39; | |||
t41 = t40 ^ t37; | |||
t42 = t29 ^ t33; | |||
t43 = t29 ^ t40; | |||
t44 = t33 ^ t37; | |||
t45 = t42 ^ t41; | |||
z0 = t44 & y15; | |||
z1 = t37 & y6; | |||
z2 = t33 & x7; | |||
z3 = t43 & y16; | |||
z4 = t40 & y1; | |||
z5 = t29 & y7; | |||
z6 = t42 & y11; | |||
z7 = t45 & y17; | |||
z8 = t41 & y10; | |||
z9 = t44 & y12; | |||
z10 = t37 & y3; | |||
z11 = t33 & y4; | |||
z12 = t43 & y13; | |||
z13 = t40 & y5; | |||
z14 = t29 & y2; | |||
z15 = t42 & y9; | |||
z16 = t45 & y14; | |||
z17 = t41 & y8; | |||
/* | |||
* Bottom linear transformation. | |||
*/ | |||
t46 = z15 ^ z16; | |||
t47 = z10 ^ z11; | |||
t48 = z5 ^ z13; | |||
t49 = z9 ^ z10; | |||
t50 = z2 ^ z12; | |||
t51 = z2 ^ z5; | |||
t52 = z7 ^ z8; | |||
t53 = z0 ^ z3; | |||
t54 = z6 ^ z7; | |||
t55 = z16 ^ z17; | |||
t56 = z12 ^ t48; | |||
t57 = t50 ^ t53; | |||
t58 = z4 ^ t46; | |||
t59 = z3 ^ t54; | |||
t60 = t46 ^ t57; | |||
t61 = z14 ^ t57; | |||
t62 = t52 ^ t58; | |||
t63 = t49 ^ t58; | |||
t64 = z4 ^ t59; | |||
t65 = t61 ^ t62; | |||
t66 = z1 ^ t63; | |||
s0 = t59 ^ t63; | |||
s6 = t56 ^ ~t62; | |||
s7 = t48 ^ ~t60; | |||
t67 = t64 ^ t65; | |||
s3 = t53 ^ t66; | |||
s4 = t51 ^ t66; | |||
s5 = t47 ^ t65; | |||
s1 = t64 ^ ~s3; | |||
s2 = t55 ^ ~t67; | |||
q[7] = s0; | |||
q[6] = s1; | |||
q[5] = s2; | |||
q[4] = s3; | |||
q[3] = s4; | |||
q[2] = s5; | |||
q[1] = s6; | |||
q[0] = s7; | |||
} | |||
static void br_aes_ct_ortho(uint32_t *q) { | |||
#define SWAPN_32(cl, ch, s, x, y) do { \ | |||
uint32_t a, b; \ | |||
a = (x); \ | |||
b = (y); \ | |||
(x) = (a & (uint32_t)(cl)) | ((b & (uint32_t)(cl)) << (s)); \ | |||
(y) = ((a & (uint32_t)(ch)) >> (s)) | (b & (uint32_t)(ch)); \ | |||
} while (0) | |||
#define SWAP2_32(x, y) SWAPN_32(0x55555555, 0xAAAAAAAA, 1, x, y) | |||
#define SWAP4_32(x, y) SWAPN_32(0x33333333, 0xCCCCCCCC, 2, x, y) | |||
#define SWAP8_32(x, y) SWAPN_32(0x0F0F0F0F, 0xF0F0F0F0, 4, x, y) | |||
SWAP2_32(q[0], q[1]); | |||
SWAP2_32(q[2], q[3]); | |||
SWAP2_32(q[4], q[5]); | |||
SWAP2_32(q[6], q[7]); | |||
SWAP4_32(q[0], q[2]); | |||
SWAP4_32(q[1], q[3]); | |||
SWAP4_32(q[4], q[6]); | |||
SWAP4_32(q[5], q[7]); | |||
SWAP8_32(q[0], q[4]); | |||
SWAP8_32(q[1], q[5]); | |||
SWAP8_32(q[2], q[6]); | |||
SWAP8_32(q[3], q[7]); | |||
} | |||
static inline void add_round_key32(uint32_t *q, const uint32_t *sk) { | |||
q[0] ^= sk[0]; | |||
q[1] ^= sk[1]; | |||
q[2] ^= sk[2]; | |||
q[3] ^= sk[3]; | |||
q[4] ^= sk[4]; | |||
q[5] ^= sk[5]; | |||
q[6] ^= sk[6]; | |||
q[7] ^= sk[7]; | |||
} | |||
static inline void shift_rows32(uint32_t *q) { | |||
int i; | |||
for (i = 0; i < 8; i++) { | |||
uint32_t x; | |||
x = q[i]; | |||
q[i] = (x & 0x000000FF) | |||
| ((x & 0x0000FC00) >> 2) | ((x & 0x00000300) << 6) | |||
| ((x & 0x00F00000) >> 4) | ((x & 0x000F0000) << 4) | |||
| ((x & 0xC0000000) >> 6) | ((x & 0x3F000000) << 2); | |||
} | |||
} | |||
static inline uint32_t rotr16(uint32_t x) { | |||
return (x << 16) | (x >> 16); | |||
} | |||
static inline void mix_columns32(uint32_t *q) { | |||
uint32_t q0, q1, q2, q3, q4, q5, q6, q7; | |||
uint32_t r0, r1, r2, r3, r4, r5, r6, r7; | |||
q0 = q[0]; | |||
q1 = q[1]; | |||
q2 = q[2]; | |||
q3 = q[3]; | |||
q4 = q[4]; | |||
q5 = q[5]; | |||
q6 = q[6]; | |||
q7 = q[7]; | |||
r0 = (q0 >> 8) | (q0 << 24); | |||
r1 = (q1 >> 8) | (q1 << 24); | |||
r2 = (q2 >> 8) | (q2 << 24); | |||
r3 = (q3 >> 8) | (q3 << 24); | |||
r4 = (q4 >> 8) | (q4 << 24); | |||
r5 = (q5 >> 8) | (q5 << 24); | |||
r6 = (q6 >> 8) | (q6 << 24); | |||
r7 = (q7 >> 8) | (q7 << 24); | |||
q[0] = q7 ^ r7 ^ r0 ^ rotr16(q0 ^ r0); | |||
q[1] = q0 ^ r0 ^ q7 ^ r7 ^ r1 ^ rotr16(q1 ^ r1); | |||
q[2] = q1 ^ r1 ^ r2 ^ rotr16(q2 ^ r2); | |||
q[3] = q2 ^ r2 ^ q7 ^ r7 ^ r3 ^ rotr16(q3 ^ r3); | |||
q[4] = q3 ^ r3 ^ q7 ^ r7 ^ r4 ^ rotr16(q4 ^ r4); | |||
q[5] = q4 ^ r4 ^ r5 ^ rotr16(q5 ^ r5); | |||
q[6] = q5 ^ r5 ^ r6 ^ rotr16(q6 ^ r6); | |||
q[7] = q6 ^ r6 ^ r7 ^ rotr16(q7 ^ r7); | |||
} | |||
static void br_aes_ct64_ortho(uint64_t *q) { | |||
#define SWAPN(cl, ch, s, x, y) do { \ | |||
uint64_t a, b; \ | |||
a = (x); \ | |||
b = (y); \ | |||
(x) = (a & (uint64_t)(cl)) | ((b & (uint64_t)(cl)) << (s)); \ | |||
(y) = ((a & (uint64_t)(ch)) >> (s)) | (b & (uint64_t)(ch)); \ | |||
} while (0) | |||
#define SWAP2(x, y) SWAPN(0x5555555555555555, 0xAAAAAAAAAAAAAAAA, 1, x, y) | |||
#define SWAP4(x, y) SWAPN(0x3333333333333333, 0xCCCCCCCCCCCCCCCC, 2, x, y) | |||
#define SWAP8(x, y) SWAPN(0x0F0F0F0F0F0F0F0F, 0xF0F0F0F0F0F0F0F0, 4, x, y) | |||
SWAP2(q[0], q[1]); | |||
SWAP2(q[2], q[3]); | |||
SWAP2(q[4], q[5]); | |||
SWAP2(q[6], q[7]); | |||
SWAP4(q[0], q[2]); | |||
SWAP4(q[1], q[3]); | |||
SWAP4(q[4], q[6]); | |||
SWAP4(q[5], q[7]); | |||
SWAP8(q[0], q[4]); | |||
SWAP8(q[1], q[5]); | |||
SWAP8(q[2], q[6]); | |||
SWAP8(q[3], q[7]); | |||
} | |||
static void br_aes_ct64_interleave_in(uint64_t *q0, uint64_t *q1, const uint32_t *w) { | |||
uint64_t x0, x1, x2, x3; | |||
x0 = w[0]; | |||
x1 = w[1]; | |||
x2 = w[2]; | |||
x3 = w[3]; | |||
x0 |= (x0 << 16); | |||
x1 |= (x1 << 16); | |||
x2 |= (x2 << 16); | |||
x3 |= (x3 << 16); | |||
x0 &= (uint64_t)0x0000FFFF0000FFFF; | |||
x1 &= (uint64_t)0x0000FFFF0000FFFF; | |||
x2 &= (uint64_t)0x0000FFFF0000FFFF; | |||
x3 &= (uint64_t)0x0000FFFF0000FFFF; | |||
x0 |= (x0 << 8); | |||
x1 |= (x1 << 8); | |||
x2 |= (x2 << 8); | |||
x3 |= (x3 << 8); | |||
x0 &= (uint64_t)0x00FF00FF00FF00FF; | |||
x1 &= (uint64_t)0x00FF00FF00FF00FF; | |||
x2 &= (uint64_t)0x00FF00FF00FF00FF; | |||
x3 &= (uint64_t)0x00FF00FF00FF00FF; | |||
*q0 = x0 | (x2 << 8); | |||
*q1 = x1 | (x3 << 8); | |||
} | |||
static void br_aes_ct64_interleave_out(uint32_t *w, uint64_t q0, uint64_t q1) { | |||
uint64_t x0, x1, x2, x3; | |||
x0 = q0 & (uint64_t)0x00FF00FF00FF00FF; | |||
x1 = q1 & (uint64_t)0x00FF00FF00FF00FF; | |||
x2 = (q0 >> 8) & (uint64_t)0x00FF00FF00FF00FF; | |||
x3 = (q1 >> 8) & (uint64_t)0x00FF00FF00FF00FF; | |||
x0 |= (x0 >> 8); | |||
x1 |= (x1 >> 8); | |||
x2 |= (x2 >> 8); | |||
x3 |= (x3 >> 8); | |||
x0 &= (uint64_t)0x0000FFFF0000FFFF; | |||
x1 &= (uint64_t)0x0000FFFF0000FFFF; | |||
x2 &= (uint64_t)0x0000FFFF0000FFFF; | |||
x3 &= (uint64_t)0x0000FFFF0000FFFF; | |||
w[0] = (uint32_t)x0 | (uint32_t)(x0 >> 16); | |||
w[1] = (uint32_t)x1 | (uint32_t)(x1 >> 16); | |||
w[2] = (uint32_t)x2 | (uint32_t)(x2 >> 16); | |||
w[3] = (uint32_t)x3 | (uint32_t)(x3 >> 16); | |||
} | |||
static inline void add_round_key(uint64_t *q, const uint64_t *sk) { | |||
q[0] ^= sk[0]; | |||
q[1] ^= sk[1]; | |||
q[2] ^= sk[2]; | |||
q[3] ^= sk[3]; | |||
q[4] ^= sk[4]; | |||
q[5] ^= sk[5]; | |||
q[6] ^= sk[6]; | |||
q[7] ^= sk[7]; | |||
} | |||
static inline void shift_rows(uint64_t *q) { | |||
int i; | |||
for (i = 0; i < 8; i++) { | |||
uint64_t x; | |||
x = q[i]; | |||
q[i] = (x & (uint64_t)0x000000000000FFFF) | |||
| ((x & (uint64_t)0x00000000FFF00000) >> 4) | |||
| ((x & (uint64_t)0x00000000000F0000) << 12) | |||
| ((x & (uint64_t)0x0000FF0000000000) >> 8) | |||
| ((x & (uint64_t)0x000000FF00000000) << 8) | |||
| ((x & (uint64_t)0xF000000000000000) >> 12) | |||
| ((x & (uint64_t)0x0FFF000000000000) << 4); | |||
} | |||
} | |||
static inline uint64_t rotr32(uint64_t x) { | |||
return (x << 32) | (x >> 32); | |||
} | |||
static inline void mix_columns(uint64_t *q) { | |||
uint64_t q0, q1, q2, q3, q4, q5, q6, q7; | |||
uint64_t r0, r1, r2, r3, r4, r5, r6, r7; | |||
q0 = q[0]; | |||
q1 = q[1]; | |||
q2 = q[2]; | |||
q3 = q[3]; | |||
q4 = q[4]; | |||
q5 = q[5]; | |||
q6 = q[6]; | |||
q7 = q[7]; | |||
r0 = (q0 >> 16) | (q0 << 48); | |||
r1 = (q1 >> 16) | (q1 << 48); | |||
r2 = (q2 >> 16) | (q2 << 48); | |||
r3 = (q3 >> 16) | (q3 << 48); | |||
r4 = (q4 >> 16) | (q4 << 48); | |||
r5 = (q5 >> 16) | (q5 << 48); | |||
r6 = (q6 >> 16) | (q6 << 48); | |||
r7 = (q7 >> 16) | (q7 << 48); | |||
q[0] = q7 ^ r7 ^ r0 ^ rotr32(q0 ^ r0); | |||
q[1] = q0 ^ r0 ^ q7 ^ r7 ^ r1 ^ rotr32(q1 ^ r1); | |||
q[2] = q1 ^ r1 ^ r2 ^ rotr32(q2 ^ r2); | |||
q[3] = q2 ^ r2 ^ q7 ^ r7 ^ r3 ^ rotr32(q3 ^ r3); | |||
q[4] = q3 ^ r3 ^ q7 ^ r7 ^ r4 ^ rotr32(q4 ^ r4); | |||
q[5] = q4 ^ r4 ^ r5 ^ rotr32(q5 ^ r5); | |||
q[6] = q5 ^ r5 ^ r6 ^ rotr32(q6 ^ r6); | |||
q[7] = q6 ^ r6 ^ r7 ^ rotr32(q7 ^ r7); | |||
} | |||
static void interleave_constant(uint64_t *out, const unsigned char *in) { | |||
uint32_t tmp_32_constant[16]; | |||
int i; | |||
br_range_dec32le(tmp_32_constant, 16, in); | |||
for (i = 0; i < 4; i++) { | |||
br_aes_ct64_interleave_in(&out[i], &out[i + 4], tmp_32_constant + (i << 2)); | |||
} | |||
br_aes_ct64_ortho(out); | |||
} | |||
static void interleave_constant32(uint32_t *out, const unsigned char *in) { | |||
int i; | |||
for (i = 0; i < 4; i++) { | |||
out[2 * i] = br_dec32le(in + 4 * i); | |||
out[2 * i + 1] = br_dec32le(in + 4 * i + 16); | |||
} | |||
br_aes_ct_ortho(out); | |||
} | |||
void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_tweak_constants( | |||
const unsigned char *pk_seed, const unsigned char *sk_seed, | |||
unsigned long long seed_length) { | |||
unsigned char buf[40 * 16]; | |||
int i; | |||
/* Use the standard constants to generate tweaked ones. */ | |||
memcpy((uint8_t *)tweaked512_rc64, (uint8_t *)haraka512_rc64, 40 * 16); | |||
/* Constants for sk.seed */ | |||
if (sk_seed != NULL) { | |||
PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka_S( | |||
buf, 40 * 16, sk_seed, seed_length); | |||
/* Interleave constants */ | |||
for (i = 0; i < 10; i++) { | |||
interleave_constant32(tweaked256_rc32_sseed[i], buf + 32 * i); | |||
} | |||
} | |||
/* Constants for pk.seed */ | |||
PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka_S( | |||
buf, 40 * 16, pk_seed, seed_length); | |||
for (i = 0; i < 10; i++) { | |||
interleave_constant32(tweaked256_rc32[i], buf + 32 * i); | |||
interleave_constant(tweaked512_rc64[i], buf + 64 * i); | |||
} | |||
} | |||
static void haraka_S_absorb(unsigned char *s, | |||
const unsigned char *m, unsigned long long mlen, | |||
unsigned char p) { | |||
unsigned long long i; | |||
unsigned char t[HARAKAS_RATE]; | |||
while (mlen >= HARAKAS_RATE) { | |||
/* XOR block to state */ | |||
for (i = 0; i < HARAKAS_RATE; ++i) { | |||
s[i] ^= m[i]; | |||
} | |||
PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka512_perm(s, s); | |||
mlen -= HARAKAS_RATE; | |||
m += HARAKAS_RATE; | |||
} | |||
for (i = 0; i < HARAKAS_RATE; ++i) { | |||
t[i] = 0; | |||
} | |||
for (i = 0; i < mlen; ++i) { | |||
t[i] = m[i]; | |||
} | |||
t[i] = p; | |||
t[HARAKAS_RATE - 1] |= 128; | |||
for (i = 0; i < HARAKAS_RATE; ++i) { | |||
s[i] ^= t[i]; | |||
} | |||
} | |||
static void haraka_S_squeezeblocks(unsigned char *h, unsigned long long nblocks, | |||
unsigned char *s) { | |||
while (nblocks > 0) { | |||
PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka512_perm(s, s); | |||
memcpy(h, s, HARAKAS_RATE); | |||
h += HARAKAS_RATE; | |||
nblocks--; | |||
} | |||
} | |||
void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka_S_inc_init(uint8_t *s_inc) { | |||
size_t i; | |||
for (i = 0; i < 64; i++) { | |||
s_inc[i] = 0; | |||
} | |||
s_inc[64] = 0; | |||
} | |||
void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka_S_inc_absorb(uint8_t *s_inc, const uint8_t *m, size_t mlen) { | |||
size_t i; | |||
/* Recall that s_inc[64] is the non-absorbed bytes xored into the state */ | |||
while (mlen + s_inc[64] >= HARAKAS_RATE) { | |||
for (i = 0; i < (size_t)(HARAKAS_RATE - s_inc[64]); i++) { | |||
/* Take the i'th byte from message | |||
xor with the s_inc[64] + i'th byte of the state */ | |||
s_inc[s_inc[64] + i] ^= m[i]; | |||
} | |||
mlen -= (size_t)(HARAKAS_RATE - s_inc[64]); | |||
m += HARAKAS_RATE - s_inc[64]; | |||
s_inc[64] = 0; | |||
PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka512_perm(s_inc, s_inc); | |||
} | |||
for (i = 0; i < mlen; i++) { | |||
s_inc[s_inc[64] + i] ^= m[i]; | |||
} | |||
s_inc[64] = (uint8_t)(mlen + s_inc[64]); | |||
} | |||
void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka_S_inc_finalize(uint8_t *s_inc) { | |||
/* After haraka_S_inc_absorb, we are guaranteed that s_inc[64] < HARAKAS_RATE, | |||
so we can always use one more byte for p in the current state. */ | |||
s_inc[s_inc[64]] ^= 0x1F; | |||
s_inc[HARAKAS_RATE - 1] ^= 128; | |||
s_inc[64] = 0; | |||
} | |||
void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka_S_inc_squeeze(uint8_t *out, size_t outlen, uint8_t *s_inc) { | |||
uint8_t i; | |||
/* First consume any bytes we still have sitting around */ | |||
for (i = 0; i < outlen && i < s_inc[64]; i++) { | |||
/* There are s_inc[64] bytes left, so r - s_inc[64] is the first | |||
available byte. We consume from there, i.e., up to r. */ | |||
out[i] = s_inc[(HARAKAS_RATE - s_inc[64] + i)]; | |||
} | |||
out += i; | |||
outlen -= i; | |||
s_inc[64] = (uint8_t)(s_inc[64] - i); | |||
/* Then squeeze the remaining necessary blocks */ | |||
while (outlen > 0) { | |||
PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka512_perm(s_inc, s_inc); | |||
for (i = 0; i < outlen && i < HARAKAS_RATE; i++) { | |||
out[i] = s_inc[i]; | |||
} | |||
out += i; | |||
outlen -= i; | |||
s_inc[64] = (uint8_t)(HARAKAS_RATE - i); | |||
} | |||
} | |||
void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka_S(unsigned char *out, unsigned long long outlen, const unsigned char *in, unsigned long long inlen) { | |||
unsigned long long i; | |||
unsigned char s[64]; | |||
unsigned char d[32]; | |||
for (i = 0; i < 64; i++) { | |||
s[i] = 0; | |||
} | |||
haraka_S_absorb(s, in, inlen, 0x1F); | |||
haraka_S_squeezeblocks(out, outlen / 32, s); | |||
out += (outlen / 32) * 32; | |||
if (outlen % 32) { | |||
haraka_S_squeezeblocks(d, 1, s); | |||
for (i = 0; i < outlen % 32; i++) { | |||
out[i] = d[i]; | |||
} | |||
} | |||
} | |||
void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka512_perm(unsigned char *out, const unsigned char *in) { | |||
uint32_t w[16]; | |||
uint64_t q[8], tmp_q; | |||
unsigned int i, j; | |||
br_range_dec32le(w, 16, in); | |||
for (i = 0; i < 4; i++) { | |||
br_aes_ct64_interleave_in(&q[i], &q[i + 4], w + (i << 2)); | |||
} | |||
br_aes_ct64_ortho(q); | |||
/* AES rounds */ | |||
for (i = 0; i < 5; i++) { | |||
for (j = 0; j < 2; j++) { | |||
br_aes_ct64_bitslice_Sbox(q); | |||
shift_rows(q); | |||
mix_columns(q); | |||
add_round_key(q, tweaked512_rc64[2 * i + j]); | |||
} | |||
/* Mix states */ | |||
for (j = 0; j < 8; j++) { | |||
tmp_q = q[j]; | |||
q[j] = (tmp_q & 0x0001000100010001) << 5 | | |||
(tmp_q & 0x0002000200020002) << 12 | | |||
(tmp_q & 0x0004000400040004) >> 1 | | |||
(tmp_q & 0x0008000800080008) << 6 | | |||
(tmp_q & 0x0020002000200020) << 9 | | |||
(tmp_q & 0x0040004000400040) >> 4 | | |||
(tmp_q & 0x0080008000800080) << 3 | | |||
(tmp_q & 0x2100210021002100) >> 5 | | |||
(tmp_q & 0x0210021002100210) << 2 | | |||
(tmp_q & 0x0800080008000800) << 4 | | |||
(tmp_q & 0x1000100010001000) >> 12 | | |||
(tmp_q & 0x4000400040004000) >> 10 | | |||
(tmp_q & 0x8400840084008400) >> 3; | |||
} | |||
} | |||
br_aes_ct64_ortho(q); | |||
for (i = 0; i < 4; i ++) { | |||
br_aes_ct64_interleave_out(w + (i << 2), q[i], q[i + 4]); | |||
} | |||
br_range_enc32le(out, w, 16); | |||
} | |||
void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka512(unsigned char *out, const unsigned char *in) { | |||
int i; | |||
unsigned char buf[64]; | |||
PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka512_perm(buf, in); | |||
/* Feed-forward */ | |||
for (i = 0; i < 64; i++) { | |||
buf[i] = buf[i] ^ in[i]; | |||
} | |||
/* Truncated */ | |||
memcpy(out, buf + 8, 8); | |||
memcpy(out + 8, buf + 24, 8); | |||
memcpy(out + 16, buf + 32, 8); | |||
memcpy(out + 24, buf + 48, 8); | |||
} | |||
void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka256(unsigned char *out, const unsigned char *in) { | |||
uint32_t q[8], tmp_q; | |||
int i, j; | |||
for (i = 0; i < 4; i++) { | |||
q[2 * i] = br_dec32le(in + 4 * i); | |||
q[2 * i + 1] = br_dec32le(in + 4 * i + 16); | |||
} | |||
br_aes_ct_ortho(q); | |||
/* AES rounds */ | |||
for (i = 0; i < 5; i++) { | |||
for (j = 0; j < 2; j++) { | |||
br_aes_ct_bitslice_Sbox(q); | |||
shift_rows32(q); | |||
mix_columns32(q); | |||
add_round_key32(q, tweaked256_rc32[2 * i + j]); | |||
} | |||
/* Mix states */ | |||
for (j = 0; j < 8; j++) { | |||
tmp_q = q[j]; | |||
q[j] = (tmp_q & 0x81818181) | | |||
(tmp_q & 0x02020202) << 1 | | |||
(tmp_q & 0x04040404) << 2 | | |||
(tmp_q & 0x08080808) << 3 | | |||
(tmp_q & 0x10101010) >> 3 | | |||
(tmp_q & 0x20202020) >> 2 | | |||
(tmp_q & 0x40404040) >> 1; | |||
} | |||
} | |||
br_aes_ct_ortho(q); | |||
for (i = 0; i < 4; i++) { | |||
br_enc32le(out + 4 * i, q[2 * i]); | |||
br_enc32le(out + 4 * i + 16, q[2 * i + 1]); | |||
} | |||
for (i = 0; i < 32; i++) { | |||
out[i] ^= in[i]; | |||
} | |||
} | |||
void PQCLEAN_SPHINCSHARAKA128FSIMPLE_CLEAN_haraka256_sk(unsigned char *out, const unsigned char *in) { | |||
uint32_t q[8], tmp_q; | |||
int i, j; | |||
for (i = 0; i < 4; i++) { | |||
q[2 * i] = br_dec32le(in + 4 * i); | |||
q[2 * i + 1] = br_dec32le(in + 4 * i + 16); | |||
} | |||
br_aes_ct_ortho(q); | |||
/* AES rounds */ | |||
for (i = 0; i < 5; i++) { | |||
for (j = 0; j < 2; j++) { | |||
br_aes_ct_bitslice_Sbox(q); | |||
shift_rows32(q); | |||
mix_columns32(q); | |||
add_round_key32(q, tweaked256_rc32_sseed[2 * i + j]); | |||
} | |||
/* Mix states */ | |||
for (j = 0; j < 8; j++) { | |||
tmp_q = q[j]; | |||
q[j] = (tmp_q & 0x81818181) | | |||
(tmp_q & 0x02020202) << 1 | | |||
(tmp_q & 0x04040404) << 2 | | |||
(tmp_q & 0x08080808) << 3 | | |||
(tmp_q & 0x10101010) >> 3 | | |||
(tmp_q & 0x20202020) >> 2 | | |||
(tmp_q & 0x40404040) >> 1; | |||
} | |||
} | |||
br_aes_ct_ortho(q); | |||
for (i = 0; i < 4; i++) { | |||
br_enc32le(out + 4 * i, q[2 * i]); | |||
br_enc32le(out + 4 * i + 16, q[2 * i + 1]); | |||
} | |||
for (i = 0; i < 32; i++) { | |||
out[i] ^= in[i]; | |||
} | |||
} |
@@ -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 |
@@ -0,0 +1,22 @@ | |||
#ifndef SPX_HASH_H | |||
#define SPX_HASH_H | |||
#include <stdint.h> | |||
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 |
@@ -0,0 +1,86 @@ | |||
#include <stdint.h> | |||
#include <string.h> | |||
#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); | |||
} |
@@ -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 |
@@ -0,0 +1,344 @@ | |||
#include <stddef.h> | |||
#include <stdint.h> | |||
#include <string.h> | |||
#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; | |||
} |
@@ -0,0 +1,22 @@ | |||
#ifndef SPX_THASH_H | |||
#define SPX_THASH_H | |||
#include <stdint.h> | |||
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 |
@@ -0,0 +1,78 @@ | |||
#include <stdint.h> | |||
#include <string.h> | |||
#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); | |||
} |
@@ -0,0 +1,192 @@ | |||
#include <stddef.h> | |||
#include <string.h> | |||
#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); | |||
} |
@@ -0,0 +1,60 @@ | |||
#ifndef SPX_UTILS_H | |||
#define SPX_UTILS_H | |||
#include "params.h" | |||
#include <stddef.h> | |||
#include <stdint.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); | |||
/** | |||
* 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 |
@@ -0,0 +1,161 @@ | |||
#include <stdint.h> | |||
#include <string.h> | |||
#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); | |||
} | |||
} |
@@ -0,0 +1,38 @@ | |||
#ifndef SPX_WOTS_H | |||
#define SPX_WOTS_H | |||
#include "params.h" | |||
#include <stdint.h> | |||
/** | |||
* 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 |
@@ -0,0 +1,27 @@ | |||
name: SPHINCS+ | |||
type: signature | |||
claimed-nist-level: 1 | |||
length-public-key: 32 | |||
length-secret-key: 64 | |||
length-signature: 8080 | |||
testvectors-sha256: a7057ca5ce0d7f01d1c1aabe474f8449796b051becbc8b148a78c84893193fcf | |||
principal-submitter: Andreas Hülsing | |||
auxiliary-submitters: | |||
- Jean-Philippe Aumasson | |||
- Daniel J. Bernstein, | |||
- Christoph Dobraunig | |||
- Maria Eichlseder | |||
- Scott Fluhrer | |||
- Stefan-Lukas Gazdag | |||
- Panos Kampanakis | |||
- Stefan Kölbl | |||
- Tanja Lange | |||
- Martin M. Lauridsen | |||
- Florian Mendel | |||
- Ruben Niederhagen | |||
- Christian Rechberger | |||
- Joost Rijneveld | |||
- Peter Schwabe | |||
implementations: | |||
- name: clean | |||
version: https://github.com/sphincs/sphincsplus/commit/77755c94d0bc744478044d6efbb888dc13156441 |
@@ -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 | |||
<http://creativecommons.org/publicdomain/zero/1.0/> |
@@ -0,0 +1,20 @@ | |||
# This Makefile can be used with GNU Make or BSD Make | |||
LIB=libsphincs-haraka-128s-robust_clean.a | |||
HEADERS = params.h address.h wots.h utils.h fors.h api.h hash.h thash.h haraka.h | |||
OBJECTS = address.o wots.o utils.o fors.o sign.o hash_haraka.o thash_haraka_robust.o haraka.o | |||
CFLAGS=-O3 -Wall -Wconversion -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) | |||
all: $(LIB) | |||
%.o: %.c $(HEADERS) | |||
$(CC) $(CFLAGS) -c -o $@ $< | |||
$(LIB): $(OBJECTS) | |||
$(AR) -r $@ $(OBJECTS) | |||
clean: | |||
$(RM) $(OBJECTS) | |||
$(RM) $(LIB) |
@@ -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) |
@@ -0,0 +1,78 @@ | |||
#include <stdint.h> | |||
#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; | |||
} |
@@ -0,0 +1,50 @@ | |||
#ifndef SPX_ADDRESS_H | |||
#define SPX_ADDRESS_H | |||
#include <stdint.h> | |||
#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 |
@@ -0,0 +1,78 @@ | |||
#ifndef PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_API_H | |||
#define PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_API_H | |||
#include <stddef.h> | |||
#include <stdint.h> | |||
#define PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_CRYPTO_ALGNAME "SPHINCS+" | |||
#define PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_CRYPTO_SECRETKEYBYTES 64 | |||
#define PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES 32 | |||
#define PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_CRYPTO_BYTES 8080 | |||
#define PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_CRYPTO_SEEDBYTES 48 | |||
/* | |||
* Returns the length of a secret key, in bytes | |||
*/ | |||
size_t PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_crypto_sign_secretkeybytes(void); | |||
/* | |||
* Returns the length of a public key, in bytes | |||
*/ | |||
size_t PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_crypto_sign_publickeybytes(void); | |||
/* | |||
* Returns the length of a signature, in bytes | |||
*/ | |||
size_t PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_crypto_sign_bytes(void); | |||
/* | |||
* Returns the length of the seed required to generate a key pair, in bytes | |||
*/ | |||
size_t PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_crypto_sign_seedbytes(void); | |||
/* | |||
* Generates a SPHINCS+ key pair given a seed. | |||
* Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] | |||
* Format pk: [root || PUB_SEED] | |||
*/ | |||
int PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_crypto_sign_seed_keypair( | |||
uint8_t *pk, uint8_t *sk, const uint8_t *seed); | |||
/* | |||
* Generates a SPHINCS+ key pair. | |||
* Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] | |||
* Format pk: [root || PUB_SEED] | |||
*/ | |||
int PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_crypto_sign_keypair( | |||
uint8_t *pk, uint8_t *sk); | |||
/** | |||
* Returns an array containing a detached signature. | |||
*/ | |||
int PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_crypto_sign_signature( | |||
uint8_t *sig, size_t *siglen, | |||
const uint8_t *m, size_t mlen, const uint8_t *sk); | |||
/** | |||
* Verifies a detached signature and message under a given public key. | |||
*/ | |||
int PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_crypto_sign_verify( | |||
const uint8_t *sig, size_t siglen, | |||
const uint8_t *m, size_t mlen, const uint8_t *pk); | |||
/** | |||
* Returns an array containing the signature followed by the message. | |||
*/ | |||
int PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_crypto_sign( | |||
uint8_t *sm, size_t *smlen, | |||
const uint8_t *m, size_t mlen, const uint8_t *sk); | |||
/** | |||
* Verifies a given signature-message pair under a given public key. | |||
*/ | |||
int PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_crypto_sign_open( | |||
uint8_t *m, size_t *mlen, | |||
const uint8_t *sm, size_t smlen, const uint8_t *pk); | |||
#endif |
@@ -0,0 +1,164 @@ | |||
#include <stdint.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#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); | |||
} |
@@ -0,0 +1,30 @@ | |||
#ifndef SPX_FORS_H | |||
#define SPX_FORS_H | |||
#include <stdint.h> | |||
#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 |
@@ -0,0 +1,965 @@ | |||
/* | |||
* Constant time implementation of the Haraka hash function. | |||
* | |||
* The bit-sliced implementation of the AES round functions are | |||
* based on the AES implementation in BearSSL written | |||
* by Thomas Pornin <pornin@bolet.org> | |||
*/ | |||
#include <stdint.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include "haraka.h" | |||
#define HARAKAS_RATE 32 | |||
static const uint64_t haraka512_rc64[10][8] = { | |||
{0x24cf0ab9086f628b, 0xbdd6eeecc83b8382, 0xd96fb0306cdad0a7, 0xaace082ac8f95f89, 0x449d8e8870d7041f, 0x49bb2f80b2b3e2f8, 0x0569ae98d93bb258, 0x23dc9691e7d6a4b1}, | |||
{0xd8ba10ede0fe5b6e, 0x7ecf7dbe424c7b8e, 0x6ea9949c6df62a31, 0xbf3f3c97ec9c313e, 0x241d03a196a1861e, 0xead3a51116e5a2ea, 0x77d479fcad9574e3, 0x18657a1af894b7a0}, | |||
{0x10671e1a7f595522, 0xd9a00ff675d28c7b, 0x2f1edf0d2b9ba661, 0xb8ff58b8e3de45f9, 0xee29261da9865c02, 0xd1532aa4b50bdf43, 0x8bf858159b231bb1, 0xdf17439d22d4f599}, | |||
{0xdd4b2f0870b918c0, 0x757a81f3b39b1bb6, 0x7a5c556898952e3f, 0x7dd70a16d915d87a, 0x3ae61971982b8301, 0xc3ab319e030412be, 0x17c0033ac094a8cb, 0x5a0630fc1a8dc4ef}, | |||
{0x17708988c1632f73, 0xf92ddae090b44f4f, 0x11ac0285c43aa314, 0x509059941936b8ba, 0xd03e152fa2ce9b69, 0x3fbcbcb63a32998b, 0x6204696d692254f7, 0x915542ed93ec59b4}, | |||
{0xf4ed94aa8879236e, 0xff6cb41cd38e03c0, 0x069b38602368aeab, 0x669495b820f0ddba, 0xf42013b1b8bf9e3d, 0xcf935efe6439734d, 0xbc1dcf42ca29e3f8, 0x7e6d3ed29f78ad67}, | |||
{0xf3b0f6837ffcddaa, 0x3a76faef934ddf41, 0xcec7ae583a9c8e35, 0xe4dd18c68f0260af, 0x2c0e5df1ad398eaa, 0x478df5236ae22e8c, 0xfb944c46fe865f39, 0xaa48f82f028132ba}, | |||
{0x231b9ae2b76aca77, 0x292a76a712db0b40, 0x5850625dc8134491, 0x73137dd469810fb5, 0x8a12a6a202a474fd, 0xd36fd9daa78bdb80, 0xb34c5e733505706f, 0xbaf1cdca818d9d96}, | |||
{0x2e99781335e8c641, 0xbddfe5cce47d560e, 0xf74e9bf32e5e040c, 0x1d7a709d65996be9, 0x670df36a9cf66cdd, 0xd05ef84a176a2875, 0x0f888e828cb1c44e, 0x1a79e9c9727b052c}, | |||
{0x83497348628d84de, 0x2e9387d51f22a754, 0xb000068da2f852d6, 0x378c9e1190fd6fe5, 0x870027c316de7293, 0xe51a9d4462e047bb, 0x90ecf7f8c6251195, 0x655953bfbed90a9c}, | |||
}; | |||
static uint64_t tweaked512_rc64[10][8]; | |||
static uint32_t tweaked256_rc32[10][8]; | |||
static uint32_t tweaked256_rc32_sseed[10][8]; | |||
static inline uint32_t br_dec32le(const unsigned char *src) { | |||
return (uint32_t)src[0] | |||
| ((uint32_t)src[1] << 8) | |||
| ((uint32_t)src[2] << 16) | |||
| ((uint32_t)src[3] << 24); | |||
} | |||
static void br_range_dec32le(uint32_t *v, size_t num, const unsigned char *src) { | |||
while (num-- > 0) { | |||
*v ++ = br_dec32le(src); | |||
src += 4; | |||
} | |||
} | |||
static inline void br_enc32le(unsigned char *dst, uint32_t x) { | |||
dst[0] = (unsigned char)x; | |||
dst[1] = (unsigned char)(x >> 8); | |||
dst[2] = (unsigned char)(x >> 16); | |||
dst[3] = (unsigned char)(x >> 24); | |||
} | |||
static void br_range_enc32le(unsigned char *dst, const uint32_t *v, size_t num) { | |||
while (num-- > 0) { | |||
br_enc32le(dst, *v ++); | |||
dst += 4; | |||
} | |||
} | |||
static void br_aes_ct64_bitslice_Sbox(uint64_t *q) { | |||
/* | |||
* This S-box implementation is a straightforward translation of | |||
* the circuit described by Boyar and Peralta in "A new | |||
* combinational logic minimization technique with applications | |||
* to cryptology" (https://eprint.iacr.org/2009/191.pdf). | |||
* | |||
* Note that variables x* (input) and s* (output) are numbered | |||
* in "reverse" order (x0 is the high bit, x7 is the low bit). | |||
*/ | |||
uint64_t x0, x1, x2, x3, x4, x5, x6, x7; | |||
uint64_t y1, y2, y3, y4, y5, y6, y7, y8, y9; | |||
uint64_t y10, y11, y12, y13, y14, y15, y16, y17, y18, y19; | |||
uint64_t y20, y21; | |||
uint64_t z0, z1, z2, z3, z4, z5, z6, z7, z8, z9; | |||
uint64_t z10, z11, z12, z13, z14, z15, z16, z17; | |||
uint64_t t0, t1, t2, t3, t4, t5, t6, t7, t8, t9; | |||
uint64_t t10, t11, t12, t13, t14, t15, t16, t17, t18, t19; | |||
uint64_t t20, t21, t22, t23, t24, t25, t26, t27, t28, t29; | |||
uint64_t t30, t31, t32, t33, t34, t35, t36, t37, t38, t39; | |||
uint64_t t40, t41, t42, t43, t44, t45, t46, t47, t48, t49; | |||
uint64_t t50, t51, t52, t53, t54, t55, t56, t57, t58, t59; | |||
uint64_t t60, t61, t62, t63, t64, t65, t66, t67; | |||
uint64_t s0, s1, s2, s3, s4, s5, s6, s7; | |||
x0 = q[7]; | |||
x1 = q[6]; | |||
x2 = q[5]; | |||
x3 = q[4]; | |||
x4 = q[3]; | |||
x5 = q[2]; | |||
x6 = q[1]; | |||
x7 = q[0]; | |||
/* | |||
* Top linear transformation. | |||
*/ | |||
y14 = x3 ^ x5; | |||
y13 = x0 ^ x6; | |||
y9 = x0 ^ x3; | |||
y8 = x0 ^ x5; | |||
t0 = x1 ^ x2; | |||
y1 = t0 ^ x7; | |||
y4 = y1 ^ x3; | |||
y12 = y13 ^ y14; | |||
y2 = y1 ^ x0; | |||
y5 = y1 ^ x6; | |||
y3 = y5 ^ y8; | |||
t1 = x4 ^ y12; | |||
y15 = t1 ^ x5; | |||
y20 = t1 ^ x1; | |||
y6 = y15 ^ x7; | |||
y10 = y15 ^ t0; | |||
y11 = y20 ^ y9; | |||
y7 = x7 ^ y11; | |||
y17 = y10 ^ y11; | |||
y19 = y10 ^ y8; | |||
y16 = t0 ^ y11; | |||
y21 = y13 ^ y16; | |||
y18 = x0 ^ y16; | |||
/* | |||
* Non-linear section. | |||
*/ | |||
t2 = y12 & y15; | |||
t3 = y3 & y6; | |||
t4 = t3 ^ t2; | |||
t5 = y4 & x7; | |||
t6 = t5 ^ t2; | |||
t7 = y13 & y16; | |||
t8 = y5 & y1; | |||
t9 = t8 ^ t7; | |||
t10 = y2 & y7; | |||
t11 = t10 ^ t7; | |||
t12 = y9 & y11; | |||
t13 = y14 & y17; | |||
t14 = t13 ^ t12; | |||
t15 = y8 & y10; | |||
t16 = t15 ^ t12; | |||
t17 = t4 ^ t14; | |||
t18 = t6 ^ t16; | |||
t19 = t9 ^ t14; | |||
t20 = t11 ^ t16; | |||
t21 = t17 ^ y20; | |||
t22 = t18 ^ y19; | |||
t23 = t19 ^ y21; | |||
t24 = t20 ^ y18; | |||
t25 = t21 ^ t22; | |||
t26 = t21 & t23; | |||
t27 = t24 ^ t26; | |||
t28 = t25 & t27; | |||
t29 = t28 ^ t22; | |||
t30 = t23 ^ t24; | |||
t31 = t22 ^ t26; | |||
t32 = t31 & t30; | |||
t33 = t32 ^ t24; | |||
t34 = t23 ^ t33; | |||
t35 = t27 ^ t33; | |||
t36 = t24 & t35; | |||
t37 = t36 ^ t34; | |||
t38 = t27 ^ t36; | |||
t39 = t29 & t38; | |||
t40 = t25 ^ t39; | |||
t41 = t40 ^ t37; | |||
t42 = t29 ^ t33; | |||
t43 = t29 ^ t40; | |||
t44 = t33 ^ t37; | |||
t45 = t42 ^ t41; | |||
z0 = t44 & y15; | |||
z1 = t37 & y6; | |||
z2 = t33 & x7; | |||
z3 = t43 & y16; | |||
z4 = t40 & y1; | |||
z5 = t29 & y7; | |||
z6 = t42 & y11; | |||
z7 = t45 & y17; | |||
z8 = t41 & y10; | |||
z9 = t44 & y12; | |||
z10 = t37 & y3; | |||
z11 = t33 & y4; | |||
z12 = t43 & y13; | |||
z13 = t40 & y5; | |||
z14 = t29 & y2; | |||
z15 = t42 & y9; | |||
z16 = t45 & y14; | |||
z17 = t41 & y8; | |||
/* | |||
* Bottom linear transformation. | |||
*/ | |||
t46 = z15 ^ z16; | |||
t47 = z10 ^ z11; | |||
t48 = z5 ^ z13; | |||
t49 = z9 ^ z10; | |||
t50 = z2 ^ z12; | |||
t51 = z2 ^ z5; | |||
t52 = z7 ^ z8; | |||
t53 = z0 ^ z3; | |||
t54 = z6 ^ z7; | |||
t55 = z16 ^ z17; | |||
t56 = z12 ^ t48; | |||
t57 = t50 ^ t53; | |||
t58 = z4 ^ t46; | |||
t59 = z3 ^ t54; | |||
t60 = t46 ^ t57; | |||
t61 = z14 ^ t57; | |||
t62 = t52 ^ t58; | |||
t63 = t49 ^ t58; | |||
t64 = z4 ^ t59; | |||
t65 = t61 ^ t62; | |||
t66 = z1 ^ t63; | |||
s0 = t59 ^ t63; | |||
s6 = t56 ^ ~t62; | |||
s7 = t48 ^ ~t60; | |||
t67 = t64 ^ t65; | |||
s3 = t53 ^ t66; | |||
s4 = t51 ^ t66; | |||
s5 = t47 ^ t65; | |||
s1 = t64 ^ ~s3; | |||
s2 = t55 ^ ~t67; | |||
q[7] = s0; | |||
q[6] = s1; | |||
q[5] = s2; | |||
q[4] = s3; | |||
q[3] = s4; | |||
q[2] = s5; | |||
q[1] = s6; | |||
q[0] = s7; | |||
} | |||
static void br_aes_ct_bitslice_Sbox(uint32_t *q) { | |||
/* | |||
* This S-box implementation is a straightforward translation of | |||
* the circuit described by Boyar and Peralta in "A new | |||
* combinational logic minimization technique with applications | |||
* to cryptology" (https://eprint.iacr.org/2009/191.pdf). | |||
* | |||
* Note that variables x* (input) and s* (output) are numbered | |||
* in "reverse" order (x0 is the high bit, x7 is the low bit). | |||
*/ | |||
uint32_t x0, x1, x2, x3, x4, x5, x6, x7; | |||
uint32_t y1, y2, y3, y4, y5, y6, y7, y8, y9; | |||
uint32_t y10, y11, y12, y13, y14, y15, y16, y17, y18, y19; | |||
uint32_t y20, y21; | |||
uint32_t z0, z1, z2, z3, z4, z5, z6, z7, z8, z9; | |||
uint32_t z10, z11, z12, z13, z14, z15, z16, z17; | |||
uint32_t t0, t1, t2, t3, t4, t5, t6, t7, t8, t9; | |||
uint32_t t10, t11, t12, t13, t14, t15, t16, t17, t18, t19; | |||
uint32_t t20, t21, t22, t23, t24, t25, t26, t27, t28, t29; | |||
uint32_t t30, t31, t32, t33, t34, t35, t36, t37, t38, t39; | |||
uint32_t t40, t41, t42, t43, t44, t45, t46, t47, t48, t49; | |||
uint32_t t50, t51, t52, t53, t54, t55, t56, t57, t58, t59; | |||
uint32_t t60, t61, t62, t63, t64, t65, t66, t67; | |||
uint32_t s0, s1, s2, s3, s4, s5, s6, s7; | |||
x0 = q[7]; | |||
x1 = q[6]; | |||
x2 = q[5]; | |||
x3 = q[4]; | |||
x4 = q[3]; | |||
x5 = q[2]; | |||
x6 = q[1]; | |||
x7 = q[0]; | |||
/* | |||
* Top linear transformation. | |||
*/ | |||
y14 = x3 ^ x5; | |||
y13 = x0 ^ x6; | |||
y9 = x0 ^ x3; | |||
y8 = x0 ^ x5; | |||
t0 = x1 ^ x2; | |||
y1 = t0 ^ x7; | |||
y4 = y1 ^ x3; | |||
y12 = y13 ^ y14; | |||
y2 = y1 ^ x0; | |||
y5 = y1 ^ x6; | |||
y3 = y5 ^ y8; | |||
t1 = x4 ^ y12; | |||
y15 = t1 ^ x5; | |||
y20 = t1 ^ x1; | |||
y6 = y15 ^ x7; | |||
y10 = y15 ^ t0; | |||
y11 = y20 ^ y9; | |||
y7 = x7 ^ y11; | |||
y17 = y10 ^ y11; | |||
y19 = y10 ^ y8; | |||
y16 = t0 ^ y11; | |||
y21 = y13 ^ y16; | |||
y18 = x0 ^ y16; | |||
/* | |||
* Non-linear section. | |||
*/ | |||
t2 = y12 & y15; | |||
t3 = y3 & y6; | |||
t4 = t3 ^ t2; | |||
t5 = y4 & x7; | |||
t6 = t5 ^ t2; | |||
t7 = y13 & y16; | |||
t8 = y5 & y1; | |||
t9 = t8 ^ t7; | |||
t10 = y2 & y7; | |||
t11 = t10 ^ t7; | |||
t12 = y9 & y11; | |||
t13 = y14 & y17; | |||
t14 = t13 ^ t12; | |||
t15 = y8 & y10; | |||
t16 = t15 ^ t12; | |||
t17 = t4 ^ t14; | |||
t18 = t6 ^ t16; | |||
t19 = t9 ^ t14; | |||
t20 = t11 ^ t16; | |||
t21 = t17 ^ y20; | |||
t22 = t18 ^ y19; | |||
t23 = t19 ^ y21; | |||
t24 = t20 ^ y18; | |||
t25 = t21 ^ t22; | |||
t26 = t21 & t23; | |||
t27 = t24 ^ t26; | |||
t28 = t25 & t27; | |||
t29 = t28 ^ t22; | |||
t30 = t23 ^ t24; | |||
t31 = t22 ^ t26; | |||
t32 = t31 & t30; | |||
t33 = t32 ^ t24; | |||
t34 = t23 ^ t33; | |||
t35 = t27 ^ t33; | |||
t36 = t24 & t35; | |||
t37 = t36 ^ t34; | |||
t38 = t27 ^ t36; | |||
t39 = t29 & t38; | |||
t40 = t25 ^ t39; | |||
t41 = t40 ^ t37; | |||
t42 = t29 ^ t33; | |||
t43 = t29 ^ t40; | |||
t44 = t33 ^ t37; | |||
t45 = t42 ^ t41; | |||
z0 = t44 & y15; | |||
z1 = t37 & y6; | |||
z2 = t33 & x7; | |||
z3 = t43 & y16; | |||
z4 = t40 & y1; | |||
z5 = t29 & y7; | |||
z6 = t42 & y11; | |||
z7 = t45 & y17; | |||
z8 = t41 & y10; | |||
z9 = t44 & y12; | |||
z10 = t37 & y3; | |||
z11 = t33 & y4; | |||
z12 = t43 & y13; | |||
z13 = t40 & y5; | |||
z14 = t29 & y2; | |||
z15 = t42 & y9; | |||
z16 = t45 & y14; | |||
z17 = t41 & y8; | |||
/* | |||
* Bottom linear transformation. | |||
*/ | |||
t46 = z15 ^ z16; | |||
t47 = z10 ^ z11; | |||
t48 = z5 ^ z13; | |||
t49 = z9 ^ z10; | |||
t50 = z2 ^ z12; | |||
t51 = z2 ^ z5; | |||
t52 = z7 ^ z8; | |||
t53 = z0 ^ z3; | |||
t54 = z6 ^ z7; | |||
t55 = z16 ^ z17; | |||
t56 = z12 ^ t48; | |||
t57 = t50 ^ t53; | |||
t58 = z4 ^ t46; | |||
t59 = z3 ^ t54; | |||
t60 = t46 ^ t57; | |||
t61 = z14 ^ t57; | |||
t62 = t52 ^ t58; | |||
t63 = t49 ^ t58; | |||
t64 = z4 ^ t59; | |||
t65 = t61 ^ t62; | |||
t66 = z1 ^ t63; | |||
s0 = t59 ^ t63; | |||
s6 = t56 ^ ~t62; | |||
s7 = t48 ^ ~t60; | |||
t67 = t64 ^ t65; | |||
s3 = t53 ^ t66; | |||
s4 = t51 ^ t66; | |||
s5 = t47 ^ t65; | |||
s1 = t64 ^ ~s3; | |||
s2 = t55 ^ ~t67; | |||
q[7] = s0; | |||
q[6] = s1; | |||
q[5] = s2; | |||
q[4] = s3; | |||
q[3] = s4; | |||
q[2] = s5; | |||
q[1] = s6; | |||
q[0] = s7; | |||
} | |||
static void br_aes_ct_ortho(uint32_t *q) { | |||
#define SWAPN_32(cl, ch, s, x, y) do { \ | |||
uint32_t a, b; \ | |||
a = (x); \ | |||
b = (y); \ | |||
(x) = (a & (uint32_t)(cl)) | ((b & (uint32_t)(cl)) << (s)); \ | |||
(y) = ((a & (uint32_t)(ch)) >> (s)) | (b & (uint32_t)(ch)); \ | |||
} while (0) | |||
#define SWAP2_32(x, y) SWAPN_32(0x55555555, 0xAAAAAAAA, 1, x, y) | |||
#define SWAP4_32(x, y) SWAPN_32(0x33333333, 0xCCCCCCCC, 2, x, y) | |||
#define SWAP8_32(x, y) SWAPN_32(0x0F0F0F0F, 0xF0F0F0F0, 4, x, y) | |||
SWAP2_32(q[0], q[1]); | |||
SWAP2_32(q[2], q[3]); | |||
SWAP2_32(q[4], q[5]); | |||
SWAP2_32(q[6], q[7]); | |||
SWAP4_32(q[0], q[2]); | |||
SWAP4_32(q[1], q[3]); | |||
SWAP4_32(q[4], q[6]); | |||
SWAP4_32(q[5], q[7]); | |||
SWAP8_32(q[0], q[4]); | |||
SWAP8_32(q[1], q[5]); | |||
SWAP8_32(q[2], q[6]); | |||
SWAP8_32(q[3], q[7]); | |||
} | |||
static inline void add_round_key32(uint32_t *q, const uint32_t *sk) { | |||
q[0] ^= sk[0]; | |||
q[1] ^= sk[1]; | |||
q[2] ^= sk[2]; | |||
q[3] ^= sk[3]; | |||
q[4] ^= sk[4]; | |||
q[5] ^= sk[5]; | |||
q[6] ^= sk[6]; | |||
q[7] ^= sk[7]; | |||
} | |||
static inline void shift_rows32(uint32_t *q) { | |||
int i; | |||
for (i = 0; i < 8; i++) { | |||
uint32_t x; | |||
x = q[i]; | |||
q[i] = (x & 0x000000FF) | |||
| ((x & 0x0000FC00) >> 2) | ((x & 0x00000300) << 6) | |||
| ((x & 0x00F00000) >> 4) | ((x & 0x000F0000) << 4) | |||
| ((x & 0xC0000000) >> 6) | ((x & 0x3F000000) << 2); | |||
} | |||
} | |||
static inline uint32_t rotr16(uint32_t x) { | |||
return (x << 16) | (x >> 16); | |||
} | |||
static inline void mix_columns32(uint32_t *q) { | |||
uint32_t q0, q1, q2, q3, q4, q5, q6, q7; | |||
uint32_t r0, r1, r2, r3, r4, r5, r6, r7; | |||
q0 = q[0]; | |||
q1 = q[1]; | |||
q2 = q[2]; | |||
q3 = q[3]; | |||
q4 = q[4]; | |||
q5 = q[5]; | |||
q6 = q[6]; | |||
q7 = q[7]; | |||
r0 = (q0 >> 8) | (q0 << 24); | |||
r1 = (q1 >> 8) | (q1 << 24); | |||
r2 = (q2 >> 8) | (q2 << 24); | |||
r3 = (q3 >> 8) | (q3 << 24); | |||
r4 = (q4 >> 8) | (q4 << 24); | |||
r5 = (q5 >> 8) | (q5 << 24); | |||
r6 = (q6 >> 8) | (q6 << 24); | |||
r7 = (q7 >> 8) | (q7 << 24); | |||
q[0] = q7 ^ r7 ^ r0 ^ rotr16(q0 ^ r0); | |||
q[1] = q0 ^ r0 ^ q7 ^ r7 ^ r1 ^ rotr16(q1 ^ r1); | |||
q[2] = q1 ^ r1 ^ r2 ^ rotr16(q2 ^ r2); | |||
q[3] = q2 ^ r2 ^ q7 ^ r7 ^ r3 ^ rotr16(q3 ^ r3); | |||
q[4] = q3 ^ r3 ^ q7 ^ r7 ^ r4 ^ rotr16(q4 ^ r4); | |||
q[5] = q4 ^ r4 ^ r5 ^ rotr16(q5 ^ r5); | |||
q[6] = q5 ^ r5 ^ r6 ^ rotr16(q6 ^ r6); | |||
q[7] = q6 ^ r6 ^ r7 ^ rotr16(q7 ^ r7); | |||
} | |||
static void br_aes_ct64_ortho(uint64_t *q) { | |||
#define SWAPN(cl, ch, s, x, y) do { \ | |||
uint64_t a, b; \ | |||
a = (x); \ | |||
b = (y); \ | |||
(x) = (a & (uint64_t)(cl)) | ((b & (uint64_t)(cl)) << (s)); \ | |||
(y) = ((a & (uint64_t)(ch)) >> (s)) | (b & (uint64_t)(ch)); \ | |||
} while (0) | |||
#define SWAP2(x, y) SWAPN(0x5555555555555555, 0xAAAAAAAAAAAAAAAA, 1, x, y) | |||
#define SWAP4(x, y) SWAPN(0x3333333333333333, 0xCCCCCCCCCCCCCCCC, 2, x, y) | |||
#define SWAP8(x, y) SWAPN(0x0F0F0F0F0F0F0F0F, 0xF0F0F0F0F0F0F0F0, 4, x, y) | |||
SWAP2(q[0], q[1]); | |||
SWAP2(q[2], q[3]); | |||
SWAP2(q[4], q[5]); | |||
SWAP2(q[6], q[7]); | |||
SWAP4(q[0], q[2]); | |||
SWAP4(q[1], q[3]); | |||
SWAP4(q[4], q[6]); | |||
SWAP4(q[5], q[7]); | |||
SWAP8(q[0], q[4]); | |||
SWAP8(q[1], q[5]); | |||
SWAP8(q[2], q[6]); | |||
SWAP8(q[3], q[7]); | |||
} | |||
static void br_aes_ct64_interleave_in(uint64_t *q0, uint64_t *q1, const uint32_t *w) { | |||
uint64_t x0, x1, x2, x3; | |||
x0 = w[0]; | |||
x1 = w[1]; | |||
x2 = w[2]; | |||
x3 = w[3]; | |||
x0 |= (x0 << 16); | |||
x1 |= (x1 << 16); | |||
x2 |= (x2 << 16); | |||
x3 |= (x3 << 16); | |||
x0 &= (uint64_t)0x0000FFFF0000FFFF; | |||
x1 &= (uint64_t)0x0000FFFF0000FFFF; | |||
x2 &= (uint64_t)0x0000FFFF0000FFFF; | |||
x3 &= (uint64_t)0x0000FFFF0000FFFF; | |||
x0 |= (x0 << 8); | |||
x1 |= (x1 << 8); | |||
x2 |= (x2 << 8); | |||
x3 |= (x3 << 8); | |||
x0 &= (uint64_t)0x00FF00FF00FF00FF; | |||
x1 &= (uint64_t)0x00FF00FF00FF00FF; | |||
x2 &= (uint64_t)0x00FF00FF00FF00FF; | |||
x3 &= (uint64_t)0x00FF00FF00FF00FF; | |||
*q0 = x0 | (x2 << 8); | |||
*q1 = x1 | (x3 << 8); | |||
} | |||
static void br_aes_ct64_interleave_out(uint32_t *w, uint64_t q0, uint64_t q1) { | |||
uint64_t x0, x1, x2, x3; | |||
x0 = q0 & (uint64_t)0x00FF00FF00FF00FF; | |||
x1 = q1 & (uint64_t)0x00FF00FF00FF00FF; | |||
x2 = (q0 >> 8) & (uint64_t)0x00FF00FF00FF00FF; | |||
x3 = (q1 >> 8) & (uint64_t)0x00FF00FF00FF00FF; | |||
x0 |= (x0 >> 8); | |||
x1 |= (x1 >> 8); | |||
x2 |= (x2 >> 8); | |||
x3 |= (x3 >> 8); | |||
x0 &= (uint64_t)0x0000FFFF0000FFFF; | |||
x1 &= (uint64_t)0x0000FFFF0000FFFF; | |||
x2 &= (uint64_t)0x0000FFFF0000FFFF; | |||
x3 &= (uint64_t)0x0000FFFF0000FFFF; | |||
w[0] = (uint32_t)x0 | (uint32_t)(x0 >> 16); | |||
w[1] = (uint32_t)x1 | (uint32_t)(x1 >> 16); | |||
w[2] = (uint32_t)x2 | (uint32_t)(x2 >> 16); | |||
w[3] = (uint32_t)x3 | (uint32_t)(x3 >> 16); | |||
} | |||
static inline void add_round_key(uint64_t *q, const uint64_t *sk) { | |||
q[0] ^= sk[0]; | |||
q[1] ^= sk[1]; | |||
q[2] ^= sk[2]; | |||
q[3] ^= sk[3]; | |||
q[4] ^= sk[4]; | |||
q[5] ^= sk[5]; | |||
q[6] ^= sk[6]; | |||
q[7] ^= sk[7]; | |||
} | |||
static inline void shift_rows(uint64_t *q) { | |||
int i; | |||
for (i = 0; i < 8; i++) { | |||
uint64_t x; | |||
x = q[i]; | |||
q[i] = (x & (uint64_t)0x000000000000FFFF) | |||
| ((x & (uint64_t)0x00000000FFF00000) >> 4) | |||
| ((x & (uint64_t)0x00000000000F0000) << 12) | |||
| ((x & (uint64_t)0x0000FF0000000000) >> 8) | |||
| ((x & (uint64_t)0x000000FF00000000) << 8) | |||
| ((x & (uint64_t)0xF000000000000000) >> 12) | |||
| ((x & (uint64_t)0x0FFF000000000000) << 4); | |||
} | |||
} | |||
static inline uint64_t rotr32(uint64_t x) { | |||
return (x << 32) | (x >> 32); | |||
} | |||
static inline void mix_columns(uint64_t *q) { | |||
uint64_t q0, q1, q2, q3, q4, q5, q6, q7; | |||
uint64_t r0, r1, r2, r3, r4, r5, r6, r7; | |||
q0 = q[0]; | |||
q1 = q[1]; | |||
q2 = q[2]; | |||
q3 = q[3]; | |||
q4 = q[4]; | |||
q5 = q[5]; | |||
q6 = q[6]; | |||
q7 = q[7]; | |||
r0 = (q0 >> 16) | (q0 << 48); | |||
r1 = (q1 >> 16) | (q1 << 48); | |||
r2 = (q2 >> 16) | (q2 << 48); | |||
r3 = (q3 >> 16) | (q3 << 48); | |||
r4 = (q4 >> 16) | (q4 << 48); | |||
r5 = (q5 >> 16) | (q5 << 48); | |||
r6 = (q6 >> 16) | (q6 << 48); | |||
r7 = (q7 >> 16) | (q7 << 48); | |||
q[0] = q7 ^ r7 ^ r0 ^ rotr32(q0 ^ r0); | |||
q[1] = q0 ^ r0 ^ q7 ^ r7 ^ r1 ^ rotr32(q1 ^ r1); | |||
q[2] = q1 ^ r1 ^ r2 ^ rotr32(q2 ^ r2); | |||
q[3] = q2 ^ r2 ^ q7 ^ r7 ^ r3 ^ rotr32(q3 ^ r3); | |||
q[4] = q3 ^ r3 ^ q7 ^ r7 ^ r4 ^ rotr32(q4 ^ r4); | |||
q[5] = q4 ^ r4 ^ r5 ^ rotr32(q5 ^ r5); | |||
q[6] = q5 ^ r5 ^ r6 ^ rotr32(q6 ^ r6); | |||
q[7] = q6 ^ r6 ^ r7 ^ rotr32(q7 ^ r7); | |||
} | |||
static void interleave_constant(uint64_t *out, const unsigned char *in) { | |||
uint32_t tmp_32_constant[16]; | |||
int i; | |||
br_range_dec32le(tmp_32_constant, 16, in); | |||
for (i = 0; i < 4; i++) { | |||
br_aes_ct64_interleave_in(&out[i], &out[i + 4], tmp_32_constant + (i << 2)); | |||
} | |||
br_aes_ct64_ortho(out); | |||
} | |||
static void interleave_constant32(uint32_t *out, const unsigned char *in) { | |||
int i; | |||
for (i = 0; i < 4; i++) { | |||
out[2 * i] = br_dec32le(in + 4 * i); | |||
out[2 * i + 1] = br_dec32le(in + 4 * i + 16); | |||
} | |||
br_aes_ct_ortho(out); | |||
} | |||
void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_tweak_constants( | |||
const unsigned char *pk_seed, const unsigned char *sk_seed, | |||
unsigned long long seed_length) { | |||
unsigned char buf[40 * 16]; | |||
int i; | |||
/* Use the standard constants to generate tweaked ones. */ | |||
memcpy((uint8_t *)tweaked512_rc64, (uint8_t *)haraka512_rc64, 40 * 16); | |||
/* Constants for sk.seed */ | |||
if (sk_seed != NULL) { | |||
PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka_S( | |||
buf, 40 * 16, sk_seed, seed_length); | |||
/* Interleave constants */ | |||
for (i = 0; i < 10; i++) { | |||
interleave_constant32(tweaked256_rc32_sseed[i], buf + 32 * i); | |||
} | |||
} | |||
/* Constants for pk.seed */ | |||
PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka_S( | |||
buf, 40 * 16, pk_seed, seed_length); | |||
for (i = 0; i < 10; i++) { | |||
interleave_constant32(tweaked256_rc32[i], buf + 32 * i); | |||
interleave_constant(tweaked512_rc64[i], buf + 64 * i); | |||
} | |||
} | |||
static void haraka_S_absorb(unsigned char *s, | |||
const unsigned char *m, unsigned long long mlen, | |||
unsigned char p) { | |||
unsigned long long i; | |||
unsigned char t[HARAKAS_RATE]; | |||
while (mlen >= HARAKAS_RATE) { | |||
/* XOR block to state */ | |||
for (i = 0; i < HARAKAS_RATE; ++i) { | |||
s[i] ^= m[i]; | |||
} | |||
PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka512_perm(s, s); | |||
mlen -= HARAKAS_RATE; | |||
m += HARAKAS_RATE; | |||
} | |||
for (i = 0; i < HARAKAS_RATE; ++i) { | |||
t[i] = 0; | |||
} | |||
for (i = 0; i < mlen; ++i) { | |||
t[i] = m[i]; | |||
} | |||
t[i] = p; | |||
t[HARAKAS_RATE - 1] |= 128; | |||
for (i = 0; i < HARAKAS_RATE; ++i) { | |||
s[i] ^= t[i]; | |||
} | |||
} | |||
static void haraka_S_squeezeblocks(unsigned char *h, unsigned long long nblocks, | |||
unsigned char *s) { | |||
while (nblocks > 0) { | |||
PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka512_perm(s, s); | |||
memcpy(h, s, HARAKAS_RATE); | |||
h += HARAKAS_RATE; | |||
nblocks--; | |||
} | |||
} | |||
void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka_S_inc_init(uint8_t *s_inc) { | |||
size_t i; | |||
for (i = 0; i < 64; i++) { | |||
s_inc[i] = 0; | |||
} | |||
s_inc[64] = 0; | |||
} | |||
void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka_S_inc_absorb(uint8_t *s_inc, const uint8_t *m, size_t mlen) { | |||
size_t i; | |||
/* Recall that s_inc[64] is the non-absorbed bytes xored into the state */ | |||
while (mlen + s_inc[64] >= HARAKAS_RATE) { | |||
for (i = 0; i < (size_t)(HARAKAS_RATE - s_inc[64]); i++) { | |||
/* Take the i'th byte from message | |||
xor with the s_inc[64] + i'th byte of the state */ | |||
s_inc[s_inc[64] + i] ^= m[i]; | |||
} | |||
mlen -= (size_t)(HARAKAS_RATE - s_inc[64]); | |||
m += HARAKAS_RATE - s_inc[64]; | |||
s_inc[64] = 0; | |||
PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka512_perm(s_inc, s_inc); | |||
} | |||
for (i = 0; i < mlen; i++) { | |||
s_inc[s_inc[64] + i] ^= m[i]; | |||
} | |||
s_inc[64] = (uint8_t)(mlen + s_inc[64]); | |||
} | |||
void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka_S_inc_finalize(uint8_t *s_inc) { | |||
/* After haraka_S_inc_absorb, we are guaranteed that s_inc[64] < HARAKAS_RATE, | |||
so we can always use one more byte for p in the current state. */ | |||
s_inc[s_inc[64]] ^= 0x1F; | |||
s_inc[HARAKAS_RATE - 1] ^= 128; | |||
s_inc[64] = 0; | |||
} | |||
void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka_S_inc_squeeze(uint8_t *out, size_t outlen, uint8_t *s_inc) { | |||
uint8_t i; | |||
/* First consume any bytes we still have sitting around */ | |||
for (i = 0; i < outlen && i < s_inc[64]; i++) { | |||
/* There are s_inc[64] bytes left, so r - s_inc[64] is the first | |||
available byte. We consume from there, i.e., up to r. */ | |||
out[i] = s_inc[(HARAKAS_RATE - s_inc[64] + i)]; | |||
} | |||
out += i; | |||
outlen -= i; | |||
s_inc[64] = (uint8_t)(s_inc[64] - i); | |||
/* Then squeeze the remaining necessary blocks */ | |||
while (outlen > 0) { | |||
PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka512_perm(s_inc, s_inc); | |||
for (i = 0; i < outlen && i < HARAKAS_RATE; i++) { | |||
out[i] = s_inc[i]; | |||
} | |||
out += i; | |||
outlen -= i; | |||
s_inc[64] = (uint8_t)(HARAKAS_RATE - i); | |||
} | |||
} | |||
void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka_S(unsigned char *out, unsigned long long outlen, const unsigned char *in, unsigned long long inlen) { | |||
unsigned long long i; | |||
unsigned char s[64]; | |||
unsigned char d[32]; | |||
for (i = 0; i < 64; i++) { | |||
s[i] = 0; | |||
} | |||
haraka_S_absorb(s, in, inlen, 0x1F); | |||
haraka_S_squeezeblocks(out, outlen / 32, s); | |||
out += (outlen / 32) * 32; | |||
if (outlen % 32) { | |||
haraka_S_squeezeblocks(d, 1, s); | |||
for (i = 0; i < outlen % 32; i++) { | |||
out[i] = d[i]; | |||
} | |||
} | |||
} | |||
void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka512_perm(unsigned char *out, const unsigned char *in) { | |||
uint32_t w[16]; | |||
uint64_t q[8], tmp_q; | |||
unsigned int i, j; | |||
br_range_dec32le(w, 16, in); | |||
for (i = 0; i < 4; i++) { | |||
br_aes_ct64_interleave_in(&q[i], &q[i + 4], w + (i << 2)); | |||
} | |||
br_aes_ct64_ortho(q); | |||
/* AES rounds */ | |||
for (i = 0; i < 5; i++) { | |||
for (j = 0; j < 2; j++) { | |||
br_aes_ct64_bitslice_Sbox(q); | |||
shift_rows(q); | |||
mix_columns(q); | |||
add_round_key(q, tweaked512_rc64[2 * i + j]); | |||
} | |||
/* Mix states */ | |||
for (j = 0; j < 8; j++) { | |||
tmp_q = q[j]; | |||
q[j] = (tmp_q & 0x0001000100010001) << 5 | | |||
(tmp_q & 0x0002000200020002) << 12 | | |||
(tmp_q & 0x0004000400040004) >> 1 | | |||
(tmp_q & 0x0008000800080008) << 6 | | |||
(tmp_q & 0x0020002000200020) << 9 | | |||
(tmp_q & 0x0040004000400040) >> 4 | | |||
(tmp_q & 0x0080008000800080) << 3 | | |||
(tmp_q & 0x2100210021002100) >> 5 | | |||
(tmp_q & 0x0210021002100210) << 2 | | |||
(tmp_q & 0x0800080008000800) << 4 | | |||
(tmp_q & 0x1000100010001000) >> 12 | | |||
(tmp_q & 0x4000400040004000) >> 10 | | |||
(tmp_q & 0x8400840084008400) >> 3; | |||
} | |||
} | |||
br_aes_ct64_ortho(q); | |||
for (i = 0; i < 4; i ++) { | |||
br_aes_ct64_interleave_out(w + (i << 2), q[i], q[i + 4]); | |||
} | |||
br_range_enc32le(out, w, 16); | |||
} | |||
void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka512(unsigned char *out, const unsigned char *in) { | |||
int i; | |||
unsigned char buf[64]; | |||
PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka512_perm(buf, in); | |||
/* Feed-forward */ | |||
for (i = 0; i < 64; i++) { | |||
buf[i] = buf[i] ^ in[i]; | |||
} | |||
/* Truncated */ | |||
memcpy(out, buf + 8, 8); | |||
memcpy(out + 8, buf + 24, 8); | |||
memcpy(out + 16, buf + 32, 8); | |||
memcpy(out + 24, buf + 48, 8); | |||
} | |||
void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka256(unsigned char *out, const unsigned char *in) { | |||
uint32_t q[8], tmp_q; | |||
int i, j; | |||
for (i = 0; i < 4; i++) { | |||
q[2 * i] = br_dec32le(in + 4 * i); | |||
q[2 * i + 1] = br_dec32le(in + 4 * i + 16); | |||
} | |||
br_aes_ct_ortho(q); | |||
/* AES rounds */ | |||
for (i = 0; i < 5; i++) { | |||
for (j = 0; j < 2; j++) { | |||
br_aes_ct_bitslice_Sbox(q); | |||
shift_rows32(q); | |||
mix_columns32(q); | |||
add_round_key32(q, tweaked256_rc32[2 * i + j]); | |||
} | |||
/* Mix states */ | |||
for (j = 0; j < 8; j++) { | |||
tmp_q = q[j]; | |||
q[j] = (tmp_q & 0x81818181) | | |||
(tmp_q & 0x02020202) << 1 | | |||
(tmp_q & 0x04040404) << 2 | | |||
(tmp_q & 0x08080808) << 3 | | |||
(tmp_q & 0x10101010) >> 3 | | |||
(tmp_q & 0x20202020) >> 2 | | |||
(tmp_q & 0x40404040) >> 1; | |||
} | |||
} | |||
br_aes_ct_ortho(q); | |||
for (i = 0; i < 4; i++) { | |||
br_enc32le(out + 4 * i, q[2 * i]); | |||
br_enc32le(out + 4 * i + 16, q[2 * i + 1]); | |||
} | |||
for (i = 0; i < 32; i++) { | |||
out[i] ^= in[i]; | |||
} | |||
} | |||
void PQCLEAN_SPHINCSHARAKA128SROBUST_CLEAN_haraka256_sk(unsigned char *out, const unsigned char *in) { | |||
uint32_t q[8], tmp_q; | |||
int i, j; | |||
for (i = 0; i < 4; i++) { | |||
q[2 * i] = br_dec32le(in + 4 * i); | |||
q[2 * i + 1] = br_dec32le(in + 4 * i + 16); | |||
} | |||
br_aes_ct_ortho(q); | |||
/* AES rounds */ | |||
for (i = 0; i < 5; i++) { | |||
for (j = 0; j < 2; j++) { | |||
br_aes_ct_bitslice_Sbox(q); | |||
shift_rows32(q); | |||
mix_columns32(q); | |||
add_round_key32(q, tweaked256_rc32_sseed[2 * i + j]); | |||
} | |||
/* Mix states */ | |||
for (j = 0; j < 8; j++) { | |||
tmp_q = q[j]; | |||
q[j] = (tmp_q & 0x81818181) | | |||
(tmp_q & 0x02020202) << 1 | | |||
(tmp_q & 0x04040404) << 2 | | |||
(tmp_q & 0x08080808) << 3 | | |||
(tmp_q & 0x10101010) >> 3 | | |||
(tmp_q & 0x20202020) >> 2 | | |||
(tmp_q & 0x40404040) >> 1; | |||
} | |||
} | |||
br_aes_ct_ortho(q); | |||
for (i = 0; i < 4; i++) { | |||
br_enc32le(out + 4 * i, q[2 * i]); | |||
br_enc32le(out + 4 * i + 16, q[2 * i + 1]); | |||
} | |||
for (i = 0; i < 32; i++) { | |||
out[i] ^= in[i]; | |||
} | |||
} |
@@ -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 |
@@ -0,0 +1,22 @@ | |||
#ifndef SPX_HASH_H | |||
#define SPX_HASH_H | |||
#include <stdint.h> | |||
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 |
@@ -0,0 +1,86 @@ | |||
#include <stdint.h> | |||
#include <string.h> | |||
#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); | |||
} |
@@ -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 |
@@ -0,0 +1,344 @@ | |||
#include <stddef.h> | |||
#include <stdint.h> | |||
#include <string.h> | |||
#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; | |||
} |
@@ -0,0 +1,22 @@ | |||
#ifndef SPX_THASH_H | |||
#define SPX_THASH_H | |||
#include <stdint.h> | |||
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 |
@@ -0,0 +1,88 @@ | |||
#include <stdint.h> | |||
#include <string.h> | |||
#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); | |||
} |
@@ -0,0 +1,192 @@ | |||
#include <stddef.h> | |||
#include <string.h> | |||
#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); | |||
} |
@@ -0,0 +1,60 @@ | |||
#ifndef SPX_UTILS_H | |||
#define SPX_UTILS_H | |||
#include "params.h" | |||
#include <stddef.h> | |||
#include <stdint.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); | |||
/** | |||
* 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 |
@@ -0,0 +1,161 @@ | |||
#include <stdint.h> | |||
#include <string.h> | |||
#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); | |||
} | |||
} |
@@ -0,0 +1,38 @@ | |||
#ifndef SPX_WOTS_H | |||
#define SPX_WOTS_H | |||
#include "params.h" | |||
#include <stdint.h> | |||
/** | |||
* 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 |
@@ -0,0 +1,27 @@ | |||
name: SPHINCS+ | |||
type: signature | |||
claimed-nist-level: 1 | |||
length-public-key: 32 | |||
length-secret-key: 64 | |||
length-signature: 8080 | |||
testvectors-sha256: fcc816e14d200e212b4b955d3011f5a6b61240c7c0003e17acb1bf396ca5d4ad | |||
principal-submitter: Andreas Hülsing | |||
auxiliary-submitters: | |||
- Jean-Philippe Aumasson | |||
- Daniel J. Bernstein, | |||
- Christoph Dobraunig | |||
- Maria Eichlseder | |||
- Scott Fluhrer | |||
- Stefan-Lukas Gazdag | |||
- Panos Kampanakis | |||
- Stefan Kölbl | |||
- Tanja Lange | |||
- Martin M. Lauridsen | |||
- Florian Mendel | |||
- Ruben Niederhagen | |||
- Christian Rechberger | |||
- Joost Rijneveld | |||
- Peter Schwabe | |||
implementations: | |||
- name: clean | |||
version: https://github.com/sphincs/sphincsplus/commit/77755c94d0bc744478044d6efbb888dc13156441 |
@@ -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 | |||
<http://creativecommons.org/publicdomain/zero/1.0/> |
@@ -0,0 +1,20 @@ | |||
# This Makefile can be used with GNU Make or BSD Make | |||
LIB=libsphincs-haraka-128s-simple_clean.a | |||
HEADERS = params.h address.h wots.h utils.h fors.h api.h hash.h thash.h haraka.h | |||
OBJECTS = address.o wots.o utils.o fors.o sign.o hash_haraka.o thash_haraka_simple.o haraka.o | |||
CFLAGS=-O3 -Wall -Wconversion -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) | |||
all: $(LIB) | |||
%.o: %.c $(HEADERS) | |||
$(CC) $(CFLAGS) -c -o $@ $< | |||
$(LIB): $(OBJECTS) | |||
$(AR) -r $@ $(OBJECTS) | |||
clean: | |||
$(RM) $(OBJECTS) | |||
$(RM) $(LIB) |
@@ -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) |
@@ -0,0 +1,78 @@ | |||
#include <stdint.h> | |||
#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; | |||
} |
@@ -0,0 +1,50 @@ | |||
#ifndef SPX_ADDRESS_H | |||
#define SPX_ADDRESS_H | |||
#include <stdint.h> | |||
#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 |
@@ -0,0 +1,78 @@ | |||
#ifndef PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_API_H | |||
#define PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_API_H | |||
#include <stddef.h> | |||
#include <stdint.h> | |||
#define PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_CRYPTO_ALGNAME "SPHINCS+" | |||
#define PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_CRYPTO_SECRETKEYBYTES 64 | |||
#define PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES 32 | |||
#define PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_CRYPTO_BYTES 8080 | |||
#define PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_CRYPTO_SEEDBYTES 48 | |||
/* | |||
* Returns the length of a secret key, in bytes | |||
*/ | |||
size_t PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_crypto_sign_secretkeybytes(void); | |||
/* | |||
* Returns the length of a public key, in bytes | |||
*/ | |||
size_t PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_crypto_sign_publickeybytes(void); | |||
/* | |||
* Returns the length of a signature, in bytes | |||
*/ | |||
size_t PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_crypto_sign_bytes(void); | |||
/* | |||
* Returns the length of the seed required to generate a key pair, in bytes | |||
*/ | |||
size_t PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_crypto_sign_seedbytes(void); | |||
/* | |||
* Generates a SPHINCS+ key pair given a seed. | |||
* Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] | |||
* Format pk: [root || PUB_SEED] | |||
*/ | |||
int PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_crypto_sign_seed_keypair( | |||
uint8_t *pk, uint8_t *sk, const uint8_t *seed); | |||
/* | |||
* Generates a SPHINCS+ key pair. | |||
* Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] | |||
* Format pk: [root || PUB_SEED] | |||
*/ | |||
int PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_crypto_sign_keypair( | |||
uint8_t *pk, uint8_t *sk); | |||
/** | |||
* Returns an array containing a detached signature. | |||
*/ | |||
int PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_crypto_sign_signature( | |||
uint8_t *sig, size_t *siglen, | |||
const uint8_t *m, size_t mlen, const uint8_t *sk); | |||
/** | |||
* Verifies a detached signature and message under a given public key. | |||
*/ | |||
int PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_crypto_sign_verify( | |||
const uint8_t *sig, size_t siglen, | |||
const uint8_t *m, size_t mlen, const uint8_t *pk); | |||
/** | |||
* Returns an array containing the signature followed by the message. | |||
*/ | |||
int PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_crypto_sign( | |||
uint8_t *sm, size_t *smlen, | |||
const uint8_t *m, size_t mlen, const uint8_t *sk); | |||
/** | |||
* Verifies a given signature-message pair under a given public key. | |||
*/ | |||
int PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_crypto_sign_open( | |||
uint8_t *m, size_t *mlen, | |||
const uint8_t *sm, size_t smlen, const uint8_t *pk); | |||
#endif |
@@ -0,0 +1,164 @@ | |||
#include <stdint.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#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); | |||
} |
@@ -0,0 +1,30 @@ | |||
#ifndef SPX_FORS_H | |||
#define SPX_FORS_H | |||
#include <stdint.h> | |||
#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 |
@@ -0,0 +1,965 @@ | |||
/* | |||
* Constant time implementation of the Haraka hash function. | |||
* | |||
* The bit-sliced implementation of the AES round functions are | |||
* based on the AES implementation in BearSSL written | |||
* by Thomas Pornin <pornin@bolet.org> | |||
*/ | |||
#include <stdint.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include "haraka.h" | |||
#define HARAKAS_RATE 32 | |||
static const uint64_t haraka512_rc64[10][8] = { | |||
{0x24cf0ab9086f628b, 0xbdd6eeecc83b8382, 0xd96fb0306cdad0a7, 0xaace082ac8f95f89, 0x449d8e8870d7041f, 0x49bb2f80b2b3e2f8, 0x0569ae98d93bb258, 0x23dc9691e7d6a4b1}, | |||
{0xd8ba10ede0fe5b6e, 0x7ecf7dbe424c7b8e, 0x6ea9949c6df62a31, 0xbf3f3c97ec9c313e, 0x241d03a196a1861e, 0xead3a51116e5a2ea, 0x77d479fcad9574e3, 0x18657a1af894b7a0}, | |||
{0x10671e1a7f595522, 0xd9a00ff675d28c7b, 0x2f1edf0d2b9ba661, 0xb8ff58b8e3de45f9, 0xee29261da9865c02, 0xd1532aa4b50bdf43, 0x8bf858159b231bb1, 0xdf17439d22d4f599}, | |||
{0xdd4b2f0870b918c0, 0x757a81f3b39b1bb6, 0x7a5c556898952e3f, 0x7dd70a16d915d87a, 0x3ae61971982b8301, 0xc3ab319e030412be, 0x17c0033ac094a8cb, 0x5a0630fc1a8dc4ef}, | |||
{0x17708988c1632f73, 0xf92ddae090b44f4f, 0x11ac0285c43aa314, 0x509059941936b8ba, 0xd03e152fa2ce9b69, 0x3fbcbcb63a32998b, 0x6204696d692254f7, 0x915542ed93ec59b4}, | |||
{0xf4ed94aa8879236e, 0xff6cb41cd38e03c0, 0x069b38602368aeab, 0x669495b820f0ddba, 0xf42013b1b8bf9e3d, 0xcf935efe6439734d, 0xbc1dcf42ca29e3f8, 0x7e6d3ed29f78ad67}, | |||
{0xf3b0f6837ffcddaa, 0x3a76faef934ddf41, 0xcec7ae583a9c8e35, 0xe4dd18c68f0260af, 0x2c0e5df1ad398eaa, 0x478df5236ae22e8c, 0xfb944c46fe865f39, 0xaa48f82f028132ba}, | |||
{0x231b9ae2b76aca77, 0x292a76a712db0b40, 0x5850625dc8134491, 0x73137dd469810fb5, 0x8a12a6a202a474fd, 0xd36fd9daa78bdb80, 0xb34c5e733505706f, 0xbaf1cdca818d9d96}, | |||
{0x2e99781335e8c641, 0xbddfe5cce47d560e, 0xf74e9bf32e5e040c, 0x1d7a709d65996be9, 0x670df36a9cf66cdd, 0xd05ef84a176a2875, 0x0f888e828cb1c44e, 0x1a79e9c9727b052c}, | |||
{0x83497348628d84de, 0x2e9387d51f22a754, 0xb000068da2f852d6, 0x378c9e1190fd6fe5, 0x870027c316de7293, 0xe51a9d4462e047bb, 0x90ecf7f8c6251195, 0x655953bfbed90a9c}, | |||
}; | |||
static uint64_t tweaked512_rc64[10][8]; | |||
static uint32_t tweaked256_rc32[10][8]; | |||
static uint32_t tweaked256_rc32_sseed[10][8]; | |||
static inline uint32_t br_dec32le(const unsigned char *src) { | |||
return (uint32_t)src[0] | |||
| ((uint32_t)src[1] << 8) | |||
| ((uint32_t)src[2] << 16) | |||
| ((uint32_t)src[3] << 24); | |||
} | |||
static void br_range_dec32le(uint32_t *v, size_t num, const unsigned char *src) { | |||
while (num-- > 0) { | |||
*v ++ = br_dec32le(src); | |||
src += 4; | |||
} | |||
} | |||
static inline void br_enc32le(unsigned char *dst, uint32_t x) { | |||
dst[0] = (unsigned char)x; | |||
dst[1] = (unsigned char)(x >> 8); | |||
dst[2] = (unsigned char)(x >> 16); | |||
dst[3] = (unsigned char)(x >> 24); | |||
} | |||
static void br_range_enc32le(unsigned char *dst, const uint32_t *v, size_t num) { | |||
while (num-- > 0) { | |||
br_enc32le(dst, *v ++); | |||
dst += 4; | |||
} | |||
} | |||
static void br_aes_ct64_bitslice_Sbox(uint64_t *q) { | |||
/* | |||
* This S-box implementation is a straightforward translation of | |||
* the circuit described by Boyar and Peralta in "A new | |||
* combinational logic minimization technique with applications | |||
* to cryptology" (https://eprint.iacr.org/2009/191.pdf). | |||
* | |||
* Note that variables x* (input) and s* (output) are numbered | |||
* in "reverse" order (x0 is the high bit, x7 is the low bit). | |||
*/ | |||
uint64_t x0, x1, x2, x3, x4, x5, x6, x7; | |||
uint64_t y1, y2, y3, y4, y5, y6, y7, y8, y9; | |||
uint64_t y10, y11, y12, y13, y14, y15, y16, y17, y18, y19; | |||
uint64_t y20, y21; | |||
uint64_t z0, z1, z2, z3, z4, z5, z6, z7, z8, z9; | |||
uint64_t z10, z11, z12, z13, z14, z15, z16, z17; | |||
uint64_t t0, t1, t2, t3, t4, t5, t6, t7, t8, t9; | |||
uint64_t t10, t11, t12, t13, t14, t15, t16, t17, t18, t19; | |||
uint64_t t20, t21, t22, t23, t24, t25, t26, t27, t28, t29; | |||
uint64_t t30, t31, t32, t33, t34, t35, t36, t37, t38, t39; | |||
uint64_t t40, t41, t42, t43, t44, t45, t46, t47, t48, t49; | |||
uint64_t t50, t51, t52, t53, t54, t55, t56, t57, t58, t59; | |||
uint64_t t60, t61, t62, t63, t64, t65, t66, t67; | |||
uint64_t s0, s1, s2, s3, s4, s5, s6, s7; | |||
x0 = q[7]; | |||
x1 = q[6]; | |||
x2 = q[5]; | |||
x3 = q[4]; | |||
x4 = q[3]; | |||
x5 = q[2]; | |||
x6 = q[1]; | |||
x7 = q[0]; | |||
/* | |||
* Top linear transformation. | |||
*/ | |||
y14 = x3 ^ x5; | |||
y13 = x0 ^ x6; | |||
y9 = x0 ^ x3; | |||
y8 = x0 ^ x5; | |||
t0 = x1 ^ x2; | |||
y1 = t0 ^ x7; | |||
y4 = y1 ^ x3; | |||
y12 = y13 ^ y14; | |||
y2 = y1 ^ x0; | |||
y5 = y1 ^ x6; | |||
y3 = y5 ^ y8; | |||
t1 = x4 ^ y12; | |||
y15 = t1 ^ x5; | |||
y20 = t1 ^ x1; | |||
y6 = y15 ^ x7; | |||
y10 = y15 ^ t0; | |||
y11 = y20 ^ y9; | |||
y7 = x7 ^ y11; | |||
y17 = y10 ^ y11; | |||
y19 = y10 ^ y8; | |||
y16 = t0 ^ y11; | |||
y21 = y13 ^ y16; | |||
y18 = x0 ^ y16; | |||
/* | |||
* Non-linear section. | |||
*/ | |||
t2 = y12 & y15; | |||
t3 = y3 & y6; | |||
t4 = t3 ^ t2; | |||
t5 = y4 & x7; | |||
t6 = t5 ^ t2; | |||
t7 = y13 & y16; | |||
t8 = y5 & y1; | |||
t9 = t8 ^ t7; | |||
t10 = y2 & y7; | |||
t11 = t10 ^ t7; | |||
t12 = y9 & y11; | |||
t13 = y14 & y17; | |||
t14 = t13 ^ t12; | |||
t15 = y8 & y10; | |||
t16 = t15 ^ t12; | |||
t17 = t4 ^ t14; | |||
t18 = t6 ^ t16; | |||
t19 = t9 ^ t14; | |||
t20 = t11 ^ t16; | |||
t21 = t17 ^ y20; | |||
t22 = t18 ^ y19; | |||
t23 = t19 ^ y21; | |||
t24 = t20 ^ y18; | |||
t25 = t21 ^ t22; | |||
t26 = t21 & t23; | |||
t27 = t24 ^ t26; | |||
t28 = t25 & t27; | |||
t29 = t28 ^ t22; | |||
t30 = t23 ^ t24; | |||
t31 = t22 ^ t26; | |||
t32 = t31 & t30; | |||
t33 = t32 ^ t24; | |||
t34 = t23 ^ t33; | |||
t35 = t27 ^ t33; | |||
t36 = t24 & t35; | |||
t37 = t36 ^ t34; | |||
t38 = t27 ^ t36; | |||
t39 = t29 & t38; | |||
t40 = t25 ^ t39; | |||
t41 = t40 ^ t37; | |||
t42 = t29 ^ t33; | |||
t43 = t29 ^ t40; | |||
t44 = t33 ^ t37; | |||
t45 = t42 ^ t41; | |||
z0 = t44 & y15; | |||
z1 = t37 & y6; | |||
z2 = t33 & x7; | |||
z3 = t43 & y16; | |||
z4 = t40 & y1; | |||
z5 = t29 & y7; | |||
z6 = t42 & y11; | |||
z7 = t45 & y17; | |||
z8 = t41 & y10; | |||
z9 = t44 & y12; | |||
z10 = t37 & y3; | |||
z11 = t33 & y4; | |||
z12 = t43 & y13; | |||
z13 = t40 & y5; | |||
z14 = t29 & y2; | |||
z15 = t42 & y9; | |||
z16 = t45 & y14; | |||
z17 = t41 & y8; | |||
/* | |||
* Bottom linear transformation. | |||
*/ | |||
t46 = z15 ^ z16; | |||
t47 = z10 ^ z11; | |||
t48 = z5 ^ z13; | |||
t49 = z9 ^ z10; | |||
t50 = z2 ^ z12; | |||
t51 = z2 ^ z5; | |||
t52 = z7 ^ z8; | |||
t53 = z0 ^ z3; | |||
t54 = z6 ^ z7; | |||
t55 = z16 ^ z17; | |||
t56 = z12 ^ t48; | |||
t57 = t50 ^ t53; | |||
t58 = z4 ^ t46; | |||
t59 = z3 ^ t54; | |||
t60 = t46 ^ t57; | |||
t61 = z14 ^ t57; | |||
t62 = t52 ^ t58; | |||
t63 = t49 ^ t58; | |||
t64 = z4 ^ t59; | |||
t65 = t61 ^ t62; | |||
t66 = z1 ^ t63; | |||
s0 = t59 ^ t63; | |||
s6 = t56 ^ ~t62; | |||
s7 = t48 ^ ~t60; | |||
t67 = t64 ^ t65; | |||
s3 = t53 ^ t66; | |||
s4 = t51 ^ t66; | |||
s5 = t47 ^ t65; | |||
s1 = t64 ^ ~s3; | |||
s2 = t55 ^ ~t67; | |||
q[7] = s0; | |||
q[6] = s1; | |||
q[5] = s2; | |||
q[4] = s3; | |||
q[3] = s4; | |||
q[2] = s5; | |||
q[1] = s6; | |||
q[0] = s7; | |||
} | |||
static void br_aes_ct_bitslice_Sbox(uint32_t *q) { | |||
/* | |||
* This S-box implementation is a straightforward translation of | |||
* the circuit described by Boyar and Peralta in "A new | |||
* combinational logic minimization technique with applications | |||
* to cryptology" (https://eprint.iacr.org/2009/191.pdf). | |||
* | |||
* Note that variables x* (input) and s* (output) are numbered | |||
* in "reverse" order (x0 is the high bit, x7 is the low bit). | |||
*/ | |||
uint32_t x0, x1, x2, x3, x4, x5, x6, x7; | |||
uint32_t y1, y2, y3, y4, y5, y6, y7, y8, y9; | |||
uint32_t y10, y11, y12, y13, y14, y15, y16, y17, y18, y19; | |||
uint32_t y20, y21; | |||
uint32_t z0, z1, z2, z3, z4, z5, z6, z7, z8, z9; | |||
uint32_t z10, z11, z12, z13, z14, z15, z16, z17; | |||
uint32_t t0, t1, t2, t3, t4, t5, t6, t7, t8, t9; | |||
uint32_t t10, t11, t12, t13, t14, t15, t16, t17, t18, t19; | |||
uint32_t t20, t21, t22, t23, t24, t25, t26, t27, t28, t29; | |||
uint32_t t30, t31, t32, t33, t34, t35, t36, t37, t38, t39; | |||
uint32_t t40, t41, t42, t43, t44, t45, t46, t47, t48, t49; | |||
uint32_t t50, t51, t52, t53, t54, t55, t56, t57, t58, t59; | |||
uint32_t t60, t61, t62, t63, t64, t65, t66, t67; | |||
uint32_t s0, s1, s2, s3, s4, s5, s6, s7; | |||
x0 = q[7]; | |||
x1 = q[6]; | |||
x2 = q[5]; | |||
x3 = q[4]; | |||
x4 = q[3]; | |||
x5 = q[2]; | |||
x6 = q[1]; | |||
x7 = q[0]; | |||
/* | |||
* Top linear transformation. | |||
*/ | |||
y14 = x3 ^ x5; | |||
y13 = x0 ^ x6; | |||
y9 = x0 ^ x3; | |||
y8 = x0 ^ x5; | |||
t0 = x1 ^ x2; | |||
y1 = t0 ^ x7; | |||
y4 = y1 ^ x3; | |||
y12 = y13 ^ y14; | |||
y2 = y1 ^ x0; | |||
y5 = y1 ^ x6; | |||
y3 = y5 ^ y8; | |||
t1 = x4 ^ y12; | |||
y15 = t1 ^ x5; | |||
y20 = t1 ^ x1; | |||
y6 = y15 ^ x7; | |||
y10 = y15 ^ t0; | |||
y11 = y20 ^ y9; | |||
y7 = x7 ^ y11; | |||
y17 = y10 ^ y11; | |||
y19 = y10 ^ y8; | |||
y16 = t0 ^ y11; | |||
y21 = y13 ^ y16; | |||
y18 = x0 ^ y16; | |||
/* | |||
* Non-linear section. | |||
*/ | |||
t2 = y12 & y15; | |||
t3 = y3 & y6; | |||
t4 = t3 ^ t2; | |||
t5 = y4 & x7; | |||
t6 = t5 ^ t2; | |||
t7 = y13 & y16; | |||
t8 = y5 & y1; | |||
t9 = t8 ^ t7; | |||
t10 = y2 & y7; | |||
t11 = t10 ^ t7; | |||
t12 = y9 & y11; | |||
t13 = y14 & y17; | |||
t14 = t13 ^ t12; | |||
t15 = y8 & y10; | |||
t16 = t15 ^ t12; | |||
t17 = t4 ^ t14; | |||
t18 = t6 ^ t16; | |||
t19 = t9 ^ t14; | |||
t20 = t11 ^ t16; | |||
t21 = t17 ^ y20; | |||
t22 = t18 ^ y19; | |||
t23 = t19 ^ y21; | |||
t24 = t20 ^ y18; | |||
t25 = t21 ^ t22; | |||
t26 = t21 & t23; | |||
t27 = t24 ^ t26; | |||
t28 = t25 & t27; | |||
t29 = t28 ^ t22; | |||
t30 = t23 ^ t24; | |||
t31 = t22 ^ t26; | |||
t32 = t31 & t30; | |||
t33 = t32 ^ t24; | |||
t34 = t23 ^ t33; | |||
t35 = t27 ^ t33; | |||
t36 = t24 & t35; | |||
t37 = t36 ^ t34; | |||
t38 = t27 ^ t36; | |||
t39 = t29 & t38; | |||
t40 = t25 ^ t39; | |||
t41 = t40 ^ t37; | |||
t42 = t29 ^ t33; | |||
t43 = t29 ^ t40; | |||
t44 = t33 ^ t37; | |||
t45 = t42 ^ t41; | |||
z0 = t44 & y15; | |||
z1 = t37 & y6; | |||
z2 = t33 & x7; | |||
z3 = t43 & y16; | |||
z4 = t40 & y1; | |||
z5 = t29 & y7; | |||
z6 = t42 & y11; | |||
z7 = t45 & y17; | |||
z8 = t41 & y10; | |||
z9 = t44 & y12; | |||
z10 = t37 & y3; | |||
z11 = t33 & y4; | |||
z12 = t43 & y13; | |||
z13 = t40 & y5; | |||
z14 = t29 & y2; | |||
z15 = t42 & y9; | |||
z16 = t45 & y14; | |||
z17 = t41 & y8; | |||
/* | |||
* Bottom linear transformation. | |||
*/ | |||
t46 = z15 ^ z16; | |||
t47 = z10 ^ z11; | |||
t48 = z5 ^ z13; | |||
t49 = z9 ^ z10; | |||
t50 = z2 ^ z12; | |||
t51 = z2 ^ z5; | |||
t52 = z7 ^ z8; | |||
t53 = z0 ^ z3; | |||
t54 = z6 ^ z7; | |||
t55 = z16 ^ z17; | |||
t56 = z12 ^ t48; | |||
t57 = t50 ^ t53; | |||
t58 = z4 ^ t46; | |||
t59 = z3 ^ t54; | |||
t60 = t46 ^ t57; | |||
t61 = z14 ^ t57; | |||
t62 = t52 ^ t58; | |||
t63 = t49 ^ t58; | |||
t64 = z4 ^ t59; | |||
t65 = t61 ^ t62; | |||
t66 = z1 ^ t63; | |||
s0 = t59 ^ t63; | |||
s6 = t56 ^ ~t62; | |||
s7 = t48 ^ ~t60; | |||
t67 = t64 ^ t65; | |||
s3 = t53 ^ t66; | |||
s4 = t51 ^ t66; | |||
s5 = t47 ^ t65; | |||
s1 = t64 ^ ~s3; | |||
s2 = t55 ^ ~t67; | |||
q[7] = s0; | |||
q[6] = s1; | |||
q[5] = s2; | |||
q[4] = s3; | |||
q[3] = s4; | |||
q[2] = s5; | |||
q[1] = s6; | |||
q[0] = s7; | |||
} | |||
static void br_aes_ct_ortho(uint32_t *q) { | |||
#define SWAPN_32(cl, ch, s, x, y) do { \ | |||
uint32_t a, b; \ | |||
a = (x); \ | |||
b = (y); \ | |||
(x) = (a & (uint32_t)(cl)) | ((b & (uint32_t)(cl)) << (s)); \ | |||
(y) = ((a & (uint32_t)(ch)) >> (s)) | (b & (uint32_t)(ch)); \ | |||
} while (0) | |||
#define SWAP2_32(x, y) SWAPN_32(0x55555555, 0xAAAAAAAA, 1, x, y) | |||
#define SWAP4_32(x, y) SWAPN_32(0x33333333, 0xCCCCCCCC, 2, x, y) | |||
#define SWAP8_32(x, y) SWAPN_32(0x0F0F0F0F, 0xF0F0F0F0, 4, x, y) | |||
SWAP2_32(q[0], q[1]); | |||
SWAP2_32(q[2], q[3]); | |||
SWAP2_32(q[4], q[5]); | |||
SWAP2_32(q[6], q[7]); | |||
SWAP4_32(q[0], q[2]); | |||
SWAP4_32(q[1], q[3]); | |||
SWAP4_32(q[4], q[6]); | |||
SWAP4_32(q[5], q[7]); | |||
SWAP8_32(q[0], q[4]); | |||
SWAP8_32(q[1], q[5]); | |||
SWAP8_32(q[2], q[6]); | |||
SWAP8_32(q[3], q[7]); | |||
} | |||
static inline void add_round_key32(uint32_t *q, const uint32_t *sk) { | |||
q[0] ^= sk[0]; | |||
q[1] ^= sk[1]; | |||
q[2] ^= sk[2]; | |||
q[3] ^= sk[3]; | |||
q[4] ^= sk[4]; | |||
q[5] ^= sk[5]; | |||
q[6] ^= sk[6]; | |||
q[7] ^= sk[7]; | |||
} | |||
static inline void shift_rows32(uint32_t *q) { | |||
int i; | |||
for (i = 0; i < 8; i++) { | |||
uint32_t x; | |||
x = q[i]; | |||
q[i] = (x & 0x000000FF) | |||
| ((x & 0x0000FC00) >> 2) | ((x & 0x00000300) << 6) | |||
| ((x & 0x00F00000) >> 4) | ((x & 0x000F0000) << 4) | |||
| ((x & 0xC0000000) >> 6) | ((x & 0x3F000000) << 2); | |||
} | |||
} | |||
static inline uint32_t rotr16(uint32_t x) { | |||
return (x << 16) | (x >> 16); | |||
} | |||
static inline void mix_columns32(uint32_t *q) { | |||
uint32_t q0, q1, q2, q3, q4, q5, q6, q7; | |||
uint32_t r0, r1, r2, r3, r4, r5, r6, r7; | |||
q0 = q[0]; | |||
q1 = q[1]; | |||
q2 = q[2]; | |||
q3 = q[3]; | |||
q4 = q[4]; | |||
q5 = q[5]; | |||
q6 = q[6]; | |||
q7 = q[7]; | |||
r0 = (q0 >> 8) | (q0 << 24); | |||
r1 = (q1 >> 8) | (q1 << 24); | |||
r2 = (q2 >> 8) | (q2 << 24); | |||
r3 = (q3 >> 8) | (q3 << 24); | |||
r4 = (q4 >> 8) | (q4 << 24); | |||
r5 = (q5 >> 8) | (q5 << 24); | |||
r6 = (q6 >> 8) | (q6 << 24); | |||
r7 = (q7 >> 8) | (q7 << 24); | |||
q[0] = q7 ^ r7 ^ r0 ^ rotr16(q0 ^ r0); | |||
q[1] = q0 ^ r0 ^ q7 ^ r7 ^ r1 ^ rotr16(q1 ^ r1); | |||
q[2] = q1 ^ r1 ^ r2 ^ rotr16(q2 ^ r2); | |||
q[3] = q2 ^ r2 ^ q7 ^ r7 ^ r3 ^ rotr16(q3 ^ r3); | |||
q[4] = q3 ^ r3 ^ q7 ^ r7 ^ r4 ^ rotr16(q4 ^ r4); | |||
q[5] = q4 ^ r4 ^ r5 ^ rotr16(q5 ^ r5); | |||
q[6] = q5 ^ r5 ^ r6 ^ rotr16(q6 ^ r6); | |||
q[7] = q6 ^ r6 ^ r7 ^ rotr16(q7 ^ r7); | |||
} | |||
static void br_aes_ct64_ortho(uint64_t *q) { | |||
#define SWAPN(cl, ch, s, x, y) do { \ | |||
uint64_t a, b; \ | |||
a = (x); \ | |||
b = (y); \ | |||
(x) = (a & (uint64_t)(cl)) | ((b & (uint64_t)(cl)) << (s)); \ | |||
(y) = ((a & (uint64_t)(ch)) >> (s)) | (b & (uint64_t)(ch)); \ | |||
} while (0) | |||
#define SWAP2(x, y) SWAPN(0x5555555555555555, 0xAAAAAAAAAAAAAAAA, 1, x, y) | |||
#define SWAP4(x, y) SWAPN(0x3333333333333333, 0xCCCCCCCCCCCCCCCC, 2, x, y) | |||
#define SWAP8(x, y) SWAPN(0x0F0F0F0F0F0F0F0F, 0xF0F0F0F0F0F0F0F0, 4, x, y) | |||
SWAP2(q[0], q[1]); | |||
SWAP2(q[2], q[3]); | |||
SWAP2(q[4], q[5]); | |||
SWAP2(q[6], q[7]); | |||
SWAP4(q[0], q[2]); | |||
SWAP4(q[1], q[3]); | |||
SWAP4(q[4], q[6]); | |||
SWAP4(q[5], q[7]); | |||
SWAP8(q[0], q[4]); | |||
SWAP8(q[1], q[5]); | |||
SWAP8(q[2], q[6]); | |||
SWAP8(q[3], q[7]); | |||
} | |||
static void br_aes_ct64_interleave_in(uint64_t *q0, uint64_t *q1, const uint32_t *w) { | |||
uint64_t x0, x1, x2, x3; | |||
x0 = w[0]; | |||
x1 = w[1]; | |||
x2 = w[2]; | |||
x3 = w[3]; | |||
x0 |= (x0 << 16); | |||
x1 |= (x1 << 16); | |||
x2 |= (x2 << 16); | |||
x3 |= (x3 << 16); | |||
x0 &= (uint64_t)0x0000FFFF0000FFFF; | |||
x1 &= (uint64_t)0x0000FFFF0000FFFF; | |||
x2 &= (uint64_t)0x0000FFFF0000FFFF; | |||
x3 &= (uint64_t)0x0000FFFF0000FFFF; | |||
x0 |= (x0 << 8); | |||
x1 |= (x1 << 8); | |||
x2 |= (x2 << 8); | |||
x3 |= (x3 << 8); | |||
x0 &= (uint64_t)0x00FF00FF00FF00FF; | |||
x1 &= (uint64_t)0x00FF00FF00FF00FF; | |||
x2 &= (uint64_t)0x00FF00FF00FF00FF; | |||
x3 &= (uint64_t)0x00FF00FF00FF00FF; | |||
*q0 = x0 | (x2 << 8); | |||
*q1 = x1 | (x3 << 8); | |||
} | |||
static void br_aes_ct64_interleave_out(uint32_t *w, uint64_t q0, uint64_t q1) { | |||
uint64_t x0, x1, x2, x3; | |||
x0 = q0 & (uint64_t)0x00FF00FF00FF00FF; | |||
x1 = q1 & (uint64_t)0x00FF00FF00FF00FF; | |||
x2 = (q0 >> 8) & (uint64_t)0x00FF00FF00FF00FF; | |||
x3 = (q1 >> 8) & (uint64_t)0x00FF00FF00FF00FF; | |||
x0 |= (x0 >> 8); | |||
x1 |= (x1 >> 8); | |||
x2 |= (x2 >> 8); | |||
x3 |= (x3 >> 8); | |||
x0 &= (uint64_t)0x0000FFFF0000FFFF; | |||
x1 &= (uint64_t)0x0000FFFF0000FFFF; | |||
x2 &= (uint64_t)0x0000FFFF0000FFFF; | |||
x3 &= (uint64_t)0x0000FFFF0000FFFF; | |||
w[0] = (uint32_t)x0 | (uint32_t)(x0 >> 16); | |||
w[1] = (uint32_t)x1 | (uint32_t)(x1 >> 16); | |||
w[2] = (uint32_t)x2 | (uint32_t)(x2 >> 16); | |||
w[3] = (uint32_t)x3 | (uint32_t)(x3 >> 16); | |||
} | |||
static inline void add_round_key(uint64_t *q, const uint64_t *sk) { | |||
q[0] ^= sk[0]; | |||
q[1] ^= sk[1]; | |||
q[2] ^= sk[2]; | |||
q[3] ^= sk[3]; | |||
q[4] ^= sk[4]; | |||
q[5] ^= sk[5]; | |||
q[6] ^= sk[6]; | |||
q[7] ^= sk[7]; | |||
} | |||
static inline void shift_rows(uint64_t *q) { | |||
int i; | |||
for (i = 0; i < 8; i++) { | |||
uint64_t x; | |||
x = q[i]; | |||
q[i] = (x & (uint64_t)0x000000000000FFFF) | |||
| ((x & (uint64_t)0x00000000FFF00000) >> 4) | |||
| ((x & (uint64_t)0x00000000000F0000) << 12) | |||
| ((x & (uint64_t)0x0000FF0000000000) >> 8) | |||
| ((x & (uint64_t)0x000000FF00000000) << 8) | |||
| ((x & (uint64_t)0xF000000000000000) >> 12) | |||
| ((x & (uint64_t)0x0FFF000000000000) << 4); | |||
} | |||
} | |||
static inline uint64_t rotr32(uint64_t x) { | |||
return (x << 32) | (x >> 32); | |||
} | |||
static inline void mix_columns(uint64_t *q) { | |||
uint64_t q0, q1, q2, q3, q4, q5, q6, q7; | |||
uint64_t r0, r1, r2, r3, r4, r5, r6, r7; | |||
q0 = q[0]; | |||
q1 = q[1]; | |||
q2 = q[2]; | |||
q3 = q[3]; | |||
q4 = q[4]; | |||
q5 = q[5]; | |||
q6 = q[6]; | |||
q7 = q[7]; | |||
r0 = (q0 >> 16) | (q0 << 48); | |||
r1 = (q1 >> 16) | (q1 << 48); | |||
r2 = (q2 >> 16) | (q2 << 48); | |||
r3 = (q3 >> 16) | (q3 << 48); | |||
r4 = (q4 >> 16) | (q4 << 48); | |||
r5 = (q5 >> 16) | (q5 << 48); | |||
r6 = (q6 >> 16) | (q6 << 48); | |||
r7 = (q7 >> 16) | (q7 << 48); | |||
q[0] = q7 ^ r7 ^ r0 ^ rotr32(q0 ^ r0); | |||
q[1] = q0 ^ r0 ^ q7 ^ r7 ^ r1 ^ rotr32(q1 ^ r1); | |||
q[2] = q1 ^ r1 ^ r2 ^ rotr32(q2 ^ r2); | |||
q[3] = q2 ^ r2 ^ q7 ^ r7 ^ r3 ^ rotr32(q3 ^ r3); | |||
q[4] = q3 ^ r3 ^ q7 ^ r7 ^ r4 ^ rotr32(q4 ^ r4); | |||
q[5] = q4 ^ r4 ^ r5 ^ rotr32(q5 ^ r5); | |||
q[6] = q5 ^ r5 ^ r6 ^ rotr32(q6 ^ r6); | |||
q[7] = q6 ^ r6 ^ r7 ^ rotr32(q7 ^ r7); | |||
} | |||
static void interleave_constant(uint64_t *out, const unsigned char *in) { | |||
uint32_t tmp_32_constant[16]; | |||
int i; | |||
br_range_dec32le(tmp_32_constant, 16, in); | |||
for (i = 0; i < 4; i++) { | |||
br_aes_ct64_interleave_in(&out[i], &out[i + 4], tmp_32_constant + (i << 2)); | |||
} | |||
br_aes_ct64_ortho(out); | |||
} | |||
static void interleave_constant32(uint32_t *out, const unsigned char *in) { | |||
int i; | |||
for (i = 0; i < 4; i++) { | |||
out[2 * i] = br_dec32le(in + 4 * i); | |||
out[2 * i + 1] = br_dec32le(in + 4 * i + 16); | |||
} | |||
br_aes_ct_ortho(out); | |||
} | |||
void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_tweak_constants( | |||
const unsigned char *pk_seed, const unsigned char *sk_seed, | |||
unsigned long long seed_length) { | |||
unsigned char buf[40 * 16]; | |||
int i; | |||
/* Use the standard constants to generate tweaked ones. */ | |||
memcpy((uint8_t *)tweaked512_rc64, (uint8_t *)haraka512_rc64, 40 * 16); | |||
/* Constants for sk.seed */ | |||
if (sk_seed != NULL) { | |||
PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka_S( | |||
buf, 40 * 16, sk_seed, seed_length); | |||
/* Interleave constants */ | |||
for (i = 0; i < 10; i++) { | |||
interleave_constant32(tweaked256_rc32_sseed[i], buf + 32 * i); | |||
} | |||
} | |||
/* Constants for pk.seed */ | |||
PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka_S( | |||
buf, 40 * 16, pk_seed, seed_length); | |||
for (i = 0; i < 10; i++) { | |||
interleave_constant32(tweaked256_rc32[i], buf + 32 * i); | |||
interleave_constant(tweaked512_rc64[i], buf + 64 * i); | |||
} | |||
} | |||
static void haraka_S_absorb(unsigned char *s, | |||
const unsigned char *m, unsigned long long mlen, | |||
unsigned char p) { | |||
unsigned long long i; | |||
unsigned char t[HARAKAS_RATE]; | |||
while (mlen >= HARAKAS_RATE) { | |||
/* XOR block to state */ | |||
for (i = 0; i < HARAKAS_RATE; ++i) { | |||
s[i] ^= m[i]; | |||
} | |||
PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka512_perm(s, s); | |||
mlen -= HARAKAS_RATE; | |||
m += HARAKAS_RATE; | |||
} | |||
for (i = 0; i < HARAKAS_RATE; ++i) { | |||
t[i] = 0; | |||
} | |||
for (i = 0; i < mlen; ++i) { | |||
t[i] = m[i]; | |||
} | |||
t[i] = p; | |||
t[HARAKAS_RATE - 1] |= 128; | |||
for (i = 0; i < HARAKAS_RATE; ++i) { | |||
s[i] ^= t[i]; | |||
} | |||
} | |||
static void haraka_S_squeezeblocks(unsigned char *h, unsigned long long nblocks, | |||
unsigned char *s) { | |||
while (nblocks > 0) { | |||
PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka512_perm(s, s); | |||
memcpy(h, s, HARAKAS_RATE); | |||
h += HARAKAS_RATE; | |||
nblocks--; | |||
} | |||
} | |||
void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka_S_inc_init(uint8_t *s_inc) { | |||
size_t i; | |||
for (i = 0; i < 64; i++) { | |||
s_inc[i] = 0; | |||
} | |||
s_inc[64] = 0; | |||
} | |||
void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka_S_inc_absorb(uint8_t *s_inc, const uint8_t *m, size_t mlen) { | |||
size_t i; | |||
/* Recall that s_inc[64] is the non-absorbed bytes xored into the state */ | |||
while (mlen + s_inc[64] >= HARAKAS_RATE) { | |||
for (i = 0; i < (size_t)(HARAKAS_RATE - s_inc[64]); i++) { | |||
/* Take the i'th byte from message | |||
xor with the s_inc[64] + i'th byte of the state */ | |||
s_inc[s_inc[64] + i] ^= m[i]; | |||
} | |||
mlen -= (size_t)(HARAKAS_RATE - s_inc[64]); | |||
m += HARAKAS_RATE - s_inc[64]; | |||
s_inc[64] = 0; | |||
PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka512_perm(s_inc, s_inc); | |||
} | |||
for (i = 0; i < mlen; i++) { | |||
s_inc[s_inc[64] + i] ^= m[i]; | |||
} | |||
s_inc[64] = (uint8_t)(mlen + s_inc[64]); | |||
} | |||
void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka_S_inc_finalize(uint8_t *s_inc) { | |||
/* After haraka_S_inc_absorb, we are guaranteed that s_inc[64] < HARAKAS_RATE, | |||
so we can always use one more byte for p in the current state. */ | |||
s_inc[s_inc[64]] ^= 0x1F; | |||
s_inc[HARAKAS_RATE - 1] ^= 128; | |||
s_inc[64] = 0; | |||
} | |||
void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka_S_inc_squeeze(uint8_t *out, size_t outlen, uint8_t *s_inc) { | |||
uint8_t i; | |||
/* First consume any bytes we still have sitting around */ | |||
for (i = 0; i < outlen && i < s_inc[64]; i++) { | |||
/* There are s_inc[64] bytes left, so r - s_inc[64] is the first | |||
available byte. We consume from there, i.e., up to r. */ | |||
out[i] = s_inc[(HARAKAS_RATE - s_inc[64] + i)]; | |||
} | |||
out += i; | |||
outlen -= i; | |||
s_inc[64] = (uint8_t)(s_inc[64] - i); | |||
/* Then squeeze the remaining necessary blocks */ | |||
while (outlen > 0) { | |||
PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka512_perm(s_inc, s_inc); | |||
for (i = 0; i < outlen && i < HARAKAS_RATE; i++) { | |||
out[i] = s_inc[i]; | |||
} | |||
out += i; | |||
outlen -= i; | |||
s_inc[64] = (uint8_t)(HARAKAS_RATE - i); | |||
} | |||
} | |||
void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka_S(unsigned char *out, unsigned long long outlen, const unsigned char *in, unsigned long long inlen) { | |||
unsigned long long i; | |||
unsigned char s[64]; | |||
unsigned char d[32]; | |||
for (i = 0; i < 64; i++) { | |||
s[i] = 0; | |||
} | |||
haraka_S_absorb(s, in, inlen, 0x1F); | |||
haraka_S_squeezeblocks(out, outlen / 32, s); | |||
out += (outlen / 32) * 32; | |||
if (outlen % 32) { | |||
haraka_S_squeezeblocks(d, 1, s); | |||
for (i = 0; i < outlen % 32; i++) { | |||
out[i] = d[i]; | |||
} | |||
} | |||
} | |||
void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka512_perm(unsigned char *out, const unsigned char *in) { | |||
uint32_t w[16]; | |||
uint64_t q[8], tmp_q; | |||
unsigned int i, j; | |||
br_range_dec32le(w, 16, in); | |||
for (i = 0; i < 4; i++) { | |||
br_aes_ct64_interleave_in(&q[i], &q[i + 4], w + (i << 2)); | |||
} | |||
br_aes_ct64_ortho(q); | |||
/* AES rounds */ | |||
for (i = 0; i < 5; i++) { | |||
for (j = 0; j < 2; j++) { | |||
br_aes_ct64_bitslice_Sbox(q); | |||
shift_rows(q); | |||
mix_columns(q); | |||
add_round_key(q, tweaked512_rc64[2 * i + j]); | |||
} | |||
/* Mix states */ | |||
for (j = 0; j < 8; j++) { | |||
tmp_q = q[j]; | |||
q[j] = (tmp_q & 0x0001000100010001) << 5 | | |||
(tmp_q & 0x0002000200020002) << 12 | | |||
(tmp_q & 0x0004000400040004) >> 1 | | |||
(tmp_q & 0x0008000800080008) << 6 | | |||
(tmp_q & 0x0020002000200020) << 9 | | |||
(tmp_q & 0x0040004000400040) >> 4 | | |||
(tmp_q & 0x0080008000800080) << 3 | | |||
(tmp_q & 0x2100210021002100) >> 5 | | |||
(tmp_q & 0x0210021002100210) << 2 | | |||
(tmp_q & 0x0800080008000800) << 4 | | |||
(tmp_q & 0x1000100010001000) >> 12 | | |||
(tmp_q & 0x4000400040004000) >> 10 | | |||
(tmp_q & 0x8400840084008400) >> 3; | |||
} | |||
} | |||
br_aes_ct64_ortho(q); | |||
for (i = 0; i < 4; i ++) { | |||
br_aes_ct64_interleave_out(w + (i << 2), q[i], q[i + 4]); | |||
} | |||
br_range_enc32le(out, w, 16); | |||
} | |||
void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka512(unsigned char *out, const unsigned char *in) { | |||
int i; | |||
unsigned char buf[64]; | |||
PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka512_perm(buf, in); | |||
/* Feed-forward */ | |||
for (i = 0; i < 64; i++) { | |||
buf[i] = buf[i] ^ in[i]; | |||
} | |||
/* Truncated */ | |||
memcpy(out, buf + 8, 8); | |||
memcpy(out + 8, buf + 24, 8); | |||
memcpy(out + 16, buf + 32, 8); | |||
memcpy(out + 24, buf + 48, 8); | |||
} | |||
void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka256(unsigned char *out, const unsigned char *in) { | |||
uint32_t q[8], tmp_q; | |||
int i, j; | |||
for (i = 0; i < 4; i++) { | |||
q[2 * i] = br_dec32le(in + 4 * i); | |||
q[2 * i + 1] = br_dec32le(in + 4 * i + 16); | |||
} | |||
br_aes_ct_ortho(q); | |||
/* AES rounds */ | |||
for (i = 0; i < 5; i++) { | |||
for (j = 0; j < 2; j++) { | |||
br_aes_ct_bitslice_Sbox(q); | |||
shift_rows32(q); | |||
mix_columns32(q); | |||
add_round_key32(q, tweaked256_rc32[2 * i + j]); | |||
} | |||
/* Mix states */ | |||
for (j = 0; j < 8; j++) { | |||
tmp_q = q[j]; | |||
q[j] = (tmp_q & 0x81818181) | | |||
(tmp_q & 0x02020202) << 1 | | |||
(tmp_q & 0x04040404) << 2 | | |||
(tmp_q & 0x08080808) << 3 | | |||
(tmp_q & 0x10101010) >> 3 | | |||
(tmp_q & 0x20202020) >> 2 | | |||
(tmp_q & 0x40404040) >> 1; | |||
} | |||
} | |||
br_aes_ct_ortho(q); | |||
for (i = 0; i < 4; i++) { | |||
br_enc32le(out + 4 * i, q[2 * i]); | |||
br_enc32le(out + 4 * i + 16, q[2 * i + 1]); | |||
} | |||
for (i = 0; i < 32; i++) { | |||
out[i] ^= in[i]; | |||
} | |||
} | |||
void PQCLEAN_SPHINCSHARAKA128SSIMPLE_CLEAN_haraka256_sk(unsigned char *out, const unsigned char *in) { | |||
uint32_t q[8], tmp_q; | |||
int i, j; | |||
for (i = 0; i < 4; i++) { | |||
q[2 * i] = br_dec32le(in + 4 * i); | |||
q[2 * i + 1] = br_dec32le(in + 4 * i + 16); | |||
} | |||
br_aes_ct_ortho(q); | |||
/* AES rounds */ | |||
for (i = 0; i < 5; i++) { | |||
for (j = 0; j < 2; j++) { | |||
br_aes_ct_bitslice_Sbox(q); | |||
shift_rows32(q); | |||
mix_columns32(q); | |||
add_round_key32(q, tweaked256_rc32_sseed[2 * i + j]); | |||
} | |||
/* Mix states */ | |||
for (j = 0; j < 8; j++) { | |||
tmp_q = q[j]; | |||
q[j] = (tmp_q & 0x81818181) | | |||
(tmp_q & 0x02020202) << 1 | | |||
(tmp_q & 0x04040404) << 2 | | |||
(tmp_q & 0x08080808) << 3 | | |||
(tmp_q & 0x10101010) >> 3 | | |||
(tmp_q & 0x20202020) >> 2 | | |||
(tmp_q & 0x40404040) >> 1; | |||
} | |||
} | |||
br_aes_ct_ortho(q); | |||
for (i = 0; i < 4; i++) { | |||
br_enc32le(out + 4 * i, q[2 * i]); | |||
br_enc32le(out + 4 * i + 16, q[2 * i + 1]); | |||
} | |||
for (i = 0; i < 32; i++) { | |||
out[i] ^= in[i]; | |||
} | |||
} |
@@ -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 |
@@ -0,0 +1,22 @@ | |||
#ifndef SPX_HASH_H | |||
#define SPX_HASH_H | |||
#include <stdint.h> | |||
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 |
@@ -0,0 +1,86 @@ | |||
#include <stdint.h> | |||
#include <string.h> | |||
#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); | |||
} |
@@ -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 |
@@ -0,0 +1,344 @@ | |||
#include <stddef.h> | |||
#include <stdint.h> | |||
#include <string.h> | |||
#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; | |||
} |
@@ -0,0 +1,22 @@ | |||
#ifndef SPX_THASH_H | |||
#define SPX_THASH_H | |||
#include <stdint.h> | |||
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 |
@@ -0,0 +1,78 @@ | |||
#include <stdint.h> | |||
#include <string.h> | |||
#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); | |||
} |
@@ -0,0 +1,192 @@ | |||
#include <stddef.h> | |||
#include <string.h> | |||
#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); | |||
} |
@@ -0,0 +1,60 @@ | |||
#ifndef SPX_UTILS_H | |||
#define SPX_UTILS_H | |||
#include "params.h" | |||
#include <stddef.h> | |||
#include <stdint.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); | |||
/** | |||
* 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 |
@@ -0,0 +1,161 @@ | |||
#include <stdint.h> | |||
#include <string.h> | |||
#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); | |||
} | |||
} |
@@ -0,0 +1,38 @@ | |||
#ifndef SPX_WOTS_H | |||
#define SPX_WOTS_H | |||
#include "params.h" | |||
#include <stdint.h> | |||
/** | |||
* 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 |
@@ -0,0 +1,27 @@ | |||
name: SPHINCS+ | |||
type: signature | |||
claimed-nist-level: 3 | |||
length-public-key: 48 | |||
length-secret-key: 96 | |||
length-signature: 35664 | |||
testvectors-sha256: a88d3adbeb5c1805a90e506c93f5000b266d1227f1621c0f77adf75bdbe4ba02 | |||
principal-submitter: Andreas Hülsing | |||
auxiliary-submitters: | |||
- Jean-Philippe Aumasson | |||
- Daniel J. Bernstein, | |||
- Christoph Dobraunig | |||
- Maria Eichlseder | |||
- Scott Fluhrer | |||
- Stefan-Lukas Gazdag | |||
- Panos Kampanakis | |||
- Stefan Kölbl | |||
- Tanja Lange | |||
- Martin M. Lauridsen | |||
- Florian Mendel | |||
- Ruben Niederhagen | |||
- Christian Rechberger | |||
- Joost Rijneveld | |||
- Peter Schwabe | |||
implementations: | |||
- name: clean | |||
version: https://github.com/sphincs/sphincsplus/commit/77755c94d0bc744478044d6efbb888dc13156441 |
@@ -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 | |||
<http://creativecommons.org/publicdomain/zero/1.0/> |
@@ -0,0 +1,20 @@ | |||
# This Makefile can be used with GNU Make or BSD Make | |||
LIB=libsphincs-haraka-192f-robust_clean.a | |||
HEADERS = params.h address.h wots.h utils.h fors.h api.h hash.h thash.h haraka.h | |||
OBJECTS = address.o wots.o utils.o fors.o sign.o hash_haraka.o thash_haraka_robust.o haraka.o | |||
CFLAGS=-O3 -Wall -Wconversion -Wextra -Wpedantic -Wvla -Werror -Wmissing-prototypes -std=c99 -I../../../common $(EXTRAFLAGS) | |||
all: $(LIB) | |||
%.o: %.c $(HEADERS) | |||
$(CC) $(CFLAGS) -c -o $@ $< | |||
$(LIB): $(OBJECTS) | |||
$(AR) -r $@ $(OBJECTS) | |||
clean: | |||
$(RM) $(OBJECTS) | |||
$(RM) $(LIB) |
@@ -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) |
@@ -0,0 +1,78 @@ | |||
#include <stdint.h> | |||
#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; | |||
} |
@@ -0,0 +1,50 @@ | |||
#ifndef SPX_ADDRESS_H | |||
#define SPX_ADDRESS_H | |||
#include <stdint.h> | |||
#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 |
@@ -0,0 +1,78 @@ | |||
#ifndef PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_API_H | |||
#define PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_API_H | |||
#include <stddef.h> | |||
#include <stdint.h> | |||
#define PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_CRYPTO_ALGNAME "SPHINCS+" | |||
#define PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_CRYPTO_SECRETKEYBYTES 96 | |||
#define PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_CRYPTO_PUBLICKEYBYTES 48 | |||
#define PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_CRYPTO_BYTES 35664 | |||
#define PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_CRYPTO_SEEDBYTES 72 | |||
/* | |||
* Returns the length of a secret key, in bytes | |||
*/ | |||
size_t PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_crypto_sign_secretkeybytes(void); | |||
/* | |||
* Returns the length of a public key, in bytes | |||
*/ | |||
size_t PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_crypto_sign_publickeybytes(void); | |||
/* | |||
* Returns the length of a signature, in bytes | |||
*/ | |||
size_t PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_crypto_sign_bytes(void); | |||
/* | |||
* Returns the length of the seed required to generate a key pair, in bytes | |||
*/ | |||
size_t PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_crypto_sign_seedbytes(void); | |||
/* | |||
* Generates a SPHINCS+ key pair given a seed. | |||
* Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] | |||
* Format pk: [root || PUB_SEED] | |||
*/ | |||
int PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_crypto_sign_seed_keypair( | |||
uint8_t *pk, uint8_t *sk, const uint8_t *seed); | |||
/* | |||
* Generates a SPHINCS+ key pair. | |||
* Format sk: [SK_SEED || SK_PRF || PUB_SEED || root] | |||
* Format pk: [root || PUB_SEED] | |||
*/ | |||
int PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_crypto_sign_keypair( | |||
uint8_t *pk, uint8_t *sk); | |||
/** | |||
* Returns an array containing a detached signature. | |||
*/ | |||
int PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_crypto_sign_signature( | |||
uint8_t *sig, size_t *siglen, | |||
const uint8_t *m, size_t mlen, const uint8_t *sk); | |||
/** | |||
* Verifies a detached signature and message under a given public key. | |||
*/ | |||
int PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_crypto_sign_verify( | |||
const uint8_t *sig, size_t siglen, | |||
const uint8_t *m, size_t mlen, const uint8_t *pk); | |||
/** | |||
* Returns an array containing the signature followed by the message. | |||
*/ | |||
int PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_crypto_sign( | |||
uint8_t *sm, size_t *smlen, | |||
const uint8_t *m, size_t mlen, const uint8_t *sk); | |||
/** | |||
* Verifies a given signature-message pair under a given public key. | |||
*/ | |||
int PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_crypto_sign_open( | |||
uint8_t *m, size_t *mlen, | |||
const uint8_t *sm, size_t smlen, const uint8_t *pk); | |||
#endif |
@@ -0,0 +1,164 @@ | |||
#include <stdint.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#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); | |||
} |
@@ -0,0 +1,30 @@ | |||
#ifndef SPX_FORS_H | |||
#define SPX_FORS_H | |||
#include <stdint.h> | |||
#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 |
@@ -0,0 +1,965 @@ | |||
/* | |||
* Constant time implementation of the Haraka hash function. | |||
* | |||
* The bit-sliced implementation of the AES round functions are | |||
* based on the AES implementation in BearSSL written | |||
* by Thomas Pornin <pornin@bolet.org> | |||
*/ | |||
#include <stdint.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include "haraka.h" | |||
#define HARAKAS_RATE 32 | |||
static const uint64_t haraka512_rc64[10][8] = { | |||
{0x24cf0ab9086f628b, 0xbdd6eeecc83b8382, 0xd96fb0306cdad0a7, 0xaace082ac8f95f89, 0x449d8e8870d7041f, 0x49bb2f80b2b3e2f8, 0x0569ae98d93bb258, 0x23dc9691e7d6a4b1}, | |||
{0xd8ba10ede0fe5b6e, 0x7ecf7dbe424c7b8e, 0x6ea9949c6df62a31, 0xbf3f3c97ec9c313e, 0x241d03a196a1861e, 0xead3a51116e5a2ea, 0x77d479fcad9574e3, 0x18657a1af894b7a0}, | |||
{0x10671e1a7f595522, 0xd9a00ff675d28c7b, 0x2f1edf0d2b9ba661, 0xb8ff58b8e3de45f9, 0xee29261da9865c02, 0xd1532aa4b50bdf43, 0x8bf858159b231bb1, 0xdf17439d22d4f599}, | |||
{0xdd4b2f0870b918c0, 0x757a81f3b39b1bb6, 0x7a5c556898952e3f, 0x7dd70a16d915d87a, 0x3ae61971982b8301, 0xc3ab319e030412be, 0x17c0033ac094a8cb, 0x5a0630fc1a8dc4ef}, | |||
{0x17708988c1632f73, 0xf92ddae090b44f4f, 0x11ac0285c43aa314, 0x509059941936b8ba, 0xd03e152fa2ce9b69, 0x3fbcbcb63a32998b, 0x6204696d692254f7, 0x915542ed93ec59b4}, | |||
{0xf4ed94aa8879236e, 0xff6cb41cd38e03c0, 0x069b38602368aeab, 0x669495b820f0ddba, 0xf42013b1b8bf9e3d, 0xcf935efe6439734d, 0xbc1dcf42ca29e3f8, 0x7e6d3ed29f78ad67}, | |||
{0xf3b0f6837ffcddaa, 0x3a76faef934ddf41, 0xcec7ae583a9c8e35, 0xe4dd18c68f0260af, 0x2c0e5df1ad398eaa, 0x478df5236ae22e8c, 0xfb944c46fe865f39, 0xaa48f82f028132ba}, | |||
{0x231b9ae2b76aca77, 0x292a76a712db0b40, 0x5850625dc8134491, 0x73137dd469810fb5, 0x8a12a6a202a474fd, 0xd36fd9daa78bdb80, 0xb34c5e733505706f, 0xbaf1cdca818d9d96}, | |||
{0x2e99781335e8c641, 0xbddfe5cce47d560e, 0xf74e9bf32e5e040c, 0x1d7a709d65996be9, 0x670df36a9cf66cdd, 0xd05ef84a176a2875, 0x0f888e828cb1c44e, 0x1a79e9c9727b052c}, | |||
{0x83497348628d84de, 0x2e9387d51f22a754, 0xb000068da2f852d6, 0x378c9e1190fd6fe5, 0x870027c316de7293, 0xe51a9d4462e047bb, 0x90ecf7f8c6251195, 0x655953bfbed90a9c}, | |||
}; | |||
static uint64_t tweaked512_rc64[10][8]; | |||
static uint32_t tweaked256_rc32[10][8]; | |||
static uint32_t tweaked256_rc32_sseed[10][8]; | |||
static inline uint32_t br_dec32le(const unsigned char *src) { | |||
return (uint32_t)src[0] | |||
| ((uint32_t)src[1] << 8) | |||
| ((uint32_t)src[2] << 16) | |||
| ((uint32_t)src[3] << 24); | |||
} | |||
static void br_range_dec32le(uint32_t *v, size_t num, const unsigned char *src) { | |||
while (num-- > 0) { | |||
*v ++ = br_dec32le(src); | |||
src += 4; | |||
} | |||
} | |||
static inline void br_enc32le(unsigned char *dst, uint32_t x) { | |||
dst[0] = (unsigned char)x; | |||
dst[1] = (unsigned char)(x >> 8); | |||
dst[2] = (unsigned char)(x >> 16); | |||
dst[3] = (unsigned char)(x >> 24); | |||
} | |||
static void br_range_enc32le(unsigned char *dst, const uint32_t *v, size_t num) { | |||
while (num-- > 0) { | |||
br_enc32le(dst, *v ++); | |||
dst += 4; | |||
} | |||
} | |||
static void br_aes_ct64_bitslice_Sbox(uint64_t *q) { | |||
/* | |||
* This S-box implementation is a straightforward translation of | |||
* the circuit described by Boyar and Peralta in "A new | |||
* combinational logic minimization technique with applications | |||
* to cryptology" (https://eprint.iacr.org/2009/191.pdf). | |||
* | |||
* Note that variables x* (input) and s* (output) are numbered | |||
* in "reverse" order (x0 is the high bit, x7 is the low bit). | |||
*/ | |||
uint64_t x0, x1, x2, x3, x4, x5, x6, x7; | |||
uint64_t y1, y2, y3, y4, y5, y6, y7, y8, y9; | |||
uint64_t y10, y11, y12, y13, y14, y15, y16, y17, y18, y19; | |||
uint64_t y20, y21; | |||
uint64_t z0, z1, z2, z3, z4, z5, z6, z7, z8, z9; | |||
uint64_t z10, z11, z12, z13, z14, z15, z16, z17; | |||
uint64_t t0, t1, t2, t3, t4, t5, t6, t7, t8, t9; | |||
uint64_t t10, t11, t12, t13, t14, t15, t16, t17, t18, t19; | |||
uint64_t t20, t21, t22, t23, t24, t25, t26, t27, t28, t29; | |||
uint64_t t30, t31, t32, t33, t34, t35, t36, t37, t38, t39; | |||
uint64_t t40, t41, t42, t43, t44, t45, t46, t47, t48, t49; | |||
uint64_t t50, t51, t52, t53, t54, t55, t56, t57, t58, t59; | |||
uint64_t t60, t61, t62, t63, t64, t65, t66, t67; | |||
uint64_t s0, s1, s2, s3, s4, s5, s6, s7; | |||
x0 = q[7]; | |||
x1 = q[6]; | |||
x2 = q[5]; | |||
x3 = q[4]; | |||
x4 = q[3]; | |||
x5 = q[2]; | |||
x6 = q[1]; | |||
x7 = q[0]; | |||
/* | |||
* Top linear transformation. | |||
*/ | |||
y14 = x3 ^ x5; | |||
y13 = x0 ^ x6; | |||
y9 = x0 ^ x3; | |||
y8 = x0 ^ x5; | |||
t0 = x1 ^ x2; | |||
y1 = t0 ^ x7; | |||
y4 = y1 ^ x3; | |||
y12 = y13 ^ y14; | |||
y2 = y1 ^ x0; | |||
y5 = y1 ^ x6; | |||
y3 = y5 ^ y8; | |||
t1 = x4 ^ y12; | |||
y15 = t1 ^ x5; | |||
y20 = t1 ^ x1; | |||
y6 = y15 ^ x7; | |||
y10 = y15 ^ t0; | |||
y11 = y20 ^ y9; | |||
y7 = x7 ^ y11; | |||
y17 = y10 ^ y11; | |||
y19 = y10 ^ y8; | |||
y16 = t0 ^ y11; | |||
y21 = y13 ^ y16; | |||
y18 = x0 ^ y16; | |||
/* | |||
* Non-linear section. | |||
*/ | |||
t2 = y12 & y15; | |||
t3 = y3 & y6; | |||
t4 = t3 ^ t2; | |||
t5 = y4 & x7; | |||
t6 = t5 ^ t2; | |||
t7 = y13 & y16; | |||
t8 = y5 & y1; | |||
t9 = t8 ^ t7; | |||
t10 = y2 & y7; | |||
t11 = t10 ^ t7; | |||
t12 = y9 & y11; | |||
t13 = y14 & y17; | |||
t14 = t13 ^ t12; | |||
t15 = y8 & y10; | |||
t16 = t15 ^ t12; | |||
t17 = t4 ^ t14; | |||
t18 = t6 ^ t16; | |||
t19 = t9 ^ t14; | |||
t20 = t11 ^ t16; | |||
t21 = t17 ^ y20; | |||
t22 = t18 ^ y19; | |||
t23 = t19 ^ y21; | |||
t24 = t20 ^ y18; | |||
t25 = t21 ^ t22; | |||
t26 = t21 & t23; | |||
t27 = t24 ^ t26; | |||
t28 = t25 & t27; | |||
t29 = t28 ^ t22; | |||
t30 = t23 ^ t24; | |||
t31 = t22 ^ t26; | |||
t32 = t31 & t30; | |||
t33 = t32 ^ t24; | |||
t34 = t23 ^ t33; | |||
t35 = t27 ^ t33; | |||
t36 = t24 & t35; | |||
t37 = t36 ^ t34; | |||
t38 = t27 ^ t36; | |||
t39 = t29 & t38; | |||
t40 = t25 ^ t39; | |||
t41 = t40 ^ t37; | |||
t42 = t29 ^ t33; | |||
t43 = t29 ^ t40; | |||
t44 = t33 ^ t37; | |||
t45 = t42 ^ t41; | |||
z0 = t44 & y15; | |||
z1 = t37 & y6; | |||
z2 = t33 & x7; | |||
z3 = t43 & y16; | |||
z4 = t40 & y1; | |||
z5 = t29 & y7; | |||
z6 = t42 & y11; | |||
z7 = t45 & y17; | |||
z8 = t41 & y10; | |||
z9 = t44 & y12; | |||
z10 = t37 & y3; | |||
z11 = t33 & y4; | |||
z12 = t43 & y13; | |||
z13 = t40 & y5; | |||
z14 = t29 & y2; | |||
z15 = t42 & y9; | |||
z16 = t45 & y14; | |||
z17 = t41 & y8; | |||
/* | |||
* Bottom linear transformation. | |||
*/ | |||
t46 = z15 ^ z16; | |||
t47 = z10 ^ z11; | |||
t48 = z5 ^ z13; | |||
t49 = z9 ^ z10; | |||
t50 = z2 ^ z12; | |||
t51 = z2 ^ z5; | |||
t52 = z7 ^ z8; | |||
t53 = z0 ^ z3; | |||
t54 = z6 ^ z7; | |||
t55 = z16 ^ z17; | |||
t56 = z12 ^ t48; | |||
t57 = t50 ^ t53; | |||
t58 = z4 ^ t46; | |||
t59 = z3 ^ t54; | |||
t60 = t46 ^ t57; | |||
t61 = z14 ^ t57; | |||
t62 = t52 ^ t58; | |||
t63 = t49 ^ t58; | |||
t64 = z4 ^ t59; | |||
t65 = t61 ^ t62; | |||
t66 = z1 ^ t63; | |||
s0 = t59 ^ t63; | |||
s6 = t56 ^ ~t62; | |||
s7 = t48 ^ ~t60; | |||
t67 = t64 ^ t65; | |||
s3 = t53 ^ t66; | |||
s4 = t51 ^ t66; | |||
s5 = t47 ^ t65; | |||
s1 = t64 ^ ~s3; | |||
s2 = t55 ^ ~t67; | |||
q[7] = s0; | |||
q[6] = s1; | |||
q[5] = s2; | |||
q[4] = s3; | |||
q[3] = s4; | |||
q[2] = s5; | |||
q[1] = s6; | |||
q[0] = s7; | |||
} | |||
static void br_aes_ct_bitslice_Sbox(uint32_t *q) { | |||
/* | |||
* This S-box implementation is a straightforward translation of | |||
* the circuit described by Boyar and Peralta in "A new | |||
* combinational logic minimization technique with applications | |||
* to cryptology" (https://eprint.iacr.org/2009/191.pdf). | |||
* | |||
* Note that variables x* (input) and s* (output) are numbered | |||
* in "reverse" order (x0 is the high bit, x7 is the low bit). | |||
*/ | |||
uint32_t x0, x1, x2, x3, x4, x5, x6, x7; | |||
uint32_t y1, y2, y3, y4, y5, y6, y7, y8, y9; | |||
uint32_t y10, y11, y12, y13, y14, y15, y16, y17, y18, y19; | |||
uint32_t y20, y21; | |||
uint32_t z0, z1, z2, z3, z4, z5, z6, z7, z8, z9; | |||
uint32_t z10, z11, z12, z13, z14, z15, z16, z17; | |||
uint32_t t0, t1, t2, t3, t4, t5, t6, t7, t8, t9; | |||
uint32_t t10, t11, t12, t13, t14, t15, t16, t17, t18, t19; | |||
uint32_t t20, t21, t22, t23, t24, t25, t26, t27, t28, t29; | |||
uint32_t t30, t31, t32, t33, t34, t35, t36, t37, t38, t39; | |||
uint32_t t40, t41, t42, t43, t44, t45, t46, t47, t48, t49; | |||
uint32_t t50, t51, t52, t53, t54, t55, t56, t57, t58, t59; | |||
uint32_t t60, t61, t62, t63, t64, t65, t66, t67; | |||
uint32_t s0, s1, s2, s3, s4, s5, s6, s7; | |||
x0 = q[7]; | |||
x1 = q[6]; | |||
x2 = q[5]; | |||
x3 = q[4]; | |||
x4 = q[3]; | |||
x5 = q[2]; | |||
x6 = q[1]; | |||
x7 = q[0]; | |||
/* | |||
* Top linear transformation. | |||
*/ | |||
y14 = x3 ^ x5; | |||
y13 = x0 ^ x6; | |||
y9 = x0 ^ x3; | |||
y8 = x0 ^ x5; | |||
t0 = x1 ^ x2; | |||
y1 = t0 ^ x7; | |||
y4 = y1 ^ x3; | |||
y12 = y13 ^ y14; | |||
y2 = y1 ^ x0; | |||
y5 = y1 ^ x6; | |||
y3 = y5 ^ y8; | |||
t1 = x4 ^ y12; | |||
y15 = t1 ^ x5; | |||
y20 = t1 ^ x1; | |||
y6 = y15 ^ x7; | |||
y10 = y15 ^ t0; | |||
y11 = y20 ^ y9; | |||
y7 = x7 ^ y11; | |||
y17 = y10 ^ y11; | |||
y19 = y10 ^ y8; | |||
y16 = t0 ^ y11; | |||
y21 = y13 ^ y16; | |||
y18 = x0 ^ y16; | |||
/* | |||
* Non-linear section. | |||
*/ | |||
t2 = y12 & y15; | |||
t3 = y3 & y6; | |||
t4 = t3 ^ t2; | |||
t5 = y4 & x7; | |||
t6 = t5 ^ t2; | |||
t7 = y13 & y16; | |||
t8 = y5 & y1; | |||
t9 = t8 ^ t7; | |||
t10 = y2 & y7; | |||
t11 = t10 ^ t7; | |||
t12 = y9 & y11; | |||
t13 = y14 & y17; | |||
t14 = t13 ^ t12; | |||
t15 = y8 & y10; | |||
t16 = t15 ^ t12; | |||
t17 = t4 ^ t14; | |||
t18 = t6 ^ t16; | |||
t19 = t9 ^ t14; | |||
t20 = t11 ^ t16; | |||
t21 = t17 ^ y20; | |||
t22 = t18 ^ y19; | |||
t23 = t19 ^ y21; | |||
t24 = t20 ^ y18; | |||
t25 = t21 ^ t22; | |||
t26 = t21 & t23; | |||
t27 = t24 ^ t26; | |||
t28 = t25 & t27; | |||
t29 = t28 ^ t22; | |||
t30 = t23 ^ t24; | |||
t31 = t22 ^ t26; | |||
t32 = t31 & t30; | |||
t33 = t32 ^ t24; | |||
t34 = t23 ^ t33; | |||
t35 = t27 ^ t33; | |||
t36 = t24 & t35; | |||
t37 = t36 ^ t34; | |||
t38 = t27 ^ t36; | |||
t39 = t29 & t38; | |||
t40 = t25 ^ t39; | |||
t41 = t40 ^ t37; | |||
t42 = t29 ^ t33; | |||
t43 = t29 ^ t40; | |||
t44 = t33 ^ t37; | |||
t45 = t42 ^ t41; | |||
z0 = t44 & y15; | |||
z1 = t37 & y6; | |||
z2 = t33 & x7; | |||
z3 = t43 & y16; | |||
z4 = t40 & y1; | |||
z5 = t29 & y7; | |||
z6 = t42 & y11; | |||
z7 = t45 & y17; | |||
z8 = t41 & y10; | |||
z9 = t44 & y12; | |||
z10 = t37 & y3; | |||
z11 = t33 & y4; | |||
z12 = t43 & y13; | |||
z13 = t40 & y5; | |||
z14 = t29 & y2; | |||
z15 = t42 & y9; | |||
z16 = t45 & y14; | |||
z17 = t41 & y8; | |||
/* | |||
* Bottom linear transformation. | |||
*/ | |||
t46 = z15 ^ z16; | |||
t47 = z10 ^ z11; | |||
t48 = z5 ^ z13; | |||
t49 = z9 ^ z10; | |||
t50 = z2 ^ z12; | |||
t51 = z2 ^ z5; | |||
t52 = z7 ^ z8; | |||
t53 = z0 ^ z3; | |||
t54 = z6 ^ z7; | |||
t55 = z16 ^ z17; | |||
t56 = z12 ^ t48; | |||
t57 = t50 ^ t53; | |||
t58 = z4 ^ t46; | |||
t59 = z3 ^ t54; | |||
t60 = t46 ^ t57; | |||
t61 = z14 ^ t57; | |||
t62 = t52 ^ t58; | |||
t63 = t49 ^ t58; | |||
t64 = z4 ^ t59; | |||
t65 = t61 ^ t62; | |||
t66 = z1 ^ t63; | |||
s0 = t59 ^ t63; | |||
s6 = t56 ^ ~t62; | |||
s7 = t48 ^ ~t60; | |||
t67 = t64 ^ t65; | |||
s3 = t53 ^ t66; | |||
s4 = t51 ^ t66; | |||
s5 = t47 ^ t65; | |||
s1 = t64 ^ ~s3; | |||
s2 = t55 ^ ~t67; | |||
q[7] = s0; | |||
q[6] = s1; | |||
q[5] = s2; | |||
q[4] = s3; | |||
q[3] = s4; | |||
q[2] = s5; | |||
q[1] = s6; | |||
q[0] = s7; | |||
} | |||
static void br_aes_ct_ortho(uint32_t *q) { | |||
#define SWAPN_32(cl, ch, s, x, y) do { \ | |||
uint32_t a, b; \ | |||
a = (x); \ | |||
b = (y); \ | |||
(x) = (a & (uint32_t)(cl)) | ((b & (uint32_t)(cl)) << (s)); \ | |||
(y) = ((a & (uint32_t)(ch)) >> (s)) | (b & (uint32_t)(ch)); \ | |||
} while (0) | |||
#define SWAP2_32(x, y) SWAPN_32(0x55555555, 0xAAAAAAAA, 1, x, y) | |||
#define SWAP4_32(x, y) SWAPN_32(0x33333333, 0xCCCCCCCC, 2, x, y) | |||
#define SWAP8_32(x, y) SWAPN_32(0x0F0F0F0F, 0xF0F0F0F0, 4, x, y) | |||
SWAP2_32(q[0], q[1]); | |||
SWAP2_32(q[2], q[3]); | |||
SWAP2_32(q[4], q[5]); | |||
SWAP2_32(q[6], q[7]); | |||
SWAP4_32(q[0], q[2]); | |||
SWAP4_32(q[1], q[3]); | |||
SWAP4_32(q[4], q[6]); | |||
SWAP4_32(q[5], q[7]); | |||
SWAP8_32(q[0], q[4]); | |||
SWAP8_32(q[1], q[5]); | |||
SWAP8_32(q[2], q[6]); | |||
SWAP8_32(q[3], q[7]); | |||
} | |||
static inline void add_round_key32(uint32_t *q, const uint32_t *sk) { | |||
q[0] ^= sk[0]; | |||
q[1] ^= sk[1]; | |||
q[2] ^= sk[2]; | |||
q[3] ^= sk[3]; | |||
q[4] ^= sk[4]; | |||
q[5] ^= sk[5]; | |||
q[6] ^= sk[6]; | |||
q[7] ^= sk[7]; | |||
} | |||
static inline void shift_rows32(uint32_t *q) { | |||
int i; | |||
for (i = 0; i < 8; i++) { | |||
uint32_t x; | |||
x = q[i]; | |||
q[i] = (x & 0x000000FF) | |||
| ((x & 0x0000FC00) >> 2) | ((x & 0x00000300) << 6) | |||
| ((x & 0x00F00000) >> 4) | ((x & 0x000F0000) << 4) | |||
| ((x & 0xC0000000) >> 6) | ((x & 0x3F000000) << 2); | |||
} | |||
} | |||
static inline uint32_t rotr16(uint32_t x) { | |||
return (x << 16) | (x >> 16); | |||
} | |||
static inline void mix_columns32(uint32_t *q) { | |||
uint32_t q0, q1, q2, q3, q4, q5, q6, q7; | |||
uint32_t r0, r1, r2, r3, r4, r5, r6, r7; | |||
q0 = q[0]; | |||
q1 = q[1]; | |||
q2 = q[2]; | |||
q3 = q[3]; | |||
q4 = q[4]; | |||
q5 = q[5]; | |||
q6 = q[6]; | |||
q7 = q[7]; | |||
r0 = (q0 >> 8) | (q0 << 24); | |||
r1 = (q1 >> 8) | (q1 << 24); | |||
r2 = (q2 >> 8) | (q2 << 24); | |||
r3 = (q3 >> 8) | (q3 << 24); | |||
r4 = (q4 >> 8) | (q4 << 24); | |||
r5 = (q5 >> 8) | (q5 << 24); | |||
r6 = (q6 >> 8) | (q6 << 24); | |||
r7 = (q7 >> 8) | (q7 << 24); | |||
q[0] = q7 ^ r7 ^ r0 ^ rotr16(q0 ^ r0); | |||
q[1] = q0 ^ r0 ^ q7 ^ r7 ^ r1 ^ rotr16(q1 ^ r1); | |||
q[2] = q1 ^ r1 ^ r2 ^ rotr16(q2 ^ r2); | |||
q[3] = q2 ^ r2 ^ q7 ^ r7 ^ r3 ^ rotr16(q3 ^ r3); | |||
q[4] = q3 ^ r3 ^ q7 ^ r7 ^ r4 ^ rotr16(q4 ^ r4); | |||
q[5] = q4 ^ r4 ^ r5 ^ rotr16(q5 ^ r5); | |||
q[6] = q5 ^ r5 ^ r6 ^ rotr16(q6 ^ r6); | |||
q[7] = q6 ^ r6 ^ r7 ^ rotr16(q7 ^ r7); | |||
} | |||
static void br_aes_ct64_ortho(uint64_t *q) { | |||
#define SWAPN(cl, ch, s, x, y) do { \ | |||
uint64_t a, b; \ | |||
a = (x); \ | |||
b = (y); \ | |||
(x) = (a & (uint64_t)(cl)) | ((b & (uint64_t)(cl)) << (s)); \ | |||
(y) = ((a & (uint64_t)(ch)) >> (s)) | (b & (uint64_t)(ch)); \ | |||
} while (0) | |||
#define SWAP2(x, y) SWAPN(0x5555555555555555, 0xAAAAAAAAAAAAAAAA, 1, x, y) | |||
#define SWAP4(x, y) SWAPN(0x3333333333333333, 0xCCCCCCCCCCCCCCCC, 2, x, y) | |||
#define SWAP8(x, y) SWAPN(0x0F0F0F0F0F0F0F0F, 0xF0F0F0F0F0F0F0F0, 4, x, y) | |||
SWAP2(q[0], q[1]); | |||
SWAP2(q[2], q[3]); | |||
SWAP2(q[4], q[5]); | |||
SWAP2(q[6], q[7]); | |||
SWAP4(q[0], q[2]); | |||
SWAP4(q[1], q[3]); | |||
SWAP4(q[4], q[6]); | |||
SWAP4(q[5], q[7]); | |||
SWAP8(q[0], q[4]); | |||
SWAP8(q[1], q[5]); | |||
SWAP8(q[2], q[6]); | |||
SWAP8(q[3], q[7]); | |||
} | |||
static void br_aes_ct64_interleave_in(uint64_t *q0, uint64_t *q1, const uint32_t *w) { | |||
uint64_t x0, x1, x2, x3; | |||
x0 = w[0]; | |||
x1 = w[1]; | |||
x2 = w[2]; | |||
x3 = w[3]; | |||
x0 |= (x0 << 16); | |||
x1 |= (x1 << 16); | |||
x2 |= (x2 << 16); | |||
x3 |= (x3 << 16); | |||
x0 &= (uint64_t)0x0000FFFF0000FFFF; | |||
x1 &= (uint64_t)0x0000FFFF0000FFFF; | |||
x2 &= (uint64_t)0x0000FFFF0000FFFF; | |||
x3 &= (uint64_t)0x0000FFFF0000FFFF; | |||
x0 |= (x0 << 8); | |||
x1 |= (x1 << 8); | |||
x2 |= (x2 << 8); | |||
x3 |= (x3 << 8); | |||
x0 &= (uint64_t)0x00FF00FF00FF00FF; | |||
x1 &= (uint64_t)0x00FF00FF00FF00FF; | |||
x2 &= (uint64_t)0x00FF00FF00FF00FF; | |||
x3 &= (uint64_t)0x00FF00FF00FF00FF; | |||
*q0 = x0 | (x2 << 8); | |||
*q1 = x1 | (x3 << 8); | |||
} | |||
static void br_aes_ct64_interleave_out(uint32_t *w, uint64_t q0, uint64_t q1) { | |||
uint64_t x0, x1, x2, x3; | |||
x0 = q0 & (uint64_t)0x00FF00FF00FF00FF; | |||
x1 = q1 & (uint64_t)0x00FF00FF00FF00FF; | |||
x2 = (q0 >> 8) & (uint64_t)0x00FF00FF00FF00FF; | |||
x3 = (q1 >> 8) & (uint64_t)0x00FF00FF00FF00FF; | |||
x0 |= (x0 >> 8); | |||
x1 |= (x1 >> 8); | |||
x2 |= (x2 >> 8); | |||
x3 |= (x3 >> 8); | |||
x0 &= (uint64_t)0x0000FFFF0000FFFF; | |||
x1 &= (uint64_t)0x0000FFFF0000FFFF; | |||
x2 &= (uint64_t)0x0000FFFF0000FFFF; | |||
x3 &= (uint64_t)0x0000FFFF0000FFFF; | |||
w[0] = (uint32_t)x0 | (uint32_t)(x0 >> 16); | |||
w[1] = (uint32_t)x1 | (uint32_t)(x1 >> 16); | |||
w[2] = (uint32_t)x2 | (uint32_t)(x2 >> 16); | |||
w[3] = (uint32_t)x3 | (uint32_t)(x3 >> 16); | |||
} | |||
static inline void add_round_key(uint64_t *q, const uint64_t *sk) { | |||
q[0] ^= sk[0]; | |||
q[1] ^= sk[1]; | |||
q[2] ^= sk[2]; | |||
q[3] ^= sk[3]; | |||
q[4] ^= sk[4]; | |||
q[5] ^= sk[5]; | |||
q[6] ^= sk[6]; | |||
q[7] ^= sk[7]; | |||
} | |||
static inline void shift_rows(uint64_t *q) { | |||
int i; | |||
for (i = 0; i < 8; i++) { | |||
uint64_t x; | |||
x = q[i]; | |||
q[i] = (x & (uint64_t)0x000000000000FFFF) | |||
| ((x & (uint64_t)0x00000000FFF00000) >> 4) | |||
| ((x & (uint64_t)0x00000000000F0000) << 12) | |||
| ((x & (uint64_t)0x0000FF0000000000) >> 8) | |||
| ((x & (uint64_t)0x000000FF00000000) << 8) | |||
| ((x & (uint64_t)0xF000000000000000) >> 12) | |||
| ((x & (uint64_t)0x0FFF000000000000) << 4); | |||
} | |||
} | |||
static inline uint64_t rotr32(uint64_t x) { | |||
return (x << 32) | (x >> 32); | |||
} | |||
static inline void mix_columns(uint64_t *q) { | |||
uint64_t q0, q1, q2, q3, q4, q5, q6, q7; | |||
uint64_t r0, r1, r2, r3, r4, r5, r6, r7; | |||
q0 = q[0]; | |||
q1 = q[1]; | |||
q2 = q[2]; | |||
q3 = q[3]; | |||
q4 = q[4]; | |||
q5 = q[5]; | |||
q6 = q[6]; | |||
q7 = q[7]; | |||
r0 = (q0 >> 16) | (q0 << 48); | |||
r1 = (q1 >> 16) | (q1 << 48); | |||
r2 = (q2 >> 16) | (q2 << 48); | |||
r3 = (q3 >> 16) | (q3 << 48); | |||
r4 = (q4 >> 16) | (q4 << 48); | |||
r5 = (q5 >> 16) | (q5 << 48); | |||
r6 = (q6 >> 16) | (q6 << 48); | |||
r7 = (q7 >> 16) | (q7 << 48); | |||
q[0] = q7 ^ r7 ^ r0 ^ rotr32(q0 ^ r0); | |||
q[1] = q0 ^ r0 ^ q7 ^ r7 ^ r1 ^ rotr32(q1 ^ r1); | |||
q[2] = q1 ^ r1 ^ r2 ^ rotr32(q2 ^ r2); | |||
q[3] = q2 ^ r2 ^ q7 ^ r7 ^ r3 ^ rotr32(q3 ^ r3); | |||
q[4] = q3 ^ r3 ^ q7 ^ r7 ^ r4 ^ rotr32(q4 ^ r4); | |||
q[5] = q4 ^ r4 ^ r5 ^ rotr32(q5 ^ r5); | |||
q[6] = q5 ^ r5 ^ r6 ^ rotr32(q6 ^ r6); | |||
q[7] = q6 ^ r6 ^ r7 ^ rotr32(q7 ^ r7); | |||
} | |||
static void interleave_constant(uint64_t *out, const unsigned char *in) { | |||
uint32_t tmp_32_constant[16]; | |||
int i; | |||
br_range_dec32le(tmp_32_constant, 16, in); | |||
for (i = 0; i < 4; i++) { | |||
br_aes_ct64_interleave_in(&out[i], &out[i + 4], tmp_32_constant + (i << 2)); | |||
} | |||
br_aes_ct64_ortho(out); | |||
} | |||
static void interleave_constant32(uint32_t *out, const unsigned char *in) { | |||
int i; | |||
for (i = 0; i < 4; i++) { | |||
out[2 * i] = br_dec32le(in + 4 * i); | |||
out[2 * i + 1] = br_dec32le(in + 4 * i + 16); | |||
} | |||
br_aes_ct_ortho(out); | |||
} | |||
void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_tweak_constants( | |||
const unsigned char *pk_seed, const unsigned char *sk_seed, | |||
unsigned long long seed_length) { | |||
unsigned char buf[40 * 16]; | |||
int i; | |||
/* Use the standard constants to generate tweaked ones. */ | |||
memcpy((uint8_t *)tweaked512_rc64, (uint8_t *)haraka512_rc64, 40 * 16); | |||
/* Constants for sk.seed */ | |||
if (sk_seed != NULL) { | |||
PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka_S( | |||
buf, 40 * 16, sk_seed, seed_length); | |||
/* Interleave constants */ | |||
for (i = 0; i < 10; i++) { | |||
interleave_constant32(tweaked256_rc32_sseed[i], buf + 32 * i); | |||
} | |||
} | |||
/* Constants for pk.seed */ | |||
PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka_S( | |||
buf, 40 * 16, pk_seed, seed_length); | |||
for (i = 0; i < 10; i++) { | |||
interleave_constant32(tweaked256_rc32[i], buf + 32 * i); | |||
interleave_constant(tweaked512_rc64[i], buf + 64 * i); | |||
} | |||
} | |||
static void haraka_S_absorb(unsigned char *s, | |||
const unsigned char *m, unsigned long long mlen, | |||
unsigned char p) { | |||
unsigned long long i; | |||
unsigned char t[HARAKAS_RATE]; | |||
while (mlen >= HARAKAS_RATE) { | |||
/* XOR block to state */ | |||
for (i = 0; i < HARAKAS_RATE; ++i) { | |||
s[i] ^= m[i]; | |||
} | |||
PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka512_perm(s, s); | |||
mlen -= HARAKAS_RATE; | |||
m += HARAKAS_RATE; | |||
} | |||
for (i = 0; i < HARAKAS_RATE; ++i) { | |||
t[i] = 0; | |||
} | |||
for (i = 0; i < mlen; ++i) { | |||
t[i] = m[i]; | |||
} | |||
t[i] = p; | |||
t[HARAKAS_RATE - 1] |= 128; | |||
for (i = 0; i < HARAKAS_RATE; ++i) { | |||
s[i] ^= t[i]; | |||
} | |||
} | |||
static void haraka_S_squeezeblocks(unsigned char *h, unsigned long long nblocks, | |||
unsigned char *s) { | |||
while (nblocks > 0) { | |||
PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka512_perm(s, s); | |||
memcpy(h, s, HARAKAS_RATE); | |||
h += HARAKAS_RATE; | |||
nblocks--; | |||
} | |||
} | |||
void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka_S_inc_init(uint8_t *s_inc) { | |||
size_t i; | |||
for (i = 0; i < 64; i++) { | |||
s_inc[i] = 0; | |||
} | |||
s_inc[64] = 0; | |||
} | |||
void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka_S_inc_absorb(uint8_t *s_inc, const uint8_t *m, size_t mlen) { | |||
size_t i; | |||
/* Recall that s_inc[64] is the non-absorbed bytes xored into the state */ | |||
while (mlen + s_inc[64] >= HARAKAS_RATE) { | |||
for (i = 0; i < (size_t)(HARAKAS_RATE - s_inc[64]); i++) { | |||
/* Take the i'th byte from message | |||
xor with the s_inc[64] + i'th byte of the state */ | |||
s_inc[s_inc[64] + i] ^= m[i]; | |||
} | |||
mlen -= (size_t)(HARAKAS_RATE - s_inc[64]); | |||
m += HARAKAS_RATE - s_inc[64]; | |||
s_inc[64] = 0; | |||
PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka512_perm(s_inc, s_inc); | |||
} | |||
for (i = 0; i < mlen; i++) { | |||
s_inc[s_inc[64] + i] ^= m[i]; | |||
} | |||
s_inc[64] = (uint8_t)(mlen + s_inc[64]); | |||
} | |||
void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka_S_inc_finalize(uint8_t *s_inc) { | |||
/* After haraka_S_inc_absorb, we are guaranteed that s_inc[64] < HARAKAS_RATE, | |||
so we can always use one more byte for p in the current state. */ | |||
s_inc[s_inc[64]] ^= 0x1F; | |||
s_inc[HARAKAS_RATE - 1] ^= 128; | |||
s_inc[64] = 0; | |||
} | |||
void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka_S_inc_squeeze(uint8_t *out, size_t outlen, uint8_t *s_inc) { | |||
uint8_t i; | |||
/* First consume any bytes we still have sitting around */ | |||
for (i = 0; i < outlen && i < s_inc[64]; i++) { | |||
/* There are s_inc[64] bytes left, so r - s_inc[64] is the first | |||
available byte. We consume from there, i.e., up to r. */ | |||
out[i] = s_inc[(HARAKAS_RATE - s_inc[64] + i)]; | |||
} | |||
out += i; | |||
outlen -= i; | |||
s_inc[64] = (uint8_t)(s_inc[64] - i); | |||
/* Then squeeze the remaining necessary blocks */ | |||
while (outlen > 0) { | |||
PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka512_perm(s_inc, s_inc); | |||
for (i = 0; i < outlen && i < HARAKAS_RATE; i++) { | |||
out[i] = s_inc[i]; | |||
} | |||
out += i; | |||
outlen -= i; | |||
s_inc[64] = (uint8_t)(HARAKAS_RATE - i); | |||
} | |||
} | |||
void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka_S(unsigned char *out, unsigned long long outlen, const unsigned char *in, unsigned long long inlen) { | |||
unsigned long long i; | |||
unsigned char s[64]; | |||
unsigned char d[32]; | |||
for (i = 0; i < 64; i++) { | |||
s[i] = 0; | |||
} | |||
haraka_S_absorb(s, in, inlen, 0x1F); | |||
haraka_S_squeezeblocks(out, outlen / 32, s); | |||
out += (outlen / 32) * 32; | |||
if (outlen % 32) { | |||
haraka_S_squeezeblocks(d, 1, s); | |||
for (i = 0; i < outlen % 32; i++) { | |||
out[i] = d[i]; | |||
} | |||
} | |||
} | |||
void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka512_perm(unsigned char *out, const unsigned char *in) { | |||
uint32_t w[16]; | |||
uint64_t q[8], tmp_q; | |||
unsigned int i, j; | |||
br_range_dec32le(w, 16, in); | |||
for (i = 0; i < 4; i++) { | |||
br_aes_ct64_interleave_in(&q[i], &q[i + 4], w + (i << 2)); | |||
} | |||
br_aes_ct64_ortho(q); | |||
/* AES rounds */ | |||
for (i = 0; i < 5; i++) { | |||
for (j = 0; j < 2; j++) { | |||
br_aes_ct64_bitslice_Sbox(q); | |||
shift_rows(q); | |||
mix_columns(q); | |||
add_round_key(q, tweaked512_rc64[2 * i + j]); | |||
} | |||
/* Mix states */ | |||
for (j = 0; j < 8; j++) { | |||
tmp_q = q[j]; | |||
q[j] = (tmp_q & 0x0001000100010001) << 5 | | |||
(tmp_q & 0x0002000200020002) << 12 | | |||
(tmp_q & 0x0004000400040004) >> 1 | | |||
(tmp_q & 0x0008000800080008) << 6 | | |||
(tmp_q & 0x0020002000200020) << 9 | | |||
(tmp_q & 0x0040004000400040) >> 4 | | |||
(tmp_q & 0x0080008000800080) << 3 | | |||
(tmp_q & 0x2100210021002100) >> 5 | | |||
(tmp_q & 0x0210021002100210) << 2 | | |||
(tmp_q & 0x0800080008000800) << 4 | | |||
(tmp_q & 0x1000100010001000) >> 12 | | |||
(tmp_q & 0x4000400040004000) >> 10 | | |||
(tmp_q & 0x8400840084008400) >> 3; | |||
} | |||
} | |||
br_aes_ct64_ortho(q); | |||
for (i = 0; i < 4; i ++) { | |||
br_aes_ct64_interleave_out(w + (i << 2), q[i], q[i + 4]); | |||
} | |||
br_range_enc32le(out, w, 16); | |||
} | |||
void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka512(unsigned char *out, const unsigned char *in) { | |||
int i; | |||
unsigned char buf[64]; | |||
PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka512_perm(buf, in); | |||
/* Feed-forward */ | |||
for (i = 0; i < 64; i++) { | |||
buf[i] = buf[i] ^ in[i]; | |||
} | |||
/* Truncated */ | |||
memcpy(out, buf + 8, 8); | |||
memcpy(out + 8, buf + 24, 8); | |||
memcpy(out + 16, buf + 32, 8); | |||
memcpy(out + 24, buf + 48, 8); | |||
} | |||
void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka256(unsigned char *out, const unsigned char *in) { | |||
uint32_t q[8], tmp_q; | |||
int i, j; | |||
for (i = 0; i < 4; i++) { | |||
q[2 * i] = br_dec32le(in + 4 * i); | |||
q[2 * i + 1] = br_dec32le(in + 4 * i + 16); | |||
} | |||
br_aes_ct_ortho(q); | |||
/* AES rounds */ | |||
for (i = 0; i < 5; i++) { | |||
for (j = 0; j < 2; j++) { | |||
br_aes_ct_bitslice_Sbox(q); | |||
shift_rows32(q); | |||
mix_columns32(q); | |||
add_round_key32(q, tweaked256_rc32[2 * i + j]); | |||
} | |||
/* Mix states */ | |||
for (j = 0; j < 8; j++) { | |||
tmp_q = q[j]; | |||
q[j] = (tmp_q & 0x81818181) | | |||
(tmp_q & 0x02020202) << 1 | | |||
(tmp_q & 0x04040404) << 2 | | |||
(tmp_q & 0x08080808) << 3 | | |||
(tmp_q & 0x10101010) >> 3 | | |||
(tmp_q & 0x20202020) >> 2 | | |||
(tmp_q & 0x40404040) >> 1; | |||
} | |||
} | |||
br_aes_ct_ortho(q); | |||
for (i = 0; i < 4; i++) { | |||
br_enc32le(out + 4 * i, q[2 * i]); | |||
br_enc32le(out + 4 * i + 16, q[2 * i + 1]); | |||
} | |||
for (i = 0; i < 32; i++) { | |||
out[i] ^= in[i]; | |||
} | |||
} | |||
void PQCLEAN_SPHINCSHARAKA192FROBUST_CLEAN_haraka256_sk(unsigned char *out, const unsigned char *in) { | |||
uint32_t q[8], tmp_q; | |||
int i, j; | |||
for (i = 0; i < 4; i++) { | |||
q[2 * i] = br_dec32le(in + 4 * i); | |||
q[2 * i + 1] = br_dec32le(in + 4 * i + 16); | |||
} | |||
br_aes_ct_ortho(q); | |||
/* AES rounds */ | |||
for (i = 0; i < 5; i++) { | |||
for (j = 0; j < 2; j++) { | |||
br_aes_ct_bitslice_Sbox(q); | |||
shift_rows32(q); | |||
mix_columns32(q); | |||
add_round_key32(q, tweaked256_rc32_sseed[2 * i + j]); | |||
} | |||
/* Mix states */ | |||
for (j = 0; j < 8; j++) { | |||
tmp_q = q[j]; | |||
q[j] = (tmp_q & 0x81818181) | | |||
(tmp_q & 0x02020202) << 1 | | |||
(tmp_q & 0x04040404) << 2 | | |||
(tmp_q & 0x08080808) << 3 | | |||
(tmp_q & 0x10101010) >> 3 | | |||
(tmp_q & 0x20202020) >> 2 | | |||
(tmp_q & 0x40404040) >> 1; | |||
} | |||
} | |||
br_aes_ct_ortho(q); | |||
for (i = 0; i < 4; i++) { | |||
br_enc32le(out + 4 * i, q[2 * i]); | |||
br_enc32le(out + 4 * i + 16, q[2 * i + 1]); | |||
} | |||
for (i = 0; i < 32; i++) { | |||
out[i] ^= in[i]; | |||
} | |||
} |
@@ -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 |
@@ -0,0 +1,22 @@ | |||
#ifndef SPX_HASH_H | |||
#define SPX_HASH_H | |||
#include <stdint.h> | |||
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 |
@@ -0,0 +1,86 @@ | |||
#include <stdint.h> | |||
#include <string.h> | |||
#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); | |||
} |