peyotls-0.0.0.24: src/Network/PeyoTLS/Run.hs
{-# LANGUAGE OverloadedStrings, TypeFamilies, TupleSections, PackageImports #-}
module Network.PeyoTLS.Run ( H.debug,
H.TlsM, H.run, H.TlsHandleBase(..),
adGet, adGetLine, adGetContent, H.adPut, H.adDebug, H.adClose,
HandshakeM, execHandshakeM, rerunHandshakeM, withRandom,
chGet, ccsPut, hsPut, updateHash, flushAd,
H.CertSecretKey(..), H.isRsaKey, H.isEcdsaKey,
H.SettingsC, getSettingsC, setSettingsC,
H.SettingsS, getSettingsS, setSettingsS,
getCipherSuite, setCipherSuite,
getClFinished, getSvFinished, setClFinished, setSvFinished,
H.getNames,
H.RW(..), flushCipherSuite, makeKeys,
H.Side(..), handshakeHash, finishedHash,
H.ValidateHandle(..), handshakeValidate, validateAlert,
H.AlertLevel(..), H.AlertDesc(..), debugCipherSuite, throw ) where
import Control.Applicative ((<$>), (<*>))
import Control.Arrow (first, second, (***))
import Control.Monad (liftM)
import "monads-tf" Control.Monad.Trans (lift)
import "monads-tf" Control.Monad.State (
StateT, evalStateT, execStateT, get, gets, modify )
import "monads-tf" Control.Monad.Error (throwError)
import Data.Word (Word8)
import Data.HandleLike (HandleLike(..))
import "crypto-random" Crypto.Random (CPRG)
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as BSC
import qualified Data.ASN1.Types as ASN1
import qualified Data.X509 as X509
import qualified Data.X509.Validation as X509
import qualified Data.X509.CertificateStore as X509
import qualified Codec.Bytable.BigEndian as B
import qualified Crypto.Hash.SHA256 as SHA256
import qualified Network.PeyoTLS.Handle as H ( debug,
TlsM, run, withRandom,
TlsHandleBase(..), CipherSuite,
newHandle, chGet, ccsPut, hsPut,
adGet, adGetLine, adGetContent, adPut, adDebug, adClose,
flushAd, getBuf, setBuf,
getCipherSuite, setCipherSuite,
SettingsC, getSettingsC, setSettingsC,
SettingsS, getSettingsS, setSettingsS,
getClFinished, getSvFinished, setClFinished, setSvFinished,
makeKeys, setKeys,
getNames, setNames,
Side(..), finishedHash,
RW(..), flushCipherSuite,
ValidateHandle(..), tValidate,
CertSecretKey(..), isRsaKey, isEcdsaKey,
Alert(..), AlertLevel(..), AlertDesc(..), debugCipherSuite )
modNm :: String
modNm = "Network.PeyoTLS.Run"
type RenegoProc h g = H.TlsHandleBase h g -> H.TlsM h g ()
adGet :: (HandleLike h, CPRG g) =>
RenegoProc h g -> H.TlsHandleBase h g -> Int -> H.TlsM h g BS.ByteString
adGet rp t n = H.getBuf t >>= \b -> if BS.length b >= n
then uncurry (>>) . (H.setBuf t *** return) $ BS.splitAt n b
else BS.append b `liftM` H.adGet rp t (n - BS.length b)
adGetLine :: (HandleLike h, CPRG g) =>
RenegoProc h g -> H.TlsHandleBase h g -> H.TlsM h g BS.ByteString
adGetLine rp t = H.getBuf t >>= \b -> if '\n' `BSC.elem` b || '\r' `BSC.elem` b
then uncurry (>>) . (H.setBuf t *** return) $ case BSC.span (/= '\r') b of
(_, "") -> second BS.tail $ BSC.span (/= '\n') b
(l, ls) -> case BSC.uncons ls of
Just ('\r', ls') -> case BSC.uncons ls' of
Just ('\n', ls'') -> (l, ls'')
_ -> (l, ls')
_ -> (l, ls)
else (b `BS.append`) `liftM` H.adGetLine rp t
adGetContent :: (HandleLike h, CPRG g) =>
RenegoProc h g -> H.TlsHandleBase h g -> H.TlsM h g BS.ByteString
adGetContent rp t = H.getBuf t >>= \b ->
if BS.null b then H.adGetContent rp t else H.setBuf t "" >> return b
type HandshakeM h g = StateT (H.TlsHandleBase h g, SHA256.Ctx) (H.TlsM h g)
execHandshakeM ::
HandleLike h => h -> HandshakeM h g () -> H.TlsM h g (H.TlsHandleBase h g)
execHandshakeM h =
liftM fst . ((, SHA256.init) `liftM` H.newHandle h >>=) . execStateT
rerunHandshakeM ::
HandleLike h => H.TlsHandleBase h g -> HandshakeM h g a -> H.TlsM h g a
rerunHandshakeM = flip evalStateT . (, SHA256.init)
withRandom :: HandleLike h => (g -> (a, g)) -> HandshakeM h g a
withRandom = lift . H.withRandom
chGet :: (HandleLike h, CPRG g) => HandshakeM h g (Either Word8 BS.ByteString)
chGet = gets fst >>= lift . flip H.chGet 1 >>= \ch -> case ch of
Left w -> return $ Left w
Right t -> Right `liftM` do
len <- hsGet 3
body <- hsGet . either error id $ B.decode len
return $ BS.concat [t, len, body]
hsGet :: (HandleLike h, CPRG g) => Int -> HandshakeM h g BS.ByteString
hsGet n = gets fst >>= lift . flip H.chGet n >>= \ch -> case ch of
Right bs -> return bs
_ -> throw H.ALFatal H.ADUnexMsg $ modNm ++ ".hsGet: not handshake"
ccsPut :: (HandleLike h, CPRG g) => Word8 -> HandshakeM h g ()
ccsPut = (gets fst >>=) . (lift .) . flip H.ccsPut
hsPut :: (HandleLike h, CPRG g) => BS.ByteString -> HandshakeM h g ()
hsPut = (gets fst >>=) . (lift .) . flip H.hsPut
updateHash :: HandleLike h => BS.ByteString -> HandshakeM h g ()
updateHash = modify . second . flip SHA256.update
flushAd :: (HandleLike h, CPRG g) => HandshakeM h g Bool
flushAd = gets fst >>= lift . H.flushAd >>= uncurry (>>) . (push *** return)
where push bs = gets fst >>= lift . H.getBuf >>=
(gets fst >>=) . (lift .) . flip H.setBuf . (`BS.append` bs)
getSettingsC :: HandleLike h => HandshakeM h g H.SettingsC
getSettingsC = gets fst >>= lift . H.getSettingsC
setSettingsC :: HandleLike h => H.SettingsC -> HandshakeM h g ()
setSettingsC = (gets fst >>=) . (lift .) . flip H.setSettingsC
getSettingsS :: HandleLike h => HandshakeM h g H.SettingsS
getSettingsS = gets fst >>= lift . H.getSettingsS
setSettingsS :: HandleLike h => H.SettingsS -> HandshakeM h g ()
setSettingsS = (gets fst >>=) . (lift .) . flip H.setSettingsS
getCipherSuite :: HandleLike h => HandshakeM h g H.CipherSuite
getCipherSuite = gets fst >>= lift . H.getCipherSuite
setCipherSuite :: HandleLike h => H.CipherSuite -> HandshakeM h g ()
setCipherSuite = (gets fst >>=) . (lift .) . flip H.setCipherSuite
getClFinished, getSvFinished :: HandleLike h => HandshakeM h g BS.ByteString
getClFinished = gets fst >>= lift . H.getClFinished
getSvFinished = gets fst >>= lift . H.getSvFinished
setClFinished, setSvFinished :: HandleLike h => BS.ByteString -> HandshakeM h g ()
setClFinished = (gets fst >>=) . (lift .) . flip H.setClFinished
setSvFinished = (gets fst >>=) . (lift .) . flip H.setSvFinished
flushCipherSuite :: (HandleLike h, CPRG g) => H.RW -> HandshakeM h g ()
flushCipherSuite = (gets fst >>=) . (lift .) . H.flushCipherSuite
makeKeys :: HandleLike h => H.Side ->
(BS.ByteString, BS.ByteString) -> BS.ByteString -> HandshakeM h g ()
makeKeys s (cr, sr) pms = gets fst >>= \t -> lift $
H.getCipherSuite t >>= H.makeKeys t s cr sr pms >>= H.setKeys t
handshakeHash :: HandleLike h => HandshakeM h g BS.ByteString
handshakeHash = SHA256.finalize `liftM` gets snd
finishedHash :: (HandleLike h, CPRG g) => H.Side -> HandshakeM h g BS.ByteString
finishedHash s = get >>= lift . uncurry (H.finishedHash s) . second SHA256.finalize
validateAlert :: [X509.FailedReason] -> H.AlertDesc
validateAlert vr
| X509.UnknownCA `elem` vr = H.ADUnknownCa
| X509.Expired `elem` vr = H.ADCertificateExpired
| X509.InFuture `elem` vr = H.ADCertificateExpired
| otherwise = H.ADCertificateUnknown
handshakeValidate :: H.ValidateHandle h => X509.CertificateStore ->
X509.CertificateChain -> HandshakeM h g [X509.FailedReason]
handshakeValidate cs cc@(X509.CertificateChain (c : _)) = gets fst >>= \t -> do
lift . H.setNames t . certNames $ X509.getCertificate c
modify . first $ const t { H.names = certNames $ X509.getCertificate c }
lift $ H.tValidate t cs cc
handshakeValidate _ _ = error $ modNm ++ ".handshakeValidate: empty cert chain"
certNames :: X509.Certificate -> [String]
certNames = maybe id (:) <$> nm <*> nms
where
nm = (ASN1.asn1CharacterToString =<<) .
X509.getDnElement X509.DnCommonName . X509.certSubjectDN
nms = maybe [] ((\ns -> [s | X509.AltNameDNS s <- ns])
. \(X509.ExtSubjectAltName ns) -> ns)
. X509.extensionGet . X509.certExtensions
debugCipherSuite :: HandleLike h => String -> HandshakeM h g ()
debugCipherSuite = (gets fst >>=) . (lift .) . flip H.debugCipherSuite
throw :: HandleLike h => H.AlertLevel -> H.AlertDesc -> String -> HandshakeM h g a
throw = ((throwError .) .) . H.Alert