{-# OPTIONS_HADDOCK hide #-}
module Network.TLS.Crypto
	( HashType(..)
	, HashCtx

	-- * incremental interface with algorithm type wrapping for genericity
	, initHash
	, updateHash
	, finalizeHash

	-- * single pass lazy bytestring interface for each algorithm
	, hashMD5
	, hashSHA1
	-- * incremental interface for each algorithm
	, initMD5
	, updateMD5
	, finalizeMD5
	, initSHA1
	, updateSHA1
	, finalizeSHA1

	-- * key exchange generic interface
	, PublicKey(..)
	, PrivateKey(..)
	, kxEncrypt
	, kxDecrypt
	, KxError(..)
	) where

import qualified Crypto.Hash.SHA1 as SHA1
import qualified Crypto.Hash.MD5 as MD5
import qualified Data.ByteString as B
import Data.ByteString (ByteString)
import qualified Crypto.Cipher.RSA as RSA
import Crypto.Random (CryptoRandomGen)

data PublicKey = PubRSA RSA.PublicKey

data PrivateKey = PrivRSA RSA.PrivateKey

instance Show PublicKey where
	show (_) = "PublicKey(..)"

instance Show PrivateKey where
	show (_) = "privateKey(..)"

data KxError = RSAError RSA.Error
	deriving (Show)

data HashCtx =
	  SHA1 !SHA1.Ctx
	| MD5 !MD5.Ctx

data KeyXchg =
	  KxRSA RSA.PublicKey RSA.PrivateKey
	deriving (Show)

instance Show HashCtx where
	show (SHA1 _) = "sha1"
	show (MD5 _) = "md5"

data HashType = HashTypeSHA1 | HashTypeMD5

{- MD5 -}

initMD5 :: MD5.Ctx
initMD5 = MD5.init

updateMD5 :: MD5.Ctx -> ByteString -> MD5.Ctx
updateMD5 = MD5.update

finalizeMD5 :: MD5.Ctx -> ByteString
finalizeMD5 = MD5.finalize

hashMD5 :: ByteString -> ByteString
hashMD5 = MD5.hash

{- SHA1 -}

initSHA1 :: SHA1.Ctx
initSHA1 = SHA1.init

updateSHA1 :: SHA1.Ctx -> ByteString -> SHA1.Ctx
updateSHA1 = SHA1.update

finalizeSHA1 :: SHA1.Ctx -> ByteString
finalizeSHA1 = SHA1.finalize

hashSHA1 :: ByteString -> ByteString
hashSHA1 = SHA1.hash

{- generic Hashing -}

initHash :: HashType -> HashCtx
initHash HashTypeSHA1 = SHA1 (initSHA1)
initHash HashTypeMD5  = MD5 (initMD5)

updateHash :: HashCtx -> B.ByteString -> HashCtx
updateHash (SHA1 ctx) = SHA1 . updateSHA1 ctx
updateHash (MD5 ctx)  = MD5 . updateMD5 ctx

finalizeHash :: HashCtx -> B.ByteString
finalizeHash (SHA1 ctx) = finalizeSHA1 ctx
finalizeHash (MD5 ctx)  = finalizeMD5 ctx

{- key exchange methods encrypt and decrypt for each supported algorithm -}
generalizeRSAError :: Either RSA.Error a -> Either KxError a
generalizeRSAError (Left e)  = Left (RSAError e)
generalizeRSAError (Right x) = Right x

kxEncrypt :: CryptoRandomGen g => g -> PublicKey -> ByteString -> Either KxError (ByteString, g)
kxEncrypt g (PubRSA pk) b = generalizeRSAError $ RSA.encrypt g pk b

kxDecrypt :: PrivateKey -> ByteString -> Either KxError ByteString
kxDecrypt (PrivRSA pk) b  = generalizeRSAError $ RSA.decrypt pk b