Coverage Report

Created: 2024-11-21 07:03

/src/SymCrypt/lib/modexp.c
Line
Count
Source (jump to first uncovered line)
1
//
2
// modexp.c   Modular exponentiation functions
3
//
4
// Copyright (c) Microsoft Corporation. Licensed under the MIT license.
5
//
6
7
#include "precomp.h"
8
9
//
10
// The windowed modular exponentiation algorithm works by generating a
11
// side-channel table of all the powers of the base from 0 up to 2^W - 1
12
// where W is the window size:
13
//      scsPrecomp = { 1, base, base^2, ..., base^(2^W-1) }
14
//
15
// TODO: To mitigate power analysis attacks when multiplying by 1 (which might
16
//       contain a lot of zeros in non-Montgomery moduli), future work is to
17
//       get rid of the 1 in the table. The leak is limited since now we always
18
//       have Montgomery moduli.
19
//
20
// Then it slices the exponent into chunks of W bits and goes through
21
// each chunk of the exponent starting from the most significant
22
// chunk. For each chunk c_i it squares a temporary modelement
23
// W times and then multiplies it by scsPrecomp[c_i]. The starting
24
// value of the temporary modelement is scsPrecomp[c_0] i.e. the one
25
// corresponding to the most significant chunk.
26
//
27
// Denote by M and SQ the multiplications and squarings and by B = nBitsExp
28
// number of bits of the exponent. Then the algorithm does
29
//      (2^W - 2)*M + (B-1)/W*(W*SQ + M) =
30
//      (2^W + (B-1)/W -2) multiplications and (B-1) squarings
31
//
32
// It is beneficial to change the window size from W to W+1 when
33
//      2^(W+1) + (B-1)/(W+1) < 2^W + (B-1)/W =>
34
//      B > 2^W*W(W+1)+1
35
// A simple table that calculates the optimal values for the window size
36
// is shown below.
37
//
38
// The minimum value of W is W=4 as 2^W should be a multiple
39
// of the groupsize of the scsTable, which is 4 by default.
40
41
0
#define MIN_WINDOW_SIZE     (4)
42
43
static const UINT32 cutoffs[] =
44
{
45
    // 5,      // W should be 2 for 5 < B <= 25
46
    // 25,     // W should be 3 for 25 < B <= 97
47
    // 97,     // W should be 4 for 97 < B <= 321
48
    321,    // W should be 5 for 321 < B <= 961
49
    // 961,    // W should be 6 for 961 < B
50
};
51
52
static const UINT32 nCuttoffs = sizeof(cutoffs) / sizeof(cutoffs[0]);
53
54
VOID
55
SYMCRYPT_CALL
56
SymCryptModExpWindowed(
57
    _In_                            PCSYMCRYPT_MODULUS      pmMod,
58
    _In_                            PCSYMCRYPT_MODELEMENT   peBase,
59
    _In_                            PCSYMCRYPT_INT          piExp,
60
                                    UINT32                  nBitsExp,
61
    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
62
    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
63
                                    SIZE_T                  cbScratch )
64
0
{
65
0
    UINT32 W = 0;
66
0
    UINT32 nTableElements = 0;
67
68
0
    SYMCRYPT_SCSTABLE scsPrecomp = { 0 };
69
0
    UINT32 cbScsPrecomp = 0;
70
71
0
    UINT32 cbModElement = SymCryptSizeofModElementFromModulus( pmMod );
72
73
0
    PSYMCRYPT_MODELEMENT peT1 = NULL;
74
0
    PSYMCRYPT_MODELEMENT peT2 = NULL;
75
76
0
    UINT32 nIterations = 0;
77
0
    UINT32 iBit = 0;
78
0
    UINT32 nBits = 0;
79
0
    UINT32 index = 0;
80
81
    // Truncate the nBitsExp if above the object size
82
0
    nBitsExp = SYMCRYPT_MIN( nBitsExp, SymCryptIntBitsizeOfObject(piExp) );
83
84
    // Calculate the window size
85
0
    W = MIN_WINDOW_SIZE;
86
0
    while ((W-MIN_WINDOW_SIZE < nCuttoffs) && (cutoffs[W-MIN_WINDOW_SIZE]<nBitsExp))
87
0
    {
88
0
        W++;
89
0
    }
90
0
    nTableElements = (1<<W);
91
92
    // Initialize the table of temporary modelements
93
0
    cbScsPrecomp = SymCryptScsTableInit( &scsPrecomp, nTableElements, cbModElement );
94
95
0
    SYMCRYPT_ASSERT( cbScratch >= cbScsPrecomp + 2*cbModElement + SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( pmMod->nDigits ) );
96
97
0
    SymCryptScsTableSetBuffer( &scsPrecomp, pbScratch, cbScsPrecomp );
98
0
    pbScratch += cbScsPrecomp;
99
0
    cbScratch -= cbScsPrecomp;
100
101
    // Create the temporary modelement
102
0
    peT1 = SymCryptModElementCreate( pbScratch, cbModElement, pmMod );
103
0
    SYMCRYPT_ASSERT( peT1 != NULL );
104
0
    pbScratch += cbModElement;
105
0
    cbScratch -= cbModElement;
106
0
    peT2 = SymCryptModElementCreate( pbScratch, cbModElement, pmMod );
107
0
    SYMCRYPT_ASSERT( peT2 != NULL );
108
0
    pbScratch += cbModElement;
109
0
    cbScratch -= cbModElement;
110
111
    // Fill the first element with 1 (**note: this will cause 0^0 = 1)
112
    // and the second with peBase
113
0
    SYMCRYPT_ASSERT( nTableElements >= 2 );
114
115
0
    SymCryptModElementSetValueUint32( 1, pmMod, peT1, pbScratch, cbScratch );
116
0
    SymCryptScsTableStore( &scsPrecomp, 0, (PBYTE)peT1, cbModElement );
117
118
0
    SymCryptModElementCopy( pmMod, peBase, peT1 );
119
0
    SymCryptScsTableStore( &scsPrecomp, 1, (PBYTE)peT1, cbModElement );
120
121
    // Fill the table with the powers of peBase
122
0
    for (UINT32 i=2; i<nTableElements; i++)
123
0
    {
124
        // TODO: Future improvement, use squarings for this table.
125
0
        SymCryptModMul( pmMod, peT1, peBase, peT1, pbScratch, cbScratch );
126
0
        SymCryptScsTableStore( &scsPrecomp, i, (PBYTE)peT1, cbModElement );
127
0
    }
128
129
    // Find the number of iterations (minus one) and the starting position bit
130
0
    SYMCRYPT_ASSERT( nBitsExp != 0 );
131
0
    nIterations = (nBitsExp - 1) / W;
132
0
    iBit = nIterations * W;
133
134
    // Do the first chunk (it might be smaller than W bits)
135
0
    nBits = nBitsExp - iBit;
136
0
    index = SymCryptIntGetBits( piExp, iBit, nBits );
137
0
    SymCryptScsTableLoad( &scsPrecomp, index, (PBYTE)peT1, cbModElement );
138
139
    // Work in batches of W bits in the exponent
140
0
    for (UINT32 i=0; i<nIterations; i++)
141
0
    {
142
        // Square W times
143
0
        for (UINT32 j=0; j<W; j++)
144
0
        {
145
0
            SymCryptModSquare( pmMod, peT1, peT1, pbScratch, cbScratch );
146
0
        }
147
148
0
        iBit -= W;
149
0
        index = SymCryptIntGetBits( piExp, iBit, W );
150
0
        SymCryptScsTableLoad( &scsPrecomp, index, (PBYTE)peT2, cbModElement );
151
152
0
        SymCryptModMul( pmMod, peT1, peT2, peT1, pbScratch, cbScratch );
153
0
    }
154
155
0
    SYMCRYPT_ASSERT( iBit == 0 );
156
157
0
    SymCryptModElementCopy( pmMod, peT1, peDst );
158
0
}
159
160
VOID
161
SYMCRYPT_CALL
162
SymCryptModExpSquareAndMultiply32(
163
    _In_                            PCSYMCRYPT_MODULUS      pmMod,
164
    _In_                            PCSYMCRYPT_MODELEMENT   peBase,
165
    _In_                            PCSYMCRYPT_INT          piExp,
166
    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
167
    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
168
                                    SIZE_T                  cbScratch )
169
0
{
170
0
    UINT32 cbModElement = SymCryptSizeofModElementFromModulus( pmMod );
171
172
0
    PSYMCRYPT_MODELEMENT peT1 = NULL;
173
0
    PSYMCRYPT_MODELEMENT peT2 = NULL;
174
175
    // The bits of the exponent when this function is called are
176
    // always less than 32.
177
0
    UINT32 exp = SymCryptIntGetValueLsbits32( piExp );
178
179
0
    SYMCRYPT_ASSERT( cbScratch >= 2*cbModElement + SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( pmMod->nDigits ) );
180
181
    // Create the temporary modelements
182
0
    peT1 = SymCryptModElementCreate( pbScratch, cbModElement, pmMod );
183
0
    SYMCRYPT_ASSERT( peT1 != NULL );
184
0
    pbScratch += cbModElement;
185
0
    cbScratch -= cbModElement;
186
0
    peT2 = SymCryptModElementCreate( pbScratch, cbModElement, pmMod );
187
0
    SYMCRYPT_ASSERT( peT2 != NULL );
188
0
    pbScratch += cbModElement;
189
0
    cbScratch -= cbModElement;
190
191
0
    if (exp == 0)
192
0
    {
193
0
        SymCryptModElementSetValueUint32( 1, pmMod, peDst, pbScratch, cbScratch );
194
0
    }
195
0
    else
196
0
    {
197
0
        SymCryptModElementSetValueUint32( 1, pmMod, peT1, pbScratch, cbScratch );
198
0
        SymCryptModElementCopy( pmMod, peBase, peT2 );
199
200
0
        while (exp>1)
201
0
        {
202
0
            if (exp%2 == 1)
203
0
            {
204
0
                SymCryptModMul( pmMod, peT1, peT2, peT1, pbScratch, cbScratch );
205
0
            }
206
207
0
            SymCryptModSquare( pmMod, peT2, peT2, pbScratch, cbScratch );
208
0
            exp /= 2;
209
0
        }
210
211
0
        SymCryptModMul( pmMod, peT1, peT2, peDst, pbScratch, cbScratch );
212
0
    }
213
0
}
214
215
VOID
216
SYMCRYPT_CALL
217
SymCryptModExpGeneric(
218
    _In_                            PCSYMCRYPT_MODULUS      pmMod,
219
    _In_                            PCSYMCRYPT_MODELEMENT   peBase,
220
    _In_                            PCSYMCRYPT_INT          piExp,
221
                                    UINT32                  nBitsExp,
222
                                    UINT32                  flags,
223
    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
224
    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
225
                                    SIZE_T                  cbScratch )
226
0
{
227
0
    if ( ((flags & SYMCRYPT_FLAG_DATA_PUBLIC)!=0) && (nBitsExp <= sizeof(UINT32)*8) )
228
0
    {
229
0
        SymCryptModExpSquareAndMultiply32( pmMod, peBase, piExp, peDst, pbScratch, cbScratch );
230
0
    }
231
0
    else
232
0
    {
233
0
        SymCryptModExpWindowed( pmMod, peBase, piExp, nBitsExp, peDst, pbScratch, cbScratch );  // This is the default
234
0
    }
235
0
}
236
237
//
238
// MultiExponentiation
239
//
240
241
// SYMCRYPT_MODMULTIEXP_MAX_NPRECOMP: The maximum number of precomputed powers of the
242
// base point allowed for the multi-exponentiation operation.
243
//  It should be equal to 2^(SYMCRYPT_FDEF_MAX_WINDOW_MODEXP-1)
244
#define SYMCRYPT_MODMULTIEXP_MAX_NPRECOMP       (1<<(SYMCRYPT_FDEF_MAX_WINDOW_MODEXP-1))
245
246
// SYMCRYPT_MODMULTIEXP_WINDOW_SIZE: Fixed window size for the WnafWithInterleaving
247
// implementation. It is found to give the faster running times for sizes
248
// 512 - 2048 bits.
249
0
#define SYMCRYPT_MODMULTIEXP_WINDOW_SIZE        (5)
250
251
C_ASSERT( (1 << (SYMCRYPT_MODMULTIEXP_WINDOW_SIZE-1)) <= SYMCRYPT_MODMULTIEXP_MAX_NPRECOMP );
252
253
//
254
// The following function fills the table with odd powers
255
// of the base point B.
256
//
257
// The first value must be filled by the caller.
258
VOID
259
SYMCRYPT_CALL
260
SymCryptModExpPrecomputation(
261
    _In_    PCSYMCRYPT_MODULUS      pmP,
262
            UINT32                  nPrecomputedPowers,
263
    _In_reads_( SYMCRYPT_MODMULTIEXP_MAX_NPRECOMP )
264
            PSYMCRYPT_MODELEMENT *  pePIs,
265
            PSYMCRYPT_MODELEMENT    peTemp,
266
    _Out_writes_bytes_opt_( cbScratch )
267
            PBYTE               pbScratch,
268
            SIZE_T              cbScratch
269
)
270
0
{
271
0
    SYMCRYPT_ASSERT(nPrecomputedPowers>=2);
272
273
    // Calculate B^2
274
0
    SymCryptModSquare( pmP, pePIs[0], peTemp, pbScratch, cbScratch );
275
276
0
    for (UINT32 i=1; i<nPrecomputedPowers; i++)
277
0
    {
278
        // B[i] = B^2*B[i-1]
279
0
        SymCryptModMul( pmP, peTemp, pePIs[i-1], pePIs[i], pbScratch, cbScratch );
280
0
    }
281
0
}
282
283
//
284
// The following is a similar algorithm to SymCryptEcpointMultiScalarMulWnafWithInterleaving.
285
// It is a NON SIDE-CHANNEL SAFE algorithm.
286
//
287
VOID
288
SYMCRYPT_CALL
289
SymCryptModMultiExpWnafWithInterleaving(
290
    _In_                            PCSYMCRYPT_MODULUS      pmMod,
291
    _In_reads_( nBases )            PCSYMCRYPT_MODELEMENT * peBaseArray,
292
    _In_reads_( nBases )            PCSYMCRYPT_INT *        piExpArray,
293
                                    UINT32                  nBases,
294
                                    UINT32                  nBitsExp,
295
    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
296
    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
297
                                    SIZE_T                  cbScratch )
298
0
{
299
0
    UINT32  i, j;
300
301
0
    UINT32  w = 0;
302
0
    UINT32  nPrecompPoints = 0;
303
0
    UINT32  nRecodedDigits = 0;
304
305
    // Masks
306
0
    UINT32  fOne[SYMCRYPT_MODMULTIEXP_MAX_NBASES] = { 0 };
307
0
    UINT32  fOneTot = 0xffffffff;       // Final result 1
308
309
0
    UINT32  fZeroExp = 0;               // Zero exponent
310
0
    UINT32  fZeroTot = 0;               // Final result 0
311
312
0
    UINT32 cbModElement = SymCryptSizeofModElementFromModulus( pmMod );
313
314
    // ====================================================
315
    // Temporaries
316
0
    PSYMCRYPT_MODELEMENT    pePIs[SYMCRYPT_MODMULTIEXP_MAX_NBASES*SYMCRYPT_MODMULTIEXP_MAX_NPRECOMP] = { 0 };
317
0
    PSYMCRYPT_MODELEMENT    peTemp = NULL;
318
0
    PSYMCRYPT_MODELEMENT    peOne = NULL;
319
320
0
    PUINT32                 absofKIs = NULL;
321
    // ===================================================
322
323
    // Calculate the window size
324
0
    w = SYMCRYPT_MODMULTIEXP_WINDOW_SIZE;
325
0
    nPrecompPoints = (1 << (w-1));          // We only store odd powers of the base point
326
327
    // Number of recoded digits
328
0
    nRecodedDigits = nBitsExp;
329
330
    //
331
    // From symcrypt_internal.h we have:
332
    //      - sizeof results are upper bounded by 2^19
333
    //      - SYMCRYPT_SCRATCH_BYTES results are upper bounded by 2^27 (including RSA and ECURVE)
334
    //      - nBases, nPrecompPoints, and nRecodedDigits are bounded by SYMCRYPT_MODMULTIEXP_MAX_NBASES,
335
    //        SYMCRYPT_MODMULTIEXP_MAX_NBITSEXP, and SYMCRYPT_MODMULTIEXP_MAX_NPRECOMP, repspectively.
336
    // Thus the following calculation does not overflow cbScratch.
337
    //
338
0
    SYMCRYPT_ASSERT( SYMCRYPT_MODMULTIEXP_MAX_NBASES >= nBases );
339
0
    SYMCRYPT_ASSERT( SYMCRYPT_MODMULTIEXP_MAX_NPRECOMP >= nPrecompPoints );
340
341
    // Creating temporary precomputed modelements
342
0
    for (i=0; i<nBases*nPrecompPoints; i++)
343
0
    {
344
0
        SYMCRYPT_ASSERT( cbScratch >= cbModElement );
345
0
        pePIs[i] = SymCryptModElementCreate( pbScratch, cbModElement, pmMod );
346
0
        SYMCRYPT_ASSERT( pePIs[i] != NULL );
347
0
        pbScratch += cbModElement;
348
0
        cbScratch -= cbModElement;
349
0
    }
350
351
0
    SYMCRYPT_ASSERT( cbScratch >=
352
0
            2*cbModElement +
353
0
            ((nBases*nRecodedDigits*sizeof(UINT32) + SYMCRYPT_ASYM_ALIGN_VALUE - 1)/SYMCRYPT_ASYM_ALIGN_VALUE)*SYMCRYPT_ASYM_ALIGN_VALUE +
354
0
            SYMCRYPT_SCRATCH_BYTES_FOR_COMMON_MOD_OPERATIONS( SymCryptModulusDigitsizeOfObject( pmMod ) ) );
355
356
    // Creating temporary points
357
0
    peTemp = SymCryptModElementCreate( pbScratch, cbModElement, pmMod );
358
0
    SYMCRYPT_ASSERT( peTemp != NULL );
359
0
    pbScratch += cbModElement;
360
0
    cbScratch -= cbModElement;
361
362
0
    peOne = SymCryptModElementCreate( pbScratch, cbModElement, pmMod );
363
0
    SYMCRYPT_ASSERT( peOne != NULL );
364
0
    pbScratch += cbModElement;
365
0
    cbScratch -= cbModElement;
366
367
    // Fixing pointers to recoded digits (be careful that the remaining space is SYMCRYPT_ASYM_ALIGNed)
368
0
    absofKIs = (PUINT32) pbScratch;
369
0
    pbScratch += nBases * nRecodedDigits * sizeof(UINT32);
370
0
    cbScratch -= nBases * nRecodedDigits * sizeof(UINT32);
371
372
    // Update cbScratch first using pbScratch, as the amount of scratch skipped for alignment depends upon the alignment of pbScratch
373
0
    cbScratch -=        ( ((ULONG_PTR)pbScratch + SYMCRYPT_ASYM_ALIGN_VALUE - 1) & ~(SYMCRYPT_ASYM_ALIGN_VALUE - 1) ) - (ULONG_PTR)pbScratch;
374
0
    pbScratch = (PBYTE) ( ((ULONG_PTR)pbScratch + SYMCRYPT_ASYM_ALIGN_VALUE - 1) & ~(SYMCRYPT_ASYM_ALIGN_VALUE - 1) );
375
376
377
    //
378
    // Main algorithm
379
    //
380
381
    // Set peOne to 1
382
0
    SymCryptModElementSetValueUint32( 1, pmMod, peOne, pbScratch, cbScratch );
383
384
    // Zero-out all recoded digits
385
0
    SymCryptWipe( (PBYTE)absofKIs, nBases*nRecodedDigits*sizeof(UINT32) );
386
387
0
    for (j = 0; j<nBases; j++)
388
0
    {
389
        // Check if the exponent is zero
390
0
        fZeroExp = SymCryptIntIsEqualUint32( piExpArray[j], 0 );
391
392
        // Check if the result is 0 (i.e. 0^e with e!=0)
393
0
        if( !fZeroExp && SymCryptModElementIsZero(pmMod, peBaseArray[j]) )
394
0
        {
395
0
            fZeroTot = 0xffffffff;
396
0
            break;
397
0
        }
398
399
        // Check if the exponent is 0 or if the base point is 1
400
0
        fOne[j] = ( fZeroExp | SymCryptModElementIsEqual( pmMod, peBaseArray[j], peOne ) );
401
0
        fOneTot &= fOne[j];
402
403
        // Skip the recoding stage (and all remaining steps) if this point will give result 1
404
0
        if (!fOne[j])
405
0
        {
406
            // Recoding stage
407
0
            SymCryptPositiveWidthNafRecoding( w, piExpArray[j], nBitsExp, &absofKIs[j*nRecodedDigits], nRecodedDigits );
408
409
            // Copy the base in the start of the pePIs array
410
0
            SymCryptModElementCopy( pmMod, peBaseArray[j], pePIs[j*nPrecompPoints] );
411
412
            // Precomputation stage
413
0
            SymCryptModExpPrecomputation( pmMod, nPrecompPoints, &pePIs[j*nPrecompPoints], peTemp, pbScratch, cbScratch );
414
0
        }
415
0
    }
416
417
0
    if (fZeroTot)
418
0
    {
419
0
        SymCryptModElementSetValueUint32( 0, pmMod, peDst, pbScratch, cbScratch );
420
0
    }
421
0
    else
422
0
    {
423
0
        SymCryptModElementSetValueUint32( 1, pmMod, peTemp, pbScratch, cbScratch );
424
425
0
        if (!fOneTot)
426
0
        {
427
            // Main loop
428
0
            for (INT32 i = nRecodedDigits-1; i>-1; i--)
429
0
            {
430
0
                SymCryptModSquare( pmMod, peTemp, peTemp, pbScratch, cbScratch );
431
432
0
                for (j = 0; j<nBases; j++)
433
0
                {
434
0
                    if (absofKIs[j*nRecodedDigits + i] != 0)
435
0
                    {
436
0
                        SymCryptModMul( pmMod, peTemp, pePIs[j*nPrecompPoints + absofKIs[j*nRecodedDigits + i]/2], peTemp, pbScratch, cbScratch );
437
0
                    }
438
0
                }
439
0
            }
440
0
        }
441
442
        // Copy the result into the destination
443
0
        SymCryptModElementCopy( pmMod, peTemp, peDst );
444
0
    }
445
0
}
446
447
SYMCRYPT_ERROR
448
SYMCRYPT_CALL
449
SymCryptModMultiExpGeneric(
450
    _In_                            PCSYMCRYPT_MODULUS      pmMod,
451
    _In_reads_( nBases )            PCSYMCRYPT_MODELEMENT * peBaseArray,
452
    _In_reads_( nBases )            PCSYMCRYPT_INT *        piExpArray,
453
                                    UINT32                  nBases,
454
                                    UINT32                  nBitsExp,
455
                                    UINT32                  flags,
456
    _Out_                           PSYMCRYPT_MODELEMENT    peDst,
457
    _Out_writes_bytes_( cbScratch ) PBYTE                   pbScratch,
458
                                    SIZE_T                  cbScratch )
459
0
{
460
0
    SYMCRYPT_ERROR scError = SYMCRYPT_NO_ERROR;
461
462
0
    if ( (nBases > SYMCRYPT_MODMULTIEXP_MAX_NBASES) ||
463
0
         (nBitsExp > SYMCRYPT_MODMULTIEXP_MAX_NBITSEXP) )
464
0
    {
465
0
        scError = SYMCRYPT_INVALID_ARGUMENT;
466
0
        goto cleanup;
467
0
    }
468
469
0
    if ((flags & SYMCRYPT_FLAG_DATA_PUBLIC)!=0)
470
0
    {
471
0
        SymCryptModMultiExpWnafWithInterleaving( pmMod, peBaseArray, piExpArray, nBases, nBitsExp, peDst, pbScratch, cbScratch );
472
0
    }
473
0
    else
474
0
    {
475
0
        UINT32 cbModElement = 0;
476
0
        PSYMCRYPT_MODELEMENT peTemp = NULL;
477
0
        PSYMCRYPT_MODELEMENT peAcc = NULL;
478
479
        // Use two temporary modelements to store the results
480
        // *** Make sure that the scratch space is enough i.e. the scratch space of ModMultiExp is
481
        //  at least 2 modelements bigger than the scratch space of ModExp
482
0
        cbModElement = SymCryptSizeofModElementFromModulus( pmMod );
483
484
0
        SYMCRYPT_ASSERT( SYMCRYPT_SCRATCH_BYTES_FOR_MODEXP(SymCryptModulusDigitsizeOfObject(pmMod)) + 2*cbModElement <=
485
0
                         SYMCRYPT_SCRATCH_BYTES_FOR_MODMULTIEXP( SymCryptModulusDigitsizeOfObject(pmMod), nBases, nBitsExp ) );
486
0
        SYMCRYPT_ASSERT( cbScratch >= 2*cbModElement + SYMCRYPT_SCRATCH_BYTES_FOR_MODEXP(SymCryptModulusDigitsizeOfObject(pmMod)) );
487
488
0
        peTemp = SymCryptModElementCreate( pbScratch, cbModElement, pmMod );
489
0
        pbScratch += cbModElement; cbScratch -= cbModElement;
490
491
0
        peAcc = SymCryptModElementCreate( pbScratch, cbModElement, pmMod );
492
0
        pbScratch += cbModElement; cbScratch -= cbModElement;
493
494
        // Set peAcc to 1
495
0
        SymCryptModElementSetValueUint32( 1, pmMod, peAcc, pbScratch, cbScratch );
496
497
0
        for (UINT32 i=0; i<nBases; i++)
498
0
        {
499
0
            SymCryptModExpWindowed( pmMod, peBaseArray[i], piExpArray[i], nBitsExp, peTemp, pbScratch, cbScratch );
500
501
0
            SymCryptModMul( pmMod, peAcc, peTemp, peAcc, pbScratch, cbScratch );
502
0
        }
503
504
        // Copy the result into the destination
505
0
        SymCryptModElementCopy( pmMod, peAcc, peDst );
506
0
    }
507
508
0
cleanup:
509
0
    return scError;
510
0
}