Subversion Repositories Kolibri OS

Rev

Rev 6469 | Rev 9070 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. ;    mpint.inc - Multi precision integer procedures
  2. ;
  3. ;    Copyright (C) 2015-2017 Jeffrey Amelynck
  4. ;
  5. ;    This program is free software: you can redistribute it and/or modify
  6. ;    it under the terms of the GNU General Public License as published by
  7. ;    the Free Software Foundation, either version 3 of the License, or
  8. ;    (at your option) any later version.
  9. ;
  10. ;    This program is distributed in the hope that it will be useful,
  11. ;    but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ;    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. ;    GNU General Public License for more details.
  14. ;
  15. ;    You should have received a copy of the GNU General Public License
  16. ;    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  17.  
  18. ; Notes:
  19. ;
  20. ; These procedures work only with positive integers.
  21. ; For compatibility reasons, the highest bit must always be 0.
  22. ; However, leading 0 bytes MUST at all other times be omitted.
  23. ;
  24. ; You have been warned!
  25.  
  26. MPINT_MAX_LEN = MAX_BITS/8
  27.  
  28.  
  29. ;;===========================================================================;;
  30. proc mpint_to_little_endian uses esi edi ecx ;///////////////////////////////;;
  31. ;;---------------------------------------------------------------------------;;
  32. ;? Convert big endian MPINT to little endian MPINT.                          ;;
  33. ;;---------------------------------------------------------------------------;;
  34. ;> esi = pointer to big endian MPINT                                         ;;
  35. ;> edi = pointer to buffer for little endian MPINT                           ;;
  36. ;;---------------------------------------------------------------------------;;
  37. ;< eax = MPINT number length                                                 ;;
  38. ;;===========================================================================;;
  39.  
  40. ; Load length dword
  41.         lodsd
  42. ; Convert to little endian
  43.         bswap   eax
  44.         stosd
  45.         test    eax, eax
  46.         jz      .zero
  47. ; Copy data, convert to little endian meanwhile
  48.         push    eax
  49.         add     esi, eax
  50.         push    esi
  51.         dec     esi
  52.         mov     ecx, eax
  53.         std
  54.   @@:
  55.         lodsb
  56.         mov     byte[edi], al
  57.         inc     edi
  58.         dec     ecx
  59.         jnz     @r
  60.         cld
  61.         pop     esi eax
  62.   .zero:
  63.         ret
  64.  
  65. endp
  66.  
  67. ;;===========================================================================;;
  68. proc mpint_to_big_endian uses esi edi ecx ;//////////////////////////////////;;
  69. ;;---------------------------------------------------------------------------;;
  70. ;? Convert little endian MPINT to big endian MPINT.                          ;;
  71. ;;---------------------------------------------------------------------------;;
  72. ;> esi = pointer to little endian MPINT                                      ;;
  73. ;> edi = pointer to buffer for big endian MPINT                              ;;
  74. ;;---------------------------------------------------------------------------;;
  75. ;< eax = MPINT number length                                                 ;;
  76. ;;===========================================================================;;
  77.  
  78. ; Load length dword
  79.         lodsd
  80.         test    eax, eax
  81.         jz      .zero
  82.         mov     ecx, eax
  83.         add     esi, eax
  84.         dec     esi
  85.         push    eax     ; we'll return length to the caller later
  86.         bswap   eax
  87.         stosd
  88. ; Copy data, convert to big endian meanwhile
  89.         std
  90.   @@:
  91.         lodsb
  92.         mov     byte[edi], al
  93.         inc     edi
  94.         dec     ecx
  95.         jnz     @r
  96.         cld
  97.         pop     eax
  98.         ret
  99.  
  100.   .zero:
  101.         stosd           ; Number 0 has 0 data bytes
  102.         ret
  103.  
  104. endp
  105.  
  106. ;;===========================================================================;;
  107. proc mpint_print uses ecx esi eax, src ;/////////////////////////////////////;;
  108. ;;---------------------------------------------------------------------------;;
  109. ;? Print MPINT to the debug board.                                           ;;
  110. ;;---------------------------------------------------------------------------;;
  111. ;> src = pointer to little endian MPINT                                      ;;
  112. ;;---------------------------------------------------------------------------;;
  113. ;< -                                                                         ;;
  114. ;;===========================================================================;;
  115.  
  116.         DEBUGF  1, "0x"
  117.         mov     esi, [src]
  118.         mov     ecx, [esi]
  119.         test    ecx, ecx
  120.         jz      .zero
  121.         lea     esi, [esi + ecx + 4 - 1]
  122.         pushf
  123.         std
  124.   .loop:
  125.         lodsb
  126.         DEBUGF  1, "%x", eax:2
  127.         dec     ecx
  128.         jnz     .loop
  129.         DEBUGF  1, "\n"
  130.         popf
  131.  
  132.         ret
  133.  
  134.   .zero:
  135.         DEBUGF  1, "00\n"
  136.         ret
  137.  
  138. endp
  139.  
  140. ;;===========================================================================;;
  141. proc mpint_hob uses edi ecx eax, dst ;///////////////////////////////////////;;
  142. ;;---------------------------------------------------------------------------;;
  143. ;? Return an index number giving the position of the highest order bit.      ;;
  144. ;;---------------------------------------------------------------------------;;
  145. ;> src = pointer to little endian MPINT                                      ;;
  146. ;;---------------------------------------------------------------------------;;
  147. ;< eax = highest order bit number                                            ;;
  148. ;;===========================================================================;;
  149.  
  150.         mov     edi, [dst]
  151.         lodsd
  152.         dec     eax                     ; total length minus one
  153.         mov     cl, [edi+eax]           ; load the highest order byte
  154.         shl     eax, 3                  ; multiply eax by 8 to get nr of bits
  155.  
  156. ; Now shift bits of the highest order byte right, until the byte reaches zero, counting bits meanwhile
  157.         test    cl, cl
  158.         jz      .end
  159.   @@:
  160.         inc     eax
  161.         shr     cl, 1
  162.         jnz     @r
  163.   .end:
  164.         ret
  165.  
  166. endp
  167.  
  168. ;;===========================================================================;;
  169. proc mpint_cmp uses esi edi ecx eax, dst, src ;//////////////////////////////;;
  170. ;;---------------------------------------------------------------------------;;
  171. ;? Compare two mpints.                                                       ;;
  172. ;;---------------------------------------------------------------------------;;
  173. ;> dst = pointer to little endian MPINT                                      ;;
  174. ;> src = pointer to little endian MPINT                                      ;;
  175. ;;---------------------------------------------------------------------------;;
  176. ;< flags are set as for single precision CMP instruction                     ;;
  177. ;;===========================================================================;;
  178.  
  179. ; First, check if number of significant bytes is the same
  180. ; If not, number with more bytes is bigger
  181.         mov     esi, [src]
  182.         mov     edi, [dst]
  183.         mov     ecx, [esi]
  184.         cmp     ecx, [edi]
  185.         jne     .got_answer
  186.  
  187. ; Numbers have equal amount of bytes, compare starting from the high order byte
  188.         add     edi, ecx
  189.         add     esi, ecx
  190.         std
  191.   .do_byte:
  192.         test    ecx, 11b
  193.         jz      .do_dword
  194.         dec     esi
  195.         dec     edi
  196.         cmpsb
  197.         jne     .got_answer
  198.         dec     ecx
  199.         jmp     .do_byte
  200.   .do_dword:
  201.         shr     ecx, 2
  202.         jz      .got_answer
  203.         sub     esi, 4
  204.         sub     edi, 4
  205.         repe cmpsd
  206.   .got_answer:
  207.         cld
  208.         ret
  209.  
  210. endp
  211.  
  212. ;;===========================================================================;;
  213. proc mpint_mov uses esi edi ecx, dst, src ;//////////////////////////////////;;
  214. ;;---------------------------------------------------------------------------;;
  215. ;? Copy mpint.                                                               ;;
  216. ;;---------------------------------------------------------------------------;;
  217. ;> dst = pointer to buffer for little endian MPINT                           ;;
  218. ;> src = pointer to little endian MPINT                                      ;;
  219. ;;---------------------------------------------------------------------------;;
  220. ;< dst = src                                                                 ;;
  221. ;;===========================================================================;;
  222.  
  223.         mov     esi, [src]
  224.         mov     edi, [dst]
  225.         mov     ecx, [esi]
  226.         push    ecx
  227.         shr     ecx, 2
  228.         inc     ecx             ; for length dword
  229.         rep movsd
  230.         pop     ecx
  231.         and     ecx, 11b
  232.         jz      @f
  233.         rep movsb
  234.   @@:
  235.  
  236.         ret
  237.  
  238. endp
  239.  
  240. ;;===========================================================================;;
  241. proc mpint_shl1 uses esi ecx, dst ;//////////////////////////////////////////;;
  242. ;;---------------------------------------------------------------------------;;
  243. ;? Shift little endian MPINT one bit to the left.                            ;;
  244. ;;---------------------------------------------------------------------------;;
  245. ;> dst = pointer to little endian MPINT                                      ;;
  246. ;;---------------------------------------------------------------------------;;
  247. ;< dst = dst SHL 1                                                           ;;
  248. ;;===========================================================================;;
  249.  
  250.         mov     esi, [dst]
  251.         mov     ecx, [esi]
  252.         test    ecx, ecx
  253.         jz      .done
  254.  
  255. ; Test if high order byte will overflow
  256. ; Remember: highest bit must never be set for positive numbers!
  257.         test    byte[esi+ecx+3], 11000000b
  258.         jz      @f
  259. ; We must grow a byte in size!
  260. ; TODO: check for overflow
  261.         inc     ecx
  262.         mov     [esi], ecx
  263.         mov     byte[esi+ecx+3], 0        ; Add the new MSB
  264.   @@:
  265.         add     esi, 4
  266. ; Do the lowest order byte first
  267.         shl     byte[esi], 1
  268.         dec     ecx
  269.         jz      .done
  270. ; And the remaining bytes
  271.   @@:
  272.         inc     esi
  273.         rcl     byte[esi], 1
  274.         dec     ecx
  275.         jnz     @r
  276.   .done:
  277.         ret
  278.  
  279. endp
  280.  
  281. ;;===========================================================================;;
  282. proc mpint_shr1 uses edi ecx, dst ;//////////////////////////////////////////;;
  283. ;;---------------------------------------------------------------------------;;
  284. ;? Shift little endian MPINT one bit to the right.                           ;;
  285. ;;---------------------------------------------------------------------------;;
  286. ;> dst = pointer to little endian MPINT                                      ;;
  287. ;;---------------------------------------------------------------------------;;
  288. ;< dst = dst SHR 1                                                           ;;
  289. ;;===========================================================================;;
  290.  
  291.         mov     edi, [dst]
  292.         mov     ecx, [edi]
  293.         test    ecx, ecx
  294.         jz      .done
  295.  
  296. ; Do the highest order byte first
  297.         dec     ecx
  298.         shr     byte[edi+ecx+3], 1
  299. ; Was it 0? If so, we must decrement total length
  300.         jnz     @f
  301.         jc      @f
  302.         mov     [edi], ecx
  303.   @@:
  304.         test    ecx, ecx
  305.         jz      .done
  306. ; Now do the trailing bytes
  307.         add     edi, 4
  308.         add     edi, ecx
  309.   @@:
  310.         dec     edi
  311.         rcr     byte[edi], 1
  312.         dec     ecx             ; does not affect carry flag, hooray!
  313.         jnz     @r
  314.   .done:
  315.         ret
  316.  
  317. endp
  318.  
  319. ;;===========================================================================;;
  320. proc mpint_shl uses eax ebx ecx edx esi edi, dst, shift ;////////////////////;;
  321. ;;---------------------------------------------------------------------------;;
  322. ;? Left shift little endian MPINT by x bits.                                 ;;
  323. ;;---------------------------------------------------------------------------;;
  324. ;> dst = pointer to little endian MPINT                                      ;;
  325. ;> shift = number of bits to shift the MPINT                                 ;;
  326. ;;---------------------------------------------------------------------------;;
  327. ;< -                                                                         ;;
  328. ;;===========================================================================;;
  329.  
  330.         mov     ecx, [shift]
  331.         shr     ecx, 3                  ; 8 bits in one byte
  332.         cmp     ecx, MPINT_MAX_LEN
  333.         jge     .zero
  334.         mov     esi, [dst]
  335.         add     esi, MPINT_MAX_LEN+4-4
  336.         mov     edi, esi
  337.         and     ecx, not 11b
  338.         sub     esi, ecx
  339.         mov     edx, MPINT_MAX_LEN/4-1
  340.         shr     ecx, 2                  ; 4 bytes in one dword
  341.         push    ecx
  342.         sub     edx, ecx
  343.         mov     ecx, [shift]
  344.         and     ecx, 11111b
  345.         std
  346.   .loop:
  347.         lodsd
  348.         mov     ebx, [esi]
  349.         shld    eax, ebx, cl
  350.         stosd
  351.         dec     edx
  352.         jnz     .loop
  353.         lodsd
  354.         shl     eax, cl
  355.         stosd
  356.  
  357.         ; fill the lsb bytes with zeros
  358.         pop     ecx
  359.         test    ecx, ecx
  360.         jz      @f
  361.         xor     eax, eax
  362.         rep stosd
  363.   @@:
  364.         cld
  365.         ret
  366.  
  367.   .zero:
  368.         mov     eax, [dst]
  369.         mov     dword[eax], 0
  370.         ret
  371.  
  372. endp
  373.  
  374. ;;===========================================================================;;
  375. proc mpint_shlmov uses eax ebx ecx edx esi edi, dst, src, shift ;////////////;;
  376. ;;---------------------------------------------------------------------------;;
  377. ;? Left shift by x bits and copy little endian MPINT.                        ;;
  378. ;;---------------------------------------------------------------------------;;
  379. ;> src = pointer to little endian MPINT                                      ;;
  380. ;> dst = pointer to little endian MPINT                                      ;;
  381. ;> shift = number of bits to shift the MPINT to the left                     ;;
  382. ;;---------------------------------------------------------------------------;;
  383. ;< dst = src SHL shift                                                       ;;
  384. ;;===========================================================================;;
  385.  
  386.         mov     ecx, [shift]
  387.         shr     ecx, 3                  ; 8 bits in one byte
  388.         cmp     ecx, MPINT_MAX_LEN
  389.         jge     .zero
  390.         mov     esi, [src]
  391.         add     esi, MPINT_MAX_LEN+4-4
  392.         mov     edi, [dst]
  393.         add     edi, MPINT_MAX_LEN+4-4
  394.         and     ecx, not 11b
  395.         sub     esi, ecx
  396.         mov     edx, MPINT_MAX_LEN/4-1
  397.         shr     ecx, 2                  ; 4 bytes in one dword
  398.         push    ecx
  399.         sub     edx, ecx
  400.         mov     ecx, [shift]
  401.         and     ecx, 11111b
  402.         std
  403.   .loop:
  404.         lodsd
  405.         mov     ebx, [esi]
  406.         shld    eax, ebx, cl
  407.         stosd
  408.         dec     edx
  409.         jnz     .loop
  410.         lodsd
  411.         shl     eax, cl
  412.         stosd
  413.  
  414.         ; fill the lsb bytes with zeros
  415.         pop     ecx
  416.         test    ecx, ecx
  417.         jz      @f
  418.         xor     eax, eax
  419.         rep stosd
  420.   @@:
  421.         cld
  422.         ret
  423.  
  424.   .zero:
  425.         mov     eax, [dst]
  426.         mov     dword[eax], 0
  427.         ret
  428.  
  429. endp
  430.  
  431. ;;===========================================================================;;
  432. proc mpint_add uses esi edi ecx eax, dst, src ;//////////////////////////////;;
  433. ;;---------------------------------------------------------------------------;;
  434. ;? Add a little endian MPINT to another little endian MPINT.                 ;;
  435. ;;---------------------------------------------------------------------------;;
  436. ;> src = pointer to little endian MPINT                                      ;;
  437. ;> dst = pointer to little endian MPINT                                      ;;
  438. ;;---------------------------------------------------------------------------;;
  439. ;< dst = dst + src                                                           ;;
  440. ;;===========================================================================;;
  441.  
  442.         mov     esi, [src]
  443.         mov     edi, [dst]
  444.         mov     ecx, [esi]      ; source number length
  445.         sub     ecx, [dst]
  446.         jbe     .length_ok
  447. ; Length of the destination is currently smaller then the source, pad with 0 bytes
  448.         add     edi, [edi]
  449.         add     edi, 4
  450.         mov     al, 0
  451.         rep stosb
  452.   .length_ok:
  453.         mov     ecx, [esi]
  454.         mov     edi, [dst]
  455.         add     esi, 4
  456.         add     edi, 4
  457. ; Add the first byte
  458.         lodsb
  459.         add     byte[edi], al
  460.         dec     ecx
  461.         jz      .done
  462. ; Add the other bytes
  463.   @@:
  464.         inc     edi
  465.         lodsb
  466.         adc     byte[edi], al
  467.         dec     ecx
  468.         jnz     @r
  469.   .done:
  470. ; check if highest bit OR carry flag is set
  471. ; if so, add a byte if we have the buffer space
  472. ; TODO: check if we have the buffer space
  473.         jc      .carry
  474.         cmp     byte[edi], 0x80
  475.         jnz     .high_bit_set
  476.  
  477.         ret
  478.  
  479.   .carry:
  480.         inc     edi
  481.         mov     byte[edi], 1
  482.         mov     eax, [dst]
  483.         inc     dword[eax]
  484.  
  485.         ret
  486.  
  487.   .high_bit_set:
  488.         inc     edi
  489.         mov     byte[edi], 0
  490.         mov     eax, [dst]
  491.         inc     dword[eax]
  492.  
  493.         ret
  494.  
  495. endp
  496.  
  497. ;;===========================================================================;;
  498. proc mpint_sub uses eax esi edi ecx, dst, src ;//////////////////////////////;;
  499. ;;---------------------------------------------------------------------------;;
  500. ;? Subtract a little endian MPINT to another little endian MPINT.            ;;
  501. ;;---------------------------------------------------------------------------;;
  502. ;> src = pointer to little endian MPINT                                      ;;
  503. ;> dst = pointer to little endian MPINT                                      ;;
  504. ;;---------------------------------------------------------------------------;;
  505. ;< dst = dst - src                                                           ;;
  506. ;;===========================================================================;;
  507.  
  508.         mov     esi, [src]
  509.         mov     edi, [dst]
  510.         mov     ecx, [esi]      ; destination number length
  511.         cmp     ecx, [edi]
  512.         ja      .overflow
  513.  
  514.         add     esi, 4
  515.         add     edi, 4
  516. ; Subtract the first byte
  517.         lodsb
  518.         sub     byte[edi], al
  519.         dec     ecx
  520.         jz      .done
  521. ; Subtract the other bytes
  522.   @@:
  523.         inc     edi
  524.         lodsb
  525.         sbb     byte[edi], al
  526.         dec     ecx
  527.         jnz     @r
  528.   .done:
  529.         stdcall mpint_shrink, [dst]
  530.         ret
  531.  
  532.   .overflow:
  533.         mov     dword[edi], 0
  534.         stc
  535.         ret
  536.  
  537. endp
  538.  
  539.  
  540. ;;===========================================================================;;
  541. proc mpint_shrink uses eax edi ecx, dst ;////////////////////////////////////;;
  542. ;;---------------------------------------------------------------------------;;
  543. ;? Get rid of leading zeroes on a little endian MPINT.                       ;;
  544. ;;---------------------------------------------------------------------------;;
  545. ;> src = pointer to little endian MPINT                                      ;;
  546. ;;---------------------------------------------------------------------------;;
  547. ;<                                                                           ;;
  548. ;;===========================================================================;;
  549.  
  550.         mov     edi, [dst]
  551.         lodsd
  552.         std
  553.         mov     ecx, eax
  554.         dec     eax             ; total length minus one
  555.         add     edi, eax
  556.         xor     al, al
  557.         repe cmpsb
  558.         inc     ecx
  559.         mov     edi, [dst]
  560.         mov     [edi], ecx
  561.         cld
  562.  
  563.         ret
  564.  
  565. endp
  566.  
  567. ;;===========================================================================;;
  568. proc mpint_mul uses esi edi ecx ebx eax, dst, A, B ;/////////////////////////;;
  569. ;;---------------------------------------------------------------------------;;
  570. ;? Multiply to little endian MPINTS and store them in a new one.             ;;
  571. ;;---------------------------------------------------------------------------;;
  572. ;> A = pointer to little endian MPINT                                        ;;
  573. ;> B = pointer to little endian MPINT                                        ;;
  574. ;> dst = pointer to buffer for little endian MPINT                           ;;
  575. ;;---------------------------------------------------------------------------;;
  576. ;< dst = A * B                                                               ;;
  577. ;;===========================================================================;;
  578.  
  579.         ; Set result to zero
  580.         mov     eax, [dst]
  581.         mov     dword[eax], 0
  582.  
  583.         ; first, find the byte in A containing the highest order bit
  584.         mov     edi, [A]
  585.         mov     eax, [edi]
  586.         test    eax, eax
  587.         jz      .zero
  588.         add     edi, eax
  589.         mov     al, [edi+1]
  590.         mov     esi, edi
  591.         mov     bl, 8
  592.   @@:
  593.         shl     al, 1
  594.         jc      .first_hit
  595.         dec     bl
  596.         jnz     @r
  597.  
  598.         ; Then, starting from this byte, iterate through the bits in A,
  599.         ; starting from the highest order bit down to the lowest order bit.
  600.   .next_byte:
  601.         mov     al, [edi]
  602.         dec     edi
  603.         mov     bl, 8
  604.   .next_bit:
  605.         stdcall mpint_shl1, [dst]
  606.         shl     al, 1
  607.         jnc     .zero_bit
  608.   .first_hit:
  609.         stdcall mpint_add, [dst], [B]
  610.   .zero_bit:
  611.         dec     bl
  612.         jnz     .next_bit
  613.         dec     ecx
  614.         jnz     .next_byte
  615.   .zero:
  616.         ret
  617.  
  618. endp
  619.  
  620. ;;===========================================================================;;
  621. proc mpint_mod uses eax ebx ecx, dst, mod ;//////////////////////////////////;;
  622. ;;---------------------------------------------------------------------------;;
  623. ;? Find the modulo (remainder after division) of dst by mod.                 ;;
  624. ;;---------------------------------------------------------------------------;;
  625. ;> dst = pointer to little endian MPINT                                      ;;
  626. ;> mod = pointer to little endian MPINT                                      ;;
  627. ;;---------------------------------------------------------------------------;;
  628. ;< dst = dst MOD mod                                                         ;;
  629. ;;===========================================================================;;
  630.  
  631. locals
  632.         mpint_tmp       rb MPINT_MAX_LEN+4
  633. endl
  634.  
  635.         ; if mod is zero, return
  636.         mov     eax, [mod]
  637.         cmp     dword[eax], 0
  638.         je      .zero
  639.  
  640.         stdcall mpint_cmp, eax, [dst]
  641.         jb      .done                           ; if dst < mod, dst = dst
  642.         je      .zero                           ; if dst == mod, dst = 0
  643.  
  644.         lea     ebx, [mpint_tmp]
  645.  
  646.         ; left shift mod until the high order bits of mod and dst are aligned
  647.         stdcall mpint_hob, [dst]
  648.         mov     ecx, eax
  649.         stdcall mpint_hob, [mod]
  650.         sub     ecx, eax
  651.         stdcall mpint_shlmov, ebx, [mod], ecx
  652.         inc     ecx
  653.  
  654.         ; For every bit in dst (starting from the high order bit):
  655.   .loop:
  656.         ;   determine if dst is bigger than mpint_tmp
  657.         stdcall mpint_cmp, [dst], ebx
  658.         ja      @f
  659.         ;   if so, subtract mpint_tmp from dst
  660.         stdcall mpint_sub, [dst], ebx
  661.   @@:
  662.         dec     ecx
  663.         jz      .done
  664.         ;   shift mpint_tmp right by 1
  665.         stdcall mpint_shr1, ebx
  666.         jmp     .loop
  667.  
  668.   .zero:
  669.         mov     eax, [dst]
  670.         mov     dword[eax], 0
  671.   .done:
  672.         ret
  673.  
  674. endp
  675.  
  676. ;;===========================================================================;;
  677. proc mpint_modexp uses edi eax ebx ecx edx, dst, base, exp, mod ;////////////;;
  678. ;;---------------------------------------------------------------------------;;
  679. ;? Find the modulo (remainder after division) of dst by mod.                 ;;
  680. ;;---------------------------------------------------------------------------;;
  681. ;> dst = pointer to buffer for little endian MPINT                           ;;
  682. ;> base = pointer to little endian MPINT                                     ;;
  683. ;> exp = pointer to little endian MPINT                                      ;;
  684. ;> mod = pointer to little endian MPINT                                      ;;
  685. ;;---------------------------------------------------------------------------;;
  686. ;< dst = base ** exp MOD mod                                                 ;;
  687. ;;===========================================================================;;
  688.  
  689. locals
  690.         mpint_tmp       rb MPINT_MAX_LEN+4
  691. endl
  692.  
  693.         ; If mod is zero, return
  694.         mov     eax, [mod]
  695.         cmp     dword[eax], 0
  696.         je      .mod_zero
  697.  
  698.         ; Find the highest order byte in exponent
  699.         mov     edi, [exp]
  700.         mov     ecx, [edi]
  701.         lea     edi, [edi + 4 + ecx - 1]
  702.         ; Find the highest order bit in this byte
  703.         mov     al, [edi]
  704.         test    al, al
  705.         jz      .invalid
  706.         mov     bl, 9
  707.   @@:
  708.         dec     bl
  709.         shl     al, 1
  710.         jnc     @r
  711.  
  712.         lea     edx, [mpint_tmp]
  713.         ; Initialise result to base, to take care of the highest order bit
  714.         stdcall mpint_mov, [dst], [base]
  715.         dec     bl
  716.         jz      .next_byte
  717.   .bit_loop:
  718.         ; For each bit, square result
  719.         stdcall mpint_mov, edx, [dst]
  720.         stdcall mpint_mul, [dst], edx, edx
  721.         stdcall mpint_mod, [dst], [mod]
  722.  
  723.         ; If the bit is set, multiply result by the base
  724.         shl     al, 1
  725.         jnc     .next_bit
  726.         stdcall mpint_mov, edx, [dst]
  727.         stdcall mpint_mul, [dst], [base], edx
  728.         stdcall mpint_mod, [dst], [mod]
  729.   .next_bit:
  730.         dec     bl
  731.         jnz     .bit_loop
  732.   .next_byte:
  733.         dec     ecx
  734.         jz      .done
  735.         dec     edi
  736.         mov     al, [edi]
  737.         mov     bl, 8
  738.         jmp     .bit_loop
  739.   .done:
  740.         ret
  741.  
  742.   .mod_zero:
  743.         DEBUGF  3, "modexp with modulo 0\n"
  744.         ; if mod is zero, result = 0
  745.         mov     eax, [dst]
  746.         mov     dword[eax], 0
  747.         ret
  748.  
  749.   .exp_zero:
  750.         DEBUGF  3, "modexp with exponent 0\n"
  751.         ; if exponent is zero, result = 1
  752.         mov     eax, [dst]
  753.         mov     dword[eax], 1
  754.         mov     byte[eax+4], 1
  755.         ret
  756.  
  757.   .invalid:
  758.         DEBUGF  3, "modexp: Invalid input!\n"
  759.         ret
  760.  
  761. endp