| 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 = Arrays.clone(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]; |
| } |
| } |