1 package org.bouncycastle.pqc.crypto.rainbow; 2 3 import java.security.SecureRandom; 4 5 import org.bouncycastle.crypto.CipherParameters; 6 import org.bouncycastle.crypto.CryptoServicesRegistrar; 7 import org.bouncycastle.crypto.params.ParametersWithRandom; 8 import org.bouncycastle.pqc.crypto.MessageSigner; 9 import org.bouncycastle.pqc.crypto.rainbow.util.ComputeInField; 10 import org.bouncycastle.pqc.crypto.rainbow.util.GF2Field; 11 12 /** 13 * It implements the sign and verify functions for the Rainbow Signature Scheme. 14 * Here the message, which has to be signed, is updated. The use of 15 * different hash functions is possible. 16 * <p> 17 * Detailed information about the signature and the verify-method is to be found 18 * in the paper of Jintai Ding, Dieter Schmidt: Rainbow, a New Multivariable 19 * Polynomial Signature Scheme. ACNS 2005: 164-175 20 * (https://dx.doi.org/10.1007/11496137_12) 21 */ 22 public class RainbowSigner 23 implements MessageSigner 24 { 25 private static final int MAXITS = 65536; 26 27 // Source of randomness 28 private SecureRandom random; 29 30 // The length of a document that can be signed with the privKey 31 int signableDocumentLength; 32 33 // Container for the oil and vinegar variables of all the layers 34 private short[] x; 35 36 private ComputeInField cf = new ComputeInField(); 37 38 RainbowKeyParameters key; 39 init(boolean forSigning, CipherParameters param)40 public void init(boolean forSigning, 41 CipherParameters param) 42 { 43 if (forSigning) 44 { 45 if (param instanceof ParametersWithRandom) 46 { 47 ParametersWithRandom rParam = (ParametersWithRandom)param; 48 49 this.random = rParam.getRandom(); 50 this.key = (RainbowPrivateKeyParameters)rParam.getParameters(); 51 52 } 53 else 54 { 55 56 this.random = CryptoServicesRegistrar.getSecureRandom(); 57 this.key = (RainbowPrivateKeyParameters)param; 58 } 59 } 60 else 61 { 62 this.key = (RainbowPublicKeyParameters)param; 63 } 64 65 this.signableDocumentLength = this.key.getDocLength(); 66 } 67 68 69 /** 70 * initial operations before solving the Linear equation system. 71 * 72 * @param layer the current layer for which a LES is to be solved. 73 * @param msg the message that should be signed. 74 * @return Y_ the modified document needed for solving LES, (Y_ = 75 * A1^{-1}*(Y-b1)) linear map L1 = A1 x + b1. 76 */ initSign(Layer[] layer, short[] msg)77 private short[] initSign(Layer[] layer, short[] msg) 78 { 79 80 /* preparation: Modifies the document with the inverse of L1 */ 81 // tmp = Y - b1: 82 short[] tmpVec = new short[msg.length]; 83 84 tmpVec = cf.addVect(((RainbowPrivateKeyParameters)this.key).getB1(), msg); 85 86 // Y_ = A1^{-1} * (Y - b1) : 87 short[] Y_ = cf.multiplyMatrix(((RainbowPrivateKeyParameters)this.key).getInvA1(), tmpVec); 88 89 /* generates the vinegar vars of the first layer at random */ 90 for (int i = 0; i < layer[0].getVi(); i++) 91 { 92 x[i] = (short)random.nextInt(); 93 x[i] = (short)(x[i] & GF2Field.MASK); 94 } 95 96 return Y_; 97 } 98 99 /** 100 * This function signs the message that has been updated, making use of the 101 * private key. 102 * <p> 103 * For computing the signature, L1 and L2 are needed, as well as LES should 104 * be solved for each layer in order to find the Oil-variables in the layer. 105 * <p> 106 * The Vinegar-variables of the first layer are random generated. 107 * 108 * @param message the message 109 * @return the signature of the message. 110 */ generateSignature(byte[] message)111 public byte[] generateSignature(byte[] message) 112 { 113 Layer[] layer = ((RainbowPrivateKeyParameters)this.key).getLayers(); 114 int numberOfLayers = layer.length; 115 116 x = new short[((RainbowPrivateKeyParameters)this.key).getInvA2().length]; // all variables 117 118 short[] Y_; // modified document 119 short[] y_i; // part of Y_ each polynomial 120 int counter; // index of the current part of the doc 121 122 short[] solVec; // the solution of LES pro layer 123 short[] tmpVec; 124 125 // the signature as an array of shorts: 126 short[] signature; 127 // the signature as a byte-array: 128 byte[] S = new byte[layer[numberOfLayers - 1].getViNext()]; 129 130 short[] msgHashVals = makeMessageRepresentative(message); 131 int itCount = 0; 132 133 // shows if an exception is caught 134 boolean ok; 135 do 136 { 137 ok = true; 138 counter = 0; 139 try 140 { 141 Y_ = initSign(layer, msgHashVals); 142 143 for (int i = 0; i < numberOfLayers; i++) 144 { 145 146 y_i = new short[layer[i].getOi()]; 147 solVec = new short[layer[i].getOi()]; // solution of LES 148 149 /* copy oi elements of Y_ into y_i */ 150 for (int k = 0; k < layer[i].getOi(); k++) 151 { 152 y_i[k] = Y_[counter]; 153 counter++; // current index of Y_ 154 } 155 156 /* 157 * plug in the vars of the previous layer in order to get 158 * the vars of the current layer 159 */ 160 solVec = cf.solveEquation(layer[i].plugInVinegars(x), y_i); 161 162 if (solVec == null) 163 { // LES is not solveable 164 throw new Exception("LES is not solveable!"); 165 } 166 167 /* copy the new vars into the x-array */ 168 for (int j = 0; j < solVec.length; j++) 169 { 170 x[layer[i].getVi() + j] = solVec[j]; 171 } 172 } 173 174 /* apply the inverse of L2: (signature = A2^{-1}*(b2+x)) */ 175 tmpVec = cf.addVect(((RainbowPrivateKeyParameters)this.key).getB2(), x); 176 signature = cf.multiplyMatrix(((RainbowPrivateKeyParameters)this.key).getInvA2(), tmpVec); 177 178 /* cast signature from short[] to byte[] */ 179 for (int i = 0; i < S.length; i++) 180 { 181 S[i] = ((byte)signature[i]); 182 } 183 } 184 catch (Exception se) 185 { 186 // if one of the LESs was not solveable - sign again 187 ok = false; 188 } 189 } 190 while (!ok && ++itCount < MAXITS); 191 /* return the signature in bytes */ 192 193 if (itCount == MAXITS) 194 { 195 throw new IllegalStateException("unable to generate signature - LES not solvable"); 196 } 197 198 return S; 199 } 200 201 /** 202 * This function verifies the signature of the message that has been 203 * updated, with the aid of the public key. 204 * 205 * @param message the message 206 * @param signature the signature of the message 207 * @return true if the signature has been verified, false otherwise. 208 */ verifySignature(byte[] message, byte[] signature)209 public boolean verifySignature(byte[] message, byte[] signature) 210 { 211 short[] sigInt = new short[signature.length]; 212 short tmp; 213 214 for (int i = 0; i < signature.length; i++) 215 { 216 tmp = (short)signature[i]; 217 tmp &= (short)0xff; 218 sigInt[i] = tmp; 219 } 220 221 short[] msgHashVal = makeMessageRepresentative(message); 222 223 // verify 224 short[] verificationResult = verifySignatureIntern(sigInt); 225 226 // compare 227 boolean verified = true; 228 if (msgHashVal.length != verificationResult.length) 229 { 230 return false; 231 } 232 for (int i = 0; i < msgHashVal.length; i++) 233 { 234 verified = verified && msgHashVal[i] == verificationResult[i]; 235 } 236 237 return verified; 238 } 239 240 /** 241 * Signature verification using public key 242 * 243 * @param signature vector of dimension n 244 * @return document hash of length n - v1 245 */ verifySignatureIntern(short[] signature)246 private short[] verifySignatureIntern(short[] signature) 247 { 248 249 short[][] coeff_quadratic = ((RainbowPublicKeyParameters)this.key).getCoeffQuadratic(); 250 short[][] coeff_singular = ((RainbowPublicKeyParameters)this.key).getCoeffSingular(); 251 short[] coeff_scalar = ((RainbowPublicKeyParameters)this.key).getCoeffScalar(); 252 253 short[] rslt = new short[coeff_quadratic.length];// n - v1 254 int n = coeff_singular[0].length; 255 int offset = 0; // array position 256 short tmp = 0; // for scalar 257 258 for (int p = 0; p < coeff_quadratic.length; p++) 259 { // no of polynomials 260 offset = 0; 261 for (int x = 0; x < n; x++) 262 { 263 // calculate quadratic terms 264 for (int y = x; y < n; y++) 265 { 266 tmp = GF2Field.multElem(coeff_quadratic[p][offset], 267 GF2Field.multElem(signature[x], signature[y])); 268 rslt[p] = GF2Field.addElem(rslt[p], tmp); 269 offset++; 270 } 271 // calculate singular terms 272 tmp = GF2Field.multElem(coeff_singular[p][x], signature[x]); 273 rslt[p] = GF2Field.addElem(rslt[p], tmp); 274 } 275 // add scalar 276 rslt[p] = GF2Field.addElem(rslt[p], coeff_scalar[p]); 277 } 278 279 return rslt; 280 } 281 282 /** 283 * This function creates the representative of the message which gets signed 284 * or verified. 285 * 286 * @param message the message 287 * @return message representative 288 */ makeMessageRepresentative(byte[] message)289 private short[] makeMessageRepresentative(byte[] message) 290 { 291 // the message representative 292 short[] output = new short[this.signableDocumentLength]; 293 294 int h = 0; 295 int i = 0; 296 do 297 { 298 if (i >= message.length) 299 { 300 break; 301 } 302 output[i] = (short)message[h]; 303 output[i] &= (short)0xff; 304 h++; 305 i++; 306 } 307 while (i < output.length); 308 309 return output; 310 } 311 } 312