package org.bouncycastle.pqc.crypto.rainbow;

import java.security.SecureRandom;

import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.CryptoServicesRegistrar;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.pqc.crypto.MessageSigner;
import org.bouncycastle.pqc.crypto.rainbow.util.ComputeInField;
import org.bouncycastle.pqc.crypto.rainbow.util.GF2Field;

It implements the sign and verify functions for the Rainbow Signature Scheme. Here the message, which has to be signed, is updated. The use of different hash functions is possible.

Detailed information about the signature and the verify-method is to be found in the paper of Jintai Ding, Dieter Schmidt: Rainbow, a New Multivariable Polynomial Signature Scheme. ACNS 2005: 164-175 (http://dx.doi.org/10.1007/11496137_12)

/** * It implements the sign and verify functions for the Rainbow Signature Scheme. * Here the message, which has to be signed, is updated. The use of * different hash functions is possible. * <p> * Detailed information about the signature and the verify-method is to be found * in the paper of Jintai Ding, Dieter Schmidt: Rainbow, a New Multivariable * Polynomial Signature Scheme. ACNS 2005: 164-175 * (http://dx.doi.org/10.1007/11496137_12) */
public class RainbowSigner implements MessageSigner { private static final int MAXITS = 65536; // Source of randomness private SecureRandom random; // The length of a document that can be signed with the privKey int signableDocumentLength; // Container for the oil and vinegar variables of all the layers private short[] x; private ComputeInField cf = new ComputeInField(); RainbowKeyParameters key; public void init(boolean forSigning, CipherParameters param) { if (forSigning) { if (param instanceof ParametersWithRandom) { ParametersWithRandom rParam = (ParametersWithRandom)param; this.random = rParam.getRandom(); this.key = (RainbowPrivateKeyParameters)rParam.getParameters(); } else { this.random = CryptoServicesRegistrar.getSecureRandom(); this.key = (RainbowPrivateKeyParameters)param; } } else { this.key = (RainbowPublicKeyParameters)param; } this.signableDocumentLength = this.key.getDocLength(); }
initial operations before solving the Linear equation system.
Params:
  • layer – the current layer for which a LES is to be solved.
  • msg – the message that should be signed.
Returns:Y_ the modified document needed for solving LES, (Y_ = A1^{-1}*(Y-b1)) linear map L1 = A1 x + b1.
/** * initial operations before solving the Linear equation system. * * @param layer the current layer for which a LES is to be solved. * @param msg the message that should be signed. * @return Y_ the modified document needed for solving LES, (Y_ = * A1^{-1}*(Y-b1)) linear map L1 = A1 x + b1. */
private short[] initSign(Layer[] layer, short[] msg) { /* preparation: Modifies the document with the inverse of L1 */ // tmp = Y - b1: short[] tmpVec = new short[msg.length]; tmpVec = cf.addVect(((RainbowPrivateKeyParameters)this.key).getB1(), msg); // Y_ = A1^{-1} * (Y - b1) : short[] Y_ = cf.multiplyMatrix(((RainbowPrivateKeyParameters)this.key).getInvA1(), tmpVec); /* generates the vinegar vars of the first layer at random */ for (int i = 0; i < layer[0].getVi(); i++) { x[i] = (short)random.nextInt(); x[i] = (short)(x[i] & GF2Field.MASK); } return Y_; }
This function signs the message that has been updated, making use of the private key.

For computing the signature, L1 and L2 are needed, as well as LES should be solved for each layer in order to find the Oil-variables in the layer.

The Vinegar-variables of the first layer are random generated.

Params:
  • message – the message
Returns:the signature of the message.
/** * This function signs the message that has been updated, making use of the * private key. * <p> * For computing the signature, L1 and L2 are needed, as well as LES should * be solved for each layer in order to find the Oil-variables in the layer. * <p> * The Vinegar-variables of the first layer are random generated. * * @param message the message * @return the signature of the message. */
public byte[] generateSignature(byte[] message) { Layer[] layer = ((RainbowPrivateKeyParameters)this.key).getLayers(); int numberOfLayers = layer.length; x = new short[((RainbowPrivateKeyParameters)this.key).getInvA2().length]; // all variables short[] Y_; // modified document short[] y_i; // part of Y_ each polynomial int counter; // index of the current part of the doc short[] solVec; // the solution of LES pro layer short[] tmpVec; // the signature as an array of shorts: short[] signature; // the signature as a byte-array: byte[] S = new byte[layer[numberOfLayers - 1].getViNext()]; short[] msgHashVals = makeMessageRepresentative(message); int itCount = 0; // shows if an exception is caught boolean ok; do { ok = true; counter = 0; try { Y_ = initSign(layer, msgHashVals); for (int i = 0; i < numberOfLayers; i++) { y_i = new short[layer[i].getOi()]; solVec = new short[layer[i].getOi()]; // solution of LES /* copy oi elements of Y_ into y_i */ for (int k = 0; k < layer[i].getOi(); k++) { y_i[k] = Y_[counter]; counter++; // current index of Y_ } /* * plug in the vars of the previous layer in order to get * the vars of the current layer */ solVec = cf.solveEquation(layer[i].plugInVinegars(x), y_i); if (solVec == null) { // LES is not solveable throw new Exception("LES is not solveable!"); } /* copy the new vars into the x-array */ for (int j = 0; j < solVec.length; j++) { x[layer[i].getVi() + j] = solVec[j]; } } /* apply the inverse of L2: (signature = A2^{-1}*(b2+x)) */ tmpVec = cf.addVect(((RainbowPrivateKeyParameters)this.key).getB2(), x); signature = cf.multiplyMatrix(((RainbowPrivateKeyParameters)this.key).getInvA2(), tmpVec); /* cast signature from short[] to byte[] */ for (int i = 0; i < S.length; i++) { S[i] = ((byte)signature[i]); } } catch (Exception se) { // if one of the LESs was not solveable - sign again ok = false; } } while (!ok && ++itCount < MAXITS); /* return the signature in bytes */ if (itCount == MAXITS) { throw new IllegalStateException("unable to generate signature - LES not solvable"); } return S; }
This function verifies the signature of the message that has been updated, with the aid of the public key.
Params:
  • message – the message
  • signature – the signature of the message
Returns:true if the signature has been verified, false otherwise.
/** * 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 of the message * @return true if the signature has been verified, false otherwise. */
public boolean verifySignature(byte[] message, byte[] signature) { short[] sigInt = new short[signature.length]; short tmp; for (int i = 0; i < signature.length; i++) { tmp = (short)signature[i]; tmp &= (short)0xff; sigInt[i] = tmp; } short[] msgHashVal = makeMessageRepresentative(message); // verify short[] verificationResult = verifySignatureIntern(sigInt); // compare boolean verified = true; if (msgHashVal.length != verificationResult.length) { return false; } for (int i = 0; i < msgHashVal.length; i++) { verified = verified && msgHashVal[i] == verificationResult[i]; } return verified; }
Signature verification using public key
Params:
  • signature – vector of dimension n
Returns:document hash of length n - v1
/** * Signature verification using public key * * @param signature vector of dimension n * @return document hash of length n - v1 */
private short[] verifySignatureIntern(short[] signature) { short[][] coeff_quadratic = ((RainbowPublicKeyParameters)this.key).getCoeffQuadratic(); short[][] coeff_singular = ((RainbowPublicKeyParameters)this.key).getCoeffSingular(); short[] coeff_scalar = ((RainbowPublicKeyParameters)this.key).getCoeffScalar(); short[] rslt = new short[coeff_quadratic.length];// n - v1 int n = coeff_singular[0].length; int offset = 0; // array position short tmp = 0; // for scalar for (int p = 0; p < coeff_quadratic.length; p++) { // no of polynomials offset = 0; for (int x = 0; x < n; x++) { // calculate quadratic terms for (int y = x; y < n; y++) { tmp = GF2Field.multElem(coeff_quadratic[p][offset], GF2Field.multElem(signature[x], signature[y])); rslt[p] = GF2Field.addElem(rslt[p], tmp); offset++; } // calculate singular terms tmp = GF2Field.multElem(coeff_singular[p][x], signature[x]); rslt[p] = GF2Field.addElem(rslt[p], tmp); } // add scalar rslt[p] = GF2Field.addElem(rslt[p], coeff_scalar[p]); } return rslt; }
This function creates the representative of the message which gets signed or verified.
Params:
  • message – the message
Returns:message representative
/** * This function creates the representative of the message which gets signed * or verified. * * @param message the message * @return message representative */
private short[] makeMessageRepresentative(byte[] message) { // the message representative short[] output = new short[this.signableDocumentLength]; int h = 0; int i = 0; do { if (i >= message.length) { break; } output[i] = (short)message[h]; output[i] &= (short)0xff; h++; i++; } while (i < output.length); return output; } }