{-# LANGUAGE OverloadedStrings #-} module Network.PeyoTLS.Codec.Hello ( ContType(..), ClHello(..), SvHello(..), PrtVrsn(..), SssnId(..), CipherSuite(..), KeyEx(..), BulkEnc(..), CmpMtd(..), HSAlg(..), SignAlg(..), HashAlg(..), Extension(..), isRnInfo, emptyRnInfo ) where import Control.Applicative ((<$>), (<*>)) import Data.Word (Word8, Word16) import qualified Data.ByteString as BS import qualified Codec.Bytable.BigEndian as B import Network.PeyoTLS.Codec.Extension ( Extension(..), isRnInfo, emptyRnInfo, HSAlg(..), SignAlg(..), HashAlg(..) ) import Network.PeyoTLS.Codec.ContentTypes (ContType(..), PrtVrsn(..)) import Network.PeyoTLS.CipherSuite (CipherSuite(..), KeyEx(..), BulkEnc(..)) modNm :: String modNm = "Network.PeyoTLS.Codec.Hello" -- | RFC 5246 7.4.1.2. Client Hello -- -- @ -- struct { -- uint32 gmt_unix_time; -- opaque random_bytes[28]; -- } Random -- -- opaque SessionID\<0..32>; -- -- uint8 CipherSuite[2]; -- -- enum { null(0), (255) } CompressionMethod; -- -- struct { -- ProtocolVersion client_version; -- Random random; -- SessionID session_id; -- CipherSuite cipher_suites\<2..2^16-2>; -- CompressionMethod compression_methods\<1..2^8-1>; -- select (extensions_present) { -- case false: struct {}; -- case true: Extension extensions\<0..2^16-1>; -- }; -- } ClientHello; -- @ data ClHello = ClHello PrtVrsn BS.ByteString SssnId [CipherSuite] [CmpMtd] (Maybe [Extension]) | ClHelloRaw BS.ByteString deriving Show instance B.Bytable ClHello where decode = B.evalBytableM $ ClHello <$> B.take 2 <*> B.take 32 <*> (B.take =<< B.take 1) <*> (flip B.list (B.take 2) =<< B.take 2) <*> (flip B.list (B.take 1) =<< B.take 1) <*> do nl <- B.null if nl then return Nothing else Just <$> (flip B.list B.parse =<< B.take 2) encode (ClHello vjvn r sid css cms mel) = BS.concat [ B.encode vjvn, B.encode r, B.addLen w8 $ B.encode sid, B.addLen w16 . BS.concat $ map B.encode css, B.addLen w8 . BS.concat $ map B.encode cms, maybe "" (B.addLen w16 . BS.concat . map B.encode) mel ] encode (ClHelloRaw bs) = bs -- | RFC 5246 7.4.1.3. Server Hello -- -- @ -- struct { -- ProtocolVersion server_version; -- Random random; -- SessionID session_id; -- CipherSuite cipher_suite; -- CompressionMethod compression_method; -- select (extensions_present) { -- case false: struct {}; -- case true: Extension extensions\<0..2^16-1>; -- }; -- } ServerHello; -- @ data SvHello = SvHello PrtVrsn BS.ByteString SssnId CipherSuite CmpMtd (Maybe [Extension]) | SvHelloRaw BS.ByteString deriving Show instance B.Bytable SvHello where decode = B.evalBytableM $ SvHello <$> B.take 2 <*> B.take 32 <*> (B.take =<< B.take 1) <*> B.take 2 <*> B.take 1 <*> do n <- B.null if n then return Nothing else Just <$> (flip B.list B.parse =<< B.take 2) encode (SvHello vjvn r sid cs cm mes) = BS.concat [ B.encode vjvn, B.encode r, B.addLen w8 $ B.encode sid, B.encode cs, B.encode cm, maybe "" (B.addLen w16 . BS.concat . map B.encode) mes ] encode (SvHelloRaw sh) = sh -- | RFC 5246 7.4.1.2 Client Hello -- -- @ -- opaque SessionID\<0..32>; -- @ data SssnId = SssnId BS.ByteString deriving Show instance B.Bytable SssnId where decode = Right . SssnId; encode (SssnId bs) = bs -- | RFC 5246 7.4.1.2. Client Hello -- -- @ -- enum { null(0), (255) } CompressionMethod; -- @ data CmpMtd = CmpMtdNull | CmpMtdRaw Word8 deriving (Show, Eq) instance B.Bytable CmpMtd where decode bs = case BS.unpack bs of [cm] -> Right $ case cm of 0 -> CmpMtdNull; _ -> CmpMtdRaw cm _ -> Left $ modNm ++ ": CmpMtd.decode" encode CmpMtdNull = "\0" encode (CmpMtdRaw cm) = BS.pack [cm] w8 :: Word8; w8 = undefined w16 :: Word16; w16 = undefined