Add SPHINCS-SHAKE256-128f-simple

This commit is contained in:
Joost Rijneveld 2019-04-05 17:58:48 +02:00
parent 56e8d17a7d
commit a14dcefb32
No known key found for this signature in database
GPG Key ID: A4FE39CF49CBC553
19 changed files with 1557 additions and 0 deletions

View File

@ -0,0 +1,27 @@
name: SPHINCS+
type: signature
claimed-nist-level: 1
length-public-key: 32
length-signature: 16976
testvectors-sha256: a14cb8e4f149493fc5979e465e09ce943e8d669186ff5c7c3d11239fa869def6
principal-submitter: Andreas Hülsing
auxiliary-submitters:
- Jean-Philippe Aumasson
- Daniel J. Bernstein,
- Christoph Dobraunig
- Maria Eichlseder
- Scott Fluhrer
- Stefan-Lukas Gazdag
- Panos Kampanakis
- Stefan Kölbl
- Tanja Lange
- Martin M. Lauridsen
- Florian Mendel
- Ruben Niederhagen
- Christian Rechberger
- Joost Rijneveld
- Peter Schwabe
implementations:
- name: clean
version: https://github.com/sphincs/sphincsplus/commit/492ec4f1f6d3b3dc4b435783bbaaf4e41cdb6f32
length-secret-key: 64

View File

@ -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/>

View File

@ -0,0 +1,22 @@
# This Makefile can be used with GNU Make or BSD Make
LIB=libsphincs-shake256-128f-simple_clean.a
HASH = shake256
THASH = simple
HEADERS = params.h address.h wots.h utils.h fors.h api.h hash.h thash.h
OBJECTS = address.o wots.o utils.o fors.o sign.o hash_shake256.o thash_shake256_simple.o
CFLAGS=-Wall -Wextra -Wpedantic -Werror -std=c99 -I../../../common $(EXTRAFLAGS)
all: $(LIB)
%.o: %.c $(HEADERS)
$(CC) $(CFLAGS) -c -o $@ $<
$(LIB): $(OBJECTS)
$(AR) -r $@ $(OBJECTS)
clean:
$(RM) $(OBJECTS)
$(RM) $(LIB)

View File

@ -0,0 +1,19 @@
# This Makefile can be used with Microsoft Visual Studio's nmake using the command:
# nmake /f Makefile.Microsoft_nmake
LIBRARY=libsphincs-shake256-128f-simple_clean.lib
OBJECTS=address.obj wots.obj utils.obj fors.obj sign.obj hash_shake256.obj thash_shake256_simple.obj
CFLAGS=/nologo /I ..\..\..\common /W4 /WX
all: $(LIBRARY)
# Make sure objects are recompiled if headers change.
$(OBJECTS): *.h
$(LIBRARY): $(OBJECTS)
LIB.EXE /NOLOGO /WX /OUT:$@ $**
clean:
-DEL $(OBJECTS)
-DEL $(LIBRARY)

View File

@ -0,0 +1,80 @@
#include <stdint.h>
#include "params.h"
#include "utils.h"
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_addr_to_bytes(
unsigned char *bytes, const uint32_t addr[8]) {
int i;
for (i = 0; i < 8; i++) {
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_ull_to_bytes(
bytes + i * 4, 4, addr[i]);
}
}
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_layer_addr(
uint32_t addr[8], uint32_t layer) {
addr[0] = layer;
}
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_tree_addr(
uint32_t addr[8], uint64_t tree) {
#if (SPX_TREE_HEIGHT * (SPX_D - 1)) > 64
#error Subtree addressing is currently limited to at most 2^64 trees
#endif
addr[1] = 0;
addr[2] = (uint32_t) (tree >> 32);
addr[3] = (uint32_t) tree;
}
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_type(
uint32_t addr[8], uint32_t type) {
addr[4] = type;
}
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_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_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_keypair_addr(
uint32_t addr[8], uint32_t keypair) {
addr[5] = keypair;
}
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_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_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_chain_addr(
uint32_t addr[8], uint32_t chain) {
addr[6] = chain;
}
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_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_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_tree_height(
uint32_t addr[8], uint32_t tree_height) {
addr[6] = tree_height;
}
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_tree_index(
uint32_t addr[8], uint32_t tree_index) {
addr[7] = tree_index;
}

View File

@ -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_SPHINCSSHAKE256128FSIMPLE_CLEAN_addr_to_bytes(
unsigned char *bytes, const uint32_t addr[8]);
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_layer_addr(
uint32_t addr[8], uint32_t layer);
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_tree_addr(
uint32_t addr[8], uint64_t tree);
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_type(
uint32_t addr[8], uint32_t type);
/* Copies the layer and tree part of one address into the other */
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_copy_subtree_addr(
uint32_t out[8], const uint32_t in[8]);
/* These functions are used for WOTS and FORS addresses. */
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_keypair_addr(
uint32_t addr[8], uint32_t keypair);
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_chain_addr(
uint32_t addr[8], uint32_t chain);
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_hash_addr(
uint32_t addr[8], uint32_t hash);
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_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_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_tree_height(
uint32_t addr[8], uint32_t tree_height);
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_tree_index(
uint32_t addr[8], uint32_t tree_index);
#endif

View File

@ -0,0 +1,80 @@
#ifndef PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_API_H
#define PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_API_H
#include <stddef.h>
#include <stdint.h>
#include "params.h"
#define PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_CRYPTO_ALGNAME "SPHINCS+"
#define PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_CRYPTO_SECRETKEYBYTES 64
#define PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES 32
#define PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_CRYPTO_BYTES 16976
#define PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_CRYPTO_SEEDBYTES 48
/*
* Returns the length of a secret key, in bytes
*/
size_t PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_crypto_sign_secretkeybytes(void);
/*
* Returns the length of a public key, in bytes
*/
size_t PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_crypto_sign_publickeybytes(void);
/*
* Returns the length of a signature, in bytes
*/
size_t PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_crypto_sign_bytes(void);
/*
* Returns the length of the seed required to generate a key pair, in bytes
*/
size_t PQCLEAN_SPHINCSSHAKE256128FSIMPLE_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_SPHINCSSHAKE256128FSIMPLE_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_SPHINCSSHAKE256128FSIMPLE_CLEAN_crypto_sign_keypair(
uint8_t *pk, uint8_t *sk);
/**
* Returns an array containing a detached signature.
*/
int PQCLEAN_SPHINCSSHAKE256128FSIMPLE_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_SPHINCSSHAKE256128FSIMPLE_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_SPHINCSSHAKE256128FSIMPLE_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_SPHINCSSHAKE256128FSIMPLE_CLEAN_crypto_sign_open(
uint8_t *m, size_t *mlen,
const uint8_t *sm, size_t smlen, const uint8_t *pk);
#endif

View File

@ -0,0 +1,165 @@
#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_SPHINCSSHAKE256128FSIMPLE_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_SPHINCSSHAKE256128FSIMPLE_CLEAN_thash(
leaf, sk, 1, 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_SPHINCSSHAKE256128FSIMPLE_CLEAN_copy_keypair_addr(
fors_leaf_addr, fors_tree_addr);
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_type(
fors_leaf_addr, SPX_ADDR_TYPE_FORSTREE);
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_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] ^= ((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_SPHINCSSHAKE256128FSIMPLE_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_SPHINCSSHAKE256128FSIMPLE_CLEAN_copy_keypair_addr(
fors_tree_addr, fors_addr);
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_copy_keypair_addr(
fors_pk_addr, fors_addr);
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_type(
fors_tree_addr, SPX_ADDR_TYPE_FORSTREE);
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_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_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_tree_height(
fors_tree_addr, 0);
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_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_SPHINCSSHAKE256128FSIMPLE_CLEAN_treehash(
roots + i * SPX_N, sig, sk_seed, pub_seed,
indices[i], idx_offset, SPX_FORS_HEIGHT, fors_gen_leaf,
fors_tree_addr);
sig += SPX_N * SPX_FORS_HEIGHT;
}
/* Hash horizontally across all tree roots to derive the public key. */
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_thash(
pk, roots, SPX_FORS_TREES, 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_SPHINCSSHAKE256128FSIMPLE_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_SPHINCSSHAKE256128FSIMPLE_CLEAN_copy_keypair_addr(
fors_tree_addr, fors_addr);
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_copy_keypair_addr(
fors_pk_addr, fors_addr);
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_type(
fors_tree_addr, SPX_ADDR_TYPE_FORSTREE);
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_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_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_tree_height(
fors_tree_addr, 0);
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_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_SPHINCSSHAKE256128FSIMPLE_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_SPHINCSSHAKE256128FSIMPLE_CLEAN_thash(
pk, roots, SPX_FORS_TREES, pub_seed, fors_pk_addr);
}

View File

@ -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_SPHINCSSHAKE256128FSIMPLE_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_SPHINCSSHAKE256128FSIMPLE_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

View File

@ -0,0 +1,22 @@
#ifndef SPX_HASH_H
#define SPX_HASH_H
#include <stdint.h>
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_initialize_hash_function(
const unsigned char *pub_seed, const unsigned char *sk_seed);
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_prf_addr(
unsigned char *out, const unsigned char *key, const uint32_t addr[8]);
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_gen_message_random(
unsigned char *R,
const unsigned char *sk_prf, const unsigned char *optrand,
const unsigned char *m, unsigned long long mlen);
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_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, unsigned long long mlen);
#endif

View File

@ -0,0 +1,90 @@
#include <stdint.h>
#include <string.h>
#include "address.h"
#include "fips202.h"
#include "hash.h"
#include "params.h"
#include "utils.h"
/* For SHAKE256, there is no immediate reason to initialize at the start,
so this function is an empty operation. */
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_initialize_hash_function(
const unsigned char *pub_seed, const unsigned char *sk_seed) {
(void)pub_seed; /* Suppress an 'unused parameter' warning. */
(void)sk_seed; /* Suppress an 'unused parameter' warning. */
}
/*
* Computes PRF(key, addr), given a secret key of SPX_N bytes and an address
*/
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_prf_addr(
unsigned char *out, const unsigned char *key, const uint32_t addr[8]) {
unsigned char buf[SPX_N + SPX_ADDR_BYTES];
memcpy(buf, key, SPX_N);
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_addr_to_bytes(buf + SPX_N, addr);
shake256(out, SPX_N, buf, SPX_N + SPX_ADDR_BYTES);
}
/**
* Computes the message-dependent randomness R, using a secret seed and an
* optional randomization value as well as the message.
*/
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_gen_message_random(
unsigned char *R,
const unsigned char *sk_prf, const unsigned char *optrand,
const unsigned char *m, unsigned long long mlen) {
uint64_t s_inc[26];
shake256_inc_init(s_inc);
shake256_inc_absorb(s_inc, sk_prf, SPX_N);
shake256_inc_absorb(s_inc, optrand, SPX_N);
shake256_inc_absorb(s_inc, m, mlen);
shake256_inc_finalize(s_inc);
shake256_inc_squeeze(R, SPX_N, s_inc);
}
/**
* Computes the message hash using R, the public key, and the message.
* Outputs the message digest and the index of the leaf. The index is split in
* the tree index and the leaf index, for convenient copying to an address.
*/
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_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, unsigned long long mlen) {
#define SPX_TREE_BITS (SPX_TREE_HEIGHT * (SPX_D - 1))
#define SPX_TREE_BYTES ((SPX_TREE_BITS + 7) / 8)
#define SPX_LEAF_BITS SPX_TREE_HEIGHT
#define SPX_LEAF_BYTES ((SPX_LEAF_BITS + 7) / 8)
#define SPX_DGST_BYTES (SPX_FORS_MSG_BYTES + SPX_TREE_BYTES + SPX_LEAF_BYTES)
unsigned char buf[SPX_DGST_BYTES];
unsigned char *bufp = buf;
uint64_t s_inc[26];
shake256_inc_init(s_inc);
shake256_inc_absorb(s_inc, R, SPX_N);
shake256_inc_absorb(s_inc, pk, SPX_PK_BYTES);
shake256_inc_absorb(s_inc, m, mlen);
shake256_inc_finalize(s_inc);
shake256_inc_squeeze(buf, SPX_DGST_BYTES, s_inc);
memcpy(digest, bufp, SPX_FORS_MSG_BYTES);
bufp += SPX_FORS_MSG_BYTES;
#if SPX_TREE_BITS > 64
#error For given height and depth, 64 bits cannot represent all subtrees
#endif
*tree = PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_bytes_to_ull(
bufp, SPX_TREE_BYTES);
*tree &= (~(uint64_t)0) >> (64 - SPX_TREE_BITS);
bufp += SPX_TREE_BYTES;
*leaf_idx = PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_bytes_to_ull(
bufp, SPX_LEAF_BYTES);
*leaf_idx &= (~(uint32_t)0) >> (32 - SPX_LEAF_BITS);
}

View File

@ -0,0 +1,81 @@
#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. */
#if SPX_WOTS_W == 256
#define SPX_WOTS_LOGW 8
#elif SPX_WOTS_W == 16
#define SPX_WOTS_LOGW 4
#else
#error SPX_WOTS_W assumed 16 or 256
#endif
#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 */
#if SPX_WOTS_W == 256
#if SPX_N <= 1
#define SPX_WOTS_LEN2 1
#elif SPX_N <= 256
#define SPX_WOTS_LEN2 2
#else
#error Did not precompute SPX_WOTS_LEN2 for n outside {2, .., 256}
#endif
#elif SPX_WOTS_W == 16
#if SPX_N <= 8
#define SPX_WOTS_LEN2 2
#elif SPX_N <= 136
#define SPX_WOTS_LEN2 3
#elif SPX_N <= 256
#define SPX_WOTS_LEN2 4
#else
#error Did not precompute SPX_WOTS_LEN2 for n outside {2, .., 256}
#endif
#endif
#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)
#if SPX_TREE_HEIGHT * SPX_D != SPX_FULL_HEIGHT
#error SPX_D should always divide SPX_FULL_HEIGHT
#endif
/* 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

View File

@ -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_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_type(
wots_addr, SPX_ADDR_TYPE_WOTS);
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_type(
wots_pk_addr, SPX_ADDR_TYPE_WOTSPK);
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_copy_subtree_addr(
wots_addr, tree_addr);
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_keypair_addr(
wots_addr, addr_idx);
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_wots_gen_pk(
pk, sk_seed, pub_seed, wots_addr);
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_copy_keypair_addr(
wots_pk_addr, wots_addr);
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_thash(
leaf, pk, SPX_WOTS_LEN, pub_seed, wots_pk_addr);
}
/*
* Returns the length of a secret key, in bytes
*/
size_t PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_crypto_sign_secretkeybytes(void) {
return PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_CRYPTO_SECRETKEYBYTES;
}
/*
* Returns the length of a public key, in bytes
*/
size_t PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_crypto_sign_publickeybytes(void) {
return PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_CRYPTO_PUBLICKEYBYTES;
}
/*
* Returns the length of a signature, in bytes
*/
size_t PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_crypto_sign_bytes(void) {
return PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_CRYPTO_BYTES;
}
/*
* Returns the length of the seed required to generate a key pair, in bytes
*/
size_t PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_crypto_sign_seedbytes(void) {
return PQCLEAN_SPHINCSSHAKE256128FSIMPLE_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_SPHINCSSHAKE256128FSIMPLE_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_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_layer_addr(
top_tree_addr, SPX_D - 1);
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_type(
top_tree_addr, SPX_ADDR_TYPE_HASHTREE);
/* Initialize SK_SEED, SK_PRF and PUB_SEED from seed. */
memcpy(sk, seed, PQCLEAN_SPHINCSSHAKE256128FSIMPLE_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_SPHINCSSHAKE256128FSIMPLE_CLEAN_initialize_hash_function(pk, sk);
/* Compute root node of the top-most subtree. */
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_treehash(
sk + 3 * SPX_N, auth_path, sk, sk + 2 * SPX_N, 0, 0, SPX_TREE_HEIGHT,
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_SPHINCSSHAKE256128FSIMPLE_CLEAN_crypto_sign_keypair(
uint8_t *pk, uint8_t *sk) {
unsigned char seed[PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_CRYPTO_SEEDBYTES];
randombytes(seed, PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_CRYPTO_SEEDBYTES);
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_crypto_sign_seed_keypair(
pk, sk, seed);
return 0;
}
/**
* Returns an array containing a detached signature.
*/
int PQCLEAN_SPHINCSSHAKE256128FSIMPLE_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];
unsigned long long 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_SPHINCSSHAKE256128FSIMPLE_CLEAN_initialize_hash_function(
pub_seed, sk_seed);
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_type(
wots_addr, SPX_ADDR_TYPE_WOTS);
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_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_SPHINCSSHAKE256128FSIMPLE_CLEAN_gen_message_random(
sig, sk_prf, optrand, m, mlen);
/* Derive the message digest and leaf index from R, PK and M. */
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_hash_message(
mhash, &tree, &idx_leaf, sig, pk, m, mlen);
sig += SPX_N;
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_tree_addr(wots_addr, tree);
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_keypair_addr(
wots_addr, idx_leaf);
/* Sign the message hash using FORS. */
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_fors_sign(
sig, root, mhash, sk_seed, pub_seed, wots_addr);
sig += SPX_FORS_BYTES;
for (i = 0; i < SPX_D; i++) {
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_layer_addr(tree_addr, i);
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_tree_addr(tree_addr, tree);
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_copy_subtree_addr(
wots_addr, tree_addr);
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_keypair_addr(
wots_addr, idx_leaf);
/* Compute a WOTS signature. */
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_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_SPHINCSSHAKE256128FSIMPLE_CLEAN_treehash(
root, sig, sk_seed, pub_seed, idx_leaf, 0,
SPX_TREE_HEIGHT, 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_SPHINCSSHAKE256128FSIMPLE_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_SPHINCSSHAKE256128FSIMPLE_CLEAN_initialize_hash_function(
pub_seed, NULL);
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_type(
wots_addr, SPX_ADDR_TYPE_WOTS);
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_type(
tree_addr, SPX_ADDR_TYPE_HASHTREE);
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_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_SPHINCSSHAKE256128FSIMPLE_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_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_tree_addr(wots_addr, tree);
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_keypair_addr(
wots_addr, idx_leaf);
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_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_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_layer_addr(tree_addr, i);
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_tree_addr(tree_addr, tree);
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_copy_subtree_addr(
wots_addr, tree_addr);
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_keypair_addr(
wots_addr, idx_leaf);
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_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_SPHINCSSHAKE256128FSIMPLE_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_SPHINCSSHAKE256128FSIMPLE_CLEAN_thash(
leaf, wots_pk, SPX_WOTS_LEN, pub_seed, wots_pk_addr);
/* Compute the root node of this subtree. */
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_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_SPHINCSSHAKE256128FSIMPLE_CLEAN_crypto_sign(
uint8_t *sm, size_t *smlen,
const uint8_t *m, size_t mlen, const uint8_t *sk) {
size_t siglen;
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_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_SPHINCSSHAKE256128FSIMPLE_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_SPHINCSSHAKE256128FSIMPLE_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;
}

View File

@ -0,0 +1,10 @@
#ifndef SPX_THASH_H
#define SPX_THASH_H
#include <stdint.h>
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_thash(
unsigned char *out, const unsigned char *in, unsigned int inblocks,
const unsigned char *pub_seed, uint32_t addr[8]);
#endif

View File

@ -0,0 +1,23 @@
#include <stdint.h>
#include <string.h>
#include "thash.h"
#include "address.h"
#include "params.h"
#include "fips202.h"
/**
* Takes an array of inblocks concatenated arrays of SPX_N bytes.
*/
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_thash(
unsigned char *out, const unsigned char *in, unsigned int inblocks,
const unsigned char *pub_seed, uint32_t addr[8]) {
unsigned char buf[SPX_N + SPX_ADDR_BYTES + inblocks * SPX_N];
memcpy(buf, pub_seed, SPX_N);
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_addr_to_bytes(buf + SPX_N, addr);
memcpy(buf + SPX_N + SPX_ADDR_BYTES, in, inblocks * SPX_N);
shake256(out, SPX_N, buf, SPX_N + SPX_ADDR_BYTES + inblocks * SPX_N);
}

View File

@ -0,0 +1,153 @@
#include <string.h>
#include "utils.h"
#include "address.h"
#include "hash.h"
#include "params.h"
#include "thash.h"
/**
* Converts the value of 'in' to 'outlen' bytes in big-endian byte order.
*/
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_ull_to_bytes(
unsigned char *out, unsigned int outlen, unsigned long long in) {
int i;
/* Iterate over out in decreasing order, for big-endianness. */
for (i = outlen - 1; i >= 0; i--) {
out[i] = in & 0xff;
in = in >> 8;
}
}
/**
* Converts the inlen bytes in 'in' from big-endian byte order to an integer.
*/
unsigned long long PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_bytes_to_ull(
const unsigned char *in, unsigned int inlen) {
unsigned long long retval = 0;
unsigned int i;
for (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_SPHINCSSHAKE256128FSIMPLE_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_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_tree_height(addr, i + 1);
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_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_SPHINCSSHAKE256128FSIMPLE_CLEAN_thash(
buffer + SPX_N, buffer, 2, pub_seed, addr);
memcpy(buffer, auth_path, SPX_N);
} else {
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_thash(
buffer, buffer, 2, 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_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_tree_height(addr, tree_height);
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_tree_index(
addr, leaf_idx + idx_offset);
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_thash(
root, buffer, 2, 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.
*/
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_treehash(
unsigned char *root, unsigned char *auth_path,
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 char stack[(tree_height + 1)*SPX_N];
unsigned int heights[tree_height + 1];
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_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_tree_height(
tree_addr, heights[offset - 1] + 1);
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_tree_index(
tree_addr, tree_idx + (idx_offset >> (heights[offset - 1] + 1)));
/* Hash the top-most nodes from the stack together. */
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_thash(
stack + (offset - 2)*SPX_N, stack + (offset - 2)*SPX_N, 2,
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);
}

View File

@ -0,0 +1,48 @@
#ifndef SPX_UTILS_H
#define SPX_UTILS_H
#include "params.h"
#include <stdint.h>
/**
* Converts the value of 'in' to 'outlen' bytes in big-endian byte order.
*/
void PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_ull_to_bytes(
unsigned char *out, unsigned int outlen, unsigned long long in);
/**
* Converts the inlen bytes in 'in' from big-endian byte order to an integer.
*/
unsigned long long PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_bytes_to_ull(
const unsigned char *in, unsigned int 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_SPHINCSSHAKE256128FSIMPLE_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_SPHINCSSHAKE256128FSIMPLE_CLEAN_treehash(
unsigned char *root, unsigned char *auth_path,
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]);
#endif

View File

@ -0,0 +1,159 @@
#include <stdint.h>
#include <string.h>
#include "utils.h"
#include "address.h"
#include "hash.h"
#include "params.h"
#include "thash.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_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_hash_addr(wots_addr, 0);
/* Generate sk element. */
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_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_SPHINCSSHAKE256128FSIMPLE_CLEAN_set_hash_addr(addr, i);
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_CLEAN_thash(
out, out, 1, 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(int *output, const int out_len, const unsigned char *input) {
int in = 0;
int out = 0;
unsigned char total;
int bits = 0;
int consumed;
for (consumed = 0; consumed < out_len; consumed++) {
if (bits == 0) {
total = input[in];
in++;
bits += 8;
}
bits -= SPX_WOTS_LOGW;
output[out] = (total >> bits) & (SPX_WOTS_W - 1);
out++;
}
}
/* Computes the WOTS+ checksum over a message (in base_w). */
static void wots_checksum(int *csum_base_w, const int *msg_base_w) {
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_SPHINCSSHAKE256128FSIMPLE_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(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_SPHINCSSHAKE256128FSIMPLE_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_SPHINCSSHAKE256128FSIMPLE_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_SPHINCSSHAKE256128FSIMPLE_CLEAN_wots_sign(
unsigned char *sig, const unsigned char *msg,
const unsigned char *sk_seed, const unsigned char *pub_seed,
uint32_t addr[8]) {
int lengths[SPX_WOTS_LEN];
uint32_t i;
chain_lengths(lengths, msg);
for (i = 0; i < SPX_WOTS_LEN; i++) {
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_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_SPHINCSSHAKE256128FSIMPLE_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]) {
int lengths[SPX_WOTS_LEN];
uint32_t i;
chain_lengths(lengths, msg);
for (i = 0; i < SPX_WOTS_LEN; i++) {
PQCLEAN_SPHINCSSHAKE256128FSIMPLE_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);
}
}

View File

@ -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_SPHINCSSHAKE256128FSIMPLE_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_SPHINCSSHAKE256128FSIMPLE_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_SPHINCSSHAKE256128FSIMPLE_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