diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Ilya
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/WMSigner.cabal b/WMSigner.cabal
new file mode 100644
--- /dev/null
+++ b/WMSigner.cabal
@@ -0,0 +1,63 @@
+name:                   WMSigner
+version:                0.1.0.0
+synopsis:               WebMoney authentication module
+description:
+    Pure haskell implementation WMSigner authentication module.
+    .
+    Simple examples:
+    .
+    > import Data.Digest.Webmoney
+    >
+    > signer = newSigner (exponent :: [Word8) (modulus :: [Word8])
+    >
+    > main = do
+    >   putStrLn $ signUnsafe signer "message" -- static version
+    >   sign signer "message" >>= putStrLn     -- randomized version
+license:                MIT
+license-file:           LICENSE
+copyright:              Ilya Smelkov <triplepointfive@gmail.com>
+author:                 Ilya Smelkov <triplepointfive@gmail.com>
+maintainer:             Ilya Smelkov <triplepointfive@gmail.com>
+category:               Data, Cryptography
+build-type:             Simple
+cabal-version:          >=1.8
+
+library
+    exposed-modules:    Data.Digest.WebMoney
+    other-modules:      Data.Digest.WebMoney.Signer
+                        Data.Digest.WebMoney.Montgomery
+                        Data.Digest.WebMoney.Algebra
+    build-depends:      base >=4.6 && <4.8
+                      , bytestring >= 0.10
+                      , directory >= 1.0.0.0
+                      , cryptohash
+                      , random
+                      , vector >= 0.10
+                      , base64-bytestring >= 1
+                      , binary >= 0.5
+                      , random >= 1.0.1
+                      , mtl >= 2.1
+                      , split >= 0.2
+                      , lens >= 4.4
+    hs-source-dirs:     src
+    ghc-options:        -Wall -fno-warn-unused-do-bind -fno-warn-missing-signatures
+
+Test-Suite test-WMSigner
+    type:               exitcode-stdio-1.0
+    main-is:            Spec.hs
+    build-depends:      base
+                      , hspec
+                      , lens >= 4.4
+                      , bytestring >= 0.10
+                      , split >= 0.2
+                      , cryptohash
+                      , random
+                      , vector >= 0.10
+    buildable:          True
+    cpp-options:        -DTEST_MODE=true
+    hs-source-dirs:     src test
+    ghc-options:        -Wall -fno-warn-unused-do-bind -fno-warn-missing-signatures
+
+source-repository head
+  type:     git
+  location: git://github.com/triplepointfive/wmsigner
diff --git a/src/Data/Digest/WebMoney.hs b/src/Data/Digest/WebMoney.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Digest/WebMoney.hs
@@ -0,0 +1,20 @@
+module Data.Digest.WebMoney
+    (
+      newSigner
+    , sign
+    , signUnsafe
+    , S.Signer
+    )
+    where
+
+import qualified Data.Digest.WebMoney.Signer as S (Signer, newSigner, sign,
+                                                   signUnsafe)
+
+-- |Initializes new 'Signer' object, takes exponent and modulus as arguments
+newSigner = S.newSigner
+
+-- |Calculates static signature for string
+signUnsafe = S.signUnsafe
+
+-- |Calculates randomized signature for string
+sign = S.sign
diff --git a/src/Data/Digest/WebMoney/Algebra.hs b/src/Data/Digest/WebMoney/Algebra.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Digest/WebMoney/Algebra.hs
@@ -0,0 +1,172 @@
+module Data.Digest.WebMoney.Algebra where
+
+import           Data.Bits    (Bits, bitSize, shiftL, shiftR, testBit, (.&.),
+                               (.|.))
+import           Data.Int     (Int32, Int64)
+import           Data.Word    (Word32, Word64)
+
+import           Control.Lens (ix, (&), (.~))
+import           Data.Vector  (Vector, singleton, (!))
+import qualified Data.Vector  as V (init, last, length, null, replicate, take,
+                                    (++))
+
+longMask :: Int64
+longMask = 0xFFFFFFFF
+
+intSize :: Int
+intSize = 32
+
+logicalShiftR :: Integral a => a -> Int -> a
+logicalShiftR x i = fromIntegral ((fromIntegral x :: Word64) `shiftR` i)
+
+logicalShiftRight :: Int32 -> Int -> Int32
+logicalShiftRight x i = fromIntegral ((fromIntegral x :: Word32) `shiftR` i)
+
+getBitsNumber :: Bits a => a -> Int
+getBitsNumber x = intSize - numberOfLeadingZeros x
+
+numberOfLeadingZeros :: Bits a => a -> Int
+numberOfLeadingZeros x = length $ takeWhile (not . testBit x) [size - 1, size - 2 .. 0]
+  where size = bitSize x
+
+getBitsCount :: (Bits a, Num a) => Vector a -> Int
+getBitsCount xs = ( vLenght - 1 ) * intSize + getBitsNumber ( xs ! (vLenght - 1) )
+  where vLenght = significance xs
+
+compareLists :: Vector Int32 -> Vector Int32 -> Ordering
+compareLists lhs rhs
+    | lhsLenght > rhsLenght = GT
+    | lhsLenght < rhsLenght = LT
+    | otherwise             = comp (V.take lhsLenght lhs) (V.take lhsLenght rhs)
+  where
+    lhsLenght = significance lhs
+    rhsLenght = significance rhs
+
+    comp :: Vector Int32 -> Vector Int32 -> Ordering
+    comp ls rs
+      | V.null ls || V.null rs = EQ
+      | lb > rb                = GT
+      | lb < rb                = LT
+      | otherwise              = comp (V.init ls) (V.init rs)
+      where
+        lb = fromIntegral (V.last ls) .&. longMask
+        rb = fromIntegral (V.last rs) .&. longMask
+
+significance :: (Eq a, Bits a, Num a) => Vector a -> Int
+significance xs
+  | V.null xs      = 0
+  | V.last xs == 0 = significance ( V.init xs )
+  | otherwise      = V.length xs
+
+shift :: Vector Int32 -> Int -> Vector Int32
+shift lhs rhs
+    | outWordsCount <= 0        = singleton 0
+    | shiftBits == 0 && rhs > 0 = V.take shiftWords r0 V.++ V.take (outWordsCount - shiftWords) lhs
+    | rhs > 0                   =
+      let (res, carry) = foldl shRight (r0, 0) [0 .. inWordsCount - 1]
+      in if inWordsCount - 1 + shiftWords < outWordsCount
+            then res & ix ( inWordsCount + shiftWords ) .~ (res ! (inWordsCount + shiftWords) .|. carry)
+            else res
+    | shiftBits == 0            = error "3"
+    | otherwise                 =
+      let carry = if outWordsCount + shiftWords < inWordsCount
+                    then (lhs ! (outWordsCount + shiftWords)) `shiftL` ( intSize - shiftBits)
+                    else 0
+      in fst $ foldl shLeft (r0, carry) [inWordsCount - 1, inWordsCount - 2 .. 0]
+  where
+    shiftBits, shiftWords, inBitsCount, inWordsCount, outBitsCount, outWordsCount :: Int
+    shiftBits     = abs rhs `mod` intSize
+    shiftWords    = abs rhs `div` intSize
+    inBitsCount   = getBitsCount lhs
+    inWordsCount  = inBitsCount `div` intSize + (if inBitsCount `mod` intSize > 0 then 1 else 0)
+    outBitsCount  = inBitsCount + rhs
+    outWordsCount = outBitsCount `div` intSize + (if outBitsCount `mod` intSize > 0 then 1 else 0)
+
+    r0 = V.replicate (max inWordsCount outWordsCount) 0
+
+    shRight, shLeft :: (Vector Int32, Int32) -> Int -> (Vector Int32, Int32)
+    shRight (res, carry) pos = ( res & ix ( pos + shiftWords ) .~ val, nextCarry )
+      where
+        temp      = lhs ! pos
+        val       = ( temp `shiftL` shiftBits ) .|. carry
+        nextCarry = temp `logicalShiftRight` ( intSize - shiftBits )
+    shLeft (res, carry) pos = ( res & ix ( pos + shiftWords ) .~ val, nextCarry )
+      where
+        temp      = lhs ! (pos + shiftWords)
+        val       = (temp `logicalShiftRight` shiftBits) .|. carry
+        nextCarry = temp `shiftL` ( intSize - shiftBits )
+
+shiftRight :: Vector Int32 -> Vector Int32
+shiftRight value = fst $ foldl right (value, 0) [len-1, len-2..0]
+  where
+    len = significance value
+    right :: (Vector Int32, Int64) -> Int -> (Vector Int32, Int64)
+    right (v, carry) pos = ( v & ix pos .~ fromIntegral val, nextCarry )
+      where
+        temp, nextCarry, val :: Int64
+        temp      = fromIntegral ( v ! pos)               .&. longMask
+        nextCarry = (temp .&. 1) `shiftL` ( intSize - 1)  .&. longMask
+        val       = ((temp `logicalShiftR` 1) .|. carry ) .&. longMask
+
+sub :: Vector Int32 -> Vector Int32 -> Vector Int32
+sub lhs rhs
+    | lhsLength < rhsLength = error "Difference should not be negative."
+    | otherwise             = modulo $ rest subscribed
+  where
+    lhsLength = significance lhs
+    rhsLength = significance rhs
+
+    modulo :: (Vector Int32, Int32) -> Vector Int32
+    modulo (_, 1) = error "Difference should not be negative."
+    modulo (l, _) = l
+
+    subscribed :: (Vector Int32, Int32)
+    subscribed = foldl substr (lhs, 0) [0..rhsLength - 1]
+      where
+        substr :: (Vector Int32, Int32) -> Int -> (Vector Int32, Int32)
+        substr (l, borrow) pos = ( l & ix pos .~ fromIntegral temp, nBorrow )
+          where
+            temp = (fromIntegral ( l ! pos ) .&. longMask )
+              - (fromIntegral ( rhs ! pos ) .&. longMask )
+              - fromIntegral borrow
+            nBorrow = if temp .&. ( 1 `shiftL` intSize ) /= 0 then 1 else 0
+
+    rest :: (Vector Int32, Int32) -> (Vector Int32, Int32)
+    rest (ls, b) = foldl substr (ls, b) [rhsLength..lhsLength - 1]
+      where
+        substr :: (Vector Int32, Int32) -> Int -> (Vector Int32, Int32)
+        substr (l, borrow) pos = ( l & ix pos .~ fromIntegral temp, nBorrow )
+          where
+            temp = (fromIntegral ( l ! pos ) .&. longMask ) - fromIntegral borrow
+            nBorrow = if temp .&. ( 1 `shiftL` intSize ) /= 0 then 1 else 0
+
+remainder :: Vector Int32 -> Vector Int32 -> Vector Int32
+remainder lhs rhs = divide lhs rhs
+  where
+    rhsBitsCount = getBitsCount rhs -- check attemption to divide by zero
+
+    divide :: Vector Int32 -> Vector Int32 -> Vector Int32
+    divide l r
+      | LT == compareLists l r = l
+      | lhsBitsCount == 0      = l
+      | otherwise              =
+        let temp' = if compareLists l temp == LT then shiftRight temp else temp
+        in divide ( subs l temp' ) r
+      where
+        lhsBitsCount = getBitsCount l
+        temp         = shift r (lhsBitsCount - rhsBitsCount)
+    subs :: Vector Int32 -> Vector Int32 -> Vector Int32
+    subs l t =
+      if compareLists l t /= LT
+        then subs (sub l t) t
+        else l
+
+resize :: Vector Int32 -> Int -> Vector Int32
+resize v l
+  | l < 0       = error "Invalid value for length"
+  | vLength < l = v V.++ V.replicate (l - vLength) 0
+  | otherwise   = V.take l v
+  where vLength = V.length v
+
+normalize :: Vector Int32 -> Vector Int32
+normalize x = resize x ( significance x )
diff --git a/src/Data/Digest/WebMoney/Montgomery.hs b/src/Data/Digest/WebMoney/Montgomery.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Digest/WebMoney/Montgomery.hs
@@ -0,0 +1,114 @@
+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
diff --git a/src/Data/Digest/WebMoney/Signer.hs b/src/Data/Digest/WebMoney/Signer.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Digest/WebMoney/Signer.hs
@@ -0,0 +1,102 @@
+{-# LANGUAGE GADTs #-}
+
+module Data.Digest.WebMoney.Signer where
+
+import           Control.Monad                   (replicateM)
+import           Data.Bits                       (shiftL, (.|.))
+import           Data.Char                       (ord)
+import           Data.Int                        (Int16, Int32)
+import           Data.List                       (foldl')
+import           Data.Word                       (Word8)
+import           System.Random                   (randomIO)
+import           Text.Printf                     (printf)
+
+import           Crypto.Hash.MD4                 (hash)
+import qualified Data.ByteString                 as B (ByteString, elem, pack,
+                                                       unpack)
+import qualified Data.ByteString.Internal        as B (c2w)
+import           Data.List.Split                 (chunksOf)
+
+import           Data.Digest.WebMoney.Algebra    (logicalShiftRight,
+                                                  significance)
+import           Data.Digest.WebMoney.Montgomery (exponentation)
+
+import           Data.Vector                     (Vector, fromList, (!))
+
+intSize, shortSize, byteSize, hashSize :: Int
+intSize   = 32
+shortSize = 16
+byteSize  = 8
+hashSize  = 128
+
+header, trail :: [Word8]
+header = [0x38, 0]
+trail = [1, 0]
+
+data Signer where
+  Signer :: { expon     :: !(Vector Int32)
+            , modulus   :: !(Vector Int32)
+            , keyLength :: !Int
+            } -> Signer
+
+newSigner :: [Word8] -> [Word8] -> Signer
+newSigner expon' modul = Signer { expon     = fromList (cast expon')
+                                , modulus   = modulus'
+                                , keyLength = significance (fromList modul) * byteSize
+                                }
+  where modulus' = fromList (cast modul)
+
+-- |Calculates static signature for string
+signUnsafe :: Signer -> String -> String
+signUnsafe signer = signWithBuffer signer (zeroBuffer signer)
+
+-- |Calculates randomized signature for string
+sign :: Signer -> String -> IO String
+sign signer message = do
+    rndBuf <- randomBuffer signer
+    return $ signWithBuffer signer rndBuf message
+
+signWithBuffer :: Signer -> [Word8] -> String -> String
+signWithBuffer signer buffer =
+  buildSignature signer . signBytes signer buffer . validate . B.pack . map ( fromIntegral . ord )
+
+validate :: B.ByteString -> B.ByteString
+validate message = if B.c2w '\r' `B.elem` message
+  then error "Message cannot contain of the following character: '\r'."
+  else message
+
+zeroBuffer :: Signer -> [Word8]
+zeroBuffer signer = replicate (bufferLength signer) 0
+
+randomBuffer :: Signer -> IO [Word8]
+randomBuffer signer = replicateM (bufferLength signer) randomIO
+
+bufferLength :: Signer -> Int
+bufferLength signer = keyLength signer `div` byteSize
+                    - length header
+                    - hashSize `div` byteSize
+                    - length trail
+
+buildSignature :: Signer -> Vector Int32 -> String
+buildSignature signer signature = concat
+    [ if significance signature > pos `div` ( intSize `div` shortSize )
+      then
+        let
+          shift = if 0 == pos `mod` ( intSize `div` shortSize ) then 0 else shortSize :: Int
+          letter = signature ! fromIntegral (pos `div` ( intSize `div` shortSize ))
+        in printf "%04x" (fromIntegral (letter `logicalShiftRight` shift) :: Int16)
+      else
+        printf "%04x" ( 0 :: Int )
+      | pos <- [0, 1 .. keyLength signer `div` shortSize - 1]
+    ]
+
+signBytes :: Signer -> [Word8] -> B.ByteString -> Vector Int32
+signBytes signer rndBuffer message = exponentation (fromList $ cast blob) (expon signer) (modulus signer)
+  where blob = header ++ B.unpack ( hash message ) ++ rndBuffer ++ trail
+
+cast :: Integral a => [a] -> [Int32]
+cast xs = map ( fromOctets . reverse ) $ chunksOf 4 xs
+
+fromOctets :: Integral a => [a] -> Int32
+fromOctets = foldl' accum 0
+  where accum a o = (a `shiftL` 8) .|. fromIntegral o
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,113 @@
+module Main where
+
+import           Data.Int        (Int32)
+
+import           Data.Digest.WebMoney.Algebra
+import           Data.Digest.WebMoney.Montgomery
+import           Data.Digest.WebMoney.Signer
+
+import qualified Data.ByteString as B (pack)
+import           Data.Vector     (Vector, fromList)
+import           Test.Hspec
+
+signer :: Signer
+signer = newSigner
+      [125, -78, 54, 8, -67, -98, -116, 15, -88, -19, -82, 2, 108, 41, 89, 27, 34, -80, -49, 55, -62, -2, -89, 19, -79, 41, -65, 11, -120, 87, -64, 89, 27, -67, -92, -23, -77, 29, 46, -52, 95, -96, -113, -32, -99, -26, -95, -27, 12, 108, -18, -101, -46, 103, -29, -112, 16, -50, -67, 88, -2, -86, 40, -69, -65, 0]
+      [85, 31, -1, -98, -62, -2, -13, 76, -48, 2, 103, -66, -71, -41, -49, 106, 114, 99, 102, -28, -55, 93, -75, 98, 119, 68, 60, 66, 123, 101, -99, -16, 30, 118, -66, 123, 77, -103, -59, -116, -92, -90, 9, 98, 108, -92, -111, -108, -18, -62, -20, -35, 83, -11, 115, 70, -20, -42, -31, -80, -44, -53, -42, -16, 110, 5]
+
+main :: IO ()
+main = hspec $ do
+  describe "Signer" $ do
+    it "signBytes" $ do
+      ( signBytes
+        signer
+        (replicate 46 0)
+        (B.pack [109, 101, 115, 115, 97, 103, 101]))
+      `shouldBe`
+      (fromList [1420192846, -514184231, -858634323, -658448382, 52207915, -437346630, -666018004, -2043170752, 1241009014, 879655623, -1601019036, -1233902541, 17763596, -1169198420, 1209444264, 223198074, 1327])
+    it "buildSignature" $ do
+      ( buildSignature
+        signer
+        (fromList [1420192846, -514184231, -858634323, -658448382, 52207915, -437346630, -666018004, -2043170752, 1241009014, 879655623, -1601019036, -1233902541, 17763596, -1169198420, 1209444264, 223198074, 1327]))
+      `shouldBe`
+      "6c4e54a62bd9e15a47adccd2e002d8c0a12b031c9ebae5ee5f2cd84db04086374b7649f87ac7346e6364a0922433b6740d0c010f72acba4fa7a84816bb7a0d4d052f"
+    it "blank" $ do
+      ( signUnsafe
+        signer
+        "                                                                  ")
+      `shouldBe`
+      "d5295349168cdb6fdbdb9c1984836cc1db939dda1dd6e18112db007154db8065b25583c71bd952f88f24f2515fbc4bb3037e5cedd5ef33cd336960b6a655537304bf"
+    it "symbols" $ do
+      ( signUnsafe
+        signer
+        "!@#$%^&*()-=_{}[]\"'1234567890.,%:?<>/\\")
+      `shouldBe`
+      "61b8260b9d5d7cce8e1c0262f0722b8114a7b3e233d76f4b93d08933a89d23d73acfb2d960966b0c175a8604e7c71c391b7f2a64743b3c3b11c5d07ed224445300b3"
+    it "specSymbols" $ do
+      ( signUnsafe
+        signer
+        "\n\n\n\n\t\t\t\t\n\n\n\n")
+      `shouldBe`
+      "c37c96e02f8678a13a43a3bade0ba77275da0d9869d18856daaf4b413106a9bfea74054462d851a0c5a2596dbf9c371284c61b3741b35343c91d2978db328c2f00c9"
+  describe "Montgomery" $ do
+    it "multiplication" $ do
+      ( multiplication
+        (fromList [-1476460488, -1118323957, 1120423587, -1596341302, 43673, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1])
+        (fromList [887153153, 1663090240, 2023161700, -1792225711, -2037349738, 431338827, 936480955, 1341971236, 1200687489, 795067959, -325209191, 1292772351, 905855361, -230695685, -1798442900, 313894787, 1080, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
+        (fromList [-1627447467, 1291058882, -1100545328, 1792006073, -463051918, 1656053193, 1111245943, -258120325, 2076079646, -1933207219, 1644799652, -1802394516, -571686162, 1182004563, -1327376660, -254358572, 1390])
+        3397655043)
+      `shouldBe`
+      (fromList [629254954, -1321769800, 1900654924, 2056436422, 1791614990, 475542676, 547583796, 1358516486, 1650142553, -1578392030, -1395505261, 1389520284, -494774702, 536840267, 790850704, -1567780526, 64, 0])
+    it "exponentaion" $ do
+      ( exponentation
+        (fromList [-1476460488, -1118323957, 1120423587, -1596341302, 43673, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1])
+        (fromList [137802365, 260873917, 45018536, 458828140, 936357922, 329776834, 197077425, 1505777544, -375079653, -869392973, -527458209, -442374499, -1678873588, -1864144942, 1488834064, -1154962690, 191])
+        (fromList [-1627447467, 1291058882, -1100545328, 1792006073, -463051918, 1656053193, 1111245943, -258120325, 2076079646, -1933207219, 1644799652, -1802394516, -571686162, 1182004563, -1327376660, -254358572, 1390]) )
+      `shouldBe`
+      (fromList [1420192846, -514184231, -858634323, -658448382, 52207915, -437346630, -666018004, -2043170752, 1241009014, 879655623, -1601019036, -1233902541, 17763596, -1169198420, 1209444264, 223198074, 1327])
+  describe "Algebra" $ do
+    it "reminder" $ do
+      ( remainder
+        (fromList [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1])
+        (fromList [-1627447467, 1291058882, -1100545328, 1792006073, -463051918, 1656053193, 1111245943, -258120325, 2076079646, -1933207219, 1644799652, -1802394516, -571686162, 1182004563, -1327376660, -254358572, 1390]) )
+      `shouldBe`
+      (fromList [1602626370, 296859807, -1933305503, 1018504923, 2106164031, -469194707, 1605789230, 1881367321, 127989226, -1751912682, 2087886977, 1741983051, -1090589209, 1428161158, 647631356, -121976833, 889, 0])
+    it "getBitsCount" $ do
+      ( getBitsCount
+        (fromList [-1627447467, 1291058882, -1100545328, 1792006073, -463051918, 1656053193, 1111245943, -258120325, 2076079646, -1933207219, 1644799652, -1802394516, -571686162, 1182004563, -1327376660, -254358572, 1390] :: Vector Int32))
+      `shouldBe`
+      523
+    it "getBitsCount" $ do
+      ( getBitsCount
+        (fromList [358612992, 665591836, -1510579840, 147338015, -1850563067, 1187214131, 1897113940, 1352120439, 1008856147, -699365327, 728655692, 1920188107, -1574081077, 1434731111, 578236801, -2056657979, 1377953062, 0] :: Vector Int32))
+      `shouldBe`
+      543
+    it "comparison with equal length" $ do
+      ( compareLists
+        (fromList [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1])
+        (fromList [-1627447467, 1291058882, -1100545328, 1792006073, -463051918, 1656053193, 1111245943, -258120325, 2076079646, -1933207219, 1644799652, -1802394516, -571686162, 1182004563, -1327376660, -254358572, 1390]) )
+      `shouldBe`
+      GT
+    it "comparison with different length" $ do
+      ( compareLists
+        (fromList [358612992, 665591836, -1510579840, 147338015, -1850563067, 1187214131, 1897113940, 1352120439, 1008856147, -699365327, 728655692, 1920188107, -1574081077, 1434731111, 578236801, -2056657979, 1377953062, 0])
+        (fromList [-1627447467, 1291058882, -1100545328, 1792006073, -463051918, 1656053193, 1111245943, -258120325, 2076079646, -1933207219, 1644799652, -1802394516, -571686162, 1182004563, -1327376660, -254358572, 1390]) )
+      `shouldBe`
+      GT
+    it "sub" $ do
+      ( sub
+        (fromList [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1])
+        (fromList [-358612992, -665591837, 1510579839, -147338016, 1850563066, -1187214132, -1897113941, -1352120440, -1008856148, 699365326, -728655693, -1920188108, 1574081076, -1434731112, -578236802, 2056657978, -1377953063, 0]) )
+      `shouldBe`
+      (fromList [358612992, 665591836, -1510579840, 147338015, -1850563067, 1187214131, 1897113940, 1352120439, 1008856147, -699365327, 728655692, 1920188107, -1574081077, 1434731111, 578236801, -2056657979, 1377953062, 0])
+    it "shiftRight" $ do
+      ( shiftRight
+        (fromList [-717225984, -1331183673, -1273807617, -294676032, -593841163, 1920539032, 500739415, 1590726417, -2017712295, 1398730653, -1457311386, 454591081, -1146805143, 1425505072, -1156473603, -181651339, 1539061170, 1]))
+      `shouldBe`
+      (fromList [-358612992, -665591837, 1510579839, -147338016, 1850563066, -1187214132, -1897113941, -1352120440, -1008856148, 699365326, -728655693, -1920188108, 1574081076, -1434731112, -578236802, 2056657978, -1377953063, 0])
+    it "shift" $ do
+      ( shift
+        (fromList [-1627447467, 1291058882, -1100545328, 1792006073, -463051918, 1656053193, 1111245943, -258120325, 2076079646, -1933207219, 1644799652, -1802394516, -571686162, 1182004563, -1327376660, -254358572, 1390])
+        22)
+      `shouldBe`
+      (fromList [-717225984, -1331183673, -1273807617, -294676032, -593841163, 1920539032, 500739415, 1590726417, -2017712295, 1398730653, -1457311386, 454591081, -1146805143, 1425505072, -1156473603, -181651339, 1539061170, 1])
