hs-tls/Network/TLS/State.hs

474 lines
19 KiB
Haskell
Raw Normal View History

{-# LANGUAGE GeneralizedNewtypeDeriving, FlexibleContexts, MultiParamTypeClasses, ExistentialQuantification, RankNTypes, CPP #-}
2010-09-09 21:47:19 +00:00
-- |
-- Module : Network.TLS.State
-- License : BSD-style
-- Maintainer : Vincent Hanquez <vincent@snarc.org>
-- Stability : experimental
-- Portability : unknown
--
-- the State module contains calls related to state initialization/manipulation
-- which is use by the Receiving module and the Sending module.
--
module Network.TLS.State
2012-03-27 07:57:51 +00:00
( TLSState(..)
, TLSSt
, runTLSState
, TLSHandshakeState(..)
, TLSCryptState(..)
, TLSMacState(..)
, newTLSState
, genTLSRandom
, withTLSRNG
, withCompression
, assert -- FIXME move somewhere else (Internal.hs ?)
, updateVerifiedData
, finishHandshakeTypeMaterial
, finishHandshakeMaterial
, makeDigest
, setMasterSecret
, setMasterSecretFromPre
, setPublicKey
, setPrivateKey
, setClientPublicKey
, setClientPrivateKey
, setClientCertSent
, getClientCertSent
, setClientCertChain
, getClientCertChain
, setClientCertRequest
, getClientCertRequest
2012-03-27 07:57:51 +00:00
, setKeyBlock
, setVersion
, setCipher
, setServerRandom
, setSecureRenegotiation
, getSecureRenegotiation
, setExtensionNPN
, getExtensionNPN
, setNegotiatedProtocol
, getNegotiatedProtocol
, setServerNextProtocolSuggest
, getServerNextProtocolSuggest
, getClientCertificateChain
, setClientCertificateChain
2012-03-27 07:57:51 +00:00
, getVerifiedData
, setSession
, getSession
, getSessionData
, isSessionResuming
, switchTxEncryption
, switchRxEncryption
, getCipherKeyExchangeType
, isClientContext
, startHandshakeClient
, updateHandshakeDigest
, getHandshakeDigest
, getCertVerifyDigest
2012-03-27 07:57:51 +00:00
, endHandshake
) where
2010-09-09 21:47:19 +00:00
import Data.Word
import Data.Maybe (isNothing)
import Network.TLS.Util
2010-09-09 21:47:19 +00:00
import Network.TLS.Struct
import Network.TLS.Wire
import Network.TLS.Packet
import Network.TLS.Crypto
import Network.TLS.Cipher
import Network.TLS.Compression
import Network.TLS.MAC
import qualified Data.ByteString as B
2011-07-07 21:21:23 +00:00
import Control.Applicative ((<$>))
2010-09-09 21:47:19 +00:00
import Control.Monad
import Control.Monad.State
import Control.Monad.Error
import Crypto.Random
import Data.Certificate.X509
2010-09-09 21:47:19 +00:00
assert :: Monad m => String -> [(String,Bool)] -> m ()
assert fctname list = forM_ list $ \ (name, assumption) -> do
2012-03-27 07:57:51 +00:00
when assumption $ fail (fctname ++ ": assumption about " ++ name ++ " failed")
2010-09-09 21:47:19 +00:00
data TLSCryptState = TLSCryptState
2012-03-27 07:57:51 +00:00
{ cstKey :: !Bytes
, cstIV :: !Bytes
, cstMacSecret :: !Bytes
} deriving (Show)
2010-09-09 21:47:19 +00:00
data TLSMacState = TLSMacState
2012-03-27 07:57:51 +00:00
{ msSequence :: Word64
} deriving (Show)
2010-09-09 21:47:19 +00:00
type ClientCertRequestData = ([CertificateType],
Maybe [(HashAlgorithm, SignatureAlgorithm)],
[DistinguishedName])
2010-09-09 21:47:19 +00:00
data TLSHandshakeState = TLSHandshakeState
2012-03-27 07:57:51 +00:00
{ hstClientVersion :: !(Version)
, hstClientRandom :: !ClientRandom
, hstServerRandom :: !(Maybe ServerRandom)
, hstMasterSecret :: !(Maybe Bytes)
, hstRSAPublicKey :: !(Maybe PublicKey)
, hstRSAPrivateKey :: !(Maybe PrivateKey)
, hstRSAClientPublicKey :: !(Maybe PublicKey)
, hstRSAClientPrivateKey :: !(Maybe PrivateKey)
2012-03-27 07:57:51 +00:00
, hstHandshakeDigest :: !HashCtx
, hstClientCertRequest :: !(Maybe ClientCertRequestData) -- ^ Set to Just-value when certificate request was received
, hstClientCertSent :: !Bool -- ^ Set to true when a client certificate chain was sent
, hstClientCertChain :: !(Maybe [X509])
2012-03-27 07:57:51 +00:00
} deriving (Show)
2010-09-09 21:47:19 +00:00
data StateRNG = forall g . CryptoRandomGen g => StateRNG g
instance Show StateRNG where
2012-03-27 07:57:51 +00:00
show _ = "rng[..]"
2010-09-09 21:47:19 +00:00
data TLSState = TLSState
2012-03-27 07:57:51 +00:00
{ stClientContext :: Bool
, stVersion :: !Version
, stHandshake :: !(Maybe TLSHandshakeState)
, stSession :: Session
, stSessionResuming :: Bool
, stTxEncrypted :: Bool
, stRxEncrypted :: Bool
, stTxCryptState :: !(Maybe TLSCryptState)
, stRxCryptState :: !(Maybe TLSCryptState)
, stTxMacState :: !(Maybe TLSMacState)
, stRxMacState :: !(Maybe TLSMacState)
, stCipher :: Maybe Cipher
, stCompression :: Compression
, stRandomGen :: StateRNG
, stSecureRenegotiation :: Bool -- RFC 5746
, stClientVerifiedData :: Bytes -- RFC 5746
, stServerVerifiedData :: Bytes -- RFC 5746
, stExtensionNPN :: Bool -- NPN draft extension
, stNegotiatedProtocol :: Maybe B.ByteString -- NPN protocol
, stServerNextProtocolSuggest :: Maybe [B.ByteString]
, stClientCertificateChain :: Maybe [X509]
2012-03-27 07:57:51 +00:00
} deriving (Show)
2010-09-09 21:47:19 +00:00
newtype TLSSt a = TLSSt { runTLSSt :: ErrorT TLSError (State TLSState) a }
2012-03-27 07:57:51 +00:00
deriving (Monad, MonadError TLSError)
instance Functor TLSSt where
2012-03-27 07:57:51 +00:00
fmap f = TLSSt . fmap f . runTLSSt
instance MonadState TLSState TLSSt where
2012-03-27 07:57:51 +00:00
put x = TLSSt (lift $ put x)
get = TLSSt (lift get)
#if MIN_VERSION_mtl(2,1,0)
state f = TLSSt (lift $ state f)
#endif
runTLSState :: TLSSt a -> TLSState -> (Either TLSError a, TLSState)
runTLSState f st = runState (runErrorT (runTLSSt f)) st
2010-09-09 21:47:19 +00:00
newTLSState :: CryptoRandomGen g => g -> TLSState
2010-09-09 21:47:19 +00:00
newTLSState rng = TLSState
2012-03-27 07:57:51 +00:00
{ stClientContext = False
, stVersion = TLS10
, stHandshake = Nothing
, stSession = Session Nothing
, stSessionResuming = False
, stTxEncrypted = False
, stRxEncrypted = False
, stTxCryptState = Nothing
, stRxCryptState = Nothing
, stTxMacState = Nothing
, stRxMacState = Nothing
, stCipher = Nothing
, stCompression = nullCompression
, stRandomGen = StateRNG rng
, stSecureRenegotiation = False
, stClientVerifiedData = B.empty
, stServerVerifiedData = B.empty
, stExtensionNPN = False
, stNegotiatedProtocol = Nothing
, stServerNextProtocolSuggest = Nothing
, stClientCertificateChain = Nothing
2012-03-27 07:57:51 +00:00
}
2010-09-09 21:47:19 +00:00
withTLSRNG :: StateRNG -> (forall g . CryptoRandomGen g => g -> Either e (a,g)) -> Either e (a, StateRNG)
withTLSRNG (StateRNG rng) f = case f rng of
2012-03-27 07:57:51 +00:00
Left err -> Left err
Right (a, rng') -> Right (a, StateRNG rng')
withCompression :: (Compression -> (Compression, a)) -> TLSSt a
withCompression f = do
2012-03-27 07:57:51 +00:00
compression <- stCompression <$> get
let (nc, a) = f compression
modify (\st -> st { stCompression = nc })
return a
genTLSRandom :: (MonadState TLSState m, MonadError TLSError m) => Int -> m Bytes
genTLSRandom n = do
2012-03-27 07:57:51 +00:00
st <- get
case withTLSRNG (stRandomGen st) (genBytes n) of
Left err -> throwError $ Error_Random $ show err
Right (bytes, rng') -> put (st { stRandomGen = rng' }) >> return bytes
makeDigest :: MonadState TLSState m => Bool -> Header -> Bytes -> m Bytes
2010-09-09 21:47:19 +00:00
makeDigest w hdr content = do
2012-03-27 07:57:51 +00:00
st <- get
let ver = stVersion st
let cst = fromJust "crypt state" $ if w then stTxCryptState st else stRxCryptState st
let ms = fromJust "mac state" $ if w then stTxMacState st else stRxMacState st
let cipher = fromJust "cipher" $ stCipher st
let hashf = hashF $ cipherHash cipher
2010-09-09 21:47:19 +00:00
2012-03-27 07:57:51 +00:00
let (macF, msg) =
if ver < TLS10
then (macSSL hashf, B.concat [ encodeWord64 $ msSequence ms, encodeHeaderNoVer hdr, content ])
else (hmac hashf 64, B.concat [ encodeWord64 $ msSequence ms, encodeHeader hdr, content ])
let digest = macF (cstMacSecret cst) msg
2010-09-09 21:47:19 +00:00
2012-03-27 07:57:51 +00:00
let newms = ms { msSequence = (msSequence ms) + 1 }
2010-09-09 21:47:19 +00:00
2012-03-27 07:57:51 +00:00
modify (\_ -> if w then st { stTxMacState = Just newms } else st { stRxMacState = Just newms })
return digest
2010-09-09 21:47:19 +00:00
updateVerifiedData :: MonadState TLSState m => Bool -> Bytes -> m ()
updateVerifiedData sending bs = do
2012-03-27 07:57:51 +00:00
cc <- isClientContext
if cc /= sending
then modify (\st -> st { stServerVerifiedData = bs })
else modify (\st -> st { stClientVerifiedData = bs })
2010-09-09 21:47:19 +00:00
finishHandshakeTypeMaterial :: HandshakeType -> Bool
finishHandshakeTypeMaterial HandshakeType_ClientHello = True
finishHandshakeTypeMaterial HandshakeType_ServerHello = True
finishHandshakeTypeMaterial HandshakeType_Certificate = True
finishHandshakeTypeMaterial HandshakeType_HelloRequest = False
finishHandshakeTypeMaterial HandshakeType_ServerHelloDone = True
finishHandshakeTypeMaterial HandshakeType_ClientKeyXchg = True
finishHandshakeTypeMaterial HandshakeType_ServerKeyXchg = True
finishHandshakeTypeMaterial HandshakeType_CertRequest = True
finishHandshakeTypeMaterial HandshakeType_CertVerify = False
finishHandshakeTypeMaterial HandshakeType_Finished = True
2012-02-07 21:24:30 +00:00
finishHandshakeTypeMaterial HandshakeType_NPN = True
2010-09-09 21:47:19 +00:00
finishHandshakeMaterial :: Handshake -> Bool
finishHandshakeMaterial = finishHandshakeTypeMaterial . typeOfHandshake
switchTxEncryption, switchRxEncryption :: MonadState TLSState m => m ()
switchTxEncryption = modify (\st -> st { stTxEncrypted = True })
switchRxEncryption = modify (\st -> st { stRxEncrypted = True })
2010-09-09 21:47:19 +00:00
setServerRandom :: MonadState TLSState m => ServerRandom -> m ()
2010-09-09 21:47:19 +00:00
setServerRandom ran = updateHandshake "srand" (\hst -> hst { hstServerRandom = Just ran })
setMasterSecret :: MonadState TLSState m => Bytes -> m ()
setMasterSecret masterSecret = do
2012-03-27 07:57:51 +00:00
hasValidHandshake "master secret"
2010-09-09 21:47:19 +00:00
2012-03-27 07:57:51 +00:00
updateHandshake "master secret" (\hst -> hst { hstMasterSecret = Just masterSecret } )
setKeyBlock
return ()
2010-09-09 21:47:19 +00:00
setMasterSecretFromPre :: MonadState TLSState m => Bytes -> m ()
setMasterSecretFromPre premasterSecret = do
2012-03-27 07:57:51 +00:00
hasValidHandshake "generate master secret"
st <- get
setMasterSecret $ genSecret st
where
genSecret st =
let hst = fromJust "handshake" $ stHandshake st in
generateMasterSecret (stVersion st)
premasterSecret
(hstClientRandom hst)
(fromJust "server random" $ hstServerRandom hst)
setPublicKey :: MonadState TLSState m => PublicKey -> m ()
2010-09-09 21:47:19 +00:00
setPublicKey pk = updateHandshake "publickey" (\hst -> hst { hstRSAPublicKey = Just pk })
setPrivateKey :: MonadState TLSState m => PrivateKey -> m ()
2010-09-09 21:47:19 +00:00
setPrivateKey pk = updateHandshake "privatekey" (\hst -> hst { hstRSAPrivateKey = Just pk })
setClientPublicKey :: MonadState TLSState m => PublicKey -> m ()
setClientPublicKey pk = updateHandshake "client publickey" (\hst -> hst { hstRSAClientPublicKey = Just pk })
setClientPrivateKey :: MonadState TLSState m => PrivateKey -> m ()
setClientPrivateKey pk = updateHandshake "client privatekey" (\hst -> hst { hstRSAClientPrivateKey = Just pk })
setClientCertSent :: MonadState TLSState m => Bool -> m ()
setClientCertSent b = updateHandshake "client cert sent" (\hst -> hst { hstClientCertSent = b })
getClientCertSent :: MonadState TLSState m => m (Maybe Bool)
getClientCertSent = do
st <- get
return (stHandshake st >>= Just . hstClientCertSent)
setClientCertChain :: MonadState TLSState m => [X509] -> m ()
setClientCertChain b = updateHandshake "client certificate chain" (\hst -> hst { hstClientCertChain = Just b })
getClientCertChain :: MonadState TLSState m => m (Maybe [X509])
getClientCertChain = do
st <- get
return (stHandshake st >>= hstClientCertChain)
setClientCertRequest :: MonadState TLSState m => ClientCertRequestData -> m ()
setClientCertRequest d = updateHandshake "client cert data" (\hst -> hst { hstClientCertRequest = Just d })
getClientCertRequest :: MonadState TLSState m => m (Maybe ClientCertRequestData)
getClientCertRequest = do
st <- get
return (stHandshake st >>= hstClientCertRequest)
2011-12-20 07:38:35 +00:00
getSessionData :: MonadState TLSState m => m (Maybe SessionData)
getSessionData = do
2012-03-27 07:57:51 +00:00
st <- get
return (stHandshake st >>= hstMasterSecret >>= wrapSessionData st)
where wrapSessionData st masterSecret = do
return $ SessionData
{ sessionVersion = stVersion st
, sessionCipher = cipherID $ fromJust "cipher" $ stCipher st
, sessionSecret = masterSecret
}
2011-12-20 07:38:35 +00:00
setSession :: MonadState TLSState m => Session -> Bool -> m ()
setSession session resuming = modify (\st -> st { stSession = session, stSessionResuming = resuming })
getSession :: MonadState TLSState m => m Session
getSession = gets stSession
isSessionResuming :: MonadState TLSState m => m Bool
isSessionResuming = gets stSessionResuming
setKeyBlock :: MonadState TLSState m => m ()
2010-09-09 21:47:19 +00:00
setKeyBlock = do
2012-03-27 07:57:51 +00:00
st <- get
let hst = fromJust "handshake" $ stHandshake st
let cc = stClientContext st
let cipher = fromJust "cipher" $ stCipher st
let keyblockSize = cipherKeyBlockSize cipher
let bulk = cipherBulk cipher
let digestSize = hashSize $ cipherHash cipher
let keySize = bulkKeySize bulk
let ivSize = bulkIVSize bulk
let kb = generateKeyBlock (stVersion st) (hstClientRandom hst)
(fromJust "server random" $ hstServerRandom hst)
(fromJust "master secret" $ hstMasterSecret hst) keyblockSize
let (cMACSecret, sMACSecret, cWriteKey, sWriteKey, cWriteIV, sWriteIV) =
fromJust "p6" $ partition6 kb (digestSize, digestSize, keySize, keySize, ivSize, ivSize)
let cstClient = TLSCryptState
{ cstKey = cWriteKey
, cstIV = cWriteIV
, cstMacSecret = cMACSecret }
let cstServer = TLSCryptState
{ cstKey = sWriteKey
, cstIV = sWriteIV
, cstMacSecret = sMACSecret }
let msClient = TLSMacState { msSequence = 0 }
let msServer = TLSMacState { msSequence = 0 }
put $ st
{ stTxCryptState = Just $ if cc then cstClient else cstServer
, stRxCryptState = Just $ if cc then cstServer else cstClient
, stTxMacState = Just $ if cc then msClient else msServer
, stRxMacState = Just $ if cc then msServer else msClient
}
2010-09-09 21:47:19 +00:00
setCipher :: MonadState TLSState m => Cipher -> m ()
setCipher cipher = modify (\st -> st { stCipher = Just cipher })
2010-09-09 21:47:19 +00:00
setVersion :: MonadState TLSState m => Version -> m ()
setVersion ver = modify (\st -> st { stVersion = ver })
2010-09-09 21:47:19 +00:00
setSecureRenegotiation :: MonadState TLSState m => Bool -> m ()
setSecureRenegotiation b = modify (\st -> st { stSecureRenegotiation = b })
getSecureRenegotiation :: MonadState TLSState m => m Bool
getSecureRenegotiation = get >>= return . stSecureRenegotiation
setExtensionNPN :: MonadState TLSState m => Bool -> m ()
setExtensionNPN b = modify (\st -> st { stExtensionNPN = b })
getExtensionNPN :: MonadState TLSState m => m Bool
getExtensionNPN = get >>= return . stExtensionNPN
setNegotiatedProtocol :: MonadState TLSState m => B.ByteString -> m ()
setNegotiatedProtocol s = modify (\st -> st { stNegotiatedProtocol = Just s })
getNegotiatedProtocol :: MonadState TLSState m => m (Maybe B.ByteString)
getNegotiatedProtocol = get >>= return . stNegotiatedProtocol
setServerNextProtocolSuggest :: MonadState TLSState m => [B.ByteString] -> m ()
setServerNextProtocolSuggest ps = modify (\st -> st { stServerNextProtocolSuggest = Just ps})
getServerNextProtocolSuggest :: MonadState TLSState m => m (Maybe [B.ByteString])
getServerNextProtocolSuggest = get >>= return . stServerNextProtocolSuggest
setClientCertificateChain :: MonadState TLSState m => [X509] -> m ()
setClientCertificateChain s = modify (\st -> st { stClientCertificateChain = Just s })
getClientCertificateChain :: MonadState TLSState m => m (Maybe [X509])
getClientCertificateChain = get >>= return . stClientCertificateChain
getCipherKeyExchangeType :: MonadState TLSState m => m (Maybe CipherKeyExchangeType)
2011-08-07 09:03:34 +00:00
getCipherKeyExchangeType = get >>= return . (maybe Nothing (Just . cipherKeyExchange) . stCipher)
getVerifiedData :: MonadState TLSState m => Bool -> m Bytes
getVerifiedData client = get >>= return . (if client then stClientVerifiedData else stServerVerifiedData)
isClientContext :: MonadState TLSState m => m Bool
isClientContext = get >>= return . stClientContext
2010-09-09 21:47:19 +00:00
-- create a new empty handshake state
newEmptyHandshake :: Version -> ClientRandom -> HashCtx -> TLSHandshakeState
newEmptyHandshake ver crand digestInit = TLSHandshakeState
2012-03-27 07:57:51 +00:00
{ hstClientVersion = ver
, hstClientRandom = crand
, hstServerRandom = Nothing
, hstMasterSecret = Nothing
, hstRSAPublicKey = Nothing
, hstRSAPrivateKey = Nothing
, hstRSAClientPublicKey = Nothing
, hstRSAClientPrivateKey = Nothing
2012-03-27 07:57:51 +00:00
, hstHandshakeDigest = digestInit
, hstClientCertRequest = Nothing
, hstClientCertSent = False
, hstClientCertChain = Nothing
2012-03-27 07:57:51 +00:00
}
2010-09-09 21:47:19 +00:00
startHandshakeClient :: MonadState TLSState m => Version -> ClientRandom -> m ()
2010-09-09 21:47:19 +00:00
startHandshakeClient ver crand = do
2012-03-27 07:57:51 +00:00
-- FIXME check if handshake is already not null
let initCtx = if ver < TLS12 then hashMD5SHA1 else hashSHA256
chs <- get >>= return . stHandshake
when (isNothing chs) $
modify (\st -> st { stHandshake = Just $ newEmptyHandshake ver crand initCtx })
2010-09-09 21:47:19 +00:00
hasValidHandshake :: MonadState TLSState m => String -> m ()
hasValidHandshake name = get >>= \st -> assert name [ ("valid handshake", isNothing $ stHandshake st) ]
2010-09-09 21:47:19 +00:00
updateHandshake :: MonadState TLSState m => String -> (TLSHandshakeState -> TLSHandshakeState) -> m ()
2010-09-09 21:47:19 +00:00
updateHandshake n f = do
2012-03-27 07:57:51 +00:00
hasValidHandshake n
modify (\st -> st { stHandshake = f <$> stHandshake st })
2010-09-09 21:47:19 +00:00
updateHandshakeDigest :: MonadState TLSState m => Bytes -> m ()
updateHandshakeDigest content = updateHandshake "update digest" $ \hs ->
2012-03-27 07:57:51 +00:00
hs { hstHandshakeDigest = hashUpdate (hstHandshakeDigest hs) content }
2010-09-09 21:47:19 +00:00
getHandshakeDigest :: MonadState TLSState m => Bool -> m Bytes
2010-09-09 21:47:19 +00:00
getHandshakeDigest client = do
2012-03-27 07:57:51 +00:00
st <- get
let hst = fromJust "handshake" $ stHandshake st
let hashctx = hstHandshakeDigest hst
let msecret = fromJust "master secret" $ hstMasterSecret hst
return $ (if client then generateClientFinished else generateServerFinished) (stVersion st) msecret hashctx
2010-09-09 21:47:19 +00:00
getCertVerifyDigest :: MonadState TLSState m => m Bytes
getCertVerifyDigest = do
st <- get
let hst = fromJust "handshake" $ stHandshake st
let hashctx = hstHandshakeDigest hst
return (hashFinal hashctx)
endHandshake :: MonadState TLSState m => m ()
endHandshake = modify (\st -> st { stHandshake = Nothing })