blob: 6823ce3f1e0efa7abef6befbefc93f3928637604 [file] [log] [blame]
package org.bouncycastle.pqc.crypto.gmss;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.pqc.crypto.gmss.util.GMSSRandom;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.encoders.Hex;
/**
* This class implements the distributed computation of the public key of the
* Winternitz one-time signature scheme (OTSS). The class is used by the GMSS
* classes for calculation of upcoming leafs.
*/
public class GMSSLeaf
{
/**
* The hash function used by the OTS and the PRNG
*/
private Digest messDigestOTS;
/**
* The length of the message digest and private key
*/
private int mdsize, keysize;
/**
* The source of randomness for OTS private key generation
*/
private GMSSRandom gmssRandom;
/**
* Byte array for distributed computation of the upcoming leaf
*/
private byte[] leaf;
/**
* Byte array for storing the concatenated hashes of private key parts
*/
private byte[] concHashs;
/**
* indices for distributed computation
*/
private int i, j;
/**
* storing 2^w
*/
private int two_power_w;
/**
* Winternitz parameter w
*/
private int w;
/**
* the amount of distributed computation steps when updateLeaf is called
*/
private int steps;
/**
* the internal seed
*/
private byte[] seed;
/**
* the OTS privateKey parts
*/
byte[] privateKeyOTS;
/**
* This constructor regenerates a prior GMSSLeaf object
*
* @param digest an array of strings, containing the name of the used hash
* function and PRNG and the name of the corresponding
* provider
* @param otsIndex status bytes
* @param numLeafs status ints
*/
public GMSSLeaf(Digest digest, byte[][] otsIndex, int[] numLeafs)
{
this.i = numLeafs[0];
this.j = numLeafs[1];
this.steps = numLeafs[2];
this.w = numLeafs[3];
messDigestOTS = digest;
gmssRandom = new GMSSRandom(messDigestOTS);
// calulate keysize for private key and the help array
mdsize = messDigestOTS.getDigestSize();
int mdsizeBit = mdsize << 3;
int messagesize = (int)Math.ceil((double)(mdsizeBit) / (double)w);
int checksumsize = getLog((messagesize << w) + 1);
this.keysize = messagesize
+ (int)Math.ceil((double)checksumsize / (double)w);
this.two_power_w = 1 << w;
// calculate steps
// ((2^w)-1)*keysize + keysize + 1 / (2^h -1)
// initialize arrays
this.privateKeyOTS = otsIndex[0];
this.seed = otsIndex[1];
this.concHashs = otsIndex[2];
this.leaf = otsIndex[3];
}
/**
* The constructor precomputes some needed variables for distributed leaf
* calculation
*
* @param digest an array of strings, containing the digest of the used hash
* function and PRNG and the digest of the corresponding
* provider
* @param w the winterniz parameter of that tree the leaf is computed
* for
* @param numLeafs the number of leafs of the tree from where the distributed
* computation is called
*/
GMSSLeaf(Digest digest, int w, int numLeafs)
{
this.w = w;
messDigestOTS = digest;
gmssRandom = new GMSSRandom(messDigestOTS);
// calulate keysize for private key and the help array
mdsize = messDigestOTS.getDigestSize();
int mdsizeBit = mdsize << 3;
int messagesize = (int)Math.ceil((double)(mdsizeBit) / (double)w);
int checksumsize = getLog((messagesize << w) + 1);
this.keysize = messagesize
+ (int)Math.ceil((double)checksumsize / (double)w);
this.two_power_w = 1 << w;
// calculate steps
// ((2^w)-1)*keysize + keysize + 1 / (2^h -1)
this.steps = (int)Math
.ceil((double)(((1 << w) - 1) * keysize + 1 + keysize)
/ (double)(numLeafs));
// initialize arrays
this.seed = new byte[mdsize];
this.leaf = new byte[mdsize];
this.privateKeyOTS = new byte[mdsize];
this.concHashs = new byte[mdsize * keysize];
}
public GMSSLeaf(Digest digest, int w, int numLeafs, byte[] seed0)
{
this.w = w;
messDigestOTS = digest;
gmssRandom = new GMSSRandom(messDigestOTS);
// calulate keysize for private key and the help array
mdsize = messDigestOTS.getDigestSize();
int mdsizeBit = mdsize << 3;
int messagesize = (int)Math.ceil((double)(mdsizeBit) / (double)w);
int checksumsize = getLog((messagesize << w) + 1);
this.keysize = messagesize
+ (int)Math.ceil((double)checksumsize / (double)w);
this.two_power_w = 1 << w;
// calculate steps
// ((2^w)-1)*keysize + keysize + 1 / (2^h -1)
this.steps = (int)Math
.ceil((double)(((1 << w) - 1) * keysize + 1 + keysize)
/ (double)(numLeafs));
// initialize arrays
this.seed = new byte[mdsize];
this.leaf = new byte[mdsize];
this.privateKeyOTS = new byte[mdsize];
this.concHashs = new byte[mdsize * keysize];
initLeafCalc(seed0);
}
private GMSSLeaf(GMSSLeaf original)
{
this.messDigestOTS = original.messDigestOTS;
this.mdsize = original.mdsize;
this.keysize = original.keysize;
this.gmssRandom = original.gmssRandom;
this.leaf = Arrays.clone(original.leaf);
this.concHashs = Arrays.clone(original.concHashs);
this.i = original.i;
this.j = original.j;
this.two_power_w = original.two_power_w;
this.w = original.w;
this.steps = original.steps;
this.seed = Arrays.clone(original.seed);
this.privateKeyOTS = Arrays.clone(original.privateKeyOTS);
}
/**
* initialize the distributed leaf calculation reset i,j and compute OTSseed
* with seed0
*
* @param seed0 the starting seed
*/
// TODO: this really looks like it should be either always called from a constructor or nextLeaf.
void initLeafCalc(byte[] seed0)
{
this.i = 0;
this.j = 0;
byte[] dummy = new byte[mdsize];
System.arraycopy(seed0, 0, dummy, 0, seed.length);
this.seed = gmssRandom.nextSeed(dummy);
}
GMSSLeaf nextLeaf()
{
GMSSLeaf nextLeaf = new GMSSLeaf(this);
nextLeaf.updateLeafCalc();
return nextLeaf;
}
/**
* Processes <code>steps</code> steps of distributed leaf calculation
*
* @return true if leaf is completed, else false
*/
private void updateLeafCalc()
{
byte[] buf = new byte[messDigestOTS.getDigestSize()];
// steps times do
// TODO: this really needs to be looked at, the 10000 has been added as
// prior to this the leaf value always ended up as zeros.
for (int s = 0; s < steps + 10000; s++)
{
if (i == keysize && j == two_power_w - 1)
{ // [3] at last hash the
// concatenation
messDigestOTS.update(concHashs, 0, concHashs.length);
leaf = new byte[messDigestOTS.getDigestSize()];
messDigestOTS.doFinal(leaf, 0);
return;
}
else if (i == 0 || j == two_power_w - 1)
{ // [1] at the
// beginning and
// when [2] is
// finished: get the
// next private key
// part
i++;
j = 0;
// get next privKey part
this.privateKeyOTS = gmssRandom.nextSeed(seed);
}
else
{ // [2] hash the privKey part
messDigestOTS.update(privateKeyOTS, 0, privateKeyOTS.length);
privateKeyOTS = buf;
messDigestOTS.doFinal(privateKeyOTS, 0);
j++;
if (j == two_power_w - 1)
{ // after w hashes add to the
// concatenated array
System.arraycopy(privateKeyOTS, 0, concHashs, mdsize
* (i - 1), mdsize);
}
}
}
throw new IllegalStateException("unable to updateLeaf in steps: " + steps + " " + i + " " + j);
}
/**
* Returns the leaf value.
*
* @return the leaf value
*/
public byte[] getLeaf()
{
return Arrays.clone(leaf);
}
/**
* This method returns the least integer that is greater or equal to the
* logarithm to the base 2 of an integer <code>intValue</code>.
*
* @param intValue an integer
* @return The least integer greater or equal to the logarithm to the base 2
* of <code>intValue</code>
*/
private int getLog(int intValue)
{
int log = 1;
int i = 2;
while (i < intValue)
{
i <<= 1;
log++;
}
return log;
}
/**
* Returns the status byte array used by the GMSSPrivateKeyASN.1 class
*
* @return The status bytes
*/
public byte[][] getStatByte()
{
byte[][] statByte = new byte[4][];
statByte[0] = new byte[mdsize];
statByte[1] = new byte[mdsize];
statByte[2] = new byte[mdsize * keysize];
statByte[3] = new byte[mdsize];
statByte[0] = privateKeyOTS;
statByte[1] = seed;
statByte[2] = concHashs;
statByte[3] = leaf;
return statByte;
}
/**
* Returns the status int array used by the GMSSPrivateKeyASN.1 class
*
* @return The status ints
*/
public int[] getStatInt()
{
int[] statInt = new int[4];
statInt[0] = i;
statInt[1] = j;
statInt[2] = steps;
statInt[3] = w;
return statInt;
}
/**
* Returns a String representation of the main part of this element
*
* @return a String representation of the main part of this element
*/
public String toString()
{
String out = "";
for (int i = 0; i < 4; i++)
{
out = out + this.getStatInt()[i] + " ";
}
out = out + " " + this.mdsize + " " + this.keysize + " "
+ this.two_power_w + " ";
byte[][] temp = this.getStatByte();
for (int i = 0; i < 4; i++)
{
if (temp[i] != null)
{
out = out + new String(Hex.encode(temp[i])) + " ";
}
else
{
out = out + "null ";
}
}
return out;
}
}