package org.bouncycastle.pqc.crypto.gmss; import java.util.Vector; import org.bouncycastle.crypto.Digest; import org.bouncycastle.pqc.crypto.gmss.util.GMSSRandom; import org.bouncycastle.pqc.crypto.gmss.util.WinternitzOTSignature; import org.bouncycastle.util.Arrays; /** * This class provides a specification for a GMSS private key. */ public class GMSSPrivateKeyParameters extends GMSSKeyParameters { private int[] index; private byte[][] currentSeeds; private byte[][] nextNextSeeds; private byte[][][] currentAuthPaths; private byte[][][] nextAuthPaths; private Treehash[][] currentTreehash; private Treehash[][] nextTreehash; private Vector[] currentStack; private Vector[] nextStack; private Vector[][] currentRetain; private Vector[][] nextRetain; private byte[][][] keep; private GMSSLeaf[] nextNextLeaf; private GMSSLeaf[] upperLeaf; private GMSSLeaf[] upperTreehashLeaf; private int[] minTreehash; private GMSSParameters gmssPS; private byte[][] nextRoot; private GMSSRootCalc[] nextNextRoot; private byte[][] currentRootSig; private GMSSRootSig[] nextRootSig; private GMSSDigestProvider digestProvider; private boolean used = false; /** * An array of the heights of the authentication trees of each layer */ private int[] heightOfTrees; /** * An array of the Winternitz parameter 'w' of each layer */ private int[] otsIndex; /** * The parameter K needed for the authentication path computation */ private int[] K; /** * the number of Layers */ private int numLayer; /** * The hash function used to construct the authentication trees */ private Digest messDigestTrees; /** * The message digest length */ private int mdLength; /** * The PRNG used for private key generation */ private GMSSRandom gmssRandom; /** * The number of leafs of one tree of each layer */ private int[] numLeafs; /** * Generates a new GMSS private key * * @param currentSeed seed for the generation of private OTS keys for the * current subtrees * @param nextNextSeed seed for the generation of private OTS keys for the next * subtrees * @param currentAuthPath array of current authentication paths * @param nextAuthPath array of next authentication paths * @param currentTreehash array of current treehash instances * @param nextTreehash array of next treehash instances * @param currentStack array of current shared stacks * @param nextStack array of next shared stacks * @param currentRetain array of current retain stacks * @param nextRetain array of next retain stacks * @param nextRoot the roots of the next subtree * @param currentRootSig array of signatures of the roots of the current subtrees * @param gmssParameterset the GMSS Parameterset * @see org.bouncycastle.pqc.crypto.gmss.GMSSKeyPairGenerator */ public GMSSPrivateKeyParameters(byte[][] currentSeed, byte[][] nextNextSeed, byte[][][] currentAuthPath, byte[][][] nextAuthPath, Treehash[][] currentTreehash, Treehash[][] nextTreehash, Vector[] currentStack, Vector[] nextStack, Vector[][] currentRetain, Vector[][] nextRetain, byte[][] nextRoot, byte[][] currentRootSig, GMSSParameters gmssParameterset, GMSSDigestProvider digestProvider) { this(null, currentSeed, nextNextSeed, currentAuthPath, nextAuthPath, null, currentTreehash, nextTreehash, currentStack, nextStack, currentRetain, nextRetain, null, null, null, null, nextRoot, null, currentRootSig, null, gmssParameterset, digestProvider); } /** * /** * * @param index tree indices * @param keep keep array for the authPath algorithm * @param currentTreehash treehash for authPath algorithm of current tree * @param nextTreehash treehash for authPath algorithm of next tree (TREE+) * @param currentStack shared stack for authPath algorithm of current tree * @param nextStack shared stack for authPath algorithm of next tree (TREE+) * @param currentRetain retain stack for authPath algorithm of current tree * @param nextRetain retain stack for authPath algorithm of next tree (TREE+) * @param nextNextLeaf array of upcoming leafs of the tree after next (LEAF++) of * each layer * @param upperLeaf needed for precomputation of upper nodes * @param upperTreehashLeaf needed for precomputation of upper treehash nodes * @param minTreehash index of next treehash instance to receive an update * @param nextRoot the roots of the next trees (ROOT+) * @param nextNextRoot the roots of the tree after next (ROOT++) * @param currentRootSig array of signatures of the roots of the current subtrees * (SIG) * @param nextRootSig array of signatures of the roots of the next subtree * (SIG+) * @param gmssParameterset the GMSS Parameterset */ public GMSSPrivateKeyParameters(int[] index, byte[][] currentSeeds, byte[][] nextNextSeeds, byte[][][] currentAuthPaths, byte[][][] nextAuthPaths, byte[][][] keep, Treehash[][] currentTreehash, Treehash[][] nextTreehash, Vector[] currentStack, Vector[] nextStack, Vector[][] currentRetain, Vector[][] nextRetain, GMSSLeaf[] nextNextLeaf, GMSSLeaf[] upperLeaf, GMSSLeaf[] upperTreehashLeaf, int[] minTreehash, byte[][] nextRoot, GMSSRootCalc[] nextNextRoot, byte[][] currentRootSig, GMSSRootSig[] nextRootSig, GMSSParameters gmssParameterset, GMSSDigestProvider digestProvider) { super(true, gmssParameterset); // construct message digest this.messDigestTrees = digestProvider.get(); this.mdLength = messDigestTrees.getDigestSize(); // Parameter this.gmssPS = gmssParameterset; this.otsIndex = gmssParameterset.getWinternitzParameter(); this.K = gmssParameterset.getK(); this.heightOfTrees = gmssParameterset.getHeightOfTrees(); // initialize numLayer this.numLayer = gmssPS.getNumOfLayers(); // initialize index if null if (index == null) { this.index = new int[numLayer]; for (int i = 0; i < numLayer; i++) { this.index[i] = 0; } } else { this.index = index; } this.currentSeeds = currentSeeds; this.nextNextSeeds = nextNextSeeds; this.currentAuthPaths = currentAuthPaths; this.nextAuthPaths = nextAuthPaths; // initialize keep if null if (keep == null) { this.keep = new byte[numLayer][][]; for (int i = 0; i < numLayer; i++) { this.keep[i] = new byte[(int)Math.floor(heightOfTrees[i] / 2)][mdLength]; } } else { this.keep = keep; } // initialize stack if null if (currentStack == null) { this.currentStack = new Vector[numLayer]; for (int i = 0; i < numLayer; i++) { this.currentStack[i] = new Vector(); } } else { this.currentStack = currentStack; } // initialize nextStack if null if (nextStack == null) { this.nextStack = new Vector[numLayer - 1]; for (int i = 0; i < numLayer - 1; i++) { this.nextStack[i] = new Vector(); } } else { this.nextStack = nextStack; } this.currentTreehash = currentTreehash; this.nextTreehash = nextTreehash; this.currentRetain = currentRetain; this.nextRetain = nextRetain; this.nextRoot = nextRoot; this.digestProvider = digestProvider; if (nextNextRoot == null) { this.nextNextRoot = new GMSSRootCalc[numLayer - 1]; for (int i = 0; i < numLayer - 1; i++) { this.nextNextRoot[i] = new GMSSRootCalc( this.heightOfTrees[i + 1], this.K[i + 1], this.digestProvider); } } else { this.nextNextRoot = nextNextRoot; } this.currentRootSig = currentRootSig; // calculate numLeafs numLeafs = new int[numLayer]; for (int i = 0; i < numLayer; i++) { numLeafs[i] = 1 << heightOfTrees[i]; } // construct PRNG this.gmssRandom = new GMSSRandom(messDigestTrees); if (numLayer > 1) { // construct the nextNextLeaf (LEAFs++) array for upcoming leafs in // tree after next (TREE++) if (nextNextLeaf == null) { this.nextNextLeaf = new GMSSLeaf[numLayer - 2]; for (int i = 0; i < numLayer - 2; i++) { this.nextNextLeaf[i] = new GMSSLeaf(digestProvider.get(), otsIndex[i + 1], numLeafs[i + 2], this.nextNextSeeds[i]); } } else { this.nextNextLeaf = nextNextLeaf; } } else { this.nextNextLeaf = new GMSSLeaf[0]; } // construct the upperLeaf array for upcoming leafs in tree over the // actual if (upperLeaf == null) { this.upperLeaf = new GMSSLeaf[numLayer - 1]; for (int i = 0; i < numLayer - 1; i++) { this.upperLeaf[i] = new GMSSLeaf(digestProvider.get(), otsIndex[i], numLeafs[i + 1], this.currentSeeds[i]); } } else { this.upperLeaf = upperLeaf; } // construct the leafs for upcoming leafs in treehashs in tree over the // actual if (upperTreehashLeaf == null) { this.upperTreehashLeaf = new GMSSLeaf[numLayer - 1]; for (int i = 0; i < numLayer - 1; i++) { this.upperTreehashLeaf[i] = new GMSSLeaf(digestProvider.get(), otsIndex[i], numLeafs[i + 1]); } } else { this.upperTreehashLeaf = upperTreehashLeaf; } if (minTreehash == null) { this.minTreehash = new int[numLayer - 1]; for (int i = 0; i < numLayer - 1; i++) { this.minTreehash[i] = -1; } } else { this.minTreehash = minTreehash; } // construct the nextRootSig (RootSig++) byte[] dummy = new byte[mdLength]; byte[] OTSseed = new byte[mdLength]; if (nextRootSig == null) { this.nextRootSig = new GMSSRootSig[numLayer - 1]; for (int i = 0; i < numLayer - 1; i++) { System.arraycopy(currentSeeds[i], 0, dummy, 0, mdLength); gmssRandom.nextSeed(dummy); OTSseed = gmssRandom.nextSeed(dummy); this.nextRootSig[i] = new GMSSRootSig(digestProvider.get(), otsIndex[i], heightOfTrees[i + 1]); this.nextRootSig[i].initSign(OTSseed, nextRoot[i]); } } else { this.nextRootSig = nextRootSig; } } // we assume this only gets called from nextKey so used is never copied. private GMSSPrivateKeyParameters(GMSSPrivateKeyParameters original) { super(true, original.getParameters()); this.index = Arrays.clone(original.index); this.currentSeeds = Arrays.clone(original.currentSeeds); this.nextNextSeeds = Arrays.clone(original.nextNextSeeds); this.currentAuthPaths = Arrays.clone(original.currentAuthPaths); this.nextAuthPaths = Arrays.clone(original.nextAuthPaths); this.currentTreehash = original.currentTreehash; this.nextTreehash = original.nextTreehash; this.currentStack = original.currentStack; this.nextStack = original.nextStack; this.currentRetain = original.currentRetain; this.nextRetain = original.nextRetain; this.keep = Arrays.clone(original.keep); this.nextNextLeaf = original.nextNextLeaf; this.upperLeaf = original.upperLeaf; this.upperTreehashLeaf = original.upperTreehashLeaf; this.minTreehash = original.minTreehash; this.gmssPS = original.gmssPS; this.nextRoot = Arrays.clone(original.nextRoot); this.nextNextRoot = original.nextNextRoot; this.currentRootSig = original.currentRootSig; this.nextRootSig = original.nextRootSig; this.digestProvider = original.digestProvider; this.heightOfTrees = original.heightOfTrees; this.otsIndex = original.otsIndex; this.K = original.K; this.numLayer = original.numLayer; this.messDigestTrees = original.messDigestTrees; this.mdLength = original.mdLength; this.gmssRandom = original.gmssRandom; this.numLeafs = original.numLeafs; } public boolean isUsed() { return this.used; } public void markUsed() { this.used = true; } public GMSSPrivateKeyParameters nextKey() { GMSSPrivateKeyParameters nKey = new GMSSPrivateKeyParameters(this); nKey.nextKey(gmssPS.getNumOfLayers() - 1); return nKey; } /** * This method updates the GMSS private key for the next signature * * @param layer the layer where the next key is processed */ private void nextKey(int layer) { // only for lowest layer ( other layers indices are raised in nextTree() // method ) if (layer == numLayer - 1) { index[layer]++; } // else System.out.println(" --- nextKey on layer " + layer + " // index is now : " + index[layer]); // if tree of this layer is depleted if (index[layer] == numLeafs[layer]) { if (numLayer != 1) { nextTree(layer); index[layer] = 0; } } else { updateKey(layer); } } /** * Switch to next subtree if the current one is depleted * * @param layer the layer where the next tree is processed */ private void nextTree(int layer) { // System.out.println("NextTree method called on layer " + layer); // dont create next tree for the top layer if (layer > 0) { // raise index for upper layer index[layer - 1]++; // test if it is already the last tree boolean lastTree = true; int z = layer; do { z--; if (index[z] < numLeafs[z]) { lastTree = false; } } while (lastTree && (z > 0)); // only construct next subtree if last one is not already in use if (!lastTree) { gmssRandom.nextSeed(currentSeeds[layer]); // last step of distributed signature calculation nextRootSig[layer - 1].updateSign(); // last step of distributed leaf calculation for nextNextLeaf if (layer > 1) { nextNextLeaf[layer - 1 - 1] = nextNextLeaf[layer - 1 - 1].nextLeaf(); } // last step of distributed leaf calculation for upper leaf upperLeaf[layer - 1] = upperLeaf[layer - 1].nextLeaf(); // last step of distributed leaf calculation for all treehashs if (minTreehash[layer - 1] >= 0) { upperTreehashLeaf[layer - 1] = upperTreehashLeaf[layer - 1].nextLeaf(); byte[] leaf = this.upperTreehashLeaf[layer - 1].getLeaf(); // if update is required use the precomputed leaf to update // treehash try { currentTreehash[layer - 1][minTreehash[layer - 1]] .update(this.gmssRandom, leaf); // System.out.println("UUUpdated TH " + // minTreehash[layer - 1]); if (currentTreehash[layer - 1][minTreehash[layer - 1]] .wasFinished()) { // System.out.println("FFFinished TH " + // minTreehash[layer - 1]); } } catch (Exception e) { System.out.println(e); } } // last step of nextNextAuthRoot calculation this.updateNextNextAuthRoot(layer); // ******************************************************** / // NOW: advance to next tree on layer 'layer' // NextRootSig --> currentRootSigs this.currentRootSig[layer - 1] = nextRootSig[layer - 1] .getSig(); // ----------------------- // nextTreehash --> currentTreehash // nextNextTreehash --> nextTreehash for (int i = 0; i < heightOfTrees[layer] - K[layer]; i++) { this.currentTreehash[layer][i] = this.nextTreehash[layer - 1][i]; this.nextTreehash[layer - 1][i] = this.nextNextRoot[layer - 1] .getTreehash()[i]; } // NextAuthPath --> currentAuthPath // nextNextAuthPath --> nextAuthPath for (int i = 0; i < heightOfTrees[layer]; i++) { System.arraycopy(nextAuthPaths[layer - 1][i], 0, currentAuthPaths[layer][i], 0, mdLength); System.arraycopy(nextNextRoot[layer - 1].getAuthPath()[i], 0, nextAuthPaths[layer - 1][i], 0, mdLength); } // nextRetain --> currentRetain // nextNextRetain --> nextRetain for (int i = 0; i < K[layer] - 1; i++) { this.currentRetain[layer][i] = this.nextRetain[layer - 1][i]; this.nextRetain[layer - 1][i] = this.nextNextRoot[layer - 1] .getRetain()[i]; } // nextStack --> currentStack this.currentStack[layer] = this.nextStack[layer - 1]; // nextNextStack --> nextStack this.nextStack[layer - 1] = this.nextNextRoot[layer - 1] .getStack(); // nextNextRoot --> nextRoot this.nextRoot[layer - 1] = this.nextNextRoot[layer - 1] .getRoot(); // ----------------------- // ----------------- byte[] OTSseed = new byte[mdLength]; byte[] dummy = new byte[mdLength]; // gmssRandom.setSeed(currentSeeds[layer]); System .arraycopy(currentSeeds[layer - 1], 0, dummy, 0, mdLength); OTSseed = gmssRandom.nextSeed(dummy); // only need OTSSeed OTSseed = gmssRandom.nextSeed(dummy); OTSseed = gmssRandom.nextSeed(dummy); // nextWinSig[layer-1]=new // GMSSWinSig(OTSseed,algNames,otsIndex[layer-1],heightOfTrees[layer],nextRoot[layer-1]); nextRootSig[layer - 1].initSign(OTSseed, nextRoot[layer - 1]); // nextKey for upper layer nextKey(layer - 1); } } } /** * This method computes the authpath (AUTH) for the current tree, * Additionally the root signature for the next tree (SIG+), the authpath * (AUTH++) and root (ROOT++) for the tree after next in layer * <code>layer</code>, and the LEAF++^1 for the next next tree in the * layer above are updated This method is used by nextKey() * * @param layer */ private void updateKey(int layer) { // ----------current tree processing of actual layer--------- // compute upcoming authpath for current Tree (AUTH) computeAuthPaths(layer); // -----------distributed calculations part------------ // not for highest tree layer if (layer > 0) { // compute (partial) next leaf on TREE++ (not on layer 1 and 0) if (layer > 1) { nextNextLeaf[layer - 1 - 1] = nextNextLeaf[layer - 1 - 1].nextLeaf(); } // compute (partial) next leaf on tree above (not on layer 0) upperLeaf[layer - 1] = upperLeaf[layer - 1].nextLeaf(); // compute (partial) next leaf for all treehashs on tree above (not // on layer 0) int t = (int)Math .floor((double)(this.getNumLeafs(layer) * 2) / (double)(this.heightOfTrees[layer - 1] - this.K[layer - 1])); if (index[layer] % t == 1) { // System.out.println(" layer: " + layer + " index: " + // index[layer] + " t : " + t); // take precomputed node for treehash update // ------------------------------------------------ if (index[layer] > 1 && minTreehash[layer - 1] >= 0) { byte[] leaf = this.upperTreehashLeaf[layer - 1].getLeaf(); // if update is required use the precomputed leaf to update // treehash try { currentTreehash[layer - 1][minTreehash[layer - 1]] .update(this.gmssRandom, leaf); // System.out.println("Updated TH " + minTreehash[layer // - 1]); if (currentTreehash[layer - 1][minTreehash[layer - 1]] .wasFinished()) { // System.out.println("Finished TH " + // minTreehash[layer - 1]); } } catch (Exception e) { System.out.println(e); } // ------------------------------------------------ } // initialize next leaf precomputation // ------------------------------------------------ // get lowest index of treehashs this.minTreehash[layer - 1] = getMinTreehashIndex(layer - 1); if (this.minTreehash[layer - 1] >= 0) { // initialize leaf byte[] seed = this.currentTreehash[layer - 1][this.minTreehash[layer - 1]] .getSeedActive(); this.upperTreehashLeaf[layer - 1] = new GMSSLeaf( this.digestProvider.get(), this.otsIndex[layer - 1], t, seed); this.upperTreehashLeaf[layer - 1] = this.upperTreehashLeaf[layer - 1].nextLeaf(); // System.out.println("restarted treehashleaf (" + (layer - // 1) + "," + this.minTreehash[layer - 1] + ")"); } // ------------------------------------------------ } else { // update the upper leaf for the treehash one step if (this.minTreehash[layer - 1] >= 0) { this.upperTreehashLeaf[layer - 1] = this.upperTreehashLeaf[layer - 1].nextLeaf(); // if (minTreehash[layer - 1] > 3) // System.out.print("#"); } } // compute (partial) the signature of ROOT+ (RootSig+) (not on top // layer) nextRootSig[layer - 1].updateSign(); // compute (partial) AUTHPATH++ & ROOT++ (not on top layer) if (index[layer] == 1) { // init root and authpath calculation for tree after next // (AUTH++, ROOT++) this.nextNextRoot[layer - 1].initialize(new Vector()); } // update root and authpath calculation for tree after next (AUTH++, // ROOT++) this.updateNextNextAuthRoot(layer); } // ----------- end distributed calculations part----------------- } /** * This method returns the index of the next Treehash instance that should * receive an update * * @param layer the layer of the GMSS tree * @return index of the treehash instance that should get the update */ private int getMinTreehashIndex(int layer) { int minTreehash = -1; for (int h = 0; h < heightOfTrees[layer] - K[layer]; h++) { if (currentTreehash[layer][h].wasInitialized() && !currentTreehash[layer][h].wasFinished()) { if (minTreehash == -1) { minTreehash = h; } else if (currentTreehash[layer][h].getLowestNodeHeight() < currentTreehash[layer][minTreehash] .getLowestNodeHeight()) { minTreehash = h; } } } return minTreehash; } /** * Computes the upcoming currentAuthpath of layer <code>layer</code> using * the revisited authentication path computation of Dahmen/Schneider 2008 * * @param layer the actual layer */ private void computeAuthPaths(int layer) { int Phi = index[layer]; int H = heightOfTrees[layer]; int K = this.K[layer]; // update all nextSeeds for seed scheduling for (int i = 0; i < H - K; i++) { currentTreehash[layer][i].updateNextSeed(gmssRandom); } // STEP 1 of Algorithm int Tau = heightOfPhi(Phi); byte[] OTSseed = new byte[mdLength]; OTSseed = gmssRandom.nextSeed(currentSeeds[layer]); // STEP 2 of Algorithm // if phi's parent on height tau + 1 if left node, store auth_tau // in keep_tau. // TODO check it, formerly was // int L = Phi / (int) Math.floor(Math.pow(2, Tau + 1)); // L %= 2; int L = (Phi >>> (Tau + 1)) & 1; byte[] tempKeep = new byte[mdLength]; // store the keep node not in keep[layer][tau/2] because it might be in // use // wait until the space is freed in step 4a if (Tau < H - 1 && L == 0) { System.arraycopy(currentAuthPaths[layer][Tau], 0, tempKeep, 0, mdLength); } byte[] help = new byte[mdLength]; // STEP 3 of Algorithm // if phi is left child, compute and store leaf for next currentAuthPath // path, // (obtained by veriying current signature) if (Tau == 0) { // LEAFCALC !!! if (layer == numLayer - 1) { // lowest layer computes the // necessary leaf completely at this // time WinternitzOTSignature ots = new WinternitzOTSignature(OTSseed, digestProvider.get(), otsIndex[layer]); help = ots.getPublicKey(); } else { // other layers use the precomputed leafs in // nextNextLeaf byte[] dummy = new byte[mdLength]; System.arraycopy(currentSeeds[layer], 0, dummy, 0, mdLength); gmssRandom.nextSeed(dummy); help = upperLeaf[layer].getLeaf(); this.upperLeaf[layer].initLeafCalc(dummy); // WinternitzOTSVerify otsver = new // WinternitzOTSVerify(algNames, otsIndex[layer]); // byte[] help2 = otsver.Verify(currentRoot[layer], // currentRootSig[layer]); // System.out.println(" --- " + layer + " " + // ByteUtils.toHexString(help) + " " + // ByteUtils.toHexString(help2)); } System.arraycopy(help, 0, currentAuthPaths[layer][0], 0, mdLength); } else { // STEP 4a of Algorithm // get new left currentAuthPath node on height tau byte[] toBeHashed = new byte[mdLength << 1]; System.arraycopy(currentAuthPaths[layer][Tau - 1], 0, toBeHashed, 0, mdLength); // free the shared keep[layer][tau/2] System.arraycopy(keep[layer][(int)Math.floor((Tau - 1) / 2)], 0, toBeHashed, mdLength, mdLength); messDigestTrees.update(toBeHashed, 0, toBeHashed.length); currentAuthPaths[layer][Tau] = new byte[messDigestTrees.getDigestSize()]; messDigestTrees.doFinal(currentAuthPaths[layer][Tau], 0); // STEP 4b and 4c of Algorithm // copy right nodes to currentAuthPath on height 0..Tau-1 for (int i = 0; i < Tau; i++) { // STEP 4b of Algorithm // 1st: copy from treehashs if (i < H - K) { if (currentTreehash[layer][i].wasFinished()) { System.arraycopy(currentTreehash[layer][i] .getFirstNode(), 0, currentAuthPaths[layer][i], 0, mdLength); currentTreehash[layer][i].destroy(); } else { System.err .println("Treehash (" + layer + "," + i + ") not finished when needed in AuthPathComputation"); } } // 2nd: copy precomputed values from Retain if (i < H - 1 && i >= H - K) { if (currentRetain[layer][i - (H - K)].size() > 0) { // pop element from retain System.arraycopy(currentRetain[layer][i - (H - K)] .lastElement(), 0, currentAuthPaths[layer][i], 0, mdLength); currentRetain[layer][i - (H - K)] .removeElementAt(currentRetain[layer][i - (H - K)].size() - 1); } } // STEP 4c of Algorithm // initialize new stack at heights 0..Tau-1 if (i < H - K) { // create stacks anew int startPoint = Phi + 3 * (1 << i); if (startPoint < numLeafs[layer]) { // if (layer < 2) { // System.out.println("initialized TH " + i + " on layer // " + layer); // } currentTreehash[layer][i].initialize(); } } } } // now keep space is free to use if (Tau < H - 1 && L == 0) { System.arraycopy(tempKeep, 0, keep[layer][(int)Math.floor(Tau / 2)], 0, mdLength); } // only update empty stack at height h if all other stacks have // tailnodes with height >h // finds active stack with lowest node height, choses lower index in // case of tie // on the lowest layer leafs must be computed at once, no precomputation // is possible. So all treehash updates are done at once here if (layer == numLayer - 1) { for (int tmp = 1; tmp <= (H - K) / 2; tmp++) { // index of the treehash instance that receives the next update int minTreehash = getMinTreehashIndex(layer); // if active treehash is found update with a leaf if (minTreehash >= 0) { try { byte[] seed = new byte[mdLength]; System.arraycopy( this.currentTreehash[layer][minTreehash] .getSeedActive(), 0, seed, 0, mdLength); byte[] seed2 = gmssRandom.nextSeed(seed); WinternitzOTSignature ots = new WinternitzOTSignature( seed2, this.digestProvider.get(), this.otsIndex[layer]); byte[] leaf = ots.getPublicKey(); currentTreehash[layer][minTreehash].update( this.gmssRandom, leaf); } catch (Exception e) { System.out.println(e); } } } } else { // on higher layers the updates are done later this.minTreehash[layer] = getMinTreehashIndex(layer); } } /** * Returns the largest h such that 2^h | Phi * * @param Phi the leaf index * @return The largest <code>h</code> with <code>2^h | Phi</code> if * <code>Phi!=0</code> else return <code>-1</code> */ private int heightOfPhi(int Phi) { if (Phi == 0) { return -1; } int Tau = 0; int modul = 1; while (Phi % modul == 0) { modul *= 2; Tau += 1; } return Tau - 1; } /** * Updates the authentication path and root calculation for the tree after * next (AUTH++, ROOT++) in layer <code>layer</code> * * @param layer */ private void updateNextNextAuthRoot(int layer) { byte[] OTSseed = new byte[mdLength]; OTSseed = gmssRandom.nextSeed(nextNextSeeds[layer - 1]); // get the necessary leaf if (layer == numLayer - 1) { // lowest layer computes the necessary // leaf completely at this time WinternitzOTSignature ots = new WinternitzOTSignature(OTSseed, digestProvider.get(), otsIndex[layer]); this.nextNextRoot[layer - 1].update(nextNextSeeds[layer - 1], ots .getPublicKey()); } else { // other layers use the precomputed leafs in nextNextLeaf this.nextNextRoot[layer - 1].update(nextNextSeeds[layer - 1], nextNextLeaf[layer - 1].getLeaf()); this.nextNextLeaf[layer - 1].initLeafCalc(nextNextSeeds[layer - 1]); } } public int[] getIndex() { return index; } /** * @return The current index of layer i */ public int getIndex(int i) { return index[i]; } public byte[][] getCurrentSeeds() { return Arrays.clone(currentSeeds); } public byte[][][] getCurrentAuthPaths() { return Arrays.clone(currentAuthPaths); } /** * @return The one-time signature of the root of the current subtree */ public byte[] getSubtreeRootSig(int i) { return currentRootSig[i]; } public GMSSDigestProvider getName() { return digestProvider; } /** * @return The number of leafs of each tree of layer i */ public int getNumLeafs(int i) { return numLeafs[i]; } }