packages feed

WMSigner (empty) → 0.1.0.0

raw patch · 8 files changed

+607/−0 lines, 8 filesdep +basedep +base64-bytestringdep +binarysetup-changed

Dependencies added: base, base64-bytestring, binary, bytestring, cryptohash, directory, hspec, lens, mtl, random, split, vector

Files

+ LICENSE view
@@ -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.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ WMSigner.cabal view
@@ -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
+ src/Data/Digest/WebMoney.hs view
@@ -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
+ src/Data/Digest/WebMoney/Algebra.hs view
@@ -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 )
+ src/Data/Digest/WebMoney/Montgomery.hs view
@@ -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
+ src/Data/Digest/WebMoney/Signer.hs view
@@ -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
+ test/Spec.hs view
@@ -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])