package org.bouncycastle.pqc.crypto.gmss;
import java.security.SecureRandom;
import java.util.Vector;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator;
import org.bouncycastle.crypto.CryptoServicesRegistrar;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.KeyGenerationParameters;
import org.bouncycastle.pqc.crypto.gmss.util.GMSSRandom;
import org.bouncycastle.pqc.crypto.gmss.util.WinternitzOTSVerify;
import org.bouncycastle.pqc.crypto.gmss.util.WinternitzOTSignature;
This class implements key pair generation of the generalized Merkle signature
scheme (GMSS).
See Also: - GMSSSigner
/**
* This class implements key pair generation of the generalized Merkle signature
* scheme (GMSS).
*
* @see GMSSSigner
*/
public class GMSSKeyPairGenerator
implements AsymmetricCipherKeyPairGenerator
{
The source of randomness for OTS private key generation
/**
* The source of randomness for OTS private key generation
*/
private GMSSRandom gmssRandom;
The hash function used for the construction of the authentication trees
/**
* The hash function used for the construction of the authentication trees
*/
private Digest messDigestTree;
An array of the seeds for the PRGN (for main tree, and all current
subtrees)
/**
* An array of the seeds for the PRGN (for main tree, and all current
* subtrees)
*/
private byte[][] currentSeeds;
An array of seeds for the PRGN (for all subtrees after next)
/**
* An array of seeds for the PRGN (for all subtrees after next)
*/
private byte[][] nextNextSeeds;
An array of the RootSignatures
/**
* An array of the RootSignatures
*/
private byte[][] currentRootSigs;
Class of hash function to use
/**
* Class of hash function to use
*/
private GMSSDigestProvider digestProvider;
The length of the seed for the PRNG
/**
* The length of the seed for the PRNG
*/
private int mdLength;
the number of Layers
/**
* the number of Layers
*/
private int numLayer;
Flag indicating if the class already has been initialized
/**
* Flag indicating if the class already has been initialized
*/
private boolean initialized = false;
Instance of GMSSParameterset
/**
* Instance of GMSSParameterset
*/
private GMSSParameters gmssPS;
An array of the heights of the authentication trees of each layer
/**
* 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
/**
* An array of the Winternitz parameter 'w' of each layer
*/
private int[] otsIndex;
The parameter K needed for the authentication path computation
/**
* The parameter K needed for the authentication path computation
*/
private int[] K;
private GMSSKeyGenerationParameters gmssParams;
The GMSS OID.
/**
* The GMSS OID.
*/
public static final String OID = "1.3.6.1.4.1.8301.3.1.3.3";
The standard constructor tries to generate the GMSS algorithm identifier
with the corresponding OID.
Params: - digestProvider – provider for digest implementations.
/**
* The standard constructor tries to generate the GMSS algorithm identifier
* with the corresponding OID.
*
* @param digestProvider provider for digest implementations.
*/
public GMSSKeyPairGenerator(GMSSDigestProvider digestProvider)
{
this.digestProvider = digestProvider;
messDigestTree = digestProvider.get();
// set mdLength
this.mdLength = messDigestTree.getDigestSize();
// construct randomizer
this.gmssRandom = new GMSSRandom(messDigestTree);
}
Generates the GMSS key pair. The public key is an instance of
JDKGMSSPublicKey, the private key is an instance of JDKGMSSPrivateKey.
Returns: Key pair containing a JDKGMSSPublicKey and a JDKGMSSPrivateKey
/**
* Generates the GMSS key pair. The public key is an instance of
* JDKGMSSPublicKey, the private key is an instance of JDKGMSSPrivateKey.
*
* @return Key pair containing a JDKGMSSPublicKey and a JDKGMSSPrivateKey
*/
private AsymmetricCipherKeyPair genKeyPair()
{
if (!initialized)
{
initializeDefault();
}
// initialize authenticationPaths and treehash instances
byte[][][] currentAuthPaths = new byte[numLayer][][];
byte[][][] nextAuthPaths = new byte[numLayer - 1][][];
Treehash[][] currentTreehash = new Treehash[numLayer][];
Treehash[][] nextTreehash = new Treehash[numLayer - 1][];
Vector[] currentStack = new Vector[numLayer];
Vector[] nextStack = new Vector[numLayer - 1];
Vector[][] currentRetain = new Vector[numLayer][];
Vector[][] nextRetain = new Vector[numLayer - 1][];
for (int i = 0; i < numLayer; i++)
{
currentAuthPaths[i] = new byte[heightOfTrees[i]][mdLength];
currentTreehash[i] = new Treehash[heightOfTrees[i] - K[i]];
if (i > 0)
{
nextAuthPaths[i - 1] = new byte[heightOfTrees[i]][mdLength];
nextTreehash[i - 1] = new Treehash[heightOfTrees[i] - K[i]];
}
currentStack[i] = new Vector();
if (i > 0)
{
nextStack[i - 1] = new Vector();
}
}
// initialize roots
byte[][] currentRoots = new byte[numLayer][mdLength];
byte[][] nextRoots = new byte[numLayer - 1][mdLength];
// initialize seeds
byte[][] seeds = new byte[numLayer][mdLength];
// initialize seeds[] by copying starting-seeds of first trees of each
// layer
for (int i = 0; i < numLayer; i++)
{
System.arraycopy(currentSeeds[i], 0, seeds[i], 0, mdLength);
}
// initialize rootSigs
currentRootSigs = new byte[numLayer - 1][mdLength];
// -------------------------
// -------------------------
// --- calculation of current authpaths and current rootsigs (AUTHPATHS,
// SIG)------
// from bottom up to the root
for (int h = numLayer - 1; h >= 0; h--)
{
GMSSRootCalc tree;
// on lowest layer no lower root is available, so just call
// the method with null as first parameter
if (h == numLayer - 1)
{
tree = this.generateCurrentAuthpathAndRoot(null, currentStack[h], seeds[h], h);
}
else
// otherwise call the method with the former computed root
// value
{
tree = this.generateCurrentAuthpathAndRoot(currentRoots[h + 1], currentStack[h], seeds[h], h);
}
// set initial values needed for the private key construction
for (int i = 0; i < heightOfTrees[h]; i++)
{
System.arraycopy(tree.getAuthPath()[i], 0, currentAuthPaths[h][i], 0, mdLength);
}
currentRetain[h] = tree.getRetain();
currentTreehash[h] = tree.getTreehash();
System.arraycopy(tree.getRoot(), 0, currentRoots[h], 0, mdLength);
}
// --- calculation of next authpaths and next roots (AUTHPATHS+, ROOTS+)
// ------
for (int h = numLayer - 2; h >= 0; h--)
{
GMSSRootCalc tree = this.generateNextAuthpathAndRoot(nextStack[h], seeds[h + 1], h + 1);
// set initial values needed for the private key construction
for (int i = 0; i < heightOfTrees[h + 1]; i++)
{
System.arraycopy(tree.getAuthPath()[i], 0, nextAuthPaths[h][i], 0, mdLength);
}
nextRetain[h] = tree.getRetain();
nextTreehash[h] = tree.getTreehash();
System.arraycopy(tree.getRoot(), 0, nextRoots[h], 0, mdLength);
// create seed for the Merkle tree after next (nextNextSeeds)
// SEEDs++
System.arraycopy(seeds[h + 1], 0, this.nextNextSeeds[h], 0, mdLength);
}
// ------------
// generate JDKGMSSPublicKey
GMSSPublicKeyParameters publicKey = new GMSSPublicKeyParameters(currentRoots[0], gmssPS);
// generate the JDKGMSSPrivateKey
GMSSPrivateKeyParameters privateKey = new GMSSPrivateKeyParameters(currentSeeds, nextNextSeeds, currentAuthPaths,
nextAuthPaths, currentTreehash, nextTreehash, currentStack, nextStack, currentRetain, nextRetain, nextRoots, currentRootSigs, gmssPS, digestProvider);
// return the KeyPair
return (new AsymmetricCipherKeyPair(publicKey, privateKey));
}
calculates the authpath for tree in layer h which starts with seed[h]
additionally computes the rootSignature of underlaying root
Params: - currentStack – stack used for the treehash instance created by this method
- lowerRoot – stores the root of the lower tree
- seed – starting seeds
- h – actual layer
/**
* calculates the authpath for tree in layer h which starts with seed[h]
* additionally computes the rootSignature of underlaying root
*
* @param currentStack stack used for the treehash instance created by this method
* @param lowerRoot stores the root of the lower tree
* @param seed starting seeds
* @param h actual layer
*/
private GMSSRootCalc generateCurrentAuthpathAndRoot(byte[] lowerRoot, Vector currentStack, byte[] seed, int h)
{
byte[] help = new byte[mdLength];
byte[] OTSseed = new byte[mdLength];
OTSseed = gmssRandom.nextSeed(seed);
WinternitzOTSignature ots;
// data structure that constructs the whole tree and stores
// the initial values for treehash, Auth and retain
GMSSRootCalc treeToConstruct = new GMSSRootCalc(this.heightOfTrees[h], this.K[h], digestProvider);
treeToConstruct.initialize(currentStack);
// generate the first leaf
if (h == numLayer - 1)
{
ots = new WinternitzOTSignature(OTSseed, digestProvider.get(), otsIndex[h]);
help = ots.getPublicKey();
}
else
{
// for all layers except the lowest, generate the signature of the
// underlying root
// and reuse this signature to compute the first leaf of acual layer
// more efficiently (by verifiing the signature)
ots = new WinternitzOTSignature(OTSseed, digestProvider.get(), otsIndex[h]);
currentRootSigs[h] = ots.getSignature(lowerRoot);
WinternitzOTSVerify otsver = new WinternitzOTSVerify(digestProvider.get(), otsIndex[h]);
help = otsver.Verify(lowerRoot, currentRootSigs[h]);
}
// update the tree with the first leaf
treeToConstruct.update(help);
int seedForTreehashIndex = 3;
int count = 0;
// update the tree 2^(H) - 1 times, from the second to the last leaf
for (int i = 1; i < (1 << this.heightOfTrees[h]); i++)
{
// initialize the seeds for the leaf generation with index 3 * 2^h
if (i == seedForTreehashIndex && count < this.heightOfTrees[h] - this.K[h])
{
treeToConstruct.initializeTreehashSeed(seed, count);
seedForTreehashIndex *= 2;
count++;
}
OTSseed = gmssRandom.nextSeed(seed);
ots = new WinternitzOTSignature(OTSseed, digestProvider.get(), otsIndex[h]);
treeToConstruct.update(ots.getPublicKey());
}
if (treeToConstruct.wasFinished())
{
return treeToConstruct;
}
System.err.println("Baum noch nicht fertig konstruiert!!!");
return null;
}
calculates the authpath and root for tree in layer h which starts with
seed[h]
Params: - nextStack – stack used for the treehash instance created by this method
- seed – starting seeds
- h – actual layer
/**
* calculates the authpath and root for tree in layer h which starts with
* seed[h]
*
* @param nextStack stack used for the treehash instance created by this method
* @param seed starting seeds
* @param h actual layer
*/
private GMSSRootCalc generateNextAuthpathAndRoot(Vector nextStack, byte[] seed, int h)
{
byte[] OTSseed = new byte[numLayer];
WinternitzOTSignature ots;
// data structure that constructs the whole tree and stores
// the initial values for treehash, Auth and retain
GMSSRootCalc treeToConstruct = new GMSSRootCalc(this.heightOfTrees[h], this.K[h], this.digestProvider);
treeToConstruct.initialize(nextStack);
int seedForTreehashIndex = 3;
int count = 0;
// update the tree 2^(H) times, from the first to the last leaf
for (int i = 0; i < (1 << this.heightOfTrees[h]); i++)
{
// initialize the seeds for the leaf generation with index 3 * 2^h
if (i == seedForTreehashIndex && count < this.heightOfTrees[h] - this.K[h])
{
treeToConstruct.initializeTreehashSeed(seed, count);
seedForTreehashIndex *= 2;
count++;
}
OTSseed = gmssRandom.nextSeed(seed);
ots = new WinternitzOTSignature(OTSseed, digestProvider.get(), otsIndex[h]);
treeToConstruct.update(ots.getPublicKey());
}
if (treeToConstruct.wasFinished())
{
return treeToConstruct;
}
System.err.println("N�chster Baum noch nicht fertig konstruiert!!!");
return null;
}
This method initializes the GMSS KeyPairGenerator using an integer value
keySize
as input. It provides a simple use of the GMSS for
testing demands.
A given keysize
of less than 10 creates an amount 2^10
signatures. A keySize between 10 and 20 creates 2^20 signatures. Given an
integer greater than 20 the key pair generator creates 2^40 signatures.
Params: - keySize – Assigns the parameters used for the GMSS signatures. There are
3 choices:
1. keysize <= 10: creates 2^10 signatures using the
parameterset
P = (2, (5, 5), (3, 3), (3, 3))
2. keysize > 10 and <= 20: creates 2^20 signatures using the
parameterset
P = (2, (10, 10), (5, 4), (2, 2))
3. keysize > 20: creates 2^40 signatures using the
parameterset
P = (2, (10, 10, 10, 10), (9, 9, 9, 3), (2, 2, 2, 2)) - secureRandom – not used by GMSS, the SHA1PRNG of the SUN Provider is always
used
/**
* This method initializes the GMSS KeyPairGenerator using an integer value
* <code>keySize</code> as input. It provides a simple use of the GMSS for
* testing demands.
* <p>
* A given <code>keysize</code> of less than 10 creates an amount 2^10
* signatures. A keySize between 10 and 20 creates 2^20 signatures. Given an
* integer greater than 20 the key pair generator creates 2^40 signatures.
*
* @param keySize Assigns the parameters used for the GMSS signatures. There are
* 3 choices:<br>
* 1. keysize <= 10: creates 2^10 signatures using the
* parameterset<br>
* P = (2, (5, 5), (3, 3), (3, 3))<br>
* 2. keysize > 10 and <= 20: creates 2^20 signatures using the
* parameterset<br>
* P = (2, (10, 10), (5, 4), (2, 2))<br>
* 3. keysize > 20: creates 2^40 signatures using the
* parameterset<br>
* P = (2, (10, 10, 10, 10), (9, 9, 9, 3), (2, 2, 2, 2))
* @param secureRandom not used by GMSS, the SHA1PRNG of the SUN Provider is always
* used
*/
public void initialize(int keySize, SecureRandom secureRandom)
{
KeyGenerationParameters kgp;
if (keySize <= 10)
{ // create 2^10 keys
int[] defh = {10};
int[] defw = {3};
int[] defk = {2};
// XXX sec random neede?
kgp = new GMSSKeyGenerationParameters(secureRandom, new GMSSParameters(defh.length, defh, defw, defk));
}
else if (keySize <= 20)
{ // create 2^20 keys
int[] defh = {10, 10};
int[] defw = {5, 4};
int[] defk = {2, 2};
kgp = new GMSSKeyGenerationParameters(secureRandom, new GMSSParameters(defh.length, defh, defw, defk));
}
else
{ // create 2^40 keys, keygen lasts around 80 seconds
int[] defh = {10, 10, 10, 10};
int[] defw = {9, 9, 9, 3};
int[] defk = {2, 2, 2, 2};
kgp = new GMSSKeyGenerationParameters(secureRandom, new GMSSParameters(defh.length, defh, defw, defk));
}
// call the initializer with the chosen parameters
this.initialize(kgp);
}
Initalizes the key pair generator using a parameter set as input
/**
* Initalizes the key pair generator using a parameter set as input
*/
public void initialize(KeyGenerationParameters param)
{
this.gmssParams = (GMSSKeyGenerationParameters)param;
// generate GMSSParameterset
this.gmssPS = new GMSSParameters(gmssParams.getParameters().getNumOfLayers(), gmssParams.getParameters().getHeightOfTrees(),
gmssParams.getParameters().getWinternitzParameter(), gmssParams.getParameters().getK());
this.numLayer = gmssPS.getNumOfLayers();
this.heightOfTrees = gmssPS.getHeightOfTrees();
this.otsIndex = gmssPS.getWinternitzParameter();
this.K = gmssPS.getK();
// seeds
this.currentSeeds = new byte[numLayer][mdLength];
this.nextNextSeeds = new byte[numLayer - 1][mdLength];
// construct SecureRandom for initial seed generation
SecureRandom secRan = CryptoServicesRegistrar.getSecureRandom();
// generation of initial seeds
for (int i = 0; i < numLayer; i++)
{
secRan.nextBytes(currentSeeds[i]);
gmssRandom.nextSeed(currentSeeds[i]);
}
this.initialized = true;
}
This method is called by generateKeyPair() in case that no other
initialization method has been called by the user
/**
* This method is called by generateKeyPair() in case that no other
* initialization method has been called by the user
*/
private void initializeDefault()
{
int[] defh = {10, 10, 10, 10};
int[] defw = {3, 3, 3, 3};
int[] defk = {2, 2, 2, 2};
KeyGenerationParameters kgp = new GMSSKeyGenerationParameters(CryptoServicesRegistrar.getSecureRandom(), new GMSSParameters(defh.length, defh, defw, defk));
this.initialize(kgp);
}
public void init(KeyGenerationParameters param)
{
this.initialize(param);
}
public AsymmetricCipherKeyPair generateKeyPair()
{
return genKeyPair();
}
}