module Network.TLS.Sending (
encodePacket
, encodePacket13
, updateHandshake
, updateHandshake13
) where
import Network.TLS.Cipher
import Network.TLS.Context.Internal
import Network.TLS.Handshake.Random
import Network.TLS.Handshake.State
import Network.TLS.Handshake.State13
import Network.TLS.Imports
import Network.TLS.Packet
import Network.TLS.Packet13
import Network.TLS.Parameters
import Network.TLS.Record
import Network.TLS.Record.Layer
import Network.TLS.State
import Network.TLS.Struct
import Network.TLS.Struct13
import Network.TLS.Types (Role(..))
import Network.TLS.Util
import Control.Concurrent.MVar
import Control.Monad.State.Strict
import qualified Data.ByteString as B
import Data.IORef
encodePacket :: Monoid bytes
=> Context -> RecordLayer bytes -> Packet -> IO (Either TLSError bytes)
encodePacket ctx recordLayer pkt = do
(ver, _) <- decideRecordVersion ctx
let pt = packetType pkt
mkRecord bs = Record pt ver (fragmentPlaintext bs)
len = ctxFragmentSize ctx
records <- map mkRecord <$> packetToFragments ctx len pkt
bs <- fmap mconcat <$> forEitherM records (recordEncode recordLayer)
when (pkt == ChangeCipherSpec) $ switchTxEncryption ctx
return bs
packetToFragments :: Context -> Maybe Int -> Packet -> IO [ByteString]
packetToFragments ctx len (Handshake hss) =
getChunks len . B.concat <$> mapM (updateHandshake ctx ClientRole) hss
packetToFragments _ _ (Alert a) = return [encodeAlerts a]
packetToFragments _ _ ChangeCipherSpec = return [encodeChangeCipherSpec]
packetToFragments _ _ (AppData x) = return [x]
switchTxEncryption :: Context -> IO ()
switchTxEncryption ctx = do
tx <- usingHState ctx (fromJust "tx-state" <$> gets hstPendingTxState)
(ver, cc) <- usingState_ ctx $ do v <- getVersion
c <- isClientContext
return (v, c)
liftIO $ modifyMVar_ (ctxTxState ctx) (\_ -> return tx)
when (ver <= TLS10 && cc == ClientRole && isCBC tx && supportedEmptyPacket (ctxSupported ctx)) $ liftIO $ writeIORef (ctxNeedEmptyPacket ctx) True
where isCBC tx = maybe False (\c -> bulkBlockSize (cipherBulk c) > 0) (stCipher tx)
updateHandshake :: Context -> Role -> Handshake -> IO ByteString
updateHandshake ctx role hs = do
case hs of
Finished fdata -> usingState_ ctx $ updateVerifiedData role fdata
_ -> return ()
usingHState ctx $ do
when (certVerifyHandshakeMaterial hs) $ addHandshakeMessage encoded
when (finishHandshakeTypeMaterial $ typeOfHandshake hs) $ updateHandshakeDigest encoded
return encoded
where
encoded = encodeHandshake hs
encodePacket13 :: Monoid bytes
=> Context -> RecordLayer bytes -> Packet13 -> IO (Either TLSError bytes)
encodePacket13 ctx recordLayer pkt = do
let pt = contentType pkt
mkRecord bs = Record pt TLS12 (fragmentPlaintext bs)
len = ctxFragmentSize ctx
records <- map mkRecord <$> packetToFragments13 ctx len pkt
fmap mconcat <$> forEitherM records (recordEncode13 recordLayer)
packetToFragments13 :: Context -> Maybe Int -> Packet13 -> IO [ByteString]
packetToFragments13 ctx len (Handshake13 hss) =
getChunks len . B.concat <$> mapM (updateHandshake13 ctx) hss
packetToFragments13 _ _ (Alert13 a) = return [encodeAlerts a]
packetToFragments13 _ _ (AppData13 x) = return [x]
packetToFragments13 _ _ ChangeCipherSpec13 = return [encodeChangeCipherSpec]
updateHandshake13 :: Context -> Handshake13 -> IO ByteString
updateHandshake13 ctx hs
| isIgnored hs = return encoded
| otherwise = usingHState ctx $ do
when (isHRR hs) wrapAsMessageHash13
updateHandshakeDigest encoded
addHandshakeMessage encoded
return encoded
where
encoded = encodeHandshake13 hs
isHRR (ServerHello13 srand _ _ _) = isHelloRetryRequest srand
isHRR _ = False
isIgnored NewSessionTicket13{} = True
isIgnored KeyUpdate13{} = True
isIgnored _ = False