/** * 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; }
/** * /** * * @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; } }