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;
}
}