domain-auth-0.2.0: Network/DomainAuth/Pubkey/RSAPub.hs
{-# LANGUAGE OverloadedStrings #-}
module Network.DomainAuth.Pubkey.RSAPub where
import Codec.Crypto.RSA
import Control.Applicative
import Data.ByteString (ByteString)
import qualified Data.ByteString as BS (foldl', dropWhile, length, tail)
import qualified Data.ByteString.Char8 as BS ()
import qualified Data.ByteString.Lazy as BL
import Network.DNS (Domain)
import qualified Network.DNS as DNS hiding (Domain)
import Network.DomainAuth.Mail
import qualified Network.DomainAuth.Pubkey.Base64 as B
import qualified Network.DomainAuth.Pubkey.Der as D
lookupPublicKey :: DNS.Resolver -> Domain -> IO (Maybe PublicKey)
lookupPublicKey resolver domain = decode <$> lookupPublicKey' resolver domain
where
decode = (>>= return . decodeRSAPublicyKey)
lookupPublicKey' :: DNS.Resolver -> Domain -> IO (Maybe ByteString)
lookupPublicKey' resolver domain = extractPub <$> DNS.lookupTXT resolver domain
extractPub :: Maybe [ByteString] -> Maybe ByteString
extractPub = (>>= lookup "p" . parseTaggedValue . head)
decodeRSAPublicyKey :: ByteString -> PublicKey
decodeRSAPublicyKey bs = PublicKey size n e
where
subjectPublicKeyInfo = D.decode . B.decode $ bs
[_, subjectPublicKey] = D.tlv subjectPublicKeyInfo
rsaPublicKey = D.decode . toLazy . bitString . D.cnt $ subjectPublicKey
[bn',be'] = D.tlv rsaPublicKey
bn = BS.dropWhile (== 0) $ D.cnt bn'
be = D.cnt be'
n = toNum bn
e = toNum be
size = fromIntegral . BS.length $ bn
toNum = BS.foldl' (\x y -> x*256 + fromIntegral y) 0
bitString = BS.tail
toLazy x = BL.fromChunks [x]