module Network.DomainAuth.DKIM.Verify (
verifyDKIM, prepareDKIM
) where
import Codec.Crypto.RSA
import qualified Data.ByteString.Lazy.Char8 as L
import Data.Char
import Data.Digest.Pure.SHA
import Network.DomainAuth.DKIM.Btag
import Network.DomainAuth.DKIM.Types
import Network.DomainAuth.Mail
import qualified Network.DomainAuth.Pubkey.Base64 as B
import Network.DomainAuth.Utils
prepareDKIM :: DKIM -> Mail -> L.ByteString
prepareDKIM dkim mail = header
where
dkimField:fields = fieldsFrom dkimFieldKey (mailHeader mail)
hCanon = canonDkimField (dkimHeaderCanon dkim)
canon = removeBtagValue . hCanon
targets = fieldsWith (dkimFields dkim) fields
header = concatCRLFWith hCanon targets +++ canon dkimField
canonDkimField :: DkimCanonAlgo -> Field -> L.ByteString
canonDkimField DKIM_SIMPLE fld = fieldKey fld +++ ": " +++ fieldValueFolded fld
canonDkimField DKIM_RELAXED fld = fieldSearchKey fld +++ ":" +++ canon fld
where
canon = L.dropWhile isSpace . removeTrailingWSP . reduceWSP . L.concat . fieldValue
canonDkimBody :: DkimCanonAlgo -> Body -> L.ByteString
canonDkimBody DKIM_SIMPLE = fromBody . removeTrailingEmptyLine
canonDkimBody DKIM_RELAXED = fromBodyWith relax . removeTrailingEmptyLine
where
relax = removeTrailingWSP . reduceWSP
verifyDKIM :: Mail -> DKIM -> PublicKey -> Bool
verifyDKIM mail dkim pub = bodyHash1 mail == bodyHash2 dkim &&
rsassa_pkcs1_v1_5_verify hashfunc pub cmail sig
where
hashfunc = hashAlgo1 (dkimSigAlgo dkim)
hashfunc2 = hashAlgo2 (dkimSigAlgo dkim)
sig = B.decode (dkimSignature dkim)
cmail = prepareDKIM dkim mail
bodyHash1 = bytestringDigest . hashfunc2 . canonDkimBody (dkimBodyCanon dkim) . mailBody
bodyHash2 = B.decode . dkimBodyHash
hashAlgo1 :: DkimSigAlgo -> HashInfo
hashAlgo1 RSA_SHA1 = ha_SHA1
hashAlgo1 RSA_SHA256 = ha_SHA256
hashAlgo2 :: DkimSigAlgo -> L.ByteString -> Digest
hashAlgo2 RSA_SHA1 = sha1
hashAlgo2 RSA_SHA256 = sha256