{-# LANGUAGE TypeSynonymInstances #-}
-- |
-- Maintainer: Henning Guenther
--
-- This module is a helper for signature checking. It gives the text
-- that needs to be hashed in order to create or verify a signature.
module Network.AdHoc.Signature
	(Signature(..)
	,SignType(..)
	,SignatureStatus(..)
	,NoSignature
	,InternalSignature
	,ExternalSignature
	,ToInternalSignature(..)
	,getHashString
	,verifySignature
	) where

import Data.ByteString hiding (foldl,concatMap)
import Text.XML.HaXml.Types
import Codec.Binary.UTF8.String (encodeString)
import qualified Network.GnuTLS.X509 as X509

-- | A raw signature.
data Signature = Signature
	{sign_type :: SignType   -- ^ The type of the signature
	,signature :: ByteString -- ^ The actual signature
	}
	deriving (Show,Eq)

-- | Specifies the signature algorithm used to sign a message.
data SignType
	= MD5 -- ^ MD5 has been used to sign the message
	| SignUnknown String -- ^ An unknown signature algorithm was used
	deriving (Show,Eq)

-- | This type represents the possible results of verifying a signature.
data SignatureStatus
	= SignatureOK    -- ^ The signature was valid
	| SignatureWrong -- ^ The signature was invalid
	| CertificateMissing (X509.Certificate -> SignatureStatus) -- ^ The signature could not be
		-- checked, becuase a 'X509.Certificate' was missing. The function
		-- is a callback to be called when the missing certificate has been
		-- acquired.

instance Show SignatureStatus where
	show SignatureOK = "SignatureOK"
	show SignatureWrong = "SignatureWrong"
	show (CertificateMissing _) = "CertificateMissing"

instance Eq SignatureStatus where
	SignatureOK == SignatureOK		= True
	SignatureWrong == SignatureWrong	= True
	_ == _ 					= False

-- | No signature has been given.
type NoSignature = ()

-- | The message has been locally created, so we know the private key.
type InternalSignature = Either X509.PrivateKey ExternalSignature

-- | The signature has been received via network. The signature might be
--   knwon or not.
type ExternalSignature = Maybe (Signature,SignatureStatus)

-- | A class of types that have an internal signature status.
class ToInternalSignature s where
	toInternal :: s -> InternalSignature

instance ToInternalSignature NoSignature where
	toInternal _ = Right Nothing

instance ToInternalSignature ExternalSignature where
	toInternal = Right

instance ToInternalSignature InternalSignature where
	toInternal = id

-- | Given a certificate, this function verifies the Signature of a 'String'.
verifySignature :: String -> Signature -> X509.Certificate -> SignatureStatus
verifySignature str sig cert = case X509.verifySignature cert (encodeString str) (signature sig) of
	Left _ -> CertificateMissing (verifySignature (encodeString str) sig)
	Right False -> SignatureWrong
	Right True -> SignatureOK

-- | Calculates the concatenation of all text nodes in the list of contents, except for receiver elements
getHashString :: [Content i] -> String
getHashString = concatMap getHashStringContent

getHashStringContent :: Content i -> String
getHashStringContent (CElem (Elem "receiver" _ _) _) = "" -- As given by standard
getHashStringContent (CElem (Elem _ _ conts) _) = getHashString conts
getHashStringContent (CString _ str _) = str
getHashStringContent _ = ""

-- getHashStringDocument :: Document i -> String
-- getHashStringDocument (Document _ _ (Elem _ _ conts) _) = getHashString conts