diff options
Diffstat (limited to 'libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSSigner.java')
-rw-r--r-- | libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSSigner.java | 404 |
1 files changed, 404 insertions, 0 deletions
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSSigner.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSSigner.java new file mode 100644 index 000000000..0bb04fa0c --- /dev/null +++ b/libraries/spongycastle/core/src/main/java/org/spongycastle/pqc/crypto/gmss/GMSSSigner.java @@ -0,0 +1,404 @@ +package org.spongycastle.pqc.crypto.gmss; + +import java.security.SecureRandom; + +import org.spongycastle.crypto.CipherParameters; +import org.spongycastle.crypto.Digest; +import org.spongycastle.crypto.params.ParametersWithRandom; +import org.spongycastle.pqc.crypto.MessageSigner; +import org.spongycastle.pqc.crypto.gmss.util.GMSSRandom; +import org.spongycastle.pqc.crypto.gmss.util.GMSSUtil; +import org.spongycastle.pqc.crypto.gmss.util.WinternitzOTSVerify; +import org.spongycastle.pqc.crypto.gmss.util.WinternitzOTSignature; +import org.spongycastle.util.Arrays; + +/** + * This class implements the GMSS signature scheme. + */ +public class GMSSSigner + implements MessageSigner +{ + + /** + * Instance of GMSSParameterSpec + */ + //private GMSSParameterSpec gmssParameterSpec; + + /** + * Instance of GMSSUtilities + */ + private GMSSUtil gmssUtil = new GMSSUtil(); + + + /** + * The raw GMSS public key + */ + private byte[] pubKeyBytes; + + /** + * Hash function for the construction of the authentication trees + */ + private Digest messDigestTrees; + + /** + * The length of the hash function output + */ + private int mdLength; + + /** + * The number of tree layers + */ + private int numLayer; + + /** + * The hash function used by the OTS + */ + private Digest messDigestOTS; + + /** + * An instance of the Winternitz one-time signature + */ + private WinternitzOTSignature ots; + + /** + * Array of strings containing the name of the hash function used by the OTS + * and the corresponding provider name + */ + private GMSSDigestProvider digestProvider; + + /** + * The current main tree and subtree indices + */ + private int[] index; + + /** + * Array of the authentication paths for the current trees of all layers + */ + private byte[][][] currentAuthPaths; + + /** + * The one-time signature of the roots of the current subtrees + */ + private byte[][] subtreeRootSig; + + + /** + * The GMSSParameterset + */ + private GMSSParameters gmssPS; + + /** + * The PRNG + */ + private GMSSRandom gmssRandom; + + GMSSKeyParameters key; + + // XXX needed? Source of randomness + private SecureRandom random; + + + /** + * The standard constructor tries to generate the MerkleTree Algorithm + * identifier with the corresponding OID. + * + * @param digest the digest to use + */ + // TODO + public GMSSSigner(GMSSDigestProvider digest) + { + digestProvider = digest; + messDigestTrees = digest.get(); + messDigestOTS = messDigestTrees; + mdLength = messDigestTrees.getDigestSize(); + gmssRandom = new GMSSRandom(messDigestTrees); + } + + public void init(boolean forSigning, + CipherParameters param) + { + + if (forSigning) + { + if (param instanceof ParametersWithRandom) + { + ParametersWithRandom rParam = (ParametersWithRandom)param; + + // XXX random needed? + this.random = rParam.getRandom(); + this.key = (GMSSPrivateKeyParameters)rParam.getParameters(); + initSign(); + + } + else + { + + this.random = new SecureRandom(); + this.key = (GMSSPrivateKeyParameters)param; + initSign(); + } + } + else + { + this.key = (GMSSPublicKeyParameters)param; + initVerify(); + + } + + } + + + /** + * 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(); + } + + /** + * Signs a message. + * <p/> + * + * @return the signature. + */ + public byte[] generateSignature(byte[] message) + { + + byte[] otsSig = new byte[mdLength]; + byte[] authPathBytes; + byte[] indexBytes; + + otsSig = ots.getSignature(message); + + // get concatenated lowest layer tree authentication path + authPathBytes = gmssUtil.concatenateArray(currentAuthPaths[numLayer - 1]); + + // put lowest layer index into a byte array + indexBytes = gmssUtil.intToBytesLittleEndian(index[numLayer - 1]); + + // create first part of GMSS signature + byte[] gmssSigFirstPart = new byte[indexBytes.length + otsSig.length + authPathBytes.length]; + System.arraycopy(indexBytes, 0, gmssSigFirstPart, 0, indexBytes.length); + System.arraycopy(otsSig, 0, gmssSigFirstPart, indexBytes.length, otsSig.length); + System.arraycopy(authPathBytes, 0, gmssSigFirstPart, (indexBytes.length + otsSig.length), authPathBytes.length); + // --- end first part + + // --- next parts of the signature + // create initial array with length 0 for iteration + byte[] gmssSigNextPart = new byte[0]; + + for (int i = numLayer - 1 - 1; i >= 0; i--) + { + + // get concatenated next tree authentication path + authPathBytes = gmssUtil.concatenateArray(currentAuthPaths[i]); + + // put next tree index into a byte array + indexBytes = gmssUtil.intToBytesLittleEndian(index[i]); + + // create next part of GMSS signature + + // create help array and copy actual gmssSig into it + byte[] helpGmssSig = new byte[gmssSigNextPart.length]; + System.arraycopy(gmssSigNextPart, 0, helpGmssSig, 0, gmssSigNextPart.length); + // adjust length of gmssSigNextPart for adding next part + gmssSigNextPart = new byte[helpGmssSig.length + indexBytes.length + subtreeRootSig[i].length + authPathBytes.length]; + + // copy old data (help array) and new data in gmssSigNextPart + System.arraycopy(helpGmssSig, 0, gmssSigNextPart, 0, helpGmssSig.length); + System.arraycopy(indexBytes, 0, gmssSigNextPart, helpGmssSig.length, indexBytes.length); + System.arraycopy(subtreeRootSig[i], 0, gmssSigNextPart, (helpGmssSig.length + indexBytes.length), subtreeRootSig[i].length); + System.arraycopy(authPathBytes, 0, gmssSigNextPart, (helpGmssSig.length + indexBytes.length + subtreeRootSig[i].length), authPathBytes.length); + + } + // --- end next parts + + // concatenate the two parts of the GMSS signature + byte[] gmssSig = new byte[gmssSigFirstPart.length + gmssSigNextPart.length]; + System.arraycopy(gmssSigFirstPart, 0, gmssSig, 0, gmssSigFirstPart.length); + System.arraycopy(gmssSigNextPart, 0, gmssSig, gmssSigFirstPart.length, gmssSigNextPart.length); + + // return the GMSS signature + return gmssSig; + } + + /** + * Initializes the signature algorithm for verifying a signature. + */ + private void initVerify() + { + messDigestTrees.reset(); + + GMSSPublicKeyParameters gmssPublicKey = (GMSSPublicKeyParameters)key; + pubKeyBytes = gmssPublicKey.getPublicKey(); + gmssPS = gmssPublicKey.getParameters(); + // get numLayer + this.numLayer = gmssPS.getNumOfLayers(); + + + } + + /** + * 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; + } + + +}
\ No newline at end of file |