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
|
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
module Network.TLS.Handshake.Server.ServerHello12 (
sendServerHello12,
) where
import Network.TLS.Cipher
import Network.TLS.Compression
import Network.TLS.Context.Internal
import Network.TLS.Credentials
import Network.TLS.Crypto
import Network.TLS.Extension
import Network.TLS.Handshake.Certificate
import Network.TLS.Handshake.Common
import Network.TLS.Handshake.Key
import Network.TLS.Handshake.Random
import Network.TLS.Handshake.Server.Common
import Network.TLS.Handshake.Signature
import Network.TLS.Handshake.State
import Network.TLS.IO
import Network.TLS.Imports
import Network.TLS.Parameters
import Network.TLS.Session
import Network.TLS.State
import Network.TLS.Struct
import Network.TLS.Types
import Network.TLS.X509 hiding (Certificate)
sendServerHello12
:: ServerParams
-> Context
-> (Cipher, Maybe Credential)
-> CH
-> IO (Maybe SessionData)
sendServerHello12 sparams ctx (usedCipher, mcred) ch@CH{..} = do
resumeSessionData <- recoverSessionData ctx ch
case resumeSessionData of
Nothing -> do
serverSession <- newSession ctx
usingState_ ctx $ setSession serverSession
serverhello <-
makeServerHello sparams ctx usedCipher mcred chExtensions serverSession
build <- sendServerFirstFlight sparams ctx usedCipher mcred chExtensions
let ff = serverhello : build [ServerHelloDone]
sendPacket12 ctx $ Handshake ff
contextFlush ctx
Just sessionData -> do
usingState_ ctx $ do
setSession chSession
setTLS12SessionResuming True
serverhello <-
makeServerHello sparams ctx usedCipher mcred chExtensions chSession
sendPacket12 ctx $ Handshake [serverhello]
let mainSecret = sessionSecret sessionData
usingHState ctx $ setMainSecret TLS12 ServerRole mainSecret
logKey ctx $ MainSecret mainSecret
sendCCSandFinished ctx ServerRole
return resumeSessionData
recoverSessionData :: Context -> CH -> IO (Maybe SessionData)
recoverSessionData ctx CH{..} = do
serverName <- usingState_ ctx getClientSNI
ems <- processExtendedMainSecret ctx TLS12 MsgTClientHello chExtensions
let mticket =
lookupAndDecode
EID_SessionTicket
MsgTClientHello
chExtensions
Nothing
(\(SessionTicket ticket) -> Just ticket)
midentity = ticketOrSessionID12 mticket chSession
case midentity of
Nothing -> return Nothing
Just identity -> do
sd <- sessionResume (sharedSessionManager $ ctxShared ctx) identity
validateSession ctx chCiphers serverName ems sd
validateSession
:: Context
-> [CipherId]
-> Maybe HostName
-> Bool
-> Maybe SessionData
-> IO (Maybe SessionData)
validateSession _ _ _ _ Nothing = return Nothing
validateSession ctx ciphers sni ems m@(Just sd)
-- SessionData parameters are assumed to match the local server configuration
-- so we need to compare only to ClientHello inputs. Abbreviated handshake
-- uses the same server_name than full handshake so the same
-- credentials (and thus ciphers) are available.
| TLS12 < sessionVersion sd = return Nothing -- fixme
| CipherId (sessionCipher sd) `notElem` ciphers =
throwCore $
Error_Protocol "new cipher is diffrent from the old one" IllegalParameter
| isJust sni && sessionClientSNI sd /= sni = do
usingState_ ctx clearClientSNI
return Nothing
| ems && not emsSession = return Nothing
| not ems && emsSession =
let err = "client resumes an EMS session without EMS"
in throwCore $ Error_Protocol err HandshakeFailure
| otherwise = return m
where
emsSession = SessionEMS `elem` sessionFlags sd
sendServerFirstFlight
:: ServerParams
-> Context
-> Cipher
-> Maybe Credential
-> [ExtensionRaw]
-> IO ([Handshake] -> [Handshake])
sendServerFirstFlight ServerParams{..} ctx usedCipher mcred chExts = do
let b0 = id
let cc = case mcred of
Just (srvCerts, _) -> srvCerts
_ -> CertificateChain []
let b1 = b0 . (Certificate (TLSCertificateChain cc) :)
usingState_ ctx $ setServerCertificateChain cc
-- send server key exchange if needed
skx <- case cipherKeyExchange usedCipher of
CipherKeyExchange_DH_Anon -> Just <$> generateSKX_DH_Anon
CipherKeyExchange_DHE_RSA -> Just <$> generateSKX_DHE KX_RSA
CipherKeyExchange_DHE_DSA -> Just <$> generateSKX_DHE KX_DSA
CipherKeyExchange_ECDHE_RSA -> Just <$> generateSKX_ECDHE KX_RSA
CipherKeyExchange_ECDHE_ECDSA -> Just <$> generateSKX_ECDHE KX_ECDSA
_ -> return Nothing
let b2 = case skx of
Nothing -> b1
Just kx -> b1 . (ServerKeyXchg kx :)
-- FIXME we don't do this on a Anonymous server
-- When configured, send a certificate request with the DNs of all
-- configured CA certificates.
--
-- Client certificates MUST NOT be accepted if not requested.
--
if serverWantClientCert
then do
let (certTypes, hashSigs) =
let as = supportedHashSignatures serverSupported
in (nub $ mapMaybe hashSigToCertType as, as)
creq =
CertRequest
certTypes
hashSigs
(map extractCAname serverCACertificates)
usingHState ctx $ setCertReqSent True
return $ b2 . (creq :)
else return b2
where
commonGroups = negotiatedGroupsInCommon (supportedGroups serverSupported) chExts
commonHashSigs = hashAndSignaturesInCommon (supportedHashSignatures serverSupported) chExts
setup_DHE = do
let possibleFFGroups = commonGroups `intersect` availableFFGroups
(dhparams, priv, pub) <-
case possibleFFGroups of
[] ->
let dhparams = fromJust serverDHEParams
in case findFiniteFieldGroup dhparams of
Just g -> do
usingHState ctx $ setSupportedGroup g
generateFFDHE ctx g
Nothing -> do
(priv, pub) <- generateDHE ctx dhparams
return (dhparams, priv, pub)
g : _ -> do
usingHState ctx $ setSupportedGroup g
generateFFDHE ctx g
let serverParams = serverDHParamsFrom dhparams pub
usingHState ctx $ setServerDHParams serverParams
usingHState ctx $ setDHPrivate priv
return serverParams
-- Choosing a hash algorithm to sign (EC)DHE parameters
-- in ServerKeyExchange. Hash algorithm is not suggested by
-- the chosen cipher suite. So, it should be selected based on
-- the "signature_algorithms" extension in a client hello.
-- If RSA is also used for key exchange, this function is
-- not called.
decideHashSig pubKey = do
case filter (pubKey `signatureCompatible`) commonHashSigs of
[] -> error ("no hash signature for " ++ pubkeyType pubKey)
x : _ -> return x
generateSKX_DHE kxsAlg = do
serverParams <- setup_DHE
pubKey <- getLocalPublicKey ctx
mhashSig <- decideHashSig pubKey
signed <- digitallySignDHParams ctx serverParams pubKey mhashSig
case kxsAlg of
KX_RSA -> return $ SKX_DHE_RSA serverParams signed
KX_DSA -> return $ SKX_DHE_DSA serverParams signed
_ ->
error ("generate skx_dhe unsupported key exchange signature: " ++ show kxsAlg)
generateSKX_DH_Anon = SKX_DH_Anon <$> setup_DHE
setup_ECDHE grp = do
usingHState ctx $ setSupportedGroup grp
(srvpri, srvpub) <- generateECDHE ctx grp
let serverParams = ServerECDHParams grp srvpub
usingHState ctx $ setServerECDHParams serverParams
usingHState ctx $ setGroupPrivate srvpri
return serverParams
generateSKX_ECDHE kxsAlg = do
let possibleECGroups = commonGroups `intersect` availableECGroups
grp <- case possibleECGroups of
[] -> throwCore $ Error_Protocol "no common group" HandshakeFailure
g : _ -> return g
serverParams <- setup_ECDHE grp
pubKey <- getLocalPublicKey ctx
mhashSig <- decideHashSig pubKey
signed <- digitallySignECDHParams ctx serverParams pubKey mhashSig
case kxsAlg of
KX_RSA -> return $ SKX_ECDHE_RSA serverParams signed
KX_ECDSA -> return $ SKX_ECDHE_ECDSA serverParams signed
_ ->
error ("generate skx_ecdhe unsupported key exchange signature: " ++ show kxsAlg)
---
-- When the client sends a certificate, check whether
-- it is acceptable for the application.
--
---
makeServerHello
:: ServerParams
-> Context
-> Cipher
-> Maybe Credential
-> [ExtensionRaw]
-> Session
-> IO Handshake
makeServerHello sparams ctx usedCipher mcred chExts session = do
resuming <- usingState_ ctx getTLS12SessionResuming
srand <-
serverRandom ctx TLS12 $ supportedVersions $ serverSupported sparams
case mcred of
Just cred -> storePrivInfoServer ctx cred
_ -> return () -- return a sensible error
sniExt <- do
if resuming
then return Nothing
else do
msni <- usingState_ ctx getClientSNI
case msni of
-- RFC6066: In this event, the server SHALL include
-- an extension of type "server_name" in the
-- (extended) server hello. The "extension_data"
-- field of this extension SHALL be empty.
Just _ -> return $ Just $ toExtensionRaw $ ServerName []
Nothing -> return Nothing
let ecPointExt = case extensionLookup EID_EcPointFormats chExts of
Nothing -> Nothing
Just _ -> Just $ toExtensionRaw $ EcPointFormatsSupported [EcPointFormat_Uncompressed]
alpnExt <- applicationProtocol ctx chExts sparams
ems <- usingHState ctx getExtendedMainSecret
let emsExt
| ems = Just $ toExtensionRaw ExtendedMainSecret
| otherwise = Nothing
let useTicket = sessionUseTicket $ sharedSessionManager $ serverShared sparams
sessionTicketExt
| not resuming && useTicket = Just $ toExtensionRaw $ SessionTicket ""
| otherwise = Nothing
-- in TLS12, we need to check as well the certificates we are sending if they have in the extension
-- the necessary bits set.
secReneg <- usingState_ ctx getSecureRenegotiation
secureRenegExt <-
if secReneg
then do
vd <- usingState_ ctx $ do
VerifyData cvd <- getVerifyData ClientRole
VerifyData svd <- getVerifyData ServerRole
return $ SecureRenegotiation cvd svd
return $ Just $ toExtensionRaw vd
else return Nothing
recodeSizeLimitExt <- processRecordSizeLimit ctx chExts False
let shExts =
sharedHelloExtensions (serverShared sparams)
++ catMaybes
[ {- 0x00 -} sniExt
, {- 0x0b -} ecPointExt
, {- 0x10 -} alpnExt
, {- 0x17 -} emsExt
, {- 0x1c -} recodeSizeLimitExt
, {- 0x23 -} sessionTicketExt
, {- 0xff01 -} secureRenegExt
]
usingState_ ctx $ setVersion TLS12
usingHState ctx $
setServerHelloParameters TLS12 srand usedCipher nullCompression
return $
ServerHello
TLS12
srand
session
(CipherId (cipherID usedCipher))
(compressionID nullCompression)
shExts
negotiatedGroupsInCommon :: [Group] -> [ExtensionRaw] -> [Group]
negotiatedGroupsInCommon serverGroups chExts =
lookupAndDecode
EID_SupportedGroups
MsgTClientHello
chExts
[]
common
where
common (SupportedGroups clientGroups) = serverGroups `intersect` clientGroups
|