expand tabs.
This commit is contained in:
parent
3b4baf2f91
commit
9da6b9c8c8
20 changed files with 1662 additions and 1662 deletions
|
@ -7,9 +7,9 @@
|
|||
--
|
||||
|
||||
module Network.TLS.Cap
|
||||
( hasHelloExtensions
|
||||
, hasExplicitBlockIV
|
||||
) where
|
||||
( hasHelloExtensions
|
||||
, hasExplicitBlockIV
|
||||
) where
|
||||
|
||||
import Network.TLS.Struct
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 . (:[])
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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 ()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 })
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue