{-# LANGUAGE RecordWildCards #-}
-- | A manager for TLS 1.2/1.3 session ticket.
--
-- Tracking client hello is not implemented yet.
-- So, if this is used for TLS 1.3 0-RTT,
-- replay attack is possible.
-- If your application data in 0-RTT changes the status of server side,
-- use 'Network.TLS.SessionManager' instead.
--
-- A dedicated thread is running repeatedly to replece
-- secret keys. So, energy saving is not achieved.
module Network.TLS.SessionTicket (
newSessionTicketManager,
Config,
defaultConfig,
ticketLifetime,
secretKeyInterval,
) where
import Codec.Serialise
import qualified Crypto.Token as CT
import qualified Data.ByteString.Lazy as L
import Network.TLS
import Network.TLS.Internal
-- | Configuration for session tickets.
data Config = Config
{ ticketLifetime :: Int
-- ^ Ticket lifetime in seconds.
, secretKeyInterval :: Int
}
-- | ticketLifetime: 2 hours (7200 seconds), secretKeyInterval: 30 minutes (1800 seconds)
defaultConfig :: Config
defaultConfig =
Config
{ ticketLifetime = 7200 -- 2 hours
, secretKeyInterval = 1800 -- 30 minites
}
-- | Creating a session ticket manager.
newSessionTicketManager :: Config -> IO SessionManager
newSessionTicketManager Config{..} =
sessionTicketManager <$> CT.spawnTokenManager conf
where
conf =
CT.defaultConfig
{ CT.interval = secretKeyInterval
, CT.tokenLifetime = ticketLifetime
, CT.threadName = "TLS ticket manager"
}
sessionTicketManager :: CT.TokenManager -> SessionManager
sessionTicketManager ctmgr =
noSessionManager
{ sessionResume = resume ctmgr
, sessionResumeOnlyOnce = resume ctmgr
, sessionEstablish = establish ctmgr
, sessionInvalidate = \_ -> return ()
, sessionUseTicket = True
}
establish :: CT.TokenManager -> SessionID -> SessionData -> IO (Maybe Ticket)
establish ctmgr _ sd = Just <$> CT.encryptToken ctmgr b
where
b = L.toStrict $ serialise sd
resume :: CT.TokenManager -> Ticket -> IO (Maybe SessionData)
resume ctmgr ticket
| isTicket ticket = do
msdb <- CT.decryptToken ctmgr ticket
case msdb of
Nothing -> return Nothing
Just sdb -> case deserialiseOrFail $ L.fromStrict sdb of
Left _ -> return Nothing
Right sd -> return $ Just sd
| otherwise = return Nothing