mirror of
https://github.com/luxfi/pqclean.git
synced 2026-11-02 15:23:24 +00:00
149 lines
6.2 KiB
C
149 lines
6.2 KiB
C
#include <stdint.h>
|
|
#include <string.h>
|
|
|
|
#include "context.h"
|
|
#include "utils.h"
|
|
#include "utilsx8.h"
|
|
|
|
#include "address.h"
|
|
#include "params.h"
|
|
#include "thashx8.h"
|
|
|
|
/*
|
|
* Generate the entire Merkle tree, computing the authentication path for leaf_idx,
|
|
* 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)
|
|
*
|
|
* This expects tree_addrx8 to be initialized to 8 parallel addr structures for
|
|
* the Merkle tree nodes
|
|
*
|
|
* Applies the offset idx_offset to indices before building addresses, so that
|
|
* it is possible to continue counting indices across trees.
|
|
*
|
|
* This works by using the standard Merkle tree building algorithm, except
|
|
* that each 'node' tracked is actually 8 consecutive nodes in the real tree.
|
|
* When we combine two logical nodes ABCDEFGH and STUVWXYZ, we perform the H
|
|
* operation on adjacent real nodes, forming the parent logical node
|
|
* (AB)(CD)(EF)(GH)(ST)(UV)(WX)(YZ)
|
|
*
|
|
* When we get to the top three levels of the real tree (where there is only
|
|
* one logical node), we continue this operation three more times; the right
|
|
* most real node will by the actual root (and the other 7 nodes will be
|
|
* garbage). We follow the same thashx8 logic so that the 'extract
|
|
* authentication path components' part of the loop is still executed (and
|
|
* to simplify the code somewhat)
|
|
*
|
|
* This currently assumes tree_height >= 3; I suspect that doing an adjusting
|
|
* idx, addr_idx on the gen_leafx8 call if tree_height < 3 would fix it; since
|
|
* we don't actually use such short trees, I haven't bothered
|
|
*/
|
|
void treehashx8(unsigned char *root, unsigned char *auth_path,
|
|
const spx_ctx *ctx,
|
|
uint32_t leaf_idx, uint32_t idx_offset,
|
|
uint32_t tree_height,
|
|
void (*gen_leafx8)(
|
|
unsigned char * /* Where to write the leaves */,
|
|
const spx_ctx *,
|
|
uint32_t idx, void *info),
|
|
uint32_t tree_addrx8[8 * 8],
|
|
void *info) {
|
|
/* This is where we keep the intermediate nodes */
|
|
PQCLEAN_VLA(unsigned char, stackx8, tree_height * 8 * SPX_N);
|
|
uint32_t left_adj = 0, prev_left_adj = 0; /* When we're doing the top 3 */
|
|
/* levels, the left-most part of the tree isn't at the beginning */
|
|
/* of current[]. These give the offset of the actual start */
|
|
|
|
uint32_t idx;
|
|
uint32_t max_idx = ((uint32_t)1 << (tree_height - 3)) - 1;
|
|
for (idx = 0;; idx++) {
|
|
unsigned char current[8 * SPX_N]; /* Current logical node */
|
|
gen_leafx8( current, ctx, (8 * idx) + idx_offset,
|
|
info );
|
|
|
|
/* Now combine the freshly generated right node with previously */
|
|
/* generated left ones */
|
|
uint32_t internal_idx_offset = idx_offset;
|
|
uint32_t internal_idx = idx;
|
|
uint32_t internal_leaf = leaf_idx;
|
|
uint32_t h; /* The height we are in the Merkle tree */
|
|
for (h = 0;; h++, internal_idx >>= 1, internal_leaf >>= 1) {
|
|
|
|
/* Special processing if we're at the top of the tree */
|
|
if (h >= tree_height - 3) {
|
|
if (h == tree_height) {
|
|
/* We hit the root; return it */
|
|
memcpy( root, ¤t[7 * SPX_N], SPX_N );
|
|
return;
|
|
}
|
|
/* The tree indexing logic is a bit off in this case */
|
|
/* Adjust it so that the left-most node of the part of */
|
|
/* the tree that we're processing has index 0 */
|
|
prev_left_adj = left_adj;
|
|
left_adj = (uint32_t)(8 - (1 << (tree_height - h - 1)));
|
|
}
|
|
|
|
/* Check if we hit the top of the tree */
|
|
if (h == tree_height) {
|
|
/* We hit the root; return it */
|
|
memcpy( root, ¤t[7 * SPX_N], SPX_N );
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Check if one of the nodes we have is a part of the
|
|
* authentication path; if it is, write it out
|
|
*/
|
|
if ((((internal_idx << 3) ^ internal_leaf) & ~0x7U) == 0) {
|
|
memcpy( &auth_path[ h * SPX_N ],
|
|
¤t[(((internal_leaf & 7) ^ 1) + prev_left_adj) * SPX_N],
|
|
SPX_N );
|
|
}
|
|
|
|
/*
|
|
* Check if we're at a left child; if so, stop going up the stack
|
|
* Exception: if we've reached the end of the tree, keep on going
|
|
* (so we combine the last 8 nodes into the one root node in three
|
|
* more iterations)
|
|
*/
|
|
if ((internal_idx & 1) == 0 && idx < max_idx) {
|
|
break;
|
|
}
|
|
|
|
/* Ok, we're at a right node (or doing the top 3 levels) */
|
|
/* Now combine the left and right logical nodes together */
|
|
|
|
/* Set the address of the node we're creating. */
|
|
uint32_t j;
|
|
internal_idx_offset >>= 1;
|
|
for (j = 0; j < 8; j++) {
|
|
set_tree_height(tree_addrx8 + (j * 8), h + 1);
|
|
set_tree_index(tree_addrx8 + (j * 8),
|
|
((8 / 2) * (internal_idx & ~1U)) + j - left_adj + internal_idx_offset );
|
|
}
|
|
unsigned char *left = &stackx8[h * 8 * SPX_N];
|
|
thashx8( ¤t[0 * SPX_N],
|
|
¤t[1 * SPX_N],
|
|
¤t[2 * SPX_N],
|
|
¤t[3 * SPX_N],
|
|
¤t[4 * SPX_N],
|
|
¤t[5 * SPX_N],
|
|
¤t[6 * SPX_N],
|
|
¤t[7 * SPX_N],
|
|
&left [0 * SPX_N],
|
|
&left [2 * SPX_N],
|
|
&left [4 * SPX_N],
|
|
&left [6 * SPX_N],
|
|
¤t[0 * SPX_N],
|
|
¤t[2 * SPX_N],
|
|
¤t[4 * SPX_N],
|
|
¤t[6 * SPX_N],
|
|
2, ctx, tree_addrx8);
|
|
}
|
|
|
|
/* We've hit a left child; save the current for when we get the */
|
|
/* corresponding right right */
|
|
memcpy( &stackx8[h * 8 * SPX_N], current, 8 * SPX_N);
|
|
}
|
|
}
|