eccrypto-0.1.0: src/Crypto/FPrime.hs
-----------------------------------------------------------------------------
-- |
-- Module : Crypto.FPrime
-- Copyright : (c) Marcel Fourné 20[14..]
-- License : BSD3
-- Maintainer : Marcel Fourné (haskell@marcelfourne.de)
-- Stability : experimental
-- Portability : Good
--
-- Reimplementation of Bigint math for crypto use
-- Re Timing-Attacks: We depend on (==) being resistant for Integer.
--
-----------------------------------------------------------------------------
{-# OPTIONS_GHC -O2 -feager-blackholing #-}
{-# LANGUAGE Safe, DeriveDataTypeable #-}
module Crypto.FPrime ( FPrime()
, eq
, add
, addr
, sub
, subr
, neg
, negr
, shift
, mul
, mulr
, redc
, square
, pow
, inv
, testBit
, fromInteger
, toInteger
)
where
import safe Prelude (Eq,Show,(==),(&&),Integer,Int,show,Bool(False,True),(++),($),fail,undefined,(+),(-),(*),(^),abs,mod,Integral,otherwise,(<),div,not,String,flip,takeWhile,length,iterate,(>),(<=),(>=),maxBound,rem,quot,quotRem,error,even)
import safe qualified Prelude as P (fromInteger,toInteger)
import safe qualified Data.Bits as B (Bits(..),testBit)
import safe Data.Typeable(Typeable)
import safe qualified Data.Array as A
import safe qualified Data.Array.Unboxed as U
import safe qualified Data.Word as W (Word)
import safe Crypto.Common
-- | FPrime consist of an exact length of meaningful bits, an indicator if the number is negative and a representation of bits in a possibly larger Vector of Words
-- | Note: The vectors use small to large indices, but the Data.Word endianness is of no concern as it is hidden by Data.Bits
-- | Be careful with those indices! The usage of quotRem with them has caused some headache.
data FPrime = FPrime {-# UNPACK #-} !Int !Bool !(U.UArray W.Word)
deriving (Show,Typeable)
-- TODO: think of efficient radix-choices, f.e. 25519:-> 51*5
radix :: Int
radix = wordSize - 2
halfradix :: Int
halfradix = radix `div` 2
radmax :: Int
radmax = 2^radix-1
sizeinradwords :: Int -> Int
sizeinradwords 0 = 1
sizeinradwords l = let (w,r) = abs l `quotRem` radix
in if r > 0 then w + 1 else w
-- | a == b
eq :: FPrime -> FPrime -> Bool
eq (FPrime la sa va) (FPrime lb sb vb) = ((la == lb) && (sa == sb)) && undefined -- V.all (== True) (V.zipWith (==) va vb)
-- | a + b
-- TODO: implement add with spare overflow bit, carry-loop
add :: FPrime -> FPrime -> FPrime
add a@(FPrime la sa va) b@(FPrime lb sb vb) =
let fun res = undefined
in fun (FPrime ((if la >= lb then la else lb) + 1) sa $ undefined) -- V.singleton (0::W.Word))
-- | a + b `mod` p
-- TODO: implement addr with spare overflow bit,
addr :: FPrime -> FPrime -> FPrime -> FPrime
addr p@(FPrime lp sp vp) a b =
let summe = add a b
in undefined
-- | a - b, different cost than fpplus but other operation, so no key bit leakage
-- TODO: implement
sub :: FPrime -> FPrime -> FPrime
sub a b = undefined
-- | a - b mod p, different cost than fpplus but other operation, so no key bit leakage
subr :: FPrime -> FPrime -> FPrime -> FPrime
subr p a b = addr p a $ sub p b
-- | (-a)
neg :: FPrime -> FPrime
neg (FPrime la sa va) = FPrime la (not sa) va
-- | (-a) `mod` p
negr :: FPrime -> FPrime -> FPrime
negr p a = redc p $ add p a
-- | internal function
-- TODO: implement shift
shift :: FPrime -> Int -> FPrime
shift a l = undefined
-- | testBit on Words, but highest Bit is overflow, so leave it out
testBit :: FPrime -> Int -> Bool
testBit (FPrime l _ v) i =
(i >= 0 ) && (if i < radix
then flip B.testBit i $ undefined -- V.head v
else (i < l) && (let (index1,index2) = i `quotRem` radix
in flip B.testBit index2 $ (A.!) v index1)
)
-- | modular reduction, a `mod` p
-- TODO: implement redc
redc :: FPrime -> FPrime -> FPrime
redc p a = undefined
-- | internal multiply, x * y
-- TODO: implement mul
mul :: FPrime -> FPrime -> FPrime
mul x@(FPrime l1 s1 _) y@(FPrime l2 s2 _) =
-- computations on half-size words, results word-size
let xh = shift x (-halfradix)
xl = shift (shift x halfradix) (-halfradix)
yh = shift y (-halfradix)
yl = shift (shift y halfradix) (-halfradix)
in undefined
-- | multiply followed by reduction, a * b `mod` p
mulr :: FPrime -> FPrime -> FPrime -> FPrime
mulr p a b = redc p $ mul a b
square :: FPrime -> FPrime -> FPrime
square p a = redc p $ mul a a
pow :: (B.Bits a, Integral a) => FPrime -> FPrime -> a -> FPrime
pow p a k = let binlog = log2len k
ex p1 p2 i
| i < 0 = p1
| not (B.testBit k i) = redc p $ ex (square p p1) (redc p $ mul p1 p2) (i - 1)
| otherwise = redc p $ ex (redc p $ mul p1 p2) (square p p2) (i - 1)
in redc p $ ex a (square p a) (binlog - 2)
inv :: FPrime -> FPrime -> FPrime
inv p a = pow p a (toInteger p - 2)
-- | this is a chunked converter from Integer into eccrypto native format
-- | TODO: implement low-level Integer conversion
fromInteger :: Int -> Integer -> FPrime
fromInteger l i =
let i' = i `rem` (2^l) -- we take only non-negative Integers that fit into l bits
s = i < 0
binlog = log2len i'
helper a =
if a <= P.toInteger radmax
then undefined -- V.singleton $ P.fromInteger a
else let (d,rest) = quotRem a (P.toInteger radmax + 1)
in undefined -- V.singleton (P.fromInteger rest) V.++ helper d
filler b = if binlog == l
then helper b
else let lendiff = sizeinradwords l - sizeinradwords binlog
in helper b undefined -- V.++ V.replicate lendiff 0
in FPrime l s (filler i')
-- | this is a chunked converter from eccrypto native format into Integer
-- | TODO: implement low-level Integer conversion
toInteger :: FPrime -> Integer
toInteger (FPrime la s va) =
if la <= radix
then P.toInteger undefined -- (V.head va) * if s then (-1) else 1
else let len = undefined -- V.length va
helper r z i =
if i > 1
then helper undefined -- (V.tail r) (z + B.shift (P.toInteger $ V.head r) ((len - i) * radix)) (i - 1)
else z + B.shift (P.toInteger $ undefined) -- V.head r) ((len - i) * radix)
in helper va 0 len * if s then (-1) else 1