expand tabs.

This commit is contained in:
Vincent Hanquez 2012-03-27 08:57:51 +01:00
parent 3b4baf2f91
commit 9da6b9c8c8
20 changed files with 1662 additions and 1662 deletions

View file

@ -7,9 +7,9 @@
--
module Network.TLS.Cap
( hasHelloExtensions
, hasExplicitBlockIV
) where
( hasHelloExtensions
, hasExplicitBlockIV
) where
import Network.TLS.Struct

View file

@ -8,16 +8,16 @@
-- Portability : unknown
--
module Network.TLS.Cipher
( BulkFunctions(..)
, CipherKeyExchangeType(..)
, Bulk(..)
, Hash(..)
, Cipher(..)
, cipherKeyBlockSize
, Key
, IV
, cipherExchangeNeedMoreData
) where
( BulkFunctions(..)
, CipherKeyExchangeType(..)
, Bulk(..)
, Hash(..)
, Cipher(..)
, cipherKeyBlockSize
, Key
, IV
, cipherExchangeNeedMoreData
) where
import Data.Word
import Network.TLS.Struct (Version(..))
@ -29,59 +29,59 @@ type Key = B.ByteString
type IV = B.ByteString
data BulkFunctions =
BulkNoneF -- special value for 0
| BulkBlockF (Key -> IV -> B.ByteString -> B.ByteString)
(Key -> IV -> B.ByteString -> B.ByteString)
| BulkStreamF (Key -> IV)
(IV -> B.ByteString -> (B.ByteString, IV))
(IV -> B.ByteString -> (B.ByteString, IV))
BulkNoneF -- special value for 0
| BulkBlockF (Key -> IV -> B.ByteString -> B.ByteString)
(Key -> IV -> B.ByteString -> B.ByteString)
| BulkStreamF (Key -> IV)
(IV -> B.ByteString -> (B.ByteString, IV))
(IV -> B.ByteString -> (B.ByteString, IV))
data CipherKeyExchangeType =
CipherKeyExchange_RSA
| CipherKeyExchange_DH_Anon
| CipherKeyExchange_DHE_RSA
| CipherKeyExchange_ECDHE_RSA
| CipherKeyExchange_DHE_DSS
| CipherKeyExchange_DH_DSS
| CipherKeyExchange_DH_RSA
| CipherKeyExchange_ECDH_ECDSA
| CipherKeyExchange_ECDH_RSA
| CipherKeyExchange_ECDHE_ECDSA
deriving (Show,Eq)
CipherKeyExchange_RSA
| CipherKeyExchange_DH_Anon
| CipherKeyExchange_DHE_RSA
| CipherKeyExchange_ECDHE_RSA
| CipherKeyExchange_DHE_DSS
| CipherKeyExchange_DH_DSS
| CipherKeyExchange_DH_RSA
| CipherKeyExchange_ECDH_ECDSA
| CipherKeyExchange_ECDH_RSA
| CipherKeyExchange_ECDHE_ECDSA
deriving (Show,Eq)
data Bulk = Bulk
{ bulkName :: String
, bulkKeySize :: Int
, bulkIVSize :: Int
, bulkBlockSize :: Int
, bulkF :: BulkFunctions
}
{ bulkName :: String
, bulkKeySize :: Int
, bulkIVSize :: Int
, bulkBlockSize :: Int
, bulkF :: BulkFunctions
}
data Hash = Hash
{ hashName :: String
, hashSize :: Int
, hashF :: B.ByteString -> B.ByteString
}
{ hashName :: String
, hashSize :: Int
, hashF :: B.ByteString -> B.ByteString
}
-- | Cipher algorithm
data Cipher = Cipher
{ cipherID :: Word16
, cipherName :: String
, cipherHash :: Hash
, cipherBulk :: Bulk
, cipherKeyExchange :: CipherKeyExchangeType
, cipherMinVer :: Maybe Version
}
{ cipherID :: Word16
, cipherName :: String
, cipherHash :: Hash
, cipherBulk :: Bulk
, cipherKeyExchange :: CipherKeyExchangeType
, cipherMinVer :: Maybe Version
}
cipherKeyBlockSize :: Cipher -> Int
cipherKeyBlockSize cipher = 2 * (hashSize (cipherHash cipher) + bulkIVSize bulk + bulkKeySize bulk)
where bulk = cipherBulk cipher
where bulk = cipherBulk cipher
instance Show Cipher where
show c = cipherName c
show c = cipherName c
instance Eq Cipher where
(==) c1 c2 = cipherID c1 == cipherID c2
(==) c1 c2 = cipherID c1 == cipherID c2
cipherExchangeNeedMoreData :: CipherKeyExchangeType -> Bool
cipherExchangeNeedMoreData CipherKeyExchange_RSA = False

View file

@ -8,18 +8,18 @@
-- Portability : unknown
--
module Network.TLS.Compression
( CompressionC(..)
, Compression(..)
, nullCompression
( CompressionC(..)
, Compression(..)
, nullCompression
-- * member redefined for the class abstraction
, compressionID
, compressionDeflate
, compressionInflate
-- * member redefined for the class abstraction
, compressionID
, compressionDeflate
, compressionInflate
-- * helper
, compressionIntersectID
) where
-- * helper
, compressionIntersectID
) where
import Data.Word
import Data.ByteString (ByteString)
@ -27,9 +27,9 @@ import Control.Arrow (first)
-- | supported compression algorithms need to be part of this class
class CompressionC a where
compressionCID :: a -> Word8
compressionCDeflate :: a -> ByteString -> (a, ByteString)
compressionCInflate :: a -> ByteString -> (a, ByteString)
compressionCID :: a -> Word8
compressionCDeflate :: a -> ByteString -> (a, ByteString)
compressionCInflate :: a -> ByteString -> (a, ByteString)
-- | every compression need to be wrapped in this, to fit in structure
data Compression = forall a . CompressionC a => Compression a
@ -49,7 +49,7 @@ compressionInflate :: ByteString -> Compression -> (Compression, ByteString)
compressionInflate bytes (Compression c) = first Compression $ compressionCInflate c bytes
instance Show Compression where
show = show . compressionID
show = show . compressionID
-- | intersect a list of ids commonly given by the other side with a list of compression
-- the function keeps the list of compression in order, to be able to find quickly the prefered
@ -60,9 +60,9 @@ compressionIntersectID l ids = filter (\c -> elem (compressionID c) ids) l
data NullCompression = NullCompression
instance CompressionC NullCompression where
compressionCID _ = 0
compressionCDeflate s b = (s, b)
compressionCInflate s b = (s, b)
compressionCID _ = 0
compressionCDeflate s b = (s, b)
compressionCInflate s b = (s, b)
-- | default null compression
nullCompression :: Compression

View file

@ -6,54 +6,54 @@
-- Portability : unknown
--
module Network.TLS.Context
(
-- * Context configuration
Params(..)
, Logging(..)
, SessionData(..)
, Measurement(..)
, CertificateUsage(..)
, CertificateRejectReason(..)
, defaultLogging
, defaultParamsClient
, defaultParamsServer
(
-- * Context configuration
Params(..)
, Logging(..)
, SessionData(..)
, Measurement(..)
, CertificateUsage(..)
, CertificateRejectReason(..)
, defaultLogging
, defaultParamsClient
, defaultParamsServer
-- * Context object and accessor
, Backend(..)
, Context
, ctxParams
, ctxConnection
, ctxEOF
, ctxEstablished
, ctxLogging
, setEOF
, setEstablished
, connectionFlush
, connectionSend
, connectionRecv
, updateMeasure
, withMeasure
-- * Context object and accessor
, Backend(..)
, Context
, ctxParams
, ctxConnection
, ctxEOF
, ctxEstablished
, ctxLogging
, setEOF
, setEstablished
, connectionFlush
, connectionSend
, connectionRecv
, updateMeasure
, withMeasure
-- * deprecated types
, TLSParams
, TLSLogging
, TLSCertificateUsage
, TLSCertificateRejectReason
, TLSCtx
-- * deprecated types
, TLSParams
, TLSLogging
, TLSCertificateUsage
, TLSCertificateRejectReason
, TLSCtx
-- * deprecated values
, defaultParams
-- * deprecated values
, defaultParams
-- * New contexts
, contextNew
, contextNewOnHandle
-- * New contexts
, contextNew
, contextNewOnHandle
-- * Using context states
, throwCore
, usingState
, usingState_
, getStateRNG
) where
-- * Using context states
, throwCore
, usingState
, usingState_
, getStateRNG
) where
import Network.TLS.Struct
import Network.TLS.Cipher
@ -77,11 +77,11 @@ import System.IO (Handle, hSetBuffering, BufferMode(..), hFlush)
import Prelude hiding (catch)
data Logging = Logging
{ loggingPacketSent :: String -> IO ()
, loggingPacketRecv :: String -> IO ()
, loggingIOSent :: B.ByteString -> IO ()
, loggingIORecv :: Header -> B.ByteString -> IO ()
}
{ loggingPacketSent :: String -> IO ()
, loggingPacketRecv :: String -> IO ()
, loggingIOSent :: B.ByteString -> IO ()
, loggingIORecv :: Header -> B.ByteString -> IO ()
}
data ClientParams = ClientParams
data ServerParams = ServerParams
@ -89,61 +89,61 @@ data ServerParams = ServerParams
data RoleParams = Client ClientParams | Server ServerParams
data Params = Params
{ pConnectVersion :: Version -- ^ version to use on client connection.
, pAllowedVersions :: [Version] -- ^ allowed versions that we can use.
, pCiphers :: [Cipher] -- ^ all ciphers supported ordered by priority.
, pCompressions :: [Compression] -- ^ all compression supported ordered by priority.
, pWantClientCert :: Bool -- ^ request a certificate from client.
-- use by server only.
, pUseSecureRenegotiation :: Bool -- ^ notify that we want to use secure renegotation
, pUseSession :: Bool -- ^ generate new session if specified
, pCertificates :: [(X509, Maybe PrivateKey)] -- ^ the cert chain for this context with the associated keys if any.
, pLogging :: Logging -- ^ callback for logging
, onHandshake :: Measurement -> IO Bool -- ^ callback on a beggining of handshake
, onCertificatesRecv :: [X509] -> IO CertificateUsage -- ^ callback to verify received cert chain.
, onSessionResumption :: SessionID -> IO (Maybe SessionData) -- ^ callback to maybe resume session on server.
, onSessionEstablished :: SessionID -> SessionData -> IO () -- ^ callback when session have been established
, onSessionInvalidated :: SessionID -> IO () -- ^ callback when session is invalidated by error
{ pConnectVersion :: Version -- ^ version to use on client connection.
, pAllowedVersions :: [Version] -- ^ allowed versions that we can use.
, pCiphers :: [Cipher] -- ^ all ciphers supported ordered by priority.
, pCompressions :: [Compression] -- ^ all compression supported ordered by priority.
, pWantClientCert :: Bool -- ^ request a certificate from client.
-- use by server only.
, pUseSecureRenegotiation :: Bool -- ^ notify that we want to use secure renegotation
, pUseSession :: Bool -- ^ generate new session if specified
, pCertificates :: [(X509, Maybe PrivateKey)] -- ^ the cert chain for this context with the associated keys if any.
, pLogging :: Logging -- ^ callback for logging
, onHandshake :: Measurement -> IO Bool -- ^ callback on a beggining of handshake
, onCertificatesRecv :: [X509] -> IO CertificateUsage -- ^ callback to verify received cert chain.
, onSessionResumption :: SessionID -> IO (Maybe SessionData) -- ^ callback to maybe resume session on server.
, onSessionEstablished :: SessionID -> SessionData -> IO () -- ^ callback when session have been established
, onSessionInvalidated :: SessionID -> IO () -- ^ callback when session is invalidated by error
, onSuggestNextProtocols :: IO (Maybe [B.ByteString]) -- ^ suggested next protocols accoring to the next protocol negotiation extension.
, onNPNServerSuggest :: Maybe ([B.ByteString] -> IO B.ByteString)
, sessionResumeWith :: Maybe (SessionID, SessionData) -- ^ try to establish a connection using this session.
, roleParams :: RoleParams
}
, sessionResumeWith :: Maybe (SessionID, SessionData) -- ^ try to establish a connection using this session.
, roleParams :: RoleParams
}
defaultLogging :: Logging
defaultLogging = Logging
{ loggingPacketSent = (\_ -> return ())
, loggingPacketRecv = (\_ -> return ())
, loggingIOSent = (\_ -> return ())
, loggingIORecv = (\_ _ -> return ())
}
{ loggingPacketSent = (\_ -> return ())
, loggingPacketRecv = (\_ -> return ())
, loggingIOSent = (\_ -> return ())
, loggingIORecv = (\_ _ -> return ())
}
defaultParamsClient :: Params
defaultParamsClient = Params
{ pConnectVersion = TLS10
, pAllowedVersions = [TLS10,TLS11,TLS12]
, pCiphers = []
, pCompressions = [nullCompression]
, pWantClientCert = False
, pUseSecureRenegotiation = True
, pUseSession = True
, pCertificates = []
, pLogging = defaultLogging
, onHandshake = (\_ -> return True)
, onCertificatesRecv = (\_ -> return CertificateUsageAccept)
, onSessionResumption = (\_ -> return Nothing)
, onSessionEstablished = (\_ _ -> return ())
, onSessionInvalidated = (\_ -> return ())
{ pConnectVersion = TLS10
, pAllowedVersions = [TLS10,TLS11,TLS12]
, pCiphers = []
, pCompressions = [nullCompression]
, pWantClientCert = False
, pUseSecureRenegotiation = True
, pUseSession = True
, pCertificates = []
, pLogging = defaultLogging
, onHandshake = (\_ -> return True)
, onCertificatesRecv = (\_ -> return CertificateUsageAccept)
, onSessionResumption = (\_ -> return Nothing)
, onSessionEstablished = (\_ _ -> return ())
, onSessionInvalidated = (\_ -> return ())
, onSuggestNextProtocols = return Nothing
, onNPNServerSuggest = Nothing
, sessionResumeWith = Nothing
, roleParams = Client $ ClientParams
}
, sessionResumeWith = Nothing
, roleParams = Client $ ClientParams
}
defaultParamsServer :: Params
defaultParamsServer = defaultParamsClient
{ roleParams = Server $ ServerParams
}
{ roleParams = Server $ ServerParams
}
defaultParams :: Params
defaultParams = defaultParamsClient
@ -151,45 +151,45 @@ defaultParams = defaultParamsClient
instance Show Params where
show p = "Params { " ++ (intercalate "," $ map (\(k,v) -> k ++ "=" ++ v)
[ ("connectVersion", show $ pConnectVersion p)
, ("allowedVersions", show $ pAllowedVersions p)
, ("ciphers", show $ pCiphers p)
, ("compressions", show $ pCompressions p)
, ("want-client-cert", show $ pWantClientCert p)
, ("certificates", show $ length $ pCertificates p)
]) ++ " }"
show p = "Params { " ++ (intercalate "," $ map (\(k,v) -> k ++ "=" ++ v)
[ ("connectVersion", show $ pConnectVersion p)
, ("allowedVersions", show $ pAllowedVersions p)
, ("ciphers", show $ pCiphers p)
, ("compressions", show $ pCompressions p)
, ("want-client-cert", show $ pWantClientCert p)
, ("certificates", show $ length $ pCertificates p)
]) ++ " }"
-- | Certificate and Chain rejection reason
data CertificateRejectReason =
CertificateRejectExpired
| CertificateRejectRevoked
| CertificateRejectUnknownCA
| CertificateRejectOther String
deriving (Show,Eq)
CertificateRejectExpired
| CertificateRejectRevoked
| CertificateRejectUnknownCA
| CertificateRejectOther String
deriving (Show,Eq)
-- | Certificate Usage callback possible returns values.
data CertificateUsage =
CertificateUsageAccept -- ^ usage of certificate accepted
| CertificateUsageReject CertificateRejectReason -- ^ usage of certificate rejected
deriving (Show,Eq)
CertificateUsageAccept -- ^ usage of certificate accepted
| CertificateUsageReject CertificateRejectReason -- ^ usage of certificate rejected
deriving (Show,Eq)
-- |
data Backend = Backend
{ backendFlush :: IO () -- ^ Flush the connection sending buffer, if any.
, backendSend :: ByteString -> IO () -- ^ Send a bytestring through the connection.
, backendRecv :: Int -> IO ByteString -- ^ Receive specified number of bytes from the connection.
}
{ backendFlush :: IO () -- ^ Flush the connection sending buffer, if any.
, backendSend :: ByteString -> IO () -- ^ Send a bytestring through the connection.
, backendRecv :: Int -> IO ByteString -- ^ Receive specified number of bytes from the connection.
}
-- | A TLS Context keep tls specific state, parameters and backend information.
data Context = Context
{ ctxConnection :: Backend -- ^ return the backend object associated with this context
, ctxParams :: Params
, 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.
}
{ ctxConnection :: Backend -- ^ return the backend object associated with this context
, ctxParams :: Params
, 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.
}
-- deprecated types, setup as aliases for compatibility.
type TLSParams = Params
@ -238,23 +238,23 @@ contextNew :: (MonadIO m, CryptoRandomGen rng)
-> m Context
contextNew backend params rng = liftIO $ do
let clientContext = case roleParams params of
Client {} -> True
Server {} -> False
let st = (newTLSState rng) { stClientContext = clientContext }
let clientContext = case roleParams params of
Client {} -> True
Server {} -> False
let st = (newTLSState rng) { stClientContext = clientContext }
stvar <- newMVar st
eof <- newIORef False
established <- newIORef False
stats <- newIORef newMeasurement
return $ Context
{ ctxConnection = backend
, ctxParams = params
, ctxState = stvar
, ctxMeasurement = stats
, ctxEOF_ = eof
, ctxEstablished_ = established
}
stvar <- newMVar st
eof <- newIORef False
established <- newIORef False
stats <- newIORef newMeasurement
return $ Context
{ ctxConnection = backend
, ctxParams = params
, ctxState = stvar
, ctxMeasurement = stats
, ctxEOF_ = eof
, ctxEstablished_ = established
}
-- | create a new context on an handle.
contextNewOnHandle :: (MonadIO m, CryptoRandomGen rng)
@ -263,8 +263,8 @@ contextNewOnHandle :: (MonadIO m, CryptoRandomGen rng)
-> rng -- ^ Random number generator associated with this context.
-> m Context
contextNewOnHandle handle params st =
liftIO (hSetBuffering handle NoBuffering) >> contextNew backend params st
where backend = Backend (hFlush handle) (B.hPut handle) (B.hGet handle)
liftIO (hSetBuffering handle NoBuffering) >> contextNew backend params st
where backend = Backend (hFlush handle) (B.hPut handle) (B.hGet handle)
throwCore :: (MonadIO m, Exception e) => e -> m a
throwCore = liftIO . throwIO
@ -272,16 +272,16 @@ throwCore = liftIO . throwIO
usingState :: MonadIO m => Context -> TLSSt a -> m (Either TLSError a)
usingState ctx f =
liftIO $ modifyMVar (ctxState ctx) $ \st ->
let (a, newst) = runTLSState f st
in newst `seq` return (newst, a)
liftIO $ modifyMVar (ctxState ctx) $ \st ->
let (a, newst) = runTLSState f st
in newst `seq` return (newst, a)
usingState_ :: MonadIO m => Context -> TLSSt a -> m a
usingState_ ctx f = do
ret <- usingState ctx f
case ret of
Left err -> throwCore err
Right r -> return r
ret <- usingState ctx f
case ret of
Left err -> throwCore err
Right r -> return r
getStateRNG :: MonadIO m => Context -> Int -> m Bytes
getStateRNG ctx n = usingState_ ctx (genTLSRandom n)

View file

@ -8,25 +8,25 @@
-- Portability : unknown
--
module Network.TLS.Core
(
-- * Internal packet sending and receiving
sendPacket
, recvPacket
(
-- * Internal packet sending and receiving
sendPacket
, recvPacket
-- * Initialisation and Termination of context
, bye
, handshake
, HandshakeFailed(..)
, ConnectionNotEstablished(..)
-- * Initialisation and Termination of context
, bye
, handshake
, HandshakeFailed(..)
, ConnectionNotEstablished(..)
-- * Next Protocol Negotiation
, getNegotiatedProtocol
-- * Next Protocol Negotiation
, getNegotiatedProtocol
-- * High level API
, sendData
, recvData
, recvData'
) where
-- * High level API
, sendData
, recvData
, recvData'
) where
import Network.TLS.Context
import Network.TLS.Struct
@ -54,10 +54,10 @@ import System.IO.Error (mkIOError, eofErrorType)
import Prelude hiding (catch)
data HandshakeFailed = HandshakeFailed TLSError
deriving (Show,Eq,Typeable)
deriving (Show,Eq,Typeable)
data ConnectionNotEstablished = ConnectionNotEstablished
deriving (Show,Eq,Typeable)
deriving (Show,Eq,Typeable)
instance Exception HandshakeFailed
instance Exception ConnectionNotEstablished
@ -71,29 +71,29 @@ handshakeFailed err = throwIO $ HandshakeFailed err
checkValid :: MonadIO m => Context -> m ()
checkValid ctx = do
established <- ctxEstablished ctx
unless established $ liftIO $ throwIO ConnectionNotEstablished
eofed <- ctxEOF ctx
when eofed $ liftIO $ throwIO $ mkIOError eofErrorType "data" Nothing Nothing
established <- ctxEstablished ctx
unless established $ liftIO $ throwIO ConnectionNotEstablished
eofed <- ctxEOF ctx
when eofed $ liftIO $ throwIO $ mkIOError eofErrorType "data" Nothing Nothing
readExact :: MonadIO m => Context -> Int -> m Bytes
readExact ctx sz = do
hdrbs <- liftIO $ connectionRecv ctx sz
when (B.length hdrbs < sz) $ do
setEOF ctx
if B.null hdrbs
then throwCore Error_EOF
else throwCore (Error_Packet ("partial packet: expecting " ++ show sz ++ " bytes, got: " ++ (show $B.length hdrbs)))
return hdrbs
hdrbs <- liftIO $ connectionRecv ctx sz
when (B.length hdrbs < sz) $ do
setEOF ctx
if B.null hdrbs
then throwCore Error_EOF
else throwCore (Error_Packet ("partial packet: expecting " ++ show sz ++ " bytes, got: " ++ (show $B.length hdrbs)))
return hdrbs
recvRecord :: MonadIO m => Context -> m (Either TLSError (Record Plaintext))
recvRecord ctx = readExact ctx 5 >>= either (return . Left) recvLength . decodeHeader
where recvLength header@(Header _ _ readlen)
| readlen > 16384 + 2048 = return $ Left $ Error_Protocol ("record exceeding maximum size", True, RecordOverflow)
| otherwise = do
content <- readExact ctx (fromIntegral readlen)
liftIO $ (loggingIORecv $ ctxLogging ctx) header content
usingState ctx $ disengageRecord $ rawToRecord header (fragmentCiphertext content)
where recvLength header@(Header _ _ readlen)
| readlen > 16384 + 2048 = return $ Left $ Error_Protocol ("record exceeding maximum size", True, RecordOverflow)
| otherwise = do
content <- readExact ctx (fromIntegral readlen)
liftIO $ (loggingIORecv $ ctxLogging ctx) header content
usingState ctx $ disengageRecord $ rawToRecord header (fragmentCiphertext content)
-- | receive one packet from the context that contains 1 or
@ -101,45 +101,45 @@ recvRecord ctx = readExact ctx 5 >>= either (return . Left) recvLength . decodeH
-- TLSError if the packet is unexpected or malformed
recvPacket :: MonadIO m => Context -> m (Either TLSError Packet)
recvPacket ctx = do
erecord <- recvRecord ctx
case erecord of
Left err -> return $ Left err
Right record -> do
pkt <- usingState ctx $ processPacket record
case pkt of
Right p -> liftIO $ (loggingPacketRecv $ ctxLogging ctx) $ show p
_ -> return ()
return pkt
erecord <- recvRecord ctx
case erecord of
Left err -> return $ Left err
Right record -> do
pkt <- usingState ctx $ processPacket record
case pkt of
Right p -> liftIO $ (loggingPacketRecv $ ctxLogging ctx) $ show p
_ -> return ()
return pkt
recvPacketHandshake :: MonadIO m => Context -> m [Handshake]
recvPacketHandshake ctx = do
pkts <- recvPacket ctx
case pkts of
Right (Handshake l) -> return l
Right x -> fail ("unexpected type received. expecting handshake and got: " ++ show x)
Left err -> throwCore err
pkts <- recvPacket ctx
case pkts of
Right (Handshake l) -> return l
Right x -> fail ("unexpected type received. expecting handshake and got: " ++ show x)
Left err -> throwCore err
data RecvState m =
RecvStateNext (Packet -> m (RecvState m))
| RecvStateHandshake (Handshake -> m (RecvState m))
| RecvStateDone
RecvStateNext (Packet -> m (RecvState m))
| RecvStateHandshake (Handshake -> m (RecvState m))
| RecvStateDone
runRecvState :: MonadIO m => Context -> RecvState m -> m ()
runRecvState _ (RecvStateDone) = return ()
runRecvState ctx (RecvStateNext f) = recvPacket ctx >>= either throwCore f >>= runRecvState ctx
runRecvState ctx iniState = recvPacketHandshake ctx >>= loop iniState >>= runRecvState ctx
where
loop :: MonadIO m => RecvState m -> [Handshake] -> m (RecvState m)
loop recvState [] = return recvState
loop (RecvStateHandshake f) (x:xs) = do
nstate <- f x
usingState_ ctx $ processHandshake x
loop nstate xs
loop _ _ = unexpected "spurious handshake" Nothing
where
loop :: MonadIO m => RecvState m -> [Handshake] -> m (RecvState m)
loop recvState [] = return recvState
loop (RecvStateHandshake f) (x:xs) = do
nstate <- f x
usingState_ ctx $ processHandshake x
loop nstate xs
loop _ _ = unexpected "spurious handshake" Nothing
sendChangeCipherAndFinish :: MonadIO m => Context -> Bool -> m ()
sendChangeCipherAndFinish ctx isClient = do
sendPacket ctx ChangeCipherSpec
sendPacket ctx ChangeCipherSpec
when isClient $ do
suggest <- usingState_ ctx $ getServerNextProtocolSuggest
case (onNPNServerSuggest (ctxParams ctx), suggest) of
@ -151,34 +151,34 @@ sendChangeCipherAndFinish ctx isClient = do
(Just _, Nothing) -> return ()
-- client didn't offer. do nothing.
(Nothing, _) -> return ()
liftIO $ connectionFlush ctx
cf <- usingState_ ctx $ getHandshakeDigest isClient
sendPacket ctx (Handshake [Finished cf])
liftIO $ connectionFlush ctx
liftIO $ connectionFlush ctx
cf <- usingState_ ctx $ getHandshakeDigest isClient
sendPacket ctx (Handshake [Finished cf])
liftIO $ connectionFlush ctx
recvChangeCipherAndFinish :: MonadIO m => Context -> m ()
recvChangeCipherAndFinish ctx = runRecvState ctx (RecvStateNext expectChangeCipher)
where
expectChangeCipher ChangeCipherSpec = return $ RecvStateHandshake expectFinish
expectChangeCipher p = unexpected (show p) (Just "change cipher")
expectFinish (Finished _) = return RecvStateDone
expectFinish p = unexpected (show p) (Just "Handshake Finished")
where
expectChangeCipher ChangeCipherSpec = return $ RecvStateHandshake expectFinish
expectChangeCipher p = unexpected (show p) (Just "change cipher")
expectFinish (Finished _) = return RecvStateDone
expectFinish p = unexpected (show p) (Just "Handshake Finished")
unexpected :: MonadIO m => String -> Maybe [Char] -> m a
unexpected msg expected = throwCore $ Error_Packet_unexpected msg (maybe "" (" expected: " ++) expected)
newSession :: MonadIO m => Context -> m Session
newSession ctx
| pUseSession $ ctxParams ctx = getStateRNG ctx 32 >>= return . Session . Just
| otherwise = return $ Session Nothing
| pUseSession $ ctxParams ctx = getStateRNG ctx 32 >>= return . Session . Just
| otherwise = return $ Session Nothing
-- | Send one packet to the context
sendPacket :: MonadIO m => Context -> Packet -> m ()
sendPacket ctx pkt = do
liftIO $ (loggingPacketSent $ ctxLogging ctx) (show pkt)
dataToSend <- usingState_ ctx $ writePacket pkt
liftIO $ (loggingIOSent $ ctxLogging ctx) dataToSend
liftIO $ connectionSend ctx dataToSend
liftIO $ (loggingPacketSent $ ctxLogging ctx) (show pkt)
dataToSend <- usingState_ ctx $ writePacket pkt
liftIO $ (loggingIOSent $ ctxLogging ctx) dataToSend
liftIO $ connectionSend ctx dataToSend
-- | notify the context that this side wants to close connection.
-- this is important that it is called before closing the handle, otherwise
@ -196,249 +196,249 @@ getNegotiatedProtocol ctx = usingState_ ctx S.getNegotiatedProtocol
-- | when a new handshake is done, wrap up & clean up.
handshakeTerminate :: MonadIO m => Context -> m ()
handshakeTerminate ctx = do
session <- usingState_ ctx getSession
-- only callback the session established if we have a session
case session of
Session (Just sessionId) -> do
sessionData <- usingState_ ctx getSessionData
liftIO $ (onSessionEstablished $ ctxParams ctx) sessionId (fromJust sessionData)
_ -> return ()
-- forget all handshake data now and reset bytes counters.
usingState_ ctx endHandshake
updateMeasure ctx resetBytesCounters
-- mark the secure connection up and running.
setEstablished ctx True
return ()
session <- usingState_ ctx getSession
-- only callback the session established if we have a session
case session of
Session (Just sessionId) -> do
sessionData <- usingState_ ctx getSessionData
liftIO $ (onSessionEstablished $ ctxParams ctx) sessionId (fromJust sessionData)
_ -> return ()
-- forget all handshake data now and reset bytes counters.
usingState_ ctx endHandshake
updateMeasure ctx resetBytesCounters
-- mark the secure connection up and running.
setEstablished ctx True
return ()
-- client part of handshake. send a bunch of handshake of client
-- values intertwined with response from the server.
handshakeClient :: MonadIO m => Context -> m ()
handshakeClient ctx = do
updateMeasure ctx incrementNbHandshakes
sendClientHello
recvServerHello
sessionResuming <- usingState_ ctx isSessionResuming
if sessionResuming
then sendChangeCipherAndFinish ctx True
else do
sendCertificate >> sendClientKeyXchg >> sendCertificateVerify
sendChangeCipherAndFinish ctx True
recvChangeCipherAndFinish ctx
handshakeTerminate ctx
where
params = ctxParams ctx
ver = pConnectVersion params
allowedvers = pAllowedVersions params
ciphers = pCiphers params
compressions = pCompressions params
clientCerts = map fst $ pCertificates params
updateMeasure ctx incrementNbHandshakes
sendClientHello
recvServerHello
sessionResuming <- usingState_ ctx isSessionResuming
if sessionResuming
then sendChangeCipherAndFinish ctx True
else do
sendCertificate >> sendClientKeyXchg >> sendCertificateVerify
sendChangeCipherAndFinish ctx True
recvChangeCipherAndFinish ctx
handshakeTerminate ctx
where
params = ctxParams ctx
ver = pConnectVersion params
allowedvers = pAllowedVersions params
ciphers = pCiphers params
compressions = pCompressions params
clientCerts = map fst $ pCertificates params
getExtensions = sequence [secureReneg, npnExtention] >>= return . catMaybes
secureReneg =
if pUseSecureRenegotiation params
then usingState_ ctx (getVerifiedData True) >>= \vd -> return $ Just (0xff01, encodeExtSecureRenegotiation vd Nothing)
else return Nothing
secureReneg =
if pUseSecureRenegotiation params
then usingState_ ctx (getVerifiedData True) >>= \vd -> return $ Just (0xff01, encodeExtSecureRenegotiation vd Nothing)
else return Nothing
npnExtention = if isJust $ onNPNServerSuggest params
then return $ Just (13172, "")
else return Nothing
sendClientHello = do
crand <- getStateRNG ctx 32 >>= return . ClientRandom
let clientSession = Session . maybe Nothing (Just . fst) $ sessionResumeWith params
extensions <- getExtensions
usingState_ ctx (startHandshakeClient ver crand)
sendPacket ctx $ Handshake
[ ClientHello ver crand clientSession (map cipherID ciphers)
(map compressionID compressions) extensions
]
sendClientHello = do
crand <- getStateRNG ctx 32 >>= return . ClientRandom
let clientSession = Session . maybe Nothing (Just . fst) $ sessionResumeWith params
extensions <- getExtensions
usingState_ ctx (startHandshakeClient ver crand)
sendPacket ctx $ Handshake
[ ClientHello ver crand clientSession (map cipherID ciphers)
(map compressionID compressions) extensions
]
expectChangeCipher ChangeCipherSpec = return $ RecvStateHandshake expectFinish
expectChangeCipher p = unexpected (show p) (Just "change cipher")
expectFinish (Finished _) = return RecvStateDone
expectFinish p = unexpected (show p) (Just "Handshake Finished")
expectChangeCipher ChangeCipherSpec = return $ RecvStateHandshake expectFinish
expectChangeCipher p = unexpected (show p) (Just "change cipher")
expectFinish (Finished _) = return RecvStateDone
expectFinish p = unexpected (show p) (Just "Handshake Finished")
sendCertificate = do
-- Send Certificate if requested. XXX disabled for now.
certRequested <- return False
when certRequested (sendPacket ctx $ Handshake [Certificates clientCerts])
sendCertificate = do
-- Send Certificate if requested. XXX disabled for now.
certRequested <- return False
when certRequested (sendPacket ctx $ Handshake [Certificates clientCerts])
sendCertificateVerify =
{- maybe send certificateVerify -}
{- FIXME not implemented yet -}
return ()
sendCertificateVerify =
{- maybe send certificateVerify -}
{- FIXME not implemented yet -}
return ()
recvServerHello = runRecvState ctx (RecvStateHandshake onServerHello)
recvServerHello = runRecvState ctx (RecvStateHandshake onServerHello)
onServerHello :: MonadIO m => Handshake -> m (RecvState m)
onServerHello sh@(ServerHello rver _ serverSession cipher _ exts) = do
when (rver == SSL2) $ throwCore $ Error_Protocol ("ssl2 is not supported", True, ProtocolVersion)
case find ((==) rver) allowedvers of
Nothing -> throwCore $ Error_Protocol ("version " ++ show ver ++ "is not supported", True, ProtocolVersion)
Just _ -> usingState_ ctx $ setVersion ver
case find ((==) cipher . cipherID) ciphers of
Nothing -> throwCore $ Error_Protocol ("no cipher in common with the server", True, HandshakeFailure)
Just c -> usingState_ ctx $ setCipher c
onServerHello :: MonadIO m => Handshake -> m (RecvState m)
onServerHello sh@(ServerHello rver _ serverSession cipher _ exts) = do
when (rver == SSL2) $ throwCore $ Error_Protocol ("ssl2 is not supported", True, ProtocolVersion)
case find ((==) rver) allowedvers of
Nothing -> throwCore $ Error_Protocol ("version " ++ show ver ++ "is not supported", True, ProtocolVersion)
Just _ -> usingState_ ctx $ setVersion ver
case find ((==) cipher . cipherID) ciphers of
Nothing -> throwCore $ Error_Protocol ("no cipher in common with the server", True, HandshakeFailure)
Just c -> usingState_ ctx $ setCipher c
let resumingSession = case sessionResumeWith params of
Just (sessionId, sessionData) -> if serverSession == Session (Just sessionId) then Just sessionData else Nothing
Nothing -> Nothing
usingState_ ctx $ setSession serverSession (isJust resumingSession)
usingState_ ctx $ processServerHello sh
let resumingSession = case sessionResumeWith params of
Just (sessionId, sessionData) -> if serverSession == Session (Just sessionId) then Just sessionData else Nothing
Nothing -> Nothing
usingState_ ctx $ setSession serverSession (isJust resumingSession)
usingState_ ctx $ processServerHello sh
case decodeExtNextProtocolNegotiation `fmap` (lookup 13172 exts) of
Just (Right protos) -> usingState_ ctx $ do
setExtensionNPN True
setServerNextProtocolSuggest protos
Just (Left err) -> throwCore (Error_Protocol ("could not decode NPN handshake: " ++ show err, True, DecodeError))
Nothing -> return ()
case resumingSession of
Nothing -> return $ RecvStateHandshake processCertificate
Just sessionData -> do
usingState_ ctx (setMasterSecret $ sessionSecret sessionData)
return $ RecvStateNext expectChangeCipher
onServerHello p = unexpected (show p) (Just "server hello")
case resumingSession of
Nothing -> return $ RecvStateHandshake processCertificate
Just sessionData -> do
usingState_ ctx (setMasterSecret $ sessionSecret sessionData)
return $ RecvStateNext expectChangeCipher
onServerHello p = unexpected (show p) (Just "server hello")
processCertificate :: MonadIO m => Handshake -> m (RecvState m)
processCertificate (Certificates certs) = do
usage <- liftIO $ catch (onCertificatesRecv params $ certs) rejectOnException
case usage of
CertificateUsageAccept -> return ()
CertificateUsageReject reason -> certificateRejected reason
return $ RecvStateHandshake processServerKeyExchange
where
rejectOnException :: SomeException -> IO TLSCertificateUsage
rejectOnException e = return $ CertificateUsageReject $ CertificateRejectOther $ show e
processCertificate p = processServerKeyExchange p
processCertificate :: MonadIO m => Handshake -> m (RecvState m)
processCertificate (Certificates certs) = do
usage <- liftIO $ catch (onCertificatesRecv params $ certs) rejectOnException
case usage of
CertificateUsageAccept -> return ()
CertificateUsageReject reason -> certificateRejected reason
return $ RecvStateHandshake processServerKeyExchange
where
rejectOnException :: SomeException -> IO TLSCertificateUsage
rejectOnException e = return $ CertificateUsageReject $ CertificateRejectOther $ show e
processCertificate p = processServerKeyExchange p
processServerKeyExchange :: MonadIO m => Handshake -> m (RecvState m)
processServerKeyExchange (ServerKeyXchg _) = return $ RecvStateHandshake processCertificateRequest
processServerKeyExchange p = processCertificateRequest p
processServerKeyExchange :: MonadIO m => Handshake -> m (RecvState m)
processServerKeyExchange (ServerKeyXchg _) = return $ RecvStateHandshake processCertificateRequest
processServerKeyExchange p = processCertificateRequest p
processCertificateRequest (CertRequest _ _ _) = do
--modify (\sc -> sc { scCertRequested = True })
return $ RecvStateHandshake processServerHelloDone
processCertificateRequest p = processServerHelloDone p
processCertificateRequest (CertRequest _ _ _) = do
--modify (\sc -> sc { scCertRequested = True })
return $ RecvStateHandshake processServerHelloDone
processCertificateRequest p = processServerHelloDone p
processServerHelloDone ServerHelloDone = return RecvStateDone
processServerHelloDone p = unexpected (show p) (Just "server hello data")
processServerHelloDone ServerHelloDone = return RecvStateDone
processServerHelloDone p = unexpected (show p) (Just "server hello data")
sendClientKeyXchg = do
encryptedPreMaster <- usingState_ ctx $ do
xver <- stVersion <$> get
prerand <- genTLSRandom 46
let premaster = encodePreMasterSecret xver prerand
setMasterSecretFromPre premaster
sendClientKeyXchg = do
encryptedPreMaster <- usingState_ ctx $ do
xver <- stVersion <$> get
prerand <- genTLSRandom 46
let premaster = encodePreMasterSecret xver prerand
setMasterSecretFromPre premaster
-- SSL3 implementation generally forget this length field since it's redundant,
-- however TLS10 make it clear that the length field need to be present.
e <- encryptRSA premaster
let extra = if xver < TLS10
then B.empty
else encodeWord16 $ fromIntegral $ B.length e
return $ extra `B.append` e
sendPacket ctx $ Handshake [ClientKeyXchg encryptedPreMaster]
-- SSL3 implementation generally forget this length field since it's redundant,
-- however TLS10 make it clear that the length field need to be present.
e <- encryptRSA premaster
let extra = if xver < TLS10
then B.empty
else encodeWord16 $ fromIntegral $ B.length e
return $ extra `B.append` e
sendPacket ctx $ Handshake [ClientKeyXchg encryptedPreMaster]
-- on certificate reject, throw an exception with the proper protocol alert error.
certificateRejected CertificateRejectRevoked =
throwCore $ Error_Protocol ("certificate is revoked", True, CertificateRevoked)
certificateRejected CertificateRejectExpired =
throwCore $ Error_Protocol ("certificate has expired", True, CertificateExpired)
certificateRejected CertificateRejectUnknownCA =
throwCore $ Error_Protocol ("certificate has unknown CA", True, UnknownCa)
certificateRejected (CertificateRejectOther s) =
throwCore $ Error_Protocol ("certificate rejected: " ++ s, True, CertificateUnknown)
-- on certificate reject, throw an exception with the proper protocol alert error.
certificateRejected CertificateRejectRevoked =
throwCore $ Error_Protocol ("certificate is revoked", True, CertificateRevoked)
certificateRejected CertificateRejectExpired =
throwCore $ Error_Protocol ("certificate has expired", True, CertificateExpired)
certificateRejected CertificateRejectUnknownCA =
throwCore $ Error_Protocol ("certificate has unknown CA", True, UnknownCa)
certificateRejected (CertificateRejectOther s) =
throwCore $ Error_Protocol ("certificate rejected: " ++ s, True, CertificateUnknown)
handshakeServerWith :: MonadIO m => Context -> Handshake -> m ()
handshakeServerWith ctx clientHello@(ClientHello ver _ clientSession ciphers compressions exts) = do
-- check if policy allow this new handshake to happens
handshakeAuthorized <- withMeasure ctx (onHandshake $ ctxParams ctx)
unless handshakeAuthorized (throwCore $ Error_HandshakePolicy "server: handshake denied")
updateMeasure ctx incrementNbHandshakes
-- check if policy allow this new handshake to happens
handshakeAuthorized <- withMeasure ctx (onHandshake $ ctxParams ctx)
unless handshakeAuthorized (throwCore $ Error_HandshakePolicy "server: handshake denied")
updateMeasure ctx incrementNbHandshakes
-- Handle Client hello
usingState_ ctx $ processHandshake clientHello
when (ver == SSL2) $ throwCore $ Error_Protocol ("ssl2 is not supported", True, ProtocolVersion)
when (not $ elem ver (pAllowedVersions params)) $
throwCore $ Error_Protocol ("version " ++ show ver ++ "is not supported", True, ProtocolVersion)
when (commonCiphers == []) $
throwCore $ Error_Protocol ("no cipher in common with the client", True, HandshakeFailure)
when (null commonCompressions) $
throwCore $ Error_Protocol ("no compression in common with the client", True, HandshakeFailure)
usingState_ ctx $ modify (\st -> st
{ stVersion = ver
, stCipher = Just usedCipher
, stCompression = usedCompression
})
-- Handle Client hello
usingState_ ctx $ processHandshake clientHello
when (ver == SSL2) $ throwCore $ Error_Protocol ("ssl2 is not supported", True, ProtocolVersion)
when (not $ elem ver (pAllowedVersions params)) $
throwCore $ Error_Protocol ("version " ++ show ver ++ "is not supported", True, ProtocolVersion)
when (commonCiphers == []) $
throwCore $ Error_Protocol ("no cipher in common with the client", True, HandshakeFailure)
when (null commonCompressions) $
throwCore $ Error_Protocol ("no compression in common with the client", True, HandshakeFailure)
usingState_ ctx $ modify (\st -> st
{ stVersion = ver
, stCipher = Just usedCipher
, stCompression = usedCompression
})
resumeSessionData <- case clientSession of
(Session (Just clientSessionId)) -> liftIO $ onSessionResumption params $ clientSessionId
(Session Nothing) -> return Nothing
case resumeSessionData of
Nothing -> do
handshakeSendServerData
liftIO $ connectionFlush ctx
resumeSessionData <- case clientSession of
(Session (Just clientSessionId)) -> liftIO $ onSessionResumption params $ clientSessionId
(Session Nothing) -> return Nothing
case resumeSessionData of
Nothing -> do
handshakeSendServerData
liftIO $ connectionFlush ctx
-- Receive client info until client Finished.
recvClientData
sendChangeCipherAndFinish ctx False
Just sessionData -> do
usingState_ ctx (setSession clientSession True)
serverhello <- makeServerHello clientSession
sendPacket ctx $ Handshake [serverhello]
usingState_ ctx $ setMasterSecret $ sessionSecret sessionData
sendChangeCipherAndFinish ctx False
recvChangeCipherAndFinish ctx
handshakeTerminate ctx
where
params = ctxParams ctx
commonCiphers = intersect ciphers (map cipherID $ pCiphers params)
usedCipher = fromJust $ find (\c -> cipherID c == head commonCiphers) (pCiphers params)
commonCompressions = compressionIntersectID (pCompressions params) compressions
usedCompression = head commonCompressions
srvCerts = map fst $ pCertificates params
privKeys = map snd $ pCertificates params
needKeyXchg = cipherExchangeNeedMoreData $ cipherKeyExchange usedCipher
-- Receive client info until client Finished.
recvClientData
sendChangeCipherAndFinish ctx False
Just sessionData -> do
usingState_ ctx (setSession clientSession True)
serverhello <- makeServerHello clientSession
sendPacket ctx $ Handshake [serverhello]
usingState_ ctx $ setMasterSecret $ sessionSecret sessionData
sendChangeCipherAndFinish ctx False
recvChangeCipherAndFinish ctx
handshakeTerminate ctx
where
params = ctxParams ctx
commonCiphers = intersect ciphers (map cipherID $ pCiphers params)
usedCipher = fromJust $ find (\c -> cipherID c == head commonCiphers) (pCiphers params)
commonCompressions = compressionIntersectID (pCompressions params) compressions
usedCompression = head commonCompressions
srvCerts = map fst $ pCertificates params
privKeys = map snd $ pCertificates params
needKeyXchg = cipherExchangeNeedMoreData $ cipherKeyExchange usedCipher
clientRequestedNPN = isJust $ lookup 13172 exts
---
recvClientData = runRecvState ctx (RecvStateHandshake processClientCertificate)
---
recvClientData = runRecvState ctx (RecvStateHandshake processClientCertificate)
processClientCertificate (Certificates _) = return $ RecvStateHandshake processClientKeyExchange
processClientCertificate p = processClientKeyExchange p
processClientCertificate (Certificates _) = return $ RecvStateHandshake processClientKeyExchange
processClientCertificate p = processClientKeyExchange p
processClientKeyExchange (ClientKeyXchg _) = return $ RecvStateNext processCertificateVerify
processClientKeyExchange p = unexpected (show p) (Just "client key exchange")
processClientKeyExchange (ClientKeyXchg _) = return $ RecvStateNext processCertificateVerify
processClientKeyExchange p = unexpected (show p) (Just "client key exchange")
processCertificateVerify (Handshake [CertVerify _]) = return $ RecvStateNext expectChangeCipher
processCertificateVerify p = expectChangeCipher p
processCertificateVerify (Handshake [CertVerify _]) = return $ RecvStateNext expectChangeCipher
processCertificateVerify p = expectChangeCipher p
expectChangeCipher ChangeCipherSpec = do npn <- usingState_ ctx getExtensionNPN
expectChangeCipher ChangeCipherSpec = do npn <- usingState_ ctx getExtensionNPN
return $ RecvStateHandshake $ if npn
then expectNPN
else expectFinish
expectChangeCipher p = unexpected (show p) (Just "change cipher")
expectChangeCipher p = unexpected (show p) (Just "change cipher")
expectNPN (NextProtocolNegotiation _) = return $ RecvStateHandshake expectFinish
expectNPN p = unexpected (show p) (Just "Handshake NextProtocolNegotiation")
expectFinish (Finished _) = return RecvStateDone
expectFinish p = unexpected (show p) (Just "Handshake Finished")
---
expectFinish (Finished _) = return RecvStateDone
expectFinish p = unexpected (show p) (Just "Handshake Finished")
---
makeServerHello session = do
srand <- getStateRNG ctx 32 >>= return . ServerRandom
case privKeys of
(Just privkey : _) -> usingState_ ctx $ setPrivateKey privkey
_ -> return () -- return a sensible error
makeServerHello session = do
srand <- getStateRNG ctx 32 >>= return . ServerRandom
case privKeys of
(Just privkey : _) -> usingState_ ctx $ setPrivateKey privkey
_ -> return () -- return a sensible error
-- in TLS12, we need to check as well the certificates we are sending if they have in the extension
-- the necessary bits set.
secReneg <- usingState_ ctx getSecureRenegotiation
secRengExt <- if secReneg
then do
vf <- usingState_ ctx $ do
cvf <- getVerifiedData True
svf <- getVerifiedData False
return $ encodeExtSecureRenegotiation cvf (Just svf)
return [ (0xff01, vf) ]
else return []
-- in TLS12, we need to check as well the certificates we are sending if they have in the extension
-- the necessary bits set.
secReneg <- usingState_ ctx getSecureRenegotiation
secRengExt <- if secReneg
then do
vf <- usingState_ ctx $ do
cvf <- getVerifiedData True
svf <- getVerifiedData False
return $ encodeExtSecureRenegotiation cvf (Just svf)
return [ (0xff01, vf) ]
else return []
nextProtocols <-
if clientRequestedNPN
then liftIO $ onSuggestNextProtocols params
@ -449,83 +449,83 @@ handshakeServerWith ctx clientHello@(ClientHello ver _ clientSession ciphers com
return [ (13172, encodeExtNextProtocolNegotiation protos) ]
Nothing -> return []
let extensions = secRengExt ++ npnExt
usingState_ ctx (setVersion ver >> setServerRandom srand)
return $ ServerHello ver srand session (cipherID usedCipher)
(compressionID usedCompression) extensions
usingState_ ctx (setVersion ver >> setServerRandom srand)
return $ ServerHello ver srand session (cipherID usedCipher)
(compressionID usedCompression) extensions
handshakeSendServerData = do
serverSession <- newSession ctx
usingState_ ctx (setSession serverSession False)
serverhello <- makeServerHello serverSession
-- send ServerHello & Certificate & ServerKeyXchg & CertReq
sendPacket ctx $ Handshake [ serverhello, Certificates srvCerts ]
when needKeyXchg $ do
let skg = SKX_RSA Nothing
sendPacket ctx (Handshake [ServerKeyXchg skg])
-- FIXME we don't do this on a Anonymous server
when (pWantClientCert params) $ do
let certTypes = [ CertificateType_RSA_Sign ]
let creq = CertRequest certTypes Nothing [0,0,0]
sendPacket ctx (Handshake [creq])
-- Send HelloDone
sendPacket ctx (Handshake [ServerHelloDone])
handshakeSendServerData = do
serverSession <- newSession ctx
usingState_ ctx (setSession serverSession False)
serverhello <- makeServerHello serverSession
-- send ServerHello & Certificate & ServerKeyXchg & CertReq
sendPacket ctx $ Handshake [ serverhello, Certificates srvCerts ]
when needKeyXchg $ do
let skg = SKX_RSA Nothing
sendPacket ctx (Handshake [ServerKeyXchg skg])
-- FIXME we don't do this on a Anonymous server
when (pWantClientCert params) $ do
let certTypes = [ CertificateType_RSA_Sign ]
let creq = CertRequest certTypes Nothing [0,0,0]
sendPacket ctx (Handshake [creq])
-- Send HelloDone
sendPacket ctx (Handshake [ServerHelloDone])
handshakeServerWith _ _ = fail "unexpected handshake type received. expecting client hello"
-- after receiving a client hello, we need to redo a handshake
handshakeServer :: MonadIO m => Context -> m ()
handshakeServer ctx = do
hss <- recvPacketHandshake ctx
case hss of
[ch] -> handshakeServerWith ctx ch
_ -> fail ("unexpected handshake received, excepting client hello and received " ++ show hss)
hss <- recvPacketHandshake ctx
case hss of
[ch] -> handshakeServerWith ctx ch
_ -> fail ("unexpected handshake received, excepting client hello and received " ++ show hss)
-- | Handshake for a new TLS connection
-- This is to be called at the beginning of a connection, and during renegotiation
handshake :: MonadIO m => Context -> m ()
handshake ctx = do
cc <- usingState_ ctx (stClientContext <$> get)
liftIO $ handleException $ if cc then handshakeClient ctx else handshakeServer ctx
where
handleException f = catch f $ \exception -> do
let tlserror = maybe (Error_Misc $ show exception) id $ fromException exception
setEstablished ctx False
sendPacket ctx (errorToAlert tlserror)
handshakeFailed tlserror
cc <- usingState_ ctx (stClientContext <$> get)
liftIO $ handleException $ if cc then handshakeClient ctx else handshakeServer ctx
where
handleException f = catch f $ \exception -> do
let tlserror = maybe (Error_Misc $ show exception) id $ fromException exception
setEstablished ctx False
sendPacket ctx (errorToAlert tlserror)
handshakeFailed tlserror
-- | sendData sends a bunch of data.
-- It will automatically chunk data to acceptable packet size
sendData :: MonadIO m => Context -> L.ByteString -> m ()
sendData ctx dataToSend = checkValid ctx >> mapM_ sendDataChunk (L.toChunks dataToSend)
where sendDataChunk d
| B.length d > 16384 = do
let (sending, remain) = B.splitAt 16384 d
sendPacket ctx $ AppData sending
sendDataChunk remain
| otherwise = sendPacket ctx $ AppData d
where sendDataChunk d
| B.length d > 16384 = do
let (sending, remain) = B.splitAt 16384 d
sendPacket ctx $ AppData sending
sendDataChunk remain
| otherwise = sendPacket ctx $ AppData d
-- | recvData get data out of Data packet, and automatically renegotiate if
-- a Handshake ClientHello is received
recvData :: MonadIO m => Context -> m B.ByteString
recvData ctx = do
checkValid ctx
pkt <- recvPacket ctx
case pkt of
-- on server context receiving a client hello == renegotiation
Right (Handshake [ch@(ClientHello {})]) ->
handshakeServerWith ctx ch >> recvData ctx
-- on client context, receiving a hello request == renegotiation
Right (Handshake [HelloRequest]) ->
handshakeClient ctx >> recvData ctx
Right (Alert [(AlertLevel_Fatal, _)]) -> do
setEOF ctx
return B.empty
Right (Alert [(AlertLevel_Warning, CloseNotify)]) -> do
setEOF ctx
return B.empty
Right (AppData x) -> return x
Right p -> error ("error unexpected packet: " ++ show p)
Left err -> error ("error received: " ++ show err)
checkValid ctx
pkt <- recvPacket ctx
case pkt of
-- on server context receiving a client hello == renegotiation
Right (Handshake [ch@(ClientHello {})]) ->
handshakeServerWith ctx ch >> recvData ctx
-- on client context, receiving a hello request == renegotiation
Right (Handshake [HelloRequest]) ->
handshakeClient ctx >> recvData ctx
Right (Alert [(AlertLevel_Fatal, _)]) -> do
setEOF ctx
return B.empty
Right (Alert [(AlertLevel_Warning, CloseNotify)]) -> do
setEOF ctx
return B.empty
Right (AppData x) -> return x
Right p -> error ("error unexpected packet: " ++ show p)
Left err -> error ("error received: " ++ show err)
recvData' :: MonadIO m => Context -> m L.ByteString
recvData' ctx = recvData ctx >>= return . L.fromChunks . (:[])

View file

@ -1,23 +1,23 @@
{-# OPTIONS_HADDOCK hide #-}
{-# LANGUAGE ExistentialQuantification #-}
module Network.TLS.Crypto
( HashCtx(..)
, hashInit
, hashUpdate
, hashUpdateSSL
, hashFinal
( HashCtx(..)
, hashInit
, hashUpdate
, hashUpdateSSL
, hashFinal
-- * constructor
, hashMD5SHA1
, hashSHA256
-- * constructor
, hashMD5SHA1
, hashSHA256
-- * key exchange generic interface
, PublicKey(..)
, PrivateKey(..)
, kxEncrypt
, kxDecrypt
, KxError(..)
) where
-- * key exchange generic interface
, PublicKey(..)
, PrivateKey(..)
, kxEncrypt
, kxDecrypt
, KxError(..)
) where
import qualified Crypto.Hash.SHA256 as SHA256
import qualified Crypto.Hash.SHA1 as SHA1
@ -32,48 +32,48 @@ data PublicKey = PubRSA RSA.PublicKey
data PrivateKey = PrivRSA RSA.PrivateKey
instance Show PublicKey where
show (_) = "PublicKey(..)"
show (_) = "PublicKey(..)"
instance Show PrivateKey where
show (_) = "privateKey(..)"
show (_) = "privateKey(..)"
data KxError = RSAError RSA.Error
deriving (Show)
deriving (Show)
data KeyXchg =
KxRSA RSA.PublicKey RSA.PrivateKey
deriving (Show)
KxRSA RSA.PublicKey RSA.PrivateKey
deriving (Show)
class HashCtxC a where
hashCName :: a -> String
hashCInit :: a -> a
hashCUpdate :: a -> B.ByteString -> a
hashCUpdateSSL :: a -> (B.ByteString,B.ByteString) -> a
hashCFinal :: a -> B.ByteString
hashCName :: a -> String
hashCInit :: a -> a
hashCUpdate :: a -> B.ByteString -> a
hashCUpdateSSL :: a -> (B.ByteString,B.ByteString) -> a
hashCFinal :: a -> B.ByteString
data HashCtx = forall h . HashCtxC h => HashCtx h
instance Show HashCtx where
show (HashCtx c) = hashCName c
show (HashCtx c) = hashCName c
{- MD5 & SHA1 joined -}
data HashMD5SHA1 = HashMD5SHA1 SHA1.Ctx MD5.Ctx
instance HashCtxC HashMD5SHA1 where
hashCName _ = "MD5-SHA1"
hashCInit _ = HashMD5SHA1 SHA1.init MD5.init
hashCUpdate (HashMD5SHA1 sha1ctx md5ctx) b = HashMD5SHA1 (SHA1.update sha1ctx b) (MD5.update md5ctx b)
hashCUpdateSSL (HashMD5SHA1 sha1ctx md5ctx) (b1,b2) = HashMD5SHA1 (SHA1.update sha1ctx b2) (MD5.update md5ctx b1)
hashCFinal (HashMD5SHA1 sha1ctx md5ctx) = B.concat [MD5.finalize md5ctx, SHA1.finalize sha1ctx]
hashCName _ = "MD5-SHA1"
hashCInit _ = HashMD5SHA1 SHA1.init MD5.init
hashCUpdate (HashMD5SHA1 sha1ctx md5ctx) b = HashMD5SHA1 (SHA1.update sha1ctx b) (MD5.update md5ctx b)
hashCUpdateSSL (HashMD5SHA1 sha1ctx md5ctx) (b1,b2) = HashMD5SHA1 (SHA1.update sha1ctx b2) (MD5.update md5ctx b1)
hashCFinal (HashMD5SHA1 sha1ctx md5ctx) = B.concat [MD5.finalize md5ctx, SHA1.finalize sha1ctx]
data HashSHA256 = HashSHA256 SHA256.Ctx
instance HashCtxC HashSHA256 where
hashCName _ = "SHA256"
hashCInit _ = HashSHA256 SHA256.init
hashCUpdate (HashSHA256 ctx) b = HashSHA256 (SHA256.update ctx b)
hashCUpdateSSL _ _ = undefined
hashCFinal (HashSHA256 ctx) = SHA256.finalize ctx
hashCName _ = "SHA256"
hashCInit _ = HashSHA256 SHA256.init
hashCUpdate (HashSHA256 ctx) b = HashSHA256 (SHA256.update ctx b)
hashCUpdateSSL _ _ = undefined
hashCFinal (HashSHA256 ctx) = SHA256.finalize ctx
-- functions to use the hidden class.
hashInit :: HashCtx -> HashCtx

View file

@ -7,13 +7,13 @@
-- Portability : unknown
--
module Network.TLS.Internal
( module Network.TLS.Struct
, module Network.TLS.Packet
, module Network.TLS.Receiving
, module Network.TLS.Sending
, sendPacket
, recvPacket
) where
( module Network.TLS.Struct
, module Network.TLS.Packet
, module Network.TLS.Receiving
, module Network.TLS.Sending
, sendPacket
, recvPacket
) where
import Network.TLS.Struct
import Network.TLS.Packet

View file

@ -1,14 +1,14 @@
module Network.TLS.MAC
( hmacMD5
, hmacSHA1
, hmacSHA256
, macSSL
, hmac
, prf_MD5
, prf_SHA1
, prf_SHA256
, prf_MD5SHA1
) where
( hmacMD5
, hmacSHA1
, hmacSHA256
, macSSL
, hmac
, prf_MD5
, prf_SHA1
, prf_SHA256
, prf_MD5SHA1
) where
import qualified Crypto.Hash.MD5 as MD5
import qualified Crypto.Hash.SHA1 as SHA1
@ -21,22 +21,22 @@ type HMAC = ByteString -> ByteString -> ByteString
macSSL :: (ByteString -> ByteString) -> HMAC
macSSL f secret msg = f $! B.concat [ secret, B.replicate padlen 0x5c,
f $! B.concat [ secret, B.replicate padlen 0x36, msg ] ]
where
-- get the type of algorithm out of the digest length by using the hash fct.
padlen = if (B.length $ f B.empty) == 16 then 48 else 40
f $! B.concat [ secret, B.replicate padlen 0x36, msg ] ]
where
-- get the type of algorithm out of the digest length by using the hash fct.
padlen = if (B.length $ f B.empty) == 16 then 48 else 40
hmac :: (ByteString -> ByteString) -> Int -> HMAC
hmac f bl secret msg =
f $! B.append opad (f $! B.append ipad msg)
where
opad = B.map (xor 0x5c) k'
ipad = B.map (xor 0x36) k'
f $! B.append opad (f $! B.append ipad msg)
where
opad = B.map (xor 0x5c) k'
ipad = B.map (xor 0x36) k'
k' = B.append kt pad
where
kt = if B.length secret > fromIntegral bl then f secret else secret
pad = B.replicate (fromIntegral bl - B.length kt) 0
k' = B.append kt pad
where
kt = if B.length secret > fromIntegral bl then f secret else secret
pad = B.replicate (fromIntegral bl - B.length kt) 0
hmacMD5 :: HMAC
hmacMD5 secret msg = hmac MD5.hash 64 secret msg
@ -49,12 +49,12 @@ hmacSHA256 secret msg = hmac SHA256.hash 64 secret msg
hmacIter :: HMAC -> ByteString -> ByteString -> ByteString -> Int -> [ByteString]
hmacIter f secret seed aprev len =
let an = f secret aprev in
let out = f secret (B.concat [an, seed]) in
let digestsize = fromIntegral $ B.length out in
if digestsize >= len
then [ B.take (fromIntegral len) out ]
else out : hmacIter f secret seed an (len - digestsize)
let an = f secret aprev in
let out = f secret (B.concat [an, seed]) in
let digestsize = fromIntegral $ B.length out in
if digestsize >= len
then [ B.take (fromIntegral len) out ]
else out : hmacIter f secret seed an (len - digestsize)
prf_SHA1 :: ByteString -> ByteString -> Int -> ByteString
prf_SHA1 secret seed len = B.concat $ hmacIter hmacSHA1 secret seed seed len
@ -64,11 +64,11 @@ prf_MD5 secret seed len = B.concat $ hmacIter hmacMD5 secret seed seed len
prf_MD5SHA1 :: ByteString -> ByteString -> Int -> ByteString
prf_MD5SHA1 secret seed len =
B.pack $ B.zipWith xor (prf_MD5 s1 seed len) (prf_SHA1 s2 seed len)
where
slen = B.length secret
s1 = B.take (slen `div` 2 + slen `mod` 2) secret
s2 = B.drop (slen `div` 2) secret
B.pack $ B.zipWith xor (prf_MD5 s1 seed len) (prf_SHA1 s2 seed len)
where
slen = B.length secret
s1 = B.take (slen `div` 2 + slen `mod` 2) secret
s2 = B.drop (slen `div` 2) secret
prf_SHA256 :: ByteString -> ByteString -> Int -> ByteString
prf_SHA256 secret seed len = B.concat $ hmacIter hmacSHA256 secret seed seed len

View file

@ -6,41 +6,41 @@
-- Portability : unknown
--
module Network.TLS.Measurement
( Measurement(..)
, newMeasurement
, addBytesReceived
, addBytesSent
, resetBytesCounters
, incrementNbHandshakes
) where
( Measurement(..)
, newMeasurement
, addBytesReceived
, addBytesSent
, resetBytesCounters
, incrementNbHandshakes
) where
import Data.Word
-- | record some data about this connection.
data Measurement = Measurement
{ nbHandshakes :: !Word32 -- ^ number of handshakes on this context
, bytesReceived :: !Word32 -- ^ bytes received since last handshake
, bytesSent :: !Word32 -- ^ bytes sent since last handshake
} deriving (Show,Eq)
{ nbHandshakes :: !Word32 -- ^ number of handshakes on this context
, bytesReceived :: !Word32 -- ^ bytes received since last handshake
, bytesSent :: !Word32 -- ^ bytes sent since last handshake
} deriving (Show,Eq)
newMeasurement :: Measurement
newMeasurement = Measurement
{ nbHandshakes = 0
, bytesReceived = 0
, bytesSent = 0
}
{ nbHandshakes = 0
, bytesReceived = 0
, bytesSent = 0
}
addBytesReceived :: Int -> Measurement -> Measurement
addBytesReceived sz measure =
measure { bytesReceived = bytesReceived measure + fromIntegral sz }
measure { bytesReceived = bytesReceived measure + fromIntegral sz }
addBytesSent :: Int -> Measurement -> Measurement
addBytesSent sz measure =
measure { bytesSent = bytesSent measure + fromIntegral sz }
measure { bytesSent = bytesSent measure + fromIntegral sz }
resetBytesCounters :: Measurement -> Measurement
resetBytesCounters measure = measure { bytesReceived = 0, bytesSent = 0 }
incrementNbHandshakes :: Measurement -> Measurement
incrementNbHandshakes measure =
measure { nbHandshakes = nbHandshakes measure + 1 }
measure { nbHandshakes = nbHandshakes measure + 1 }

View file

@ -10,46 +10,46 @@
-- with only explicit parameters, no TLS state is involved here.
--
module Network.TLS.Packet
(
-- * params for encoding and decoding
CurrentParams(..)
-- * marshall functions for header messages
, decodeHeader
, encodeHeader
, encodeHeaderNoVer -- use for SSL3
(
-- * params for encoding and decoding
CurrentParams(..)
-- * marshall functions for header messages
, decodeHeader
, encodeHeader
, encodeHeaderNoVer -- use for SSL3
-- * marshall functions for alert messages
, decodeAlert
, decodeAlerts
, encodeAlerts
-- * marshall functions for alert messages
, decodeAlert
, decodeAlerts
, encodeAlerts
-- * marshall functions for handshake messages
, decodeHandshakes
, decodeHandshake
, encodeHandshake
, encodeHandshakes
, encodeHandshakeHeader
, encodeHandshakeContent
-- * marshall functions for handshake messages
, decodeHandshakes
, decodeHandshake
, encodeHandshake
, encodeHandshakes
, encodeHandshakeHeader
, encodeHandshakeContent
-- * marshall functions for change cipher spec message
, decodeChangeCipherSpec
, encodeChangeCipherSpec
-- * marshall functions for change cipher spec message
, decodeChangeCipherSpec
, encodeChangeCipherSpec
, decodePreMasterSecret
, encodePreMasterSecret
, decodePreMasterSecret
, encodePreMasterSecret
-- * marshall extensions
, decodeExtSecureRenegotiation
, encodeExtSecureRenegotiation
, decodeExtNextProtocolNegotiation
, encodeExtNextProtocolNegotiation
-- * marshall extensions
, decodeExtSecureRenegotiation
, encodeExtSecureRenegotiation
, decodeExtNextProtocolNegotiation
, encodeExtNextProtocolNegotiation
-- * generate things for packet content
, generateMasterSecret
, generateKeyBlock
, generateClientFinished
, generateServerFinished
) where
-- * generate things for packet content
, generateMasterSecret
, generateKeyBlock
, generateClientFinished
, generateServerFinished
) where
import Network.TLS.Struct
import Network.TLS.Wire
@ -72,10 +72,10 @@ import qualified Crypto.Hash.SHA1 as SHA1
import qualified Crypto.Hash.MD5 as MD5
data CurrentParams = CurrentParams
{ cParamsVersion :: Version -- ^ current protocol version
, cParamsKeyXchgType :: CipherKeyExchangeType -- ^ current key exchange type
, cParamsSupportNPN :: Bool -- ^ support Next Protocol Negotiation extension
} deriving (Show,Eq)
{ cParamsVersion :: Version -- ^ current protocol version
, cParamsKeyXchgType :: CipherKeyExchangeType -- ^ current key exchange type
, cParamsSupportNPN :: Bool -- ^ support Next Protocol Negotiation extension
} deriving (Show,Eq)
runGetErr :: String -> Get a -> ByteString -> Either TLSError a
runGetErr lbl f = either (Left . Error_Packet_Parsing) Right . runGet lbl f
@ -83,32 +83,32 @@ runGetErr lbl f = either (Left . Error_Packet_Parsing) Right . runGet lbl f
{- marshall helpers -}
getVersion :: Get Version
getVersion = do
major <- getWord8
minor <- getWord8
case verOfNum (major, minor) of
Nothing -> fail ("invalid version : " ++ show major ++ "," ++ show minor)
Just v -> return v
major <- getWord8
minor <- getWord8
case verOfNum (major, minor) of
Nothing -> fail ("invalid version : " ++ show major ++ "," ++ show minor)
Just v -> return v
putVersion :: Version -> Put
putVersion ver = putWord8 major >> putWord8 minor
where (major, minor) = numericalVer ver
where (major, minor) = numericalVer ver
getHeaderType :: Get ProtocolType
getHeaderType = do
ty <- getWord8
case valToType ty of
Nothing -> fail ("invalid header type: " ++ show ty)
Just t -> return t
ty <- getWord8
case valToType ty of
Nothing -> fail ("invalid header type: " ++ show ty)
Just t -> return t
putHeaderType :: ProtocolType -> Put
putHeaderType = putWord8 . valOfType
getHandshakeType :: Get HandshakeType
getHandshakeType = do
ty <- getWord8
case valToType ty of
Nothing -> fail ("invalid handshake type: " ++ show ty)
Just t -> return t
ty <- getWord8
case valToType ty of
Nothing -> fail ("invalid handshake type: " ++ show ty)
Just t -> return t
{-
- decode and encode headers
@ -118,122 +118,122 @@ decodeHeader = runGetErr "header" $ liftM3 Header getHeaderType getVersion getWo
encodeHeader :: Header -> ByteString
encodeHeader (Header pt ver len) = runPut (putHeaderType pt >> putVersion ver >> putWord16 len)
{- FIXME check len <= 2^14 -}
{- FIXME check len <= 2^14 -}
encodeHeaderNoVer :: Header -> ByteString
encodeHeaderNoVer (Header pt _ len) = runPut (putHeaderType pt >> putWord16 len)
{- FIXME check len <= 2^14 -}
{- FIXME check len <= 2^14 -}
{-
- decode and encode ALERT
-}
decodeAlert :: Get (AlertLevel, AlertDescription)
decodeAlert = do
al <- getWord8
ad <- getWord8
case (valToType al, valToType ad) of
(Just a, Just d) -> return (a, d)
(Nothing, _) -> fail "cannot decode alert level"
(_, Nothing) -> fail "cannot decode alert description"
al <- getWord8
ad <- getWord8
case (valToType al, valToType ad) of
(Just a, Just d) -> return (a, d)
(Nothing, _) -> fail "cannot decode alert level"
(_, Nothing) -> fail "cannot decode alert description"
decodeAlerts :: ByteString -> Either TLSError [(AlertLevel, AlertDescription)]
decodeAlerts = runGetErr "alerts" $ loop
where loop = do
r <- remaining
if r == 0
then return []
else liftM2 (:) decodeAlert loop
where loop = do
r <- remaining
if r == 0
then return []
else liftM2 (:) decodeAlert loop
encodeAlerts :: [(AlertLevel, AlertDescription)] -> ByteString
encodeAlerts l = runPut $ mapM_ encodeAlert l
where encodeAlert (al, ad) = putWord8 (valOfType al) >> putWord8 (valOfType ad)
where encodeAlert (al, ad) = putWord8 (valOfType al) >> putWord8 (valOfType ad)
{- decode and encode HANDSHAKE -}
decodeHandshakeHeader :: Get (HandshakeType, Bytes)
decodeHandshakeHeader = do
ty <- getHandshakeType
content <- getOpaque24
return (ty, content)
ty <- getHandshakeType
content <- getOpaque24
return (ty, content)
decodeHandshakes :: ByteString -> Either TLSError [(HandshakeType, Bytes)]
decodeHandshakes b = runGetErr "handshakes" getAll b where
getAll = do
x <- decodeHandshakeHeader
empty <- isEmpty
if empty
then return [x]
else getAll >>= \l -> return (x : l)
getAll = do
x <- decodeHandshakeHeader
empty <- isEmpty
if empty
then return [x]
else getAll >>= \l -> return (x : l)
decodeHandshake :: CurrentParams -> HandshakeType -> ByteString -> Either TLSError Handshake
decodeHandshake cp ty = runGetErr "handshake" $ case ty of
HandshakeType_HelloRequest -> decodeHelloRequest
HandshakeType_ClientHello -> decodeClientHello
HandshakeType_ServerHello -> decodeServerHello
HandshakeType_Certificate -> decodeCertificates
HandshakeType_ServerKeyXchg -> decodeServerKeyXchg cp
HandshakeType_CertRequest -> decodeCertRequest cp
HandshakeType_ServerHelloDone -> decodeServerHelloDone
HandshakeType_CertVerify -> decodeCertVerify
HandshakeType_ClientKeyXchg -> decodeClientKeyXchg
HandshakeType_Finished -> decodeFinished
HandshakeType_NPN -> do
unless (cParamsSupportNPN cp) $ fail "unsupported handshake type"
decodeNextProtocolNegotiation
HandshakeType_HelloRequest -> decodeHelloRequest
HandshakeType_ClientHello -> decodeClientHello
HandshakeType_ServerHello -> decodeServerHello
HandshakeType_Certificate -> decodeCertificates
HandshakeType_ServerKeyXchg -> decodeServerKeyXchg cp
HandshakeType_CertRequest -> decodeCertRequest cp
HandshakeType_ServerHelloDone -> decodeServerHelloDone
HandshakeType_CertVerify -> decodeCertVerify
HandshakeType_ClientKeyXchg -> decodeClientKeyXchg
HandshakeType_Finished -> decodeFinished
HandshakeType_NPN -> do
unless (cParamsSupportNPN cp) $ fail "unsupported handshake type"
decodeNextProtocolNegotiation
decodeHelloRequest :: Get Handshake
decodeHelloRequest = return HelloRequest
decodeClientHello :: Get Handshake
decodeClientHello = do
ver <- getVersion
random <- getClientRandom32
session <- getSession
ciphers <- getWords16
compressions <- getWords8
r <- remaining
exts <- if hasHelloExtensions ver && r > 0
then fmap fromIntegral getWord16 >>= getExtensions
else return []
return $ ClientHello ver random session ciphers compressions exts
ver <- getVersion
random <- getClientRandom32
session <- getSession
ciphers <- getWords16
compressions <- getWords8
r <- remaining
exts <- if hasHelloExtensions ver && r > 0
then fmap fromIntegral getWord16 >>= getExtensions
else return []
return $ ClientHello ver random session ciphers compressions exts
decodeServerHello :: Get Handshake
decodeServerHello = do
ver <- getVersion
random <- getServerRandom32
session <- getSession
cipherid <- getWord16
compressionid <- getWord8
r <- remaining
exts <- if hasHelloExtensions ver && r > 0
then fmap fromIntegral getWord16 >>= getExtensions
else return []
return $ ServerHello ver random session cipherid compressionid exts
ver <- getVersion
random <- getServerRandom32
session <- getSession
cipherid <- getWord16
compressionid <- getWord8
r <- remaining
exts <- if hasHelloExtensions ver && r > 0
then fmap fromIntegral getWord16 >>= getExtensions
else return []
return $ ServerHello ver random session cipherid compressionid exts
decodeServerHelloDone :: Get Handshake
decodeServerHelloDone = return ServerHelloDone
decodeCertificates :: Get Handshake
decodeCertificates = do
certs <- getWord24 >>= getCerts >>= return . map (decodeCertificate . L.fromChunks . (:[]))
let (l, r) = partitionEithers certs
if length l > 0
then fail ("error certificate parsing: " ++ show l)
else return $ Certificates r
certs <- getWord24 >>= getCerts >>= return . map (decodeCertificate . L.fromChunks . (:[]))
let (l, r) = partitionEithers certs
if length l > 0
then fail ("error certificate parsing: " ++ show l)
else return $ Certificates r
decodeFinished :: Get Handshake
decodeFinished = Finished <$> (remaining >>= getBytes)
decodeNextProtocolNegotiation :: Get Handshake
decodeNextProtocolNegotiation = do
opaque <- getOpaque8
_ <- getOpaque8
return $ NextProtocolNegotiation opaque
opaque <- getOpaque8
_ <- getOpaque8
return $ NextProtocolNegotiation opaque
getSignatureHashAlgorithm :: Get (HashAlgorithm, SignatureAlgorithm)
getSignatureHashAlgorithm = do
h <- fromJust . valToType <$> getWord8
s <- fromJust . valToType <$> getWord8
return (h,s)
h <- fromJust . valToType <$> getWord8
s <- fromJust . valToType <$> getWord8
return (h,s)
getSignatureHashAlgorithms :: Int -> Get [ (HashAlgorithm, SignatureAlgorithm) ]
getSignatureHashAlgorithms 0 = return []
@ -241,22 +241,22 @@ getSignatureHashAlgorithms len = liftM2 (:) getSignatureHashAlgorithm (getSignat
decodeCertRequest :: CurrentParams -> Get Handshake
decodeCertRequest cp = do
certTypes <- map (fromJust . valToType . fromIntegral) <$> getWords8
certTypes <- map (fromJust . valToType . fromIntegral) <$> getWords8
sigHashAlgs <- if cParamsVersion cp >= TLS12
then do
sighashlen <- getWord16
Just <$> getSignatureHashAlgorithms (fromIntegral sighashlen)
else return Nothing
dNameLen <- getWord16
when (cParamsVersion cp < TLS12 && dNameLen < 3) $ fail "certrequest distinguishname not of the correct size"
dName <- getBytes $ fromIntegral dNameLen
return $ CertRequest certTypes sigHashAlgs (B.unpack dName)
sigHashAlgs <- if cParamsVersion cp >= TLS12
then do
sighashlen <- getWord16
Just <$> getSignatureHashAlgorithms (fromIntegral sighashlen)
else return Nothing
dNameLen <- getWord16
when (cParamsVersion cp < TLS12 && dNameLen < 3) $ fail "certrequest distinguishname not of the correct size"
dName <- getBytes $ fromIntegral dNameLen
return $ CertRequest certTypes sigHashAlgs (B.unpack dName)
decodeCertVerify :: Get Handshake
decodeCertVerify =
{- FIXME -}
return $ CertVerify []
{- FIXME -}
return $ CertVerify []
decodeClientKeyXchg :: Get Handshake
decodeClientKeyXchg = ClientKeyXchg <$> (remaining >>= getBytes)
@ -266,39 +266,39 @@ os2ip = B.foldl' (\a b -> (256 * a) .|. (fromIntegral b)) 0
decodeServerKeyXchg_DH :: Get ServerDHParams
decodeServerKeyXchg_DH = do
p <- getOpaque16
g <- getOpaque16
y <- getOpaque16
return $ ServerDHParams { dh_p = os2ip p, dh_g = os2ip g, dh_Ys = os2ip y }
p <- getOpaque16
g <- getOpaque16
y <- getOpaque16
return $ ServerDHParams { dh_p = os2ip p, dh_g = os2ip g, dh_Ys = os2ip y }
decodeServerKeyXchg_RSA :: Get ServerRSAParams
decodeServerKeyXchg_RSA = do
modulus <- getOpaque16
expo <- getOpaque16
return $ ServerRSAParams { rsa_modulus = os2ip modulus, rsa_exponent = os2ip expo }
modulus <- getOpaque16
expo <- getOpaque16
return $ ServerRSAParams { rsa_modulus = os2ip modulus, rsa_exponent = os2ip expo }
decodeServerKeyXchg :: CurrentParams -> Get Handshake
decodeServerKeyXchg cp = ServerKeyXchg <$> case cParamsKeyXchgType cp of
CipherKeyExchange_RSA -> SKX_RSA . Just <$> decodeServerKeyXchg_RSA
CipherKeyExchange_DH_Anon -> SKX_DH_Anon <$> decodeServerKeyXchg_DH
CipherKeyExchange_DHE_RSA -> do
dhparams <- decodeServerKeyXchg_DH
signature <- getOpaque16
return $ SKX_DHE_RSA dhparams (B.unpack signature)
CipherKeyExchange_DHE_DSS -> do
dhparams <- decodeServerKeyXchg_DH
signature <- getOpaque16
return $ SKX_DHE_DSS dhparams (B.unpack signature)
_ -> do
bs <- remaining >>= getBytes
return $ SKX_Unknown bs
CipherKeyExchange_RSA -> SKX_RSA . Just <$> decodeServerKeyXchg_RSA
CipherKeyExchange_DH_Anon -> SKX_DH_Anon <$> decodeServerKeyXchg_DH
CipherKeyExchange_DHE_RSA -> do
dhparams <- decodeServerKeyXchg_DH
signature <- getOpaque16
return $ SKX_DHE_RSA dhparams (B.unpack signature)
CipherKeyExchange_DHE_DSS -> do
dhparams <- decodeServerKeyXchg_DH
signature <- getOpaque16
return $ SKX_DHE_DSS dhparams (B.unpack signature)
_ -> do
bs <- remaining >>= getBytes
return $ SKX_Unknown bs
encodeHandshake :: Handshake -> ByteString
encodeHandshake o =
let content = runPut $ encodeHandshakeContent o in
let len = fromIntegral $ B.length content in
let header = runPut $ encodeHandshakeHeader (typeOfHandshake o) len in
B.concat [ header, content ]
let content = runPut $ encodeHandshakeContent o in
let len = fromIntegral $ B.length content in
let header = runPut $ encodeHandshakeHeader (typeOfHandshake o) len in
B.concat [ header, content ]
encodeHandshakes :: [Handshake] -> ByteString
encodeHandshakes hss = B.concat $ map encodeHandshake hss
@ -309,46 +309,46 @@ encodeHandshakeHeader ty len = putWord8 (valOfType ty) >> putWord24 len
encodeHandshakeContent :: Handshake -> Put
encodeHandshakeContent (ClientHello version random session cipherIDs compressionIDs exts) = do
putVersion version
putClientRandom32 random
putSession session
putWords16 cipherIDs
putWords8 compressionIDs
putExtensions exts
return ()
putVersion version
putClientRandom32 random
putSession session
putWords16 cipherIDs
putWords8 compressionIDs
putExtensions exts
return ()
encodeHandshakeContent (ServerHello version random session cipherID compressionID exts) =
putVersion version >> putServerRandom32 random >> putSession session
>> putWord16 cipherID >> putWord8 compressionID
>> putExtensions exts >> return ()
putVersion version >> putServerRandom32 random >> putSession session
>> putWord16 cipherID >> putWord8 compressionID
>> putExtensions exts >> return ()
encodeHandshakeContent (Certificates certs) = putOpaque24 (runPut $ mapM_ putCert certs)
encodeHandshakeContent (ClientKeyXchg content) = do
putBytes content
putBytes content
encodeHandshakeContent (ServerKeyXchg _) = do
-- FIXME
return ()
-- FIXME
return ()
encodeHandshakeContent (HelloRequest) = return ()
encodeHandshakeContent (ServerHelloDone) = return ()
encodeHandshakeContent (CertRequest certTypes sigAlgs certAuthorities) = do
putWords8 (map valOfType certTypes)
case sigAlgs of
Nothing -> return ()
Just l -> putWords16 $ map (\(x,y) -> (fromIntegral $ valOfType x) * 256 + (fromIntegral $ valOfType y)) l
putBytes $ B.pack certAuthorities
putWords8 (map valOfType certTypes)
case sigAlgs of
Nothing -> return ()
Just l -> putWords16 $ map (\(x,y) -> (fromIntegral $ valOfType x) * 256 + (fromIntegral $ valOfType y)) l
putBytes $ B.pack certAuthorities
encodeHandshakeContent (CertVerify _) = undefined
encodeHandshakeContent (Finished opaque) = putBytes opaque
encodeHandshakeContent (NextProtocolNegotiation protocol) = do
putOpaque8 protocol
putOpaque8 $ B.replicate paddingLen 0
where paddingLen = 32 - ((B.length protocol + 2) `mod` 32)
putOpaque8 protocol
putOpaque8 $ B.replicate paddingLen 0
where paddingLen = 32 - ((B.length protocol + 2) `mod` 32)
{- FIXME make sure it return error if not 32 available -}
getRandom32 :: Get Bytes
@ -371,10 +371,10 @@ putServerRandom32 (ServerRandom r) = putRandom32 r
getSession :: Get Session
getSession = do
len8 <- getWord8
case fromIntegral len8 of
0 -> return $ Session Nothing
len -> Session . Just <$> getBytes len
len8 <- getWord8
case fromIntegral len8 of
0 -> return $ Session Nothing
len -> Session . Just <$> getBytes len
putSession :: Session -> Put
putSession (Session Nothing) = putWord8 0
@ -383,10 +383,10 @@ putSession (Session (Just s)) = putOpaque8 s
getCerts :: Int -> Get [Bytes]
getCerts 0 = return []
getCerts len = do
certlen <- getWord24
cert <- getBytes certlen
certxs <- getCerts (len - certlen - 3)
return (cert : certxs)
certlen <- getWord24
cert <- getBytes certlen
certxs <- getCerts (len - certlen - 3)
return (cert : certxs)
putCert :: X509 -> Put
putCert cert = putOpaque24 (B.concat $ L.toChunks $ encodeCertificate cert)
@ -394,11 +394,11 @@ putCert cert = putOpaque24 (B.concat $ L.toChunks $ encodeCertificate cert)
getExtensions :: Int -> Get [Extension]
getExtensions 0 = return []
getExtensions len = do
extty <- getWord16
extdatalen <- getWord16
extdata <- getBytes $ fromIntegral extdatalen
extxs <- getExtensions (len - fromIntegral extdatalen - 4)
return $ (extty, extdata) : extxs
extty <- getWord16
extdatalen <- getWord16
extdata <- getBytes $ fromIntegral extdatalen
extxs <- getExtensions (len - fromIntegral extdatalen - 4)
return $ (extty, extdata) : extxs
putExtension :: Extension -> Put
putExtension (ty, l) = putWord16 ty >> putOpaque16 l
@ -413,8 +413,8 @@ putExtensions es = putOpaque16 (runPut $ mapM_ putExtension es)
decodeChangeCipherSpec :: ByteString -> Either TLSError ()
decodeChangeCipherSpec = runGetErr "changecipherspec" $ do
x <- getWord8
when (x /= 1) (fail "unknown change cipher spec content")
x <- getWord8
when (x /= 1) (fail "unknown change cipher spec content")
encodeChangeCipherSpec :: ByteString
encodeChangeCipherSpec = runPut (putWord8 1)
@ -425,18 +425,18 @@ encodeChangeCipherSpec = runPut (putWord8 1)
-}
decodeExtSecureRenegotiation :: Bool -> Bytes -> Either TLSError (Bytes, Maybe Bytes)
decodeExtSecureRenegotiation isServerHello = runGetErr "ext-secure-renegotiation" $ do
l <- fromIntegral <$> getWord8
if isServerHello
then do
cvd <- getBytes (l `div` 2)
svd <- getBytes (l `div` 2)
return (cvd, Just svd)
else getBytes (l `div` 2) >>= \cvd -> return (cvd, Nothing)
l <- fromIntegral <$> getWord8
if isServerHello
then do
cvd <- getBytes (l `div` 2)
svd <- getBytes (l `div` 2)
return (cvd, Just svd)
else getBytes (l `div` 2) >>= \cvd -> return (cvd, Nothing)
encodeExtSecureRenegotiation :: Bytes -> Maybe Bytes -> Bytes
encodeExtSecureRenegotiation cvd msvd = runPut $ do
let svd = maybe B.empty id msvd
putOpaque8 (cvd `B.append` svd)
let svd = maybe B.empty id msvd
putOpaque8 (cvd `B.append` svd)
decodeExtNextProtocolNegotiation :: Bytes -> Either TLSError [Bytes]
decodeExtNextProtocolNegotiation = runGetErr "ext-next-protocol-negotiation" p
@ -451,7 +451,7 @@ encodeExtNextProtocolNegotiation = runPut . mapM_ putOpaque8
-- rsa pre master secret
decodePreMasterSecret :: Bytes -> Either TLSError (Version, Bytes)
decodePreMasterSecret = runGetErr "pre-master-secret" $ do
liftM2 (,) getVersion (getBytes 46)
liftM2 (,) getVersion (getBytes 46)
encodePreMasterSecret :: Version -> Bytes -> Bytes
encodePreMasterSecret version bytes = runPut (putVersion version >> putBytes bytes)
@ -463,16 +463,16 @@ type PRF = Bytes -> Bytes -> Int -> Bytes
generateMasterSecret_SSL :: Bytes -> ClientRandom -> ServerRandom -> Bytes
generateMasterSecret_SSL premasterSecret (ClientRandom c) (ServerRandom s) =
B.concat $ map (computeMD5) ["A","BB","CCC"]
where
computeMD5 label = MD5.hash $ B.concat [ premasterSecret, computeSHA1 label ]
computeSHA1 label = SHA1.hash $ B.concat [ label, premasterSecret, c, s ]
B.concat $ map (computeMD5) ["A","BB","CCC"]
where
computeMD5 label = MD5.hash $ B.concat [ premasterSecret, computeSHA1 label ]
computeSHA1 label = SHA1.hash $ B.concat [ label, premasterSecret, c, s ]
generateMasterSecret_TLS :: PRF -> Bytes -> ClientRandom -> ServerRandom -> Bytes
generateMasterSecret_TLS prf premasterSecret (ClientRandom c) (ServerRandom s) =
prf premasterSecret seed 48
where
seed = B.concat [ "master secret", c, s ]
prf premasterSecret seed 48
where
seed = B.concat [ "master secret", c, s ]
generateMasterSecret :: Version -> Bytes -> ClientRandom -> ServerRandom -> Bytes
generateMasterSecret SSL2 = generateMasterSecret_SSL
@ -483,15 +483,15 @@ generateMasterSecret TLS12 = generateMasterSecret_TLS prf_SHA256
generateKeyBlock_TLS :: PRF -> ClientRandom -> ServerRandom -> Bytes -> Int -> Bytes
generateKeyBlock_TLS prf (ClientRandom c) (ServerRandom s) mastersecret kbsize =
prf mastersecret seed kbsize where seed = B.concat [ "key expansion", s, c ]
prf mastersecret seed kbsize where seed = B.concat [ "key expansion", s, c ]
generateKeyBlock_SSL :: ClientRandom -> ServerRandom -> Bytes -> Int -> Bytes
generateKeyBlock_SSL (ClientRandom c) (ServerRandom s) mastersecret kbsize =
B.concat $ map computeMD5 $ take ((kbsize `div` 16) + 1) labels
where
labels = [ uncurry BC.replicate x | x <- zip [1..] ['A'..'Z'] ]
computeMD5 label = MD5.hash $ B.concat [ mastersecret, computeSHA1 label ]
computeSHA1 label = SHA1.hash $ B.concat [ label, mastersecret, s, c ]
B.concat $ map computeMD5 $ take ((kbsize `div` 16) + 1) labels
where
labels = [ uncurry BC.replicate x | x <- zip [1..] ['A'..'Z'] ]
computeMD5 label = MD5.hash $ B.concat [ mastersecret, computeSHA1 label ]
computeSHA1 label = SHA1.hash $ B.concat [ label, mastersecret, s, c ]
generateKeyBlock :: Version -> ClientRandom -> ServerRandom -> Bytes -> Int -> Bytes
generateKeyBlock SSL2 = generateKeyBlock_SSL
@ -502,29 +502,29 @@ generateKeyBlock TLS12 = generateKeyBlock_TLS prf_SHA256
generateFinished_TLS :: PRF -> Bytes -> Bytes -> HashCtx -> Bytes
generateFinished_TLS prf label mastersecret hashctx = prf mastersecret seed 12
where
seed = B.concat [ label, hashFinal hashctx ]
where
seed = B.concat [ label, hashFinal hashctx ]
generateFinished_SSL :: Bytes -> Bytes -> HashCtx -> Bytes
generateFinished_SSL sender mastersecret hashctx = B.concat [md5hash, sha1hash]
where
md5hash = MD5.hash $ B.concat [ mastersecret, pad2, md5left ]
sha1hash = SHA1.hash $ B.concat [ mastersecret, B.take 40 pad2, sha1left ]
where
md5hash = MD5.hash $ B.concat [ mastersecret, pad2, md5left ]
sha1hash = SHA1.hash $ B.concat [ mastersecret, B.take 40 pad2, sha1left ]
lefthash = hashFinal $ flip hashUpdateSSL (pad1, B.take 40 pad1)
$ foldl hashUpdate hashctx [sender,mastersecret]
(md5left,sha1left) = B.splitAt 16 lefthash
pad2 = B.replicate 48 0x5c
pad1 = B.replicate 48 0x36
lefthash = hashFinal $ flip hashUpdateSSL (pad1, B.take 40 pad1)
$ foldl hashUpdate hashctx [sender,mastersecret]
(md5left,sha1left) = B.splitAt 16 lefthash
pad2 = B.replicate 48 0x5c
pad1 = B.replicate 48 0x36
generateClientFinished :: Version -> Bytes -> HashCtx -> Bytes
generateClientFinished ver
| ver < TLS10 = generateFinished_SSL "CLNT"
| ver < TLS12 = generateFinished_TLS prf_MD5SHA1 "client finished"
| otherwise = generateFinished_TLS prf_SHA256 "client finished"
| ver < TLS10 = generateFinished_SSL "CLNT"
| ver < TLS12 = generateFinished_TLS prf_MD5SHA1 "client finished"
| otherwise = generateFinished_TLS prf_SHA256 "client finished"
generateServerFinished :: Version -> Bytes -> HashCtx -> Bytes
generateServerFinished ver
| ver < TLS10 = generateFinished_SSL "SRVR"
| ver < TLS12 = generateFinished_TLS prf_MD5SHA1 "server finished"
| otherwise = generateFinished_TLS prf_SHA256 "server finished"
| ver < TLS10 = generateFinished_SSL "SRVR"
| ver < TLS12 = generateFinished_TLS prf_MD5SHA1 "server finished"
| otherwise = generateFinished_TLS prf_SHA256 "server finished"

View file

@ -37,75 +37,75 @@ processPacket (Record ProtocolType_AppData _ fragment) = return $ AppData $ frag
processPacket (Record ProtocolType_Alert _ fragment) = return . Alert =<< returnEither (decodeAlerts $ fragmentGetBytes fragment)
processPacket (Record ProtocolType_ChangeCipherSpec _ fragment) = do
returnEither $ decodeChangeCipherSpec $ fragmentGetBytes fragment
switchRxEncryption
return ChangeCipherSpec
returnEither $ decodeChangeCipherSpec $ fragmentGetBytes fragment
switchRxEncryption
return ChangeCipherSpec
processPacket (Record ProtocolType_Handshake ver fragment) = do
keyxchg <- getCipherKeyExchangeType
keyxchg <- getCipherKeyExchangeType
npn <- getExtensionNPN
let currentparams = CurrentParams
{ cParamsVersion = ver
, cParamsKeyXchgType = maybe CipherKeyExchange_RSA id $ keyxchg
, cParamsSupportNPN = npn
}
handshakes <- returnEither (decodeHandshakes $ fragmentGetBytes fragment)
hss <- forM handshakes $ \(ty, content) -> do
case decodeHandshake currentparams ty content of
Left err -> throwError err
Right hs -> return hs
return $ Handshake hss
let currentparams = CurrentParams
{ cParamsVersion = ver
, cParamsKeyXchgType = maybe CipherKeyExchange_RSA id $ keyxchg
, cParamsSupportNPN = npn
}
handshakes <- returnEither (decodeHandshakes $ fragmentGetBytes fragment)
hss <- forM handshakes $ \(ty, content) -> do
case decodeHandshake currentparams ty content of
Left err -> throwError err
Right hs -> return hs
return $ Handshake hss
processHandshake :: Handshake -> TLSSt ()
processHandshake hs = do
clientmode <- isClientContext
case hs of
ClientHello cver ran _ _ _ ex -> unless clientmode $ do
mapM_ processClientExtension ex
startHandshakeClient cver ran
Certificates certs -> when clientmode $ do processCertificates certs
ClientKeyXchg content -> unless clientmode $ do
processClientKeyXchg content
clientmode <- isClientContext
case hs of
ClientHello cver ran _ _ _ ex -> unless clientmode $ do
mapM_ processClientExtension ex
startHandshakeClient cver ran
Certificates certs -> when clientmode $ do processCertificates certs
ClientKeyXchg content -> unless clientmode $ do
processClientKeyXchg content
NextProtocolNegotiation selected_protocol ->
unless clientmode $ do
setNegotiatedProtocol selected_protocol
Finished fdata -> processClientFinished fdata
_ -> return ()
when (finishHandshakeTypeMaterial $ typeOfHandshake hs) (updateHandshakeDigest $ encodeHandshake hs)
where
-- secure renegotiation
processClientExtension (0xff01, content) = do
v <- getVerifiedData True
let bs = encodeExtSecureRenegotiation v Nothing
when (bs /= content) $ throwError $
Error_Protocol ("client verified data not matching: " ++ show v ++ ":" ++ show content, True, HandshakeFailure)
setSecureRenegotiation True
-- unknown extensions
processClientExtension _ = return ()
Finished fdata -> processClientFinished fdata
_ -> return ()
when (finishHandshakeTypeMaterial $ typeOfHandshake hs) (updateHandshakeDigest $ encodeHandshake hs)
where
-- secure renegotiation
processClientExtension (0xff01, content) = do
v <- getVerifiedData True
let bs = encodeExtSecureRenegotiation v Nothing
when (bs /= content) $ throwError $
Error_Protocol ("client verified data not matching: " ++ show v ++ ":" ++ show content, True, HandshakeFailure)
setSecureRenegotiation True
-- unknown extensions
processClientExtension _ = return ()
decryptRSA :: ByteString -> TLSSt (Either KxError ByteString)
decryptRSA econtent = do
ver <- stVersion <$> get
rsapriv <- fromJust "rsa private key" . hstRSAPrivateKey . fromJust "handshake" . stHandshake <$> get
return $ kxDecrypt rsapriv (if ver < TLS10 then econtent else B.drop 2 econtent)
ver <- stVersion <$> get
rsapriv <- fromJust "rsa private key" . hstRSAPrivateKey . fromJust "handshake" . stHandshake <$> get
return $ kxDecrypt rsapriv (if ver < TLS10 then econtent else B.drop 2 econtent)
processServerHello :: Handshake -> TLSSt ()
processServerHello (ServerHello sver ran _ _ _ ex) = do
-- FIXME notify the user to take action if the extension requested is missing
-- secreneg <- getSecureRenegotiation
-- when (secreneg && (isNothing $ lookup 0xff01 ex)) $ ...
mapM_ processServerExtension ex
setServerRandom ran
setVersion sver
where
processServerExtension (0xff01, content) = do
cv <- getVerifiedData True
sv <- getVerifiedData False
let bs = encodeExtSecureRenegotiation cv (Just sv)
when (bs /= content) $ throwError $ Error_Protocol ("server secure renegotiation data not matching", True, HandshakeFailure)
return ()
-- FIXME notify the user to take action if the extension requested is missing
-- secreneg <- getSecureRenegotiation
-- when (secreneg && (isNothing $ lookup 0xff01 ex)) $ ...
mapM_ processServerExtension ex
setServerRandom ran
setVersion sver
where
processServerExtension (0xff01, content) = do
cv <- getVerifiedData True
sv <- getVerifiedData False
let bs = encodeExtSecureRenegotiation cv (Just sv)
when (bs /= content) $ throwError $ Error_Protocol ("server secure renegotiation data not matching", True, HandshakeFailure)
return ()
processServerExtension _ = return ()
processServerExtension _ = return ()
processServerHello _ = error "processServerHello called on wrong type"
-- process the client key exchange message. the protocol expects the initial
@ -113,29 +113,29 @@ processServerHello _ = error "processServerHello called on wrong type"
-- in case the version mismatch, generate a random master secret
processClientKeyXchg :: ByteString -> TLSSt ()
processClientKeyXchg encryptedPremaster = do
expectedVer <- hstClientVersion . fromJust "handshake" . stHandshake <$> get
random <- genTLSRandom 48
ePremaster <- decryptRSA encryptedPremaster
case ePremaster of
Left _ -> setMasterSecretFromPre random
Right premaster -> case decodePreMasterSecret premaster of
Left _ -> setMasterSecretFromPre random
Right (ver, _)
| ver /= expectedVer -> setMasterSecretFromPre random
| otherwise -> setMasterSecretFromPre premaster
expectedVer <- hstClientVersion . fromJust "handshake" . stHandshake <$> get
random <- genTLSRandom 48
ePremaster <- decryptRSA encryptedPremaster
case ePremaster of
Left _ -> setMasterSecretFromPre random
Right premaster -> case decodePreMasterSecret premaster of
Left _ -> setMasterSecretFromPre random
Right (ver, _)
| ver /= expectedVer -> setMasterSecretFromPre random
| otherwise -> setMasterSecretFromPre premaster
processClientFinished :: FinishedData -> TLSSt ()
processClientFinished fdata = do
cc <- stClientContext <$> get
expected <- getHandshakeDigest (not cc)
when (expected /= fdata) $ do
throwError $ Error_Protocol("bad record mac", True, BadRecordMac)
updateVerifiedData False fdata
return ()
cc <- stClientContext <$> get
expected <- getHandshakeDigest (not cc)
when (expected /= fdata) $ do
throwError $ Error_Protocol("bad record mac", True, BadRecordMac)
updateVerifiedData False fdata
return ()
processCertificates :: [X509] -> TLSSt ()
processCertificates certs = do
let (X509 mainCert _ _ _ _) = head certs
case certPubKey mainCert of
PubKeyRSA pubkey -> setPublicKey (PubRSA pubkey)
_ -> return ()
let (X509 mainCert _ _ _ _) = head certs
case certPubKey mainCert of
PubKeyRSA pubkey -> setPublicKey (PubRSA pubkey)
_ -> return ()

View file

@ -12,20 +12,20 @@
-- higher-level clients.
--
module Network.TLS.Record
( Record(..)
, Fragment
, fragmentGetBytes
, fragmentPlaintext
, fragmentCiphertext
, recordToRaw
, rawToRecord
, recordToHeader
, Plaintext
, Compressed
, Ciphertext
, engageRecord
, disengageRecord
) where
( Record(..)
, Fragment
, fragmentGetBytes
, fragmentPlaintext
, fragmentCiphertext
, recordToRaw
, rawToRecord
, recordToHeader
, Plaintext
, Compressed
, Ciphertext
, engageRecord
, disengageRecord
) where
import Network.TLS.Record.Types
import Network.TLS.Record.Engage

View file

@ -9,8 +9,8 @@
-- The record is decrypted, checked for integrity and then decompressed.
--
module Network.TLS.Record.Disengage
( disengageRecord
) where
( disengageRecord
) where
import Control.Monad.State
import Control.Monad.Error
@ -30,88 +30,88 @@ disengageRecord = decryptRecord >=> uncompressRecord
uncompressRecord :: Record Compressed -> TLSSt (Record Plaintext)
uncompressRecord record = onRecordFragment record $ fragmentUncompress $ \bytes ->
withCompression $ compressionInflate bytes
withCompression $ compressionInflate bytes
decryptRecord :: Record Ciphertext -> TLSSt (Record Compressed)
decryptRecord record = onRecordFragment record $ fragmentUncipher $ \e -> do
st <- get
if stRxEncrypted st
then decryptData e >>= getCipherData record
else return e
st <- get
if stRxEncrypted st
then decryptData e >>= getCipherData record
else return e
getCipherData :: Record a -> CipherData -> TLSSt ByteString
getCipherData (Record pt ver _) cdata = do
-- check if the MAC is valid.
macValid <- case cipherDataMAC cdata of
Nothing -> return True
Just digest -> do
let new_hdr = Header pt ver (fromIntegral $ B.length $ cipherDataContent cdata)
expected_digest <- makeDigest False new_hdr $ cipherDataContent cdata
return (expected_digest `bytesEq` digest)
-- check if the MAC is valid.
macValid <- case cipherDataMAC cdata of
Nothing -> return True
Just digest -> do
let new_hdr = Header pt ver (fromIntegral $ B.length $ cipherDataContent cdata)
expected_digest <- makeDigest False new_hdr $ cipherDataContent cdata
return (expected_digest `bytesEq` digest)
-- check if the padding is filled with the correct pattern if it exists
paddingValid <- case cipherDataPadding cdata of
Nothing -> return True
Just pad -> do
cver <- gets stVersion
let b = B.length pad - 1
return (if cver < TLS10 then True else B.replicate (B.length pad) (fromIntegral b) `bytesEq` pad)
-- check if the padding is filled with the correct pattern if it exists
paddingValid <- case cipherDataPadding cdata of
Nothing -> return True
Just pad -> do
cver <- gets stVersion
let b = B.length pad - 1
return (if cver < TLS10 then True else B.replicate (B.length pad) (fromIntegral b) `bytesEq` pad)
unless (macValid &&! paddingValid) $ do
throwError $ Error_Protocol ("bad record mac", True, BadRecordMac)
unless (macValid &&! paddingValid) $ do
throwError $ Error_Protocol ("bad record mac", True, BadRecordMac)
return $ cipherDataContent cdata
return $ cipherDataContent cdata
decryptData :: Bytes -> TLSSt CipherData
decryptData econtent = do
st <- get
st <- get
let cipher = fromJust "cipher" $ stCipher st
let bulk = cipherBulk cipher
let cst = fromJust "rx crypt state" $ stRxCryptState st
let digestSize = hashSize $ cipherHash cipher
let writekey = cstKey cst
let cipher = fromJust "cipher" $ stCipher st
let bulk = cipherBulk cipher
let cst = fromJust "rx crypt state" $ stRxCryptState st
let digestSize = hashSize $ cipherHash cipher
let writekey = cstKey cst
case bulkF bulk of
BulkNoneF -> do
let contentlen = B.length econtent - digestSize
case partition3 econtent (contentlen, digestSize, 0) of
Nothing ->
throwError $ Error_Misc "partition3 failed"
Just (content, mac, _) ->
return $ CipherData
{ cipherDataContent = content
, cipherDataMAC = Just mac
, cipherDataPadding = Nothing
}
BulkBlockF _ decryptF -> do
{- update IV -}
let (iv, econtent') =
if hasExplicitBlockIV $ stVersion st
then B.splitAt (bulkIVSize bulk) econtent
else (cstIV cst, econtent)
let newiv = fromJust "new iv" $ takelast (bulkBlockSize bulk) econtent'
put $ st { stRxCryptState = Just $ cst { cstIV = newiv } }
case bulkF bulk of
BulkNoneF -> do
let contentlen = B.length econtent - digestSize
case partition3 econtent (contentlen, digestSize, 0) of
Nothing ->
throwError $ Error_Misc "partition3 failed"
Just (content, mac, _) ->
return $ CipherData
{ cipherDataContent = content
, cipherDataMAC = Just mac
, cipherDataPadding = Nothing
}
BulkBlockF _ decryptF -> do
{- update IV -}
let (iv, econtent') =
if hasExplicitBlockIV $ stVersion st
then B.splitAt (bulkIVSize bulk) econtent
else (cstIV cst, econtent)
let newiv = fromJust "new iv" $ takelast (bulkBlockSize bulk) econtent'
put $ st { stRxCryptState = Just $ cst { cstIV = newiv } }
let content' = decryptF writekey iv econtent'
let paddinglength = fromIntegral (B.last content') + 1
let contentlen = B.length content' - paddinglength - digestSize
let (content, mac, padding) = fromJust "p3" $ partition3 content' (contentlen, digestSize, paddinglength)
return $ CipherData
{ cipherDataContent = content
, cipherDataMAC = Just mac
, cipherDataPadding = Just padding
}
BulkStreamF initF _ decryptF -> do
let iv = cstIV cst
let (content', newiv) = decryptF (if iv /= B.empty then iv else initF writekey) econtent
{- update Ctx -}
let contentlen = B.length content' - digestSize
let (content, mac, _) = fromJust "p3" $ partition3 content' (contentlen, digestSize, 0)
put $ st { stRxCryptState = Just $ cst { cstIV = newiv } }
return $ CipherData
{ cipherDataContent = content
, cipherDataMAC = Just mac
, cipherDataPadding = Nothing
}
let content' = decryptF writekey iv econtent'
let paddinglength = fromIntegral (B.last content') + 1
let contentlen = B.length content' - paddinglength - digestSize
let (content, mac, padding) = fromJust "p3" $ partition3 content' (contentlen, digestSize, paddinglength)
return $ CipherData
{ cipherDataContent = content
, cipherDataMAC = Just mac
, cipherDataPadding = Just padding
}
BulkStreamF initF _ decryptF -> do
let iv = cstIV cst
let (content', newiv) = decryptF (if iv /= B.empty then iv else initF writekey) econtent
{- update Ctx -}
let contentlen = B.length content' - digestSize
let (content, mac, _) = fromJust "p3" $ partition3 content' (contentlen, digestSize, 0)
put $ st { stRxCryptState = Just $ cst { cstIV = newiv } }
return $ CipherData
{ cipherDataContent = content
, cipherDataMAC = Just mac
, cipherDataPadding = Nothing
}

View file

@ -9,8 +9,8 @@
-- The record is compressed, added some integrity field, then encrypted.
--
module Network.TLS.Record.Engage
( engageRecord
) where
( engageRecord
) where
import Control.Monad.State
@ -28,8 +28,8 @@ engageRecord = compressRecord >=> encryptRecord
compressRecord :: Record Plaintext -> TLSSt (Record Compressed)
compressRecord record =
onRecordFragment record $ fragmentCompress $ \bytes -> do
withCompression $ compressionDeflate bytes
onRecordFragment record $ fragmentCompress $ \bytes -> do
withCompression $ compressionDeflate bytes
{-
- when Tx Encrypted is set, we pass the data through encryptContent, otherwise
@ -37,53 +37,53 @@ compressRecord record =
-}
encryptRecord :: Record Compressed -> TLSSt (Record Ciphertext)
encryptRecord record = onRecordFragment record $ fragmentCipher $ \bytes -> do
st <- get
if stTxEncrypted st
then encryptContent record bytes
else return bytes
st <- get
if stTxEncrypted st
then encryptContent record bytes
else return bytes
encryptContent :: Record Compressed -> ByteString -> TLSSt ByteString
encryptContent record content = do
digest <- makeDigest True (recordToHeader record) content
encryptData $ B.concat [content, digest]
digest <- makeDigest True (recordToHeader record) content
encryptData $ B.concat [content, digest]
encryptData :: ByteString -> TLSSt ByteString
encryptData content = do
st <- get
st <- get
let cipher = fromJust "cipher" $ stCipher st
let bulk = cipherBulk cipher
let cst = fromJust "tx crypt state" $ stTxCryptState st
let cipher = fromJust "cipher" $ stCipher st
let bulk = cipherBulk cipher
let cst = fromJust "tx crypt state" $ stTxCryptState st
let writekey = cstKey cst
let writekey = cstKey cst
case bulkF bulk of
BulkNoneF -> return content
BulkBlockF encrypt _ -> do
let blockSize = fromIntegral $ bulkBlockSize bulk
let msg_len = B.length content
let padding = if blockSize > 0
then
let padbyte = blockSize - (msg_len `mod` blockSize) in
let padbyte' = if padbyte == 0 then blockSize else padbyte in
B.replicate padbyte' (fromIntegral (padbyte' - 1))
else
B.empty
case bulkF bulk of
BulkNoneF -> return content
BulkBlockF encrypt _ -> do
let blockSize = fromIntegral $ bulkBlockSize bulk
let msg_len = B.length content
let padding = if blockSize > 0
then
let padbyte = blockSize - (msg_len `mod` blockSize) in
let padbyte' = if padbyte == 0 then blockSize else padbyte in
B.replicate padbyte' (fromIntegral (padbyte' - 1))
else
B.empty
-- before TLS 1.1, the block cipher IV is made of the residual of the previous block.
iv <- if hasExplicitBlockIV $ stVersion st
then genTLSRandom (bulkIVSize bulk)
else return $ cstIV cst
let e = encrypt writekey iv (B.concat [ content, padding ])
if hasExplicitBlockIV $ stVersion st
then return $ B.concat [iv,e]
else do
let newiv = fromJust "new iv" $ takelast (bulkIVSize bulk) e
put $ st { stTxCryptState = Just $ cst { cstIV = newiv } }
return e
BulkStreamF initF encryptF _ -> do
let iv = cstIV cst
let (e, newiv) = encryptF (if iv /= B.empty then iv else initF writekey) content
put $ st { stTxCryptState = Just $ cst { cstIV = newiv } }
return e
-- before TLS 1.1, the block cipher IV is made of the residual of the previous block.
iv <- if hasExplicitBlockIV $ stVersion st
then genTLSRandom (bulkIVSize bulk)
else return $ cstIV cst
let e = encrypt writekey iv (B.concat [ content, padding ])
if hasExplicitBlockIV $ stVersion st
then return $ B.concat [iv,e]
else do
let newiv = fromJust "new iv" $ takelast (bulkIVSize bulk) e
put $ st { stTxCryptState = Just $ cst { cstIV = newiv } }
return e
BulkStreamF initF encryptF _ -> do
let iv = cstIV cst
let (e, newiv) = encryptF (if iv /= B.empty then iv else initF writekey) content
put $ st { stTxCryptState = Just $ cst { cstIV = newiv } }
return e

View file

@ -13,30 +13,30 @@
-- higher-level clients.
--
module Network.TLS.Record.Types
( Header(..)
, ProtocolType(..)
, packetType
-- * TLS Records
, Record(..)
-- * TLS Record fragment and constructors
, Fragment
, fragmentPlaintext
, fragmentCiphertext
, fragmentGetBytes
, Plaintext
, Compressed
, Ciphertext
-- * manipulate record
, onRecordFragment
, fragmentCompress
, fragmentCipher
, fragmentUncipher
, fragmentUncompress
-- * serialize record
, rawToRecord
, recordToRaw
, recordToHeader
) where
( Header(..)
, ProtocolType(..)
, packetType
-- * TLS Records
, Record(..)
-- * TLS Record fragment and constructors
, Fragment
, fragmentPlaintext
, fragmentCiphertext
, fragmentGetBytes
, Plaintext
, Compressed
, Ciphertext
-- * manipulate record
, onRecordFragment
, fragmentCompress
, fragmentCipher
, fragmentUncipher
, fragmentUncompress
-- * serialize record
, rawToRecord
, recordToRaw
, recordToHeader
) where
import Network.TLS.Struct
import Network.TLS.State

View file

@ -29,17 +29,17 @@ import Network.TLS.Crypto
-}
makeRecord :: Packet -> TLSSt (Record Plaintext)
makeRecord pkt = do
ver <- stVersion <$> get
content <- writePacketContent pkt
return $ Record (packetType pkt) ver (fragmentPlaintext content)
ver <- stVersion <$> get
content <- writePacketContent pkt
return $ Record (packetType pkt) ver (fragmentPlaintext content)
{-
- Handshake data need to update a digest
-}
processRecord :: Record Plaintext -> TLSSt (Record Plaintext)
processRecord record@(Record ty _ fragment) = do
when (ty == ProtocolType_Handshake) (updateHandshakeDigest $ fragmentGetBytes fragment)
return record
when (ty == ProtocolType_Handshake) (updateHandshakeDigest $ fragmentGetBytes fragment)
return record
{-
- ChangeCipherSpec state change need to be handled after encryption otherwise
@ -48,7 +48,7 @@ processRecord record@(Record ty _ fragment) = do
-}
postprocessRecord :: Record Ciphertext -> TLSSt (Record Ciphertext)
postprocessRecord record@(Record ProtocolType_ChangeCipherSpec _ _) =
switchTxEncryption >> return record
switchTxEncryption >> return record
postprocessRecord record = return record
{-
@ -56,7 +56,7 @@ postprocessRecord record = return record
-}
encodeRecord :: Record Ciphertext -> TLSSt ByteString
encodeRecord record = return $ B.concat [ encodeHeader hdr, content ]
where (hdr, content) = recordToRaw record
where (hdr, content) = recordToRaw record
{-
- just update TLS state machine
@ -66,9 +66,9 @@ preProcessPacket (Alert _) = return ()
preProcessPacket (AppData _) = return ()
preProcessPacket (ChangeCipherSpec) = return ()
preProcessPacket (Handshake hss) = forM_ hss $ \hs -> do
case hs of
Finished fdata -> updateVerifiedData True fdata
_ -> return ()
case hs of
Finished fdata -> updateVerifiedData True fdata
_ -> return ()
{-
- writePacket transform a packet into marshalled data related to current state
@ -76,8 +76,8 @@ preProcessPacket (Handshake hss) = forM_ hss $ \hs -> do
-}
writePacket :: Packet -> TLSSt ByteString
writePacket pkt = do
preProcessPacket pkt
makeRecord pkt >>= processRecord >>= engageRecord >>= postprocessRecord >>= encodeRecord
preProcessPacket pkt
makeRecord pkt >>= processRecord >>= engageRecord >>= postprocessRecord >>= encodeRecord
{------------------------------------------------------------------------------}
{- SENDING Helpers -}
@ -88,11 +88,11 @@ writePacket pkt = do
-}
encryptRSA :: ByteString -> TLSSt ByteString
encryptRSA content = do
st <- get
let rsakey = fromJust "rsa public key" $ hstRSAPublicKey $ fromJust "handshake" $ stHandshake st
case withTLSRNG (stRandomGen st) (\g -> kxEncrypt g rsakey content) of
Left err -> fail ("rsa encrypt failed: " ++ show err)
Right (econtent, rng') -> put (st { stRandomGen = rng' }) >> return econtent
st <- get
let rsakey = fromJust "rsa public key" $ hstRSAPublicKey $ fromJust "handshake" $ stHandshake st
case withTLSRNG (stRandomGen st) (\g -> kxEncrypt g rsakey content) of
Left err -> fail ("rsa encrypt failed: " ++ show err)
Right (econtent, rng') -> put (st { stRandomGen = rng' }) >> return econtent
writePacketContent :: Packet -> TLSSt ByteString
writePacketContent (Handshake hss) = return $ encodeHandshakes hss

View file

@ -10,51 +10,51 @@
-- which is use by the Receiving module and the Sending module.
--
module Network.TLS.State
( 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
, setKeyBlock
, setVersion
, setCipher
, setServerRandom
, setSecureRenegotiation
, getSecureRenegotiation
( 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
, setKeyBlock
, setVersion
, setCipher
, setServerRandom
, setSecureRenegotiation
, getSecureRenegotiation
, setExtensionNPN
, getExtensionNPN
, setNegotiatedProtocol
, getNegotiatedProtocol
, setServerNextProtocolSuggest
, getServerNextProtocolSuggest
, getVerifiedData
, setSession
, getSession
, getSessionData
, isSessionResuming
, switchTxEncryption
, switchRxEncryption
, getCipherKeyExchangeType
, isClientContext
, startHandshakeClient
, updateHandshakeDigest
, getHandshakeDigest
, endHandshake
) where
, getVerifiedData
, setSession
, getSession
, getSessionData
, isSessionResuming
, switchTxEncryption
, switchRxEncryption
, getCipherKeyExchangeType
, isClientContext
, startHandshakeClient
, updateHandshakeDigest
, getHandshakeDigest
, endHandshake
) where
import Data.Word
import Data.Maybe (isNothing)
@ -75,138 +75,138 @@ import Crypto.Random
assert :: Monad m => String -> [(String,Bool)] -> m ()
assert fctname list = forM_ list $ \ (name, assumption) -> do
when assumption $ fail (fctname ++ ": assumption about " ++ name ++ " failed")
when assumption $ fail (fctname ++ ": assumption about " ++ name ++ " failed")
data TLSCryptState = TLSCryptState
{ cstKey :: !Bytes
, cstIV :: !Bytes
, cstMacSecret :: !Bytes
} deriving (Show)
{ cstKey :: !Bytes
, cstIV :: !Bytes
, cstMacSecret :: !Bytes
} deriving (Show)
data TLSMacState = TLSMacState
{ msSequence :: Word64
} deriving (Show)
{ msSequence :: Word64
} deriving (Show)
data TLSHandshakeState = TLSHandshakeState
{ hstClientVersion :: !(Version)
, hstClientRandom :: !ClientRandom
, hstServerRandom :: !(Maybe ServerRandom)
, hstMasterSecret :: !(Maybe Bytes)
, hstRSAPublicKey :: !(Maybe PublicKey)
, hstRSAPrivateKey :: !(Maybe PrivateKey)
, hstHandshakeDigest :: !HashCtx
} deriving (Show)
{ hstClientVersion :: !(Version)
, hstClientRandom :: !ClientRandom
, hstServerRandom :: !(Maybe ServerRandom)
, hstMasterSecret :: !(Maybe Bytes)
, hstRSAPublicKey :: !(Maybe PublicKey)
, hstRSAPrivateKey :: !(Maybe PrivateKey)
, hstHandshakeDigest :: !HashCtx
} deriving (Show)
data StateRNG = forall g . CryptoRandomGen g => StateRNG g
instance Show StateRNG where
show _ = "rng[..]"
show _ = "rng[..]"
data TLSState = TLSState
{ 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
{ 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]
} deriving (Show)
} deriving (Show)
newtype TLSSt a = TLSSt { runTLSSt :: ErrorT TLSError (State TLSState) a }
deriving (Monad, MonadError TLSError)
deriving (Monad, MonadError TLSError)
instance Functor TLSSt where
fmap f = TLSSt . fmap f . runTLSSt
fmap f = TLSSt . fmap f . runTLSSt
instance MonadState TLSState TLSSt where
put x = TLSSt (lift $ put x)
get = TLSSt (lift get)
put x = TLSSt (lift $ put x)
get = TLSSt (lift get)
runTLSState :: TLSSt a -> TLSState -> (Either TLSError a, TLSState)
runTLSState f st = runState (runErrorT (runTLSSt f)) st
newTLSState :: CryptoRandomGen g => g -> TLSState
newTLSState rng = TLSState
{ 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
{ 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
}
}
withTLSRNG :: StateRNG -> (forall g . CryptoRandomGen g => g -> Either e (a,g)) -> Either e (a, StateRNG)
withTLSRNG (StateRNG rng) f = case f rng of
Left err -> Left err
Right (a, rng') -> Right (a, StateRNG rng')
Left err -> Left err
Right (a, rng') -> Right (a, StateRNG rng')
withCompression :: (Compression -> (Compression, a)) -> TLSSt a
withCompression f = do
compression <- stCompression <$> get
let (nc, a) = f compression
modify (\st -> st { stCompression = nc })
return a
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
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
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
makeDigest w hdr content = do
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
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
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
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
let newms = ms { msSequence = (msSequence ms) + 1 }
let newms = ms { msSequence = (msSequence ms) + 1 }
modify (\_ -> if w then st { stTxMacState = Just newms } else st { stRxMacState = Just newms })
return digest
modify (\_ -> if w then st { stTxMacState = Just newms } else st { stRxMacState = Just newms })
return digest
updateVerifiedData :: MonadState TLSState m => Bool -> Bytes -> m ()
updateVerifiedData sending bs = do
cc <- isClientContext
if cc /= sending
then modify (\st -> st { stServerVerifiedData = bs })
else modify (\st -> st { stClientVerifiedData = bs })
cc <- isClientContext
if cc /= sending
then modify (\st -> st { stServerVerifiedData = bs })
else modify (\st -> st { stClientVerifiedData = bs })
finishHandshakeTypeMaterial :: HandshakeType -> Bool
finishHandshakeTypeMaterial HandshakeType_ClientHello = True
@ -233,24 +233,24 @@ setServerRandom ran = updateHandshake "srand" (\hst -> hst { hstServerRandom = J
setMasterSecret :: MonadState TLSState m => Bytes -> m ()
setMasterSecret masterSecret = do
hasValidHandshake "master secret"
hasValidHandshake "master secret"
updateHandshake "master secret" (\hst -> hst { hstMasterSecret = Just masterSecret } )
setKeyBlock
return ()
updateHandshake "master secret" (\hst -> hst { hstMasterSecret = Just masterSecret } )
setKeyBlock
return ()
setMasterSecretFromPre :: MonadState TLSState m => Bytes -> m ()
setMasterSecretFromPre premasterSecret = do
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)
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 ()
setPublicKey pk = updateHandshake "publickey" (\hst -> hst { hstRSAPublicKey = Just pk })
@ -260,14 +260,14 @@ setPrivateKey pk = updateHandshake "privatekey" (\hst -> hst { hstRSAPrivateKey
getSessionData :: MonadState TLSState m => m (Maybe SessionData)
getSessionData = do
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
}
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
}
setSession :: MonadState TLSState m => Session -> Bool -> m ()
setSession session resuming = modify (\st -> st { stSession = session, stSessionResuming = resuming })
@ -280,41 +280,41 @@ isSessionResuming = gets stSessionResuming
setKeyBlock :: MonadState TLSState m => m ()
setKeyBlock = do
st <- get
st <- get
let hst = fromJust "handshake" $ stHandshake st
let hst = fromJust "handshake" $ stHandshake st
let cc = stClientContext st
let cipher = fromJust "cipher" $ stCipher st
let keyblockSize = cipherKeyBlockSize cipher
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 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 (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
}
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
}
setCipher :: MonadState TLSState m => Cipher -> m ()
setCipher cipher = modify (\st -> st { stCipher = Just cipher })
@ -358,42 +358,42 @@ isClientContext = get >>= return . stClientContext
-- create a new empty handshake state
newEmptyHandshake :: Version -> ClientRandom -> HashCtx -> TLSHandshakeState
newEmptyHandshake ver crand digestInit = TLSHandshakeState
{ hstClientVersion = ver
, hstClientRandom = crand
, hstServerRandom = Nothing
, hstMasterSecret = Nothing
, hstRSAPublicKey = Nothing
, hstRSAPrivateKey = Nothing
, hstHandshakeDigest = digestInit
}
{ hstClientVersion = ver
, hstClientRandom = crand
, hstServerRandom = Nothing
, hstMasterSecret = Nothing
, hstRSAPublicKey = Nothing
, hstRSAPrivateKey = Nothing
, hstHandshakeDigest = digestInit
}
startHandshakeClient :: MonadState TLSState m => Version -> ClientRandom -> m ()
startHandshakeClient ver crand = do
-- 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 })
-- 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 })
hasValidHandshake :: MonadState TLSState m => String -> m ()
hasValidHandshake name = get >>= \st -> assert name [ ("valid handshake", isNothing $ stHandshake st) ]
updateHandshake :: MonadState TLSState m => String -> (TLSHandshakeState -> TLSHandshakeState) -> m ()
updateHandshake n f = do
hasValidHandshake n
modify (\st -> st { stHandshake = f <$> stHandshake st })
hasValidHandshake n
modify (\st -> st { stHandshake = f <$> stHandshake st })
updateHandshakeDigest :: MonadState TLSState m => Bytes -> m ()
updateHandshakeDigest content = updateHandshake "update digest" $ \hs ->
hs { hstHandshakeDigest = hashUpdate (hstHandshakeDigest hs) content }
hs { hstHandshakeDigest = hashUpdate (hstHandshakeDigest hs) content }
getHandshakeDigest :: MonadState TLSState m => Bool -> m Bytes
getHandshakeDigest client = do
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
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
endHandshake :: MonadState TLSState m => m ()
endHandshake = modify (\st -> st { stHandshake = Nothing })

View file

@ -10,40 +10,40 @@
-- the Struct module contains all definitions and values of the TLS protocol
--
module Network.TLS.Struct
( Bytes
, Version(..)
, ConnectionEnd(..)
, CipherType(..)
, CipherData(..)
, Extension
, CertificateType(..)
, HashAlgorithm(..)
, SignatureAlgorithm(..)
, ProtocolType(..)
, TLSError(..)
, ServerDHParams(..)
, ServerRSAParams(..)
, ServerKeyXchgAlgorithmData(..)
, Packet(..)
, Header(..)
, ServerRandom(..)
, ClientRandom(..)
, serverRandom
, clientRandom
, FinishedData
, SessionID
, Session(..)
, SessionData(..)
, AlertLevel(..)
, AlertDescription(..)
, HandshakeType(..)
, Handshake(..)
, numericalVer
, verOfNum
, TypeValuable, valOfType, valToType
, packetType
, typeOfHandshake
) where
( Bytes
, Version(..)
, ConnectionEnd(..)
, CipherType(..)
, CipherData(..)
, Extension
, CertificateType(..)
, HashAlgorithm(..)
, SignatureAlgorithm(..)
, ProtocolType(..)
, TLSError(..)
, ServerDHParams(..)
, ServerRSAParams(..)
, ServerKeyXchgAlgorithmData(..)
, Packet(..)
, Header(..)
, ServerRandom(..)
, ClientRandom(..)
, serverRandom
, clientRandom
, FinishedData
, SessionID
, Session(..)
, SessionData(..)
, AlertLevel(..)
, AlertDescription(..)
, HandshakeType(..)
, Handshake(..)
, numericalVer
, verOfNum
, TypeValuable, valOfType, valToType
, packetType
, typeOfHandshake
) where
import Data.ByteString (ByteString)
import qualified Data.ByteString as B (length)
@ -64,77 +64,77 @@ data ConnectionEnd = ConnectionServer | ConnectionClient
data CipherType = CipherStream | CipherBlock | CipherAEAD
data CipherData = CipherData
{ cipherDataContent :: Bytes
, cipherDataMAC :: Maybe Bytes
, cipherDataPadding :: Maybe Bytes
} deriving (Show,Eq)
{ cipherDataContent :: Bytes
, cipherDataMAC :: Maybe Bytes
, cipherDataPadding :: Maybe Bytes
} deriving (Show,Eq)
data CertificateType =
CertificateType_RSA_Sign -- TLS10
| CertificateType_DSS_Sign -- TLS10
| CertificateType_RSA_Fixed_DH -- TLS10
| CertificateType_DSS_Fixed_DH -- TLS10
| CertificateType_RSA_Ephemeral_DH -- TLS12
| CertificateType_DSS_Ephemeral_DH -- TLS12
| CertificateType_fortezza_dms -- TLS12
| CertificateType_Unknown Word8
deriving (Show,Eq)
CertificateType_RSA_Sign -- TLS10
| CertificateType_DSS_Sign -- TLS10
| CertificateType_RSA_Fixed_DH -- TLS10
| CertificateType_DSS_Fixed_DH -- TLS10
| CertificateType_RSA_Ephemeral_DH -- TLS12
| CertificateType_DSS_Ephemeral_DH -- TLS12
| CertificateType_fortezza_dms -- TLS12
| CertificateType_Unknown Word8
deriving (Show,Eq)
data HashAlgorithm =
HashNone
| HashMD5
| HashSHA1
| HashSHA224
| HashSHA256
| HashSHA384
| HashSHA512
| HashOther Word8
deriving (Show,Eq)
HashNone
| HashMD5
| HashSHA1
| HashSHA224
| HashSHA256
| HashSHA384
| HashSHA512
| HashOther Word8
deriving (Show,Eq)
data SignatureAlgorithm =
SignatureAnonymous
| SignatureRSA
| SignatureDSS
| SignatureECDSA
| SignatureOther Word8
deriving (Show,Eq)
SignatureAnonymous
| SignatureRSA
| SignatureDSS
| SignatureECDSA
| SignatureOther Word8
deriving (Show,Eq)
data ProtocolType =
ProtocolType_ChangeCipherSpec
| ProtocolType_Alert
| ProtocolType_Handshake
| ProtocolType_AppData
deriving (Eq, Show)
ProtocolType_ChangeCipherSpec
| ProtocolType_Alert
| ProtocolType_Handshake
| ProtocolType_AppData
deriving (Eq, Show)
-- | TLSError that might be returned through the TLS stack
data TLSError =
Error_Misc String -- ^ mainly for instance of Error
| Error_Protocol (String, Bool, AlertDescription)
| Error_Certificate String
| Error_HandshakePolicy String -- ^ handshake policy failed.
| Error_Random String
| Error_EOF
| Error_Packet String
| Error_Packet_Size_Mismatch (Int, Int)
| Error_Packet_unexpected String String
| Error_Packet_Parsing String
| Error_Internal_Packet_ByteProcessed Int Int Int
| Error_Unknown_Version Word8 Word8
| Error_Unknown_Type String
deriving (Eq, Show, Typeable)
Error_Misc String -- ^ mainly for instance of Error
| Error_Protocol (String, Bool, AlertDescription)
| Error_Certificate String
| Error_HandshakePolicy String -- ^ handshake policy failed.
| Error_Random String
| Error_EOF
| Error_Packet String
| Error_Packet_Size_Mismatch (Int, Int)
| Error_Packet_unexpected String String
| Error_Packet_Parsing String
| Error_Internal_Packet_ByteProcessed Int Int Int
| Error_Unknown_Version Word8 Word8
| Error_Unknown_Type String
deriving (Eq, Show, Typeable)
instance Error TLSError where
noMsg = Error_Misc ""
strMsg = Error_Misc
noMsg = Error_Misc ""
strMsg = Error_Misc
instance Exception TLSError
data Packet =
Handshake [Handshake]
| Alert [(AlertLevel, AlertDescription)]
| ChangeCipherSpec
| AppData ByteString
deriving (Show,Eq)
Handshake [Handshake]
| Alert [(AlertLevel, AlertDescription)]
| ChangeCipherSpec
| AppData ByteString
deriving (Show,Eq)
data Header = Header ProtocolType Version Word16 deriving (Show,Eq)
@ -144,10 +144,10 @@ type SessionID = Bytes
newtype Session = Session (Maybe SessionID) deriving (Show, Eq)
data SessionData = SessionData
{ sessionVersion :: Version
, sessionCipher :: CipherID
, sessionSecret :: Bytes
}
{ sessionVersion :: Version
, sessionCipher :: CipherID
, sessionSecret :: Bytes
}
type CipherID = Word16
type CompressionID = Word8
@ -164,84 +164,84 @@ clientRandom :: Bytes -> Maybe ClientRandom
clientRandom l = constrRandom32 ClientRandom l
data AlertLevel =
AlertLevel_Warning
| AlertLevel_Fatal
deriving (Show,Eq)
AlertLevel_Warning
| AlertLevel_Fatal
deriving (Show,Eq)
data AlertDescription =
CloseNotify
| UnexpectedMessage
| BadRecordMac
| DecryptionFailed -- ^ deprecated alert, should never be sent by compliant implementation
| RecordOverflow
| DecompressionFailure
| HandshakeFailure
| BadCertificate
| UnsupportedCertificate
| CertificateRevoked
| CertificateExpired
| CertificateUnknown
| IllegalParameter
| UnknownCa
| AccessDenied
| DecodeError
| DecryptError
| ExportRestriction
| ProtocolVersion
| InsufficientSecurity
| InternalError
| UserCanceled
| NoRenegotiation
deriving (Show,Eq)
CloseNotify
| UnexpectedMessage
| BadRecordMac
| DecryptionFailed -- ^ deprecated alert, should never be sent by compliant implementation
| RecordOverflow
| DecompressionFailure
| HandshakeFailure
| BadCertificate
| UnsupportedCertificate
| CertificateRevoked
| CertificateExpired
| CertificateUnknown
| IllegalParameter
| UnknownCa
| AccessDenied
| DecodeError
| DecryptError
| ExportRestriction
| ProtocolVersion
| InsufficientSecurity
| InternalError
| UserCanceled
| NoRenegotiation
deriving (Show,Eq)
data HandshakeType =
HandshakeType_HelloRequest
| HandshakeType_ClientHello
| HandshakeType_ServerHello
| HandshakeType_Certificate
| HandshakeType_ServerKeyXchg
| HandshakeType_CertRequest
| HandshakeType_ServerHelloDone
| HandshakeType_CertVerify
| HandshakeType_ClientKeyXchg
| HandshakeType_Finished
| HandshakeType_NPN -- Next Protocol Negotiation extension
deriving (Show,Eq)
HandshakeType_HelloRequest
| HandshakeType_ClientHello
| HandshakeType_ServerHello
| HandshakeType_Certificate
| HandshakeType_ServerKeyXchg
| HandshakeType_CertRequest
| HandshakeType_ServerHelloDone
| HandshakeType_CertVerify
| HandshakeType_ClientKeyXchg
| HandshakeType_Finished
| HandshakeType_NPN -- Next Protocol Negotiation extension
deriving (Show,Eq)
data ServerDHParams = ServerDHParams
{ dh_p :: Integer -- ^ prime modulus
, dh_g :: Integer -- ^ generator
, dh_Ys :: Integer -- ^ public value (g^X mod p)
} deriving (Show,Eq)
{ dh_p :: Integer -- ^ prime modulus
, dh_g :: Integer -- ^ generator
, dh_Ys :: Integer -- ^ public value (g^X mod p)
} deriving (Show,Eq)
data ServerRSAParams = ServerRSAParams
{ rsa_modulus :: Integer
, rsa_exponent :: Integer
} deriving (Show,Eq)
{ rsa_modulus :: Integer
, rsa_exponent :: Integer
} deriving (Show,Eq)
data ServerKeyXchgAlgorithmData =
SKX_DH_Anon ServerDHParams
| SKX_DHE_DSS ServerDHParams [Word8]
| SKX_DHE_RSA ServerDHParams [Word8]
| SKX_RSA (Maybe ServerRSAParams)
| SKX_DH_DSS (Maybe ServerRSAParams)
| SKX_DH_RSA (Maybe ServerRSAParams)
| SKX_Unknown Bytes
deriving (Show,Eq)
SKX_DH_Anon ServerDHParams
| SKX_DHE_DSS ServerDHParams [Word8]
| SKX_DHE_RSA ServerDHParams [Word8]
| SKX_RSA (Maybe ServerRSAParams)
| SKX_DH_DSS (Maybe ServerRSAParams)
| SKX_DH_RSA (Maybe ServerRSAParams)
| SKX_Unknown Bytes
deriving (Show,Eq)
data Handshake =
ClientHello !Version !ClientRandom !Session ![CipherID] ![CompressionID] [Extension]
| ServerHello !Version !ServerRandom !Session !CipherID !CompressionID [Extension]
| Certificates [X509]
| HelloRequest
| ServerHelloDone
| ClientKeyXchg Bytes
| ServerKeyXchg ServerKeyXchgAlgorithmData
| CertRequest [CertificateType] (Maybe [ (HashAlgorithm, SignatureAlgorithm) ]) [Word8]
| CertVerify [Word8]
| Finished FinishedData
| NextProtocolNegotiation Bytes -- NPN extension
deriving (Show,Eq)
ClientHello !Version !ClientRandom !Session ![CipherID] ![CompressionID] [Extension]
| ServerHello !Version !ServerRandom !Session !CipherID !CompressionID [Extension]
| Certificates [X509]
| HelloRequest
| ServerHelloDone
| ClientKeyXchg Bytes
| ServerKeyXchg ServerKeyXchgAlgorithmData
| CertRequest [CertificateType] (Maybe [ (HashAlgorithm, SignatureAlgorithm) ]) [Word8]
| CertVerify [Word8]
| Finished FinishedData
| NextProtocolNegotiation Bytes -- NPN extension
deriving (Show,Eq)
packetType :: Packet -> ProtocolType
packetType (Handshake _) = ProtocolType_Handshake
@ -278,170 +278,170 @@ verOfNum (3, 3) = Just TLS12
verOfNum _ = Nothing
class TypeValuable a where
valOfType :: a -> Word8
valToType :: Word8 -> Maybe a
valOfType :: a -> Word8
valToType :: Word8 -> Maybe a
instance TypeValuable ConnectionEnd where
valOfType ConnectionServer = 0
valOfType ConnectionClient = 1
valOfType ConnectionServer = 0
valOfType ConnectionClient = 1
valToType 0 = Just ConnectionServer
valToType 1 = Just ConnectionClient
valToType _ = Nothing
valToType 0 = Just ConnectionServer
valToType 1 = Just ConnectionClient
valToType _ = Nothing
instance TypeValuable CipherType where
valOfType CipherStream = 0
valOfType CipherBlock = 1
valOfType CipherAEAD = 2
valOfType CipherStream = 0
valOfType CipherBlock = 1
valOfType CipherAEAD = 2
valToType 0 = Just CipherStream
valToType 1 = Just CipherBlock
valToType 2 = Just CipherAEAD
valToType _ = Nothing
valToType 0 = Just CipherStream
valToType 1 = Just CipherBlock
valToType 2 = Just CipherAEAD
valToType _ = Nothing
instance TypeValuable ProtocolType where
valOfType ProtocolType_ChangeCipherSpec = 20
valOfType ProtocolType_Alert = 21
valOfType ProtocolType_Handshake = 22
valOfType ProtocolType_AppData = 23
valOfType ProtocolType_ChangeCipherSpec = 20
valOfType ProtocolType_Alert = 21
valOfType ProtocolType_Handshake = 22
valOfType ProtocolType_AppData = 23
valToType 20 = Just ProtocolType_ChangeCipherSpec
valToType 21 = Just ProtocolType_Alert
valToType 22 = Just ProtocolType_Handshake
valToType 23 = Just ProtocolType_AppData
valToType _ = Nothing
valToType 20 = Just ProtocolType_ChangeCipherSpec
valToType 21 = Just ProtocolType_Alert
valToType 22 = Just ProtocolType_Handshake
valToType 23 = Just ProtocolType_AppData
valToType _ = Nothing
instance TypeValuable HandshakeType where
valOfType HandshakeType_HelloRequest = 0
valOfType HandshakeType_ClientHello = 1
valOfType HandshakeType_ServerHello = 2
valOfType HandshakeType_Certificate = 11
valOfType HandshakeType_ServerKeyXchg = 12
valOfType HandshakeType_CertRequest = 13
valOfType HandshakeType_ServerHelloDone = 14
valOfType HandshakeType_CertVerify = 15
valOfType HandshakeType_ClientKeyXchg = 16
valOfType HandshakeType_Finished = 20
valOfType HandshakeType_NPN = 67
valOfType HandshakeType_HelloRequest = 0
valOfType HandshakeType_ClientHello = 1
valOfType HandshakeType_ServerHello = 2
valOfType HandshakeType_Certificate = 11
valOfType HandshakeType_ServerKeyXchg = 12
valOfType HandshakeType_CertRequest = 13
valOfType HandshakeType_ServerHelloDone = 14
valOfType HandshakeType_CertVerify = 15
valOfType HandshakeType_ClientKeyXchg = 16
valOfType HandshakeType_Finished = 20
valOfType HandshakeType_NPN = 67
valToType 0 = Just HandshakeType_HelloRequest
valToType 1 = Just HandshakeType_ClientHello
valToType 2 = Just HandshakeType_ServerHello
valToType 11 = Just HandshakeType_Certificate
valToType 12 = Just HandshakeType_ServerKeyXchg
valToType 13 = Just HandshakeType_CertRequest
valToType 14 = Just HandshakeType_ServerHelloDone
valToType 15 = Just HandshakeType_CertVerify
valToType 16 = Just HandshakeType_ClientKeyXchg
valToType 20 = Just HandshakeType_Finished
valToType 67 = Just HandshakeType_NPN
valToType _ = Nothing
valToType 0 = Just HandshakeType_HelloRequest
valToType 1 = Just HandshakeType_ClientHello
valToType 2 = Just HandshakeType_ServerHello
valToType 11 = Just HandshakeType_Certificate
valToType 12 = Just HandshakeType_ServerKeyXchg
valToType 13 = Just HandshakeType_CertRequest
valToType 14 = Just HandshakeType_ServerHelloDone
valToType 15 = Just HandshakeType_CertVerify
valToType 16 = Just HandshakeType_ClientKeyXchg
valToType 20 = Just HandshakeType_Finished
valToType 67 = Just HandshakeType_NPN
valToType _ = Nothing
instance TypeValuable AlertLevel where
valOfType AlertLevel_Warning = 1
valOfType AlertLevel_Fatal = 2
valOfType AlertLevel_Warning = 1
valOfType AlertLevel_Fatal = 2
valToType 1 = Just AlertLevel_Warning
valToType 2 = Just AlertLevel_Fatal
valToType _ = Nothing
valToType 1 = Just AlertLevel_Warning
valToType 2 = Just AlertLevel_Fatal
valToType _ = Nothing
instance TypeValuable AlertDescription where
valOfType CloseNotify = 0
valOfType UnexpectedMessage = 10
valOfType BadRecordMac = 20
valOfType DecryptionFailed = 21
valOfType RecordOverflow = 22
valOfType DecompressionFailure = 30
valOfType HandshakeFailure = 40
valOfType BadCertificate = 42
valOfType UnsupportedCertificate = 43
valOfType CertificateRevoked = 44
valOfType CertificateExpired = 45
valOfType CertificateUnknown = 46
valOfType IllegalParameter = 47
valOfType UnknownCa = 48
valOfType AccessDenied = 49
valOfType DecodeError = 50
valOfType DecryptError = 51
valOfType ExportRestriction = 60
valOfType ProtocolVersion = 70
valOfType InsufficientSecurity = 71
valOfType InternalError = 80
valOfType UserCanceled = 90
valOfType NoRenegotiation = 100
valOfType CloseNotify = 0
valOfType UnexpectedMessage = 10
valOfType BadRecordMac = 20
valOfType DecryptionFailed = 21
valOfType RecordOverflow = 22
valOfType DecompressionFailure = 30
valOfType HandshakeFailure = 40
valOfType BadCertificate = 42
valOfType UnsupportedCertificate = 43
valOfType CertificateRevoked = 44
valOfType CertificateExpired = 45
valOfType CertificateUnknown = 46
valOfType IllegalParameter = 47
valOfType UnknownCa = 48
valOfType AccessDenied = 49
valOfType DecodeError = 50
valOfType DecryptError = 51
valOfType ExportRestriction = 60
valOfType ProtocolVersion = 70
valOfType InsufficientSecurity = 71
valOfType InternalError = 80
valOfType UserCanceled = 90
valOfType NoRenegotiation = 100
valToType 0 = Just CloseNotify
valToType 10 = Just UnexpectedMessage
valToType 20 = Just BadRecordMac
valToType 21 = Just DecryptionFailed
valToType 22 = Just RecordOverflow
valToType 30 = Just DecompressionFailure
valToType 40 = Just HandshakeFailure
valToType 42 = Just BadCertificate
valToType 43 = Just UnsupportedCertificate
valToType 44 = Just CertificateRevoked
valToType 45 = Just CertificateExpired
valToType 46 = Just CertificateUnknown
valToType 47 = Just IllegalParameter
valToType 48 = Just UnknownCa
valToType 49 = Just AccessDenied
valToType 50 = Just DecodeError
valToType 51 = Just DecryptError
valToType 60 = Just ExportRestriction
valToType 70 = Just ProtocolVersion
valToType 71 = Just InsufficientSecurity
valToType 80 = Just InternalError
valToType 90 = Just UserCanceled
valToType 100 = Just NoRenegotiation
valToType _ = Nothing
valToType 0 = Just CloseNotify
valToType 10 = Just UnexpectedMessage
valToType 20 = Just BadRecordMac
valToType 21 = Just DecryptionFailed
valToType 22 = Just RecordOverflow
valToType 30 = Just DecompressionFailure
valToType 40 = Just HandshakeFailure
valToType 42 = Just BadCertificate
valToType 43 = Just UnsupportedCertificate
valToType 44 = Just CertificateRevoked
valToType 45 = Just CertificateExpired
valToType 46 = Just CertificateUnknown
valToType 47 = Just IllegalParameter
valToType 48 = Just UnknownCa
valToType 49 = Just AccessDenied
valToType 50 = Just DecodeError
valToType 51 = Just DecryptError
valToType 60 = Just ExportRestriction
valToType 70 = Just ProtocolVersion
valToType 71 = Just InsufficientSecurity
valToType 80 = Just InternalError
valToType 90 = Just UserCanceled
valToType 100 = Just NoRenegotiation
valToType _ = Nothing
instance TypeValuable CertificateType where
valOfType CertificateType_RSA_Sign = 1
valOfType CertificateType_DSS_Sign = 2
valOfType CertificateType_RSA_Fixed_DH = 3
valOfType CertificateType_DSS_Fixed_DH = 4
valOfType CertificateType_RSA_Ephemeral_DH = 5
valOfType CertificateType_DSS_Ephemeral_DH = 6
valOfType CertificateType_fortezza_dms = 20
valOfType (CertificateType_Unknown i) = i
valOfType CertificateType_RSA_Sign = 1
valOfType CertificateType_DSS_Sign = 2
valOfType CertificateType_RSA_Fixed_DH = 3
valOfType CertificateType_DSS_Fixed_DH = 4
valOfType CertificateType_RSA_Ephemeral_DH = 5
valOfType CertificateType_DSS_Ephemeral_DH = 6
valOfType CertificateType_fortezza_dms = 20
valOfType (CertificateType_Unknown i) = i
valToType 1 = Just CertificateType_RSA_Sign
valToType 2 = Just CertificateType_DSS_Sign
valToType 3 = Just CertificateType_RSA_Fixed_DH
valToType 4 = Just CertificateType_DSS_Fixed_DH
valToType 5 = Just CertificateType_RSA_Ephemeral_DH
valToType 6 = Just CertificateType_DSS_Ephemeral_DH
valToType 20 = Just CertificateType_fortezza_dms
valToType i = Just (CertificateType_Unknown i)
valToType 1 = Just CertificateType_RSA_Sign
valToType 2 = Just CertificateType_DSS_Sign
valToType 3 = Just CertificateType_RSA_Fixed_DH
valToType 4 = Just CertificateType_DSS_Fixed_DH
valToType 5 = Just CertificateType_RSA_Ephemeral_DH
valToType 6 = Just CertificateType_DSS_Ephemeral_DH
valToType 20 = Just CertificateType_fortezza_dms
valToType i = Just (CertificateType_Unknown i)
instance TypeValuable HashAlgorithm where
valOfType HashNone = 0
valOfType HashMD5 = 1
valOfType HashSHA1 = 2
valOfType HashSHA224 = 3
valOfType HashSHA256 = 4
valOfType HashSHA384 = 5
valOfType HashSHA512 = 6
valOfType (HashOther i) = i
valOfType HashNone = 0
valOfType HashMD5 = 1
valOfType HashSHA1 = 2
valOfType HashSHA224 = 3
valOfType HashSHA256 = 4
valOfType HashSHA384 = 5
valOfType HashSHA512 = 6
valOfType (HashOther i) = i
valToType 0 = Just HashNone
valToType 1 = Just HashMD5
valToType 2 = Just HashSHA1
valToType 3 = Just HashSHA224
valToType 4 = Just HashSHA256
valToType 5 = Just HashSHA384
valToType 6 = Just HashSHA512
valToType i = Just (HashOther i)
valToType 0 = Just HashNone
valToType 1 = Just HashMD5
valToType 2 = Just HashSHA1
valToType 3 = Just HashSHA224
valToType 4 = Just HashSHA256
valToType 5 = Just HashSHA384
valToType 6 = Just HashSHA512
valToType i = Just (HashOther i)
instance TypeValuable SignatureAlgorithm where
valOfType SignatureAnonymous = 0
valOfType SignatureRSA = 1
valOfType SignatureDSS = 2
valOfType SignatureECDSA = 3
valOfType (SignatureOther i) = i
valOfType SignatureAnonymous = 0
valOfType SignatureRSA = 1
valOfType SignatureDSS = 2
valOfType SignatureECDSA = 3
valOfType (SignatureOther i) = i
valToType 0 = Just SignatureAnonymous
valToType 1 = Just SignatureRSA
valToType 2 = Just SignatureDSS
valToType 3 = Just SignatureECDSA
valToType i = Just (SignatureOther i)
valToType 0 = Just SignatureAnonymous
valToType 1 = Just SignatureRSA
valToType 2 = Just SignatureDSS
valToType 3 = Just SignatureECDSA
valToType i = Just (SignatureOther i)

View file

@ -1,13 +1,13 @@
module Network.TLS.Util
( sub
, takelast
, partition3
, partition6
, fromJust
, and'
, (&&!)
, bytesEq
) where
( sub
, takelast
, partition3
, partition6
, fromJust
, and'
, (&&!)
, bytesEq
) where
import Data.List (foldl')
import Network.TLS.Struct (Bytes)
@ -15,32 +15,32 @@ import qualified Data.ByteString as B
sub :: Bytes -> Int -> Int -> Maybe Bytes
sub b offset len
| B.length b < offset + len = Nothing
| otherwise = Just $ B.take len $ snd $ B.splitAt offset b
| B.length b < offset + len = Nothing
| otherwise = Just $ B.take len $ snd $ B.splitAt offset b
takelast :: Int -> Bytes -> Maybe Bytes
takelast i b
| B.length b >= i = sub b (B.length b - i) i
| otherwise = Nothing
| B.length b >= i = sub b (B.length b - i) i
| otherwise = Nothing
partition3 :: Bytes -> (Int,Int,Int) -> Maybe (Bytes, Bytes, Bytes)
partition3 bytes (d1,d2,d3) = if B.length bytes /= s then Nothing else Just (p1,p2,p3)
where
s = sum [d1,d2,d3]
(p1, r1) = B.splitAt d1 bytes
(p2, r2) = B.splitAt d2 r1
(p3, _) = B.splitAt d3 r2
where
s = sum [d1,d2,d3]
(p1, r1) = B.splitAt d1 bytes
(p2, r2) = B.splitAt d2 r1
(p3, _) = B.splitAt d3 r2
partition6 :: Bytes -> (Int,Int,Int,Int,Int,Int) -> Maybe (Bytes, Bytes, Bytes, Bytes, Bytes, Bytes)
partition6 bytes (d1,d2,d3,d4,d5,d6) = if B.length bytes < s then Nothing else Just (p1,p2,p3,p4,p5,p6)
where
s = sum [d1,d2,d3,d4,d5,d6]
(p1, r1) = B.splitAt d1 bytes
(p2, r2) = B.splitAt d2 r1
(p3, r3) = B.splitAt d3 r2
(p4, r4) = B.splitAt d4 r3
(p5, r5) = B.splitAt d5 r4
(p6, _) = B.splitAt d6 r5
where
s = sum [d1,d2,d3,d4,d5,d6]
(p1, r1) = B.splitAt d1 bytes
(p2, r2) = B.splitAt d2 r1
(p3, r3) = B.splitAt d3 r2
(p4, r4) = B.splitAt d4 r3
(p5, r5) = B.splitAt d5 r4
(p6, _) = B.splitAt d6 r5
fromJust :: String -> Maybe a -> a
fromJust what Nothing = error ("fromJust " ++ what ++ ": Nothing") -- yuck

View file

@ -9,34 +9,34 @@
-- all multibytes values are written as big endian.
--
module Network.TLS.Wire
( Get
, runGet
, remaining
, getWord8
, getWords8
, getWord16
, getWords16
, getWord24
, getBytes
, getOpaque8
, getOpaque16
, getOpaque24
, processBytes
, isEmpty
, Put
, runPut
, putWord8
, putWords8
, putWord16
, putWords16
, putWord24
, putBytes
, putOpaque8
, putOpaque16
, putOpaque24
, encodeWord16
, encodeWord64
) where
( Get
, runGet
, remaining
, getWord8
, getWords8
, getWord16
, getWords16
, getWord24
, getBytes
, getOpaque8
, getOpaque16
, getOpaque24
, processBytes
, isEmpty
, Put
, runPut
, putWord8
, putWords8
, putWord16
, putWords16
, putWord24
, putBytes
, putOpaque8
, putOpaque16
, putOpaque24
, encodeWord16
, encodeWord64
) where
import Data.Serialize.Get hiding (runGet)
import qualified Data.Serialize.Get as G
@ -62,10 +62,10 @@ getWords16 = getWord16 >>= \lenb -> replicateM (fromIntegral lenb `div` 2) getWo
getWord24 :: Get Int
getWord24 = do
a <- fromIntegral <$> getWord8
b <- fromIntegral <$> getWord8
c <- fromIntegral <$> getWord8
return $ (a `shiftL` 16) .|. (b `shiftL` 8) .|. c
a <- fromIntegral <$> getWord8
b <- fromIntegral <$> getWord8
c <- fromIntegral <$> getWord8
return $ (a `shiftL` 16) .|. (b `shiftL` 8) .|. c
getOpaque8 :: Get Bytes
getOpaque8 = getWord8 >>= getBytes . fromIntegral
@ -81,23 +81,23 @@ processBytes i f = isolate i f
putWords8 :: [Word8] -> Put
putWords8 l = do
putWord8 $ fromIntegral (length l)
mapM_ putWord8 l
putWord8 $ fromIntegral (length l)
mapM_ putWord8 l
putWord16 :: Word16 -> Put
putWord16 = putWord16be
putWords16 :: [Word16] -> Put
putWords16 l = do
putWord16 $ 2 * (fromIntegral $ length l)
mapM_ putWord16 l
putWord16 $ 2 * (fromIntegral $ length l)
mapM_ putWord16 l
putWord24 :: Int -> Put
putWord24 i = do
let a = fromIntegral ((i `shiftR` 16) .&. 0xff)
let b = fromIntegral ((i `shiftR` 8) .&. 0xff)
let c = fromIntegral (i .&. 0xff)
mapM_ putWord8 [a,b,c]
let a = fromIntegral ((i `shiftR` 16) .&. 0xff)
let b = fromIntegral ((i `shiftR` 8) .&. 0xff)
let c = fromIntegral (i .&. 0xff)
mapM_ putWord8 [a,b,c]
putBytes :: Bytes -> Put
putBytes = putByteString