Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding states and parameters for TLS 1.3 #280

Closed
wants to merge 16 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion core/Network/TLS/Context.hs
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ contextNew backend params = liftIO $ do

stvar <- newMVar st
eof <- newIORef False
established <- newIORef False
established <- newIORef NotEstablished
stats <- newIORef newMeasurement
-- we enable the reception of SSLv2 ClientHello message only in the
-- server context, where we might be dealing with an old/compat client.
Expand All @@ -141,6 +141,7 @@ contextNew backend params = liftIO $ do
tx <- newMVar newRecordState
rx <- newMVar newRecordState
hs <- newMVar Nothing
as <- newMVar []
lockWrite <- newMVar ()
lockRead <- newMVar ()
lockState <- newMVar ()
Expand All @@ -164,6 +165,7 @@ contextNew backend params = liftIO $ do
, ctxLockWrite = lockWrite
, ctxLockRead = lockRead
, ctxLockState = lockState
, ctxPendingActions = as
}

-- | create a new context on an handle.
Expand Down
20 changes: 16 additions & 4 deletions core/Network/TLS/Context/Internal.hs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ module Network.TLS.Context.Internal
-- * Context object and accessor
, Context(..)
, Hooks(..)
, Established(..)
, ctxEOF
, ctxHasSSLv2ClientHello
, ctxDisableSSLv2ClientHello
Expand Down Expand Up @@ -83,6 +84,7 @@ data Information = Information
, infoMasterSecret :: Maybe ByteString
, infoClientRandom :: Maybe ClientRandom
, infoServerRandom :: Maybe ServerRandom
, infoIsEarlyDataAccepted :: Bool
} deriving (Show,Eq)

-- | A TLS Context keep tls specific state, parameters and backend information.
Expand All @@ -93,7 +95,7 @@ data Context = Context
, ctxState :: MVar TLSState
, ctxMeasurement :: IORef Measurement
, ctxEOF_ :: IORef Bool -- ^ has the handle EOFed or not.
, ctxEstablished_ :: IORef Bool -- ^ has the handshake been done and been successful.
, ctxEstablished_ :: IORef Established -- ^ has the handshake been done and been successful.
, ctxNeedEmptyPacket :: IORef Bool -- ^ empty packet workaround for CBC guessability.
, ctxSSLv2ClientHello :: IORef Bool -- ^ enable the reception of compatibility SSLv2 client hello.
-- the flag will be set to false regardless of its initial value
Expand All @@ -108,8 +110,15 @@ data Context = Context
, ctxLockRead :: MVar () -- ^ lock to use for reading data (including updating the state)
, ctxLockState :: MVar () -- ^ lock used during read/write when receiving and sending packet.
-- it is usually nested in a write or read lock.
, ctxPendingActions :: MVar [ByteString -> IO ()]
}

data Established = NotEstablished
| EarlyDataAllowed Int
| EarlyDataNotAllowed
| Established
deriving (Eq, Show)

updateMeasure :: Context -> (Measurement -> Measurement) -> IO ()
updateMeasure ctx f = do
x <- readIORef (ctxMeasurement ctx)
Expand All @@ -135,8 +144,11 @@ contextGetInformation ctx = do
hstServerRandom st)
Nothing -> (Nothing, Nothing, Nothing)
(cipher,comp) <- failOnEitherError $ runRxState ctx $ gets $ \st -> (stCipher st, stCompression st)
let accepted = case hstate of
Just st -> hstTLS13RTT0Status st == RTT0Accepted
Nothing -> False
case (ver, cipher) of
(Just v, Just c) -> return $ Just $ Information v c comp ms cr sr
(Just v, Just c) -> return $ Just $ Information v c comp ms cr sr accepted
_ -> return Nothing

contextSend :: Context -> ByteString -> IO ()
Expand All @@ -157,7 +169,7 @@ ctxDisableSSLv2ClientHello ctx = writeIORef (ctxSSLv2ClientHello ctx) False
setEOF :: Context -> IO ()
setEOF ctx = writeIORef (ctxEOF_ ctx) True

ctxEstablished :: Context -> IO Bool
ctxEstablished :: Context -> IO Established
ctxEstablished ctx = readIORef $ ctxEstablished_ ctx

ctxWithHooks :: Context -> (Hooks -> IO a) -> IO a
Expand All @@ -166,7 +178,7 @@ ctxWithHooks ctx f = readIORef (ctxHooks ctx) >>= f
contextModifyHooks :: Context -> (Hooks -> Hooks) -> IO ()
contextModifyHooks ctx f = modifyIORef (ctxHooks ctx) f

setEstablished :: Context -> Bool -> IO ()
setEstablished :: Context -> Established -> IO ()
setEstablished ctx v = writeIORef (ctxEstablished_ ctx) v

withLog :: Context -> (Logging -> IO ()) -> IO ()
Expand Down
2 changes: 1 addition & 1 deletion core/Network/TLS/Handshake.hs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ handshakeWith ctx hs =
handleException :: Context -> IO () -> IO ()
handleException ctx f = catchException f $ \exception -> do
let tlserror = fromMaybe (Error_Misc $ show exception) $ fromException exception
setEstablished ctx False
setEstablished ctx NotEstablished
sendPacket ctx (errorToAlert tlserror) `catch` ignoreIOErr
handshakeFailed tlserror
where
Expand Down
10 changes: 8 additions & 2 deletions core/Network/TLS/Handshake/Common.hs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ module Network.TLS.Handshake.Common
, recvPacketHandshake
, onRecvStateHandshake
, extensionLookup
, getSessionData
) where

import Control.Concurrent.MVar
Expand All @@ -24,7 +25,7 @@ import Network.TLS.Context.Internal
import Network.TLS.Session
import Network.TLS.Struct
import Network.TLS.IO
import Network.TLS.State hiding (getNegotiatedProtocol)
import Network.TLS.State
import Network.TLS.Handshake.Process
import Network.TLS.Handshake.State
import Network.TLS.Record.State
Expand Down Expand Up @@ -73,7 +74,7 @@ handshakeTerminate ctx = do
}
updateMeasure ctx resetBytesCounters
-- mark the secure connection up and running.
setEstablished ctx True
setEstablished ctx Established
return ()

sendChangeCipherAndFinish :: Context
Expand Down Expand Up @@ -126,6 +127,7 @@ getSessionData ctx = do
sni <- usingState_ ctx getClientSNI
mms <- usingHState ctx (gets hstMasterSecret)
tx <- liftIO $ readMVar (ctxTxState ctx)
alpn <- usingState_ ctx getNegotiatedProtocol
case mms of
Nothing -> return Nothing
Just ms -> return $ Just SessionData
Expand All @@ -134,6 +136,10 @@ getSessionData ctx = do
, sessionCompression = compressionID $ stCompression tx
, sessionClientSNI = sni
, sessionSecret = ms
, sessionGroup = Nothing
, sessionTicketInfo = Nothing
, sessionALPN = alpn
, sessionMaxEarlyDataSize = 0
}

extensionLookup :: ExtensionID -> [ExtensionRaw] -> Maybe ByteString
Expand Down
2 changes: 1 addition & 1 deletion core/Network/TLS/Handshake/Server.hs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ handshakeServerWith sparams ctx clientHello@(ClientHello clientVersion _ clientS
unless (supportedClientInitiatedRenegotiation (ctxSupported ctx)) $ do
established <- ctxEstablished ctx
eof <- ctxEOF ctx
when (established && not eof) $
when (established == Established && not eof) $
throwCore $ Error_Protocol ("renegotiation is not allowed", False, NoRenegotiation)
-- check if policy allow this new handshake to happens
handshakeAuthorized <- withMeasure ctx (onNewHandshake $ serverHooks sparams)
Expand Down
13 changes: 13 additions & 0 deletions core/Network/TLS/Handshake/Signature.hs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ module Network.TLS.Handshake.Signature
, digitallySignDHParamsVerify
, digitallySignECDHParamsVerify
, signatureCompatible
, signatureParams
, fromPubKey
, fromPrivKey
) where

import Network.TLS.Crypto
Expand All @@ -30,6 +33,16 @@ import Network.TLS.Util

import Control.Monad.State.Strict

fromPubKey :: PubKey -> Maybe DigitalSignatureAlg
fromPubKey (PubKeyRSA _) = Just RSA
fromPubKey (PubKeyDSA _) = Just DSS
fromPubKey (PubKeyEC _) = Just ECDSA
fromPubKey _ = Nothing

fromPrivKey :: PrivKey -> Maybe DigitalSignatureAlg
fromPrivKey (PrivKeyRSA _) = Just RSA
fromPrivKey (PrivKeyDSA _) = Just DSS

signatureCompatible :: DigitalSignatureAlg -> HashAndSignatureAlgorithm -> Bool
signatureCompatible RSA (_, SignatureRSA) = True
signatureCompatible RSA (_, SignatureRSApssRSAeSHA256) = True
Expand Down
74 changes: 66 additions & 8 deletions core/Network/TLS/Handshake/State.hs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
--
module Network.TLS.Handshake.State
( HandshakeState(..)
, RTT0Status(..)
, TLS13Secret(..)
, ClientCertRequestData
, HandshakeM
, newEmptyHandshake
Expand Down Expand Up @@ -40,17 +42,27 @@ module Network.TLS.Handshake.State
, addHandshakeMessage
, updateHandshakeDigest
, getHandshakeMessages
, getHandshakeMessagesRev
, getHandshakeDigest
-- * master secret
, setMasterSecret
, setMasterSecretFromPre
-- * misc accessor
, getPendingCipher
, setServerHelloParameters
, setTLS13Group
, getTLS13Group
, setTLS13RTT0Status
, getTLS13RTT0Status
, setTLS13HandshakeMsgs
, getTLS13HandshakeMsgs
, setTLS13Secret
, getTLS13Secret
) where

import Network.TLS.Util
import Network.TLS.Struct
import Network.TLS.Struct13
import Network.TLS.Record.State
import Network.TLS.Packet
import Network.TLS.Crypto
Expand All @@ -62,6 +74,11 @@ import Control.Monad.State.Strict
import Data.X509 (CertificateChain)
import Data.ByteArray (ByteArrayAccess)

data TLS13Secret = NoSecret
| EarlySecret ByteString
| ResuptionSecret ByteString
deriving (Eq, Show)

data HandshakeKeyState = HandshakeKeyState
{ hksRemotePublicKey :: !(Maybe PubKey)
, hksLocalPrivateKey :: !(Maybe PrivKey)
Expand All @@ -87,6 +104,10 @@ data HandshakeState = HandshakeState
, hstPendingRxState :: Maybe RecordState
, hstPendingCipher :: Maybe Cipher
, hstPendingCompression :: Compression
, hstTLS13Group :: Maybe Group
, hstTLS13RTT0Status :: !RTT0Status
, hstTLS13HandshakeMsgs :: [Handshake13]
, hstTLS13Secret :: TLS13Secret
} deriving (Show)

type ClientCertRequestData = ([CertificateType],
Expand Down Expand Up @@ -125,6 +146,10 @@ newEmptyHandshake ver crand = HandshakeState
, hstPendingRxState = Nothing
, hstPendingCipher = Nothing
, hstPendingCompression = nullCompression
, hstTLS13Group = Nothing
, hstTLS13RTT0Status = RTT0None
, hstTLS13HandshakeMsgs = []
, hstTLS13Secret = NoSecret
}

runHandshake :: HandshakeState -> HandshakeM a -> (a, HandshakeState)
Expand All @@ -144,30 +169,60 @@ getRemotePublicKey = fromJust "remote public key" <$> gets (hksRemotePublicKey .
getLocalPrivateKey :: HandshakeM PrivKey
getLocalPrivateKey = fromJust "local private key" <$> gets (hksLocalPrivateKey . hstKeyState)

setServerDHParams :: ServerDHParams -> HandshakeM ()
setServerDHParams shp = modify (\hst -> hst { hstServerDHParams = Just shp })

getServerDHParams :: HandshakeM ServerDHParams
getServerDHParams = fromJust "server DH params" <$> gets hstServerDHParams

setServerECDHParams :: ServerECDHParams -> HandshakeM ()
setServerECDHParams shp = modify (\hst -> hst { hstServerECDHParams = Just shp })

getServerECDHParams :: HandshakeM ServerECDHParams
getServerECDHParams = fromJust "server ECDH params" <$> gets hstServerECDHParams

setServerDHParams :: ServerDHParams -> HandshakeM ()
setServerDHParams shp = modify (\hst -> hst { hstServerDHParams = Just shp })

setServerECDHParams :: ServerECDHParams -> HandshakeM ()
setServerECDHParams shp = modify (\hst -> hst { hstServerECDHParams = Just shp })
setDHPrivate :: DHPrivate -> HandshakeM ()
setDHPrivate shp = modify (\hst -> hst { hstDHPrivate = Just shp })

getDHPrivate :: HandshakeM DHPrivate
getDHPrivate = fromJust "server DH private" <$> gets hstDHPrivate

getGroupPrivate :: HandshakeM GroupPrivate
getGroupPrivate = fromJust "server ECDH private" <$> gets hstGroupPrivate

setDHPrivate :: DHPrivate -> HandshakeM ()
setDHPrivate shp = modify (\hst -> hst { hstDHPrivate = Just shp })

setGroupPrivate :: GroupPrivate -> HandshakeM ()
setGroupPrivate shp = modify (\hst -> hst { hstGroupPrivate = Just shp })

setTLS13Group :: Group -> HandshakeM ()
setTLS13Group g = modify (\hst -> hst { hstTLS13Group = Just g })

getTLS13Group :: HandshakeM (Maybe Group)
getTLS13Group = gets hstTLS13Group

data RTT0Status = RTT0None
| RTT0Sent
| RTT0Accepted
| RTT0Rejected
deriving (Show,Eq)

setTLS13RTT0Status :: RTT0Status -> HandshakeM ()
setTLS13RTT0Status s = modify (\hst -> hst { hstTLS13RTT0Status = s })

getTLS13RTT0Status :: HandshakeM RTT0Status
getTLS13RTT0Status = gets hstTLS13RTT0Status

setTLS13HandshakeMsgs :: [Handshake13] -> HandshakeM ()
setTLS13HandshakeMsgs hmsgs = modify (\hst -> hst { hstTLS13HandshakeMsgs = hmsgs })

getTLS13HandshakeMsgs :: HandshakeM [Handshake13]
getTLS13HandshakeMsgs = gets hstTLS13HandshakeMsgs

setTLS13Secret :: TLS13Secret -> HandshakeM ()
setTLS13Secret secret = modify (\hst -> hst { hstTLS13Secret = secret })

getTLS13Secret :: HandshakeM TLS13Secret
getTLS13Secret = gets hstTLS13Secret

setCertReqSent :: Bool -> HandshakeM ()
setCertReqSent b = modify (\hst -> hst { hstCertReqSent = b })

Expand Down Expand Up @@ -201,6 +256,9 @@ addHandshakeMessage content = modify $ \hs -> hs { hstHandshakeMessages = conten
getHandshakeMessages :: HandshakeM [ByteString]
getHandshakeMessages = gets (reverse . hstHandshakeMessages)

getHandshakeMessagesRev :: HandshakeM [ByteString]
getHandshakeMessagesRev = gets hstHandshakeMessages

updateHandshakeDigest :: ByteString -> HandshakeM ()
updateHandshakeDigest content = modify $ \hs -> hs
{ hstHandshakeDigest = case hstHandshakeDigest hs of
Expand Down
2 changes: 1 addition & 1 deletion core/Network/TLS/IO.hs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import System.IO.Error (mkIOError, eofErrorType)
checkValid :: Context -> IO ()
checkValid ctx = do
established <- ctxEstablished ctx
unless established $ throwIO ConnectionNotEstablished
when (established == NotEstablished) $ throwIO ConnectionNotEstablished
eofed <- ctxEOF ctx
when eofed $ throwIO $ mkIOError eofErrorType "data" Nothing Nothing
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does that mean sendData and recvData are possible in all 3 other states?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For TLS 1.2 or earlier and clients of TLS 1.3, only Established and NotEstablished are used.

For servers of TLS 1.3, sendData can be used only on Established.
recvData can be used on either Established or EarlyDataAllowed.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you are concerned about this, let's come back to this issue after the entire merge process is finished.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can see the coming patches on https://github.com/kazu-yamamoto/hs-tls/tree/work13

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK we can see checkValid later.


Expand Down
14 changes: 13 additions & 1 deletion core/Network/TLS/Parameters.hs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ data ClientParams = ClientParams
-- of 'supportedCiphers' with a suitable cipherlist.
, clientSupported :: Supported
, clientDebug :: DebugParams
-- ^ Client tries to send this early data in TLS 1.3 if possible.
-- If not accepted by the server, it is application's responsibility
-- to re-sent it.
, clientEarlyData :: Maybe ByteString
} deriving (Show)

defaultParamsClient :: HostName -> ByteString -> ClientParams
Expand All @@ -101,6 +105,7 @@ defaultParamsClient serverName serverId = ClientParams
, clientHooks = def
, clientSupported = def
, clientDebug = defaultDebugParams
, clientEarlyData = Nothing
}

data ServerParams = ServerParams
Expand All @@ -125,6 +130,9 @@ data ServerParams = ServerParams
, serverHooks :: ServerHooks
, serverSupported :: Supported
, serverDebug :: DebugParams
-- ^ Server accepts this size of early data in TLS 1.3.
-- 0 (or lower) means that the server does not accept early data.
, serverEarlyDataSize :: Int
} deriving (Show)

defaultParamsServer :: ServerParams
Expand All @@ -136,6 +144,7 @@ defaultParamsServer = ServerParams
, serverShared = def
, serverSupported = def
, serverDebug = defaultDebugParams
, serverEarlyDataSize = 0
}

instance Default ServerParams where
Expand Down Expand Up @@ -201,7 +210,10 @@ defaultSupported = Supported
{ supportedVersions = [TLS12,TLS11,TLS10]
, supportedCiphers = []
, supportedCompressions = [nullCompression]
, supportedHashSignatures = [ (Struct.HashSHA512, SignatureRSA)
, supportedHashSignatures = [ (HashIntrinsic, SignatureRSApssRSAeSHA256)
, (HashIntrinsic, SignatureRSApssRSAeSHA384)
, (HashIntrinsic, SignatureRSApssRSAeSHA512)
, (Struct.HashSHA512, SignatureRSA)
, (Struct.HashSHA512, SignatureECDSA)
, (Struct.HashSHA384, SignatureRSA)
, (Struct.HashSHA384, SignatureECDSA)
Expand Down
Loading