{-# LANGUAGE OverloadedStrings #-} module Network.TLS.Handshake.Server.Common ( applicationProtocol, checkValidClientCertChain, clientCertificate, credentialDigitalSignatureKey, filterCredentials, filterCredentialsWithHashSignatures, isCredentialAllowed, storePrivInfoServer, ) where import Control.Monad.State.Strict import Data.X509 (ExtKeyUsageFlag (..)) 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.State import Network.TLS.Imports import Network.TLS.Parameters import Network.TLS.State import Network.TLS.Struct import Network.TLS.Util (catchException) import Network.TLS.X509 checkValidClientCertChain :: MonadIO m => Context -> String -> m CertificateChain checkValidClientCertChain ctx errmsg = do chain <- usingHState ctx getClientCertChain let throwerror = Error_Protocol errmsg UnexpectedMessage case chain of Nothing -> throwCore throwerror Just cc | isNullCertificateChain cc -> throwCore throwerror | otherwise -> return cc credentialDigitalSignatureKey :: Credential -> Maybe PubKey credentialDigitalSignatureKey cred | isDigitalSignaturePair keys = Just pubkey | otherwise = Nothing where keys@(pubkey, _) = credentialPublicPrivateKeys cred filterCredentials :: (Credential -> Bool) -> Credentials -> Credentials filterCredentials p (Credentials l) = Credentials (filter p l) isCredentialAllowed :: Version -> [ExtensionRaw] -> Credential -> Bool isCredentialAllowed ver exts cred = pubkey `versionCompatible` ver && satisfiesEcPredicate p pubkey where (pubkey, _) = credentialPublicPrivateKeys cred -- ECDSA keys are tested against supported elliptic curves until TLS12 but -- not after. With TLS13, the curve is linked to the signature algorithm -- and client support is tested with signatureCompatible13. p | ver < TLS13 = case extensionLookup EID_SupportedGroups exts >>= extensionDecode MsgTClientHello of Nothing -> const True Just (SupportedGroups sg) -> (`elem` sg) | otherwise = const True -- Filters a list of candidate credentials with credentialMatchesHashSignatures. -- -- Algorithms to filter with are taken from "signature_algorithms_cert" -- extension when it exists, else from "signature_algorithms" when clients do -- not implement the new extension (see RFC 8446 section 4.2.3). -- -- Resulting credential list can be used as input to the hybrid cipher-and- -- certificate selection for TLS12, or to the direct certificate selection -- simplified with TLS13. As filtering credential signatures with client- -- advertised algorithms is not supposed to cause negotiation failure, in case -- of dead end with the subsequent selection process, this process should always -- be restarted with the unfiltered credential list as input (see fallback -- certificate chains, described in same RFC section). -- -- Calling code should not forget to apply constraints of extension -- "signature_algorithms" to any signature-based key exchange derived from the -- output credentials. Respecting client constraints on KX signatures is -- mandatory but not implemented by this function. filterCredentialsWithHashSignatures :: [ExtensionRaw] -> Credentials -> Credentials filterCredentialsWithHashSignatures exts = case withExt EID_SignatureAlgorithmsCert of Just (SignatureAlgorithmsCert sas) -> withAlgs sas Nothing -> case withExt EID_SignatureAlgorithms of Nothing -> id Just (SignatureAlgorithms sas) -> withAlgs sas where withExt extId = extensionLookup extId exts >>= extensionDecode MsgTClientHello withAlgs sas = filterCredentials (credentialMatchesHashSignatures sas) storePrivInfoServer :: MonadIO m => Context -> Credential -> m () storePrivInfoServer ctx (cc, privkey) = void (storePrivInfo ctx cc privkey) applicationProtocol :: Context -> [ExtensionRaw] -> ServerParams -> IO [ExtensionRaw] applicationProtocol ctx exts sparams = do -- ALPN (Application Layer Protocol Negotiation) case extensionLookup EID_ApplicationLayerProtocolNegotiation exts >>= extensionDecode MsgTClientHello of Nothing -> return [] Just (ApplicationLayerProtocolNegotiation protos) -> do case onALPNClientSuggest $ serverHooks sparams of Just io -> do proto <- io protos when (proto == "") $ throwCore $ Error_Protocol "no supported application protocols" NoApplicationProtocol usingState_ ctx $ do setExtensionALPN True setNegotiatedProtocol proto return [ ExtensionRaw EID_ApplicationLayerProtocolNegotiation (extensionEncode $ ApplicationLayerProtocolNegotiation [proto]) ] _ -> return [] clientCertificate :: ServerParams -> Context -> CertificateChain -> IO () clientCertificate sparams ctx certs = do -- run certificate recv hook ctxWithHooks ctx (`hookRecvCertificates` certs) -- Call application callback to see whether the -- certificate chain is acceptable. -- usage <- liftIO $ catchException (onClientCertificate (serverHooks sparams) certs) rejectOnException case usage of CertificateUsageAccept -> verifyLeafKeyUsage [KeyUsage_digitalSignature] certs CertificateUsageReject reason -> certificateRejected reason -- Remember cert chain for later use. -- usingHState ctx $ setClientCertChain certs