-- |
-- Module      : Network.TLS.Handshake.Process
-- License     : BSD-style
-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
-- Stability   : experimental
-- Portability : unknown
--
-- process handshake message received
--
module Network.TLS.Handshake.Process
    ( processHandshake
    , processHandshake13
    , startHandshake
    ) where

import Network.TLS.Context.Internal
import Network.TLS.Crypto
import Network.TLS.ErrT
import Network.TLS.Extension
import Network.TLS.Handshake.Key
import Network.TLS.Handshake.Random
import Network.TLS.Handshake.Signature
import Network.TLS.Handshake.State
import Network.TLS.Handshake.State13
import Network.TLS.Imports
import Network.TLS.Packet
import Network.TLS.Parameters
import Network.TLS.Sending
import Network.TLS.Sending13
import Network.TLS.State
import Network.TLS.Struct
import Network.TLS.Struct13
import Network.TLS.Types (Role(..), invertRole, MasterSecret(..))
import Network.TLS.Util

import Control.Concurrent.MVar
import Control.Monad.IO.Class (liftIO)
import Control.Monad.State.Strict (gets)
import Data.X509 (CertificateChain(..), Certificate(..), getCertificate)

processHandshake :: Context -> Handshake -> IO ()
processHandshake :: Context -> Handshake -> IO ()
processHandshake ctx :: Context
ctx hs :: Handshake
hs = do
    Role
role <- Context -> TLSSt Role -> IO Role
forall a. Context -> TLSSt a -> IO a
usingState_ Context
ctx TLSSt Role
isClientContext
    case Handshake
hs of
        ClientHello cver :: Version
cver ran :: ClientRandom
ran _ cids :: [CipherID]
cids _ ex :: [ExtensionRaw]
ex _ -> Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Role
role Role -> Role -> Bool
forall a. Eq a => a -> a -> Bool
== Role
ServerRole) (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ do
            (ExtensionRaw -> IO ()) -> [ExtensionRaw] -> IO ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ (Context -> TLSSt () -> IO ()
forall a. Context -> TLSSt a -> IO a
usingState_ Context
ctx (TLSSt () -> IO ())
-> (ExtensionRaw -> TLSSt ()) -> ExtensionRaw -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ExtensionRaw -> TLSSt ()
processClientExtension) [ExtensionRaw]
ex
            -- RFC 5746: secure renegotiation
            -- TLS_EMPTY_RENEGOTIATION_INFO_SCSV: {0x00, 0xFF}
            Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Bool
secureRenegotiation Bool -> Bool -> Bool
&& (0xff CipherID -> [CipherID] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [CipherID]
cids)) (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$
                Context -> TLSSt () -> IO ()
forall a. Context -> TLSSt a -> IO a
usingState_ Context
ctx (TLSSt () -> IO ()) -> TLSSt () -> IO ()
forall a b. (a -> b) -> a -> b
$ Bool -> TLSSt ()
setSecureRenegotiation Bool
True
            Bool
hrr <- Context -> TLSSt Bool -> IO Bool
forall a. Context -> TLSSt a -> IO a
usingState_ Context
ctx TLSSt Bool
getTLS13HRR
            Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless Bool
hrr (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ Context -> Version -> ClientRandom -> IO ()
startHandshake Context
ctx Version
cver ClientRandom
ran
        Certificates certs :: CertificateChain
certs            -> Role -> CertificateChain -> IO ()
processCertificates Role
role CertificateChain
certs
        Finished fdata :: FinishedData
fdata                -> Context -> FinishedData -> IO ()
processClientFinished Context
ctx FinishedData
fdata
        _                             -> () -> IO ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
    Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Handshake -> Bool
isHRR Handshake
hs) (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ Context -> HandshakeM () -> IO ()
forall (m :: * -> *) a. MonadIO m => Context -> HandshakeM a -> m a
usingHState Context
ctx HandshakeM ()
wrapAsMessageHash13
    IO FinishedData -> IO ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (IO FinishedData -> IO ()) -> IO FinishedData -> IO ()
forall a b. (a -> b) -> a -> b
$ Context -> Role -> Handshake -> IO FinishedData
updateHandshake Context
ctx Role
ServerRole Handshake
hs
    case Handshake
hs of
        ClientKeyXchg content :: ClientKeyXchgAlgorithmData
content  -> Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Role
role Role -> Role -> Bool
forall a. Eq a => a -> a -> Bool
== Role
ServerRole) (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$
            Context -> ClientKeyXchgAlgorithmData -> IO ()
processClientKeyXchg Context
ctx ClientKeyXchgAlgorithmData
content
        _                      -> () -> IO ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
  where secureRenegotiation :: Bool
secureRenegotiation = Supported -> Bool
supportedSecureRenegotiation (Supported -> Bool) -> Supported -> Bool
forall a b. (a -> b) -> a -> b
$ Context -> Supported
ctxSupported Context
ctx
        -- RFC5746: secure renegotiation
        -- the renegotiation_info extension: 0xff01
        processClientExtension :: ExtensionRaw -> TLSSt ()
processClientExtension (ExtensionRaw 0xff01 content :: FinishedData
content) | Bool
secureRenegotiation = do
            FinishedData
v <- Role -> TLSSt FinishedData
getVerifiedData Role
ClientRole
            let bs :: FinishedData
bs = SecureRenegotiation -> FinishedData
forall a. Extension a => a -> FinishedData
extensionEncode (FinishedData -> Maybe FinishedData -> SecureRenegotiation
SecureRenegotiation FinishedData
v Maybe FinishedData
forall a. Maybe a
Nothing)
            Bool -> TLSSt () -> TLSSt ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (FinishedData
bs FinishedData -> FinishedData -> Bool
`bytesEq` FinishedData
content) (TLSSt () -> TLSSt ()) -> TLSSt () -> TLSSt ()
forall a b. (a -> b) -> a -> b
$ TLSError -> TLSSt ()
forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError (TLSError -> TLSSt ()) -> TLSError -> TLSSt ()
forall a b. (a -> b) -> a -> b
$ (String, Bool, AlertDescription) -> TLSError
Error_Protocol ("client verified data not matching: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ FinishedData -> String
forall a. Show a => a -> String
show FinishedData
v String -> String -> String
forall a. [a] -> [a] -> [a]
++ ":" String -> String -> String
forall a. [a] -> [a] -> [a]
++ FinishedData -> String
forall a. Show a => a -> String
show FinishedData
content, Bool
True, AlertDescription
HandshakeFailure)

            Bool -> TLSSt ()
setSecureRenegotiation Bool
True
        -- unknown extensions
        processClientExtension _ = () -> TLSSt ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()

        processCertificates :: Role -> CertificateChain -> IO ()
        processCertificates :: Role -> CertificateChain -> IO ()
processCertificates ServerRole (CertificateChain []) = () -> IO ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
        processCertificates ClientRole (CertificateChain []) =
            TLSError -> IO ()
forall (m :: * -> *) e a. (MonadIO m, Exception e) => e -> m a
throwCore (TLSError -> IO ()) -> TLSError -> IO ()
forall a b. (a -> b) -> a -> b
$ (String, Bool, AlertDescription) -> TLSError
Error_Protocol ("server certificate missing", Bool
True, AlertDescription
HandshakeFailure)
        processCertificates _ (CertificateChain (c :: SignedExact Certificate
c:_)) =
            Context -> HandshakeM () -> IO ()
forall (m :: * -> *) a. MonadIO m => Context -> HandshakeM a -> m a
usingHState Context
ctx (HandshakeM () -> IO ()) -> HandshakeM () -> IO ()
forall a b. (a -> b) -> a -> b
$ PubKey -> HandshakeM ()
setPublicKey PubKey
pubkey
          where pubkey :: PubKey
pubkey = Certificate -> PubKey
certPubKey (Certificate -> PubKey) -> Certificate -> PubKey
forall a b. (a -> b) -> a -> b
$ SignedExact Certificate -> Certificate
getCertificate SignedExact Certificate
c

        isHRR :: Handshake -> Bool
isHRR (ServerHello TLS12 srand :: ServerRandom
srand _ _ _ _) = ServerRandom -> Bool
isHelloRetryRequest ServerRandom
srand
        isHRR _                                 = Bool
False

processHandshake13 :: Context -> Handshake13 -> IO ()
processHandshake13 :: Context -> Handshake13 -> IO ()
processHandshake13 ctx :: Context
ctx = IO FinishedData -> IO ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (IO FinishedData -> IO ())
-> (Handshake13 -> IO FinishedData) -> Handshake13 -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Context -> Handshake13 -> IO FinishedData
updateHandshake13 Context
ctx

-- 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 :: Context -> ClientKeyXchgAlgorithmData -> IO ()
processClientKeyXchg :: Context -> ClientKeyXchgAlgorithmData -> IO ()
processClientKeyXchg ctx :: Context
ctx (CKX_RSA encryptedPremaster :: FinishedData
encryptedPremaster) = do
    (rver :: Version
rver, role :: Role
role, random :: FinishedData
random) <- Context
-> TLSSt (Version, Role, FinishedData)
-> IO (Version, Role, FinishedData)
forall a. Context -> TLSSt a -> IO a
usingState_ Context
ctx (TLSSt (Version, Role, FinishedData)
 -> IO (Version, Role, FinishedData))
-> TLSSt (Version, Role, FinishedData)
-> IO (Version, Role, FinishedData)
forall a b. (a -> b) -> a -> b
$ do
        (,,) (Version -> Role -> FinishedData -> (Version, Role, FinishedData))
-> TLSSt Version
-> TLSSt (Role -> FinishedData -> (Version, Role, FinishedData))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> TLSSt Version
getVersion TLSSt (Role -> FinishedData -> (Version, Role, FinishedData))
-> TLSSt Role
-> TLSSt (FinishedData -> (Version, Role, FinishedData))
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> TLSSt Role
isClientContext TLSSt (FinishedData -> (Version, Role, FinishedData))
-> TLSSt FinishedData -> TLSSt (Version, Role, FinishedData)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Int -> TLSSt FinishedData
genRandom 48
    Either KxError FinishedData
ePremaster <- Context -> FinishedData -> IO (Either KxError FinishedData)
decryptRSA Context
ctx FinishedData
encryptedPremaster
    FinishedData
masterSecret <- Context -> HandshakeM FinishedData -> IO FinishedData
forall (m :: * -> *) a. MonadIO m => Context -> HandshakeM a -> m a
usingHState Context
ctx (HandshakeM FinishedData -> IO FinishedData)
-> HandshakeM FinishedData -> IO FinishedData
forall a b. (a -> b) -> a -> b
$ do
        Version
expectedVer <- (HandshakeState -> Version) -> HandshakeM Version
forall s (m :: * -> *) a. MonadState s m => (s -> a) -> m a
gets HandshakeState -> Version
hstClientVersion
        case Either KxError FinishedData
ePremaster of
            Left _          -> Version -> Role -> FinishedData -> HandshakeM FinishedData
forall preMaster.
ByteArrayAccess preMaster =>
Version -> Role -> preMaster -> HandshakeM FinishedData
setMasterSecretFromPre Version
rver Role
role FinishedData
random
            Right premaster :: FinishedData
premaster -> case FinishedData -> Either TLSError (Version, FinishedData)
decodePreMasterSecret FinishedData
premaster of
                Left _                   -> Version -> Role -> FinishedData -> HandshakeM FinishedData
forall preMaster.
ByteArrayAccess preMaster =>
Version -> Role -> preMaster -> HandshakeM FinishedData
setMasterSecretFromPre Version
rver Role
role FinishedData
random
                Right (ver :: Version
ver, _)
                    | Version
ver Version -> Version -> Bool
forall a. Eq a => a -> a -> Bool
/= Version
expectedVer -> Version -> Role -> FinishedData -> HandshakeM FinishedData
forall preMaster.
ByteArrayAccess preMaster =>
Version -> Role -> preMaster -> HandshakeM FinishedData
setMasterSecretFromPre Version
rver Role
role FinishedData
random
                    | Bool
otherwise          -> Version -> Role -> FinishedData -> HandshakeM FinishedData
forall preMaster.
ByteArrayAccess preMaster =>
Version -> Role -> preMaster -> HandshakeM FinishedData
setMasterSecretFromPre Version
rver Role
role FinishedData
premaster
    IO () -> IO ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ Context -> MasterSecret -> IO ()
forall a. LogLabel a => Context -> a -> IO ()
logKey Context
ctx (FinishedData -> MasterSecret
MasterSecret FinishedData
masterSecret)

processClientKeyXchg ctx :: Context
ctx (CKX_DH clientDHValue :: DHPublic
clientDHValue) = do
    Version
rver <- Context -> TLSSt Version -> IO Version
forall a. Context -> TLSSt a -> IO a
usingState_ Context
ctx TLSSt Version
getVersion
    Role
role <- Context -> TLSSt Role -> IO Role
forall a. Context -> TLSSt a -> IO a
usingState_ Context
ctx TLSSt Role
isClientContext

    ServerDHParams
serverParams <- Context -> HandshakeM ServerDHParams -> IO ServerDHParams
forall (m :: * -> *) a. MonadIO m => Context -> HandshakeM a -> m a
usingHState Context
ctx HandshakeM ServerDHParams
getServerDHParams
    let params :: DHParams
params = ServerDHParams -> DHParams
serverDHParamsToParams ServerDHParams
serverParams
    Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (DHParams -> Integer -> Bool
dhValid DHParams
params (Integer -> Bool) -> Integer -> Bool
forall a b. (a -> b) -> a -> b
$ DHPublic -> Integer
dhUnwrapPublic DHPublic
clientDHValue) (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$
        TLSError -> IO ()
forall (m :: * -> *) e a. (MonadIO m, Exception e) => e -> m a
throwCore (TLSError -> IO ()) -> TLSError -> IO ()
forall a b. (a -> b) -> a -> b
$ (String, Bool, AlertDescription) -> TLSError
Error_Protocol ("invalid client public key", Bool
True, AlertDescription
IllegalParameter)

    DHPrivate
dhpriv       <- Context -> HandshakeM DHPrivate -> IO DHPrivate
forall (m :: * -> *) a. MonadIO m => Context -> HandshakeM a -> m a
usingHState Context
ctx HandshakeM DHPrivate
getDHPrivate
    let premaster :: DHKey
premaster = DHParams -> DHPrivate -> DHPublic -> DHKey
dhGetShared DHParams
params DHPrivate
dhpriv DHPublic
clientDHValue
    FinishedData
masterSecret <- Context -> HandshakeM FinishedData -> IO FinishedData
forall (m :: * -> *) a. MonadIO m => Context -> HandshakeM a -> m a
usingHState Context
ctx (HandshakeM FinishedData -> IO FinishedData)
-> HandshakeM FinishedData -> IO FinishedData
forall a b. (a -> b) -> a -> b
$ Version -> Role -> DHKey -> HandshakeM FinishedData
forall preMaster.
ByteArrayAccess preMaster =>
Version -> Role -> preMaster -> HandshakeM FinishedData
setMasterSecretFromPre Version
rver Role
role DHKey
premaster
    IO () -> IO ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ Context -> MasterSecret -> IO ()
forall a. LogLabel a => Context -> a -> IO ()
logKey Context
ctx (FinishedData -> MasterSecret
MasterSecret FinishedData
masterSecret)

processClientKeyXchg ctx :: Context
ctx (CKX_ECDH bytes :: FinishedData
bytes) = do
    ServerECDHParams grp :: Group
grp _ <- Context -> HandshakeM ServerECDHParams -> IO ServerECDHParams
forall (m :: * -> *) a. MonadIO m => Context -> HandshakeM a -> m a
usingHState Context
ctx HandshakeM ServerECDHParams
getServerECDHParams
    case Group -> FinishedData -> Either CryptoError GroupPublic
decodeGroupPublic Group
grp FinishedData
bytes of
      Left _ -> TLSError -> IO ()
forall (m :: * -> *) e a. (MonadIO m, Exception e) => e -> m a
throwCore (TLSError -> IO ()) -> TLSError -> IO ()
forall a b. (a -> b) -> a -> b
$ (String, Bool, AlertDescription) -> TLSError
Error_Protocol ("client public key cannot be decoded", Bool
True, AlertDescription
IllegalParameter)
      Right clipub :: GroupPublic
clipub -> do
          GroupPrivate
srvpri <- Context -> HandshakeM GroupPrivate -> IO GroupPrivate
forall (m :: * -> *) a. MonadIO m => Context -> HandshakeM a -> m a
usingHState Context
ctx HandshakeM GroupPrivate
getGroupPrivate
          case GroupPublic -> GroupPrivate -> Maybe GroupKey
groupGetShared GroupPublic
clipub GroupPrivate
srvpri of
              Just premaster :: GroupKey
premaster -> do
                  Version
rver <- Context -> TLSSt Version -> IO Version
forall a. Context -> TLSSt a -> IO a
usingState_ Context
ctx TLSSt Version
getVersion
                  Role
role <- Context -> TLSSt Role -> IO Role
forall a. Context -> TLSSt a -> IO a
usingState_ Context
ctx TLSSt Role
isClientContext
                  FinishedData
masterSecret <- Context -> HandshakeM FinishedData -> IO FinishedData
forall (m :: * -> *) a. MonadIO m => Context -> HandshakeM a -> m a
usingHState Context
ctx (HandshakeM FinishedData -> IO FinishedData)
-> HandshakeM FinishedData -> IO FinishedData
forall a b. (a -> b) -> a -> b
$ Version -> Role -> GroupKey -> HandshakeM FinishedData
forall preMaster.
ByteArrayAccess preMaster =>
Version -> Role -> preMaster -> HandshakeM FinishedData
setMasterSecretFromPre Version
rver Role
role GroupKey
premaster
                  IO () -> IO ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ Context -> MasterSecret -> IO ()
forall a. LogLabel a => Context -> a -> IO ()
logKey Context
ctx (FinishedData -> MasterSecret
MasterSecret FinishedData
masterSecret)
              Nothing -> TLSError -> IO ()
forall (m :: * -> *) e a. (MonadIO m, Exception e) => e -> m a
throwCore (TLSError -> IO ()) -> TLSError -> IO ()
forall a b. (a -> b) -> a -> b
$ (String, Bool, AlertDescription) -> TLSError
Error_Protocol ("cannot generate a shared secret on ECDH", Bool
True, AlertDescription
IllegalParameter)

processClientFinished :: Context -> FinishedData -> IO ()
processClientFinished :: Context -> FinishedData -> IO ()
processClientFinished ctx :: Context
ctx fdata :: FinishedData
fdata = do
    (cc :: Role
cc,ver :: Version
ver) <- Context -> TLSSt (Role, Version) -> IO (Role, Version)
forall a. Context -> TLSSt a -> IO a
usingState_ Context
ctx (TLSSt (Role, Version) -> IO (Role, Version))
-> TLSSt (Role, Version) -> IO (Role, Version)
forall a b. (a -> b) -> a -> b
$ (,) (Role -> Version -> (Role, Version))
-> TLSSt Role -> TLSSt (Version -> (Role, Version))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> TLSSt Role
isClientContext TLSSt (Version -> (Role, Version))
-> TLSSt Version -> TLSSt (Role, Version)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> TLSSt Version
getVersion
    FinishedData
expected <- Context -> HandshakeM FinishedData -> IO FinishedData
forall (m :: * -> *) a. MonadIO m => Context -> HandshakeM a -> m a
usingHState Context
ctx (HandshakeM FinishedData -> IO FinishedData)
-> HandshakeM FinishedData -> IO FinishedData
forall a b. (a -> b) -> a -> b
$ Version -> Role -> HandshakeM FinishedData
getHandshakeDigest Version
ver (Role -> HandshakeM FinishedData)
-> Role -> HandshakeM FinishedData
forall a b. (a -> b) -> a -> b
$ Role -> Role
invertRole Role
cc
    Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (FinishedData
expected FinishedData -> FinishedData -> Bool
forall a. Eq a => a -> a -> Bool
/= FinishedData
fdata) (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ String -> IO ()
forall (m :: * -> *) a. MonadIO m => String -> m a
decryptError "cannot verify finished"

-- initialize a new Handshake context (initial handshake or renegotiations)
startHandshake :: Context -> Version -> ClientRandom -> IO ()
startHandshake :: Context -> Version -> ClientRandom -> IO ()
startHandshake ctx :: Context
ctx ver :: Version
ver crand :: ClientRandom
crand =
    let hs :: Maybe HandshakeState
hs = HandshakeState -> Maybe HandshakeState
forall a. a -> Maybe a
Just (HandshakeState -> Maybe HandshakeState)
-> HandshakeState -> Maybe HandshakeState
forall a b. (a -> b) -> a -> b
$ Version -> ClientRandom -> HandshakeState
newEmptyHandshake Version
ver ClientRandom
crand
    in IO () -> IO ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ IO (Maybe HandshakeState) -> IO ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (IO (Maybe HandshakeState) -> IO ())
-> IO (Maybe HandshakeState) -> IO ()
forall a b. (a -> b) -> a -> b
$ MVar (Maybe HandshakeState)
-> Maybe HandshakeState -> IO (Maybe HandshakeState)
forall a. MVar a -> a -> IO a
swapMVar (Context -> MVar (Maybe HandshakeState)
ctxHandshake Context
ctx) Maybe HandshakeState
hs