{-# LANGUAGE OverloadedStrings #-}
module Network.TLS.QUIC (
tlsQUICClient
, tlsQUICServer
, QUICCallbacks(..)
, CryptLevel(..)
, KeyScheduleEvent(..)
, EarlySecretInfo(..)
, HandshakeSecretInfo(..)
, ApplicationSecretInfo(..)
, EarlySecret
, HandshakeSecret
, ApplicationSecret
, TrafficSecrets
, ServerTrafficSecret(..)
, ClientTrafficSecret(..)
, NegotiatedProtocol
, HandshakeMode13(..)
, ExtensionRaw(..)
, ExtensionID
, extensionID_QuicTransportParameters
, errorTLS
, errorToAlertDescription
, errorToAlertMessage
, fromAlertDescription
, toAlertDescription
, hkdfExpandLabel
, hkdfExtract
, hashDigestSize
, quicMaxEarlyDataSize
, defaultSupported
) where
import Network.TLS.Backend
import Network.TLS.Context
import Network.TLS.Context.Internal
import Network.TLS.Core
import Network.TLS.Crypto (hashDigestSize)
import Network.TLS.Crypto.Types
import Network.TLS.Extension (extensionID_QuicTransportParameters)
import Network.TLS.Extra.Cipher
import Network.TLS.Handshake.Common
import Network.TLS.Handshake.Control
import Network.TLS.Handshake.State
import Network.TLS.Handshake.State13
import Network.TLS.Imports
import Network.TLS.KeySchedule (hkdfExtract, hkdfExpandLabel)
import Network.TLS.Parameters
import Network.TLS.Record.Layer
import Network.TLS.Record.State
import Network.TLS.Struct
import Network.TLS.Types
import Data.Default.Class
nullBackend :: Backend
nullBackend = Backend {
backendFlush = return ()
, backendClose = return ()
, backendSend = \_ -> return ()
, backendRecv = \_ -> return ""
}
data KeyScheduleEvent
= InstallEarlyKeys (Maybe EarlySecretInfo)
| InstallHandshakeKeys HandshakeSecretInfo
| InstallApplicationKeys ApplicationSecretInfo
data QUICCallbacks = QUICCallbacks
{ quicSend :: [(CryptLevel, ByteString)] -> IO ()
, quicRecv :: CryptLevel -> IO (Either TLSError ByteString)
, quicInstallKeys :: Context -> KeyScheduleEvent -> IO ()
, quicNotifyExtensions :: Context -> [ExtensionRaw] -> IO ()
, quicDone :: Context -> IO ()
}
getTxLevel :: Context -> IO CryptLevel
getTxLevel ctx = do
(_, _, level, _) <- getTxState ctx
return level
getRxLevel :: Context -> IO CryptLevel
getRxLevel ctx = do
(_, _, level, _) <- getRxState ctx
return level
newRecordLayer :: Context -> QUICCallbacks
-> RecordLayer [(CryptLevel, ByteString)]
newRecordLayer ctx callbacks = newTransparentRecordLayer get send recv
where
get = getTxLevel ctx
send = quicSend callbacks
recv = getRxLevel ctx >>= quicRecv callbacks
tlsQUICClient :: ClientParams -> QUICCallbacks -> IO ()
tlsQUICClient cparams callbacks = do
ctx0 <- contextNew nullBackend cparams
let ctx1 = ctx0
{ ctxHandshakeSync = HandshakeSync sync (\_ _ -> return ())
, ctxFragmentSize = Nothing
, ctxQUICMode = True
}
rl = newRecordLayer ctx2 callbacks
ctx2 = updateRecordLayer rl ctx1
handshake ctx2
quicDone callbacks ctx2
void $ recvData ctx2
where
sync ctx (SendClientHello mEarlySecInfo) =
quicInstallKeys callbacks ctx (InstallEarlyKeys mEarlySecInfo)
sync ctx (RecvServerHello handSecInfo) =
quicInstallKeys callbacks ctx (InstallHandshakeKeys handSecInfo)
sync ctx (SendClientFinished exts appSecInfo) = do
let qexts = filterQTP exts
when (null qexts) $ do
throwCore $ Error_Protocol ("QUIC transport parameters are mssing", True, MissingExtension)
quicNotifyExtensions callbacks ctx qexts
quicInstallKeys callbacks ctx (InstallApplicationKeys appSecInfo)
tlsQUICServer :: ServerParams -> QUICCallbacks -> IO ()
tlsQUICServer sparams callbacks = do
ctx0 <- contextNew nullBackend sparams
let ctx1 = ctx0
{ ctxHandshakeSync = HandshakeSync (\_ _ -> return ()) sync
, ctxFragmentSize = Nothing
, ctxQUICMode = True
}
rl = newRecordLayer ctx2 callbacks
ctx2 = updateRecordLayer rl ctx1
handshake ctx2
quicDone callbacks ctx2
where
sync ctx (SendServerHello exts mEarlySecInfo handSecInfo) = do
let qexts = filterQTP exts
when (null qexts) $ do
throwCore $ Error_Protocol ("QUIC transport parameters are mssing", True, MissingExtension)
quicNotifyExtensions callbacks ctx qexts
quicInstallKeys callbacks ctx (InstallEarlyKeys mEarlySecInfo)
quicInstallKeys callbacks ctx (InstallHandshakeKeys handSecInfo)
sync ctx (SendServerFinished appSecInfo) =
quicInstallKeys callbacks ctx (InstallApplicationKeys appSecInfo)
filterQTP :: [ExtensionRaw] -> [ExtensionRaw]
filterQTP = filter (\(ExtensionRaw eid _) -> eid == extensionID_QuicTransportParameters || eid == 0xffa5)
errorTLS :: String -> IO a
errorTLS msg = throwCore $ Error_Protocol (msg, True, InternalError)
errorToAlertDescription :: TLSError -> AlertDescription
errorToAlertDescription = snd . head . errorToAlert
fromAlertDescription :: AlertDescription -> Word8
fromAlertDescription = valOfType
toAlertDescription :: Word8 -> Maybe AlertDescription
toAlertDescription = valToType
defaultSupported :: Supported
defaultSupported = def
{ supportedVersions = [TLS13]
, supportedCiphers = [ cipher_TLS13_AES256GCM_SHA384
, cipher_TLS13_AES128GCM_SHA256
, cipher_TLS13_AES128CCM_SHA256
]
, supportedGroups = [X25519,X448,P256,P384,P521]
}
quicMaxEarlyDataSize :: Int
quicMaxEarlyDataSize = 0xffffffff