1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
|
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
module Network.TLS.Handshake.Client.TLS12 (
recvServerFirstFlight12,
sendClientSecondFlight12,
recvServerSecondFlight12,
) where
import Control.Monad.State.Strict
import qualified Data.ByteString as B
import Network.TLS.Cipher
import Network.TLS.Context.Internal
import Network.TLS.Crypto
import Network.TLS.Handshake.Client.Common
import Network.TLS.Handshake.Common
import Network.TLS.Handshake.Key
import Network.TLS.Handshake.Signature
import Network.TLS.Handshake.State
import Network.TLS.IO
import Network.TLS.Imports
import Network.TLS.Packet hiding (getExtensions, getSession)
import Network.TLS.Parameters
import Network.TLS.Session
import Network.TLS.State
import Network.TLS.Struct
import Network.TLS.Types
import Network.TLS.Util (catchException)
import Network.TLS.Wire
import Network.TLS.X509 hiding (Certificate)
----------------------------------------------------------------
recvServerFirstFlight12 :: ClientParams -> Context -> [Handshake] -> IO ()
recvServerFirstFlight12 cparams ctx hs = do
resuming <- usingState_ ctx getTLS12SessionResuming
if resuming
then recvNSTandCCSandFinished ctx
else do
let st = RecvStateHandshake (expectCertificate cparams ctx)
runRecvStateHS ctx st hs
expectCertificate :: ClientParams -> Context -> Handshake -> IO (RecvState IO)
expectCertificate cparams ctx (Certificate (TLSCertificateChain certs)) = do
usingState_ ctx $ setServerCertificateChain certs
doCertificate cparams ctx certs
processCertificate ctx ClientRole certs
return $ RecvStateHandshake (expectServerKeyExchange ctx)
expectCertificate _ ctx p = expectServerKeyExchange ctx p
expectServerKeyExchange :: Context -> Handshake -> IO (RecvState IO)
expectServerKeyExchange ctx (ServerKeyXchg origSkx) = do
doServerKeyExchange ctx origSkx
return $ RecvStateHandshake (expectCertificateRequest ctx)
expectServerKeyExchange ctx p = expectCertificateRequest ctx p
expectCertificateRequest :: Context -> Handshake -> IO (RecvState IO)
expectCertificateRequest ctx (CertRequest cTypesSent sigAlgs dNames) = do
let cTypes = filter (<= lastSupportedCertificateType) cTypesSent
usingHState ctx $ setCertReqCBdata $ Just (cTypes, Just sigAlgs, dNames)
return $ RecvStateHandshake (expectServerHelloDone ctx)
expectCertificateRequest ctx p = do
usingHState ctx $ setCertReqCBdata Nothing
expectServerHelloDone ctx p
expectServerHelloDone :: Context -> Handshake -> IO (RecvState m)
expectServerHelloDone _ ServerHelloDone = return RecvStateDone
expectServerHelloDone _ p = unexpected (show p) (Just "server hello data")
----------------------------------------------------------------
sendClientSecondFlight12 :: ClientParams -> Context -> IO ()
sendClientSecondFlight12 cparams ctx = do
sessionResuming <- usingState_ ctx getTLS12SessionResuming
if sessionResuming
then sendCCSandFinished ctx ClientRole
else do
sendClientCCC cparams ctx
sendCCSandFinished ctx ClientRole
recvServerSecondFlight12 :: ClientParams -> Context -> IO ()
recvServerSecondFlight12 cparams ctx = do
sessionResuming <- usingState_ ctx getTLS12SessionResuming
unless sessionResuming $ recvNSTandCCSandFinished ctx
mticket <- usingState_ ctx getTLS12SessionTicket
session <- usingState_ ctx getSession
let midentity = ticketOrSessionID12 mticket session
case midentity of
Nothing -> return ()
Just identity -> do
sessionData <- getSessionData ctx
void $
sessionEstablish
(sharedSessionManager $ ctxShared ctx)
identity
(fromJust sessionData)
handshakeDone12 ctx
liftIO $ do
minfo <- contextGetInformation ctx
case minfo of
Nothing -> return ()
Just info -> onServerFinished (clientHooks cparams) info
recvNSTandCCSandFinished :: Context -> IO ()
recvNSTandCCSandFinished ctx = do
st <- isJust <$> usingState_ ctx getTLS12SessionTicket
if st
then runRecvState ctx $ RecvStateHandshake expectNewSessionTicket
else do runRecvState ctx $ RecvStatePacket expectChangeCipher
where
expectNewSessionTicket (NewSessionTicket _ ticket) = do
usingState_ ctx $ setTLS12SessionTicket ticket
return $ RecvStatePacket expectChangeCipher
expectNewSessionTicket p = unexpected (show p) (Just "Handshake Finished")
expectChangeCipher ChangeCipherSpec = do
enableMyRecordLimit ctx
return $ RecvStateHandshake $ expectFinished ctx
expectChangeCipher p = unexpected (show p) (Just "change cipher")
----------------------------------------------------------------
-- | TLS 1.2 and below. Send the client handshake messages that
-- follow the @ServerHello@, etc. except for @CCS@ and @Finished@.
--
-- XXX: Is any buffering done here to combined these messages into
-- a single TCP packet? Otherwise we're prone to Nagle delays, or
-- in any case needlessly generate multiple small packets, where
-- a single larger packet will do. The TLS 1.3 code path seems
-- to separating record generation and transmission and sending
-- multiple records in a single packet.
--
-- -> [certificate]
-- -> client key exchange
-- -> [cert verify]
sendClientCCC :: ClientParams -> Context -> IO ()
sendClientCCC cparams ctx = do
sendCertificate cparams ctx
sendClientKeyXchg cparams ctx
sendCertificateVerify ctx
----------------------------------------------------------------
sendCertificate :: ClientParams -> Context -> IO ()
sendCertificate cparams ctx = do
usingHState ctx $ setClientCertSent False
clientChain cparams ctx >>= \case
Nothing -> return ()
Just cc@(CertificateChain certs) -> do
unless (null certs) $
usingHState ctx $
setClientCertSent True
sendPacket12 ctx $ Handshake [Certificate (TLSCertificateChain cc)]
----------------------------------------------------------------
sendClientKeyXchg :: ClientParams -> Context -> IO ()
sendClientKeyXchg cparams ctx = do
cipher <- usingHState ctx getPendingCipher
(ckx, setMainSec) <- case cipherKeyExchange cipher of
CipherKeyExchange_RSA -> getCKX_RSA ctx
CipherKeyExchange_DHE_RSA -> getCKX_DHE cparams ctx
CipherKeyExchange_DHE_DSA -> getCKX_DHE cparams ctx
CipherKeyExchange_ECDHE_RSA -> getCKX_ECDHE ctx
CipherKeyExchange_ECDHE_ECDSA -> getCKX_ECDHE ctx
_ ->
throwCore $
Error_Protocol "client key exchange unsupported type" HandshakeFailure
sendPacket12 ctx $ Handshake [ClientKeyXchg ckx]
mainSecret <- usingHState ctx setMainSec
logKey ctx (MainSecret mainSecret)
--------------------------------
getCKX_RSA
:: Context -> IO (ClientKeyXchgAlgorithmData, HandshakeM ByteString)
getCKX_RSA ctx = do
clientVersion <- usingHState ctx $ gets hstClientVersion
(xver, prerand) <- usingState_ ctx $ (,) <$> getVersion <*> genRandom 46
let preMain = encodePreMainSecret clientVersion prerand
setMainSec = setMainSecretFromPre xver ClientRole preMain
encryptedPreMain <- do
-- 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 ctx preMain
let extra = encodeWord16 $ fromIntegral $ B.length e
return $ extra `B.append` e
return (CKX_RSA encryptedPreMain, setMainSec)
--------------------------------
getCKX_DHE
:: ClientParams
-> Context
-> IO (ClientKeyXchgAlgorithmData, HandshakeM ByteString)
getCKX_DHE cparams ctx = do
xver <- usingState_ ctx getVersion
serverParams <- usingHState ctx getServerDHParams
let params = serverDHParamsToParams serverParams
ffGroup = findFiniteFieldGroup params
srvpub = serverDHParamsToPublic serverParams
unless (maybe False (isSupportedGroup ctx) ffGroup) $ do
groupUsage <-
onCustomFFDHEGroup (clientHooks cparams) params srvpub
`catchException` throwMiscErrorOnException "custom group callback failed"
case groupUsage of
GroupUsageInsecure ->
throwCore $
Error_Protocol "FFDHE group is not secure enough" InsufficientSecurity
GroupUsageUnsupported reason ->
throwCore $
Error_Protocol ("unsupported FFDHE group: " ++ reason) HandshakeFailure
GroupUsageInvalidPublic -> throwCore $ Error_Protocol "invalid server public key" IllegalParameter
GroupUsageValid -> return ()
-- When grp is known but not in the supported list we use it
-- anyway. This provides additional validation and a more
-- efficient implementation.
(clientDHPub, preMain) <-
case ffGroup of
Nothing -> do
(clientDHPriv, clientDHPub) <- generateDHE ctx params
let preMain = dhGetShared params clientDHPriv srvpub
return (clientDHPub, preMain)
Just grp -> do
usingHState ctx $ setSupportedGroup grp
dhePair <- generateFFDHEShared ctx grp srvpub
case dhePair of
Nothing ->
throwCore $
Error_Protocol ("invalid server " ++ show grp ++ " public key") IllegalParameter
Just pair -> return pair
let setMainSec = setMainSecretFromPre xver ClientRole preMain
return (CKX_DH clientDHPub, setMainSec)
--------------------------------
getCKX_ECDHE
:: Context -> IO (ClientKeyXchgAlgorithmData, HandshakeM ByteString)
getCKX_ECDHE ctx = do
ServerECDHParams grp srvpub <- usingHState ctx getServerECDHParams
checkSupportedGroup ctx grp
usingHState ctx $ setSupportedGroup grp
ecdhePair <- generateECDHEShared ctx srvpub
case ecdhePair of
Nothing ->
throwCore $
Error_Protocol ("invalid server " ++ show grp ++ " public key") IllegalParameter
Just (clipub, preMain) -> do
xver <- usingState_ ctx getVersion
let setMainSec = setMainSecretFromPre xver ClientRole preMain
return (CKX_ECDH $ encodeGroupPublic clipub, setMainSec)
----------------------------------------------------------------
-- In order to send a proper certificate verify message,
-- we have to do the following:
--
-- 1. Determine which signing algorithm(s) the server supports
-- (we currently only support RSA).
-- 2. Get the current handshake hash from the handshake state.
-- 3. Sign the handshake hash
-- 4. Send it to the server.
--
sendCertificateVerify :: Context -> IO ()
sendCertificateVerify ctx = do
ver <- usingState_ ctx getVersion
-- Only send a certificate verify message when we
-- have sent a non-empty list of certificates.
--
certSent <- usingHState ctx getClientCertSent
when certSent $ do
pubKey <- getLocalPublicKey ctx
mhashSig <-
let cHashSigs = supportedHashSignatures $ ctxSupported ctx
in getLocalHashSigAlg ctx signatureCompatible cHashSigs pubKey
-- Fetch all handshake messages up to now.
msgs <- usingHState ctx $ B.concat <$> getHandshakeMessages
sigDig <- createCertificateVerify ctx ver pubKey mhashSig msgs
sendPacket12 ctx $ Handshake [CertVerify sigDig]
|