OpenCore  1.0.4
OpenCore Bootloader
Loading...
Searching...
No Matches
BigNumMontgomery.c
Go to the documentation of this file.
1
32#include "BigNumLibInternal.h"
33
45STATIC
48 IN CONST OC_BN_WORD *A
49 )
50{
51 OC_BN_WORD Dividend;
52 OC_BN_WORD Divisor;
53 OC_BN_WORD X;
54 OC_BN_WORD Y;
55 OC_BN_WORD Mod;
56 OC_BN_WORD Div;
57 OC_BN_WORD Tmp;
58
59 ASSERT (A != NULL);
60 //
61 // We cannot compute the Montgomery Inverse of 0.
62 //
63 if (A[0] == 0) {
64 return 0;
65 }
66
67 //
68 // The initial divisor 2^Bits(Word) obviously cannot be represented as a
69 // variable value. For this reason, the first two iterations of the loop are
70 // unrolled. 2^Bits(Word) is represented as 0 as those two values are
71 // congruent modulo 2^Bits(Word), which is the variable storage space.
72 // The modulo operation is therefor implemented as a subtraction loop.
73 //
74
75 //
76 // === INITIALISATION ===
77 //
78 // Divisor = 1U << sizeof(A->Words[0]) * OC_CHAR_BIT;
79 // Dividend = A->Words[0];
80 //
81 // Y = 1;
82 // X = 0;
83 //
84 // === LOOP UNROLL 1) ===
85 //
86 // Div = Dividend / Divisor; // => 0
87 // Mod = Dividend % Divisor; // => A->Words[0]
88 //
89 // Dividend = Divisor; // => 2^Bits(Word)
90 // Divisor = Mod; // => A->Words[0]
91 //
92 Dividend = 0;
93 Divisor = A[0];
94 //
95 // Tmp = Y - Div * X; // => 1
96 // Y = X; // => 0
97 // X = Tmp; // => 1
98 //
99 // === LOOP UNROLL 2) ===
100 //
101 // Div = Dividend / Divisor;
102 // Mod = Dividend % Divisor;
103 //
104 Div = 0;
105 do {
106 Dividend -= Divisor;
107 ++Div;
108 } while (Dividend >= Divisor);
109
110 Mod = Dividend;
111 //
112 // Dividend = Divisor;
113 // Divisor = Mod;
114 //
115 Dividend = Divisor;
116 Divisor = Mod;
117 //
118 // Tmp = Y - Div * X; // => -Div
119 // Y = X; // => 1
120 // X = Tmp; // => -Div
121 //
122 Y = 1;
123 X = (OC_BN_WORD)0U - Div;
124
125 while (Divisor != 0) {
126 //
127 // FIXME: This needs a good source for an algorithm specification.
128 //
129 Div = Dividend / Divisor;
130 Mod = Dividend % Divisor;
131
132 Dividend = Divisor;
133 Divisor = Mod;
134
135 Tmp = Y - Div * X;
136 Y = X;
137 X = Tmp;
138 }
139
140 //
141 // When the loop ends, Dividend contains the Greatest Common Divisor.
142 // If it is not 1, we cannot compute the Montgomery Inverse.
143 //
144 if (Dividend != 1) {
145 return 0;
146 }
147
148 //
149 // As per above, Y is 1 / A mod 2^#Bits(Word), so invert the result to yield
150 // -1 / A mod 2^#Bits(Word).
151 //
152 return (OC_BN_WORD)0U - Y;
153}
154
157 IN OUT OC_BN_WORD *RSqrMod,
158 IN OC_BN_NUM_WORDS NumWords,
159 IN CONST OC_BN_WORD *N,
160 IN OC_BN_WORD *Scratch
161 )
162{
163 OC_BN_WORD N0Inv;
164 OC_BN_NUM_BITS NumBits;
165 OC_BN_NUM_WORDS NumWordsRSqr;
166 OC_BN_WORD *RSqr;
167 OC_BN_WORD *Scratch2;
168
169 ASSERT (RSqrMod != NULL);
170 ASSERT (NumWords > 0);
171 ASSERT (NumWords <= OC_BN_MONT_MAX_LEN);
172 ASSERT (N != NULL);
173 //
174 // Calculate the Montgomery Inverse.
175 // This is a reduced approach of the algorithmic -1 / N mod 2^#Bits(N),
176 // where the modulus is reduced from 2^#Bits(N) to 2^#Bits(Word). This
177 // reduces N to N[0] and yields an inverse valid in the Word domain.
178 //
179 N0Inv = BigNumMontInverse (N);
180 if (N0Inv == 0) {
181 return 0;
182 }
183
184 //
185 // This cannot overflow because it holds that NumWords <= OC_BN_MONT_MAX_LEN.
186 //
187 NumWordsRSqr = OC_BN_MONT_RSQR_LEN (NumWords);
188
189 RSqr = &Scratch[0];
190 Scratch2 = &Scratch[NumWordsRSqr];
191
192 //
193 // Calculate Montgomery's R^2 mod N.
194 //
195 ZeroMem (RSqr, OC_BN_SIZE (NumWordsRSqr));
196 //
197 // Considering NumBits can be at most MAX_UINT16 * OC_CHAR_BIT, this cannot
198 // overflow.
199 //
200 NumBits = OC_BN_BITS (NumWords);
201 BigNumOrWord (RSqr, NumWordsRSqr, 1, 2 * NumBits);
202
203 BigNumMod (RSqrMod, NumWords, RSqr, NumWordsRSqr, N, Scratch2);
204
205 return N0Inv;
206}
207
219STATIC
222 OUT OC_BN_WORD *Hi,
223 IN OC_BN_WORD C,
224 IN OC_BN_WORD A,
225 IN OC_BN_WORD B
226 )
227{
228 OC_BN_WORD ResHi;
229 OC_BN_WORD ResLo;
230
231 ASSERT (Hi != NULL);
232
233 ResLo = BigNumWordMul (&ResHi, A, B) + C;
234 if (ResLo < C) {
235 ++ResHi;
236 }
237
238 *Hi = ResHi;
239 return ResLo;
240}
241
254STATIC
257 OUT OC_BN_WORD *Hi,
258 IN OC_BN_WORD C,
259 IN OC_BN_WORD A,
260 IN OC_BN_WORD B,
261 IN OC_BN_WORD Carry
262 )
263{
264 OC_BN_WORD MulResHi;
265 OC_BN_WORD MulResLo;
266
267 ASSERT (Hi != NULL);
268
269 MulResLo = BigNumWordAddMul (&MulResHi, C, A, B);
270 MulResLo += Carry;
271 if (MulResLo < Carry) {
272 ++MulResHi;
273 }
274
275 *Hi = MulResHi;
276 return MulResLo;
277}
278
290STATIC
291VOID
293 IN OUT OC_BN_WORD *Result,
294 IN OC_BN_NUM_WORDS NumWords,
295 IN OC_BN_WORD AWord,
296 IN CONST OC_BN_WORD *B,
297 IN CONST OC_BN_WORD *N,
298 IN OC_BN_WORD N0Inv
299 )
300{
301 UINTN CompIndex;
302
303 OC_BN_WORD CCurMulHi;
304 OC_BN_WORD CCurMulLo;
305 OC_BN_WORD CCurMontHi;
306 OC_BN_WORD CCurMontLo;
307 OC_BN_WORD TFirst;
308
309 ASSERT (Result != NULL);
310 ASSERT (NumWords > 0);
311 ASSERT (B != NULL);
312 ASSERT (N != NULL);
313 ASSERT (N0Inv != 0);
314 //
315 // Standard multiplication
316 // C = C + A*B
317 //
318 CCurMulLo = BigNumWordAddMul (
319 &CCurMulHi,
320 Result[0],
321 AWord,
322 B[0]
323 );
324 //
325 // Montgomery Reduction preparation
326 // As N_first is reduced mod 2^#Bits (word), we're operating in this domain
327 // and reduce both C and the result as well.
328 // t_first = C * N_first
329 //
330 TFirst = CCurMulLo * N0Inv;
331 //
332 // Montgomery Reduction
333 // 1. C = C + t_first * N
334 // 2. In the first step, only the carries are actually used, which implies
335 // division by R.
336 //
337 CCurMontLo = BigNumWordAddMul (&CCurMontHi, CCurMulLo, TFirst, N[0]);
338
339 for (CompIndex = 1; CompIndex < NumWords; ++CompIndex) {
340 //
341 // Standard multiplication
342 // C = C + A*B + carry
343 //
344 CCurMulLo = BigNumWordAddMulCarry (
345 &CCurMulHi,
346 Result[CompIndex],
347 AWord,
348 B[CompIndex],
349 CCurMulHi
350 );
351 //
352 // Montgomery Reduction
353 // 1. C = C + t_first * N + carry
354 //
355 CCurMontLo = BigNumWordAddMulCarry (
356 &CCurMontHi,
357 CCurMulLo,
358 TFirst,
359 N[CompIndex],
360 CCurMontHi
361 );
362 //
363 // 2. The index shift translates to a bitshift equivalent to the division
364 // by R = 2^#Bits (word).
365 // C = C / R
366 //
367 Result[CompIndex - 1] = CCurMontLo;
368 }
369
370 //
371 // Assign the most significant byte the remaining carrys.
372 //
373 CCurMulLo = CCurMulHi + CCurMontHi;
374 Result[CompIndex - 1] = CCurMulLo;
375 //
376 // If the result has wrapped around, C >= N is true and we reduce mod N.
377 //
378 if (CCurMulLo < CCurMulHi) {
379 //
380 // The discarded most significant word must be the last borrow of the
381 // subtraction, as C_actual = (CCurMul >> WORD_BITS)||C.
382 //
383 BigNumSub (Result, NumWords, Result, N);
384 }
385}
386
398STATIC
399VOID
401 IN OUT OC_BN_WORD *Result,
402 IN OC_BN_NUM_WORDS NumWords,
403 IN CONST OC_BN_WORD *A,
404 IN CONST OC_BN_WORD *B,
405 IN CONST OC_BN_WORD *N,
406 IN OC_BN_WORD N0Inv
407 )
408{
409 UINTN RowIndex;
410
411 ASSERT (Result != NULL);
412 ASSERT (NumWords > 0);
413 ASSERT (A != NULL);
414 ASSERT (B != NULL);
415 ASSERT (N != NULL);
416 ASSERT (N0Inv != 0);
417
418 ZeroMem (Result, OC_BN_SIZE (NumWords));
419 //
420 // RowIndex is used as an index into the words of A. Because this domain
421 // operates in mod 2^#Bits (word), 'row results' do not require multiplication
422 // as the positional factor is stripped by the word-size modulus.
423 //
424 for (RowIndex = 0; RowIndex < NumWords; ++RowIndex) {
425 BigNumMontMulRow (Result, NumWords, A[RowIndex], B, N, N0Inv);
426 }
427
428 //
429 // As this implementation only reduces mod N on overflow and not for every
430 // yes-instance of C >= N, any sequence of Montgomery Multiplications must be
431 // completed with a final reduction step.
432 //
433}
434
447STATIC
448VOID
450 IN OUT OC_BN_WORD *Result,
451 IN OC_BN_NUM_WORDS NumWords,
452 IN CONST OC_BN_WORD *N,
453 IN OC_BN_WORD N0Inv
454 )
455{
456 UINTN CompIndex;
457
458 OC_BN_WORD CCurMontHi;
459 OC_BN_WORD CCurMontLo;
460 OC_BN_WORD TFirst;
461
462 ASSERT (Result != NULL);
463 ASSERT (NumWords > 0);
464 ASSERT (N != NULL);
465 ASSERT (N0Inv != 0);
466 //
467 // Montgomery Reduction preparation
468 // As N_first is reduced mod 2^#Bits (word), we reduce C as well.
469 // Due to the reduction, the high bits are discarded safely.
470 // t_first = C * N_first
471 //
472 TFirst = Result[0] * N0Inv;
473 //
474 // Montgomery Reduction
475 // 1. C = C + t_first * N
476 // 2. In the first step, only the carries are actually used, so the
477 // division by R can be omited.
478 //
479 CCurMontLo = BigNumWordAddMul (&CCurMontHi, Result[0], TFirst, N[0]);
480 for (CompIndex = 1; CompIndex < NumWords; ++CompIndex) {
481 //
482 // Montgomery Reduction
483 // 1. C = C + t_first * N + carry
484 //
485 CCurMontLo = BigNumWordAddMulCarry (
486 &CCurMontHi,
487 Result[CompIndex],
488 TFirst,
489 N[CompIndex],
490 CCurMontHi
491 );
492 //
493 // 2. The index shift translates to a bitshift equivalent to the division
494 // by R = 2^#Bits (word).
495 // C = C / R
496 //
497 Result[CompIndex - 1] = CCurMontLo;
498 }
499
500 //
501 // Assign the most significant byte the remaining carry.
502 //
503 Result[CompIndex - 1] = CCurMontHi;
504}
505
517STATIC
518VOID
520 IN OUT OC_BN_WORD *Result,
521 IN OC_BN_NUM_WORDS NumWords,
522 IN CONST OC_BN_WORD *A,
523 IN CONST OC_BN_WORD *N,
524 IN OC_BN_WORD N0Inv
525 )
526{
527 UINTN RowIndex;
528
529 ASSERT (Result != NULL);
530 ASSERT (NumWords > 0);
531 ASSERT (A != NULL);
532 ASSERT (N != NULL);
533 ASSERT (N0Inv != 0);
534
535 ZeroMem (Result, OC_BN_SIZE (NumWords));
536 //
537 // Perform the entire standard multiplication and one Montgomery Reduction.
538 //
539 BigNumMontMulRow (Result, NumWords, 1, A, N, N0Inv);
540 //
541 // Perform the remaining Montgomery Reductions.
542 //
543 for (RowIndex = 1; RowIndex < NumWords; ++RowIndex) {
544 BigNumMontMulRow0 (Result, NumWords, N, N0Inv);
545 }
546
547 //
548 // As this implementation only reduces mod N on overflow and not for every
549 // yes-instance of C >= N, any sequence of Montgomery Multiplications must be
550 // completed with a final reduction step.
551 //
552}
553
554BOOLEAN
556 IN OUT OC_BN_WORD *Result,
557 IN OC_BN_NUM_WORDS NumWords,
558 IN CONST OC_BN_WORD *A,
559 IN UINT32 B,
560 IN CONST OC_BN_WORD *N,
561 IN OC_BN_WORD N0Inv,
562 IN CONST OC_BN_WORD *RSqrMod,
563 IN OC_BN_WORD *ATmp
564 )
565{
566 UINTN Index;
567
568 ASSERT (Result != NULL);
569 ASSERT (NumWords > 0);
570 ASSERT (A != NULL);
571 ASSERT (N != NULL);
572 ASSERT (N0Inv != 0);
573 ASSERT (RSqrMod != NULL);
574 //
575 // Currently, only the most frequent exponents are supported.
576 //
577 if ((B != 0x10001) && (B != 3)) {
578 DEBUG ((DEBUG_INFO, "OCCR: Unsupported exponent: %x\n", B));
579 return FALSE;
580 }
581
582 //
583 // Convert A into the Montgomery Domain.
584 // ATmp = MM (A, R^2 mod N)
585 //
586 BigNumMontMul (ATmp, NumWords, A, RSqrMod, N, N0Inv);
587
588 if (B == 0x10001) {
589 //
590 // Squaring the intermediate results 16 times yields A'^ (2^16).
591 //
592 for (Index = 0; Index < 16; Index += 2) {
593 //
594 // Result = MM (ATmp, ATmp)
595 //
596 BigNumMontMul (Result, NumWords, ATmp, ATmp, N, N0Inv);
597 //
598 // ATmp = MM (Result, Result)
599 //
600 BigNumMontMul (ATmp, NumWords, Result, Result, N, N0Inv);
601 }
602
603 //
604 // Because A is not within the Montgomery Domain, this implies another
605 // division by R, which takes the result out of the Montgomery Domain.
606 // C = MM (ATmp, A)
607 //
608 BigNumMontMul (Result, NumWords, ATmp, A, N, N0Inv);
609 } else {
610 //
611 // Result = MM (ATmp, ATmp)
612 //
613 BigNumMontMul (Result, NumWords, ATmp, ATmp, N, N0Inv);
614 //
615 // ATmp = MM (Result, ATmp)
616 //
617 BigNumMontMul (ATmp, NumWords, Result, ATmp, N, N0Inv);
618 //
619 // Perform a Montgomery Multiplication with 1, which effectively is a
620 // division by R, taking the result out of the Montgomery Domain.
621 // C = MM (ATmp, 1)
622 // TODO: Is this needed or can we just multiply with A above?
623 //
624 BigNumMontMul1 (Result, NumWords, ATmp, N, N0Inv);
625 }
626
627 //
628 // The Montgomery Multiplications above only ensure the result is mod N when
629 // it does not fit within #Bits(N). For N != 0, which is an obvious
630 // requirement, #Bits(N) can only ever fit values smaller than 2*N, so the
631 // result is at most one modulus too large.
632 // C = C mod N
633 //
634 if (BigNumCmp (Result, NumWords, N) >= 0) {
635 BigNumSub (Result, NumWords, Result, N);
636 }
637
638 return TRUE;
639}
UINTN OC_BN_WORD
Definition BigNumLib.h:26
#define OC_BN_BITS(NumWords)
Definition BigNumLib.h:59
#define OC_BN_MONT_RSQR_LEN(NumWords)
Definition BigNumLib.h:115
UINT32 OC_BN_NUM_BITS
Definition BigNumLib.h:37
UINT32 OC_BN_SIZE
Definition BigNumLib.h:36
#define OC_BN_MONT_MAX_LEN
Definition BigNumLib.h:106
UINT16 OC_BN_NUM_WORDS
Definition BigNumLib.h:35
VOID BigNumSub(IN OUT OC_BN_WORD *Result, IN OC_BN_NUM_WORDS NumWords, IN CONST OC_BN_WORD *A, IN CONST OC_BN_WORD *B)
VOID BigNumMod(IN OUT OC_BN_WORD *Result, IN OC_BN_NUM_WORDS NumWordsRest, IN CONST OC_BN_WORD *A, IN OC_BN_NUM_WORDS NumWordsA, IN CONST OC_BN_WORD *B, IN OC_BN_WORD *Scratch)
OC_BN_WORD BigNumWordMul(OUT OC_BN_WORD *Hi, IN OC_BN_WORD A, IN OC_BN_WORD B)
VOID BigNumOrWord(IN OUT OC_BN_WORD *A, IN OC_BN_NUM_WORDS NumWords, IN OC_BN_WORD Value, IN UINTN Exponent)
INTN BigNumCmp(IN CONST OC_BN_WORD *A, IN OC_BN_NUM_WORDS NumWords, IN CONST OC_BN_WORD *B)
STATIC OC_BN_WORD BigNumMontInverse(IN CONST OC_BN_WORD *A)
BOOLEAN BigNumPowMod(IN OUT OC_BN_WORD *Result, IN OC_BN_NUM_WORDS NumWords, IN CONST OC_BN_WORD *A, IN UINT32 B, IN CONST OC_BN_WORD *N, IN OC_BN_WORD N0Inv, IN CONST OC_BN_WORD *RSqrMod, IN OC_BN_WORD *ATmp)
OC_BN_WORD BigNumCalculateMontParams(IN OUT OC_BN_WORD *RSqrMod, IN OC_BN_NUM_WORDS NumWords, IN CONST OC_BN_WORD *N, IN OC_BN_WORD *Scratch)
STATIC VOID BigNumMontMul1(IN OUT OC_BN_WORD *Result, IN OC_BN_NUM_WORDS NumWords, IN CONST OC_BN_WORD *A, IN CONST OC_BN_WORD *N, IN OC_BN_WORD N0Inv)
STATIC VOID BigNumMontMulRow0(IN OUT OC_BN_WORD *Result, IN OC_BN_NUM_WORDS NumWords, IN CONST OC_BN_WORD *N, IN OC_BN_WORD N0Inv)
STATIC OC_BN_WORD BigNumWordAddMulCarry(OUT OC_BN_WORD *Hi, IN OC_BN_WORD C, IN OC_BN_WORD A, IN OC_BN_WORD B, IN OC_BN_WORD Carry)
STATIC VOID BigNumMontMul(IN OUT OC_BN_WORD *Result, IN OC_BN_NUM_WORDS NumWords, IN CONST OC_BN_WORD *A, IN CONST OC_BN_WORD *B, IN CONST OC_BN_WORD *N, IN OC_BN_WORD N0Inv)
STATIC VOID BigNumMontMulRow(IN OUT OC_BN_WORD *Result, IN OC_BN_NUM_WORDS NumWords, IN OC_BN_WORD AWord, IN CONST OC_BN_WORD *B, IN CONST OC_BN_WORD *N, IN OC_BN_WORD N0Inv)
STATIC OC_BN_WORD BigNumWordAddMul(OUT OC_BN_WORD *Hi, IN OC_BN_WORD C, IN OC_BN_WORD A, IN OC_BN_WORD B)
VOID *EFIAPI ZeroMem(OUT VOID *Buffer, IN UINTN Length)
#define ASSERT(x)
Definition coder.h:55
#define N
Definition lzss.c:65