diff --git a/Network/TLS.hs b/Network/TLS.hs
--- a/Network/TLS.hs
+++ b/Network/TLS.hs
@@ -17,7 +17,7 @@
 
 	-- * Context object
 	, TLSCtx
-	, ctxHandle
+	, ctxConnection
 
 	-- * Creating a context
 	, client
@@ -34,10 +34,12 @@
 	-- * Crypto Key
 	, PrivateKey(..)
 	-- * Compressions & Predefined compressions
-	, Compression
+	, CompressionC(..)
+	, Compression(..)
 	, nullCompression
 	-- * Ciphers & Predefined ciphers
-	, Cipher
+	, Cipher(..)
+	, Bulk(..)
 	-- * Versions
 	, Version(..)
 	-- * Errors
@@ -46,6 +48,6 @@
 
 import Network.TLS.Struct (Version(..), TLSError(..))
 import Network.TLS.Crypto (PrivateKey(..))
-import Network.TLS.Cipher (Cipher(..))
-import Network.TLS.Compression (Compression(..), nullCompression)
+import Network.TLS.Cipher (Cipher(..), Bulk(..))
+import Network.TLS.Compression (CompressionC(..), Compression(..), nullCompression)
 import Network.TLS.Core
diff --git a/Network/TLS/Cipher.hs b/Network/TLS/Cipher.hs
--- a/Network/TLS/Cipher.hs
+++ b/Network/TLS/Cipher.hs
@@ -1,4 +1,5 @@
 {-# OPTIONS_HADDOCK hide #-}
+{-# LANGUAGE ExistentialQuantification #-}
 -- |
 -- Module      : Network.TLS.Cipher
 -- License     : BSD-style
@@ -7,9 +8,12 @@
 -- Portability : unknown
 --
 module Network.TLS.Cipher
-	( CipherTypeFunctions(..)
+	( BulkFunctions(..)
 	, CipherKeyExchangeType(..)
+	, Bulk(..)
+	, Hash(..)
 	, Cipher(..)
+	, cipherKeyBlockSize
 	, Key
 	, IV
 	, cipherExchangeNeedMoreData
@@ -24,13 +28,13 @@
 type Key = B.ByteString
 type IV = B.ByteString
 
-data CipherTypeFunctions =
-	  CipherNoneF -- special value for 0
-	| CipherBlockF (Key -> IV -> B.ByteString -> B.ByteString)
-	               (Key -> IV -> B.ByteString -> B.ByteString)
-	| CipherStreamF (Key -> IV)
-	                (IV -> B.ByteString -> (B.ByteString, IV))
-	                (IV -> B.ByteString -> (B.ByteString, IV))
+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))
 
 data CipherKeyExchangeType =
 	  CipherKeyExchange_RSA
@@ -45,20 +49,33 @@
 	| CipherKeyExchange_ECDHE_ECDSA
 	deriving (Show,Eq)
 
+data Bulk = Bulk
+	{ bulkName         :: String
+	, bulkKeySize      :: Int
+	, bulkIVSize       :: Int
+	, bulkBlockSize    :: Int
+	, bulkF            :: BulkFunctions
+	}
+
+data Hash = Hash
+	{ hashName         :: String
+	, hashSize         :: Int
+	, hashF            :: B.ByteString -> B.ByteString
+	}
+
 -- | Cipher algorithm
 data Cipher = Cipher
 	{ cipherID           :: Word16
 	, cipherName         :: String
-	, cipherDigestSize   :: Word8
-	, cipherKeySize      :: Word8
-	, cipherIVSize       :: Word8
-	, cipherKeyBlockSize :: Word8
-	, cipherPaddingSize  :: Word8
+	, cipherHash         :: Hash
+	, cipherBulk         :: Bulk
 	, cipherKeyExchange  :: CipherKeyExchangeType
-	, cipherMACHash      :: B.ByteString -> B.ByteString
-	, cipherF            :: CipherTypeFunctions
 	, cipherMinVer       :: Maybe Version
 	}
+
+cipherKeyBlockSize :: Cipher -> Int
+cipherKeyBlockSize cipher = 2 * (hashSize (cipherHash cipher) + bulkIVSize bulk + bulkKeySize bulk)
+	where bulk = cipherBulk cipher
 
 instance Show Cipher where
 	show c = cipherName c
diff --git a/Network/TLS/Compression.hs b/Network/TLS/Compression.hs
--- a/Network/TLS/Compression.hs
+++ b/Network/TLS/Compression.hs
@@ -1,4 +1,5 @@
 {-# OPTIONS_HADDOCK hide #-}
+{-# LANGUAGE ExistentialQuantification #-}
 -- |
 -- Module      : Network.TLS.Compression
 -- License     : BSD-style
@@ -7,22 +8,62 @@
 -- Portability : unknown
 --
 module Network.TLS.Compression
-	( Compression(..)
+	( CompressionC(..)
+	, Compression(..)
 	, nullCompression
+
+	-- * member redefined for the class abstraction
+	, compressionID
+	, compressionDeflate
+	, compressionInflate
+
+	-- * helper
+	, compressionIntersectID
 	) where
 
 import Data.Word
 import Data.ByteString (ByteString)
+import Control.Arrow (first)
 
--- | Compression algorithm
-data Compression = Compression
-	{ compressionID :: Word8
-	, compressionFct :: (ByteString -> ByteString)
-	}
+-- | 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)
 
+-- | every compression need to be wrapped in this, to fit in structure
+data Compression = forall a . CompressionC a => Compression a
+
+-- | return the associated ID for this algorithm
+compressionID :: Compression -> Word8
+compressionID (Compression c) = compressionCID c
+
+-- | deflate (compress) a bytestring using a compression context and return the result
+-- along with the new compression context.
+compressionDeflate :: ByteString -> Compression -> (Compression, ByteString)
+compressionDeflate bytes (Compression c) = first Compression $ compressionCDeflate c bytes
+
+-- | inflate (decompress) a bytestring using a compression context and return the result
+-- along the new compression context.
+compressionInflate :: ByteString -> Compression -> (Compression, ByteString)
+compressionInflate bytes (Compression c) = first Compression $ compressionCInflate c bytes
+
 instance Show Compression where
 	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
+-- compression.
+compressionIntersectID :: [Compression] -> [Word8] -> [Compression]
+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)
+
 -- | default null compression
 nullCompression :: Compression
-nullCompression = Compression { compressionID = 0, compressionFct = id }
+nullCompression = Compression NullCompression
diff --git a/Network/TLS/Core.hs b/Network/TLS/Core.hs
--- a/Network/TLS/Core.hs
+++ b/Network/TLS/Core.hs
@@ -18,7 +18,7 @@
 
 	-- * Context object
 	, TLSCtx
-	, ctxHandle
+	, ctxConnection
 	, ctxEOF
 
 	-- * Internal packet sending and receiving
@@ -27,7 +27,9 @@
 
 	-- * Creating a context
 	, client
+	, clientWith
 	, server
+	, serverWith
 
 	-- * Initialisation and Termination of context
 	, bye
@@ -39,6 +41,7 @@
 	) where
 
 import Network.TLS.Struct
+import Network.TLS.Record
 import Network.TLS.Cipher
 import Network.TLS.Compression
 import Network.TLS.Crypto
@@ -93,7 +96,7 @@
 	, pUseSecureRenegotiation :: Bool           -- notify that we want to use secure renegotation
 	, pCertificates      :: [(X509, Maybe PrivateKey)] -- ^ the cert chain for this context with the associated keys if any.
 	, pLogging           :: TLSLogging          -- ^ callback for logging
-	, onCertificatesRecv :: ([X509] -> IO TLSCertificateUsage) -- ^ callback to verify received cert chain.
+	, onCertificatesRecv :: [X509] -> IO TLSCertificateUsage -- ^ callback to verify received cert chain.
 	}
 
 defaultLogging :: TLSLogging
@@ -107,7 +110,7 @@
 defaultParams :: TLSParams
 defaultParams = TLSParams
 	{ pConnectVersion         = TLS10
-	, pAllowedVersions        = [TLS10,TLS11]
+	, pAllowedVersions        = [TLS10,TLS11,TLS12]
 	, pCiphers                = []
 	, pCompressions           = [nullCompression]
 	, pWantClientCert         = False
@@ -128,35 +131,54 @@
 		]) ++ " }"
 
 -- | A TLS Context is a handle augmented by tls specific state and parameters
-data TLSCtx = TLSCtx
-	{ ctxHandle :: Handle        -- ^ return the handle associated with this context
-	, ctxParams :: TLSParams
-	, ctxState  :: MVar TLSState
-	, ctxEOF_   :: IORef Bool    -- ^ is the handle as EOFed or not.
+data TLSCtx a = TLSCtx
+	{ ctxConnection      :: a             -- ^ return the connection object associated with this context
+	, ctxParams          :: TLSParams
+	, ctxState           :: MVar TLSState
+	, ctxEOF_            :: IORef Bool    -- ^ is the handle has EOFed or not.
+	, ctxConnectionFlush :: IO ()
+	, ctxConnectionSend  :: Bytes -> IO ()
+	, ctxConnectionRecv  :: Int -> IO Bytes
 	}
 
-ctxEOF :: MonadIO m => TLSCtx -> m Bool
+connectionFlush :: TLSCtx c -> IO ()
+connectionFlush c = (ctxConnectionFlush c)
+
+connectionSend :: TLSCtx c -> Bytes -> IO ()
+connectionSend c b = (ctxConnectionSend c) b
+
+connectionRecv :: TLSCtx c -> Int -> IO Bytes
+connectionRecv c sz = (ctxConnectionRecv c) sz
+
+ctxEOF :: MonadIO m => TLSCtx a -> m Bool
 ctxEOF ctx = liftIO (readIORef $ ctxEOF_ ctx)
 
 throwCore :: (MonadIO m, Exception e) => e -> m a
 throwCore = liftIO . throwIO
 
-newCtx :: Handle -> TLSParams -> TLSState -> IO TLSCtx
-newCtx handle params st = do
-	hSetBuffering handle NoBuffering
+newCtxWith :: c -> IO () -> (Bytes -> IO ()) -> (Int -> IO Bytes) -> TLSParams -> TLSState -> IO (TLSCtx c)
+newCtxWith c flushF sendF recvF params st = do
 	stvar <- newMVar st
 	eof   <- newIORef False
 	return $ TLSCtx
-		{ ctxHandle = handle
-		, ctxParams = params
-		, ctxState  = stvar
-		, ctxEOF_   = eof
+		{ ctxConnection = c
+		, ctxParams     = params
+		, ctxState      = stvar
+		, ctxEOF_       = eof
+		, ctxConnectionFlush = flushF
+		, ctxConnectionSend  = sendF
+		, ctxConnectionRecv  = recvF
 		}
 
-ctxLogging :: TLSCtx -> TLSLogging
+newCtx :: Handle -> TLSParams -> TLSState -> IO (TLSCtx Handle)
+newCtx handle params st = do
+	hSetBuffering handle NoBuffering
+	newCtxWith handle (hFlush handle) (B.hPut handle) (B.hGet handle) params st
+
+ctxLogging :: TLSCtx a -> TLSLogging
 ctxLogging = pLogging . ctxParams
 
-usingState :: MonadIO m => TLSCtx -> TLSSt a -> m (Either TLSError a)
+usingState :: MonadIO m => TLSCtx c -> TLSSt a -> m (Either TLSError a)
 usingState ctx f = liftIO (takeMVar mvar) >>= \st -> liftIO $ onException (execAndStore st) (putMVar mvar st)
 	where
 		mvar = ctxState ctx
@@ -165,17 +187,17 @@
 			putMVar mvar newst
 			return a
 
-usingState_ :: MonadIO m => TLSCtx -> TLSSt a -> m a
+usingState_ :: MonadIO m => TLSCtx c -> TLSSt a -> m a
 usingState_ ctx f = do
 	ret <- usingState ctx f
 	case ret of
 		Left err -> throwCore err
 		Right r  -> return r
 
-getStateRNG :: MonadIO m => TLSCtx -> Int -> m Bytes
+getStateRNG :: MonadIO m => TLSCtx c -> Int -> m Bytes
 getStateRNG ctx n = usingState_ ctx (genTLSRandom n)
 
-whileStatus :: MonadIO m => TLSCtx -> (TLSStatus -> Bool) -> m a -> m ()
+whileStatus :: MonadIO m => TLSCtx c -> (TLSStatus -> Bool) -> m a -> m ()
 whileStatus ctx p a = do
 	b <- usingState_ ctx (p . stStatus <$> get)
 	when b (a >> whileStatus ctx p a)
@@ -184,12 +206,12 @@
 errorToAlert (Error_Protocol (_, _, ad)) = Alert [(AlertLevel_Fatal, ad)]
 errorToAlert _                           = Alert [(AlertLevel_Fatal, InternalError)]
 
-setEOF :: MonadIO m => TLSCtx -> m ()
+setEOF :: MonadIO m => TLSCtx c -> m ()
 setEOF ctx = liftIO $ writeIORef (ctxEOF_ ctx) True
 
-readExact :: MonadIO m => TLSCtx -> Int -> m Bytes
+readExact :: MonadIO m => TLSCtx c -> Int -> m Bytes
 readExact ctx sz = do
-	hdrbs <- liftIO $ B.hGet (ctxHandle ctx) sz
+	hdrbs <- liftIO $ connectionRecv ctx sz
 	when (B.length hdrbs < sz) $ do
 		setEOF ctx
 		if B.null hdrbs
@@ -200,7 +222,7 @@
 -- | receive one packet from the context that contains 1 or
 -- many messages (many only in case of handshake). if will returns a
 -- TLSError if the packet is unexpected or malformed
-recvPacket :: MonadIO m => TLSCtx -> m (Either TLSError Packet)
+recvPacket :: MonadIO m => TLSCtx c -> m (Either TLSError Packet)
 recvPacket ctx = do
 	hdrbs <- readExact ctx 5
 	case decodeHeader hdrbs of
@@ -212,13 +234,13 @@
 	where recvLength header readlen = do
 		content <- readExact ctx (fromIntegral readlen)
 		liftIO $ (loggingIORecv $ ctxLogging ctx) header content
-		pkt <- usingState ctx $ readPacket header (EncryptedData content)
+		pkt <- usingState ctx $ readPacket $ rawToRecord header (fragmentCiphertext content)
 		case pkt of
 			Right p -> liftIO $ (loggingPacketRecv $ ctxLogging ctx) $ show p
 			_       -> return ()
 		return pkt
 
-recvPacketSuccess :: MonadIO m => TLSCtx -> m ()
+recvPacketSuccess :: MonadIO m => TLSCtx c -> m ()
 recvPacketSuccess ctx = do
 	pkt <- recvPacket ctx
 	case pkt of
@@ -226,22 +248,34 @@
 		Right _  -> return ()
 
 -- | Send one packet to the context
-sendPacket :: MonadIO m => TLSCtx -> Packet -> m ()
+sendPacket :: MonadIO m => TLSCtx c -> Packet -> m ()
 sendPacket ctx pkt = do
 	liftIO $ (loggingPacketSent $ ctxLogging ctx) (show pkt)
 	dataToSend <- usingState_ ctx $ writePacket pkt
 	liftIO $ (loggingIOSent $ ctxLogging ctx) dataToSend
-	liftIO $ B.hPut (ctxHandle ctx) dataToSend
+	liftIO $ connectionSend ctx dataToSend
 
+-- | Create a new Client context with a configuration, a RNG, a generic connection and the connection operation.
+clientWith :: (MonadIO m, CryptoRandomGen g) => TLSParams -> g -> c -> IO () -> (Bytes -> IO ()) -> (Int -> IO Bytes) -> m (TLSCtx c)
+clientWith params rng connection flushF sendF recvF =
+	liftIO $ newCtxWith connection flushF sendF recvF params st
+	where st = (newTLSState rng) { stClientContext = True }
+
 -- | Create a new Client context with a configuration, a RNG, and a Handle.
 -- It reconfigures the handle buffermode to noBuffering
-client :: (MonadIO m, CryptoRandomGen g) => TLSParams -> g -> Handle -> m TLSCtx
+client :: (MonadIO m, CryptoRandomGen g) => TLSParams -> g -> Handle -> m (TLSCtx Handle)
 client params rng handle = liftIO $ newCtx handle params st
 	where st = (newTLSState rng) { stClientContext = True }
 
+-- | Create a new Server context with a configuration, a RNG, a generic connection and the connection operation.
+serverWith :: (MonadIO m, CryptoRandomGen g) => TLSParams -> g -> c -> IO () -> (Bytes -> IO ()) -> (Int -> IO Bytes) -> m (TLSCtx c)
+serverWith params rng connection flushF sendF recvF =
+	liftIO $ newCtxWith connection flushF sendF recvF params st
+	where st = (newTLSState rng) { stClientContext = False }
+
 -- | Create a new Server context with a configuration, a RNG, and a Handle.
 -- It reconfigures the handle buffermode to noBuffering
-server :: (MonadIO m, CryptoRandomGen g) => TLSParams -> g -> Handle -> m TLSCtx
+server :: (MonadIO m, CryptoRandomGen g) => TLSParams -> g -> Handle -> m (TLSCtx Handle)
 server params rng handle = liftIO $ newCtx handle params st
 	where st = (newTLSState rng) { stClientContext = False }
 
@@ -250,12 +284,12 @@
 -- the session might not be resumable (for version < TLS1.2).
 --
 -- this doesn't actually close the handle
-bye :: MonadIO m => TLSCtx -> m ()
+bye :: MonadIO m => TLSCtx c -> m ()
 bye ctx = sendPacket ctx $ Alert [(AlertLevel_Warning, CloseNotify)]
 
 -- client part of handshake. send a bunch of handshake of client
 -- values intertwined with response from the server.
-handshakeClient :: MonadIO m => TLSCtx -> m ()
+handshakeClient :: MonadIO m => TLSCtx c -> m ()
 handshakeClient ctx = do
 	-- Send ClientHello
 	crand <- getStateRNG ctx 32 >>= return . ClientRandom
@@ -283,7 +317,7 @@
 	{- FIXME not implemented yet -}
 
 	sendPacket ctx ChangeCipherSpec
-	liftIO $ hFlush $ ctxHandle ctx
+	liftIO $ connectionFlush ctx
 
 	-- Send Finished
 	cf <- usingState_ ctx $ getHandshakeDigest True
@@ -342,7 +376,7 @@
 		certificateRejected (CertificateRejectOther s) =
 			throwCore $ Error_Protocol ("certificate rejected: " ++ s, True, CertificateUnknown)
 
-handshakeServerWith :: MonadIO m => TLSCtx -> Handshake -> m ()
+handshakeServerWith :: MonadIO m => TLSCtx c -> Handshake -> m ()
 handshakeServerWith ctx (ClientHello ver _ _ ciphers compressions _) = do
 	-- Handle Client hello
 	when (ver == SSL2) $ throwCore $ Error_Protocol ("ssl2 is not supported", True, ProtocolVersion)
@@ -350,17 +384,17 @@
 		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 (commonCompressions == []) $
+	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 = Just usedCompression
+		{ stVersion     = ver
+		, stCipher      = Just usedCipher
+		, stCompression = usedCompression
 		})
 
 	-- send Server Data until ServerHelloDone
 	handshakeSendServerData
-	liftIO $ hFlush $ ctxHandle ctx
+	liftIO $ connectionFlush ctx
 
 	-- Receive client info until client Finished.
 	whileStatus ctx (/= (StatusHandshake HsStatusClientFinished)) (recvPacketSuccess ctx)
@@ -371,14 +405,14 @@
 	cf <- usingState_ ctx $ getHandshakeDigest False
 	sendPacket ctx (Handshake [Finished cf])
 
-	liftIO $ hFlush $ ctxHandle ctx
+	liftIO $ connectionFlush ctx
 	return ()
 	where
 		params             = ctxParams ctx
 		commonCiphers      = intersect ciphers (map cipherID $ pCiphers params)
 		usedCipher         = fromJust $ find (\c -> cipherID c == head commonCiphers) (pCiphers params)
-		commonCompressions = intersect compressions (map compressionID $ pCompressions params)
-		usedCompression    = fromJust $ find (\c -> compressionID c == head commonCompressions) (pCompressions params)
+		commonCompressions = compressionIntersectID (pCompressions params) compressions
+		usedCompression    = head commonCompressions
 		srvCerts           = map fst $ pCertificates params
 		privKeys           = map snd $ pCertificates params
 		needKeyXchg        = cipherExchangeNeedMoreData $ cipherKeyExchange usedCipher
@@ -423,7 +457,7 @@
 handshakeServerWith _ _ = fail "unexpected handshake type received. expecting client hello"
 
 -- after receiving a client hello, we need to redo a handshake -}
-handshakeServer :: MonadIO m => TLSCtx -> m ()
+handshakeServer :: MonadIO m => TLSCtx c -> m ()
 handshakeServer ctx = do
 	pkts <- recvPacket ctx
 	case pkts of
@@ -432,7 +466,7 @@
 
 -- | Handshake for a new TLS connection
 -- This is to be called at the beginning of a connection, and during renegociation
-handshake :: MonadIO m => TLSCtx -> m Bool
+handshake :: MonadIO m => TLSCtx c -> m Bool
 handshake ctx = do
 	cc <- usingState_ ctx (stClientContext <$> get)
 	liftIO $ handleException $ if cc then handshakeClient ctx else handshakeServer ctx
@@ -444,10 +478,10 @@
 
 -- | sendData sends a bunch of data.
 -- It will automatically chunk data to acceptable packet size
-sendData :: MonadIO m => TLSCtx -> L.ByteString -> m ()
+sendData :: MonadIO m => TLSCtx c -> L.ByteString -> m ()
 sendData ctx dataToSend = do
 	eofed <- ctxEOF ctx
-	when eofed $ liftIO $ throwIO $ mkIOError eofErrorType "sendData" (Just (ctxHandle ctx)) Nothing
+	when eofed $ liftIO $ throwIO $ mkIOError eofErrorType "sendData" Nothing Nothing
 	mapM_ sendDataChunk (L.toChunks dataToSend)
 		where sendDataChunk d = if B.length d > 16384
 			then do
@@ -459,10 +493,10 @@
 
 -- | recvData get data out of Data packet, and automatically renegociate if
 -- a Handshake ClientHello is received
-recvData :: MonadIO m => TLSCtx -> m L.ByteString
+recvData :: MonadIO m => TLSCtx c -> m L.ByteString
 recvData ctx = do
 	eofed <- ctxEOF ctx
-	when eofed $ liftIO $ throwIO $ mkIOError eofErrorType "recvData" (Just (ctxHandle ctx)) Nothing
+	when eofed $ liftIO $ throwIO $ mkIOError eofErrorType "recvData" Nothing Nothing
 	pkt   <- recvPacket ctx
 	case pkt of
 		-- on server context receiving a client hello == renegociation
@@ -478,5 +512,5 @@
 			setEOF ctx
 			return L.empty
 		Right (AppData x) -> return $ L.fromChunks [x]
-		Right p           -> error ("error unexpected packet: p" ++ show p)
+		Right p           -> error ("error unexpected packet: " ++ show p)
 		Left err          -> error ("error received: " ++ show err)
diff --git a/Network/TLS/Crypto.hs b/Network/TLS/Crypto.hs
--- a/Network/TLS/Crypto.hs
+++ b/Network/TLS/Crypto.hs
@@ -1,23 +1,15 @@
 {-# OPTIONS_HADDOCK hide #-}
+{-# LANGUAGE ExistentialQuantification #-}
 module Network.TLS.Crypto
-	( HashType(..)
-	, HashCtx
-
-	-- * incremental interface with algorithm type wrapping for genericity
-	, initHash
-	, updateHash
-	, finalizeHash
+	( HashCtx(..)
+	, hashInit
+	, hashUpdate
+	, hashUpdateSSL
+	, hashFinal
 
-	-- * single pass lazy bytestring interface for each algorithm
-	, hashMD5
-	, hashSHA1
-	-- * incremental interface for each algorithm
-	, initMD5
-	, updateMD5
-	, finalizeMD5
-	, initSHA1
-	, updateSHA1
-	, finalizeSHA1
+	-- * constructor
+	, hashMD5SHA1
+	, hashSHA256
 
 	-- * key exchange generic interface
 	, PublicKey(..)
@@ -27,6 +19,7 @@
 	, KxError(..)
 	) where
 
+import qualified Crypto.Hash.SHA256 as SHA256
 import qualified Crypto.Hash.SHA1 as SHA1
 import qualified Crypto.Hash.MD5 as MD5
 import qualified Data.ByteString as B
@@ -47,61 +40,58 @@
 data KxError = RSAError RSA.Error
 	deriving (Show)
 
-data HashCtx =
-	  SHA1 !SHA1.Ctx
-	| MD5 !MD5.Ctx
-
 data KeyXchg =
 	  KxRSA RSA.PublicKey RSA.PrivateKey
 	deriving (Show)
 
-instance Show HashCtx where
-	show (SHA1 _) = "sha1"
-	show (MD5 _) = "md5"
-
-data HashType = HashTypeSHA1 | HashTypeMD5
-
-{- MD5 -}
-
-initMD5 :: MD5.Ctx
-initMD5 = MD5.init
-
-updateMD5 :: MD5.Ctx -> ByteString -> MD5.Ctx
-updateMD5 = MD5.update
+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
 
-finalizeMD5 :: MD5.Ctx -> ByteString
-finalizeMD5 = MD5.finalize
+data HashCtx = forall h . HashCtxC h => HashCtx h
 
-hashMD5 :: ByteString -> ByteString
-hashMD5 = MD5.hash
+instance Show HashCtx where
+	show (HashCtx c) = hashCName c
 
-{- SHA1 -}
+{- MD5 & SHA1 joined -}
+data HashMD5SHA1 = HashMD5SHA1 SHA1.Ctx MD5.Ctx
 
-initSHA1 :: SHA1.Ctx
-initSHA1 = SHA1.init
+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]
 
-updateSHA1 :: SHA1.Ctx -> ByteString -> SHA1.Ctx
-updateSHA1 = SHA1.update
+data HashSHA256 = HashSHA256 SHA256.Ctx
 
-finalizeSHA1 :: SHA1.Ctx -> ByteString
-finalizeSHA1 = SHA1.finalize
+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
 
-hashSHA1 :: ByteString -> ByteString
-hashSHA1 = SHA1.hash
+-- functions to use the hidden class.
+hashInit :: HashCtx -> HashCtx
+hashInit   (HashCtx h)   = HashCtx $ hashCInit h
 
-{- generic Hashing -}
+hashUpdate :: HashCtx -> B.ByteString -> HashCtx
+hashUpdate (HashCtx h) b = HashCtx $ hashCUpdate h b
 
-initHash :: HashType -> HashCtx
-initHash HashTypeSHA1 = SHA1 (initSHA1)
-initHash HashTypeMD5  = MD5 (initMD5)
+hashUpdateSSL :: HashCtx -> (B.ByteString,B.ByteString) -> HashCtx
+hashUpdateSSL (HashCtx h) bs = HashCtx $ hashCUpdateSSL h bs
 
-updateHash :: HashCtx -> B.ByteString -> HashCtx
-updateHash (SHA1 ctx) = SHA1 . updateSHA1 ctx
-updateHash (MD5 ctx)  = MD5 . updateMD5 ctx
+hashFinal :: HashCtx -> B.ByteString
+hashFinal  (HashCtx h)   = hashCFinal h
 
-finalizeHash :: HashCtx -> B.ByteString
-finalizeHash (SHA1 ctx) = finalizeSHA1 ctx
-finalizeHash (MD5 ctx)  = finalizeMD5 ctx
+-- real hash constructors
+hashMD5SHA1, hashSHA256 :: HashCtx
+hashMD5SHA1 = HashCtx (HashMD5SHA1 SHA1.init MD5.init)
+hashSHA256  = HashCtx (HashSHA256 SHA256.init)
 
 {- key exchange methods encrypt and decrypt for each supported algorithm -}
 generalizeRSAError :: Either RSA.Error a -> Either KxError a
diff --git a/Network/TLS/MAC.hs b/Network/TLS/MAC.hs
--- a/Network/TLS/MAC.hs
+++ b/Network/TLS/MAC.hs
@@ -6,6 +6,7 @@
 	, hmac
 	, prf_MD5
 	, prf_SHA1
+	, prf_SHA256
 	, prf_MD5SHA1
 	) where
 
@@ -16,14 +17,16 @@
 import Data.ByteString (ByteString)
 import Data.Bits (xor)
 
-macSSL :: (ByteString -> ByteString) -> ByteString -> ByteString -> ByteString
+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
 
-hmac :: (ByteString -> ByteString) -> Int -> ByteString -> ByteString -> ByteString
+hmac :: (ByteString -> ByteString) -> Int -> HMAC
 hmac f bl secret msg =
 	f $! B.append opad (f $! B.append ipad msg)
 	where
@@ -35,16 +38,16 @@
 			kt  = if B.length secret > fromIntegral bl then f secret else secret
 			pad = B.replicate (fromIntegral bl - B.length kt) 0
 
-hmacMD5 :: ByteString -> ByteString -> ByteString
+hmacMD5 :: HMAC
 hmacMD5 secret msg = hmac MD5.hash 64 secret msg
 
-hmacSHA1 :: ByteString -> ByteString -> ByteString
+hmacSHA1 :: HMAC
 hmacSHA1 secret msg = hmac SHA1.hash 64 secret msg
 
-hmacSHA256 :: ByteString -> ByteString -> ByteString
+hmacSHA256 :: HMAC
 hmacSHA256 secret msg = hmac SHA256.hash 64 secret msg
 
-hmacIter :: (ByteString -> ByteString -> ByteString) -> ByteString -> ByteString -> ByteString -> Int -> [ByteString]
+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
@@ -66,3 +69,6 @@
 		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
diff --git a/Network/TLS/Packet.hs b/Network/TLS/Packet.hs
--- a/Network/TLS/Packet.hs
+++ b/Network/TLS/Packet.hs
@@ -63,6 +63,9 @@
 import qualified Data.ByteString.Char8 as BC
 import qualified Data.ByteString.Lazy as L
 
+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
@@ -105,11 +108,7 @@
  - decode and encode headers
  -}
 decodeHeader :: ByteString -> Either TLSError Header
-decodeHeader = runGetErr "header" $ do
-	ty  <- getHeaderType
-	v   <- getVersion
-	len <- getWord16
-	return $ Header ty v len
+decodeHeader = runGetErr "header" $ liftM3 Header getHeaderType getVersion getWord16
 
 encodeHeader :: Header -> ByteString
 encodeHeader (Header pt ver len) = runPut (putHeaderType pt >> putVersion ver >> putWord16 len)
@@ -141,7 +140,7 @@
 
 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)
@@ -207,8 +206,7 @@
 
 decodeCertificates :: Get Handshake
 decodeCertificates = do
-	certslen <- getWord24
-	certs <- getCerts certslen >>= return . map (decodeCertificate . L.fromChunks . (:[]))
+	certs <- getWord24 >>= getCerts >>= return . map (decodeCertificate . L.fromChunks . (:[]))
 	let (l, r) = partitionEithers certs
 	if length l > 0
 		then fail ("error certificate parsing: " ++ show l)
@@ -219,14 +217,16 @@
 	opaque <- remaining >>= getBytes
 	return $ Finished $ opaque
 
-getSignatureHashAlgorithm :: Int -> Get [ (HashAlgorithm, SignatureAlgorithm) ]
-getSignatureHashAlgorithm 0   = return []
-getSignatureHashAlgorithm len = do
+getSignatureHashAlgorithm :: Get (HashAlgorithm, SignatureAlgorithm)
+getSignatureHashAlgorithm = do
 	h <- fromJust . valToType <$> getWord8
 	s <- fromJust . valToType <$> getWord8
-	xs <- getSignatureHashAlgorithm (len - 2)
-	return ((h, s) : xs)
+	return (h,s)
 
+getSignatureHashAlgorithms :: Int -> Get [ (HashAlgorithm, SignatureAlgorithm) ]
+getSignatureHashAlgorithms 0   = return []
+getSignatureHashAlgorithms len = liftM2 (:) getSignatureHashAlgorithm (getSignatureHashAlgorithms (len-2))
+
 decodeCertRequest :: CurrentParams -> Get Handshake
 decodeCertRequest cp = do
 	certTypes <- map (fromJust . valToType . fromIntegral) <$> getWords8
@@ -234,7 +234,7 @@
 	sigHashAlgs <- if cParamsVersion cp >= TLS12
 		then do
 			sighashlen <- getWord16
-			Just <$> getSignatureHashAlgorithm (fromIntegral sighashlen)
+			Just <$> getSignatureHashAlgorithms (fromIntegral sighashlen)
 		else return Nothing
 	dNameLen <- getWord16
 	when (cParamsVersion cp < TLS12 && dNameLen < 3) $ fail "certrequest distinguishname not of the correct size"
@@ -372,10 +372,8 @@
 		len -> Session . Just <$> getBytes len
 
 putSession :: Session -> Put
-putSession (Session session) =
-	case session of
-		Nothing -> putWord8 0
-		Just s  -> putWord8 (fromIntegral $ B.length s) >> putBytes s
+putSession (Session Nothing)  = putWord8 0
+putSession (Session (Just s)) = putWord8 (fromIntegral $ B.length s) >> putBytes s
 
 getCerts :: Int -> Get [Bytes]
 getCerts 0   = return []
@@ -446,61 +444,72 @@
 {-
  - generate things for packet content
  -}
-generateMasterSecret_TLS, generateMasterSecret_SSL :: Bytes -> ClientRandom -> ServerRandom -> Bytes
-generateMasterSecret_TLS premasterSecret (ClientRandom c) (ServerRandom s) =
-	prf_MD5SHA1 premasterSecret seed 48
-	where
-		seed = B.concat [ "master secret", c, s ]
+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 = hashMD5 $ B.concat [ premasterSecret, computeSHA1 label ]
-		computeSHA1 label = hashSHA1 $ B.concat [ label, premasterSecret, c, s ]
+		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 ]
+
 generateMasterSecret :: Version -> Bytes -> ClientRandom -> ServerRandom -> Bytes
-generateMasterSecret ver =
-	if ver < TLS10 then generateMasterSecret_SSL else generateMasterSecret_TLS
+generateMasterSecret SSL2  = generateMasterSecret_SSL
+generateMasterSecret SSL3  = generateMasterSecret_SSL
+generateMasterSecret TLS10 = generateMasterSecret_TLS prf_MD5SHA1
+generateMasterSecret TLS11 = generateMasterSecret_TLS prf_MD5SHA1
+generateMasterSecret TLS12 = generateMasterSecret_TLS prf_SHA256
 
-generateKeyBlock_TLS :: ClientRandom -> ServerRandom -> Bytes -> Int -> Bytes
-generateKeyBlock_TLS (ClientRandom c) (ServerRandom s) mastersecret kbsize =
-	prf_MD5SHA1 mastersecret seed kbsize
-	where
-		seed = B.concat [ "key expansion", s, c ]
+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 ]
 
 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 = hashMD5 $ B.concat [ mastersecret, computeSHA1 label ]
-		computeSHA1 label = hashSHA1 $ B.concat [ label, mastersecret, s, c ]
+		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 ver =
-	if ver < TLS10 then generateKeyBlock_SSL else generateKeyBlock_TLS
+generateKeyBlock SSL2  = generateKeyBlock_SSL
+generateKeyBlock SSL3  = generateKeyBlock_SSL
+generateKeyBlock TLS10 = generateKeyBlock_TLS prf_MD5SHA1
+generateKeyBlock TLS11 = generateKeyBlock_TLS prf_MD5SHA1
+generateKeyBlock TLS12 = generateKeyBlock_TLS prf_SHA256
 
-generateFinished_TLS :: Bytes -> Bytes -> HashCtx -> HashCtx -> Bytes
-generateFinished_TLS label mastersecret md5ctx sha1ctx =
-	prf_MD5SHA1 mastersecret seed 12
+generateFinished_TLS :: PRF -> Bytes -> Bytes -> HashCtx -> Bytes
+generateFinished_TLS prf label mastersecret hashctx = prf mastersecret seed 12
 	where
-		seed = B.concat [ label, finalizeHash md5ctx, finalizeHash sha1ctx ]
+		seed = B.concat [ label, hashFinal hashctx ]
 
-generateFinished_SSL :: Bytes -> Bytes -> HashCtx -> HashCtx -> Bytes
-generateFinished_SSL sender mastersecret md5ctx sha1ctx =
-	B.concat [md5hash, sha1hash]
+generateFinished_SSL :: Bytes -> Bytes -> HashCtx -> Bytes
+generateFinished_SSL sender mastersecret hashctx = B.concat [md5hash, sha1hash]
 	where
-		md5hash  = hashMD5 $ B.concat [ mastersecret, pad2, md5left ]
-		sha1hash = hashSHA1 $ B.concat [ mastersecret, B.take 40 pad2, sha1left ]
-		md5left  = finalizeHash $ foldl updateHash md5ctx [ sender, mastersecret, pad1 ]
-		sha1left = finalizeHash $ foldl updateHash sha1ctx [ sender, mastersecret, B.take 40 pad1 ]
+		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
 
-generateClientFinished :: Version -> Bytes -> HashCtx -> HashCtx -> Bytes
-generateClientFinished ver =
-	if ver < TLS10 then generateFinished_SSL "CLNT" else generateFinished_TLS "client finished"
+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"
 
-generateServerFinished :: Version -> Bytes -> HashCtx -> HashCtx -> Bytes
-generateServerFinished ver =
-	if ver < TLS10 then generateFinished_SSL "SRVR" else generateFinished_TLS "server 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"
diff --git a/Network/TLS/Receiving.hs b/Network/TLS/Receiving.hs
--- a/Network/TLS/Receiving.hs
+++ b/Network/TLS/Receiving.hs
@@ -23,9 +23,11 @@
 import Network.TLS.Util
 import Network.TLS.Cap
 import Network.TLS.Struct
+import Network.TLS.Record
 import Network.TLS.Packet
 import Network.TLS.State
 import Network.TLS.Cipher
+import Network.TLS.Compression
 import Network.TLS.Crypto
 import Data.Certificate.X509
 
@@ -35,11 +37,11 @@
 returnEither (Left err) = throwError err
 returnEither (Right a)  = return a
 
-readPacket :: Header -> EncryptedData -> TLSSt Packet
-readPacket hdr content = checkState hdr >> decryptContent hdr content >>= processPacket hdr
+readPacket :: Record Ciphertext -> TLSSt Packet
+readPacket record = checkState record >> decryptContent record >>= uncompressContent >>= processPacket
 
-checkState :: Header -> TLSSt ()
-checkState (Header pt _ _) =
+checkState :: Record a -> TLSSt ()
+checkState (Record pt _ _) =
 		stStatus <$> get >>= \status -> unless (allowed pt status) $ throwError (err status)
 	where
 		err st = Error_Protocol ("unexpected message received: status=" ++ show st, True, UnexpectedMessage)
@@ -54,23 +56,23 @@
 		allowed ProtocolType_ChangeCipherSpec (StatusHandshake HsStatusClientCertificateVerify) = True
 		allowed _ _ = False
 
-processPacket :: Header -> Bytes -> TLSSt Packet
+processPacket :: Record Plaintext -> TLSSt Packet
 
-processPacket (Header ProtocolType_AppData _ _) content = return $ AppData content
+processPacket (Record ProtocolType_AppData _ fragment) = return $ AppData $ fragmentGetBytes fragment
 
-processPacket (Header ProtocolType_Alert _ _) content = return . Alert =<< returnEither (decodeAlerts content)
+processPacket (Record ProtocolType_Alert _ fragment) = return . Alert =<< returnEither (decodeAlerts $ fragmentGetBytes fragment)
 
-processPacket (Header ProtocolType_ChangeCipherSpec _ _) content = do
+processPacket (Record ProtocolType_ChangeCipherSpec _ fragment) = do
 	e <- updateStatusCC False
 	when (isJust e) $ throwError (fromJust "" e)
 
-	returnEither $ decodeChangeCipherSpec content
+	returnEither $ decodeChangeCipherSpec $ fragmentGetBytes fragment
 	switchRxEncryption
 	isClientContext >>= \cc -> when (not cc) setKeyBlock
 	return ChangeCipherSpec
 
-processPacket (Header ProtocolType_Handshake ver _) dcontent = do
-	handshakes <- returnEither (decodeHandshakes dcontent)
+processPacket (Record ProtocolType_Handshake ver fragment) = do
+	handshakes <- returnEither (decodeHandshakes $ fragmentGetBytes fragment)
 	hss <- forM handshakes $ \(ty, content) -> do
 		hs <- processHandshake ver ty content
 		when (finishHandshakeTypeMaterial ty) $ updateHandshakeDigestSplitted ty content
@@ -155,57 +157,57 @@
 	cc <- stClientContext <$> get
 	expected <- getHandshakeDigest (not cc)
 	when (expected /= fdata) $ do
-		throwError $ Error_Protocol( "bad record mac", True, BadRecordMac)
+		throwError $ Error_Protocol("bad record mac", True, BadRecordMac)
 	updateVerifiedData False fdata
 	return ()
 
-decryptContent :: Header -> EncryptedData -> TLSSt ByteString
-decryptContent hdr e@(EncryptedData b) = do
+-- just `decompress' by returning the data for now till we have compression implemented
+uncompressContent :: Record Compressed -> TLSSt (Record Plaintext)
+uncompressContent record = onRecordFragment record $ fragmentUncompress $ \bytes ->
+	withCompression $ compressionInflate bytes
+
+decryptContent :: Record Ciphertext -> TLSSt (Record Compressed)
+decryptContent record = onRecordFragment record $ fragmentUncipher $ \e -> do
 	st <- get
 	if stRxEncrypted st
-		then decryptData e >>= getCipherData hdr
-		else return b
+		then decryptData e >>= getCipherData record
+		else return e
 
-getCipherData :: Header -> CipherData -> TLSSt ByteString
-getCipherData hdr cdata = do
+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 (Header pt ver _) = hdr
 			let new_hdr = Header pt ver (fromIntegral $ B.length $ cipherDataContent cdata)
 			expected_digest <- makeDigest False new_hdr $ cipherDataContent cdata
-			if expected_digest == digest
-				then return True
-				else return False
+			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
-			ver <- stVersion <$> get
+			cver <- stVersion <$> get
 			let b = B.length pad - 1
-			if ver < TLS10
-				then return True
-				else return $ maybe True (const False) $ B.find (/= fromIntegral b) pad
+			return (if cver < TLS10 then True else B.replicate (B.length pad) (fromIntegral b) `bytesEq` pad)
 
-	unless (and $! [ macValid, paddingValid ]) $ do
+	unless (macValid &&! paddingValid) $ do
 		throwError $ Error_Protocol ("bad record mac", True, BadRecordMac)
 
 	return $ cipherDataContent cdata
 
-decryptData :: EncryptedData -> TLSSt CipherData
-decryptData (EncryptedData econtent) = do
+decryptData :: Bytes -> TLSSt CipherData
+decryptData econtent = do
 	st <- get
 
-	let cipher       = fromJust "cipher" $ stCipher st
-	let cst          = fromJust "rx crypt state" $ stRxCryptState st
-	let padding_size = fromIntegral $ cipherPaddingSize cipher
-	let digestSize   = fromIntegral $ cipherDigestSize 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 cipherF cipher of
-		CipherNoneF -> do
+	case bulkF bulk of
+		BulkNoneF -> do
 			let contentlen = B.length econtent - digestSize
 			case partition3 econtent (contentlen, digestSize, 0) of
 				Nothing                ->
@@ -216,13 +218,13 @@
 						, cipherDataMAC     = Just mac
 						, cipherDataPadding = Nothing
 						}
-		CipherBlockF _ decryptF -> do
+		BulkBlockF _ decryptF -> do
 			{- update IV -}
 			let (iv, econtent') =
 				if hasExplicitBlockIV $ stVersion st
-					then B.splitAt (fromIntegral $ cipherIVSize cipher) econtent
+					then B.splitAt (bulkIVSize bulk) econtent
 					else (cstIV cst, econtent)
-			let newiv = fromJust "new iv" $ takelast padding_size econtent'
+			let newiv = fromJust "new iv" $ takelast (bulkBlockSize bulk) econtent'
 			put $ st { stRxCryptState = Just $ cst { cstIV = newiv } }
 
 			let content' = decryptF writekey iv econtent'
@@ -234,7 +236,7 @@
 				, cipherDataMAC     = Just mac
 				, cipherDataPadding = Just padding
 				}
-		CipherStreamF initF _ decryptF -> do
+		BulkStreamF initF _ decryptF -> do
 			let iv = cstIV cst
 			let (content', newiv) = decryptF (if iv /= B.empty then iv else initF writekey) econtent
 			{- update Ctx -}
diff --git a/Network/TLS/Record.hs b/Network/TLS/Record.hs
new file mode 100644
--- /dev/null
+++ b/Network/TLS/Record.hs
@@ -0,0 +1,96 @@
+{-# LANGUAGE EmptyDataDecls #-}
+-- |
+-- Module      : Network.TLS.Record
+-- License     : BSD-style
+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
+-- Stability   : experimental
+-- Portability : unknown
+--
+-- The Record Protocol takes messages to be transmitted, fragments the
+-- data into manageable blocks, optionally compresses the data, applies
+-- a MAC, encrypts, and transmits the result.  Received data is
+-- decrypted, verified, decompressed, reassembled, and then delivered to
+-- higher-level clients.
+--
+module Network.TLS.Record
+	( 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
+import qualified Data.ByteString as B
+import Control.Applicative ((<$>))
+
+-- | Represent a TLS record.
+data Record a = Record !ProtocolType !Version !(Fragment a) deriving (Show,Eq)
+
+newtype Fragment a = Fragment Bytes deriving (Show,Eq)
+
+data Plaintext
+data Compressed
+data Ciphertext
+
+fragmentPlaintext :: Bytes -> Fragment Plaintext
+fragmentPlaintext bytes = Fragment bytes
+
+fragmentCiphertext :: Bytes -> Fragment Ciphertext
+fragmentCiphertext bytes = Fragment bytes
+
+fragmentGetBytes :: Fragment a -> Bytes
+fragmentGetBytes (Fragment bytes) = bytes
+
+onRecordFragment :: Record a -> (Fragment a -> TLSSt (Fragment b)) -> TLSSt (Record b)
+onRecordFragment (Record pt ver frag) f = Record pt ver <$> f frag
+
+fragmentMap :: (Bytes -> TLSSt Bytes) -> Fragment a -> TLSSt (Fragment b)
+fragmentMap f (Fragment b) = Fragment <$> f b
+
+-- | turn a plaintext record into a compressed record using the compression function supplied
+fragmentCompress :: (Bytes -> TLSSt Bytes) -> Fragment Plaintext -> TLSSt (Fragment Compressed)
+fragmentCompress f = fragmentMap f
+
+-- | turn a compressed record into a ciphertext record using the cipher function supplied
+fragmentCipher :: (Bytes -> TLSSt Bytes) -> Fragment Compressed -> TLSSt (Fragment Ciphertext)
+fragmentCipher f = fragmentMap f
+
+-- | turn a ciphertext fragment into a compressed fragment using the cipher function supplied
+fragmentUncipher :: (Bytes -> TLSSt Bytes) -> Fragment Ciphertext -> TLSSt (Fragment Compressed)
+fragmentUncipher f = fragmentMap f
+
+-- | turn a compressed fragment into a plaintext fragment using the decompression function supplied
+fragmentUncompress :: (Bytes -> TLSSt Bytes) -> Fragment Compressed -> TLSSt (Fragment Plaintext)
+fragmentUncompress f = fragmentMap f
+
+-- | turn a record into an header and bytes
+recordToRaw :: Record a -> (Header, Bytes)
+recordToRaw (Record pt ver (Fragment bytes)) = (Header pt ver (fromIntegral $ B.length bytes), bytes)
+
+-- | turn a header and a fragment into a record
+rawToRecord :: Header -> Fragment a -> Record a
+rawToRecord (Header pt ver _) fragment = Record pt ver fragment
+
+-- | turn a record into a header
+recordToHeader :: Record a -> Header
+recordToHeader (Record pt ver (Fragment bytes)) = Header pt ver (fromIntegral $ B.length bytes)
diff --git a/Network/TLS/Sending.hs b/Network/TLS/Sending.hs
--- a/Network/TLS/Sending.hs
+++ b/Network/TLS/Sending.hs
@@ -12,6 +12,7 @@
 	writePacket
 	) where
 
+import Control.Applicative ((<$>))
 import Control.Monad.State
 
 import Data.ByteString (ByteString)
@@ -21,57 +22,63 @@
 import Network.TLS.Cap
 import Network.TLS.Wire
 import Network.TLS.Struct
+import Network.TLS.Record
 import Network.TLS.Packet
 import Network.TLS.State
 import Network.TLS.Cipher
+import Network.TLS.Compression
 import Network.TLS.Crypto
 
 {-
  - 'makePacketData' create a Header and a content bytestring related to a packet
  - this doesn't change any state
  -}
-makePacketData :: Packet -> TLSSt (Header, ByteString)
-makePacketData pkt = do
-	ver <- get >>= return . stVersion
+makeRecord :: Packet -> TLSSt (Record Plaintext)
+makeRecord pkt = do
+	ver <- stVersion <$> get
 	content <- writePacketContent pkt
-	let hdr = Header (packetType pkt) ver (fromIntegral $ B.length content)
-	return (hdr, content)
+	return $ Record (packetType pkt) ver (fragmentPlaintext content)
 
 {-
  - Handshake data need to update a digest
  -}
-processPacketData :: (Header, ByteString) -> TLSSt (Header, ByteString)
-processPacketData dat@(Header ty _ _, content) = do
-	when (ty == ProtocolType_Handshake) (updateHandshakeDigest content)
-	return dat
+processRecord :: Record Plaintext -> TLSSt (Record Plaintext)
+processRecord record@(Record ty _ fragment) = do
+	when (ty == ProtocolType_Handshake) (updateHandshakeDigest $ fragmentGetBytes fragment)
+	return record
 
+compressRecord :: Record Plaintext -> TLSSt (Record Compressed)
+compressRecord record =
+	onRecordFragment record $ fragmentCompress $ \bytes -> do
+		withCompression $ compressionDeflate bytes
+
 {-
  - when Tx Encrypted is set, we pass the data through encryptContent, otherwise
  - we just return the packet
  -}
-encryptPacketData :: (Header, ByteString) -> TLSSt (Header, ByteString)
-encryptPacketData dat = do
+encryptRecord :: Record Compressed -> TLSSt (Record Ciphertext)
+encryptRecord record = onRecordFragment record $ fragmentCipher $ \bytes -> do
 	st <- get
 	if stTxEncrypted st
-		then encryptContent dat
-		else return dat
+		then encryptContent record bytes
+		else return bytes
 
 {-
  - ChangeCipherSpec state change need to be handled after encryption otherwise
  - its own packet would be encrypted with the new context, instead of beeing sent
  - under the current context
  -}
-postprocessPacketData :: (Header, ByteString) -> TLSSt (Header, ByteString)
-postprocessPacketData dat@(Header ProtocolType_ChangeCipherSpec _ _, _) =
-	switchTxEncryption >> isClientContext >>= \cc -> when cc setKeyBlock >> return dat
-
-postprocessPacketData dat = return dat
+postprocessRecord :: Record Ciphertext -> TLSSt (Record Ciphertext)
+postprocessRecord record@(Record ProtocolType_ChangeCipherSpec _ _) =
+	switchTxEncryption >> isClientContext >>= \cc -> when cc setKeyBlock >> return record
+postprocessRecord record = return record
 
 {-
  - marshall packet data
  -}
-encodePacket :: (Header, ByteString) -> TLSSt ByteString
-encodePacket (hdr, content) = return $ B.concat [ encodeHeader hdr, content ]
+encodeRecord :: Record Ciphertext -> TLSSt ByteString
+encodeRecord record = return $ B.concat [ encodeHeader hdr, content ]
+	where (hdr, content) = recordToRaw record
 
 {-
  - just update TLS state machine
@@ -92,8 +99,9 @@
  - and updating state on the go
  -}
 writePacket :: Packet -> TLSSt ByteString
-writePacket pkt = preProcessPacket pkt >> makePacketData pkt >>= processPacketData >>=
-                  encryptPacketData >>= postprocessPacketData >>= encodePacket
+writePacket pkt = do
+	preProcessPacket pkt
+	makeRecord pkt >>= processRecord >>= compressRecord >>= encryptRecord >>= postprocessRecord >>= encodeRecord
 
 {------------------------------------------------------------------------------}
 {- SENDING Helpers                                                            -}
@@ -110,46 +118,46 @@
 		Left err               -> fail ("rsa encrypt failed: " ++ show err)
 		Right (econtent, rng') -> put (st { stRandomGen = rng' }) >> return econtent
 
-encryptContent :: (Header, ByteString) -> TLSSt (Header, ByteString)
-encryptContent (hdr@(Header pt ver _), content) = do
-	digest <- makeDigest True hdr content
-	encrypted_msg <- encryptData $ B.concat [content, digest]
-	let hdrnew = Header pt ver (fromIntegral $ B.length encrypted_msg)
-	return (hdrnew, encrypted_msg)
+encryptContent :: Record Compressed -> ByteString -> TLSSt ByteString
+encryptContent record content = do
+	digest <- makeDigest True (recordToHeader record) content
+	encryptData $ B.concat [content, digest]
 
 encryptData :: ByteString -> TLSSt ByteString
 encryptData content = do
 	st <- get
 
 	let cipher = fromJust "cipher" $ stCipher st
+	let bulk = cipherBulk cipher
 	let cst = fromJust "tx crypt state" $ stTxCryptState st
-	let padding_size = fromIntegral $ cipherPaddingSize cipher
 
-	let msg_len = B.length content
-	let padding = if padding_size > 0
-		then
-			let padbyte = padding_size - (msg_len `mod` padding_size) in
-			let padbyte' = if padbyte == 0 then padding_size else padbyte in
-			B.replicate padbyte' (fromIntegral (padbyte' - 1))
-		else
-			B.empty
 	let writekey = cstKey cst
 
-	case cipherF cipher of
-		CipherNoneF -> return content
-		CipherBlockF encrypt _ -> do
+	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 (fromIntegral $ cipherIVSize cipher)
+				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 (fromIntegral $ cipherIVSize cipher) e
+					let newiv = fromJust "new iv" $ takelast (bulkIVSize bulk) e
 					put $ st { stTxCryptState = Just $ cst { cstIV = newiv } }
 					return e
-		CipherStreamF initF encryptF _ -> do
+		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 } }
diff --git a/Network/TLS/State.hs b/Network/TLS/State.hs
--- a/Network/TLS/State.hs
+++ b/Network/TLS/State.hs
@@ -21,6 +21,7 @@
 	, newTLSState
 	, genTLSRandom
 	, withTLSRNG
+	, withCompression
 	, assert -- FIXME move somewhere else (Internal.hs ?)
 	, updateStatusHs
 	, updateStatusCC
@@ -58,6 +59,7 @@
 import Network.TLS.Packet
 import Network.TLS.Crypto
 import Network.TLS.Cipher
+import Network.TLS.Compression
 import Network.TLS.MAC
 import qualified Data.ByteString as B
 import Control.Applicative ((<$>))
@@ -109,7 +111,7 @@
 	, hstMasterSecret    :: !(Maybe Bytes)
 	, hstRSAPublicKey    :: !(Maybe PublicKey)
 	, hstRSAPrivateKey   :: !(Maybe PrivateKey)
-	, hstHandshakeDigest :: Maybe (HashCtx, HashCtx) -- FIXME could be only 1 hash in tls12
+	, hstHandshakeDigest :: !HashCtx
 	} deriving (Show)
 
 data StateRNG = forall g . CryptoRandomGen g => StateRNG g
@@ -129,6 +131,7 @@
 	, stTxMacState          :: !(Maybe TLSMacState)
 	, stRxMacState          :: !(Maybe TLSMacState)
 	, stCipher              :: Maybe Cipher
+	, stCompression         :: Compression
 	, stRandomGen           :: StateRNG
 	, stSecureRenegotiation :: Bool  -- RFC 5746
 	, stClientVerifiedData  :: Bytes -- RFC 5746
@@ -161,6 +164,7 @@
 	, stTxMacState          = Nothing
 	, stRxMacState          = Nothing
 	, stCipher              = Nothing
+	, stCompression         = nullCompression
 	, stRandomGen           = StateRNG rng
 	, stSecureRenegotiation = False
 	, stClientVerifiedData  = B.empty
@@ -172,6 +176,13 @@
 	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
+
 genTLSRandom :: (MonadState TLSState m, MonadError TLSError m) => Int -> m Bytes
 genTLSRandom n = do
 	st <- get
@@ -186,12 +197,12 @@
 	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 machash = cipherMACHash cipher
+	let hashf = hashF $ cipherHash cipher
 
 	let (macF, msg) =
 		if ver < TLS10
-			then (macSSL machash, B.concat [ encodeWord64 $ msSequence ms, encodeHeaderNoVer hdr, content ])
-			else (hmac machash 64, B.concat [ encodeWord64 $ msSequence ms, encodeHeader hdr, content ])
+			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 }
@@ -314,10 +325,12 @@
 
 	let cc = stClientContext st
 	let cipher = fromJust "cipher" $ stCipher st
-	let keyblockSize = fromIntegral $ cipherKeyBlockSize cipher
-	let digestSize   = fromIntegral $ cipherDigestSize cipher
-	let keySize      = fromIntegral $ cipherKeySize cipher
-	let ivSize       = fromIntegral $ cipherIVSize cipher
+	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
@@ -364,23 +377,24 @@
 isClientContext = get >>= return . stClientContext
 
 -- create a new empty handshake state
-newEmptyHandshake :: Version -> ClientRandom -> TLSHandshakeState
-newEmptyHandshake ver crand = TLSHandshakeState
+newEmptyHandshake :: Version -> ClientRandom -> HashCtx -> TLSHandshakeState
+newEmptyHandshake ver crand digestInit = TLSHandshakeState
 	{ hstClientVersion   = ver
 	, hstClientRandom    = crand
 	, hstServerRandom    = Nothing
 	, hstMasterSecret    = Nothing
 	, hstRSAPublicKey    = Nothing
 	, hstRSAPrivateKey   = Nothing
-	, hstHandshakeDigest = 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 })
+		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) ]
@@ -391,14 +405,8 @@
 	modify (\st -> st { stHandshake = f <$> stHandshake st })
 
 updateHandshakeDigest :: MonadState TLSState m => Bytes -> m ()
-updateHandshakeDigest content = updateHandshake "update digest" (\hs ->
-	let (c1, c2) = case hstHandshakeDigest hs of
-		Nothing                -> (initHash HashTypeSHA1, initHash HashTypeMD5)
-		Just (sha1ctx, md5ctx) -> (sha1ctx, md5ctx) in
-	let nc1 = updateHash c1 content in
-	let nc2 = updateHash c2 content in
-	hs { hstHandshakeDigest = Just (nc1, nc2) }
-	)
+updateHandshakeDigest content = updateHandshake "update digest" $ \hs ->
+	hs { hstHandshakeDigest = hashUpdate (hstHandshakeDigest hs) content }
 
 updateHandshakeDigestSplitted :: MonadState TLSState m => HandshakeType -> Bytes -> m ()
 updateHandshakeDigestSplitted ty bytes = updateHandshakeDigest $ B.concat [hdr, bytes]
@@ -409,9 +417,9 @@
 getHandshakeDigest client = do
 	st <- get
 	let hst = fromJust "handshake" $ stHandshake st
-	let (sha1ctx, md5ctx) = fromJust "handshake digest" $ hstHandshakeDigest hst
-	let msecret           = fromJust "master secret" $ hstMasterSecret hst
-	return $ (if client then generateClientFinished else generateServerFinished) (stVersion st) msecret md5ctx sha1ctx
+	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 })
diff --git a/Network/TLS/Struct.hs b/Network/TLS/Struct.hs
--- a/Network/TLS/Struct.hs
+++ b/Network/TLS/Struct.hs
@@ -16,7 +16,6 @@
 	, CipherType(..)
 	, CipherData(..)
 	, Extension
-	, EncryptedData(..)
 	, CertificateType(..)
 	, HashAlgorithm(..)
 	, SignatureAlgorithm(..)
@@ -48,7 +47,7 @@
 import Data.ByteString (ByteString)
 import qualified Data.ByteString as B (length)
 import Data.Word
-import Data.Certificate.X509
+import Data.Certificate.X509 (X509)
 import Data.Typeable
 import Control.Monad.Error (Error(..))
 import Control.Exception (Exception(..))
@@ -137,7 +136,7 @@
 	| AppData ByteString
 	deriving (Show,Eq)
 
-data Header = Header ProtocolType Version Word16 deriving (Show, Eq)
+data Header = Header ProtocolType Version Word16 deriving (Show,Eq)
 
 newtype ServerRandom = ServerRandom Bytes deriving (Show, Eq)
 newtype ClientRandom = ClientRandom Bytes deriving (Show, Eq)
@@ -156,9 +155,6 @@
 
 clientRandom :: Bytes -> Maybe ClientRandom
 clientRandom l = constrRandom32 ClientRandom l
-
-newtype EncryptedData = EncryptedData ByteString
-	deriving (Show)
 
 data AlertLevel =
 	  AlertLevel_Warning
diff --git a/Network/TLS/Util.hs b/Network/TLS/Util.hs
--- a/Network/TLS/Util.hs
+++ b/Network/TLS/Util.hs
@@ -4,8 +4,12 @@
 	, partition3
 	, partition6
 	, fromJust
+	, and'
+	, (&&!)
+	, bytesEq
 	) where
 
+import Data.List (foldl')
 import Network.TLS.Struct (Bytes)
 import qualified Data.ByteString as B
 
@@ -41,3 +45,20 @@
 fromJust :: String -> Maybe a -> a
 fromJust what Nothing  = error ("fromJust " ++ what ++ ": Nothing") -- yuck
 fromJust _    (Just x) = x
+
+-- | This is a strict version of and
+and' :: [Bool] -> Bool
+and' l = foldl' (&&!) True l
+
+-- | This is a strict version of &&.
+(&&!) :: Bool -> Bool -> Bool
+True  &&! True  = True
+True  &&! False = False
+False &&! True  = False
+False &&! False = False
+
+-- | verify that 2 bytestrings are equals.
+-- it's a non lazy version, that will compare every bytes.
+-- arguments need to be of same length
+bytesEq :: Bytes -> Bytes -> Bool
+bytesEq b1 = and' . B.zipWith (==) b1
diff --git a/Network/TLS/Wire.hs b/Network/TLS/Wire.hs
--- a/Network/TLS/Wire.hs
+++ b/Network/TLS/Wire.hs
@@ -40,6 +40,7 @@
 import Data.Bits
 import Network.TLS.Struct
 
+runGet :: String -> Get a -> Bytes -> Either String a
 runGet lbl f = G.runGet (label lbl f)
 
 getWords8 :: Get [Word8]
diff --git a/README b/README
deleted file mode 100644
--- a/README
+++ /dev/null
@@ -1,7 +0,0 @@
-The hs-tls project aims to reimplement the full TLS protocol (formely known as SSL) in haskell.
-The focus of the projects is to provide a safer implementation than the ones existing,
-through more purity, more type-checking, and more units tests.
-
-While the focus is to make it safer than other implementations, this current
-implementation is *not* to be considered secure, since it doesn't fully
-implement everything necessary (full certificate checking, protocol requirements, etc)
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,25 @@
+haskell TLS
+===========
+
+This library provide native Haskell TLS and SSL protocol implementation for server and client.
+
+Description
+-----------
+
+This provides a high-level implementation of a sensitive security protocol,
+eliminating a common set of security issues through the use of the advanced
+type system, high level constructions and common Haskell features.
+
+Only core protocol available here, have a look at the tls-extra package for
+default ciphers, compressions and certificates functions.
+
+Features
+--------
+
+* tiny code base (more than 20 times smaller than openSSL, and 10 times smaller than gnuTLS)
+* permissive license: BSD3.
+* supported versions: SSL3, TLS1.0, TLS1.1, TLS1.2.
+* key exchange supported: only RSA.
+* bulk algorithm supported: any stream or block ciphers.
+* supported extensions: secure renegociation
+
diff --git a/TODO b/TODO
--- a/TODO
+++ b/TODO
@@ -4,17 +4,9 @@
 - add Client Certificates
 - process session as they should
 - put 4 bytes of time in client/server random
-- implement compression
 - proper separation for key exchange algorithm (hardcoded to RSA at the moment in differents place)
 - implements different key exchange algorithm
-
-tls v1.2:
-
-- implement finish digest generation with hmac256
-- implement finish digest generation with client/server negociated algorithm
-- proper version dispatch in marshalling packets
-- properly separate different version of the protocol
-- implement AEAD
+- implement AEAD bulk algorithm (TLS1.2)
 
 code cleanup:
 
@@ -28,7 +20,6 @@
 
 misc:
 
-- stunnel: actually make it works like stunnel instead of hardcoding the data received/sent
 - investigate an iteratee/enumerator interface
 - portability
 - implement more ciphers
diff --git a/tls.cabal b/tls.cabal
--- a/tls.cabal
+++ b/tls.cabal
@@ -1,5 +1,5 @@
 Name:                tls
-Version:             0.7.2
+Version:             0.8.0
 Description:
    Native Haskell TLS and SSL protocol implementation for server and client.
    .
@@ -7,7 +7,7 @@
    eliminating a common set of security issues through the use of the advanced
    type system, high level constructions and common Haskell features.
    .
-   Currently implement the SSL3.0, TLS1.0 and TLS1.1 protocol,
+   Currently implement the SSL3.0, TLS1.0, TLS1.1 and TLS1.2 protocol,
    with only RSA supported for Key Exchange.
    .
    Only core protocol available here, have a look at the
@@ -24,7 +24,7 @@
 stability:           experimental
 Cabal-Version:       >=1.6
 Homepage:            http://github.com/vincenthz/hs-tls
-data-files:          README, TODO
+data-files:          README.md, TODO
 
 Flag test
   Description:       Build unit test
@@ -42,7 +42,7 @@
                    , bytestring
                    , crypto-api >= 0.5
                    , cryptocipher >= 0.2.5
-                   , certificate >= 0.9 && < 1.0
+                   , certificate >= 0.9.3 && < 1.0
   Exposed-modules:   Network.TLS
                      Network.TLS.Cipher
                      Network.TLS.Compression
@@ -53,6 +53,7 @@
                      Network.TLS.Core
                      Network.TLS.Crypto
                      Network.TLS.Packet
+                     Network.TLS.Record
                      Network.TLS.State
                      Network.TLS.Sending
                      Network.TLS.Receiving
