2012-08-18 21:46:53 +00:00
|
|
|
{-# LANGUAGE DeriveDataTypeable, OverloadedStrings #-}
|
|
|
|
module Network.TLS.Handshake.Common
|
2013-11-27 07:31:45 +00:00
|
|
|
( handshakeFailed
|
2012-08-18 21:46:53 +00:00
|
|
|
, errorToAlert
|
|
|
|
, unexpected
|
|
|
|
, newSession
|
|
|
|
, handshakeTerminate
|
|
|
|
-- * sending packets
|
|
|
|
, sendChangeCipherAndFinish
|
|
|
|
-- * receiving packets
|
|
|
|
, recvChangeCipherAndFinish
|
|
|
|
, RecvState(..)
|
|
|
|
, runRecvState
|
|
|
|
, recvPacketHandshake
|
|
|
|
) where
|
|
|
|
|
2013-08-01 07:32:27 +00:00
|
|
|
import Control.Concurrent.MVar
|
|
|
|
|
2012-08-18 21:46:53 +00:00
|
|
|
import Network.TLS.Context
|
|
|
|
import Network.TLS.Session
|
|
|
|
import Network.TLS.Struct
|
|
|
|
import Network.TLS.IO
|
|
|
|
import Network.TLS.State hiding (getNegotiatedProtocol)
|
2013-07-28 08:19:28 +00:00
|
|
|
import Network.TLS.Handshake.Process
|
2013-08-01 07:47:40 +00:00
|
|
|
import Network.TLS.Handshake.State
|
2013-08-01 07:32:27 +00:00
|
|
|
import Network.TLS.Record.State
|
2012-08-18 21:46:53 +00:00
|
|
|
import Network.TLS.Measurement
|
2013-07-23 07:14:48 +00:00
|
|
|
import Network.TLS.Types
|
2013-08-01 07:32:27 +00:00
|
|
|
import Network.TLS.Cipher
|
|
|
|
import Network.TLS.Util
|
2012-08-18 21:46:53 +00:00
|
|
|
import Data.ByteString.Char8 ()
|
|
|
|
|
|
|
|
import Control.Monad.State
|
2013-11-27 07:31:45 +00:00
|
|
|
import Control.Exception (throwIO)
|
2012-08-18 21:46:53 +00:00
|
|
|
|
|
|
|
handshakeFailed :: TLSError -> IO ()
|
|
|
|
handshakeFailed err = throwIO $ HandshakeFailed err
|
|
|
|
|
|
|
|
errorToAlert :: TLSError -> Packet
|
|
|
|
errorToAlert (Error_Protocol (_, _, ad)) = Alert [(AlertLevel_Fatal, ad)]
|
|
|
|
errorToAlert _ = Alert [(AlertLevel_Fatal, InternalError)]
|
|
|
|
|
2013-08-01 07:52:42 +00:00
|
|
|
unexpected :: String -> Maybe [Char] -> IO a
|
2012-08-18 21:46:53 +00:00
|
|
|
unexpected msg expected = throwCore $ Error_Packet_unexpected msg (maybe "" (" expected: " ++) expected)
|
|
|
|
|
2013-08-01 07:52:42 +00:00
|
|
|
newSession :: Context -> IO Session
|
2012-08-18 21:46:53 +00:00
|
|
|
newSession ctx
|
2013-07-10 06:37:52 +00:00
|
|
|
| pUseSession $ ctxParams ctx = getStateRNG ctx 32 >>= return . Session . Just
|
|
|
|
| otherwise = return $ Session Nothing
|
2012-08-18 21:46:53 +00:00
|
|
|
|
|
|
|
-- | when a new handshake is done, wrap up & clean up.
|
2013-08-01 07:52:42 +00:00
|
|
|
handshakeTerminate :: Context -> IO ()
|
2012-08-18 21:46:53 +00:00
|
|
|
handshakeTerminate ctx = do
|
2013-07-10 06:37:52 +00:00
|
|
|
session <- usingState_ ctx getSession
|
|
|
|
-- only callback the session established if we have a session
|
|
|
|
case session of
|
|
|
|
Session (Just sessionId) -> do
|
2013-08-01 07:32:27 +00:00
|
|
|
sessionData <- getSessionData ctx
|
|
|
|
withSessionManager (ctxParams ctx) (\s -> liftIO $ sessionEstablish s sessionId (fromJust "session-data" sessionData))
|
2013-07-10 06:37:52 +00:00
|
|
|
_ -> return ()
|
|
|
|
-- forget all handshake data now and reset bytes counters.
|
2013-08-01 07:47:40 +00:00
|
|
|
liftIO $ modifyMVar_ (ctxHandshake ctx) (return . const Nothing)
|
2013-07-10 06:37:52 +00:00
|
|
|
updateMeasure ctx resetBytesCounters
|
|
|
|
-- mark the secure connection up and running.
|
|
|
|
setEstablished ctx True
|
|
|
|
return ()
|
2012-08-18 21:46:53 +00:00
|
|
|
|
2013-08-01 07:52:42 +00:00
|
|
|
sendChangeCipherAndFinish :: Context -> Role -> IO ()
|
2013-07-23 07:14:48 +00:00
|
|
|
sendChangeCipherAndFinish ctx role = do
|
2013-07-10 06:37:52 +00:00
|
|
|
sendPacket ctx ChangeCipherSpec
|
2012-08-18 21:46:53 +00:00
|
|
|
|
2013-07-23 07:14:48 +00:00
|
|
|
when (role == ClientRole) $ do
|
2013-07-10 06:37:52 +00:00
|
|
|
let cparams = getClientParams $ ctxParams ctx
|
|
|
|
suggest <- usingState_ ctx $ getServerNextProtocolSuggest
|
|
|
|
case (onNPNServerSuggest cparams, suggest) of
|
2012-08-18 21:46:53 +00:00
|
|
|
-- client offered, server picked up. send NPN handshake.
|
|
|
|
(Just io, Just protos) -> do proto <- liftIO $ io protos
|
|
|
|
sendPacket ctx (Handshake [HsNextProtocolNegotiation proto])
|
|
|
|
usingState_ ctx $ setNegotiatedProtocol proto
|
|
|
|
-- client offered, server didn't pick up. do nothing.
|
|
|
|
(Just _, Nothing) -> return ()
|
|
|
|
-- client didn't offer. do nothing.
|
|
|
|
(Nothing, _) -> return ()
|
2013-07-10 06:37:52 +00:00
|
|
|
liftIO $ contextFlush ctx
|
2012-08-18 21:46:53 +00:00
|
|
|
|
2013-07-23 07:39:52 +00:00
|
|
|
cf <- usingState_ ctx getVersion >>= \ver -> usingHState ctx $ getHandshakeDigest ver role
|
2013-07-10 06:37:52 +00:00
|
|
|
sendPacket ctx (Handshake [Finished cf])
|
|
|
|
liftIO $ contextFlush ctx
|
2012-08-18 21:46:53 +00:00
|
|
|
|
2013-08-01 07:52:42 +00:00
|
|
|
recvChangeCipherAndFinish :: Context -> IO ()
|
2012-08-18 21:46:53 +00:00
|
|
|
recvChangeCipherAndFinish ctx = runRecvState ctx (RecvStateNext expectChangeCipher)
|
2013-07-10 06:37:52 +00:00
|
|
|
where expectChangeCipher ChangeCipherSpec = return $ RecvStateHandshake expectFinish
|
|
|
|
expectChangeCipher p = unexpected (show p) (Just "change cipher")
|
|
|
|
expectFinish (Finished _) = return RecvStateDone
|
|
|
|
expectFinish p = unexpected (show p) (Just "Handshake Finished")
|
2012-08-18 21:46:53 +00:00
|
|
|
|
|
|
|
data RecvState m =
|
2013-07-10 06:37:52 +00:00
|
|
|
RecvStateNext (Packet -> m (RecvState m))
|
|
|
|
| RecvStateHandshake (Handshake -> m (RecvState m))
|
|
|
|
| RecvStateDone
|
2012-08-18 21:46:53 +00:00
|
|
|
|
2013-08-01 07:52:42 +00:00
|
|
|
recvPacketHandshake :: Context -> IO [Handshake]
|
2012-08-18 21:46:53 +00:00
|
|
|
recvPacketHandshake ctx = do
|
2013-07-10 06:37:52 +00:00
|
|
|
pkts <- recvPacket ctx
|
|
|
|
case pkts of
|
|
|
|
Right (Handshake l) -> return l
|
|
|
|
Right x -> fail ("unexpected type received. expecting handshake and got: " ++ show x)
|
|
|
|
Left err -> throwCore err
|
2012-08-18 21:46:53 +00:00
|
|
|
|
2013-08-01 07:52:42 +00:00
|
|
|
runRecvState :: Context -> RecvState IO -> IO ()
|
2012-08-18 21:46:53 +00:00
|
|
|
runRecvState _ (RecvStateDone) = return ()
|
|
|
|
runRecvState ctx (RecvStateNext f) = recvPacket ctx >>= either throwCore f >>= runRecvState ctx
|
|
|
|
runRecvState ctx iniState = recvPacketHandshake ctx >>= loop iniState >>= runRecvState ctx
|
2013-07-10 06:37:52 +00:00
|
|
|
where
|
2013-08-01 07:52:42 +00:00
|
|
|
loop :: RecvState IO -> [Handshake] -> IO (RecvState IO)
|
2013-07-10 06:37:52 +00:00
|
|
|
loop recvState [] = return recvState
|
|
|
|
loop (RecvStateHandshake f) (x:xs) = do
|
|
|
|
nstate <- f x
|
2013-07-30 05:14:09 +00:00
|
|
|
processHandshake ctx x
|
2013-07-10 06:37:52 +00:00
|
|
|
loop nstate xs
|
|
|
|
loop _ _ = unexpected "spurious handshake" Nothing
|
2013-08-01 07:32:27 +00:00
|
|
|
|
2013-08-01 07:52:42 +00:00
|
|
|
getSessionData :: Context -> IO (Maybe SessionData)
|
2013-08-01 07:32:27 +00:00
|
|
|
getSessionData ctx = do
|
|
|
|
ver <- usingState_ ctx getVersion
|
|
|
|
mms <- usingHState ctx (gets hstMasterSecret)
|
|
|
|
tx <- liftIO $ readMVar (ctxTxState ctx)
|
|
|
|
case mms of
|
|
|
|
Nothing -> return Nothing
|
|
|
|
Just ms -> return $ Just $ SessionData
|
|
|
|
{ sessionVersion = ver
|
|
|
|
, sessionCipher = cipherID $ fromJust "cipher" $ stCipher tx
|
|
|
|
, sessionSecret = ms
|
|
|
|
}
|