packages feed

WMSigner-0.1.0.0: src/Data/Digest/WebMoney/Montgomery.hs

module Data.Digest.WebMoney.Montgomery where

import           Control.Lens                 (ix, (&), (.~))
import           Data.Bits                    (Bits, shiftL, shiftR, (.&.))
import           Data.Int                     (Int32, Int64)

import           Data.Digest.WebMoney.Algebra (logicalShiftR, normalize,
                                               remainder, resize, significance)

import           Data.Vector                  (Vector, cons, snoc, (!))
import qualified Data.Vector                  as V (head, length, replicate)

intSize, longMask :: Int
intSize = 32
longMask = 0xFFFFFFFF

bitMask :: Int64
bitMask = 0x80000000

-- Algorithm Montgomery exponentiation
-- INPUT:
--      m = (m[l-1] ... m[0]){b},
--      R = b^l,
--      mQ = m^-1 mod b,
--      e = (e[t] ... e[0]){2}
--           with e[t] = 1,
--           and an integer x, 1 <= x < m.
-- OUTPUT: x^e mod m.
exponentation :: Vector Int32 -> Vector Int32 -> Vector Int32 -> Vector Int32
exponentation x e m = normalize a3
  where
    -- mQ = -m^1 mod b
    mQ = inverse $ V.head m
    eLength = significance e
    mLength = significance m
    -- 1. temp = Mont(x, R^2 mod m), A = R mod m.
    temp = multiplication x' r2 m mQ
      where
        r = V.replicate ( 2 * V.length m ) 0 `snoc` 1
        r2 = remainder r m
        x' = if mLength > V.length x then resize x mLength else x
    a0 = remainder a' m
      where
        a' = V.replicate (V.length m) 0 `snoc` 1

    pos0 = eLength - 1

    mask0 :: Int64
    mask0 = head $ dropWhile (\mask -> fromIntegral (e ! pos0) .&. mask == 0) $ iterate (`logicalShiftR` 1) bitMask

    -- 2. For i from t down to 0 do the following:
    a2  = mont a0 pos0 mask0
    mont :: Vector Int32 -> Int -> Int64 -> Vector Int32
    mont a pos mask
      | pos < 0    = a
      | mask' == 0 = mont a'' (pos - 1) bitMask
      | otherwise  = mont a'' pos mask'
      where
        -- 2.1 A = Mont(A, A).
        a'  = multiplication a a m mQ
        -- 2.2 If e[i] = 1 then A = Mont(A, temp).
        a'' = if 0 /= fromIntegral (e ! pos) .&. mask then multiplication a' temp m mQ else a'
        mask'  = mask `shiftR` 1

    -- 3. A Mont(A, 1).
    one = 1 `cons` V.replicate (V.length m - 1) 0
    a3  = multiplication a2 one m mQ

-- Algorithm Montgomery multiplication
-- INPUT: integers
--      m = (m[n-1] ... m[1] m[0]){b},
--      x = (x[n-1] ... x[1] x[0]){b},
--      y = (y[n-1] ... y[1] y[0]){b}
--           with 0 <= x, y < m,
--           R = b^n with gcd(m, b) = 1,
--           and mQ = -m^1 mod b.
-- OUTPUT: x * y * R^-1 mod m.
multiplication :: Vector Int32 -> Vector Int32 -> Vector Int32 -> Int32 -> Vector Int32
multiplication x y m mQ = foldl iter a0 [0..n-1]
  where
    n = significance m
    -- 1. A = 0. (Notation: A = (a[n] a[n-1] ... a[1] a[0]){b})
    a0 = V.replicate (n + 1) 0
    -- 2. For i from 0 to (n - 1) do the following:
    iter :: Vector Int32 -> Int -> Vector Int32
    iter a i = ( fin_a & ix ( n - 1 ) .~ fromIntegral fin_carry ) & ix n .~ fromIntegral ( fin_carry `logicalShiftR` intSize )
      where
        -- 2.1 u_i = (a[0] + x[i] * y[0]) * mQ mod b.
        u :: Int
        u = (( fromIntegral (V.head a)
          + (((fromIntegral (x ! i) .&. longMask) * (fromIntegral (V.head y) .&. longMask)) .&. longMask))
          * fromIntegral mQ ) .&. longMask
        -- 2.2 A = (A + x[i] * y + u_i * m) / b.
        proc :: (Int, Vector Int32) -> Int -> (Int, Vector Int32)
        proc (last_carry, a') pos = (carry, a' & ix ( pos - 1 ) .~ fromIntegral temp)
          where
            xy, um, temp, carry :: Int
            xy = (fromIntegral (x ! i) .&. longMask) * (fromIntegral (y ! pos) .&. longMask)
            um = u * (fromIntegral (m ! pos) .&. longMask)
            temp = (fromIntegral (a' ! pos) .&. longMask)
              + (xy .&. fromIntegral longMask)
              + (um .&. fromIntegral longMask)
              + (last_carry .&. fromIntegral longMask)
            carry = (last_carry `logicalShiftR` 32)
              + (xy `logicalShiftR` intSize)
              + (um `logicalShiftR` intSize)
              + (temp `logicalShiftR` intSize)
        (carry', fin_a) = foldl proc (0, a) [0..n-1]
        fin_carry = carry' + (fromIntegral ( fin_a ! n ) .&. longMask)

inverse :: ( Num a, Bits a ) => a -> a
inverse value = -1 * ( iterate (\t -> t * ( 2 - value * t) ) temp !! 4 )
  where
    temp = ( ( ( value + 2 ) .&. 4 ) `shiftL` 1 ) + value