packages feed

botan-low-0.0.2.0: test/Botan/Low/MPISpec.hs

module Main where

import Test.Prelude

import Botan.Low.MPI
import Botan.Low.RNG

-- NOTE: These unit tests are mostly checking that the functions are bound correctly.
--  These are kind of crappy and repetative, but that also exposes what needs to be done
--  in the higher libraries to make things ergonomic.

main :: IO ()
main = hspec $ do
    it "can initialize a mutable MPI reference" $ do
        mp <- mpInit
        pass
    it "can set the value from an int" $ do
        mp <- mpInit
        mpSetFromInt mp 512
        pass
    it "can set the value from another reference" $ do
        mp <- mpInit
        mpSetFromInt mp 512
        mp' <- mpInit
        mpSetFromMP mp' mp
        isEqual <- mpEqual mp mp'
        isEqual `shouldBe` True
        pass
    it "can be converted to a hex string" $ do
        mp <- mpInit
        mpSetFromInt mp 512
        hex <- mpToHex mp
        hex `shouldBe` "0x0200"
        hex' <- mpToStr mp 16
        hex' `shouldBe` "0x0200"
        pass
    it "can be converted to a decimal string" $ do
        mp <- mpInit
        mpSetFromInt mp 512
        dec <- mpToStr mp 10
        dec `shouldBe` "512"
        pass
    it "can be cleared to zero" $ do
        mp <- mpInit
        mpSetFromInt mp 512
        mpClear mp
        zero <- mpInit
        mpSetFromInt zero 0
        isClear <- mpEqual mp zero
        isClear `shouldBe` True
        pass
    it "can copy the value to a new reference" $ do
        mp <- mpInit
        mpSetFromInt mp 512
        mp' <- mpCopy mp
        isEqual <- mpEqual mp mp'
        isEqual `shouldBe` True
        pass
    it "can set the value from a decimal string" $ do
        mp <- mpInit
        mpSetFromStr mp "512"
        mp' <- mpInit
        mpSetFromInt mp' 512
        isEqualStr <- mpEqual mp mp'
        isEqualStr `shouldBe` True
        mpSetFromRadixStr mp "512" 10
        isEqualRadixStr <- mpEqual mp mp'
        isEqualRadixStr `shouldBe` True
        pass
    it "can set the value from a hex string" $ do
        mp <- mpInit
        mpSetFromStr mp "0x0200"
        mp' <- mpInit
        mpSetFromInt mp' 512
        isEqualStr <- mpEqual mp mp'
        isEqualStr `shouldBe` True
        mpSetFromRadixStr mp "0200" 16 -- NOTE: Lack of "0x" prefix
        isEqualRadixStr <- mpEqual mp mp'
        isEqualRadixStr `shouldBe` True
        pass
    it "has a number of bits" $ do
        mp <- mpInit
        mpSetFromStr mp "512"
        n <- mpNumBits mp
        n `shouldBe` 10
        pass
    it "has a number of bytes" $ do
        mp <- mpInit
        mpSetFromStr mp "512"
        n <- mpNumBytes mp
        n `shouldBe` 2
        pass
    it "can be converted to a bytestring" $ do
        mp <- mpInit
        mpSetFromInt mp 512
        bin <- mpToBin mp
        bin `shouldBe` "\STX\NUL" -- NOTE: 0b 0000 0010 0000 0000
        pass
    it "can set the value from a bytestring" $ do
        mp <- mpInit
        mpFromBin mp "\STX\NUL"
        mp' <- mpInit
        mpSetFromInt mp' 512
        isEqual <- mpEqual mp mp'
        isEqual `shouldBe` True
        pass
    it "can be converted to a 32-bit word" $ do
        mp <- mpInit
        mpSetFromInt mp 512
        w <- mpToWord32 mp
        w `shouldBe` 512
        pass
    -- NOTE: Returns whether x >= 0, not x > 0 because 0 is treated as positive.
    it "can check if a value is positive" $ do
        n <- mpInit
        mpSetFromInt n (-512)
        nIsPositive <- mpIsPositive n
        nIsPositive `shouldBe` False
        z <- mpInit
        mpSetFromInt z 0
        zIsPositive <- mpIsPositive z
        zIsPositive `shouldBe` True
        p <- mpInit
        mpSetFromInt p 512
        pIsPositive <- mpIsPositive p
        pIsPositive `shouldBe` True
        pass
    it "can check if a value is negative" $ do
        n <- mpInit
        mpSetFromInt n (-512)
        nIsPositive <- mpIsNegative n
        nIsPositive `shouldBe` True
        z <- mpInit
        mpSetFromInt z 0
        zIsPositive <- mpIsNegative z
        zIsPositive `shouldBe` False
        p <- mpInit
        mpSetFromInt p 512
        pIsPositive <- mpIsNegative p
        pIsPositive `shouldBe` False
        pass
    it "can flip the sign of a value" $ do
        p <- mpInit
        mpSetFromInt p 512
        n <- mpInit
        mpSetFromInt n (-512)
        mpFlipSign p
        isEqual <- mpEqual p n
        pass
    it "can check whether a value is zero" $ do
        mp <- mpInit
        mpSetFromInt mp 512
        isZeroA <- mpIsZero mp
        isZeroA `shouldBe` False
        mpSetFromInt mp 0
        isZeroB <- mpIsZero mp
        isZeroB `shouldBe` True
        mpSetFromInt mp (-512)
        isZeroC <- mpIsZero mp
        isZeroC `shouldBe` False
        pass
    it "can add a 32-bit word to the value and store the result" $ do
        mp <- mpInit
        mpSetFromInt mp 512
        mp' <- mpInit
        mpAddWord32 mp' mp 512
        result <- mpToWord32 mp'
        result `shouldBe` 1024
        pass
    it "can subtract a 32-bit word from the value and store the result" $ do
        mp <- mpInit
        mpSetFromInt mp 512
        mp' <- mpInit
        mpSubWord32 mp' mp 512
        result <- mpToWord32 mp'
        result `shouldBe` 0
        pass
    it "can add one value to another and store the result" $ do
        a <- mpInit
        mpSetFromInt a 512
        b <- mpInit
        mpSetFromInt b 512
        c <- mpInit
        mpAdd c a b
        result <- mpToWord32 c
        result `shouldBe` 1024
        pass
    it "can subtract one value from another and store the result" $ do
        a <- mpInit
        mpSetFromInt a 512
        b <- mpInit
        mpSetFromInt b 512
        c <- mpInit
        mpSub c a b
        result <- mpToWord32 c
        result `shouldBe` 0
        pass
    it "can multiply one value by another and store the result" $ do
        a <- mpInit
        mpSetFromInt a 512
        b <- mpInit
        mpSetFromInt b 512
        c <- mpInit
        mpMul c a b
        result <- mpToWord32 c
        result `shouldBe` 262144
        pass
    it "can divide one value by another and store the result" $ do
        a <- mpInit
        mpSetFromInt a 512
        b <- mpInit
        mpSetFromInt b 512
        q <- mpInit
        r <- mpInit
        mpDiv q r a b
        quotient <- mpToWord32 q
        quotient `shouldBe` 1
        remainder <- mpToWord32 r
        remainder `shouldBe` 0
        pass
    it "can perform multiplication and modulus at the same time and store the result" $ do
        r <- mpInit
        x <- mpInit
        mpSetFromInt x 2
        y <- mpInit
        mpSetFromInt y 4
        mod <- mpInit
        mpSetFromInt mod 7
        mpModMul r x y mod
        result <- mpToWord32 r
        result `shouldBe` 1
        pass
    it "can check whether two values are equal" $ do
        a <- mpInit
        mpSetFromInt a 512
        b <- mpInit
        mpSetFromInt b 512
        c <- mpInit
        mpSetFromInt c 256
        abEqual <- mpEqual a b
        abEqual `shouldBe` True
        bcEqual <- mpEqual b c
        bcEqual `shouldBe` False
        pass
    it "can compare two values for order" $ do
        n <- mpInit
        mpSetFromInt n (-1)
        z <- mpInit
        mpSetFromInt z 0
        p <- mpInit
        mpSetFromInt p 1
        nz <- mpCmp n z
        nz `shouldBe` -1
        zp <- mpCmp z p
        zp `shouldBe` -1
        np <- mpCmp n p
        np `shouldBe` -1
        nn <- mpCmp n n
        nn `shouldBe` 0
        zz <- mpCmp z z
        zz `shouldBe` 0
        pp <- mpCmp p p
        pp `shouldBe` 0
        pz <- mpCmp p z
        pz `shouldBe` 1
        zn <- mpCmp z n
        zn `shouldBe` 1
        pn <- mpCmp p n
        pn `shouldBe` 1
        pass
    it "can swap the values of two references" $ do
        a <- mpInit
        mpSetFromInt a 512
        b <- mpInit
        mpSetFromInt b 256
        mpSwap a b
        a' <- mpToWord32 a
        a' `shouldBe` 256
        b' <- mpToWord32 b
        b' `shouldBe` 512
        pass
    it "can perform exponentiation and modulus at the same time and store the result" $ do
        r <- mpInit
        x <- mpInit
        mpSetFromInt x 2
        y <- mpInit
        mpSetFromInt y 4
        mod <- mpInit
        mpSetFromInt mod 7
        mpPowMod r x y mod
        result <- mpToWord32 r
        result `shouldBe` 2
        pass
    it "can perform a left shift and store the result" $ do
        mp <- mpInit
        result <- mpInit
        mpSetFromInt mp 512
        mpLeftShift result mp 3
        r <- mpToWord32 result
        r `shouldBe` 4096
        pass
    it "can perform a right shift and store the result" $ do
        mp <- mpInit
        result <- mpInit
        mpSetFromInt mp 512
        mpRightShift result mp 3
        r <- mpToWord32 result
        r `shouldBe` 64
        pass
    it "can compute the modular inverse" $ do
        result <- mpInit
        x <- mpInit
        mpSetFromInt x 29
        mod <- mpInit
        mpSetFromInt mod 65535
        -- x needs to be smaller than mod
        mpModInverse result x mod
        r <- mpToWord32 result
        r `shouldBe` 13559
        pass
    it "can generate a number of random bits" $ do
        mp <- mpInit
        rng <- rngInit "system"
        mpRandBits mp rng 9
        r <- mpToWord32 mp
        0 <= r && r < 512 `shouldBe` True
        pass
    it "can generate a random value within a range [lower,upper)" $ do
        result <- mpInit
        rng <- rngInit "system"
        lower <- mpInit
        mpSetFromInt lower 4
        upper <- mpInit
        mpSetFromInt upper 8
        mpRandRange result rng lower upper
        isGTELower <- (<= 0) <$> mpCmp lower result
        isGTELower `shouldBe` True
        isLTUpper <- (== -1) <$> mpCmp result upper
        isLTUpper `shouldBe` True
        pass
    it "can calculate the greatest common divisor of a value" $ do
        r <- mpInit
        x <- mpInit
        mpSetFromInt x 35
        y <- mpInit
        mpSetFromInt y 21
        mpGCD r x y
        result <- mpToWord32 r
        result `shouldBe` 7
        pass
    it "can probabilistically calculate whether a value is prime" $ do
        mp <- mpInit
        rng <- rngInit "system"
        mpSetFromInt mp 131
        isPrime131 <- mpIsPrime mp rng 100
        isPrime131 `shouldBe` True
        mpSetFromInt mp 133
        isPrime133 <- mpIsPrime mp rng 100
        isPrime133 `shouldBe` False
        pass
    it "can get the value of a single bit" $ do
        mp <- mpInit
        mpSetFromInt mp 512
        f <- mpGetBit mp 8
        f `shouldBe` False
        t <- mpGetBit mp 9
        t `shouldBe` True
        pass
    it "can set the value of a single bit (set it to 1)" $ do
        mp <- mpInit
        mpSetFromInt mp 512
        mpSetBit mp 8
        result <- mpToWord32 mp
        result `shouldBe` 768
        pass
    it "can clear the value of a single bit (set it to 0)" $ do
        mp <- mpInit
        mpSetFromInt mp 768
        mpClearBit mp 8
        result <- mpToWord32 mp
        result `shouldBe` 512
        pass