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 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338
|
{-# LANGUAGE OverloadedStrings #-}
module Network.TLS.Handshake.Client.ClientHello (
sendClientHello,
getPreSharedKeyInfo,
) where
import Network.TLS.Cipher
import Network.TLS.Compression
import Network.TLS.Context.Internal
import Network.TLS.Crypto
import Network.TLS.Extension
import Network.TLS.Handshake.Client.Common
import Network.TLS.Handshake.Common
import Network.TLS.Handshake.Common13
import Network.TLS.Handshake.Control
import Network.TLS.Handshake.Process
import Network.TLS.Handshake.Random
import Network.TLS.Handshake.State
import Network.TLS.Handshake.State13
import Network.TLS.IO
import Network.TLS.Imports
import Network.TLS.Packet hiding (getExtensions)
import Network.TLS.Parameters
import Network.TLS.State
import Network.TLS.Struct
import Network.TLS.Types
----------------------------------------------------------------
sendClientHello
:: ClientParams
-> Context
-> [Group]
-> Maybe (ClientRandom, Session, Version)
-> PreSharedKeyInfo
-> IO ClientRandom
sendClientHello cparams ctx groups mparams pskinfo = do
crand <- generateClientHelloParams mparams
sendClientHello' cparams ctx groups crand pskinfo
return crand
where
highestVer = maximum $ supportedVersions $ ctxSupported ctx
tls13 = highestVer >= TLS13
ems = supportedExtendedMainSecret $ ctxSupported ctx
-- Client random and session in the second client hello for
-- retry must be the same as the first one.
generateClientHelloParams (Just (crand, clientSession, _)) = do
modifyTLS13State ctx $ \st -> st{tls13stSession = clientSession}
return crand
generateClientHelloParams Nothing = do
crand <- clientRandom ctx
let paramSession = case clientSessions cparams of
[] -> Session Nothing
(sidOrTkt, sdata) : _
| sessionVersion sdata >= TLS13 -> Session Nothing
| ems == RequireEMS && noSessionEMS -> Session Nothing
| isTicket sidOrTkt -> Session $ Just $ toSessionID sidOrTkt
| otherwise -> Session (Just sidOrTkt)
where
noSessionEMS = SessionEMS `notElem` sessionFlags sdata
-- In compatibility mode a client not offering a pre-TLS 1.3
-- session MUST generate a new 32-byte value
if tls13 && paramSession == Session Nothing && not (ctxQUICMode ctx)
then do
randomSession <- newSession ctx
modifyTLS13State ctx $ \st -> st{tls13stSession = randomSession}
return crand
else do
modifyTLS13State ctx $ \st -> st{tls13stSession = paramSession}
return crand
----------------------------------------------------------------
sendClientHello'
:: ClientParams
-> Context
-> [Group]
-> ClientRandom
-> PreSharedKeyInfo
-> IO ()
sendClientHello' cparams ctx groups crand (pskInfo, rtt0info, rtt0) = do
let ver = if tls13 then TLS12 else highestVer
clientSession <- tls13stSession <$> getTLS13State ctx
hrr <- usingState_ ctx getTLS13HRR
unless hrr $ startHandshake ctx ver crand
usingState_ ctx $ setVersionIfUnset highestVer
let cipherIds = map (CipherId . cipherID) ciphers
compIds = map compressionID compressions
mkClientHello exts = ClientHello ver crand compIds $ CH clientSession cipherIds exts
setMyRecordLimit ctx $ limitRecordSize $ sharedLimit $ clientShared cparams
extensions0 <- catMaybes <$> getExtensions
let extensions1 = sharedHelloExtensions (clientShared cparams) ++ extensions0
extensions <- adjustExtentions extensions1 $ mkClientHello extensions1
sendPacket12 ctx $ Handshake [mkClientHello extensions]
mEarlySecInfo <- case rtt0info of
Nothing -> return Nothing
Just info -> Just <$> getEarlySecretInfo info
unless hrr $ contextSync ctx $ SendClientHello mEarlySecInfo
let sentExtensions = map (\(ExtensionRaw i _) -> i) extensions
modifyTLS13State ctx $ \st -> st{tls13stSentExtensions = sentExtensions}
where
ciphers = supportedCiphers $ ctxSupported ctx
compressions = supportedCompressions $ ctxSupported ctx
highestVer = maximum $ supportedVersions $ ctxSupported ctx
tls13 = highestVer >= TLS13
ems = supportedExtendedMainSecret $ ctxSupported ctx
groupToSend = listToMaybe groups
-- List of extensions to send in ClientHello, ordered such that we never
-- terminate with a zero-length extension. Some buggy implementations
-- are allergic to an extension with empty data at final position.
--
-- Without TLS 1.3, the list ends with extension "signature_algorithms"
-- with length >= 2 bytes. When TLS 1.3 is enabled, extensions
-- "psk_key_exchange_modes" (currently always sent) and "pre_shared_key"
-- (not always present) have length > 0.
getExtensions =
sequence
[ {- 0x00 -} sniExt
, {- 0x0a -} groupExt
, {- 0x0b -} ecPointExt
, {- 0x0d -} signatureAlgExt
, {- 0x10 -} alpnExt
, {- 0x17 -} emsExt
, {- 0x1b -} compCertExt
, {- 0x1c -} recordSizeLimitExt
, {- 0x23 -} sessionTicketExt
, {- 0x2a -} earlyDataExt
, {- 0x2b -} versionExt
, {- 0x2c -} cookieExt
, {- 0x2d -} pskExchangeModeExt
, {- 0x31 -} postHandshakeAuthExt
, {- 0x33 -} keyShareExt
, {- 0xff01 -} secureRenegExt
, {- 0x29 -} preSharedKeyExt -- MUST be last (RFC 8446)
]
--------------------
sniExt =
if clientUseServerNameIndication cparams
then do
let sni = fst $ clientServerIdentification cparams
usingState_ ctx $ setClientSNI sni
return $ Just $ toExtensionRaw $ ServerName [ServerNameHostName sni]
else return Nothing
groupExt =
return $
Just $
toExtensionRaw $
SupportedGroups (supportedGroups $ ctxSupported ctx)
ecPointExt =
return $
Just $
toExtensionRaw $
EcPointFormatsSupported [EcPointFormat_Uncompressed]
signatureAlgExt =
return $
Just $
toExtensionRaw $
SignatureAlgorithms $
supportedHashSignatures $
clientSupported cparams
alpnExt = do
mprotos <- onSuggestALPN $ clientHooks cparams
case mprotos of
Nothing -> return Nothing
Just protos -> do
usingState_ ctx $ setClientALPNSuggest protos
return $ Just $ toExtensionRaw $ ApplicationLayerProtocolNegotiation protos
emsExt =
return $
if ems == NoEMS || all (>= TLS13) (supportedVersions $ ctxSupported ctx)
then Nothing
else Just $ toExtensionRaw ExtendedMainSecret
compCertExt = return $ Just $ toExtensionRaw (CompressCertificate [CCA_Zlib])
recordSizeLimitExt = case limitRecordSize $ sharedLimit $ clientShared cparams of
Nothing -> return Nothing
Just siz -> return $ Just $ toExtensionRaw $ RecordSizeLimit $ fromIntegral siz
sessionTicketExt = do
case clientSessions cparams of
(sidOrTkt, _) : _
| isTicket sidOrTkt -> return $ Just $ toExtensionRaw $ SessionTicket sidOrTkt
_ -> return $ Just $ toExtensionRaw $ SessionTicket ""
earlyDataExt
| rtt0 = return $ Just $ toExtensionRaw (EarlyDataIndication Nothing)
| otherwise = return Nothing
versionExt
| tls13 = do
let vers = filter (>= TLS12) $ supportedVersions $ ctxSupported ctx
return $ Just $ toExtensionRaw $ SupportedVersionsClientHello vers
| otherwise = return Nothing
cookieExt = do
mcookie <- usingState_ ctx getTLS13Cookie
case mcookie of
Nothing -> return Nothing
Just cookie -> return $ Just $ toExtensionRaw cookie
pskExchangeModeExt
| tls13 = return $ Just $ toExtensionRaw $ PskKeyExchangeModes [PSK_DHE_KE]
| otherwise = return Nothing
postHandshakeAuthExt
| ctxQUICMode ctx = return Nothing
| tls13 = return $ Just $ toExtensionRaw PostHandshakeAuth
| otherwise = return Nothing
-- FIXME
keyShareExt
| tls13 = case groupToSend of
Nothing -> return Nothing
Just grp -> do
(cpri, ent) <- makeClientKeyShare ctx grp
usingHState ctx $ setGroupPrivate cpri
return $ Just $ toExtensionRaw $ KeyShareClientHello [ent]
| otherwise = return Nothing
secureRenegExt =
if supportedSecureRenegotiation $ ctxSupported ctx
then do
VerifyData cvd <- usingState_ ctx $ getVerifyData ClientRole
return $ Just $ toExtensionRaw $ SecureRenegotiation cvd ""
else return Nothing
preSharedKeyExt =
case pskInfo of
Nothing -> return Nothing
Just (identities, _, choice, obfAge) ->
let zero = cZero choice
pskIdentities = map (\x -> PskIdentity x obfAge) identities
-- [zero] is a place holds.
-- adjustExtentions will replace them.
binders = replicate (length pskIdentities) zero
offeredPsks = PreSharedKeyClientHello pskIdentities binders
in return $ Just $ toExtensionRaw offeredPsks
----------------------------------------
adjustExtentions exts ch =
case pskInfo of
Nothing -> return exts
Just (identities, sdata, choice, _) -> do
let psk = sessionSecret sdata
earlySecret = initEarlySecret choice (Just psk)
usingHState ctx $ setTLS13EarlySecret earlySecret
let ech = encodeHandshake ch
h = cHash choice
siz = (hashDigestSize h + 1) * length identities + 2
binder <- makePSKBinder ctx earlySecret h siz (Just ech)
-- PSK is shared by the previous TLS session.
-- So, PSK is unique for identities.
let binders = replicate (length identities) binder
let exts' = init exts ++ [adjust (last exts)]
adjust (ExtensionRaw eid withoutBinders) = ExtensionRaw eid withBinders
where
withBinders = replacePSKBinder withoutBinders binders
return exts'
getEarlySecretInfo choice = do
let usedCipher = cCipher choice
usedHash = cHash choice
Just earlySecret <- usingHState ctx getTLS13EarlySecret
-- Client hello is stored in hstHandshakeDigest
-- But HandshakeDigestContext is not created yet.
earlyKey <- calculateEarlySecret ctx choice (Right earlySecret) False
let clientEarlySecret = pairClient earlyKey
unless (ctxQUICMode ctx) $ do
runPacketFlight ctx $ sendChangeCipherSpec13 ctx
setTxRecordState ctx usedHash usedCipher clientEarlySecret
setEstablished ctx EarlyDataSending
-- We set RTT0Sent even in quicMode
usingHState ctx $ setTLS13RTT0Status RTT0Sent
return $ EarlySecretInfo usedCipher clientEarlySecret
----------------------------------------------------------------
type PreSharedKeyInfo =
( Maybe ([SessionIDorTicket], SessionData, CipherChoice, Second)
, Maybe CipherChoice
, Bool
)
getPreSharedKeyInfo
:: ClientParams
-> Context
-> IO PreSharedKeyInfo
getPreSharedKeyInfo cparams ctx = do
pskInfo <- getPskInfo
let rtt0info = pskInfo >>= get0RTTinfo
rtt0 = isJust rtt0info
return (pskInfo, rtt0info, rtt0)
where
ciphers = supportedCiphers $ ctxSupported ctx
highestVer = maximum $ supportedVersions $ ctxSupported ctx
tls13 = highestVer >= TLS13
sessions = case clientSessions cparams of
[] -> Nothing
(sid, sdata) : xs -> do
guard tls13
guard (sessionVersion sdata >= TLS13)
let cid = sessionCipher sdata
sids = map fst xs
sCipher <- findCipher cid ciphers
Just (sid : sids, sdata, sCipher)
getPskInfo = case sessions of
Nothing -> return Nothing
Just (identity, sdata, sCipher) -> do
let tinfo = fromJust $ sessionTicketInfo sdata
age <- getAge tinfo
return $
if isAgeValid age tinfo
then
Just
( identity
, sdata
, makeCipherChoice TLS13 sCipher
, ageToObfuscatedAge age tinfo
)
else Nothing
get0RTTinfo (_, sdata, choice, _)
| clientUseEarlyData cparams && sessionMaxEarlyDataSize sdata > 0 = Just choice
| otherwise = Nothing
|