Welcome to mirror list, hosted at ThFree Co, Russian Federation.

RainbowSigner.java « rainbow « crypto « pqc « bouncycastle « org « java « main « src « core - gitlab.com/quite/humla-spongycastle.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 979e759ba53e0ce92d39784d8d9c8658aba010f9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
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;
    }
}