hs-tls/core/Network/TLS/Handshake/Process.hs

104 lines
4.6 KiB
Haskell
Raw Normal View History

-- |
-- Module : Network.TLS.Handshake.Process
-- License : BSD-style
-- Maintainer : Vincent Hanquez <vincent@snarc.org>
-- Stability : experimental
-- Portability : unknown
--
2013-07-28 08:20:45 +00:00
-- process handshake message received
--
module Network.TLS.Handshake.Process
( processHandshake
, startHandshake
) where
import Data.ByteString (ByteString)
import Data.Maybe (isNothing)
import Control.Applicative
2013-07-30 05:14:09 +00:00
import Control.Monad.Error
import Control.Monad.State (gets, modify)
import Network.TLS.Types (Role(..), invertRole)
import Network.TLS.Util
import Network.TLS.Packet
import Network.TLS.Struct
import Network.TLS.State
2013-07-30 05:14:09 +00:00
import Network.TLS.Context
import Network.TLS.Crypto
import Network.TLS.Handshake.State
import Network.TLS.Handshake.Key
import Network.TLS.Extension
import Data.X509
2013-07-30 05:14:09 +00:00
processHandshake :: MonadIO m => Context -> Handshake -> m ()
processHandshake ctx hs = do
role <- usingState_ ctx isClientContext
case hs of
ClientHello cver ran _ _ _ ex _ -> when (role == ServerRole) $ do
2013-07-30 05:14:09 +00:00
mapM_ (usingState_ ctx . processClientExtension) ex
startHandshake ctx cver ran
Certificates certs -> processCertificates role certs
ClientKeyXchg content -> when (role == ServerRole) $ do
processClientKeyXchg ctx content
HsNextProtocolNegotiation selected_protocol ->
2013-07-30 05:14:09 +00:00
when (role == ServerRole) $ usingState_ ctx $ setNegotiatedProtocol selected_protocol
Finished fdata -> processClientFinished ctx fdata
_ -> return ()
let encoded = encodeHandshake hs
2013-07-30 05:14:09 +00:00
when (certVerifyHandshakeMaterial hs) $ usingHState ctx $ addHandshakeMessage encoded
when (finishHandshakeTypeMaterial $ typeOfHandshake hs) $ usingHState ctx $ updateHandshakeDigest encoded
where -- secure renegotiation
processClientExtension (0xff01, content) = do
v <- getVerifiedData ClientRole
let bs = extensionEncode (SecureRenegotiation v Nothing)
unless (bs `bytesEq` content) $ throwError $ Error_Protocol ("client verified data not matching: " ++ show v ++ ":" ++ show content, True, HandshakeFailure)
setSecureRenegotiation True
-- unknown extensions
processClientExtension _ = return ()
processCertificates :: MonadIO m => Role -> CertificateChain -> m ()
2013-07-30 05:14:09 +00:00
processCertificates ServerRole (CertificateChain []) = return ()
processCertificates ClientRole (CertificateChain []) =
throwCore $ Error_Protocol ("server certificate missing", True, HandshakeFailure)
2013-07-30 05:14:09 +00:00
processCertificates role (CertificateChain (c:_))
| role == ClientRole = usingHState ctx $ setPublicKey pubkey
| otherwise = usingHState ctx $ setClientPublicKey pubkey
2013-07-30 05:14:09 +00:00
where pubkey = certPubKey $ getCertificate c
-- process the client key exchange message. the protocol expects the initial
-- client version received in ClientHello, not the negotiated version.
-- in case the version mismatch, generate a random master secret
processClientKeyXchg :: MonadIO m => Context -> ByteString -> m ()
processClientKeyXchg ctx encryptedPremaster = do
(rver, role, random, ePremaster) <- usingState_ ctx $ do
(,,,) <$> getVersion <*> isClientContext <*> genRandom 48 <*> decryptRSA encryptedPremaster
usingHState ctx $ do
expectedVer <- gets hstClientVersion
case ePremaster of
Left _ -> setMasterSecretFromPre rver role random
Right premaster -> case decodePreMasterSecret premaster of
Left _ -> setMasterSecretFromPre rver role random
Right (ver, _)
| ver /= expectedVer -> setMasterSecretFromPre rver role random
| otherwise -> setMasterSecretFromPre rver role premaster
processClientFinished :: MonadIO m => Context -> FinishedData -> m ()
processClientFinished ctx fdata = do
(cc,ver) <- usingState_ ctx $ (,) <$> isClientContext <*> getVersion
expected <- usingHState ctx $ getHandshakeDigest ver $ invertRole cc
when (expected /= fdata) $ do
throwCore $ Error_Protocol("bad record mac", True, BadRecordMac)
usingState_ ctx $ updateVerifiedData ServerRole fdata
return ()
startHandshake :: MonadIO m => Context -> Version -> ClientRandom -> m ()
startHandshake ctx ver crand = do
-- FIXME check if handshake is already not null
let initCtx = if ver < TLS12 then hashMD5SHA1 else hashSHA256
usingState_ ctx $ do
chs <- gets stHandshake
when (isNothing chs) $
modify (\st -> st { stHandshake = Just $ newEmptyHandshake ver crand initCtx })