static void validate_authpath(HashFunctions hs, byte[] root, byte[] leaf, uint leafidx, byte[] authpath, int auOff, byte[] masks, int height) { int i, j; byte[] buffer = new byte[2 * SPHINCS256Config.HASH_BYTES]; if ((leafidx & 1) != 0) { for (j = 0; j < SPHINCS256Config.HASH_BYTES; j++) { buffer[SPHINCS256Config.HASH_BYTES + j] = leaf[j]; } for (j = 0; j < SPHINCS256Config.HASH_BYTES; j++) { buffer[j] = authpath[auOff + j]; } } else { for (j = 0; j < SPHINCS256Config.HASH_BYTES; j++) { buffer[j] = leaf[j]; } for (j = 0; j < SPHINCS256Config.HASH_BYTES; j++) { buffer[SPHINCS256Config.HASH_BYTES + j] = authpath[auOff + j]; } } int authOff = auOff + SPHINCS256Config.HASH_BYTES; for (i = 0; i < height - 1; i++) { leafidx >>= 1; if ((leafidx & 1) != 0) { hs.hash_2n_n_mask(buffer, SPHINCS256Config.HASH_BYTES, buffer, 0, masks, 2 * (Wots.WOTS_LOG_L + i) * SPHINCS256Config.HASH_BYTES); for (j = 0; j < SPHINCS256Config.HASH_BYTES; j++) { buffer[j] = authpath[authOff + j]; } } else { hs.hash_2n_n_mask(buffer, 0, buffer, 0, masks, 2 * (Wots.WOTS_LOG_L + i) * SPHINCS256Config.HASH_BYTES); for (j = 0; j < SPHINCS256Config.HASH_BYTES; j++) { buffer[j + SPHINCS256Config.HASH_BYTES] = authpath[authOff + j]; } } authOff += SPHINCS256Config.HASH_BYTES; } hs.hash_2n_n_mask(root, 0, buffer, 0, masks, 2 * (Wots.WOTS_LOG_L + height - 1) * SPHINCS256Config.HASH_BYTES); }
internal static void treehash(HashFunctions hs, byte[] node, int nodeOff, int height, byte[] sk, leafaddr leaf, byte[] masks, int masksOff) { leafaddr a = new leafaddr(leaf); ulong lastnode; int i; byte[] stack = new byte[(height + 1) * SPHINCS256Config.HASH_BYTES]; int[] stacklevels = new int[height + 1]; int stackoffset = 0; lastnode = a.subleaf + (1UL << height); for (; a.subleaf < lastnode; a.subleaf++) { gen_leaf_wots(hs, stack, stackoffset * SPHINCS256Config.HASH_BYTES, masks, masksOff, sk, a); stacklevels[stackoffset] = 0; stackoffset++; while (stackoffset > 1 && stacklevels[stackoffset - 1] == stacklevels[stackoffset - 2]) { //MASKS int maskoffset = 2 * (stacklevels[stackoffset - 1] + Wots.WOTS_LOG_L) * SPHINCS256Config.HASH_BYTES; hs.hash_2n_n_mask(stack, (stackoffset - 2) * SPHINCS256Config.HASH_BYTES, stack, (stackoffset - 2) * SPHINCS256Config.HASH_BYTES, masks, masksOff + maskoffset); stacklevels[stackoffset - 2]++; stackoffset--; } } for (i = 0; i < SPHINCS256Config.HASH_BYTES; i++) { node[nodeOff + i] = stack[i]; } }
static void compute_authpath_wots(HashFunctions hs, byte[] root, byte[] authpath, int authOff, Tree.leafaddr a, byte[] sk, byte[] masks, int height) { int i, j; uint idx; Tree.leafaddr ta = new Tree.leafaddr(a); byte[] tree = new byte[2 * (1 << SPHINCS256Config.SUBTREE_HEIGHT) * SPHINCS256Config.HASH_BYTES]; byte[] seed = new byte[(1 << SPHINCS256Config.SUBTREE_HEIGHT) * SPHINCS256Config.SEED_BYTES]; byte[] pk = new byte[(1 << SPHINCS256Config.SUBTREE_HEIGHT) * Wots.WOTS_L * SPHINCS256Config.HASH_BYTES]; // level 0 for (ta.subleaf = 0; ta.subleaf < (1UL << SPHINCS256Config.SUBTREE_HEIGHT); ta.subleaf++) { Seed.get_seed(hs, seed, (int)(ta.subleaf * (ulong)SPHINCS256Config.SEED_BYTES), sk, ta); } Wots w = new Wots(); for (ta.subleaf = 0; ta.subleaf < (1UL << SPHINCS256Config.SUBTREE_HEIGHT); ta.subleaf++) { w.wots_pkgen(hs, pk, (int)(ta.subleaf * (ulong)Wots.WOTS_L * (ulong)SPHINCS256Config.HASH_BYTES), seed, (int)(ta.subleaf * (ulong)SPHINCS256Config.SEED_BYTES), masks, 0); } for (ta.subleaf = 0; ta.subleaf < (1UL << SPHINCS256Config.SUBTREE_HEIGHT); ta.subleaf++) { Tree.l_tree(hs, tree, (int)((1UL << SPHINCS256Config.SUBTREE_HEIGHT) * (ulong)SPHINCS256Config.HASH_BYTES + ta.subleaf * (ulong)SPHINCS256Config.HASH_BYTES), pk, (int)(ta.subleaf * (ulong)Wots.WOTS_L * (ulong)SPHINCS256Config.HASH_BYTES), masks, 0); } int level = 0; // tree for (i = (1 << SPHINCS256Config.SUBTREE_HEIGHT); i > 0; i >>= 1) { for (j = 0; j < i; j += 2) { hs.hash_2n_n_mask(tree, (i >> 1) * SPHINCS256Config.HASH_BYTES + (j >> 1) * SPHINCS256Config.HASH_BYTES, tree, i * SPHINCS256Config.HASH_BYTES + j * SPHINCS256Config.HASH_BYTES, masks, 2 * (Wots.WOTS_LOG_L + level) * SPHINCS256Config.HASH_BYTES); } level++; } idx = (uint)a.subleaf; // copy authpath for (i = 0; i < height; i++) { Array.Copy(tree, ((1 << SPHINCS256Config.SUBTREE_HEIGHT) >> i) * SPHINCS256Config.HASH_BYTES + ((idx >> i) ^ 1) * SPHINCS256Config.HASH_BYTES, authpath, authOff + i * SPHINCS256Config.HASH_BYTES, SPHINCS256Config.HASH_BYTES); } // copy root Array.Copy(tree, SPHINCS256Config.HASH_BYTES, root, 0, SPHINCS256Config.HASH_BYTES); }
internal static void l_tree(HashFunctions hs, byte[] leaf, int leafOff, byte[] wots_pk, int pkOff, byte[] masks, int masksOff) { uint l = (uint)Wots.WOTS_L; int i, j = 0; for (i = 0; i < Wots.WOTS_LOG_L; i++) { for (j = 0; j < (l >> 1); j++) { hs.hash_2n_n_mask(wots_pk, pkOff + j * SPHINCS256Config.HASH_BYTES, wots_pk, pkOff + j * 2 * SPHINCS256Config.HASH_BYTES, masks, masksOff + i * 2 * SPHINCS256Config.HASH_BYTES); } if ((l & 1) != 0) { Array.Copy(wots_pk, pkOff + (l - 1) * SPHINCS256Config.HASH_BYTES, wots_pk, pkOff + (l >> 1) * SPHINCS256Config.HASH_BYTES, SPHINCS256Config.HASH_BYTES); l = (l >> 1) + 1; } else { l = (l >> 1); } } Array.Copy(wots_pk, pkOff, leaf, leafOff, SPHINCS256Config.HASH_BYTES); }
internal static int horst_verify(HashFunctions hs, byte[] pk, byte[] sig, int sigOff, byte[] masks, byte[] m_hash) { byte[] buffer = new byte[32 * HASH_BYTES]; uint idx; int i, j, k; int sigOffset = sigOff + 64 * HASH_BYTES; for (i = 0; i < HORST_K; i++) { idx = (uint)((m_hash[2 * i] & 0xff) + ((m_hash[2 * i + 1] & 0xff) << 8)); if ((idx & 1) == 0) { hs.hash_n_n(buffer, 0, sig, sigOffset); for (k = 0; k < HASH_BYTES; k++) { buffer[HASH_BYTES + k] = sig[sigOffset + HORST_SKBYTES + k]; } } else { hs.hash_n_n(buffer, HASH_BYTES, sig, sigOffset); for (k = 0; k < HASH_BYTES; k++) { buffer[k] = sig[sigOffset + HORST_SKBYTES + k]; } } sigOffset += HORST_SKBYTES + HASH_BYTES; for (j = 1; j < HORST_LOGT - 6; j++) { idx = idx >> 1; // parent node if ((idx & 1) == 0) { hs.hash_2n_n_mask(buffer, 0, buffer, 0, masks, 2 * (j - 1) * HASH_BYTES); for (k = 0; k < HASH_BYTES; k++) { buffer[HASH_BYTES + k] = sig[sigOffset + k]; } } else { hs.hash_2n_n_mask(buffer, HASH_BYTES, buffer, 0, masks, 2 * (j - 1) * HASH_BYTES); for (k = 0; k < HASH_BYTES; k++) { buffer[k] = sig[sigOffset + k]; } } sigOffset += HASH_BYTES; } idx = idx >> 1; // parent node hs.hash_2n_n_mask(buffer, 0, buffer, 0, masks, 2 * (HORST_LOGT - 7) * HASH_BYTES); for (k = 0; k < HASH_BYTES; k++) { if (sig[sigOff + idx * HASH_BYTES + k] != buffer[k]) { for (k = 0; k < HASH_BYTES; k++) { pk[k] = 0; } return(-1); } } } // Compute root from level10 for (j = 0; j < 32; j++) { hs.hash_2n_n_mask(buffer, j * HASH_BYTES, sig, sigOff + 2 * j * HASH_BYTES, masks, 2 * (HORST_LOGT - 6) * HASH_BYTES); } // Hash from level 11 to 12 for (j = 0; j < 16; j++) { hs.hash_2n_n_mask(buffer, j * HASH_BYTES, buffer, 2 * j * HASH_BYTES, masks, 2 * (HORST_LOGT - 5) * HASH_BYTES); } // Hash from level 12 to 13 for (j = 0; j < 8; j++) { hs.hash_2n_n_mask(buffer, j * HASH_BYTES, buffer, 2 * j * HASH_BYTES, masks, 2 * (HORST_LOGT - 4) * HASH_BYTES); } // Hash from level 13 to 14 for (j = 0; j < 4; j++) { hs.hash_2n_n_mask(buffer, j * HASH_BYTES, buffer, 2 * j * HASH_BYTES, masks, 2 * (HORST_LOGT - 3) * HASH_BYTES); } // Hash from level 14 to 15 for (j = 0; j < 2; j++) { hs.hash_2n_n_mask(buffer, j * HASH_BYTES, buffer, 2 * j * HASH_BYTES, masks, 2 * (HORST_LOGT - 2) * HASH_BYTES); } // Hash from level 15 to 16 hs.hash_2n_n_mask(pk, 0, buffer, 0, masks, 2 * (HORST_LOGT - 1) * HASH_BYTES); return(0); }
internal static int horst_sign(HashFunctions hs, byte[] sig, int sigOff, byte[] pk, byte[] seed, byte[] masks, byte[] m_hash) { byte[] sk = new byte[HORST_T * HORST_SKBYTES]; uint idx; int i, j, k; int sigpos = sigOff; byte[] tree = new byte[(2 * HORST_T - 1) * HASH_BYTES]; /* replace by something more memory-efficient? */ expand_seed(sk, seed); // Build the whole tree and save it // Generate pk leaves for (i = 0; i < HORST_T; i++) { hs.hash_n_n(tree, (HORST_T - 1 + i) * HASH_BYTES, sk, i * HORST_SKBYTES); } long offset_in, offset_out; for (i = 0; i < HORST_LOGT; i++) { offset_in = (1 << (HORST_LOGT - i)) - 1; offset_out = (1 << (HORST_LOGT - i - 1)) - 1; for (j = 0; j < (1 << (HORST_LOGT - i - 1)); j++) { hs.hash_2n_n_mask(tree, (int)((offset_out + j) * HASH_BYTES), tree, (int)((offset_in + 2 * j) * HASH_BYTES), masks, 2 * i * HASH_BYTES); } } // First write 64 hashes from level 10 to the signature for (j = 63 * HASH_BYTES; j < 127 * HASH_BYTES; j++) { sig[sigpos++] = tree[j]; } // Signature consists of HORST_K parts; each part of secret key and HORST_LOGT-4 auth-path hashes for (i = 0; i < HORST_K; i++) { idx = (uint)((m_hash[2 * i] & 0xff) + ((m_hash[2 * i + 1] & 0xff) << 8)); for (k = 0; k < HORST_SKBYTES; k++) { sig[sigpos++] = sk[idx * HORST_SKBYTES + k]; } idx += (uint)(HORST_T - 1); for (j = 0; j < HORST_LOGT - 6; j++) { idx = ((idx & 1) != 0) ? idx + 1 : idx - 1; // neighbor node for (k = 0; k < HASH_BYTES; k++) { sig[sigpos++] = tree[idx * HASH_BYTES + k]; } idx = (idx - 1) / 2; // parent node } } for (i = 0; i < HASH_BYTES; i++) { pk[i] = tree[i]; } return(HORST_SIGBYTES); }