Skip to content

Commit f08f5c2

Browse files
authored
Initial commit
1 parent 001e095 commit f08f5c2

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+3960
-0
lines changed

Makefile

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
AR = /usr/bin/ar
2+
CC = /usr/bin/gcc
3+
CFLAGS = -Wall -O3
4+
5+
test: test.c adr.c endian.c keygen.c private_key_gen.c \
6+
build_merkle.c sphincs_hash.c hmac.c hmac_drbg.c lms_compute.c \
7+
lm_ots_common.c lm_ots_sign.c load.c param.c sha256.c \
8+
sign.c step.c verify.c wots.c zeroize.c tune.h
9+
$(CC) $(CFLAGS) -o test test.c adr.c endian.c keygen.c \
10+
private_key_gen.c build_merkle.c sphincs_hash.c hmac.c \
11+
hmac_drbg.c lms_compute.c lm_ots_common.c \
12+
lm_ots_sign.c load.c param.c sha256.c sign.c \
13+
step.c verify.c wots.c zeroize.c -lcrypto

README

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
This is a hybrid hash based signature scheme, based on Sphincs+.
2+
3+
Its goals are to preserve the security of Sphincs+, while drastically speeding
4+
up how fast we can generate signatures

adr.c

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
#include "adr.h"
2+
#include "endian.h"
3+
#include <string.h>
4+
5+
/*
6+
* Here's the layout of the compressed ADRS structure
7+
* (The Sphincs+ specification doesn't give it explicitly)
8+
*/
9+
#define LAYER_ADDRESS 0 /* 1 byte */
10+
#define TREE_ADDRESS 1 /* 8 bytes */
11+
#define TYPE 9 /* 1 byte */
12+
#define KEY_PAIR 10 /* 4 bytes */
13+
#define CHAIN_ADDRESS 14 /* 4 bytes */
14+
#define HASH_ADDRESS 18 /* 4 bytes */
15+
#define TREE_HEIGHT 14 /* 4 bytes */
16+
#define TREE_INDEX 18 /* 4 bytes */
17+
18+
/* This sets which layer of Merkle trees within the hypertree we are */
19+
/* working on; 0 is the bottom most */
20+
void set_layer_address( adr_t adr, unsigned layer_address ) {
21+
adr[LAYER_ADDRESS] = layer_address;
22+
}
23+
24+
/* This sets which tree within a layer we are working on */
25+
/* 0 is the leftmost */
26+
void set_tree_address( adr_t adr, uint_fast64_t tree_address ) {
27+
put_bigendian( adr+TREE_ADDRESS, tree_address, 8 );
28+
}
29+
30+
/* This sets the type of hash we're doing */
31+
/* See enum adr_type for the various possible types */
32+
/* This also implicitly clears out the remaining values (the ones other */
33+
/* than layer address and tree address) */
34+
void set_type( adr_t adr, enum adr_type type ) {
35+
adr[TYPE] = type;
36+
memset( &adr[TYPE+1], 0, LEN_ADR-(TYPE+1) );
37+
}
38+
39+
/* This sets which WOTS leaf within the tree we're working on */
40+
/* 0 is the leftmost */
41+
/* This assumes that we've already called set_type */
42+
void set_key_pair_address( adr_t adr, unsigned key_pair_address ) {
43+
adr[KEY_PAIR+3] = key_pair_address;
44+
}
45+
46+
/* This sets which WOTS digit we're working on */
47+
/* 0 is the leftmost */
48+
/* This assumes that we've already called set_type */
49+
void set_chain_address( adr_t adr, unsigned chain_address ) {
50+
adr[CHAIN_ADDRESS+3] = chain_address; /* We never have 256 digits in */
51+
/* a WOTS */
52+
}
53+
54+
/* This sets where in the WOTS chain we're working on */
55+
/* 0 is the lowest */
56+
/* This assumes that we've already called set_type */
57+
void set_hash_address( adr_t adr, unsigned hash_address ) {
58+
adr[HASH_ADDRESS+1] = adr[HASH_ADDRESS+2] = 0; /* We might have called */
59+
/* set_tree_index earler */
60+
adr[HASH_ADDRESS+3] = hash_address; /* We never have W > 8 */
61+
}
62+
63+
/* This sets the height of the Merkle node within the tree */
64+
/* 0 is the leaf, 1 is the lowest binary node in the tree */
65+
/* This assumes that we've already called set_type */
66+
void set_tree_height( adr_t adr, unsigned tree_height ) {
67+
adr[TREE_HEIGHT+3] = tree_height; /* We never have more than 8 levels */
68+
/* within a tree */
69+
}
70+
71+
/* This sets the index of the FORS node or the Merkle node within the tree */
72+
/* For FORS, the higher order bits indicate the FORS tree # */
73+
/* 0 is the leftmost */
74+
/* This assumes that we've already called set_type */
75+
void set_tree_index( adr_t adr, uint_fast32_t tree_index ) {
76+
adr[TREE_INDEX+1] = tree_index >> 16;
77+
adr[TREE_INDEX+2] = tree_index >> 8;
78+
adr[TREE_INDEX+3] = tree_index;
79+
}

adr.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#if !defined(ADR_H_)
2+
#define ADR_H_
3+
#include <stdint.h>
4+
5+
typedef unsigned char *adr_t;
6+
#define LEN_ADR 22 /* The lenght of a compressed ADR structure */
7+
8+
#define ADR_CONST_FOR_TREE 9 /* The first 9 bytes of the ADR are constant */
9+
/* for all ADRs for a specific Merkle tree */
10+
11+
/* The various type of hashes that Sphincs+ computes */
12+
enum adr_type {
13+
WOTS_HASH_ADDRESS = 0,
14+
WOTS_KEY_COMPRESSION = 1,
15+
HASH_TREE_ADDRESS = 2,
16+
FORS_TREE_ADDRESS = 3,
17+
FORS_TREE_ROOT_COMPRESS = 4
18+
};
19+
20+
void set_layer_address( adr_t adr, unsigned layer_address );
21+
void set_tree_address( adr_t adr, uint_fast64_t tree_address );
22+
void set_type( adr_t adr, enum adr_type type );
23+
void set_key_pair_address( adr_t adr, unsigned key_pair_address );
24+
void set_chain_address( adr_t adr, unsigned chain_address );
25+
void set_hash_address( adr_t adr, unsigned hash_address );
26+
void set_tree_height( adr_t adr, unsigned tree_height );
27+
void set_tree_index( adr_t adr, uint_fast32_t tree_index );
28+
29+
#endif /* ADR_H_ */

build_merkle.c

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
#include "build_merkle.h"
2+
#include "sphincs_hash.h"
3+
#include "adr.h"
4+
#include "private_key_gen.h"
5+
#include "zeroize.h"
6+
7+
/*
8+
* This is the object that incrementally builds a Merkle tree, and produces
9+
* the authentication path to the specified node (target_node) and the
10+
* root value
11+
* This does it incrementally, because we want to be able to spread the work
12+
* over a number of signature generation requests
13+
* This is split out of the full step procedure because the public key
14+
* generation process also uses this (and also to limit the size of the
15+
* step function, which is already too huge)
16+
*
17+
* The point of this object is to place the results into auth_path and root
18+
* If auth_path is non-NULL, then the authentication path for target_node
19+
* is placed there
20+
* If root is non-NULL, then the root node value is placed there
21+
*/
22+
23+
bool init_build_merkle( struct build_merkle_state *state,
24+
const void *sk_seed, const void *pk_seed,
25+
hash_t hash, int tree_height,
26+
unsigned layer, uint_fast64_t tree,
27+
int target_node, unsigned char *auth_path,
28+
unsigned char *root) {
29+
state->sk_seed = sk_seed;
30+
state->pk_seed = pk_seed;
31+
SHA256_set_first_block(&state->pk_seed_pre, pk_seed, hash_len(hash));
32+
state->hash = hash;
33+
state->n = hash_len(hash);
34+
switch (hash_len( hash )) {
35+
case 16: state->wots_digits = 32 + 3; break;
36+
case 24: state->wots_digits = 48 + 3; break;
37+
case 32: state->wots_digits = 64 + 3; break;
38+
default: return false;
39+
}
40+
state->tree_height = tree_height;
41+
state->target_node = target_node;
42+
set_layer_address( state->adr, layer );
43+
set_tree_address( state->adr, tree );
44+
/* The rest of adr will be initialized later */
45+
state->auth_path = auth_path;
46+
state->root = root;
47+
state->current_node = 0;
48+
49+
return true;
50+
}
51+
52+
/*
53+
* This performs the next step in producing the authentication path and/or
54+
* the root
55+
* This returns TRUE if we're done
56+
* If ret_hc is non-NULL, we place the number of hash compression operations
57+
* we've done there
58+
*/
59+
bool step_build_merkle(struct build_merkle_state *state,
60+
int *ret_hc) {
61+
62+
int hc_done_so_far = 0; /* Count of the number of hash */
63+
/* computations we've done */
64+
65+
#if SPEED_SETTING
66+
#define MERKLE_CHAINS_PER_ITER 1 /* generating 1 OTS public key takes */
67+
/* about as long as the LMS step with W=2 */
68+
#else
69+
#define MERKLE_CHAINS_PER_ITER 2 /* generating 2 OTS public keys takes */
70+
/* about as long as the LMS step with W=4 */
71+
#endif
72+
int m;
73+
struct private_key_generator gen;
74+
bool all_done_flag = false;
75+
76+
for (m=0; m<MERKLE_CHAINS_PER_ITER; m++) {
77+
int current_node = state->current_node;
78+
if (current_node >= (1 << state->tree_height)) {
79+
/* We're done */
80+
all_done_flag = true;
81+
break;
82+
}
83+
84+
/* Fire up the engine that'll produce private WOTS keys */
85+
init_private_key_gen( &gen, state->sk_seed, state->n, state->adr,
86+
ADR_CONST_FOR_TREE );
87+
hc_done_so_far += 1; /* This does about 1 hash compression operation */
88+
89+
/* Build a WOTS public key */
90+
int i;
91+
set_type( state->adr, WOTS_HASH_ADDRESS );
92+
set_key_pair_address( state->adr, current_node );
93+
int n = state->n;
94+
95+
uint32_t wots_buffer[MAX_HASH_LEN/4 * MAX_WOTS_DIGITS]; /* We store */
96+
/* the tops of the WOTS+ chains here */
97+
for (i = 0; i < 51; i++) {
98+
set_chain_address( state->adr, i );
99+
100+
/* Create the private WOTS+ key */
101+
set_hash_address( state->adr, 0 );
102+
void *digit = &wots_buffer[ (n/4)*i ];
103+
do_private_key_gen( digit, n, &gen, &state->adr[LEN_ADR-16] );
104+
105+
/* Now, advance it to the top of the WOTS+ chain */
106+
int j;
107+
for (j=0; j<15; j++) {
108+
set_hash_address( state->adr, j );
109+
do_F(digit, state->hash, &state->pk_seed_pre, state->adr,
110+
digit);
111+
}
112+
}
113+
/* The number of hash compression operations we've done in the */
114+
/* above loop */
115+
hc_done_so_far += 51 * (1 + 15);
116+
117+
/* We've computing all the public WOTS digits */
118+
/* Now, compress the hashes into a single value */
119+
set_type( state->adr, WOTS_KEY_COMPRESSION );
120+
set_key_pair_address( state->adr, current_node );
121+
unsigned char buffer[ MAX_HASH_LEN ];
122+
123+
do_thash( buffer, state->hash, &state->pk_seed_pre, state->adr,
124+
wots_buffer, n * state->wots_digits );\
125+
/* The approximate number of hashes in the above t-hash */
126+
hc_done_so_far += (n * state->wots_digits) / 16 + 1 +
127+
(n * state->wots_digits) / 32;
128+
129+
/* We've put the full WOTS public key */
130+
/* Now, walk up the Merkle tree to combine it with previous computed */
131+
/* WOTS public keys */
132+
int h;
133+
for (h = 0;; h++) {
134+
if (state->auth_path) {
135+
/* If this node is on the authentication path (that is, */
136+
/* adjacent to the path from the root to the target node), */
137+
/* write it out */
138+
if ((state->target_node^current_node) >> h == 1) {
139+
memcpy( state->auth_path + h*n, buffer, n );
140+
}
141+
}
142+
/* Check which child we are to the node immediately above us */
143+
if (current_node & (1<<h)) {
144+
/* We're the right child at this node */
145+
/* Combine it with the corresponding left child */
146+
set_type(state->adr, HASH_TREE_ADDRESS);
147+
set_tree_height(state->adr, h+1 );
148+
set_tree_index(state->adr, current_node >> (h+1));
149+
do_H(buffer, state->hash, &state->pk_seed_pre, state->adr,
150+
state->stack + h*n, buffer );
151+
hc_done_so_far += 2; /* a do_H does 2 hash compressios */
152+
} else {
153+
if (h == state->tree_height) {
154+
/* Actually, there is no node above us; we're at the top */
155+
/* of the tree (aka the root) */
156+
if (state->root) memcpy( state->root, buffer, n );
157+
all_done_flag = true; /* We built the entire tree */
158+
} else {
159+
/* We're the left child at this node */
160+
/* Store it for when we have computed the right child */
161+
memcpy( state->stack + h*n, buffer, n );
162+
}
163+
break;
164+
}
165+
}
166+
167+
/* On the next iteration, start working on the next WOTS leaf */
168+
state->current_node += 1;
169+
}
170+
171+
zeroize( &gen, sizeof gen ); /* There's private data here */
172+
173+
if (ret_hc) *ret_hc = hc_done_so_far;
174+
return all_done_flag;
175+
}

build_merkle.h

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#if !defined( BUILD_MERKLE_H_ )
2+
#define BUILD_MERKLE_H_
3+
4+
#include <stdbool.h>
5+
#include <stdint.h>
6+
#include "sphincs_hash.h"
7+
#include "adr.h"
8+
#include "sha256.h"
9+
10+
#define MAX_WOTS_DIGITS 51 /* Need to move this somewhere else */
11+
#define MAX_XMSS_HEIGHT 8 /* The maximum height of a single XMSS tree */
12+
13+
struct build_merkle_state {
14+
const void *sk_seed, *pk_seed;
15+
SHA256_FIRSTBLOCK pk_seed_pre;
16+
17+
hash_t hash; int n;
18+
int wots_digits; /* Number of digits we compute for each leaf */
19+
int tree_height; /* The height of the XMSS tree */
20+
int target_node; /* If we're generating an authentication path */
21+
/* then this is node the auth path is for */
22+
unsigned char adr[LEN_ADR];
23+
unsigned char *auth_path; /* Where to place the authentication path */
24+
unsigned char *root; /* Where to place the computed root */
25+
int current_node; /* Which XMSS leaf we're working on */
26+
unsigned char stack[MAX_HASH_LEN * MAX_XMSS_HEIGHT]; /* Stack used to */
27+
/* compute the internal XMSS tree nodes */
28+
};
29+
30+
/*
31+
* Initialize the computation of the Merkle tree, including where to
32+
* return the authentication path and the computed root
33+
*/
34+
bool init_build_merkle( struct build_merkle_state *state,
35+
const void *sk_seed, const void *pk_seed,
36+
hash_t hash, int tree_height,
37+
unsigned layer, uint_fast64_t tree,
38+
int target_node, unsigned char *auth_path,
39+
unsigned char *root);
40+
41+
/*
42+
* Perform the next step in the computation of the Merkle tree. Returns
43+
* true when we have completed the computation.
44+
* If ret_hc is non-NULL, the number of hash computations performed is
45+
* written there
46+
*/
47+
bool step_build_merkle(struct build_merkle_state *state,
48+
int *ret_hc);
49+
50+
#endif /* BUILD_MERKLE_H_ */

endian.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#include <stdint.h>
2+
#include "endian.h"
3+
4+
void put_bigendian( void *target, uint_fast64_t value, size_t bytes ) {
5+
unsigned char *b = target;
6+
int i;
7+
8+
for (i = bytes-1; i >= 0; i--) {
9+
b[i] = value & 0xff;
10+
value >>= 8;
11+
}
12+
}
13+
14+
uint_fast64_t get_bigendian( const void *target, size_t bytes ) {
15+
const unsigned char *b = target;
16+
uint_fast64_t result = 0;
17+
int i;
18+
19+
for (i=0; i<bytes; i++) {
20+
result = 256 * result + (b[i] & 0xff);
21+
}
22+
23+
return result;
24+
}

endian.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#if !defined( ENDIAN_H_ )
2+
#define ENDIAN_H_
3+
4+
#include <stddef.h>
5+
#include <stdint.h>
6+
7+
void put_bigendian( void *target, uint_fast64_t value, size_t bytes );
8+
uint_fast64_t get_bigendian( const void *target, size_t bytes );
9+
10+
#endif /* ENDIAN_H_ */

0 commit comments

Comments
 (0)