| package org.bouncycastle.pqc.crypto.gmss; |
| |
| import org.bouncycastle.crypto.Digest; |
| import org.bouncycastle.pqc.crypto.gmss.util.GMSSRandom; |
| import org.bouncycastle.util.encoders.Hex; |
| |
| |
| /** |
| * This class implements the distributed signature generation of the Winternitz |
| * one-time signature scheme (OTSS), described in C.Dods, N.P. Smart, and M. |
| * Stam, "Hash Based Digital Signature Schemes", LNCS 3796, pages 96–115, |
| * 2005. The class is used by the GMSS classes. |
| */ |
| public class GMSSRootSig |
| { |
| |
| /** |
| * The hash function used by the OTS |
| */ |
| private Digest messDigestOTS; |
| |
| /** |
| * The length of the message digest and private key |
| */ |
| private int mdsize, keysize; |
| |
| /** |
| * The private key |
| */ |
| private byte[] privateKeyOTS; |
| |
| /** |
| * The message bytes |
| */ |
| private byte[] hash; |
| |
| /** |
| * The signature bytes |
| */ |
| private byte[] sign; |
| |
| /** |
| * The Winternitz parameter |
| */ |
| private int w; |
| |
| /** |
| * The source of randomness for OTS private key generation |
| */ |
| private GMSSRandom gmssRandom; |
| |
| /** |
| * Sizes of the message |
| */ |
| private int messagesize; |
| |
| /** |
| * Some precalculated values |
| */ |
| private int k; |
| |
| /** |
| * Some variables for storing the actual status of distributed signing |
| */ |
| private int r, test, counter, ii; |
| |
| /** |
| * variables for storing big numbers for the actual status of distributed |
| * signing |
| */ |
| private long test8, big8; |
| |
| /** |
| * The necessary steps of each updateSign() call |
| */ |
| private int steps; |
| |
| /** |
| * The checksum part |
| */ |
| private int checksum; |
| |
| /** |
| * The height of the tree |
| */ |
| private int height; |
| |
| /** |
| * The current intern OTSseed |
| */ |
| private byte[] seed; |
| |
| /** |
| * This constructor regenerates a prior GMSSRootSig object used by the |
| * GMSSPrivateKeyASN.1 class |
| * |
| * @param digest an array of strings, containing the digest of the used hash |
| * function, the digest of the PRGN and the names of the |
| * corresponding providers |
| * @param statByte status byte array |
| * @param statInt status int array |
| */ |
| public GMSSRootSig(Digest digest, byte[][] statByte, int[] statInt) |
| { |
| messDigestOTS = digest; |
| gmssRandom = new GMSSRandom(messDigestOTS); |
| |
| this.counter = statInt[0]; |
| this.test = statInt[1]; |
| this.ii = statInt[2]; |
| this.r = statInt[3]; |
| this.steps = statInt[4]; |
| this.keysize = statInt[5]; |
| this.height = statInt[6]; |
| this.w = statInt[7]; |
| this.checksum = statInt[8]; |
| |
| this.mdsize = messDigestOTS.getDigestSize(); |
| |
| this.k = (1 << w) - 1; |
| |
| int mdsizeBit = mdsize << 3; |
| this.messagesize = (int)Math.ceil((double)(mdsizeBit) / (double)w); |
| |
| this.privateKeyOTS = statByte[0]; |
| this.seed = statByte[1]; |
| this.hash = statByte[2]; |
| |
| this.sign = statByte[3]; |
| |
| this.test8 = ((statByte[4][0] & 0xff)) |
| | ((long)(statByte[4][1] & 0xff) << 8) |
| | ((long)(statByte[4][2] & 0xff) << 16) |
| | ((long)(statByte[4][3] & 0xff)) << 24 |
| | ((long)(statByte[4][4] & 0xff)) << 32 |
| | ((long)(statByte[4][5] & 0xff)) << 40 |
| | ((long)(statByte[4][6] & 0xff)) << 48 |
| | ((long)(statByte[4][7] & 0xff)) << 56; |
| |
| this.big8 = ((statByte[4][8] & 0xff)) |
| | ((long)(statByte[4][9] & 0xff) << 8) |
| | ((long)(statByte[4][10] & 0xff) << 16) |
| | ((long)(statByte[4][11] & 0xff)) << 24 |
| | ((long)(statByte[4][12] & 0xff)) << 32 |
| | ((long)(statByte[4][13] & 0xff)) << 40 |
| | ((long)(statByte[4][14] & 0xff)) << 48 |
| | ((long)(statByte[4][15] & 0xff)) << 56; |
| } |
| |
| /** |
| * The constructor generates the PRNG and initializes some variables |
| * |
| * @param digest an array of strings, containing the digest of the used hash |
| * function, the digest of the PRGN and the names of the |
| * corresponding providers |
| * @param w the winternitz parameter |
| * @param height the heigth of the tree |
| */ |
| public GMSSRootSig(Digest digest, int w, int height) |
| { |
| messDigestOTS = digest; |
| gmssRandom = new GMSSRandom(messDigestOTS); |
| |
| this.mdsize = messDigestOTS.getDigestSize(); |
| this.w = w; |
| this.height = height; |
| |
| this.k = (1 << w) - 1; |
| |
| int mdsizeBit = mdsize << 3; |
| this.messagesize = (int)Math.ceil((double)(mdsizeBit) / (double)w); |
| } |
| |
| /** |
| * This method initializes the distributed sigature calculation. Variables |
| * are reseted and necessary steps are calculated |
| * |
| * @param seed0 the initial OTSseed |
| * @param message the massage which will be signed |
| */ |
| public void initSign(byte[] seed0, byte[] message) |
| { |
| |
| // create hash of message m |
| this.hash = new byte[mdsize]; |
| messDigestOTS.update(message, 0, message.length); |
| this.hash = new byte[messDigestOTS.getDigestSize()]; |
| messDigestOTS.doFinal(this.hash, 0); |
| |
| // variables for calculation of steps |
| byte[] messPart = new byte[mdsize]; |
| System.arraycopy(hash, 0, messPart, 0, mdsize); |
| int checkPart = 0; |
| int sumH = 0; |
| int checksumsize = getLog((messagesize << w) + 1); |
| |
| // ------- calculation of necessary steps ------ |
| if (8 % w == 0) |
| { |
| int dt = 8 / w; |
| // message part |
| for (int a = 0; a < mdsize; a++) |
| { |
| // count necessary hashs in 'sumH' |
| for (int b = 0; b < dt; b++) |
| { |
| sumH += messPart[a] & k; |
| messPart[a] = (byte)(messPart[a] >>> w); |
| } |
| } |
| // checksum part |
| this.checksum = (messagesize << w) - sumH; |
| checkPart = checksum; |
| // count necessary hashs in 'sumH' |
| for (int b = 0; b < checksumsize; b += w) |
| { |
| sumH += checkPart & k; |
| checkPart >>>= w; |
| } |
| } // end if ( 8 % w == 0 ) |
| else if (w < 8) |
| { |
| long big8; |
| int ii = 0; |
| int dt = mdsize / w; |
| |
| // first d*w bytes of hash (main message part) |
| for (int i = 0; i < dt; i++) |
| { |
| big8 = 0; |
| for (int j = 0; j < w; j++) |
| { |
| big8 ^= (messPart[ii] & 0xff) << (j << 3); |
| ii++; |
| } |
| // count necessary hashs in 'sumH' |
| for (int j = 0; j < 8; j++) |
| { |
| sumH += (int)(big8 & k); |
| big8 >>>= w; |
| } |
| } |
| // rest of message part |
| dt = mdsize % w; |
| big8 = 0; |
| for (int j = 0; j < dt; j++) |
| { |
| big8 ^= (messPart[ii] & 0xff) << (j << 3); |
| ii++; |
| } |
| dt <<= 3; |
| // count necessary hashs in 'sumH' |
| for (int j = 0; j < dt; j += w) |
| { |
| sumH += (int)(big8 & k); |
| big8 >>>= w; |
| } |
| // checksum part |
| this.checksum = (messagesize << w) - sumH; |
| checkPart = checksum; |
| // count necessary hashs in 'sumH' |
| for (int i = 0; i < checksumsize; i += w) |
| { |
| sumH += checkPart & k; |
| checkPart >>>= w; |
| } |
| }// end if(w<8) |
| else if (w < 57) |
| { |
| long big8; |
| int r = 0; |
| int s, f, rest, ii; |
| |
| // first a*w bits of hash where a*w <= 8*mdsize < (a+1)*w (main |
| // message part) |
| while (r <= ((mdsize << 3) - w)) |
| { |
| s = r >>> 3; |
| rest = r % 8; |
| r += w; |
| f = (r + 7) >>> 3; |
| big8 = 0; |
| ii = 0; |
| for (int j = s; j < f; j++) |
| { |
| big8 ^= (messPart[j] & 0xff) << (ii << 3); |
| ii++; |
| } |
| big8 >>>= rest; |
| // count necessary hashs in 'sumH' |
| sumH += (big8 & k); |
| |
| } |
| // rest of message part |
| s = r >>> 3; |
| if (s < mdsize) |
| { |
| rest = r % 8; |
| big8 = 0; |
| ii = 0; |
| for (int j = s; j < mdsize; j++) |
| { |
| big8 ^= (messPart[j] & 0xff) << (ii << 3); |
| ii++; |
| } |
| |
| big8 >>>= rest; |
| // count necessary hashs in 'sumH' |
| sumH += (big8 & k); |
| } |
| // checksum part |
| this.checksum = (messagesize << w) - sumH; |
| checkPart = checksum; |
| // count necessary hashs in 'sumH' |
| for (int i = 0; i < checksumsize; i += w) |
| { |
| sumH += (checkPart & k); |
| checkPart >>>= w; |
| } |
| }// end if(w<57) |
| |
| // calculate keysize |
| this.keysize = messagesize |
| + (int)Math.ceil((double)checksumsize / (double)w); |
| |
| // calculate steps: 'keysize' times PRNG, 'sumH' times hashing, |
| // (1<<height)-1 updateSign() calls |
| this.steps = (int)Math.ceil((double)(keysize + sumH) |
| / (double)((1 << height))); |
| // ---------------------------- |
| |
| // reset variables |
| this.sign = new byte[keysize * mdsize]; |
| this.counter = 0; |
| this.test = 0; |
| this.ii = 0; |
| this.test8 = 0; |
| this.r = 0; |
| // define the private key messagesize |
| this.privateKeyOTS = new byte[mdsize]; |
| // copy the seed |
| this.seed = new byte[mdsize]; |
| System.arraycopy(seed0, 0, this.seed, 0, mdsize); |
| |
| } |
| |
| /** |
| * This Method performs <code>steps</code> steps of distributed signature |
| * calculaion |
| * |
| * @return true if signature is generated completly, else false |
| */ |
| public boolean updateSign() |
| { |
| // steps times do |
| |
| for (int s = 0; s < steps; s++) |
| { // do 'step' times |
| |
| if (counter < keysize) |
| { // generate the private key or perform |
| // the next hash |
| oneStep(); |
| } |
| if (counter == keysize) |
| {// finish |
| return true; |
| } |
| } |
| |
| return false; // leaf not finished yet |
| } |
| |
| /** |
| * @return The private OTS key |
| */ |
| public byte[] getSig() |
| { |
| |
| return sign; |
| } |
| |
| /** |
| * @return The one-time signature of the message, generated step by step |
| */ |
| private void oneStep() |
| { |
| // -------- if (8 % w == 0) ---------- |
| if (8 % w == 0) |
| { |
| if (test == 0) |
| { |
| // get current OTSprivateKey |
| this.privateKeyOTS = gmssRandom.nextSeed(seed); |
| // System.arraycopy(privateKeyOTS, 0, hlp, 0, mdsize); |
| |
| if (ii < mdsize) |
| { // for main message part |
| test = hash[ii] & k; |
| hash[ii] = (byte)(hash[ii] >>> w); |
| } |
| else |
| { // for checksum part |
| test = checksum & k; |
| checksum >>>= w; |
| } |
| } |
| else if (test > 0) |
| { // hash the private Key 'test' times (on |
| // time each step) |
| messDigestOTS.update(privateKeyOTS, 0, privateKeyOTS.length); |
| privateKeyOTS = new byte[messDigestOTS.getDigestSize()]; |
| messDigestOTS.doFinal(privateKeyOTS, 0); |
| test--; |
| } |
| if (test == 0) |
| { // if all hashes done copy result to siganture |
| // array |
| System.arraycopy(privateKeyOTS, 0, sign, counter * mdsize, |
| mdsize); |
| counter++; |
| |
| if (counter % (8 / w) == 0) |
| { // raise array index for main |
| // massage part |
| ii++; |
| } |
| } |
| |
| }// ----- end if (8 % w == 0) ----- |
| // ---------- if ( w < 8 ) ---------------- |
| else if (w < 8) |
| { |
| |
| if (test == 0) |
| { |
| if (counter % 8 == 0 && ii < mdsize) |
| { // after every 8th "add |
| // to signature"-step |
| big8 = 0; |
| if (counter < ((mdsize / w) << 3)) |
| {// main massage |
| // (generate w*8 Bits |
| // every time) part |
| for (int j = 0; j < w; j++) |
| { |
| big8 ^= (hash[ii] & 0xff) << (j << 3); |
| ii++; |
| } |
| } |
| else |
| { // rest of massage part (once) |
| for (int j = 0; j < mdsize % w; j++) |
| { |
| big8 ^= (hash[ii] & 0xff) << (j << 3); |
| ii++; |
| } |
| } |
| } |
| if (counter == messagesize) |
| { // checksum part (once) |
| big8 = checksum; |
| } |
| |
| test = (int)(big8 & k); |
| // generate current OTSprivateKey |
| this.privateKeyOTS = gmssRandom.nextSeed(seed); |
| // System.arraycopy(privateKeyOTS, 0, hlp, 0, mdsize); |
| |
| } |
| else if (test > 0) |
| { // hash the private Key 'test' times (on |
| // time each step) |
| messDigestOTS.update(privateKeyOTS, 0, privateKeyOTS.length); |
| privateKeyOTS = new byte[messDigestOTS.getDigestSize()]; |
| messDigestOTS.doFinal(privateKeyOTS, 0); |
| test--; |
| } |
| if (test == 0) |
| { // if all hashes done copy result to siganture |
| // array |
| System.arraycopy(privateKeyOTS, 0, sign, counter * mdsize, |
| mdsize); |
| big8 >>>= w; |
| counter++; |
| } |
| |
| }// ------- end if(w<8)-------------------------------- |
| // --------- if w < 57 ----------------------------- |
| else if (w < 57) |
| { |
| |
| if (test8 == 0) |
| { |
| int s, f, rest; |
| big8 = 0; |
| ii = 0; |
| rest = r % 8; |
| s = r >>> 3; |
| // --- message part--- |
| if (s < mdsize) |
| { |
| if (r <= ((mdsize << 3) - w)) |
| { // first message part |
| r += w; |
| f = (r + 7) >>> 3; |
| } |
| else |
| { // rest of message part (once) |
| f = mdsize; |
| r += w; |
| } |
| // generate long 'big8' with minimum w next bits of the |
| // message array |
| for (int i = s; i < f; i++) |
| { |
| big8 ^= (hash[i] & 0xff) << (ii << 3); |
| ii++; |
| } |
| // delete bits on the right side, which were used already by |
| // the last loop |
| big8 >>>= rest; |
| test8 = (big8 & k); |
| } |
| // --- checksum part |
| else |
| { |
| test8 = (checksum & k); |
| checksum >>>= w; |
| } |
| // generate current OTSprivateKey |
| this.privateKeyOTS = gmssRandom.nextSeed(seed); |
| // System.arraycopy(privateKeyOTS, 0, hlp, 0, mdsize); |
| |
| } |
| else if (test8 > 0) |
| { // hash the private Key 'test' times (on |
| // time each step) |
| messDigestOTS.update(privateKeyOTS, 0, privateKeyOTS.length); |
| privateKeyOTS = new byte[messDigestOTS.getDigestSize()]; |
| messDigestOTS.doFinal(privateKeyOTS, 0); |
| test8--; |
| } |
| if (test8 == 0) |
| { // if all hashes done copy result to siganture |
| // array |
| System.arraycopy(privateKeyOTS, 0, sign, counter * mdsize, |
| mdsize); |
| counter++; |
| } |
| |
| } |
| } |
| |
| /** |
| * 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> |
| */ |
| public int getLog(int intValue) |
| { |
| int log = 1; |
| int i = 2; |
| while (i < intValue) |
| { |
| i <<= 1; |
| log++; |
| } |
| return log; |
| } |
| |
| /** |
| * This method returns the status byte array |
| * |
| * @return statBytes |
| */ |
| public byte[][] getStatByte() |
| { |
| |
| byte[][] statByte = new byte[5][mdsize]; |
| statByte[0] = privateKeyOTS; |
| statByte[1] = seed; |
| statByte[2] = hash; |
| statByte[3] = sign; |
| statByte[4] = this.getStatLong(); |
| |
| return statByte; |
| } |
| |
| /** |
| * This method returns the status int array |
| * |
| * @return statInt |
| */ |
| public int[] getStatInt() |
| { |
| int[] statInt = new int[9]; |
| statInt[0] = counter; |
| statInt[1] = test; |
| statInt[2] = ii; |
| statInt[3] = r; |
| statInt[4] = steps; |
| statInt[5] = keysize; |
| statInt[6] = height; |
| statInt[7] = w; |
| statInt[8] = checksum; |
| return statInt; |
| } |
| |
| /** |
| * Converts the long parameters into byte arrays to store it in |
| * statByte-Array |
| */ |
| public byte[] getStatLong() |
| { |
| byte[] bytes = new byte[16]; |
| |
| bytes[0] = (byte)((test8) & 0xff); |
| bytes[1] = (byte)((test8 >> 8) & 0xff); |
| bytes[2] = (byte)((test8 >> 16) & 0xff); |
| bytes[3] = (byte)((test8 >> 24) & 0xff); |
| bytes[4] = (byte)((test8) >> 32 & 0xff); |
| bytes[5] = (byte)((test8 >> 40) & 0xff); |
| bytes[6] = (byte)((test8 >> 48) & 0xff); |
| bytes[7] = (byte)((test8 >> 56) & 0xff); |
| |
| bytes[8] = (byte)((big8) & 0xff); |
| bytes[9] = (byte)((big8 >> 8) & 0xff); |
| bytes[10] = (byte)((big8 >> 16) & 0xff); |
| bytes[11] = (byte)((big8 >> 24) & 0xff); |
| bytes[12] = (byte)((big8) >> 32 & 0xff); |
| bytes[13] = (byte)((big8 >> 40) & 0xff); |
| bytes[14] = (byte)((big8 >> 48) & 0xff); |
| bytes[15] = (byte)((big8 >> 56) & 0xff); |
| |
| return bytes; |
| } |
| |
| /** |
| * returns a string representation of the instance |
| * |
| * @return a string representation of the instance |
| */ |
| public String toString() |
| { |
| String out = "" + this.big8 + " "; |
| int[] statInt = new int[9]; |
| statInt = this.getStatInt(); |
| byte[][] statByte = new byte[5][mdsize]; |
| statByte = this.getStatByte(); |
| for (int i = 0; i < 9; i++) |
| { |
| out = out + statInt[i] + " "; |
| } |
| for (int i = 0; i < 5; i++) |
| { |
| out = out + new String(Hex.encode(statByte[i])) + " "; |
| } |
| |
| return out; |
| } |
| |
| } |