module Crypto.Cipher.ElGamal
( Params
, PublicNumber
, PrivateNumber
, SharedKey
, generatePrivate
, generatePublic
, encryptWith
, encrypt
, decrypt
) where
import Number.ModArithmetic (exponantiation, inverse)
import Number.Generate (generateOfSize)
import Crypto.Types.PubKey.DH
import Crypto.Random
import Control.Arrow (first)
import Control.Applicative ((<$>))
import Data.Maybe (fromJust)
generatePrivate :: CryptoRandomGen g => g -> Int -> Either GenError (PrivateNumber, g)
generatePrivate rng bits = either Left (Right . first PrivateNumber) $ generateOfSize rng bits
generatePublic :: Params -> PrivateNumber -> PublicNumber
generatePublic (p,g) (PrivateNumber a) = PublicNumber $ exponantiation g a p
encryptWith :: PrivateNumber -> Params -> PublicNumber -> Integer -> (Integer,Integer)
encryptWith (PrivateNumber b) (p,g) (PublicNumber h) m = (c1,c2)
where s = exponantiation h b p
c1 = exponantiation g b p
c2 = (s * m) `mod` p
encrypt :: CryptoRandomGen g => g -> Params -> PublicNumber -> Integer -> Either GenError ((Integer,Integer), g)
encrypt rng params public m = (\(b,rng') -> (encryptWith b params public m,rng')) <$> generatePrivate rng 1024
decrypt :: Params -> PrivateNumber -> (Integer, Integer) -> Integer
decrypt (p,_) (PrivateNumber a) (c1,c2) = (c2 * sm1) `mod` p
where s = exponantiation c1 a p
sm1 = fromJust $ inverse s p