blob: 979e759ba53e0ce92d39784d8d9c8658aba010f9 [file] [log] [blame]
package org.bouncycastle.pqc.crypto.rainbow;
import java.security.SecureRandom;
import org.bouncycastle.crypto.CipherParameters;
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.
* <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
{
// 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 = new SecureRandom();
this.key = (RainbowPrivateKeyParameters)param;
}
}
else
{
this.key = (RainbowPublicKeyParameters)param;
}
this.signableDocumentLength = this.key.getDocLength();
}
/**
* 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.
* <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);
// 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);
/* return the signature in bytes */
return S;
}
/**
* 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
*
* @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.
*
* @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;
}
}