tls-2.1.9: Network/TLS/Packet13.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
module Network.TLS.Packet13 (
encodeHandshake13,
decodeHandshakeRecord13,
decodeHandshake13,
decodeHandshakes13,
encodeCertificate13,
) where
import Codec.Compression.Zlib
import qualified Control.Exception as E
import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as BL
import Data.X509 (
CertificateChain,
CertificateChainRaw (..),
decodeCertificateChain,
encodeCertificateChain,
)
import System.IO.Unsafe
import Network.TLS.ErrT
import Network.TLS.Imports
import Network.TLS.Packet
import Network.TLS.Struct
import Network.TLS.Struct13
import Network.TLS.Types
import Network.TLS.Wire
----------------------------------------------------------------
encodeHandshake13 :: Handshake13 -> ByteString
encodeHandshake13 hdsk = pkt
where
tp = typeOfHandshake13 hdsk
content = encodeHandshake13' hdsk
len = B.length content
header = encodeHandshakeHeader13 tp len
pkt = B.concat [header, content]
-- TLS 1.3 does not use "select (extensions_present)".
putExtensions :: [ExtensionRaw] -> Put
putExtensions es = putOpaque16 (runPut $ mapM_ putExtension es)
encodeHandshake13' :: Handshake13 -> ByteString
encodeHandshake13' (ServerHello13 SH{..}) = runPut $ do
putBinaryVersion shVersion
putServerRandom32 shRandom
putSession shSession
putWord16 $ fromCipherId shCipher
putWord8 shComp
putExtensions shExtensions
encodeHandshake13'
( NewSessionTicket13
life
ageadd
(TicketNonce nonce)
(SessionIDorTicket_ label)
exts
) = runPut $ do
putWord32 life
putWord32 ageadd
putOpaque8 nonce
putOpaque16 label
putExtensions exts
encodeHandshake13' EndOfEarlyData13 = ""
encodeHandshake13' (EncryptedExtensions13 exts) = runPut $ putExtensions exts
encodeHandshake13' (Certificate13 reqctx (CertificateChain_ cc) ess) = encodeCertificate13 reqctx cc ess
encodeHandshake13' (CertRequest13 reqctx exts) = runPut $ do
putOpaque8 reqctx
putExtensions exts
encodeHandshake13' (CertVerify13 (DigitallySigned hs sig)) = runPut $ do
putSignatureHashAlgorithm hs
putOpaque16 sig
encodeHandshake13' (Finished13 (VerifyData dat)) = runPut $ putBytes dat
encodeHandshake13' (KeyUpdate13 UpdateNotRequested) = runPut $ putWord8 0
encodeHandshake13' (KeyUpdate13 UpdateRequested) = runPut $ putWord8 1
encodeHandshake13' (CompressedCertificate13 reqctx (CertificateChain_ cc) ess) = runPut $ do
putWord16 1 -- zlib: fixme
let bs = encodeCertificate13 reqctx cc ess
putWord24 $ fromIntegral $ B.length bs
putOpaque24 $ BL.toStrict $ compress $ BL.fromStrict bs
encodeHandshakeHeader13 :: HandshakeType -> Int -> ByteString
encodeHandshakeHeader13 ty len = runPut $ do
putWord8 (fromHandshakeType ty)
putWord24 len
encodeCertificate13
:: CertReqContext -> CertificateChain -> [[ExtensionRaw]] -> ByteString
encodeCertificate13 reqctx cc ess = runPut $ do
putOpaque8 reqctx
putOpaque24 (runPut $ mapM_ putCert $ zip certs ess)
where
CertificateChainRaw certs = encodeCertificateChain cc
putCert (certRaw, exts) = do
putOpaque24 certRaw
putExtensions exts
----------------------------------------------------------------
decodeHandshakes13 :: MonadError TLSError m => ByteString -> m [Handshake13]
decodeHandshakes13 bs = case decodeHandshakeRecord13 bs of
GotError err -> throwError err
GotPartial _cont -> error "decodeHandshakes13"
GotSuccess (ty, content) -> case decodeHandshake13 ty content of
Left e -> throwError e
Right h -> return [h]
GotSuccessRemaining (ty, content) left -> case decodeHandshake13 ty content of
Left e -> throwError e
Right h -> (h :) <$> decodeHandshakes13 left
decodeHandshakeRecord13 :: ByteString -> GetResult (HandshakeType, ByteString)
decodeHandshakeRecord13 = runGet "handshake-record" $ do
ty <- getHandshakeType
content <- getOpaque24
return (ty, content)
{- FOURMOLU_DISABLE -}
decodeHandshake13
:: HandshakeType -> ByteString -> Either TLSError Handshake13
decodeHandshake13 ty = runGetErr ("handshake[" ++ show ty ++ "]") $ case ty of
HandshakeType_ServerHello -> decodeServerHello13
HandshakeType_NewSessionTicket -> decodeNewSessionTicket13
HandshakeType_EndOfEarlyData -> return EndOfEarlyData13
HandshakeType_EncryptedExtensions -> decodeEncryptedExtensions13
HandshakeType_Certificate -> decodeCertificate13
HandshakeType_CertRequest -> decodeCertRequest13
HandshakeType_CertVerify -> decodeCertVerify13
HandshakeType_Finished -> decodeFinished13
HandshakeType_KeyUpdate -> decodeKeyUpdate13
HandshakeType_CompressedCertificate -> decodeCompressedCertificate13
(HandshakeType x) -> fail $ "Unsupported HandshakeType " ++ show x
{- FOURMOLU_ENABLE -}
decodeServerHello13 :: Get Handshake13
decodeServerHello13 = do
ver <- getBinaryVersion
random <- getServerRandom32
session <- getSession
cipherid <- CipherId <$> getWord16
comp <- getWord8
exts <- getWord16 >>= getExtensions . fromIntegral
return $
ServerHello13 $
SH
{ shVersion = ver
, shRandom = random
, shSession = session
, shCipher = cipherid
, shComp = comp
, shExtensions = exts
}
decodeNewSessionTicket13 :: Get Handshake13
decodeNewSessionTicket13 = do
life <- getWord32
ageadd <- getWord32
nonce <- TicketNonce <$> getOpaque8
label <- SessionIDorTicket_ <$> getOpaque16
len <- fromIntegral <$> getWord16
exts <- getExtensions len
return $ NewSessionTicket13 life ageadd nonce label exts
decodeEncryptedExtensions13 :: Get Handshake13
decodeEncryptedExtensions13 =
EncryptedExtensions13 <$> do
len <- fromIntegral <$> getWord16
getExtensions len
decodeCertificate13 :: Get Handshake13
decodeCertificate13 = do
reqctx <- getOpaque8
len <- fromIntegral <$> getWord24
(certRaws, ess) <- unzip <$> getList len getCert
case decodeCertificateChain $ CertificateChainRaw certRaws of
Left (i, s) -> fail ("error certificate parsing " ++ show i ++ ":" ++ s)
Right cc -> return $ Certificate13 reqctx (CertificateChain_ cc) ess
where
getCert = do
l <- fromIntegral <$> getWord24
cert <- getBytes l
len <- fromIntegral <$> getWord16
exts <- getExtensions len
return (3 + l + 2 + len, (cert, exts))
decodeCertRequest13 :: Get Handshake13
decodeCertRequest13 = do
reqctx <- getOpaque8
len <- fromIntegral <$> getWord16
exts <- getExtensions len
return $ CertRequest13 reqctx exts
decodeCertVerify13 :: Get Handshake13
decodeCertVerify13 =
CertVerify13 <$> (DigitallySigned <$> getSignatureHashAlgorithm <*> getOpaque16)
decodeFinished13 :: Get Handshake13
decodeFinished13 = Finished13 . VerifyData <$> (remaining >>= getBytes)
decodeKeyUpdate13 :: Get Handshake13
decodeKeyUpdate13 = do
ru <- getWord8
case ru of
0 -> return $ KeyUpdate13 UpdateNotRequested
1 -> return $ KeyUpdate13 UpdateRequested
x -> fail $ "Unknown request_update: " ++ show x
decodeCompressedCertificate13 :: Get Handshake13
decodeCompressedCertificate13 = do
algo <- getWord16
when (algo /= 1) $ fail "comp algo is not supported" -- fixme
len <- getWord24
bs <- getOpaque24
if bs == ""
then fail "empty compressed certificate"
else case decompressIt bs of
Left e -> fail (show e)
Right bs' -> do
when (B.length bs' /= len) $ fail "plain length is wrong"
case runGetMaybe decodeCertificate13 bs' of
Just (Certificate13 reqctx certs ess) -> return $ CompressedCertificate13 reqctx certs ess
-- _ -> fail "compressed certificate cannot be parsed"
_ -> fail $ "invalid compressed certificate: len = " ++ show len
decompressIt :: ByteString -> Either DecompressError ByteString
decompressIt inp = unsafePerformIO $ E.handle handler $ do
Right . BL.toStrict <$> E.evaluate (decompress (BL.fromStrict inp))
where
handler e = return $ Left (e :: DecompressError)