public AsymmetricCipherKeyPair GenerateKeyPair() { Tree.leafaddr a = new Tree.leafaddr(); byte[] sk = new byte[SPHINCS256Config.CRYPTO_SECRETKEYBYTES]; random.NextBytes(sk); byte[] pk = new byte[SPHINCS256Config.CRYPTO_PUBLICKEYBYTES]; Array.Copy(sk, SPHINCS256Config.SEED_BYTES, pk, 0, Horst.N_MASKS * SPHINCS256Config.HASH_BYTES); // Initialization of top-subtree address a.level = SPHINCS256Config.N_LEVELS - 1; a.subtree = 0; a.subleaf = 0; HashFunctions hs = new HashFunctions(treeDigest); // Format pk: [|N_MASKS*params.HASH_BYTES| Bitmasks || root] // Construct top subtree Tree.treehash(hs, pk, (Horst.N_MASKS * SPHINCS256Config.HASH_BYTES), SPHINCS256Config.SUBTREE_HEIGHT, sk, a, pk, 0); return(new AsymmetricCipherKeyPair(new SphincsPublicKeyParameters(pk), new SphincsPrivateKeyParameters(sk))); }
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 get_seed(HashFunctions hs, byte[] seed, int seedOff, byte[] sk, Tree.leafaddr a) { byte[] buffer = new byte[SPHINCS256Config.SEED_BYTES + 8]; ulong t; int i; for (i = 0; i < SPHINCS256Config.SEED_BYTES; i++) { buffer[i] = sk[i]; } //4 bits to encode level t = (uint)a.level; //55 bits to encode subtree t |= a.subtree << 4; //5 bits to encode leaf t |= a.subleaf << 59; Pack.UInt64_To_LE(t, buffer, SPHINCS256Config.SEED_BYTES); hs.varlen_hash(seed, seedOff, buffer, buffer.Length); }
byte[] crypto_sign(HashFunctions hs, byte[] m, byte[] sk) { byte[] sm = new byte[CRYPTO_BYTES]; int i; ulong leafidx; byte[] R = new byte[SPHINCS256Config.MESSAGE_HASH_SEED_BYTES]; byte[] m_h = new byte[SPHINCS256Config.MSGHASH_BYTES]; ulong[] rnd = new ulong[8]; byte[] root = new byte[SPHINCS256Config.HASH_BYTES]; byte[] seed = new byte[SPHINCS256Config.SEED_BYTES]; byte[] masks = new byte[Horst.N_MASKS * SPHINCS256Config.HASH_BYTES]; int pk; byte[] tsk = new byte[SPHINCS256Config.CRYPTO_SECRETKEYBYTES]; for (i = 0; i < SPHINCS256Config.CRYPTO_SECRETKEYBYTES; i++) { tsk[i] = sk[i]; } // create leafidx deterministically { // shift scratch upwards so we can reuse msg later int scratch = CRYPTO_BYTES - SPHINCS256Config.SK_RAND_SEED_BYTES; // Copy secret random seed to scratch Array.Copy(tsk, SPHINCS256Config.CRYPTO_SECRETKEYBYTES - SPHINCS256Config.SK_RAND_SEED_BYTES, sm, scratch, SPHINCS256Config.SK_RAND_SEED_BYTES); IDigest d = hs.getMessageHash(); byte[] bRnd = new byte[d.GetDigestSize()]; d.BlockUpdate(sm, scratch, SPHINCS256Config.SK_RAND_SEED_BYTES); d.BlockUpdate(m, 0, m.Length); d.DoFinal(bRnd, 0); // wipe sk zerobytes(sm, scratch, SPHINCS256Config.SK_RAND_SEED_BYTES); for (int j = 0; j != rnd.Length; j++) { rnd[j] = Pack.LE_To_UInt64(bRnd, j * 8); } leafidx = rnd[0] & 0xfffffffffffffffUL; Array.Copy(bRnd, 16, R, 0, SPHINCS256Config.MESSAGE_HASH_SEED_BYTES); // prepare msg_hash scratch = CRYPTO_BYTES - SPHINCS256Config.MESSAGE_HASH_SEED_BYTES - SPHINCS256Config.CRYPTO_PUBLICKEYBYTES; // cpy R Array.Copy(R, 0, sm, scratch, SPHINCS256Config.MESSAGE_HASH_SEED_BYTES); // construct and cpy pk Tree.leafaddr b = new Tree.leafaddr(); b.level = SPHINCS256Config.N_LEVELS - 1; b.subtree = 0; b.subleaf = 0; pk = scratch + SPHINCS256Config.MESSAGE_HASH_SEED_BYTES; Array.Copy(tsk, SPHINCS256Config.SEED_BYTES, sm, pk, Horst.N_MASKS * SPHINCS256Config.HASH_BYTES); Tree.treehash(hs, sm, pk + (Horst.N_MASKS * SPHINCS256Config.HASH_BYTES), SPHINCS256Config.SUBTREE_HEIGHT, tsk, b, sm, pk); d = hs.getMessageHash(); d.BlockUpdate(sm, scratch, SPHINCS256Config.MESSAGE_HASH_SEED_BYTES + SPHINCS256Config.CRYPTO_PUBLICKEYBYTES); d.BlockUpdate(m, 0, m.Length); d.DoFinal(m_h, 0); } Tree.leafaddr a = new Tree.leafaddr(); a.level = SPHINCS256Config.N_LEVELS; // Use unique value $d$ for HORST address. a.subleaf = (leafidx & (1UL << SPHINCS256Config.SUBTREE_HEIGHT) - 1); a.subtree = (leafidx >> SPHINCS256Config.SUBTREE_HEIGHT); int smlen = 0; for (i = 0; i < SPHINCS256Config.MESSAGE_HASH_SEED_BYTES; i++) { sm[i] = R[i]; } int smOff = SPHINCS256Config.MESSAGE_HASH_SEED_BYTES; smlen += SPHINCS256Config.MESSAGE_HASH_SEED_BYTES; Array.Copy(tsk, SPHINCS256Config.SEED_BYTES, masks, 0, Horst.N_MASKS * SPHINCS256Config.HASH_BYTES); for (i = 0; i < (SPHINCS256Config.TOTALTREE_HEIGHT + 7) / 8; i++) { sm[smOff + i] = (byte)((leafidx >> 8 * i) & 0xff); } smOff += (SPHINCS256Config.TOTALTREE_HEIGHT + 7) / 8; smlen += (SPHINCS256Config.TOTALTREE_HEIGHT + 7) / 8; Seed.get_seed(hs, seed, 0, tsk, a); int horst_sigbytes = Horst.horst_sign(hs, sm, smOff, root, seed, masks, m_h); smOff += horst_sigbytes; smlen += horst_sigbytes; Wots w = new Wots(); for (i = 0; i < SPHINCS256Config.N_LEVELS; i++) { a.level = i; Seed.get_seed(hs, seed, 0, tsk, a); //XXX: Don't use the same address as for horst_sign here! w.wots_sign(hs, sm, smOff, root, seed, masks); smOff += Wots.WOTS_SIGBYTES; smlen += Wots.WOTS_SIGBYTES; compute_authpath_wots(hs, root, sm, smOff, a, tsk, masks, SPHINCS256Config.SUBTREE_HEIGHT); smOff += SPHINCS256Config.SUBTREE_HEIGHT * SPHINCS256Config.HASH_BYTES; smlen += SPHINCS256Config.SUBTREE_HEIGHT * SPHINCS256Config.HASH_BYTES; a.subleaf = (a.subtree & ((1UL << SPHINCS256Config.SUBTREE_HEIGHT) - 1)); a.subtree >>= SPHINCS256Config.SUBTREE_HEIGHT; } zerobytes(tsk, 0, SPHINCS256Config.CRYPTO_SECRETKEYBYTES); return(sm); }