// TODO public GMSSSigner(GMSSDigestProvider digest) { digestProvider = digest; messDigestTrees = digest.get(); messDigestOTS = messDigestTrees; mdLength = messDigestTrees.getDigestSize(); gmssRandom = new GMSSRandom(messDigestTrees); }
/** Initializes the signature algorithm for signing a message. */ private void initSign() { messDigestTrees.reset(); // set private key and take from it ots key, auth, tree and key // counter, rootSign GMSSPrivateKeyParameters gmssPrivateKey = (GMSSPrivateKeyParameters) key; if (gmssPrivateKey.isUsed()) { throw new IllegalStateException("Private key already used"); } // check if last signature has been generated if (gmssPrivateKey.getIndex(0) >= gmssPrivateKey.getNumLeafs(0)) { throw new IllegalStateException("No more signatures can be generated"); } // get Parameterset this.gmssPS = gmssPrivateKey.getParameters(); // get numLayer this.numLayer = gmssPS.getNumOfLayers(); // get OTS Instance of lowest layer byte[] seed = gmssPrivateKey.getCurrentSeeds()[numLayer - 1]; byte[] OTSSeed = new byte[mdLength]; byte[] dummy = new byte[mdLength]; System.arraycopy(seed, 0, dummy, 0, mdLength); OTSSeed = gmssRandom.nextSeed( dummy); // secureRandom.nextBytes(currentSeeds[currentSeeds.length-1]);secureRandom.nextBytes(OTSseed); this.ots = new WinternitzOTSignature( OTSSeed, digestProvider.get(), gmssPS.getWinternitzParameter()[numLayer - 1]); byte[][][] helpCurrentAuthPaths = gmssPrivateKey.getCurrentAuthPaths(); currentAuthPaths = new byte[numLayer][][]; // copy the main tree authentication path for (int j = 0; j < numLayer; j++) { currentAuthPaths[j] = new byte[helpCurrentAuthPaths[j].length][mdLength]; for (int i = 0; i < helpCurrentAuthPaths[j].length; i++) { System.arraycopy(helpCurrentAuthPaths[j][i], 0, currentAuthPaths[j][i], 0, mdLength); } } // copy index index = new int[numLayer]; System.arraycopy(gmssPrivateKey.getIndex(), 0, index, 0, numLayer); // copy subtreeRootSig byte[] helpSubtreeRootSig; subtreeRootSig = new byte[numLayer - 1][]; for (int i = 0; i < numLayer - 1; i++) { helpSubtreeRootSig = gmssPrivateKey.getSubtreeRootSig(i); subtreeRootSig[i] = new byte[helpSubtreeRootSig.length]; System.arraycopy(helpSubtreeRootSig, 0, subtreeRootSig[i], 0, helpSubtreeRootSig.length); } gmssPrivateKey.markUsed(); }
public GMSSRootCalc(int paramInt1, int paramInt2, GMSSDigestProvider paramGMSSDigestProvider) { this.bfm = paramInt1; this.bey = paramGMSSDigestProvider; this.bfu = paramGMSSDigestProvider.qH(); this.bez = this.bfu.iJ(); this.bfr = paramInt2; this.beQ = new int[paramInt1]; int i = this.bez; this.bfq = ((byte[][]) Array.newInstance(Byte.TYPE, new int[] {paramInt1, i})); this.bfp = new byte[this.bez]; this.bfo = new Vector[this.bfr - 1]; paramInt1 = 0; while (paramInt1 < paramInt2 - 1) { this.bfo[paramInt1] = new Vector(); paramInt1 += 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]); } }
/** * This function verifies the signature of the message that has been updated, with the aid of the * public key. * * @param message the message * @param signature the signature associated with the message * @return true if the signature has been verified, false otherwise. */ public boolean verifySignature(byte[] message, byte[] signature) { boolean success = false; // int halfSigLength = signature.length >>> 1; messDigestOTS.reset(); WinternitzOTSVerify otsVerify; int otsSigLength; byte[] help = message; byte[] otsSig; byte[] otsPublicKey; byte[][] authPath; byte[] dest; int nextEntry = 0; int index; // Verify signature // --- begin with message = 'message that was signed' // and then in each step message = subtree root for (int j = numLayer - 1; j >= 0; j--) { otsVerify = new WinternitzOTSVerify(digestProvider.get(), gmssPS.getWinternitzParameter()[j]); otsSigLength = otsVerify.getSignatureLength(); message = help; // get the subtree index index = gmssUtil.bytesToIntLittleEndian(signature, nextEntry); // 4 is the number of bytes in integer nextEntry += 4; // get one-time signature otsSig = new byte[otsSigLength]; System.arraycopy(signature, nextEntry, otsSig, 0, otsSigLength); nextEntry += otsSigLength; // compute public OTS key from the one-time signature otsPublicKey = otsVerify.Verify(message, otsSig); // test if OTSsignature is correct if (otsPublicKey == null) { System.err.println("OTS Public Key is null in GMSSSignature.verify"); return false; } // get authentication path from the signature authPath = new byte[gmssPS.getHeightOfTrees()[j]][mdLength]; for (int i = 0; i < authPath.length; i++) { System.arraycopy(signature, nextEntry, authPath[i], 0, mdLength); nextEntry = nextEntry + mdLength; } // compute the root of the subtree from the authentication path help = new byte[mdLength]; help = otsPublicKey; int count = 1 << authPath.length; count = count + index; for (int i = 0; i < authPath.length; i++) { dest = new byte[mdLength << 1]; if ((count % 2) == 0) { System.arraycopy(help, 0, dest, 0, mdLength); System.arraycopy(authPath[i], 0, dest, mdLength, mdLength); count = count / 2; } else { System.arraycopy(authPath[i], 0, dest, 0, mdLength); System.arraycopy(help, 0, dest, mdLength, help.length); count = (count - 1) / 2; } messDigestTrees.update(dest, 0, dest.length); help = new byte[messDigestTrees.getDigestSize()]; messDigestTrees.doFinal(help, 0); } } // now help contains the root of the maintree // test if help is equal to the GMSS public key if (Arrays.areEqual(pubKeyBytes, help)) { success = true; } return success; }
/** * 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); } }
/** * /** * * @param index tree indices * @param currentSeed seed for the generation of private OTS keys for the current subtrees (TREE) * @param nextNextSeed seed for the generation of private OTS keys for the subtrees after next * (TREE++) * @param currentAuthPath array of current authentication paths (AUTHPATH) * @param nextAuthPath array of next authentication paths (AUTHPATH+) * @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 * @param digestClass An array of strings, containing the name of the used hash function and the * name of the corresponding provider */ 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.nextNextLeaf[i].initLeafCalc(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.upperLeaf[i].initLeafCalc(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; } }