module Crypto.PubKey.ElGamal
( Params
, PublicNumber
, PrivateNumber
, EphemeralKey(..)
, SharedKey
, Signature
, generatePrivate
, generatePublic
, encryptWith
, encrypt
, decrypt
, signWith
, sign
, verify
) where
import Data.Maybe (fromJust)
import Crypto.Internal.Imports
import Crypto.Internal.ByteArray (ByteArrayAccess)
import Crypto.Number.ModArithmetic (expSafe, expFast, inverse)
import Crypto.Number.Generate (generateMax)
import Crypto.Number.Serialize (os2ip)
import Crypto.Number.Basic (gcde)
import Crypto.Random.Types
import Crypto.PubKey.DH (PrivateNumber(..), PublicNumber(..), Params(..), SharedKey(..))
import Crypto.Hash
data Signature = Signature (Integer, Integer)
newtype EphemeralKey = EphemeralKey Integer
deriving (NFData)
generatePrivate :: MonadRandom m => Integer -> m PrivateNumber
generatePrivate q = PrivateNumber <$> generateMax q
generateEphemeral :: MonadRandom m => Integer -> m EphemeralKey
generateEphemeral q = toEphemeral <$> generatePrivate q
where toEphemeral (PrivateNumber n) = EphemeralKey n
generatePublic :: Params -> PrivateNumber -> PublicNumber
generatePublic (Params p g _) (PrivateNumber a) = PublicNumber $ expSafe g a p
encryptWith :: EphemeralKey -> Params -> PublicNumber -> Integer -> (Integer,Integer)
encryptWith (EphemeralKey b) (Params p g _) (PublicNumber h) m = (c1,c2)
where s = expSafe h b p
c1 = expSafe g b p
c2 = (s * m) `mod` p
encrypt :: MonadRandom m => Params -> PublicNumber -> Integer -> m (Integer,Integer)
encrypt params@(Params p _ _) public m = (\b -> encryptWith b params public m) <$> generateEphemeral q
where q = p1
decrypt :: Params -> PrivateNumber -> (Integer, Integer) -> Integer
decrypt (Params p _ _) (PrivateNumber a) (c1,c2) = (c2 * sm1) `mod` p
where s = expSafe c1 a p
sm1 = fromJust $ inverse s p
signWith :: (ByteArrayAccess msg, HashAlgorithm hash)
=> Integer
-> Params
-> PrivateNumber
-> hash
-> msg
-> Maybe Signature
signWith k (Params p g _) (PrivateNumber x) hashAlg msg
| k >= p1 || d > 1 = Nothing
| s == 0 = Nothing
| otherwise = Just $ Signature (r,s)
where r = expSafe g k p
h = os2ip $ hashWith hashAlg msg
s = ((h x*r) * kInv) `mod` (p1)
(kInv,_,d) = gcde k (p1)
sign :: (ByteArrayAccess msg, HashAlgorithm hash, MonadRandom m)
=> Params
-> PrivateNumber
-> hash
-> msg
-> m Signature
sign params@(Params p _ _) priv hashAlg msg = do
k <- generateMax (p1)
case signWith k params priv hashAlg msg of
Nothing -> sign params priv hashAlg msg
Just sig -> return sig
verify :: (ByteArrayAccess msg, HashAlgorithm hash)
=> Params
-> PublicNumber
-> hash
-> msg
-> Signature
-> Bool
verify (Params p g _) (PublicNumber y) hashAlg msg (Signature (r,s))
| or [r <= 0,r >= p,s <= 0,s >= (p1)] = False
| otherwise = lhs == rhs
where h = os2ip $ hashWith hashAlg msg
lhs = expFast g h p
rhs = (expFast y r p * expFast r s p) `mod` p