packages feed

tls 1.4.1 → 2.4.3

raw patch · 120 files changed

Files

Benchmarks/Benchmarks.hs view
@@ -1,99 +1,118 @@ {-# LANGUAGE BangPatterns #-}+ module Main where -import Connection import Certificate-import PubKey-import Criterion.Main import Control.Concurrent.Chan-import Network.TLS+import Data.Default (def)+import Data.IORef import Data.X509 import Data.X509.Validation-import Data.Default.Class-import Data.IORef+import Test.Tasty.Bench+import Network.TLS+import Network.TLS.Extra.Cipher+import Session+import Run+import PubKey  import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as L  blockCipher :: Cipher-blockCipher = Cipher-    { cipherID   = 0xff12-    , cipherName = "rsa-id-const"-    , cipherBulk = Bulk-        { bulkName      = "id"-        , bulkKeySize   = 16-        , bulkIVSize    = 16-        , bulkExplicitIV= 0-        , bulkAuthTagLen= 0-        , bulkBlockSize = 16-        , bulkF         = BulkBlockF $ \_ _ _ -> (\m -> (m, B.empty))+blockCipher =+    Cipher+        { cipherID = 0xff12+        , cipherName = "rsa-id-const"+        , cipherBulk =+            Bulk+                { bulkName = "id"+                , bulkKeySize = 16+                , bulkIVSize = 16+                , bulkExplicitIV = 0+                , bulkAuthTagLen = 0+                , bulkBlockSize = 16+                , bulkF = BulkBlockF $ \_ _ _ m -> (m, B.empty)+                }+        , cipherHash = MD5+        , cipherPRFHash = Nothing+        , cipherKeyExchange = CipherKeyExchange_RSA+        , cipherMinVer = Nothing         }-    , cipherHash        = MD5-    , cipherPRFHash     = Nothing-    , cipherKeyExchange = CipherKeyExchange_RSA-    , cipherMinVer      = Nothing-    } -recvDataNonNull :: Context -> IO B.ByteString-recvDataNonNull ctx = recvData ctx >>= \l -> if B.null l then recvDataNonNull ctx else return l- getParams :: Version -> Cipher -> (ClientParams, ServerParams) getParams connectVer cipher = (cParams, sParams)-  where sParams = def { serverSupported = supported-                      , serverShared = def {-                          sharedCredentials = Credentials [ (CertificateChain [simpleX509 $ PubKeyRSA pubKey], PrivKeyRSA privKey) ]-                          }-                      }-        cParams = (defaultParamsClient "" B.empty)+  where+    sParams =+        def+            { serverSupported = supported+            , serverShared =+                def+                    { sharedCredentials =+                        Credentials+                            [(CertificateChain [simpleX509 $ PubKeyRSA pubKey], PrivKeyRSA privKey)]+                    }+            }+    cParams =+        (defaultParamsClient "" B.empty)             { clientSupported = supported-            , clientShared = def { sharedValidationCache = ValidationCache-                                        { cacheAdd = \_ _ _ -> return ()-                                        , cacheQuery = \_ _ _ -> return ValidationCachePass-                                        }-                                 }+            , clientShared =+                def+                    { sharedValidationCache =+                        ValidationCache+                            { cacheAdd = \_ _ _ -> return ()+                            , cacheQuery = \_ _ _ -> return ValidationCachePass+                            }+                    }             }-        supported = def { supportedCiphers = [cipher]-                        , supportedVersions = [connectVer]-                        }-        (pubKey, privKey) = getGlobalRSAPair+    supported =+        def+            { supportedCiphers = [cipher]+            , supportedVersions = [connectVer]+            , supportedGroups = [X25519, FFDHE2048]+            }+    (pubKey, privKey) = getGlobalRSAPair -runTLSPipe :: (ClientParams, ServerParams)-           -> (Context -> Chan b -> IO ())-           -> (Chan a -> Context -> IO ())-           -> a-           -> IO b+runTLSPipe+    :: (ClientParams, ServerParams)+    -> (Context -> Chan b -> IO ())+    -> (Chan a -> Context -> IO ())+    -> a+    -> IO b runTLSPipe params tlsServer tlsClient d = do-    (startQueue, resultQueue) <- establishDataPipe params tlsServer tlsClient-    writeChan startQueue d-    readChan resultQueue+    withDataPipe params tlsServer tlsClient $ \(writeStart, readResult) -> do+        writeStart d+        readResult -runTLSPipeSimple :: (ClientParams, ServerParams) -> B.ByteString -> IO B.ByteString-runTLSPipeSimple params bs = runTLSPipe params tlsServer tlsClient bs-  where tlsServer ctx queue = do-            handshake ctx-            d <- recvDataNonNull ctx-            writeChan queue d-            return ()-        tlsClient queue ctx = do-            handshake ctx-            d <- readChan queue-            sendData ctx (L.fromChunks [d])-            bye ctx-            return ()+runTLSPipeSimple+    :: (ClientParams, ServerParams) -> B.ByteString -> IO B.ByteString+runTLSPipeSimple params = runTLSPipe params tlsServer tlsClient+  where+    tlsServer ctx queue = do+        handshake ctx+        d <- recvData ctx+        writeChan queue d+        bye ctx+    tlsClient queue ctx = do+        handshake ctx+        d <- readChan queue+        sendData ctx (L.fromChunks [d])+        byeBye ctx -benchConnection :: (ClientParams, ServerParams) -> B.ByteString -> String -> Benchmark+benchConnection+    :: (ClientParams, ServerParams) -> B.ByteString -> String -> Benchmark benchConnection params !d name = bench name . nfIO $ runTLSPipeSimple params d -benchResumption :: (ClientParams, ServerParams) -> B.ByteString -> String -> Benchmark+benchResumption+    :: (ClientParams, ServerParams) -> B.ByteString -> String -> Benchmark benchResumption params !d name = env initializeSession runResumption   where     initializeSession = do-        sessionRef <- newIORef Nothing-        let sessionManager = oneSessionManager sessionRef-            params1 = setPairParamsSessionManager sessionManager params+        sessionRefs <- twoSessionRefs+        let sessionManagers = twoSessionManagers sessionRefs+            params1 = setPairParamsSessionManagers sessionManagers params         _ <- runTLSPipeSimple params1 d -        Just sessionParams <- readIORef sessionRef+        Just sessionParams <- readClientSessionRef sessionRefs         let params2 = setPairParamsSessionResuming sessionParams params1         newIORef params2 @@ -101,19 +120,68 @@         params2 <- readIORef paramsRef         runTLSPipeSimple params2 d +benchResumption13+    :: (ClientParams, ServerParams) -> B.ByteString -> String -> Benchmark+benchResumption13 params !d name = env initializeSession runResumption+  where+    initializeSession = do+        sessionRefs <- twoSessionRefs+        let sessionManagers = twoSessionManagers sessionRefs+            params1 = setPairParamsSessionManagers sessionManagers params+        _ <- runTLSPipeSimple params1 d+        newIORef (params1, sessionRefs)++    -- with TLS13 the sessionId is constantly changing so we must update+    -- our parameters at each iteration unfortunately+    runResumption paramsRef = bench name . nfIO $ do+        (params1, sessionRefs) <- readIORef paramsRef+        Just sessionParams <- readClientSessionRef sessionRefs+        let params2 = setPairParamsSessionResuming sessionParams params1+        runTLSPipeSimple params2 d++benchCiphers :: String -> Version -> B.ByteString -> [Cipher] -> Benchmark+benchCiphers name connectVer d = bgroup name . map doBench+  where+    doBench cipher =+        benchResumption13 (getParams connectVer cipher) d (cipherName cipher)+ main :: IO ()-main = defaultMain-    [ bgroup "connection"-        -- not sure the number actually make sense for anything. improve ..-        [ benchConnection (getParams SSL3 blockCipher) (B.replicate 256 0) "SSL3-256 bytes"-        , benchConnection (getParams TLS10 blockCipher) (B.replicate 256 0) "TLS10-256 bytes"-        , benchConnection (getParams TLS11 blockCipher) (B.replicate 256 0) "TLS11-256 bytes"-        , benchConnection (getParams TLS12 blockCipher) (B.replicate 256 0) "TLS12-256 bytes"-        ]-    , bgroup "resumption"-        [ benchResumption (getParams SSL3 blockCipher) (B.replicate 256 0) "SSL3-256 bytes"-        , benchResumption (getParams TLS10 blockCipher) (B.replicate 256 0) "TLS10-256 bytes"-        , benchResumption (getParams TLS11 blockCipher) (B.replicate 256 0) "TLS11-256 bytes"-        , benchResumption (getParams TLS12 blockCipher) (B.replicate 256 0) "TLS12-256 bytes"+main =+    defaultMain+        [ bgroup+            "connection"+            -- not sure the number actually make sense for anything. improve ..+            [ benchConnection (getParams TLS12 blockCipher) small "TLS12-256 bytes"+            ]+        , bgroup+            "resumption"+            [ benchResumption (getParams TLS12 blockCipher) small "TLS12-256 bytes"+            ]+        , -- Here we try to measure TLS12 and TLS13 performance with AEAD ciphers.+          -- Resumption and a larger message can be a demonstration of the symmetric+          -- crypto but for TLS13 this does not work so well because of dhe_psk.+          benchCiphers+            "TLS12"+            TLS12+            large+            [ cipher_DHE_RSA_WITH_AES_128_GCM_SHA256+            , cipher_DHE_RSA_WITH_AES_256_GCM_SHA384+            , cipher_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256+            , cipher_ECDHE_RSA_WITH_AES_128_GCM_SHA256+            , cipher_ECDHE_RSA_WITH_AES_256_GCM_SHA384+            , cipher_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256+            ]+        , benchCiphers+            "TLS13"+            TLS13+            large+            [ cipher13_AES_128_GCM_SHA256+            , cipher13_AES_256_GCM_SHA384+            , cipher13_CHACHA20_POLY1305_SHA256+            , cipher13_AES_128_CCM_SHA256+            , cipher13_AES_128_CCM_8_SHA256+            ]         ]-    ]+  where+    small = B.replicate 256 0+    large = B.replicate 102400 0
CHANGELOG.md view
@@ -1,7 +1,390 @@+# Change log for "tls"++## Version 2.4.3++* A server checks clientAuth of ExtendedKeyUsage in a client+  certificate on client authentication.++## Version 2.4.2++* The `Network.TLS.Extra.CipherCBC` module is added.+  [#526](https://github.com/haskell-tls/hs-tls/pull/526)++## Version 2.4.1++* Ensure same `supported_groups` before/after HRR.+* New `clientWantTicket` parameter makes it possible to opt-out of soliciting+  session tickets from servers.++## Version 2.4.0++* Identical to v2.3.1 but major version up as v2.3.1 breaks "quic".++## Version 2.3.1 (deprecated)++* Using ScrubbedBytes for secrets.+* Key echange with ML-KEM.+  [#517](https://github.com/haskell-tls/hs-tls/pull/517)+* Expose get certificate chain function+  [#520](https://github.com/haskell-tls/hs-tls/pull/520)++## Version 2.3.0++* Using "ram" instead of "memory".++## Version 2.2.2++* A new architecture to calculate receiver's transcript hash with wire+  format.+  [#515](https://github.com/haskell-tls/hs-tls/pull/515)+* Enabling compressed certificate on the client side again.++## Version 2.2.1++* Disabling compressed certificate on the client side.+  [#514](https://github.com/haskell-tls/hs-tls/issues/514)++## Version 2.2.0++* Using crypton-asn1-* and time-hourglass.+  [#512](https://github.com/haskell-tls/hs-tls/pull/512)+* Major version up due to re-exports.++## Version 2.1.14++* Supporting P384 and P521 curves.+  [#511](https://github.com/haskell-tls/hs-tls/pull/511)+* Fixing some bugs of `tls-client`.++## Version 2.1.13++* Don't contain early_data if serverEarlyDataSize is 0.+  [#510](https://github.com/haskell-tls/hs-tls/pull/510)++## Version 2.1.12++* Restore benchmarks.+  [#509](https://github.com/haskell-tls/hs-tls/pull/509)+* Supporting random 1.2.+  [#508](https://github.com/haskell-tls/hs-tls/pull/508)+* Add --trusted-anchor cli option to tls-client.+  [#505](https://github.com/haskell-tls/hs-tls/pull/505)++## Version 2.1.11++* Removing OVERLAPS instances.++## Version 2.1.10++* Supporting the SSLKEYLOGFILE environment variable.+  [#499](https://github.com/haskell-tls/hs-tls/pull/499)++## Version 2.1.9++* Providing ECH(Encrypted Client Hello). See `sharedECHConfigList`,+  `clientUseECH` and `serverECHKey`. Note that the `ech-gen` command,+  `loadECHConfigList` and `loadECHSecretKeys` are provided by the+  `ech-config` package.++## Version 2.1.8++* Moving `Limit` to `Shared` to maintain backward compatibility+  of `TLSParams` class.+* Deprecating 2.1.7.++## Version 2.1.7++* Introducing `Limit` parameter.+* Implementing "Record Size Limit Extension for TLS" (RFC8449).+  Set `limitRecordSize` use it.+* Implementing "TLS Certificate Compression" (RFC 8879).+  This feature is automatically used if the peer supports it.+* More tests with `tlsfuzzer` especially for client authentication+  and 0-RTT.+* Implementing a utility function, `validateClientCertificate`, for+  client authentication.+* Bug fix for echo back logic of Cookie extension.+* More pretty show for the internal `Handshake` structure for debugging.++## Version 2.1.6++* Testing with "tlsfuzzer" again. Now don't send an alert against to+  peer's alert. Double locking (aka self dead-lock) is fixed. Sending+  an alert for known-but-cannot-parse extensions. Other corner cases+  are also fixed.+* `tls-client -d` and `tls-server -d` pretty-prints `Handshake`.++## Version 2.1.5++* Removing the dependency on the async package.+* Restore a few DHE_RSA ciphers.+  [#493](https://github.com/haskell-tls/hs-tls/pull/493)++## Version 2.1.4++* Exporting defaultValidationCache.++## Version 2.1.3++* Remove `data-default` version constraint.+  [#492](https://github.com/haskell-tls/hs-tls/pull/492)+* Exporting default variables.+  [#448](https://github.com/haskell-tls/hs-tls/pull/488)++## Version 2.1.2++* Using data-default instead of data-default-class.++## Version 2.1.1++* `bye` directly calls `timeout recvHS13`, not spawning a thread for+  `timeout recvHS13`. So, `bye` can receive an exception if thrown.++## Version 2.1.0++* Breaking change: stop exporting constructors to maintain future+  compatibilities. Field names are still exported, and values can be updated+  with them using record syntax. Use `def` and `noSessionManager` as initial+  values.+* `onServerFinished` is added to `ClientHooks`.+* `clientWantSessionResumeList` is added to `ClientParams` to support+  multiple tickets for TLS 1.3.++## Version 2.0.6++* Setting `supportedCiphers` in `defaultSupported` to `ciphersuite_default`.+  So, users don't have to override this value anymore by exporting+  `Network.TLS.Extra.Cipher`.+  [#471](https://github.com/haskell-tls/hs-tls/pull/471)+* `ciphersuite_default` is the same as `ciphersuite_strong`.+  So, the duplicated definition is removed.+* Add missing modules for util/tls-client and util/tls-server.++## Version 2.0.5++* Fixing handshake13_0rtt_fallback+* Client checks if the group of PSK is contained in Supported_Groups.+* HRR is not allowed for 0-RTT.++## Version 2.0.4++* More fix for 0-RTT when application data is available while receiving CF.+* New util/tls-client and util/tls-server.++## Version 2.0.3++* Fixing a bug where `timeout` in `bye` does not work.+* util/client -> util/tls-client+* util/server -> util/tls-server++## Version 2.0.2++* Client checks sessionMaxEarlyDataSize to decide 0-RTT+* Client checks the resumption cipher properly.++## Version 2.0.1++* Fix a leak of pending data to be sent.++## Version 2.0.0++* `tls` now only supports TLS 1.2 and TLS 1.3 with safe cipher suites.+* Security: BREAKING CHANGE: TLS 1.0 and TLS 1.1 are removed.+* Security: BREAKING CHANGE: all CBC cipher suite are removed.+* Security: BREAKING CHANGE: RC4 and 3DES are removed.+* Security: BREAKING CHANGE: DSS(digital signature standard) is removed.+* Security: BREAKING CHANGE: TLS 1.2 servers require+  EMS(extended main secret) by default.+  `supportedExtendedMasterSec` is renamed to+  `supportedExtendedMainSecret`.+* BREAKING CHANGE: the package is now complied with `Strict` and `StrictData`.+* BREAKING CHANGE: Many data structures are re-defined with+ `PatternSynonyms` for extensibility.+* BREAKING CHANGE: the structure of `SessionManager` is changed+  to support session tickets.+* API: BREAKING CHANGE: `sendData` can send early data (0-RTT).+  `clientEarlyData` is removed.+  To send early data via `sendData`, set `clientUseEarlyData` to `True`.+  [#466](https://github.com/haskell-tls/hs-tls/issues/466)+* API: `handshake` can receive an alert of client authentication failure+  for TLS 1.3.+  [#463](https://github.com/haskell-tls/hs-tls/pull/463)+* API: `bye` can receive NewSessionTicket for TLS 1.3.+* Channel binding: `getFinished` and `getPeerFinished` are deprecated.+  Use `getTLSUnique` instead.+  [#462](https://github.com/haskell-tls/hs-tls/pull/462)+* Channel binding: `getTLSExporter` and `getTLSServerEndPoint` are provided.+  [#462](https://github.com/haskell-tls/hs-tls/pull/462)+* Refactoring: the monolithic `handshake` is divided to follow+  the diagram of TLS 1.2 and 1.3 for readability.+* Refactoring: test cases are refactored for maintenability+  and readablity. `hspec` is used instead of `tasty`.+* Code format: `fourmolu` is used as an official formatter.+* Catching up RFC8446bis-09.+  [#467](https://github.com/haskell-tls/hs-tls/issues/467)++## Version 1.9.0++* BREAKING CHANGE: The type of the `Error_Protocol` constructor of `TLSError` has changed.+  The "warning" case has been split off into a new `Error_Protocol_Warning` constructor.+  [#460](https://github.com/haskell-tls/hs-tls/pull/460)++## Version 1.8.0++* BREAKING CHANGE: Remove `Exception` instance for `TLSError`.+  The library now throws `TLSException` only.+  If you need to change your code, please refer to+  [this example](https://github.com/snoyberg/http-client/commit/73d1a4eb451c089878ba95e96371d0b18287ffb8) first.+  [#457](https://github.com/haskell-tls/hs-tls/pull/457)++## Version 1.7.1++* NOP on UserCanceled event+  [#454](https://github.com/haskell-tls/hs-tls/pull/454)++## Version 1.7.0++* Major version up because "crypton" is used instead of "cryptonite"++## Version 1.6.0++- Major version up because of disabling SSL3+- Some fixes against tlsfuzzer++## Version 1.5.8++- Require mtl-2.2.1 or newer+  [#448](https://github.com/haskell-tls/hs-tls/pull/448)++## Version 1.5.7++- New APIs: getFinished and getPeerFinished+  [#445](https://github.com/vincenthz/hs-tls/pull/445)++## Version 1.5.6++- Dynamically setting enctypted extensions+  [#444](https://github.com/vincenthz/hs-tls/pull/444)++## Version 1.5.5++- QUIC support+  [#419](https://github.com/vincenthz/hs-tls/pull/419)+  [#427](https://github.com/vincenthz/hs-tls/pull/427)+  [#428](https://github.com/vincenthz/hs-tls/pull/428)+  [#430](https://github.com/vincenthz/hs-tls/pull/430)+  [#433](https://github.com/vincenthz/hs-tls/pull/433)+  [#441](https://github.com/vincenthz/hs-tls/pull/441)+- Server ECDSA for P-256+  [#436](https://github.com/vincenthz/hs-tls/pull/436)+- Sort ciphersuites based on hardware-acceleration support+  [#439](https://github.com/vincenthz/hs-tls/pull/439)+- Sending no_application_protocol+  [#440](https://github.com/vincenthz/hs-tls/pull/440)+- Internal improvements+  [#426](https://github.com/vincenthz/hs-tls/pull/426)+  [#431](https://github.com/vincenthz/hs-tls/pull/431)++## Version 1.5.4++- Restore interoperability with early Java 6+  [#422](https://github.com/vincenthz/hs-tls/pull/422)+- Test cleanups for timeout and async usage+  [#416](https://github.com/vincenthz/hs-tls/pull/416)++## Version 1.5.3++- Additional verification regarding EC signatures+  [#412](https://github.com/vincenthz/hs-tls/pull/412)+- Fixing ALPN+  [#411](https://github.com/vincenthz/hs-tls/pull/411)+- Check SSLv3 padding length+  [#410](https://github.com/vincenthz/hs-tls/pull/410)+- Exposing getClientCertificateChain+  [#407](https://github.com/vincenthz/hs-tls/pull/407)+- Extended Master Secret+  [#406](https://github.com/vincenthz/hs-tls/pull/406)+- Brushing up the documentation+  [#404](https://github.com/vincenthz/hs-tls/pull/404)+  [#408](https://github.com/vincenthz/hs-tls/pull/408)+- Improving tests+  [#403](https://github.com/vincenthz/hs-tls/pull/403)+- Avoid calling onServerNameIndication twice with HRR+  [#402](https://github.com/vincenthz/hs-tls/pull/402)+- Enable X448 and FFDHE groups+  [#401](https://github.com/vincenthz/hs-tls/pull/401)+- Refactoring+  [#400](https://github.com/vincenthz/hs-tls/pull/400)+  [#399](https://github.com/vincenthz/hs-tls/pull/399)++## Version 1.5.2++- Enabled TLS 1.3 by default [#398](https://github.com/vincenthz/hs-tls/pull/398)+- Avoid handshake failure with small RSA keys [#394](https://github.com/vincenthz/hs-tls/pull/394)++NOTES:++- Starting with tls-1.5.0, the parameter `supportedVersions` contains values+  ordered by decreasing preference, so typically the higher versions first.+  This departs from code samples previously available.  For maximum+  interoperability, users overriding the default value should verify and adapt+  their code.++## Version 1.5.1++- Post-handshake authentication [#363](https://github.com/vincenthz/hs-tls/pull/363)+- Middlebox compatibility [#386](https://github.com/vincenthz/hs-tls/pull/386)+- Verification and configuration of session-ticket lifetime [#373](https://github.com/vincenthz/hs-tls/pull/373)+- Fixing memory leak [#366](https://github.com/vincenthz/hs-tls/pull/366)+- Don't send 0-RTT data when ticket is expired [#370](https://github.com/vincenthz/hs-tls/pull/370)+- Handshake packet fragmentation [#371](https://github.com/vincenthz/hs-tls/pull/371)+- Fix SSLv2 deprecated header [#383](https://github.com/vincenthz/hs-tls/pull/383)+- Other improvements to TLS 1.3 and RFC conformance [#368](https://github.com/vincenthz/hs-tls/pull/368) [#372](https://github.com/vincenthz/hs-tls/pull/372) [#375](https://github.com/vincenthz/hs-tls/pull/375) [#376](https://github.com/vincenthz/hs-tls/pull/376) [#377](https://github.com/vincenthz/hs-tls/pull/377) [#378](https://github.com/vincenthz/hs-tls/pull/378) [#380](https://github.com/vincenthz/hs-tls/pull/380) [#382](https://github.com/vincenthz/hs-tls/pull/382) [#385](https://github.com/vincenthz/hs-tls/pull/385) [#387](https://github.com/vincenthz/hs-tls/pull/387) [#388](https://github.com/vincenthz/hs-tls/pull/388)++## Version 1.5.0++- Add and enable AES CCM ciphers [#271](https://github.com/vincenthz/hs-tls/pull/271) [#287](https://github.com/vincenthz/hs-tls/pull/287)+- Verify certificate key usage [#274](https://github.com/vincenthz/hs-tls/pull/274) [#301](https://github.com/vincenthz/hs-tls/pull/301)+- TLS 1.3 support [#278](https://github.com/vincenthz/hs-tls/pull/278) [#279](https://github.com/vincenthz/hs-tls/pull/279) [#280](https://github.com/vincenthz/hs-tls/pull/280) [#283](https://github.com/vincenthz/hs-tls/pull/283) [#298](https://github.com/vincenthz/hs-tls/pull/298) [#331](https://github.com/vincenthz/hs-tls/pull/331) [#290](https://github.com/vincenthz/hs-tls/pull/290) [#314](https://github.com/vincenthz/hs-tls/pull/314)+- Enable RSASSA-PSS [#280](https://github.com/vincenthz/hs-tls/pull/280) [#353](https://github.com/vincenthz/hs-tls/pull/353)+- Add and enable ChaCha20-Poly1305 ciphers [#287](https://github.com/vincenthz/hs-tls/pull/287) [#340](https://github.com/vincenthz/hs-tls/pull/340)+- Certificate selection with extension "signature_algorithms_cert" [#302](https://github.com/vincenthz/hs-tls/pull/302)+- Preventing Logjam attack [#300](https://github.com/vincenthz/hs-tls/pull/300)+- Downgrade protection [#308](https://github.com/vincenthz/hs-tls/pull/308)+- Support for EdDSA certificates [#328](https://github.com/vincenthz/hs-tls/pull/328) [#353](https://github.com/vincenthz/hs-tls/pull/353)+- Key logging [#317](https://github.com/vincenthz/hs-tls/pull/317)+- Thread safety for writes [#329](https://github.com/vincenthz/hs-tls/pull/329)+- Verify signature schemes and (EC)DHE groups received [#337](https://github.com/vincenthz/hs-tls/pull/337) [#338](https://github.com/vincenthz/hs-tls/pull/338)+- Throw BadRecordMac when the decrypted record has invalid format [#347](https://github.com/vincenthz/hs-tls/pull/347)+- Improve documentation format [#341](https://github.com/vincenthz/hs-tls/pull/341) [#343](https://github.com/vincenthz/hs-tls/pull/343)+- Fix recvClientData with single Handshake packet [#352](https://github.com/vincenthz/hs-tls/pull/352)+- Decrease memory footprint of SessionData values [#354](https://github.com/vincenthz/hs-tls/pull/354)++FEATURES:++- TLS version 1.3 is available with most features but is not enabled by default.+  One notable omission is post-handshake authentication.  Scenarios where+  servers previously used renegotiation to conditionally request a certificate+  are not possible yet when `TLS13` is negotiated.  Users may enable the version+  in `supportedVersions` only when sure post-handshake authentication is not+  required.++API CHANGES:++- `SessionManager` implementations need to provide a `sessionResumeOnlyOnce`+  function to accommodate resumption scenarios with 0-RTT data.  The function is+  called only on the server side.+- Data type `SessionData` is extended with four new fields for TLS version 1.3.+  `SessionManager` implementations that serializes/deserializes `SessionData`+  values must deal with the new fields.+- New configuration parameters and constructors are added for TLS version 1.3+  but the API change should be backward compatible for most use-cases.+- Function `cipherExchangeNeedMoreData` has been removed.+ ## Version 1.4.1  - Enable X25519 in default parameters [#265](https://github.com/vincenthz/hs-tls/pull/265)-- Checking EOF in bye [#262] (https://github.com/vincenthz/hs-tls/pull/262)+- Checking EOF in bye [#262](https://github.com/vincenthz/hs-tls/pull/262) - Improving validation in DH key exchange [#256](https://github.com/vincenthz/hs-tls/pull/256) - Handle TCP reset during handshake [#251](https://github.com/vincenthz/hs-tls/pull/251) - Accepting hlint suggestions.
Network/TLS.hs view
@@ -1,159 +1,377 @@-{-# LANGUAGE CPP #-} -- |--- Module      : Network.TLS--- License     : BSD-style--- Maintainer  : Vincent Hanquez <vincent@snarc.org>--- Stability   : experimental--- Portability : unknown+-- Native Haskell TLS protocol implementation for servers and+-- clients. ---module Network.TLS-    (-    -- * Context configuration-      ClientParams(..)-    , HostName-    , Bytes-    , ServerParams(..)-    , DebugParams(..)-    , DHParams-    , DHPublic-    , ClientHooks(..)-    , ServerHooks(..)-    , Supported(..)-    , Shared(..)-    , Hooks(..)-    , Handshake-    , Logging(..)-    , Measurement(..)-    , GroupUsage(..)-    , CertificateUsage(..)-    , CertificateRejectReason(..)-    , defaultParamsClient-    , MaxFragmentEnum(..)-    , HashAndSignatureAlgorithm-    , HashAlgorithm(..)-    , SignatureAlgorithm(..)-    , CertificateType(..)--    -- * raw types-    , ProtocolType(..)-    , Header(..)+-- This provides a high-level implementation of a sensitive security+-- protocol, eliminating a common set of security issues through the+-- use of the advanced type system, high level constructions and+-- common Haskell features.+--+-- Currently implement the TLS1.2 and TLS 1.3+-- protocol, and support RSA and Ephemeral (Elliptic curve and+-- regular) Diffie Hellman key exchanges, and many extensions.+--+-- The typical usage is:+--+-- > socket <- ...+-- > ctx <- contextNew socket <params>+-- > handshake ctx+-- > ... (using recvData and sendData)+-- > bye+module Network.TLS (+    -- * Basic APIs+    Context,+    contextNew,+    handshake,+    sendData,+    recvData,+    bye, -    -- * Session-    , SessionID-    , SessionData(..)-    , SessionManager(..)-    , noSessionManager+    -- * Exceptions+    -- $exceptions      -- * Backend abstraction-    , Backend(..)+    HasBackend (..),+    Backend (..), -    -- * Context object-    , Context-    , ctxConnection-    , TLSParams-    , HasBackend(..)+    -- * Parameters -    -- * Creating a context-    , contextNew-    , contextNewOnHandle-#ifdef INCLUDE_NETWORK-    , contextNewOnSocket-#endif-    , contextFlush-    , contextClose-    , contextHookSetHandshakeRecv-    , contextHookSetCertificateRecv-    , contextHookSetLogging-    , contextModifyHooks+    -- intentionally hide the internal methods even haddock warns.+    TLSParams, -    -- * Information gathering-    , Information(..)-    , ClientRandom-    , ServerRandom+    -- ** Client parameters+    ClientParams,+    defaultParamsClient,+    clientServerIdentification,+    clientUseServerNameIndication,+    clientWantSessionResume,+    clientWantSessionResumeList,+    clientWantTicket,+    clientShared,+    clientHooks,+    clientSupported,+    clientDebug,+    clientUseEarlyData,+    clientUseECH, -    , unClientRandom-    , unServerRandom-    , contextGetInformation+    -- ** Server parameters+    ServerParams,+    defaultParamsServer,+    serverWantClientCert,+    serverCACertificates,+    serverDHEParams,+    serverHooks,+    serverShared,+    serverSupported,+    serverDebug,+    serverEarlyDataSize,+    serverTicketLifetime,+    serverECHKey, -    -- * Credentials-    , Credentials(..)-    , Credential-    , credentialLoadX509-    , credentialLoadX509FromMemory-    , credentialLoadX509Chain-    , credentialLoadX509ChainFromMemory+    -- ** Shared+    Shared,+    defaultShared,+    sharedCredentials,+    sharedSessionManager,+    sharedCAStore,+    sharedValidationCache,+    sharedHelloExtensions,+    sharedECHConfigList,+    sharedLimit, -    -- * Initialisation and Termination of context-    , bye-    , handshake+    -- ** Client hooks+    ClientHooks,+    defaultClientHooks,+    OnCertificateRequest,+    onCertificateRequest,+    OnServerCertificate,+    onServerCertificate,+    onSuggestALPN,+    onCustomFFDHEGroup,+    onServerFinished,+    onSelectKeyShareGroups, -    -- * Application Layer Protocol Negotiation-    , getNegotiatedProtocol+    -- ** Server hooks+    ServerHooks,+    defaultServerHooks,+    onClientCertificate,+    validateClientCertificate,+    onUnverifiedClientCert,+    onCipherChoosing,+    onServerNameIndication,+    onNewHandshake,+    onALPNClientSuggest,+    onEncryptedExtensionsCreating,+    onSelectKeyShare,+    Measurement,+    nbHandshakes,+    bytesReceived,+    bytesSent, -    -- * Server Name Indication-    , getClientSNI+    -- ** Supported+    Supported,+    defaultSupported,+    supportedVersions,+    supportedCiphers,+    supportedCompressions,+    supportedHashSignatures,+    supportedSecureRenegotiation,+    supportedClientInitiatedRenegotiation,+    supportedExtendedMainSecret,+    supportedSession,+    supportedFallbackScsv,+    supportedEmptyPacket,+    supportedGroups,+    supportedGroupsTLS13, -    -- * High level API-    , sendData-    , recvData-    , recvData'+    -- ** Debug parameters+    DebugParams,+    defaultDebugParams,+    debugSeed,+    debugPrintSeed,+    debugVersionForced,+    debugKeyLogger,+    defaultKeyLogger,+    debugError,+    debugTraceKey, -    -- * Crypto Key-    , PubKey(..)-    , PrivKey(..)+    -- ** Limit parameters+    Limit,+    defaultLimit,+    limitHandshakeFragment,+    limitRecordSize, -    -- * Compressions & Predefined compressions-    , module Network.TLS.Compression+    -- * Shared parameters -    -- * Ciphers & Predefined ciphers-    , module Network.TLS.Cipher+    -- ** Credentials+    Credentials (..),+    Credential,+    credentialLoadX509,+    credentialLoadX509FromMemory,+    credentialLoadX509Chain,+    credentialLoadX509ChainFromMemory, -    -- * Versions-    , Version(..)+    -- ** Session manager+    SessionManager,+    noSessionManager,+    sessionResume,+    sessionResumeOnlyOnce,+    sessionEstablish,+    sessionInvalidate,+    sessionUseTicket,+    SessionID,+    SessionIDorTicket,+    Ticket, -    -- * Errors-    , TLSError(..)-    , KxError(..)-    , AlertDescription(..)+    -- ** Session data+    SessionData,+    sessionVersion,+    sessionCipher,+    sessionCompression,+    sessionClientSNI,+    sessionSecret,+    sessionGroup,+    sessionTicketInfo,+    sessionALPN,+    sessionMaxEarlyDataSize,+    sessionFlags,+    SessionFlag (..),+    TLS13TicketInfo,+    is0RTTPossible, -    -- * Exceptions-    , TLSException(..)+    -- ** Validation Cache+    ValidationCache (..),+    defaultValidationCache,+    ValidationCacheQueryCallback,+    ValidationCacheAddCallback,+    ValidationCacheResult (..),+    exceptionValidationCache, -    -- * X509 Validation-    , ValidationChecks(..)-    , ValidationHooks(..)+    -- * Types -    -- * X509 Validation Cache-    , ValidationCache(..)-    , ValidationCacheResult(..)-    , exceptionValidationCache+    -- ** For 'Supported'+    Version (..),+    Compression (..),+    nullCompression,+    HashAndSignatureAlgorithm,+    supportedSignatureSchemes,+    HashAlgorithm (..),+    SignatureAlgorithm (..),+    Group (..),+    supportedNamedGroups,+    EMSMode (..), -    -- * Key exchange group-    , Group(..)-    ) where+    -- ** For parameters and hooks+    DHParams,+    DHPublic,+    GroupUsage (..),+    CertificateUsage (..),+    CertificateRejectReason (..),+    CertificateType (..),+    CertificateChain (..),+    HostName,+    MaxFragmentEnum (..), -import Network.TLS.Backend (Backend(..), HasBackend(..))-import Network.TLS.Struct ( TLSError(..), TLSException(..)-                          , HashAndSignatureAlgorithm, HashAlgorithm(..), SignatureAlgorithm(..)-                          , Header(..), ProtocolType(..), CertificateType(..)-                          , AlertDescription(..)-                          , ClientRandom(..), ServerRandom(..)-                          , Handshake)-import Network.TLS.Crypto (KxError(..), DHParams, DHPublic, Group(..))+    -- * Advanced APIs++    -- ** Backend+    ctxBackend,+    contextFlush,+    contextClose,++    -- ** Information gathering+    Information,+    contextGetInformation,+    infoVersion,+    infoCipher,+    infoCompression,+    infoMainSecret,+    infoExtendedMainSecret,+    infoClientRandom,+    infoServerRandom,+    infoSupportedGroup,+    infoTLS12Resumption,+    infoTLS13HandshakeMode,+    infoIsEarlyDataAccepted,+    infoIsECHAccepted,+    ClientRandom,+    ServerRandom,+    unClientRandom,+    unServerRandom,+    HandshakeMode13 (..),+    getClientCertificateChain,+    getServerCertificateChain,++    -- ** Negotiated+    getNegotiatedProtocol,+    getClientSNI,++    -- ** Post-handshake actions+    updateKey,+    KeyUpdateRequest (..),+    requestCertificate,+    getTLSUnique,+    getTLSExporter,+    getTLSServerEndPoint,+    getFinished,+    getPeerFinished,++    -- ** Modifying hooks in context+    Hooks,+    defaultHooks,+    hookRecvHandshake,+    hookRecvHandshake13,+    hookRecvCertificates,+    hookLogging,+    contextModifyHooks,+    Handshake,+    contextHookSetHandshakeRecv,+    Handshake13,+    contextHookSetHandshake13Recv,+    contextHookSetCertificateRecv,+    Logging,+    defaultLogging,+    loggingPacketSent,+    loggingPacketRecv,+    loggingIOSent,+    loggingIORecv,+    Header (..),+    ProtocolType (..),+    contextHookSetLogging,++    -- * Errors and exceptions++    -- ** Errors+    TLSError (..),+    KxError (..),+    AlertDescription (..),++    -- ** Exceptions+    TLSException (..),++    -- * Raw types++    -- ** Compressions class+    CompressionC (..),+    CompressionID,++    -- ** Crypto Key+    PubKey (..),+    PrivKey (..),++    -- ** Ciphers & Predefined ciphers+    module Network.TLS.Cipher,++    -- * Deprecated+    recvData',+    Bytes,+    ValidationChecks (..),+    ValidationHooks (..),+    clientUseMaxFragmentLength,+) where++import Data.X509 (PrivKey (..), PubKey (..))+import Data.X509.Validation hiding (HostName, defaultHooks)++import Network.TLS.Backend (Backend (..), HasBackend (..)) import Network.TLS.Cipher+import Network.TLS.Compression (+    Compression (..),+    CompressionC (..),+    nullCompression,+ )+import Network.TLS.Context+import Network.TLS.Core+import Network.TLS.Credentials+import Network.TLS.Crypto (+    DHParams,+    DHPublic,+    KxError (..),+    supportedNamedGroups,+ )+import Network.TLS.Extension+import Network.TLS.Handshake.State (HandshakeMode13 (..)) import Network.TLS.Hooks+import Network.TLS.Imports import Network.TLS.Measurement-import Network.TLS.Credentials-import Network.TLS.Compression (CompressionC(..), Compression(..), nullCompression)-import Network.TLS.Context import Network.TLS.Parameters-import Network.TLS.Core import Network.TLS.Session-import Network.TLS.X509+import qualified Network.TLS.State as S+import Network.TLS.Struct (+    AlertDescription (..),+    CertificateType (..),+    ClientRandom (..),+    Handshake,+    HashAlgorithm (..),+    HashAndSignatureAlgorithm,+    Header (..),+    ProtocolType (..),+    ServerRandom (..),+    SignatureAlgorithm (..),+    TLSError (..),+    TLSException (..),+    supportedSignatureSchemes,+ )+import Network.TLS.Struct13 (Handshake13) import Network.TLS.Types-import Data.X509 (PubKey(..), PrivKey(..))-import Data.X509.Validation-import Data.ByteString as B+import Network.TLS.X509  {-# DEPRECATED Bytes "Use Data.ByteString.Bytestring instead of Bytes." #-}-type Bytes = B.ByteString+type Bytes = ByteString++-- | Getting certificates from a client, if any.+--   Note that the certificates are not sent by a client+--   on resumption even if client authentication is required.+--   So, this API would be replaced by the one which can treat+--   both cases of full-negotiation and resumption.+getClientCertificateChain :: Context -> IO (Maybe CertificateChain)+getClientCertificateChain ctx = usingState_ ctx S.getClientCertificateChain++getServerCertificateChain :: Context -> IO (Maybe CertificateChain)+getServerCertificateChain ctx = usingState_ ctx S.getServerCertificateChain++-- $exceptions+--     Since 1.8.0, this library only throws exceptions of type 'TLSException'.+--     In the common case where the chosen backend is socket, 'IOException'+--     may be thrown as well. This happens because the backend for sockets,+--     opaque to most modules in the @tls@ library, throws those exceptions.
Network/TLS/Backend.hs view
@@ -1,12 +1,4 @@-{-# LANGUAGE CPP #-}--- |--- Module      : Network.TLS.Backend--- License     : BSD-style--- Maintainer  : Vincent Hanquez <vincent@snarc.org>--- Stability   : experimental--- Portability : unknown------ A Backend represents a unified way to do IO on different+-- | A Backend represents a unified way to do IO on different -- types without burdening our calling API with multiple -- ways to initialize a new context. --@@ -15,32 +7,28 @@ -- * a way to write data -- * a way to close the stream -- * a way to flush the stream----module Network.TLS.Backend-    ( HasBackend(..)-    , Backend(..)-    ) where+module Network.TLS.Backend (+    HasBackend (..),+    Backend (..),+) where -import Network.TLS.Imports import qualified Data.ByteString as B-import System.IO (Handle, hSetBuffering, BufferMode(..), hFlush, hClose)--#ifdef INCLUDE_NETWORK-import qualified Network.Socket as Network (Socket, close)+import qualified Network.Socket as Network import qualified Network.Socket.ByteString as Network-#endif+import System.IO (BufferMode (..), Handle, hClose, hFlush, hSetBuffering) -#ifdef INCLUDE_HANS-import qualified Data.ByteString.Lazy as L-import qualified Hans.NetworkStack as Hans-#endif+import Network.TLS.Imports  -- | Connection IO backend data Backend = Backend-    { backendFlush :: IO ()                -- ^ Flush the connection sending buffer, if any.-    , backendClose :: IO ()                -- ^ Close the connection.-    , backendSend  :: ByteString -> IO ()  -- ^ Send a bytestring through the connection.-    , backendRecv  :: Int -> IO ByteString -- ^ Receive specified number of bytes from the connection.+    { backendFlush :: IO ()+    -- ^ Flush the connection sending buffer, if any.+    , backendClose :: IO ()+    -- ^ Close the connection.+    , backendSend :: ByteString -> IO ()+    -- ^ Send a bytestring through the connection.+    , backendRecv :: Int -> IO ByteString+    -- ^ Receive specified number of bytes from the connection.     }  class HasBackend a where@@ -51,54 +39,34 @@     initializeBackend _ = return ()     getBackend = id -#if defined(__GLASGOW_HASKELL__) && WINDOWS--- Socket recv and accept calls on Windows platform cannot be interrupted when compiled with -threaded.--- See https://ghc.haskell.org/trac/ghc/ticket/5797 for details.--- The following enables simple workaround-#define SOCKET_ACCEPT_RECV_WORKAROUND-#endif- safeRecv :: Network.Socket -> Int -> IO ByteString-#ifndef SOCKET_ACCEPT_RECV_WORKAROUND safeRecv = Network.recv-#else-safeRecv s buf = do-    var <- newEmptyMVar-    forkIO $ Network.recv s buf `E.catch` (\(_::IOException) -> return S8.empty) >>= putMVar var-    takeMVar var-#endif -#ifdef INCLUDE_NETWORK instance HasBackend Network.Socket where     initializeBackend _ = return ()-    getBackend sock = Backend (return ()) (Network.close sock) (Network.sendAll sock) recvAll-      where recvAll n = B.concat <$> loop n-              where loop 0    = return []-                    loop left = do-                        r <- safeRecv sock left-                        if B.null r-                            then return []-                            else liftM (r:) (loop (left - B.length r))-#endif--#ifdef INCLUDE_HANS-instance HasBackend Hans.Socket where-    initializeBackend _ = return ()-    getBackend sock = Backend (return ()) (Hans.close sock) sendAll recvAll-      where sendAll x = do-              amt <- fromIntegral <$> Hans.sendBytes sock (L.fromStrict x)-              if (amt == 0) || (amt == B.length x)-                 then return ()-                 else sendAll (B.drop amt x)-            recvAll n = loop (fromIntegral n) L.empty-            loop    0 acc = return (L.toStrict acc)-            loop left acc = do-                r <- Hans.recvBytes sock left-                if L.null r-                   then loop 0 acc-                   else loop (left - L.length r) (acc `L.append` r)-#endif+    getBackend sock =+        Backend+            { backendFlush = return ()+            , backendClose = Network.close sock+            , backendSend = Network.sendAll sock+            , backendRecv = recvAll+            }+      where+        recvAll n = B.concat <$> loop n+          where+            loop 0 = return []+            loop left = do+                r <- safeRecv sock left+                if B.null r+                    then return []+                    else (r :) <$> loop (left - B.length r)  instance HasBackend Handle where     initializeBackend handle = hSetBuffering handle NoBuffering-    getBackend handle = Backend (hFlush handle) (hClose handle) (B.hPut handle) (B.hGet handle)+    getBackend handle =+        Backend+            { backendFlush = hFlush handle+            , backendClose = hClose handle+            , backendSend = B.hPut handle+            , backendRecv = B.hGet handle+            }
− Network/TLS/Cap.hs
@@ -1,19 +0,0 @@--- |--- Module      : Network.TLS.Cap--- License     : BSD-style--- Maintainer  : Vincent Hanquez <vincent@snarc.org>--- Stability   : experimental--- Portability : unknown-----module Network.TLS.Cap-    ( hasHelloExtensions-    , hasExplicitBlockIV-    ) where--import Network.TLS.Types--hasHelloExtensions, hasExplicitBlockIV :: Version -> Bool--hasHelloExtensions ver = ver >= SSL3-hasExplicitBlockIV ver = ver >= TLS11
Network/TLS/Cipher.hs view
@@ -1,158 +1,84 @@-{-# OPTIONS_HADDOCK hide #-} {-# LANGUAGE ExistentialQuantification #-}--- |--- Module      : Network.TLS.Cipher--- License     : BSD-style--- Maintainer  : Vincent Hanquez <vincent@snarc.org>--- Stability   : experimental--- Portability : unknown----module Network.TLS.Cipher-    ( CipherKeyExchangeType(..)-    , Bulk(..)-    , BulkFunctions(..)-    , BulkDirection(..)-    , BulkState(..)-    , BulkStream(..)-    , BulkBlock-    , BulkAEAD-    , bulkInit-    , Hash(..)-    , Cipher(..)-    , CipherID-    , cipherKeyBlockSize-    , BulkKey-    , BulkIV-    , BulkNonce-    , BulkAdditionalData-    , cipherAllowedForVersion-    , cipherExchangeNeedMoreData-    , hasMAC-    , hasRecordIV-    ) where--import Crypto.Cipher.Types (AuthTag)-import Network.TLS.Types (CipherID, Version(..))-import Network.TLS.Crypto (Hash(..), hashDigestSize)+{-# OPTIONS_HADDOCK hide #-} -import qualified Data.ByteString as B+module Network.TLS.Cipher (+    CipherKeyExchangeType (..),+    Bulk (..),+    BulkFunctions (..),+    BulkDirection (..),+    BulkState (..),+    BulkStream (..),+    BulkBlock,+    BulkAEAD,+    bulkInit,+    Hash (..),+    Cipher (..),+    CipherID,+    cipherKeyBlockSize,+    BulkKey,+    BulkIV,+    BulkNonce,+    BulkAdditionalData,+    cipherAllowedForVersion,+    hasMAC,+    hasRecordIV,+    elemCipher,+    intersectCiphers,+    findCipher,+) where --- FIXME convert to newtype-type BulkKey = B.ByteString-type BulkIV = B.ByteString-type BulkNonce = B.ByteString-type BulkAdditionalData = B.ByteString+import Network.TLS.Crypto (Hash (..), hashDigestSize)+import Network.TLS.Imports+import Network.TLS.Types -data BulkState =-      BulkStateStream BulkStream-    | BulkStateBlock  BulkBlock-    | BulkStateAEAD   BulkAEAD+data BulkState+    = BulkStateStream BulkStream+    | BulkStateBlock BulkBlock+    | BulkStateAEAD BulkAEAD     | BulkStateUninitialized  instance Show BulkState where-    show (BulkStateStream _)      = "BulkStateStream"-    show (BulkStateBlock _)       = "BulkStateBlock"-    show (BulkStateAEAD _)        = "BulkStateAEAD"-    show  BulkStateUninitialized  = "BulkStateUninitialized"--newtype BulkStream = BulkStream (B.ByteString -> (B.ByteString, BulkStream))--type BulkBlock = BulkIV -> B.ByteString -> (B.ByteString, BulkIV)--type BulkAEAD = BulkNonce -> B.ByteString -> BulkAdditionalData -> (B.ByteString, AuthTag)--data BulkDirection = BulkEncrypt | BulkDecrypt-    deriving (Show,Eq)+    show (BulkStateStream _) = "BulkStateStream"+    show (BulkStateBlock _) = "BulkStateBlock"+    show (BulkStateAEAD _) = "BulkStateAEAD"+    show BulkStateUninitialized = "BulkStateUninitialized"  bulkInit :: Bulk -> BulkDirection -> BulkKey -> BulkState bulkInit bulk direction key =     case bulkF bulk of-        BulkBlockF  ini -> BulkStateBlock  (ini direction key)+        BulkBlockF ini -> BulkStateBlock (ini direction key)         BulkStreamF ini -> BulkStateStream (ini direction key)-        BulkAeadF   ini -> BulkStateAEAD   (ini direction key)--data BulkFunctions =-      BulkBlockF  (BulkDirection -> BulkKey -> BulkBlock)-    | BulkStreamF (BulkDirection -> BulkKey -> BulkStream)-    | BulkAeadF   (BulkDirection -> BulkKey -> BulkAEAD)--hasMAC,hasRecordIV :: BulkFunctions -> Bool+        BulkAeadF ini -> BulkStateAEAD (ini direction key) -hasMAC (BulkBlockF _ ) = True+hasMAC, hasRecordIV :: BulkFunctions -> Bool+hasMAC (BulkBlockF _) = True hasMAC (BulkStreamF _) = True-hasMAC (BulkAeadF _  ) = False-+hasMAC (BulkAeadF _) = False hasRecordIV = hasMAC -data CipherKeyExchangeType =-      CipherKeyExchange_RSA-    | CipherKeyExchange_DH_Anon-    | CipherKeyExchange_DHE_RSA-    | CipherKeyExchange_ECDHE_RSA-    | CipherKeyExchange_DHE_DSS-    | CipherKeyExchange_DH_DSS-    | CipherKeyExchange_DH_RSA-    | CipherKeyExchange_ECDH_ECDSA-    | CipherKeyExchange_ECDH_RSA-    | CipherKeyExchange_ECDHE_ECDSA-    deriving (Show,Eq)--data Bulk = Bulk-    { bulkName         :: String-    , bulkKeySize      :: Int-    , bulkIVSize       :: Int-    , bulkExplicitIV   :: Int -- Explicit size for IV for AEAD Cipher, 0 otherwise-    , bulkAuthTagLen   :: Int -- Authentication tag length in bytes for AEAD Cipher, 0 otherwise-    , bulkBlockSize    :: Int-    , bulkF            :: BulkFunctions-    }--instance Show Bulk where-    show bulk = bulkName bulk-instance Eq Bulk where-    b1 == b2 = and [ bulkName b1 == bulkName b2-                   , bulkKeySize b1 == bulkKeySize b2-                   , bulkIVSize b1 == bulkIVSize b2-                   , bulkBlockSize b1 == bulkBlockSize b2-                   ]---- | Cipher algorithm-data Cipher = Cipher-    { cipherID           :: CipherID-    , cipherName         :: String-    , cipherHash         :: Hash-    , cipherBulk         :: Bulk-    , cipherKeyExchange  :: CipherKeyExchangeType-    , cipherMinVer       :: Maybe Version-    , cipherPRFHash      :: Maybe Hash-    }- cipherKeyBlockSize :: Cipher -> Int cipherKeyBlockSize cipher = 2 * (hashDigestSize (cipherHash cipher) + bulkIVSize bulk + bulkKeySize bulk)-  where bulk = cipherBulk cipher+  where+    bulk = cipherBulk cipher  -- | Check if a specific 'Cipher' is allowed to be used -- with the version specified cipherAllowedForVersion :: Version -> Cipher -> Bool cipherAllowedForVersion ver cipher =     case cipherMinVer cipher of-        Nothing   -> True-        Just cVer -> cVer <= ver+        Nothing -> ver < TLS13+        Just cVer -> cVer <= ver && (ver < TLS13 || cVer >= TLS13) -instance Show Cipher where-    show c = cipherName c+eqCipher :: CipherID -> Cipher -> Bool+eqCipher cid c = cipherID c == cid -instance Eq Cipher where-    (==) c1 c2 = cipherID c1 == cipherID c2+elemCipher :: [CipherId] -> Cipher -> Bool+elemCipher cids c = cid `elem` cids+  where+    cid = CipherId $ cipherID c -cipherExchangeNeedMoreData :: CipherKeyExchangeType -> Bool-cipherExchangeNeedMoreData CipherKeyExchange_RSA         = False-cipherExchangeNeedMoreData CipherKeyExchange_DH_Anon     = True-cipherExchangeNeedMoreData CipherKeyExchange_DHE_RSA     = True-cipherExchangeNeedMoreData CipherKeyExchange_ECDHE_RSA   = True-cipherExchangeNeedMoreData CipherKeyExchange_DHE_DSS     = True-cipherExchangeNeedMoreData CipherKeyExchange_DH_DSS      = False-cipherExchangeNeedMoreData CipherKeyExchange_DH_RSA      = False-cipherExchangeNeedMoreData CipherKeyExchange_ECDH_ECDSA  = True-cipherExchangeNeedMoreData CipherKeyExchange_ECDH_RSA    = True-cipherExchangeNeedMoreData CipherKeyExchange_ECDHE_ECDSA = True+intersectCiphers :: [CipherId] -> [Cipher] -> [Cipher]+intersectCiphers peerCiphers myCiphers = filter (elemCipher peerCiphers) myCiphers++findCipher :: CipherID -> [Cipher] -> Maybe Cipher+findCipher cid = find $ eqCipher cid
Network/TLS/Compression.hs view
@@ -1,40 +1,35 @@-{-# OPTIONS_HADDOCK hide #-} {-# LANGUAGE ExistentialQuantification #-}--- |--- Module      : Network.TLS.Compression--- License     : BSD-style--- Maintainer  : Vincent Hanquez <vincent@snarc.org>--- Stability   : experimental--- Portability : unknown----module Network.TLS.Compression-    ( CompressionC(..)-    , Compression(..)-    , CompressionID-    , nullCompression-    , NullCompression+{-# OPTIONS_HADDOCK hide #-} +module Network.TLS.Compression (+    CompressionC (..),+    Compression (..),+    CompressionID,+    nullCompression,+    NullCompression,+     -- * member redefined for the class abstraction-    , compressionID-    , compressionDeflate-    , compressionInflate+    compressionID,+    compressionDeflate,+    compressionInflate,      -- * helper-    , compressionIntersectID-    ) where+    compressionIntersectID,+) where -import Network.TLS.Types (CompressionID)-import Network.TLS.Imports import Control.Arrow (first) +import Network.TLS.Imports+import Network.TLS.Types (CompressionID)+ -- | supported compression algorithms need to be part of this class class CompressionC a where-    compressionCID      :: a -> CompressionID+    compressionCID :: a -> CompressionID     compressionCDeflate :: a -> ByteString -> (a, ByteString)     compressionCInflate :: a -> ByteString -> (a, ByteString)  -- | every compression need to be wrapped in this, to fit in structure-data Compression = forall a . CompressionC a => Compression a+data Compression = forall a. CompressionC a => Compression a  -- | return the associated ID for this algorithm compressionID :: Compression -> CompressionID@@ -56,7 +51,7 @@     (==) c1 c2 = compressionID c1 == compressionID c2  -- | intersect a list of ids commonly given by the other side with a list of compression--- the function keeps the list of compression in order, to be able to find quickly the prefered+-- the function keeps the list of compression in order, to be able to find quickly the preferred -- compression. compressionIntersectID :: [Compression] -> [Word8] -> [Compression] compressionIntersectID l ids = filter (\c -> compressionID c `elem` ids) l@@ -65,7 +60,7 @@ data NullCompression = NullCompression  instance CompressionC NullCompression where-    compressionCID _        = 0+    compressionCID _ = 0     compressionCDeflate s b = (s, b)     compressionCInflate s b = (s, b) 
Network/TLS/Context.hs view
@@ -1,197 +1,311 @@-{-# LANGUAGE CPP #-}--- |--- Module      : Network.TLS.Context--- License     : BSD-style--- Maintainer  : Vincent Hanquez <vincent@snarc.org>--- Stability   : experimental--- Portability : unknown----module Network.TLS.Context-    (+{-# LANGUAGE OverloadedStrings #-}++module Network.TLS.Context (     -- * Context configuration-      TLSParams+    TLSParams,      -- * Context object and accessor-    , Context(..)-    , Hooks(..)-    , ctxEOF-    , ctxHasSSLv2ClientHello-    , ctxDisableSSLv2ClientHello-    , ctxEstablished-    , withLog-    , ctxWithHooks-    , contextModifyHooks-    , setEOF-    , setEstablished-    , contextFlush-    , contextClose-    , contextSend-    , contextRecv-    , updateMeasure-    , withMeasure-    , withReadLock-    , withWriteLock-    , withStateLock-    , withRWLock+    Context (..),+    Hooks (..),+    Established (..),+    RecordLayer (..),+    ctxEOF,+    ctxEstablished,+    withLog,+    ctxWithHooks,+    contextModifyHooks,+    setEOF,+    setEstablished,+    contextFlush,+    contextClose,+    contextSend,+    contextRecv,+    updateMeasure,+    withMeasure,+    withReadLock,+    withWriteLock,+    withStateLock,+    withRWLock,      -- * information-    , Information(..)-    , contextGetInformation+    Information (..),+    contextGetInformation,      -- * New contexts-    , contextNew-    -- * Deprecated new contexts methods-    , contextNewOnHandle-#ifdef INCLUDE_NETWORK-    , contextNewOnSocket-#endif+    contextNew,      -- * Context hooks-    , contextHookSetHandshakeRecv-    , contextHookSetCertificateRecv-    , contextHookSetLogging+    contextHookSetHandshakeRecv,+    contextHookSetHandshake13Recv,+    contextHookSetCertificateRecv,+    contextHookSetLogging,      -- * Using context states-    , throwCore-    , usingState-    , usingState_-    , runTxState-    , runRxState-    , usingHState-    , getHState-    , getStateRNG-    ) where+    throwCore,+    usingState,+    usingState_,+    runTxRecordState,+    runRxRecordState,+    usingHState,+    getHState,+    getStateRNG,+    tls13orLater,+    getTLSUnique,+    getTLSExporter,+    getTLSServerEndPoint,+    getFinished,+    getPeerFinished,+    TLS13State (..),+    getTLS13State,+    modifyTLS13State,+    setMyRecordLimit,+    enableMyRecordLimit,+    getMyRecordLimit,+    checkMyRecordLimit,+    setPeerRecordLimit,+    enablePeerRecordLimit,+    getPeerRecordLimit,+    checkPeerRecordLimit,+    newRecordLimitRef,+) where +import Control.Concurrent.MVar+import Control.Monad.State.Strict+import Data.IORef+ import Network.TLS.Backend+import Network.TLS.Cipher import Network.TLS.Context.Internal-import Network.TLS.Struct-import Network.TLS.State+import Network.TLS.Crypto+import Network.TLS.Handshake (+    handshakeClient,+    handshakeClientWith,+    handshakeServer,+    handshakeServerWith,+ )+import Network.TLS.Handshake.State13 import Network.TLS.Hooks-import Network.TLS.Record.State-import Network.TLS.Parameters+import Network.TLS.Imports+import Network.TLS.KeySchedule import Network.TLS.Measurement-import Network.TLS.Types (Role(..))-import Network.TLS.Handshake (handshakeClient, handshakeClientWith, handshakeServer, handshakeServerWith)-import Network.TLS.X509+import Network.TLS.Packet+import Network.TLS.Parameters+import Network.TLS.PostHandshake (+    postHandshakeAuthClientWith,+    requestCertificateServer,+ ) import Network.TLS.RNG--import Control.Concurrent.MVar-import Control.Monad.State.Strict-import Data.IORef---- deprecated imports-#ifdef INCLUDE_NETWORK-import Network.Socket (Socket)-#endif-import System.IO (Handle)+import Network.TLS.Record.Recv+import Network.TLS.Record.Send+import Network.TLS.Record.State+import Network.TLS.State+import Network.TLS.Struct+import Network.TLS.Struct13+import Network.TLS.Types (+    Role (..),+    TranscriptHash (..),+    defaultRecordSizeLimit,+ )+import Network.TLS.X509  class TLSParams a where     getTLSCommonParams :: a -> CommonParams-    getTLSRole         :: a -> Role-    doHandshake        :: a -> Context -> IO ()-    doHandshakeWith    :: a -> Context -> Handshake -> IO ()+    getTLSRole :: a -> Role+    doHandshake :: a -> Context -> IO ()+    doHandshakeWith :: a -> Context -> HandshakeR -> IO ()+    doRequestCertificate :: a -> Context -> IO Bool+    doPostHandshakeAuthWith :: a -> Context -> Handshake13 -> IO ()  instance TLSParams ClientParams where-    getTLSCommonParams cparams = ( clientSupported cparams-                                 , clientShared cparams-                                 , clientDebug cparams-                                 )+    getTLSCommonParams cparams =+        ( clientSupported cparams+        , clientShared cparams+        , clientDebug cparams+        )     getTLSRole _ = ClientRole     doHandshake = handshakeClient     doHandshakeWith = handshakeClientWith+    doRequestCertificate _ _ = return False+    doPostHandshakeAuthWith = postHandshakeAuthClientWith  instance TLSParams ServerParams where-    getTLSCommonParams sparams = ( serverSupported sparams-                                 , serverShared sparams-                                 , serverDebug sparams-                                 )+    getTLSCommonParams sparams =+        ( serverSupported sparams+        , serverShared sparams+        , serverDebug sparams+        )     getTLSRole _ = ServerRole     doHandshake = handshakeServer     doHandshakeWith = handshakeServerWith+    doRequestCertificate = requestCertificateServer+    doPostHandshakeAuthWith = \_ _ _ -> return ()  -- | create a new context using the backend and parameters specified.-contextNew :: (MonadIO m, HasBackend backend, TLSParams params)-           => backend   -- ^ Backend abstraction with specific method to interact with the connection type.-           -> params    -- ^ Parameters of the context.-           -> m Context+contextNew+    :: (MonadIO m, HasBackend backend, TLSParams params)+    => backend+    -- ^ Backend abstraction with specific method to interact with the connection type.+    -> params+    -- ^ Parameters of the context.+    -> m Context contextNew backend params = liftIO $ do     initializeBackend backend      let (supported, shared, debug) = getTLSCommonParams params      seed <- case debugSeed debug of-                Nothing     -> do seed <- seedNew-                                  debugPrintSeed debug seed-                                  return seed-                Just determ -> return determ+        Nothing -> do+            seed <- seedNew+            debugPrintSeed debug seed+            return seed+        Just determ -> return determ     let rng = newStateRNG seed      let role = getTLSRole params-        st   = newTLSState rng role+        st = newTLSState rng role -    stvar <- newMVar st-    eof   <- newIORef False-    established <- newIORef False+    tlsstate <- newMVar st+    eof <- newIORef False+    established <- newIORef NotEstablished     stats <- newIORef newMeasurement-    -- we enable the reception of SSLv2 ClientHello message only in the-    -- server context, where we might be dealing with an old/compat client.-    sslv2Compat <- newIORef (role == ServerRole)     needEmptyPacket <- newIORef False     hooks <- newIORef defaultHooks-    tx    <- newMVar newRecordState-    rx    <- newMVar newRecordState-    hs    <- newMVar Nothing-    lockWrite <- newMVar ()-    lockRead  <- newMVar ()-    lockState <- newMVar ()+    tx <- newMVar newRecordState+    rx <- newMVar newRecordState+    hs <- newMVar Nothing+    recvActionsRef <- newIORef []+    sendActionRef <- newIORef Nothing+    locks <- Locks <$> newMVar () <*> newMVar () <*> newMVar ()+    st13ref <- newIORef defaultTLS13State+    mylimref <- newRecordLimitRef $ Just defaultRecordSizeLimit+    peerlimref <- newRecordLimitRef $ Just defaultRecordSizeLimit+    hpkeref <- newIORef Nothing+    let roleParams =+            RoleParams+                { doHandshake_ = doHandshake params+                , doHandshakeWith_ = doHandshakeWith params+                , doRequestCertificate_ = doRequestCertificate params+                , doPostHandshakeAuthWith_ = doPostHandshakeAuthWith params+                } -    return Context-            { ctxConnection   = getBackend backend-            , ctxShared       = shared-            , ctxSupported    = supported-            , ctxState        = stvar-            , ctxTxState      = tx-            , ctxRxState      = rx-            , ctxHandshake    = hs-            , ctxDoHandshake  = doHandshake params-            , ctxDoHandshakeWith  = doHandshakeWith params-            , ctxMeasurement  = stats-            , ctxEOF_         = eof-            , ctxEstablished_ = established-            , ctxSSLv2ClientHello = sslv2Compat-            , ctxNeedEmptyPacket  = needEmptyPacket-            , ctxHooks            = hooks-            , ctxLockWrite        = lockWrite-            , ctxLockRead         = lockRead-            , ctxLockState        = lockState-            }+    let ctx =+            Context+                { ctxBackend = getBackend backend+                , ctxShared = shared+                , ctxSupported = supported+                , ctxDebug = debug+                , ctxTLSState = tlsstate+                , ctxMyRecordLimit = mylimref+                , ctxPeerRecordLimit = peerlimref+                , ctxTxRecordState = tx+                , ctxRxRecordState = rx+                , ctxHandshakeState = hs+                , ctxRoleParams = roleParams+                , ctxMeasurement = stats+                , ctxEOF_ = eof+                , ctxEstablished_ = established+                , ctxNeedEmptyPacket = needEmptyPacket+                , ctxHooks = hooks+                , ctxLocks = locks+                , ctxPendingRecvActions = recvActionsRef+                , ctxPendingSendAction = sendActionRef+                , ctxRecordLayer = recordLayer+                , ctxHandshakeSync = HandshakeSync syncNoOp syncNoOp+                , ctxQUICMode = False+                , ctxTLS13State = st13ref+                , ctxHPKE = hpkeref+                } --- | create a new context on an handle.-contextNewOnHandle :: (MonadIO m, TLSParams params)-                   => Handle -- ^ Handle of the connection.-                   -> params -- ^ Parameters of the context.-                   -> m Context-contextNewOnHandle handle params = contextNew handle params-{-# DEPRECATED contextNewOnHandle "use contextNew" #-}+        syncNoOp _ _ = return () -#ifdef INCLUDE_NETWORK--- | create a new context on a socket.-contextNewOnSocket :: (MonadIO m, TLSParams params)-                   => Socket -- ^ Socket of the connection.-                   -> params -- ^ Parameters of the context.-                   -> m Context-contextNewOnSocket sock params = contextNew sock params-{-# DEPRECATED contextNewOnSocket "use contextNew" #-}-#endif+        recordLayer =+            RecordLayer+                { recordEncode12 = encodeRecord12+                , recordEncode13 = encodeRecord13+                , recordSendBytes = sendBytes+                , recordRecv12 = recvRecord12+                , recordRecv13 = recvRecord13+                } +    return ctx+ contextHookSetHandshakeRecv :: Context -> (Handshake -> IO Handshake) -> IO () contextHookSetHandshakeRecv context f =-    contextModifyHooks context (\hooks -> hooks { hookRecvHandshake = f })+    contextModifyHooks context (\hooks -> hooks{hookRecvHandshake = f}) +contextHookSetHandshake13Recv+    :: Context -> (Handshake13 -> IO Handshake13) -> IO ()+contextHookSetHandshake13Recv context f =+    contextModifyHooks context (\hooks -> hooks{hookRecvHandshake13 = f})+ contextHookSetCertificateRecv :: Context -> (CertificateChain -> IO ()) -> IO () contextHookSetCertificateRecv context f =-    contextModifyHooks context (\hooks -> hooks { hookRecvCertificates = f })+    contextModifyHooks context (\hooks -> hooks{hookRecvCertificates = f})  contextHookSetLogging :: Context -> Logging -> IO () contextHookSetLogging context loggingCallbacks =-    contextModifyHooks context (\hooks -> hooks { hookLogging = loggingCallbacks })+    contextModifyHooks context (\hooks -> hooks{hookLogging = loggingCallbacks})++{-# DEPRECATED getFinished "Use getTLSUnique instead" #-}++-- | Getting TLS Finished sent to peer.+getFinished :: Context -> IO (Maybe VerifyData)+getFinished ctx = usingState_ ctx getMyVerifyData++{-# DEPRECATED getPeerFinished "Use getTLSUnique instead" #-}++-- | Getting TLS Finished received from peer.+getPeerFinished :: Context -> IO (Maybe VerifyData)+getPeerFinished ctx = usingState_ ctx getPeerVerifyData++-- | Getting the "tls-unique" channel binding for TLS 1.2 (RFC5929).+--   For TLS 1.3, 'Nothing' is returned.+--   'supportedExtendedMainSecret' must be 'RequireEMS'+--   But in general, it is highly recommended to upgrade to TLS 1.3+--   and use the "tls-exporter" channel binding via 'getTLSExporter'.+getTLSUnique :: Context -> IO (Maybe ByteString)+getTLSUnique ctx = do+    ver <- liftIO $ usingState_ ctx getVersion+    if ver == TLS12+        then do+            mx <- usingState_ ctx getFirstVerifyData+            case mx of+                Nothing -> return Nothing+                Just (VerifyData verifyData) -> return $ Just verifyData+        else return Nothing++-- | Getting the "tls-exporter" channel binding for TLS 1.3 (RFC9266).+--   For TLS 1.2, 'Nothing' is returned.+getTLSExporter :: Context -> IO (Maybe ByteString)+getTLSExporter ctx = do+    ver <- liftIO $ usingState_ ctx getVersion+    if ver == TLS13+        then exporter ctx "EXPORTER-Channel-Binding" "" 32+        else return Nothing++exporter :: Context -> ByteString -> ByteString -> Int -> IO (Maybe ByteString)+exporter ctx label context outlen = do+    msecret <- usingState_ ctx getTLS13ExporterSecret+    mcipher <- failOnEitherError $ runRxRecordState ctx $ gets stCipher+    return $ case (msecret, mcipher) of+        (Just secret, Just cipher) ->+            let h = cipherHash cipher+                secret' = deriveSecret h secret label $ TranscriptHash ""+                label' = "exporter"+                value' = hash h context+                key = hkdfExpandLabel h secret' label' value' outlen+             in Just key+        _ -> Nothing++-- | Getting the "tls-server-end-point" channel binding for TLS 1.2+--   (RFC5929).  For 1.3, there is no specifications for how to create+--   it.  In this implementation, a certificate chain without+--   extensions is hashed like TLS 1.2.+getTLSServerEndPoint :: Context -> IO (Maybe ByteString)+getTLSServerEndPoint ctx = do+    mcc <- usingState_ ctx getServerCertificateChain+    case mcc of+        Nothing -> return Nothing+        Just cc -> do+            (usedHash, _, _, _) <- getRxRecordState ctx+            return $ Just $ hash usedHash $ encodeCertificate cc
Network/TLS/Context/Internal.hs view
@@ -1,232 +1,531 @@--- |--- Module      : Network.TLS.Context.Internal--- License     : BSD-style--- Maintainer  : Vincent Hanquez <vincent@snarc.org>--- Stability   : experimental--- Portability : unknown----module Network.TLS.Context.Internal-    (+{-# LANGUAGE ExistentialQuantification #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE RecordWildCards #-}++module Network.TLS.Context.Internal (     -- * Context configuration-      ClientParams(..)-    , ServerParams(..)-    , defaultParamsClient-    , SessionID-    , SessionData(..)-    , MaxFragmentEnum(..)-    , Measurement(..)+    ClientParams (..),+    ServerParams (..),+    defaultParamsClient,+    SessionID,+    SessionData (..),+    MaxFragmentEnum (..),+    Measurement (..),      -- * Context object and accessor-    , Context(..)-    , Hooks(..)-    , ctxEOF-    , ctxHasSSLv2ClientHello-    , ctxDisableSSLv2ClientHello-    , ctxEstablished-    , withLog-    , ctxWithHooks-    , contextModifyHooks-    , setEOF-    , setEstablished-    , contextFlush-    , contextClose-    , contextSend-    , contextRecv-    , updateMeasure-    , withMeasure-    , withReadLock-    , withWriteLock-    , withStateLock-    , withRWLock+    Context (..),+    Hooks (..),+    Limit (..),+    Established (..),+    PendingRecvAction (..),+    RecordLayer (..),+    Locks (..),+    RoleParams (..),+    ctxEOF,+    ctxEstablished,+    withLog,+    ctxWithHooks,+    contextModifyHooks,+    setEOF,+    setEstablished,+    contextFlush,+    contextClose,+    contextSend,+    contextRecv,+    updateRecordLayer,+    updateMeasure,+    withMeasure,+    withReadLock,+    withWriteLock,+    withStateLock,+    withRWLock,      -- * information-    , Information(..)-    , contextGetInformation+    Information (..),+    contextGetInformation,      -- * Using context states-    , throwCore-    , usingState-    , usingState_-    , runTxState-    , runRxState-    , usingHState-    , getHState-    , getStateRNG-    ) where+    throwCore,+    failOnEitherError,+    usingState,+    usingState_,+    runTxRecordState,+    runRxRecordState,+    usingHState,+    getHState,+    saveHState,+    restoreHState,+    getStateRNG,+    tls13orLater,+    decideRecordVersion, +    -- * Misc+    HandshakeSync (..),+    TLS13State (..),+    defaultTLS13State,+    getTLS13State,+    modifyTLS13State,+    CipherChoice (..),+    makeCipherChoice,++    -- * RecordLimit+    setMyRecordLimit,+    enableMyRecordLimit,+    getMyRecordLimit,+    checkMyRecordLimit,+    setPeerRecordLimit,+    enablePeerRecordLimit,+    getPeerRecordLimit,+    checkPeerRecordLimit,+    newRecordLimitRef,++    -- * ECH+    HPKEF,+    getTLS13HPKE,+    setTLS13HPKE,+) where++import Control.Concurrent.MVar+import Control.Exception (throwIO)+import Control.Monad.State.Strict+import Data.ByteArray (convert)+import qualified Data.ByteArray as BA+import qualified Data.ByteString as B+import Data.IORef+import Data.Tuple+ import Network.TLS.Backend-import Network.TLS.Extension import Network.TLS.Cipher-import Network.TLS.Struct-import Network.TLS.Compression (Compression)-import Network.TLS.State+import Network.TLS.Crypto+import Network.TLS.Extension+import Network.TLS.Handshake.Control import Network.TLS.Handshake.State import Network.TLS.Hooks-import Network.TLS.Record.State-import Network.TLS.Parameters-import Network.TLS.Measurement import Network.TLS.Imports-import qualified Data.ByteString as B+import Network.TLS.Measurement+import Network.TLS.Parameters+import Network.TLS.Record+import Network.TLS.Record.State+import Network.TLS.State+import Network.TLS.Struct+import Network.TLS.Struct13+import Network.TLS.Types+import Network.TLS.Util -import Control.Concurrent.MVar-import Control.Monad.State.Strict-import Control.Exception (throwIO, Exception())-import Data.IORef-import Data.Tuple+-- | A TLS Context keep tls specific state, parameters and backend information.+data Context+    = forall a.+      Monoid a =>+    Context+    { ctxBackend :: Backend+    -- ^ return the backend object associated with this context+    , ctxSupported :: Supported+    , ctxShared :: Shared+    , ctxDebug :: DebugParams+    , ctxTLSState :: MVar TLSState+    , ctxMeasurement :: IORef Measurement+    , ctxEOF_ :: IORef Bool+    -- ^ has the handle EOFed or not.+    , ctxEstablished_ :: IORef Established+    -- ^ has the handshake been done and been successful.+    , ctxTxRecordState :: MVar RecordState+    -- ^ current TX record state+    , ctxRxRecordState :: MVar RecordState+    -- ^ current RX record state+    , ctxHandshakeState :: MVar (Maybe HandshakeState)+    -- ^ optional handshake state+    , ctxRoleParams :: RoleParams+    -- ^ hooks for this context+    , ctxLocks :: Locks+    , ctxHooks :: IORef Hooks+    , -- TLS 1.3+      ctxTLS13State :: IORef TLS13State+    , ctxPendingRecvActions :: IORef [PendingRecvAction]+    , ctxPendingSendAction :: IORef (Maybe (Context -> IO ()))+    -- ^ pending post handshake authentication requests+    , -- QUIC+      ctxRecordLayer :: RecordLayer a+    , ctxHandshakeSync :: HandshakeSync+    , ctxQUICMode :: Bool+    , -- Misc+      ctxNeedEmptyPacket :: IORef Bool+    -- ^ empty packet workaround for CBC guessability.+    , ctxMyRecordLimit :: IORef RecordLimit+    -- ^ maximum size of plaintext fragments, val + 1 is used for TLS 1.3+    , ctxPeerRecordLimit :: IORef RecordLimit+    -- ^ maximum size of plaintext fragments, val + 1 is used for TLS 1.3+    , ctxHPKE :: IORef (Maybe (HPKEF, Int))+    } +type HPKEF = ByteString -> ByteString -> IO ByteString --- | Information related to a running context, e.g. current cipher-data Information = Information-    { infoVersion      :: Version-    , infoCipher       :: Cipher-    , infoCompression  :: Compression-    , infoMasterSecret :: Maybe ByteString-    , infoClientRandom :: Maybe ClientRandom-    , infoServerRandom :: Maybe ServerRandom-    } deriving (Show,Eq)+data RecordLimit+    = NoRecordLimit -- for QUIC+    | RecordLimit+        Int -- effective+        (Maybe Int) -- pending+    deriving (Eq, Show) --- | A TLS Context keep tls specific state, parameters and backend information.-data Context = Context-    { ctxConnection       :: Backend   -- ^ return the backend object associated with this context-    , ctxSupported        :: Supported-    , ctxShared           :: Shared-    , ctxState            :: MVar TLSState-    , ctxMeasurement      :: IORef Measurement-    , ctxEOF_             :: IORef Bool    -- ^ has the handle EOFed or not.-    , ctxEstablished_     :: IORef Bool    -- ^ has the handshake been done and been successful.-    , ctxNeedEmptyPacket  :: IORef Bool    -- ^ empty packet workaround for CBC guessability.-    , ctxSSLv2ClientHello :: IORef Bool    -- ^ enable the reception of compatibility SSLv2 client hello.-                                           -- the flag will be set to false regardless of its initial value-                                           -- after the first packet received.-    , ctxTxState          :: MVar RecordState -- ^ current tx state-    , ctxRxState          :: MVar RecordState -- ^ current rx state-    , ctxHandshake        :: MVar (Maybe HandshakeState) -- ^ optional handshake state-    , ctxDoHandshake      :: Context -> IO ()-    , ctxDoHandshakeWith  :: Context -> Handshake -> IO ()-    , ctxHooks            :: IORef Hooks   -- ^ hooks for this context-    , ctxLockWrite        :: MVar ()       -- ^ lock to use for writing data (including updating the state)-    , ctxLockRead         :: MVar ()       -- ^ lock to use for reading data (including updating the state)-    , ctxLockState        :: MVar ()       -- ^ lock used during read/write when receiving and sending packet.-                                           -- it is usually nested in a write or read lock.+data RoleParams = RoleParams+    { doHandshake_ :: Context -> IO ()+    , doHandshakeWith_ :: Context -> HandshakeR -> IO ()+    , doRequestCertificate_ :: Context -> IO Bool+    , doPostHandshakeAuthWith_ :: Context -> Handshake13 -> IO ()     } +data Locks = Locks+    { lockWrite :: MVar ()+    -- ^ lock to use for writing data (including updating the state)+    , lockRead :: MVar ()+    -- ^ lock to use for reading data (including updating the state)+    , lockState :: MVar ()+    -- ^ lock used during read/write when receiving and sending packet.+    -- it is usually nested in a write or read lock.+    }++data CipherChoice = CipherChoice+    { cVersion :: Version+    , cCipher :: Cipher+    , cHash :: Hash+    , cZero :: Secret+    }+    deriving (Show)++makeCipherChoice :: Version -> Cipher -> CipherChoice+makeCipherChoice ver cipher = CipherChoice ver cipher h zero+  where+    h = cipherHash cipher+    zero = BA.replicate (hashDigestSize h) 0++data TLS13State = TLS13State+    { tls13stRecvNST :: Bool -- client+    , tls13stSentClientCert :: Bool -- client+    , tls13stRecvSF :: Bool -- client+    , tls13stSentCF :: Bool -- client+    , tls13stRecvCF :: Bool -- server+    , tls13stPendingRecvData :: Maybe ByteString -- client+    , tls13stPendingSentData :: [ByteString] -> [ByteString] -- client+    , tls13stRTT :: Millisecond+    , tls13st0RTT :: Bool -- client+    , tls13st0RTTAccepted :: Bool -- client+    , tls13stClientExtensions :: [ExtensionRaw] -- client+    , tls13stChoice :: ~CipherChoice -- client+    , tls13stHsKey :: Maybe (SecretTriple HandshakeSecret) -- client+    -- Actual session id for TLS 1.2, random value for TLS 1.3+    , tls13stSession :: Session+    , tls13stSentExtensions :: [ExtensionID]+    }++defaultTLS13State :: TLS13State+defaultTLS13State =+    TLS13State+        { tls13stRecvNST = False+        , tls13stSentClientCert = False+        , tls13stRecvSF = False+        , tls13stSentCF = False+        , tls13stRecvCF = False+        , tls13stPendingRecvData = Nothing+        , tls13stPendingSentData = id+        , tls13stRTT = 0+        , tls13st0RTT = False+        , tls13st0RTTAccepted = False+        , tls13stClientExtensions = []+        , tls13stChoice = undefined+        , tls13stHsKey = Nothing+        , tls13stSession = Session Nothing+        , tls13stSentExtensions = []+        }++getTLS13State :: Context -> IO TLS13State+getTLS13State Context{..} = readIORef ctxTLS13State++modifyTLS13State :: Context -> (TLS13State -> TLS13State) -> IO ()+modifyTLS13State Context{..} f = atomicModifyIORef' ctxTLS13State $ \st -> (f st, ())++data HandshakeSync+    = HandshakeSync+        (Context -> ClientState -> IO ())+        (Context -> ServerState -> IO ())++{- FOURMOLU_DISABLE -}+data RecordLayer a = RecordLayer+    { -- Writing.hs+      recordEncode12  :: Context -> Record Plaintext -> IO (Either TLSError a)+    , recordEncode13  :: Context -> Record Plaintext -> IO (Either TLSError a)+    , recordSendBytes :: Context -> a -> IO ()+    , -- Reading.hs+      recordRecv12    :: Context -> IO (Either TLSError (Record Plaintext))+    , recordRecv13    :: Context -> IO (Either TLSError (Record Plaintext))+    }+{- FOURMOLU_ENABLE -}++updateRecordLayer :: Monoid a => RecordLayer a -> Context -> Context+updateRecordLayer recordLayer Context{..} =+    Context{ctxRecordLayer = recordLayer, ..}++data Established+    = NotEstablished+    | EarlyDataAllowed Int -- server: remaining 0-RTT bytes allowed+    | EarlyDataNotAllowed Int -- sever: remaining 0-RTT packets allowed to skip+    | EarlyDataSending+    | Established+    deriving (Eq, Show)++data PendingRecvAction+    = -- | simple pending action. The first 'Bool' is necessity of alignment.+      PendingRecvAction Bool (Handshake13 -> IO ())+    | PendingRecvActionSelfUpdate Bool (Handshake13R -> IO ())+    | -- | pending action taking transcript hash up to preceding message+      --   The first 'Bool' is necessity of alignment.+      PendingRecvActionHash+        Bool+        (TranscriptHash -> Handshake13 -> IO ())+ updateMeasure :: Context -> (Measurement -> Measurement) -> IO ()-updateMeasure ctx f = do-    x <- readIORef (ctxMeasurement ctx)-    writeIORef (ctxMeasurement ctx) $! f x+updateMeasure ctx = modifyIORef' (ctxMeasurement ctx)  withMeasure :: Context -> (Measurement -> IO a) -> IO a withMeasure ctx f = readIORef (ctxMeasurement ctx) >>= f +-- | A shortcut for 'backendFlush . ctxBackend'. contextFlush :: Context -> IO ()-contextFlush = backendFlush . ctxConnection+contextFlush = backendFlush . ctxBackend +-- | A shortcut for 'backendClose . ctxBackend'. contextClose :: Context -> IO ()-contextClose = backendClose . ctxConnection+contextClose = backendClose . ctxBackend  -- | Information about the current context contextGetInformation :: Context -> IO (Maybe Information) contextGetInformation ctx = do-    ver    <- usingState_ ctx $ gets stVersion+    ver <- usingState_ ctx $ gets stVersion     hstate <- getHState ctx-    let (ms, cr, sr) = case hstate of-                           Just st -> (hstMasterSecret st,-                                       Just (hstClientRandom st),-                                       hstServerRandom st)-                           Nothing -> (Nothing, Nothing, Nothing)-    (cipher,comp) <- failOnEitherError $ runRxState ctx $ gets $ \st -> (stCipher st, stCompression st)+    let (ms, ems, cr, sr, hm13, grp, ech) =+            case hstate of+                Just st ->+                    ( hstMainSecret st+                    , hstExtendedMainSecret st+                    , Just (hstClientRandom st)+                    , hstServerRandom st+                    , if ver == Just TLS13 then Just (hstTLS13HandshakeMode st) else Nothing+                    , hstSupportedGroup st+                    , hstTLS13ECHAccepted st+                    )+                Nothing -> (Nothing, False, Nothing, Nothing, Nothing, Nothing, False)+    (cipher, comp) <-+        readMVar (ctxRxRecordState ctx) <&> \st -> (stCipher st, stCompression st)+    let accepted = case hstate of+            Just st -> hstTLS13RTT0Status st == RTT0Accepted+            Nothing -> False+    tls12resumption <- usingState_ ctx getTLS12SessionResuming     case (ver, cipher) of-        (Just v, Just c) -> return $ Just $ Information v c comp ms cr sr-        _                -> return Nothing+        (Just v, Just c) ->+            return $+                Just $+                    Information+                        { infoVersion = v+                        , infoCipher = c+                        , infoCompression = comp+                        , infoMainSecret = convert <$> ms+                        , infoExtendedMainSecret = ems+                        , infoClientRandom = cr+                        , infoServerRandom = sr+                        , infoSupportedGroup = grp+                        , infoTLS12Resumption = tls12resumption+                        , infoTLS13HandshakeMode = hm13+                        , infoIsEarlyDataAccepted = accepted+                        , infoIsECHAccepted = ech+                        }+        _ -> return Nothing  contextSend :: Context -> ByteString -> IO ()-contextSend c b = updateMeasure c (addBytesSent $ B.length b) >> (backendSend $ ctxConnection c) b+contextSend c b =+    updateMeasure c (addBytesSent $ B.length b) >> (backendSend $ ctxBackend c) b  contextRecv :: Context -> Int -> IO ByteString-contextRecv c sz = updateMeasure c (addBytesReceived sz) >> (backendRecv $ ctxConnection c) sz+contextRecv c sz = updateMeasure c (addBytesReceived sz) >> (backendRecv $ ctxBackend c) sz  ctxEOF :: Context -> IO Bool ctxEOF ctx = readIORef $ ctxEOF_ ctx -ctxHasSSLv2ClientHello :: Context -> IO Bool-ctxHasSSLv2ClientHello ctx = readIORef $ ctxSSLv2ClientHello ctx--ctxDisableSSLv2ClientHello :: Context -> IO ()-ctxDisableSSLv2ClientHello ctx = writeIORef (ctxSSLv2ClientHello ctx) False- setEOF :: Context -> IO () setEOF ctx = writeIORef (ctxEOF_ ctx) True -ctxEstablished :: Context -> IO Bool+ctxEstablished :: Context -> IO Established ctxEstablished ctx = readIORef $ ctxEstablished_ ctx  ctxWithHooks :: Context -> (Hooks -> IO a) -> IO a ctxWithHooks ctx f = readIORef (ctxHooks ctx) >>= f  contextModifyHooks :: Context -> (Hooks -> Hooks) -> IO ()-contextModifyHooks ctx f = modifyIORef (ctxHooks ctx) f+contextModifyHooks ctx = modifyIORef' (ctxHooks ctx) -setEstablished :: Context -> Bool -> IO ()-setEstablished ctx v = writeIORef (ctxEstablished_ ctx) v+setEstablished :: Context -> Established -> IO ()+setEstablished ctx = writeIORef (ctxEstablished_ ctx)  withLog :: Context -> (Logging -> IO ()) -> IO () withLog ctx f = ctxWithHooks ctx (f . hookLogging) -throwCore :: (MonadIO m, Exception e) => e -> m a-throwCore = liftIO . throwIO+throwCore :: MonadIO m => TLSError -> m a+throwCore = liftIO . throwIO . Uncontextualized  failOnEitherError :: MonadIO m => m (Either TLSError a) -> m a failOnEitherError f = do     ret <- f     case ret of         Left err -> throwCore err-        Right r  -> return r+        Right r -> return r  usingState :: Context -> TLSSt a -> IO (Either TLSError a) usingState ctx f =-    modifyMVar (ctxState ctx) $ \st ->-            let (a, newst) = runTLSState f st-             in newst `seq` return (newst, a)+    modifyMVar (ctxTLSState ctx) $ \st ->+        let (a, newst) = runTLSState f st+         in newst `seq` return (newst, a)  usingState_ :: Context -> TLSSt a -> IO a usingState_ ctx f = failOnEitherError $ usingState ctx f -usingHState :: Context -> HandshakeM a -> IO a-usingHState ctx f = liftIO $ modifyMVar (ctxHandshake ctx) $ \mst ->-    case mst of-        Nothing -> throwCore $ Error_Misc "missing handshake"-        Just st -> return $ swap (Just <$> runHandshake st f)+usingHState :: MonadIO m => Context -> HandshakeM a -> m a+usingHState ctx f = liftIO $ modifyMVar (ctxHandshakeState ctx) $ \case+    Nothing -> liftIO $ throwIO MissingHandshake+    Just st -> return $ swap (Just <$> runHandshake st f) -getHState :: Context -> IO (Maybe HandshakeState)-getHState ctx = liftIO $ readMVar (ctxHandshake ctx)+getHState :: MonadIO m => Context -> m (Maybe HandshakeState)+getHState ctx = liftIO $ readMVar (ctxHandshakeState ctx) -runTxState :: Context -> RecordM a -> IO (Either TLSError a)-runTxState ctx f = do-    ver <- usingState_ ctx (getVersionWithDefault $ maximum $ supportedVersions $ ctxSupported ctx)-    modifyMVar (ctxTxState ctx) $ \st ->-        case runRecordM f ver st of-            Left err         -> return (st, Left err)+saveHState :: Context -> IO (Saved (Maybe HandshakeState))+saveHState ctx = saveMVar (ctxHandshakeState ctx)++restoreHState+    :: Context+    -> Saved (Maybe HandshakeState)+    -> IO (Saved (Maybe HandshakeState))+restoreHState ctx = restoreMVar (ctxHandshakeState ctx)++decideRecordVersion :: Context -> IO (Version, Bool)+decideRecordVersion ctx = usingState_ ctx $ do+    ver <- getVersionWithDefault (maximum $ supportedVersions $ ctxSupported ctx)+    hrr <- getTLS13HRR+    -- For TLS 1.3, ver' is only used in ClientHello.+    -- The record version of the first ClientHello SHOULD be TLS 1.0.+    -- The record version of the second ClientHello MUST be TLS 1.2.+    let ver'+            | ver >= TLS13 = if hrr then TLS12 else TLS10+            | otherwise = ver+    return (ver', ver >= TLS13)++runTxRecordState :: Context -> RecordM a -> IO (Either TLSError a)+runTxRecordState ctx f = do+    (ver, tls13) <- decideRecordVersion ctx+    let opt =+            RecordOptions+                { recordVersion = ver+                , recordTLS13 = tls13+                }+    modifyMVar (ctxTxRecordState ctx) $ \st ->+        case runRecordM f opt st of+            Left err -> return (st, Left err)             Right (a, newSt) -> return (newSt, Right a) -runRxState :: Context -> RecordM a -> IO (Either TLSError a)-runRxState ctx f = do-    ver <- usingState_ ctx getVersion-    modifyMVar (ctxRxState ctx) $ \st ->-        case runRecordM f ver st of-            Left err         -> return (st, Left err)+runRxRecordState :: Context -> RecordM a -> IO (Either TLSError a)+runRxRecordState ctx f = do+    ver <-+        usingState_+            ctx+            (getVersionWithDefault $ maximum $ supportedVersions $ ctxSupported ctx)+    -- For 1.3, ver is just ignored. So, it is not necessary to convert ver.+    let opt =+            RecordOptions+                { recordVersion = ver+                , recordTLS13 = ver >= TLS13+                }+    modifyMVar (ctxRxRecordState ctx) $ \st ->+        case runRecordM f opt st of+            Left err -> return (st, Left err)             Right (a, newSt) -> return (newSt, Right a)  getStateRNG :: Context -> Int -> IO ByteString getStateRNG ctx n = usingState_ ctx $ genRandom n  withReadLock :: Context -> IO a -> IO a-withReadLock ctx f = withMVar (ctxLockRead ctx) (const f)+withReadLock ctx f = withMVar (lockRead $ ctxLocks ctx) (const f)  withWriteLock :: Context -> IO a -> IO a-withWriteLock ctx f = withMVar (ctxLockWrite ctx) (const f)+withWriteLock ctx f = withMVar (lockWrite $ ctxLocks ctx) (const f)  withRWLock :: Context -> IO a -> IO a withRWLock ctx f = withReadLock ctx $ withWriteLock ctx f  withStateLock :: Context -> IO a -> IO a-withStateLock ctx f = withMVar (ctxLockState ctx) (const f)+withStateLock ctx f = withMVar (lockState $ ctxLocks ctx) (const f)++tls13orLater :: MonadIO m => Context -> m Bool+tls13orLater ctx = do+    ev <- liftIO $ usingState ctx $ getVersionWithDefault TLS12+    return $ case ev of+        Left _ -> False+        Right v -> v >= TLS13++--------------------------------++setMyRecordLimit :: Context -> Maybe Int -> IO ()+setMyRecordLimit ctx msiz = modifyIORef' (ctxMyRecordLimit ctx) change+  where+    change (RecordLimit n _) = RecordLimit n msiz+    change x = x++enableMyRecordLimit :: Context -> IO ()+enableMyRecordLimit ctx = modifyIORef' (ctxMyRecordLimit ctx) change+  where+    change (RecordLimit _ (Just n)) = RecordLimit n Nothing+    change x = x++getMyRecordLimit :: Context -> IO (Maybe Int)+getMyRecordLimit ctx = change <$> readIORef (ctxMyRecordLimit ctx)+  where+    change NoRecordLimit = Nothing+    change (RecordLimit n _) = Just n++checkMyRecordLimit :: Context -> IO Bool+checkMyRecordLimit ctx = chk <$> readIORef (ctxMyRecordLimit ctx)+  where+    chk NoRecordLimit = False+    chk (RecordLimit _ mx) = isJust mx++--------------------------------++setPeerRecordLimit :: Context -> Maybe Int -> IO ()+setPeerRecordLimit ctx msiz = modifyIORef' (ctxPeerRecordLimit ctx) change+  where+    change (RecordLimit n _) = RecordLimit n msiz+    change x = x++enablePeerRecordLimit :: Context -> IO ()+enablePeerRecordLimit ctx = modifyIORef' (ctxPeerRecordLimit ctx) change+  where+    change (RecordLimit _ (Just n)) = RecordLimit n Nothing+    change x = x++getPeerRecordLimit :: Context -> IO (Maybe Int)+getPeerRecordLimit ctx = change <$> readIORef (ctxPeerRecordLimit ctx)+  where+    change NoRecordLimit = Nothing+    change (RecordLimit n _) = Just n++checkPeerRecordLimit :: Context -> IO Bool+checkPeerRecordLimit ctx = chk <$> readIORef (ctxPeerRecordLimit ctx)+  where+    chk NoRecordLimit = False+    chk (RecordLimit _ mx) = isJust mx++newRecordLimitRef :: Maybe Int -> IO (IORef RecordLimit)+newRecordLimitRef Nothing = newIORef NoRecordLimit+newRecordLimitRef (Just n) = newIORef $ RecordLimit n Nothing++--------------------------------++setTLS13HPKE :: Context -> HPKEF -> Int -> IO ()+setTLS13HPKE ctx func nenc = writeIORef (ctxHPKE ctx) $ Just (func, nenc)++getTLS13HPKE :: Context -> IO (Maybe (HPKEF, Int))+getTLS13HPKE ctx = readIORef $ ctxHPKE ctx
Network/TLS/Core.hs view
@@ -1,129 +1,512 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ScopedTypeVariables #-} {-# OPTIONS_HADDOCK hide #-}-{-# LANGUAGE OverloadedStrings, ScopedTypeVariables #-}--- |--- Module      : Network.TLS.Core--- License     : BSD-style--- Maintainer  : Vincent Hanquez <vincent@snarc.org>--- Stability   : experimental--- Portability : unknown----module Network.TLS.Core-    (++module Network.TLS.Core (     -- * Internal packet sending and receiving-      sendPacket-    , recvPacket+    sendPacket12,+    recvPacket12,      -- * Initialisation and Termination of context-    , bye-    , handshake+    bye,+    handshake,      -- * Application Layer Protocol Negotiation-    , getNegotiatedProtocol+    getNegotiatedProtocol,      -- * Server Name Indication-    , getClientSNI+    getClientSNI,      -- * High level API-    , sendData-    , recvData-    , recvData'-    ) where+    sendData,+    recvData,+    recvData',+    updateKey,+    KeyUpdateRequest (..),+    requestCertificate,+) where +import qualified Control.Exception as E+import Control.Monad.State.Strict+import qualified Data.ByteString as B+import qualified Data.ByteString.Char8 as C8+import qualified Data.ByteString.Lazy as L+import Data.IORef+import System.Timeout+ import Network.TLS.Context-import Network.TLS.Struct-import Network.TLS.State (getSession)-import Network.TLS.Parameters+import Network.TLS.Extension+import Network.TLS.Handshake+import Network.TLS.Handshake.Common+import Network.TLS.Handshake.Common13+import Network.TLS.Handshake.Server+import Network.TLS.Handshake.State+import Network.TLS.Handshake.State13+import Network.TLS.Handshake.TranscriptHash import Network.TLS.IO+import Network.TLS.Imports+import Network.TLS.Parameters+import Network.TLS.PostHandshake import Network.TLS.Session-import Network.TLS.Handshake-import Network.TLS.Util (catchException)+import Network.TLS.State (getRole, getSession) import qualified Network.TLS.State as S-import qualified Data.ByteString as B-import qualified Data.ByteString.Lazy as L-import qualified Control.Exception as E+import Network.TLS.Struct+import Network.TLS.Struct13+import Network.TLS.Types (+    HostName,+    Role (..),+ )+import Network.TLS.Util (catchException, mapChunks_) -import Control.Monad.State.Strict+-- | Handshake for a new TLS connection+-- This is to be called at the beginning of a connection, and during renegotiation.+-- Don't use this function as the acquire resource of 'bracket'.+handshake :: MonadIO m => Context -> m ()+handshake ctx = do+    handshake_ ctx+    -- Trying to receive an alert of client authentication failure+    liftIO $ do+        role <- usingState_ ctx getRole+        tls13 <- tls13orLater ctx+        sentClientCert <- tls13stSentClientCert <$> getTLS13State ctx+        when (role == ClientRole && tls13 && sentClientCert) $ do+            rtt <- getRTT ctx+            -- This 'timeout' should work.+            mdat <- timeout rtt $ recvData13 ctx+            case mdat of+                Nothing -> return ()+                Just dat -> modifyTLS13State ctx $ \st -> st{tls13stPendingRecvData = Just dat} +rttFactor :: Int+rttFactor = 3 --- | notify the context that this side wants to close connection.--- this is important that it is called before closing the handle, otherwise+getRTT :: Context -> IO Int+getRTT ctx = do+    rtt <- tls13stRTT <$> getTLS13State ctx+    let rtt' = max (fromIntegral rtt) 10+    return (rtt' * rttFactor * 1000) -- ms to us++-- | Notify the context that this side wants to close connection.+-- This is important that it is called before closing the handle, otherwise -- the session might not be resumable (for version < TLS1.2).+-- This doesn't actually close the handle. ----- this doesn't actually close the handle+-- Proper usage is as follows:+--+-- > ctx <- contextNew <backend> <params>+-- > handshake ctx+-- > ...+-- > bye+--+-- The following code ensures nothing but is no harm.+--+-- > bracket (contextNew <backend> <params>) bye $ \ctx -> do+-- >   handshake ctx+-- >   ... bye :: MonadIO m => Context -> m ()-bye ctx = do-  eof <- liftIO $ ctxEOF ctx-  unless eof $ sendPacket ctx $ Alert [(AlertLevel_Warning, CloseNotify)]+bye ctx = liftIO $ do+    eof <- ctxEOF ctx+    tls13 <- tls13orLater ctx+    when (tls13 && not eof) $ do+        role <- usingState_ ctx getRole+        if role == ClientRole+            then do+                withWriteLock ctx $ sendCFifNecessary ctx+                -- receiving NewSessionTicket+                let chk = tls13stRecvNST <$> getTLS13State ctx+                recvNST <- chk+                unless recvNST $ do+                    rtt <- getRTT ctx+                    void $ timeout rtt $ recvHS13 ctx chk+            else do+                -- receiving Client Finished+                let chk = tls13stRecvCF <$> getTLS13State ctx+                recvCF <- chk+                unless recvCF $ do+                    -- no chance to measure RTT before receiving CF+                    -- fixme: 1sec is good enough?+                    let rtt = 1000000+                    void $ timeout rtt $ recvHS13 ctx chk+    bye_ ctx +bye_ :: MonadIO m => Context -> m ()+bye_ ctx = liftIO $ do+    -- Although setEOF is always protected by the read lock, here we don't try+    -- to wrap ctxEOF with it, so that function bye can still be called+    -- concurrently to a blocked recvData.+    eof <- ctxEOF ctx+    tls13 <- tls13orLater ctx+    unless eof $+        withWriteLock ctx $+            if tls13+                then sendPacket13 ctx $ Alert13 [(AlertLevel_Warning, CloseNotify)]+                else sendPacket12 ctx $ Alert [(AlertLevel_Warning, CloseNotify)]+ -- | If the ALPN extensions have been used, this will -- return get the protocol agreed upon.-getNegotiatedProtocol :: MonadIO m => Context -> m (Maybe B.ByteString)+getNegotiatedProtocol :: MonadIO m => Context -> m (Maybe ByteString) getNegotiatedProtocol ctx = liftIO $ usingState_ ctx S.getNegotiatedProtocol -type HostName = String- -- | If the Server Name Indication extension has been used, return the -- hostname specified by the client. getClientSNI :: MonadIO m => Context -> m (Maybe HostName) getClientSNI ctx = liftIO $ usingState_ ctx S.getClientSNI +sendCFifNecessary :: Context -> IO ()+sendCFifNecessary ctx = do+    st <- getTLS13State ctx+    let recvSF = tls13stRecvSF st+        sentCF = tls13stSentCF st+    when (recvSF && not sentCF) $ do+        msend <- readIORef (ctxPendingSendAction ctx)+        case msend of+            Nothing -> return ()+            Just sendAction -> do+                sendAction ctx+                writeIORef (ctxPendingSendAction ctx) Nothing+ -- | sendData sends a bunch of data. -- It will automatically chunk data to acceptable packet size sendData :: MonadIO m => Context -> L.ByteString -> m ()-sendData ctx dataToSend = liftIO (checkValid ctx) >> mapM_ sendDataChunk (L.toChunks dataToSend)-  where sendDataChunk d-            | B.length d > 16384 = do-                let (sending, remain) = B.splitAt 16384 d-                sendPacket ctx $ AppData sending-                sendDataChunk remain-            | otherwise = sendPacket ctx $ AppData d+sendData _ "" = return ()+sendData ctx dataToSend = liftIO $ do+    tls13 <- tls13orLater ctx+    let sendP bs+            | tls13 = do+                sendPacket13 ctx $ AppData13 bs+                role <- usingState_ ctx getRole+                sentCF <- tls13stSentCF <$> getTLS13State ctx+                rtt0 <- tls13st0RTT <$> getTLS13State ctx+                when (role == ClientRole && rtt0 && not sentCF) $+                    modifyTLS13State ctx $+                        \st -> st{tls13stPendingSentData = tls13stPendingSentData st . (bs :)}+            | otherwise = sendPacket12 ctx $ AppData bs+    when tls13 $ withWriteLock ctx $ sendCFifNecessary ctx+    withWriteLock ctx $ do+        checkValid ctx+        -- All chunks are protected with the same write lock because we don't+        -- want to interleave writes from other threads in the middle of our+        -- possibly large write.+        mlen <- getPeerRecordLimit ctx -- plaintext, don't adjust for TLS 1.3+        mapM_ (mapChunks_ mlen sendP) (L.toChunks dataToSend) --- | recvData get data out of Data packet, and automatically renegotiate if--- a Handshake ClientHello is received-recvData :: MonadIO m => Context -> m B.ByteString+-- | Get data out of Data packet, and automatically renegotiate if a Handshake+-- ClientHello is received.  An empty result means EOF.+recvData :: MonadIO m => Context -> m ByteString recvData ctx = liftIO $ do-    checkValid ctx-    pkt <- withReadLock ctx $ recvPacket ctx-    either onError process pkt-  where onError Error_EOF = -- Not really an error.-            return B.empty+    tls13 <- tls13orLater ctx+    withReadLock ctx $ do+        checkValid ctx+        -- We protect with a read lock both reception and processing of the+        -- packet, because don't want another thread to receive a new packet+        -- before this one has been fully processed.+        --+        -- Even when recvData12/recvData13 loops, we only need to call function+        -- checkValid once.  Since we hold the read lock, no concurrent call+        -- will impact the validity of the context.+        if tls13 then recvData13 ctx else recvData12 ctx -        onError err@(Error_Protocol (reason,fatal,desc)) =-            terminate err (if fatal then AlertLevel_Fatal else AlertLevel_Warning) desc reason-        onError err =-            terminate err AlertLevel_Fatal InternalError (show err)+recvData12 :: Context -> IO ByteString+recvData12 ctx = do+    pkt <- recvPacket12 ctx+    either (onError terminate12) process pkt+  where+    process (Handshake [ch@ClientHello{}] [b]) =+        handshakeWith ctx (ch, b) >> recvData12 ctx+    process (Handshake [hr@HelloRequest] [b]) =+        handshakeWith ctx (hr, b) >> recvData12 ctx+    -- UserCanceled should be followed by a close_notify.+    -- fixme: is it safe to call recvData12?+    process (Alert [(AlertLevel_Warning, UserCanceled)]) = return B.empty+    process (Alert [(AlertLevel_Warning, CloseNotify)]) = tryBye ctx >> setEOF ctx >> return B.empty+    process (Alert [(AlertLevel_Fatal, desc)]) = do+        setEOF ctx+        E.throwIO+            ( Terminated+                True+                ("received fatal error: " ++ show desc)+                (Error_Protocol "remote side fatal error" desc)+            ) -        process (Handshake [ch@ClientHello{}]) =-            handshakeWith ctx ch >> recvData ctx-        process (Handshake [hr@HelloRequest]) =-            handshakeWith ctx hr >> recvData ctx+    -- when receiving empty appdata, we just retry to get some data.+    process (AppData "") = recvData12 ctx+    process (AppData x) = return x+    process p = do+        let reason = "unexpected message " ++ show p+        terminate12 (Error_Misc reason) AlertLevel_Fatal UnexpectedMessage reason -        process (Alert [(AlertLevel_Warning, CloseNotify)]) = tryBye >> setEOF ctx >> return B.empty-        process (Alert [(AlertLevel_Fatal, desc)]) = do-            setEOF ctx-            E.throwIO (Terminated True ("received fatal error: " ++ show desc) (Error_Protocol ("remote side fatal error", True, desc)))+    terminate12 = terminateWithWriteLock ctx (sendPacket12 ctx . Alert) -        -- when receiving empty appdata, we just retry to get some data.-        process (AppData "") = recvData ctx-        process (AppData x)  = return x-        process p            = let reason = "unexpected message " ++ show p in-                               terminate (Error_Misc reason) AlertLevel_Fatal UnexpectedMessage reason+recvData13 :: Context -> IO ByteString+recvData13 ctx = do+    mdat <- tls13stPendingRecvData <$> getTLS13State ctx+    case mdat of+        Nothing -> do+            pkt <- recvPacket13 ctx+            either (onError (terminate13 ctx)) process pkt+        Just dat -> do+            modifyTLS13State ctx $ \st -> st{tls13stPendingRecvData = Nothing}+            return dat+  where+    -- UserCanceled MUST be followed by a CloseNotify.+    process (Alert13 [(AlertLevel_Warning, UserCanceled)]) = return B.empty+    process (Alert13 [(AlertLevel_Warning, CloseNotify)]) = tryBye ctx >> setEOF ctx >> return B.empty+    process (Alert13 [(AlertLevel_Fatal, desc)]) = do+        setEOF ctx+        E.throwIO+            ( Terminated+                True+                ("received fatal error: " ++ show desc)+                (Error_Protocol "remote side fatal error" desc)+            )+    process (Handshake13 hs bs) = do+        loopHandshake13 $ zip hs bs+        recvData13 ctx+    -- when receiving empty appdata, we just retry to get some data.+    process (AppData13 "") = recvData13 ctx+    process (AppData13 x) = do+        let chunkLen = C8.length x+        established <- ctxEstablished ctx+        case established of+            EarlyDataAllowed maxSize+                | chunkLen <= maxSize -> do+                    setEstablished ctx $ EarlyDataAllowed (maxSize - chunkLen)+                    return x+                | otherwise ->+                    let reason = "early data overflow"+                     in terminate13 ctx (Error_Misc reason) AlertLevel_Fatal UnexpectedMessage reason+            EarlyDataNotAllowed n+                | n > 0 -> do+                    setEstablished ctx $ EarlyDataNotAllowed (n - 1)+                    recvData13 ctx -- ignore "x"+                | otherwise -> do+                    let reason = "early data deprotect overflow"+                    terminate13 ctx (Error_Misc reason) AlertLevel_Fatal UnexpectedMessage reason+            Established -> return x+            _ -> throwCore $ Error_Protocol "data at not-established" UnexpectedMessage+    process ChangeCipherSpec13 = do+        established <- ctxEstablished ctx+        if established /= Established+            then recvData13 ctx+            else do+                let reason = "CSS after Finished"+                terminate13 ctx (Error_Misc reason) AlertLevel_Fatal UnexpectedMessage reason+    process p = do+        let reason = "unexpected message " ++ show p+        terminate13 ctx (Error_Misc reason) AlertLevel_Fatal UnexpectedMessage reason -        terminate :: TLSError -> AlertLevel -> AlertDescription -> String -> IO a-        terminate err level desc reason = do-            session <- usingState_ ctx getSession-            case session of-                Session Nothing    -> return ()-                Session (Just sid) -> sessionInvalidate (sharedSessionManager $ ctxShared ctx) sid-            catchException (sendPacket ctx $ Alert [(level, desc)]) (\_ -> return ())-            setEOF ctx-            E.throwIO (Terminated False reason err)+    loopHandshake13 [] = return ()+    -- fixme: some implementations send multiple NST at the same time.+    -- Only the first one is used at this moment.+    loopHandshake13 ((NewSessionTicket13 life add nonce (SessionIDorTicket_ ticket) exts, _b) : hbs) = do+        role <- usingState_ ctx S.getRole+        unless (role == ClientRole) $ do+            let reason = "Session ticket is allowed for client only"+            terminate13 ctx (Error_Misc reason) AlertLevel_Fatal UnexpectedMessage reason+        -- This part is similar to handshake code, so protected with+        -- read+write locks (which is also what we use for all calls to the+        -- session manager).+        withWriteLock ctx $ do+            Just resumptionSecret <- usingHState ctx getTLS13ResumptionSecret+            (_, usedCipher, _, _) <- getTxRecordState ctx+            -- mMaxSize is always Just, but anyway+            let extract (EarlyDataIndication mMaxSize) =+                    maybe 0 (fromIntegral . safeNonNegative32) mMaxSize+            let choice = makeCipherChoice TLS13 usedCipher+                psk = derivePSK choice resumptionSecret nonce+                maxSize =+                    lookupAndDecode+                        EID_EarlyData+                        MsgTNewSessionTicket+                        exts+                        0+                        extract+                life7d = min life 604800 -- 7 days max+            tinfo <- createTLS13TicketInfo life7d (Right add) Nothing+            sdata <- getSessionData13 ctx usedCipher tinfo maxSize psk+            let ticket' = B.copy ticket+            void $ sessionEstablish (sharedSessionManager $ ctxShared ctx) ticket' sdata+            modifyTLS13State ctx $ \st -> st{tls13stRecvNST = True}+        loopHandshake13 hbs+    loopHandshake13 ((KeyUpdate13 mode, _b) : hbs) = do+        let multipleKeyUpdate = any (\(h, _) -> isKeyUpdate13 h) hbs+        when multipleKeyUpdate $ do+            let reason = "Multiple KeyUpdate is not allowed in one record"+            terminate13 ctx (Error_Misc reason) AlertLevel_Fatal UnexpectedMessage reason+        when (ctxQUICMode ctx) $ do+            let reason = "KeyUpdate is not allowed for QUIC"+            terminate13 ctx (Error_Misc reason) AlertLevel_Fatal UnexpectedMessage reason+        checkAlignment ctx+        established <- ctxEstablished ctx+        -- Though RFC 8446 Sec 4.6.3 does not clearly says,+        -- unidirectional key update is legal.+        -- So, we don't have to check if this key update is corresponding+        -- to key update (update_requested) which we sent.+        if established == Established+            then do+                keyUpdate ctx getRxRecordState setRxRecordState+                -- Write lock wraps both actions because we don't want another+                -- packet to be sent by another thread before the Tx state is+                -- updated.+                when (mode == UpdateRequested) $ withWriteLock ctx $ do+                    sendPacket13 ctx $ Handshake13 [KeyUpdate13 UpdateNotRequested] []+                    keyUpdate ctx getTxRecordState setTxRecordState+                loopHandshake13 hbs+            else do+                let reason = "received key update before established"+                terminate13 ctx (Error_Misc reason) AlertLevel_Fatal UnexpectedMessage reason+    -- Client only+    loopHandshake13 ((h@CertRequest13{}, _b) : hbs) =+        postHandshakeAuthWith ctx h >> loopHandshake13 hbs+    loopHandshake13 (hb@(h, _) : hbs) = do+        rtt0 <- tls13st0RTT <$> getTLS13State ctx+        when rtt0 $ case h of+            ServerHello13 SH{..} ->+                when (isHelloRetryRequest shRandom) $ do+                    clearTxRecordState ctx+                    let reason = "HRR is not allowed for 0-RTT"+                    terminate13 ctx (Error_Misc reason) AlertLevel_Fatal UnexpectedMessage reason+            _ -> return ()+        cont <- popAction ctx hb+        when cont $ loopHandshake13 hbs -        -- the other side could have close the connection already, so wrap-        -- this in a try and ignore all exceptions-        tryBye = catchException (bye ctx) (\_ -> return ())+recvHS13 :: Context -> IO Bool -> IO ()+recvHS13 ctx breakLoop = do+    pkt <- recvPacket13 ctx+    -- fixme: Left+    either (\_ -> return ()) process pkt+  where+    -- UserCanceled MUST be followed by a CloseNotify.+    process (Alert13 [(AlertLevel_Warning, CloseNotify)]) = tryBye ctx >> setEOF ctx+    process (Alert13 [(AlertLevel_Fatal, _desc)]) = setEOF ctx+    process (Handshake13 hs bs) = do+        loopHandshake13 $ zip hs bs+        stop <- breakLoop+        unless stop $ recvHS13 ctx breakLoop+    process _ = recvHS13 ctx breakLoop +    loopHandshake13 [] = return ()+    -- fixme: some implementations send multiple NST at the same time.+    -- Only the first one is used at this moment.+    loopHandshake13 ((NewSessionTicket13 life add nonce (SessionIDorTicket_ ticket) exts, _b) : hbs) = do+        role <- usingState_ ctx S.getRole+        unless (role == ClientRole) $ do+            let reason = "Session ticket is allowed for client only"+            terminate13 ctx (Error_Misc reason) AlertLevel_Fatal UnexpectedMessage reason+        -- This part is similar to handshake code, so protected with+        -- read+write locks (which is also what we use for all calls to the+        -- session manager).+        withWriteLock ctx $ do+            Just resumptionSecret <- usingHState ctx getTLS13ResumptionSecret+            (_, usedCipher, _, _) <- getTxRecordState ctx+            let choice = makeCipherChoice TLS13 usedCipher+                psk = derivePSK choice resumptionSecret nonce+                maxSize =+                    lookupAndDecode+                        EID_EarlyData+                        MsgTNewSessionTicket+                        exts+                        0+                        (\(EarlyDataIndication mms) -> fromIntegral $ safeNonNegative32 $ fromJust mms)+                life7d = min life 604800 -- 7 days max+            tinfo <- createTLS13TicketInfo life7d (Right add) Nothing+            sdata <- getSessionData13 ctx usedCipher tinfo maxSize psk+            let ticket' = B.copy ticket+            void $ sessionEstablish (sharedSessionManager $ ctxShared ctx) ticket' sdata+            modifyTLS13State ctx $ \st -> st{tls13stRecvNST = True}+        loopHandshake13 hbs+    loopHandshake13 (hb : hbs) = do+        cont <- popAction ctx hb+        when cont $ loopHandshake13 hbs++terminate13+    :: Context -> TLSError -> AlertLevel -> AlertDescription -> String -> IO a+terminate13 ctx = terminateWithWriteLock ctx (sendPacket13 ctx . Alert13)++popAction :: Context -> Handshake13R -> IO Bool+popAction ctx hb@(h, _b) = do+    mPendingRecvAction <- popPendingRecvAction ctx+    case mPendingRecvAction of+        Nothing -> return False+        Just action -> do+            -- Pending actions are executed with read+write locks, just+            -- like regular handshake code.+            withWriteLock ctx $+                handleException ctx $ do+                    case action of+                        PendingRecvAction needAligned pa -> do+                            when needAligned $ checkAlignment ctx+                            updateTranscriptHash13 ctx hb+                            pa h+                        PendingRecvActionSelfUpdate needAligned pa -> do+                            when needAligned $ checkAlignment ctx+                            pa hb+                        PendingRecvActionHash needAligned pa -> do+                            when needAligned $ checkAlignment ctx+                            d <- transcriptHash ctx "Pending action"+                            updateTranscriptHash13 ctx hb+                            pa d h+                    -- Client: after receiving SH, app data is coming.+                    -- this loop tries to receive it.+                    -- App key must be installed before receiving+                    -- the app data.+                    sendCFifNecessary ctx+            return True++checkAlignment :: Context -> IO ()+checkAlignment ctx = do+    complete <- isRecvComplete ctx+    unless complete $ do+        let reason = "received message not aligned with record boundary"+        terminate13 ctx (Error_Misc reason) AlertLevel_Fatal UnexpectedMessage reason++-- the other side could have close the connection already, so wrap+-- this in a try and ignore all exceptions+tryBye :: Context -> IO ()+tryBye ctx = catchException (bye_ ctx) (\_ -> return ())++onError+    :: Monad m+    => (TLSError -> AlertLevel -> AlertDescription -> String -> m ByteString)+    -> TLSError+    -> m ByteString+onError _ Error_EOF =+    -- Not really an error.+    return B.empty+onError terminate err = terminate err lvl ad reason+  where+    (lvl, ad) = errorToAlert err+    reason = errorToAlertMessage err++terminateWithWriteLock+    :: Context+    -> ([(AlertLevel, AlertDescription)] -> IO ())+    -> TLSError+    -> AlertLevel+    -> AlertDescription+    -> String+    -> IO a+terminateWithWriteLock ctx send err level desc reason = withWriteLock ctx $ do+    tls13 <- tls13orLater ctx+    unless tls13 $ do+        -- TLS 1.2 uses the same session ID and session data+        -- for all resumed sessions.+        --+        -- TLS 1.3 changes session data for every resumed session.+        session <- usingState_ ctx getSession+        case session of+            Session Nothing -> return ()+            Session (Just sid) ->+                -- calling even session ticket manager anyway+                sessionInvalidate (sharedSessionManager $ ctxShared ctx) sid+    catchException (send [(level, desc)]) (\_ -> return ())+    setEOF ctx+    debugError (ctxDebug ctx) reason+    E.throwIO (Terminated False reason err)+ {-# DEPRECATED recvData' "use recvData that returns strict bytestring" #-}+ -- | same as recvData but returns a lazy bytestring. recvData' :: MonadIO m => Context -> m L.ByteString-recvData' ctx = recvData ctx >>= return . L.fromChunks . (:[])+recvData' ctx = L.fromChunks . (: []) <$> recvData ctx
Network/TLS/Credentials.hs view
@@ -1,43 +1,36 @@--- |--- Module      : Network.TLS.Credentials--- License     : BSD-style--- Maintainer  : Vincent Hanquez <vincent@snarc.org>--- Stability   : experimental--- Portability : unknown--- {-# LANGUAGE CPP #-}-module Network.TLS.Credentials-    ( Credential-    , Credentials(..)-    , credentialLoadX509-    , credentialLoadX509FromMemory-    , credentialLoadX509Chain-    , credentialLoadX509ChainFromMemory-    , credentialsFindForSigning-    , credentialsFindForDecrypting-    , credentialsListSigningAlgorithms-    , credentialPublicPrivateKeys-    , credentialMatchesHashSignatures-    ) where+{-# OPTIONS_GHC -Wno-orphans #-} -import Network.TLS.Crypto-import Network.TLS.X509-import Network.TLS.Imports+module Network.TLS.Credentials (+    Credential,+    Credentials (..),+    credentialLoadX509,+    credentialLoadX509FromMemory,+    credentialLoadX509Chain,+    credentialLoadX509ChainFromMemory,+    credentialsFindForSigning,+    credentialsFindForDecrypting,+    credentialsListSigningAlgorithms,+    credentialPublicPrivateKeys,+    credentialMatchesHashSignatures,+) where++import Data.X509+import qualified Data.X509 as X509 import Data.X509.File import Data.X509.Memory-import Data.X509 -import qualified Data.X509             as X509-import qualified Network.TLS.Struct    as TLS+import Network.TLS.Crypto+import Network.TLS.Imports+import qualified Network.TLS.Struct as TLS+import Network.TLS.X509  type Credential = (CertificateChain, PrivKey) -newtype Credentials = Credentials [Credential]+newtype Credentials = Credentials [Credential] deriving (Show) -#if MIN_VERSION_base(4,9,0) instance Semigroup Credentials where     Credentials l1 <> Credentials l2 = Credentials (l1 ++ l2)-#endif  instance Monoid Credentials where     mempty = Credentials []@@ -48,60 +41,71 @@ -- | try to create a new credential object from a public certificate -- and the associated private key that are stored on the filesystem -- in PEM format.-credentialLoadX509 :: FilePath -- ^ public certificate (X.509 format)-                   -> FilePath -- ^ private key associated-                   -> IO (Either String Credential)+credentialLoadX509+    :: FilePath+    -- ^ public certificate (X.509 format)+    -> FilePath+    -- ^ private key associated+    -> IO (Either String Credential) credentialLoadX509 certFile = credentialLoadX509Chain certFile []  -- | similar to 'credentialLoadX509' but take the certificate -- and private key from memory instead of from the filesystem.-credentialLoadX509FromMemory :: ByteString-                  -> ByteString-                  -> Either String Credential+credentialLoadX509FromMemory+    :: ByteString+    -> ByteString+    -> Either String Credential credentialLoadX509FromMemory certData =-  credentialLoadX509ChainFromMemory certData []+    credentialLoadX509ChainFromMemory certData []  -- | similar to 'credentialLoadX509' but also allow specifying chain -- certificates.-credentialLoadX509Chain ::-                      FilePath   -- ^ public certificate (X.509 format)-                   -> [FilePath] -- ^ chain certificates (X.509 format)-                   -> FilePath   -- ^ private key associated-                   -> IO (Either String Credential)+credentialLoadX509Chain+    :: FilePath+    -- ^ public certificate (X.509 format)+    -> [FilePath]+    -- ^ chain certificates (X.509 format)+    -> FilePath+    -- ^ private key associated+    -> IO (Either String Credential) credentialLoadX509Chain certFile chainFiles privateFile = do     x509 <- readSignedObject certFile     chains <- mapM readSignedObject chainFiles     keys <- readKeyFile privateFile     case keys of-        []    -> return $ Left "no keys found"-        (k:_) -> return $ Right (CertificateChain . concat $ x509 : chains, k)+        [] -> return $ Left "no keys found"+        (k : _) -> return $ Right (CertificateChain . concat $ x509 : chains, k)  -- | similar to 'credentialLoadX509FromMemory' but also allow -- specifying chain certificates.-credentialLoadX509ChainFromMemory :: ByteString-                  -> [ByteString]-                  -> ByteString-                  -> Either String Credential-credentialLoadX509ChainFromMemory certData chainData privateData = do-    let x509   = readSignedObjectFromMemory certData+credentialLoadX509ChainFromMemory+    :: ByteString+    -> [ByteString]+    -> ByteString+    -> Either String Credential+credentialLoadX509ChainFromMemory certData chainData privateData =+    let x509 = readSignedObjectFromMemory certData         chains = map readSignedObjectFromMemory chainData-        keys   = readKeyFileFromMemory privateData+        keys = readKeyFileFromMemory privateData      in case keys of-            []    -> Left "no keys found"-            (k:_) -> Right (CertificateChain . concat $ x509 : chains, k)+            [] -> Left "no keys found"+            (k : _) -> Right (CertificateChain . concat $ x509 : chains, k) -credentialsListSigningAlgorithms :: Credentials -> [DigitalSignatureAlg]+credentialsListSigningAlgorithms :: Credentials -> [KeyExchangeSignatureAlg] credentialsListSigningAlgorithms (Credentials l) = mapMaybe credentialCanSign l -credentialsFindForSigning :: DigitalSignatureAlg -> Credentials -> Maybe Credential-credentialsFindForSigning sigAlg (Credentials l) = find forSigning l-  where forSigning cred = case credentialCanSign cred of-            Nothing  -> False-            Just sig -> sig == sigAlg+credentialsFindForSigning+    :: KeyExchangeSignatureAlg -> Credentials -> Maybe Credential+credentialsFindForSigning kxsAlg (Credentials l) = find forSigning l+  where+    forSigning cred = case credentialCanSign cred of+        Nothing -> False+        Just kxs -> kxs == kxsAlg  credentialsFindForDecrypting :: Credentials -> Maybe Credential credentialsFindForDecrypting (Credentials l) = find forEncrypting l-  where forEncrypting cred = Just () == credentialCanDecrypt cred+  where+    forEncrypting cred = Just () == credentialCanDecrypt cred  -- here we assume that only RSA is supported for key encipherment (encryption/decryption) -- we keep the same construction as 'credentialCanSign', returning a Maybe of () in case@@ -111,67 +115,72 @@     case (pub, priv) of         (PubKeyRSA _, PrivKeyRSA _) ->             case extensionGet (certExtensions cert) of-                Nothing                                     -> Just ()+                Nothing -> Just ()                 Just (ExtKeyUsage flags)                     | KeyUsage_keyEncipherment `elem` flags -> Just ()-                    | otherwise                             -> Nothing-        _                           -> Nothing-    where cert   = signedObject $ getSigned signed-          pub    = certPubKey cert-          signed = getCertificateChainLeaf chain+                    | otherwise -> Nothing+        _ -> Nothing+  where+    cert = getCertificate signed+    pub = certPubKey cert+    signed = getCertificateChainLeaf chain -credentialCanSign :: Credential -> Maybe DigitalSignatureAlg+credentialCanSign :: Credential -> Maybe KeyExchangeSignatureAlg credentialCanSign (chain, priv) =     case extensionGet (certExtensions cert) of-        Nothing    -> findDigitalSignatureAlg (pub, priv)+        Nothing -> findKeyExchangeSignatureAlg (pub, priv)         Just (ExtKeyUsage flags)-            | KeyUsage_digitalSignature `elem` flags -> findDigitalSignatureAlg (pub, priv)-            | otherwise                              -> Nothing-    where cert   = signedObject $ getSigned signed-          pub    = certPubKey cert-          signed = getCertificateChainLeaf chain+            | KeyUsage_digitalSignature `elem` flags ->+                findKeyExchangeSignatureAlg (pub, priv)+            | otherwise -> Nothing+  where+    cert = getCertificate signed+    pub = certPubKey cert+    signed = getCertificateChainLeaf chain  credentialPublicPrivateKeys :: Credential -> (PubKey, PrivKey) credentialPublicPrivateKeys (chain, priv) = pub `seq` (pub, priv)-    where cert   = signedObject $ getSigned signed-          pub    = certPubKey cert-          signed = getCertificateChainLeaf chain+  where+    cert = getCertificate signed+    pub = certPubKey cert+    signed = getCertificateChainLeaf chain  getHashSignature :: SignedCertificate -> Maybe TLS.HashAndSignatureAlgorithm getHashSignature signed =     case signedAlg $ getSigned signed of-        SignatureALG hashAlg PubKeyALG_RSA    -> convertHash TLS.SignatureRSA   hashAlg-        SignatureALG hashAlg PubKeyALG_DSA    -> convertHash TLS.SignatureDSS   hashAlg-        SignatureALG hashAlg PubKeyALG_EC     -> convertHash TLS.SignatureECDSA hashAlg--        SignatureALG X509.HashSHA256 PubKeyALG_RSAPSS -> Just (TLS.HashIntrinsic, TLS.SignatureRSApssSHA256)-        SignatureALG X509.HashSHA384 PubKeyALG_RSAPSS -> Just (TLS.HashIntrinsic, TLS.SignatureRSApssSHA384)-        SignatureALG X509.HashSHA512 PubKeyALG_RSAPSS -> Just (TLS.HashIntrinsic, TLS.SignatureRSApssSHA512)--        _                                     -> Nothing+        SignatureALG hashAlg PubKeyALG_RSA -> convertHash TLS.SignatureRSA hashAlg+        SignatureALG hashAlg PubKeyALG_DSA -> convertHash TLS.SignatureDSA hashAlg+        SignatureALG hashAlg PubKeyALG_EC -> convertHash TLS.SignatureECDSA hashAlg+        SignatureALG X509.HashSHA256 PubKeyALG_RSAPSS -> Just (TLS.HashIntrinsic, TLS.SignatureRSApssRSAeSHA256)+        SignatureALG X509.HashSHA384 PubKeyALG_RSAPSS -> Just (TLS.HashIntrinsic, TLS.SignatureRSApssRSAeSHA384)+        SignatureALG X509.HashSHA512 PubKeyALG_RSAPSS -> Just (TLS.HashIntrinsic, TLS.SignatureRSApssRSAeSHA512)+        SignatureALG_IntrinsicHash PubKeyALG_Ed25519 -> Just (TLS.HashIntrinsic, TLS.SignatureEd25519)+        SignatureALG_IntrinsicHash PubKeyALG_Ed448 -> Just (TLS.HashIntrinsic, TLS.SignatureEd448)+        _ -> Nothing   where-    convertHash sig X509.HashMD5    = Just (TLS.HashMD5   , sig)-    convertHash sig X509.HashSHA1   = Just (TLS.HashSHA1  , sig)+    convertHash sig X509.HashMD5 = Just (TLS.HashMD5, sig)+    convertHash sig X509.HashSHA1 = Just (TLS.HashSHA1, sig)     convertHash sig X509.HashSHA224 = Just (TLS.HashSHA224, sig)     convertHash sig X509.HashSHA256 = Just (TLS.HashSHA256, sig)     convertHash sig X509.HashSHA384 = Just (TLS.HashSHA384, sig)     convertHash sig X509.HashSHA512 = Just (TLS.HashSHA512, sig)-    convertHash _   _               = Nothing+    convertHash _ _ = Nothing --- | Checks whether certificates in the chain comply with a list of--- hash/signature algorithm pairs.  Currently the verification applies only--- to the leaf certificate, if it is not self-signed.  This may be extended--- to additional chain elements in the future.-credentialMatchesHashSignatures :: [TLS.HashAndSignatureAlgorithm] -> Credential -> Bool+-- | Checks whether certificate signatures in the chain comply with a list of+-- hash/signature algorithm pairs.  Currently the verification applies only to+-- the signature of the leaf certificate, and when not self-signed.  This may+-- be extended to additional chain elements in the future.+credentialMatchesHashSignatures+    :: [TLS.HashAndSignatureAlgorithm] -> Credential -> Bool credentialMatchesHashSignatures hashSigs (chain, _) =     case chain of-        CertificateChain []       -> True-        CertificateChain (leaf:_) -> isSelfSigned leaf || matchHashSig leaf+        CertificateChain [] -> True+        CertificateChain (leaf : _) -> isSelfSigned leaf || matchHashSig leaf   where     matchHashSig signed = case getHashSignature signed of-                              Nothing -> False-                              Just hs -> hs `elem` hashSigs+        Nothing -> False+        Just hs -> hs `elem` hashSigs      isSelfSigned signed =-        let cert = signedObject $ getSigned signed+        let cert = getCertificate signed          in certSubjectDN cert == certIssuerDN cert
Network/TLS/Crypto.hs view
@@ -1,153 +1,204 @@-{-# OPTIONS_HADDOCK hide #-} {-# LANGUAGE ExistentialQuantification #-}-module Network.TLS.Crypto-    ( HashContext-    , HashCtx-    , hashInit-    , hashUpdate-    , hashUpdateSSL-    , hashFinal+{-# LANGUAGE RankNTypes #-}+{-# OPTIONS_HADDOCK hide #-} -    , module Network.TLS.Crypto.DH-    , module Network.TLS.Crypto.IES-    , module Network.TLS.Crypto.Types+module Network.TLS.Crypto (+    HashContext,+    HashCtx,+    hashInit,+    hashUpdate,+    hashUpdates,+    hashUpdateSSL,+    hashFinal,+    module Network.TLS.Crypto.DH,+    module Network.TLS.Crypto.IES,+    module Network.TLS.Crypto.Types,      -- * Hash-    , hash-    , Hash(..)-    , hashName-    , hashDigestSize-    , hashBlockSize+    hash,+    hashChunks,+    Hash (..),+    hashName,+    hashDigestSize,+    hashBlockSize,      -- * key exchange generic interface-    , PubKey(..)-    , PrivKey(..)-    , PublicKey-    , PrivateKey-    , SignatureParams(..)-    , findDigitalSignatureAlg-    , findFiniteFieldGroup-    , kxEncrypt-    , kxDecrypt-    , kxSign-    , kxVerify-    , KxError(..)-    , RSAEncoding(..)-    ) where+    PubKey (..),+    PrivKey (..),+    PublicKey,+    PrivateKey,+    SignatureParams (..),+    isKeyExchangeSignatureKey,+    findKeyExchangeSignatureAlg,+    findFiniteFieldGroup,+    findEllipticCurveGroup,+    kxEncrypt,+    kxDecrypt,+    kxSign,+    kxVerify,+    kxCanUseRSApkcs1,+    kxCanUseRSApss,+    kxSupportedPrivKeyEC,+    KxError (..),+    RSAEncoding (..),+) where +import qualified Crypto.ECC as ECDSA+import Crypto.Error import qualified Crypto.Hash as H-import qualified Data.ByteString as B-import qualified Data.ByteArray as B (convert)-import Crypto.Random+import Crypto.Number.Basic (numBits) import qualified Crypto.PubKey.DH as DH import qualified Crypto.PubKey.DSA as DSA-import qualified Crypto.PubKey.ECC.ECDSA as ECDSA-import qualified Crypto.PubKey.ECC.Prim as ECC+import qualified Crypto.PubKey.ECC.ECDSA as ECDSA_ECC import qualified Crypto.PubKey.ECC.Types as ECC+import qualified Crypto.PubKey.ECDSA as ECDSA+import qualified Crypto.PubKey.Ed25519 as Ed25519+import qualified Crypto.PubKey.Ed448 as Ed448 import qualified Crypto.PubKey.RSA as RSA import qualified Crypto.PubKey.RSA.PKCS15 as RSA import qualified Crypto.PubKey.RSA.PSS as PSS-import Crypto.Number.Serialize (os2ip)+import Crypto.Random+import Data.ASN1.BinaryEncoding (BER (..), DER (..))+import Data.ASN1.Encoding+import Data.ASN1.Types+import Data.ByteArray (ByteArray, ByteArrayAccess, ScrubbedBytes, convert)+import qualified Data.ByteArray as BA+import qualified Data.ByteString as B+import Data.Proxy+import Data.X509 (+    PrivKey (..),+    PrivKeyEC (..),+    PubKey (..),+    PubKeyEC (..),+    SerializedPoint (..),+ )+import Data.X509.EC (ecPrivKeyCurveName, ecPubKeyCurveName, unserializePoint) -import Data.X509 (PrivKey(..), PubKey(..), PubKeyEC(..), SerializedPoint(..)) import Network.TLS.Crypto.DH import Network.TLS.Crypto.IES import Network.TLS.Crypto.Types import Network.TLS.Imports -import Data.ASN1.Types-import Data.ASN1.Encoding-import Data.ASN1.BinaryEncoding (DER(..), BER(..))+----------------------------------------------------------------  {-# DEPRECATED PublicKey "use PubKey" #-} type PublicKey = PubKey {-# DEPRECATED PrivateKey "use PrivKey" #-} type PrivateKey = PrivKey -data KxError =-      RSAError RSA.Error+data KxError+    = RSAError RSA.Error     | KxUnsupported     deriving (Show) -findDigitalSignatureAlg :: (PubKey, PrivKey) -> Maybe DigitalSignatureAlg-findDigitalSignatureAlg keyPair =+isKeyExchangeSignatureKey :: KeyExchangeSignatureAlg -> PubKey -> Bool+isKeyExchangeSignatureKey = f+  where+    f KX_RSA (PubKeyRSA _) = True+    f KX_DSA (PubKeyDSA _) = True+    f KX_ECDSA (PubKeyEC _) = True+    f KX_ECDSA (PubKeyEd25519 _) = True+    f KX_ECDSA (PubKeyEd448 _) = True+    f _ _ = False++findKeyExchangeSignatureAlg+    :: (PubKey, PrivKey) -> Maybe KeyExchangeSignatureAlg+findKeyExchangeSignatureAlg keyPair =     case keyPair of-        (PubKeyRSA     _, PrivKeyRSA      _)  -> Just RSA-        (PubKeyDSA     _, PrivKeyDSA      _)  -> Just DSS-        --(PubKeyECDSA   _, PrivKeyECDSA    _)  -> Just ECDSA-        _                                     -> Nothing+        (PubKeyRSA _, PrivKeyRSA _) -> Just KX_RSA+        (PubKeyDSA _, PrivKeyDSA _) -> Just KX_DSA+        (PubKeyEC _, PrivKeyEC _) -> Just KX_ECDSA+        (PubKeyEd25519 _, PrivKeyEd25519 _) -> Just KX_ECDSA+        (PubKeyEd448 _, PrivKeyEd448 _) -> Just KX_ECDSA+        _ -> Nothing  findFiniteFieldGroup :: DH.Params -> Maybe Group findFiniteFieldGroup params = lookup (pg params) table   where     pg (DH.Params p g _) = (p, g) -    table = [ (pg prms, grp) | grp <- availableFFGroups-                             , let Just prms = dhParamsForGroup grp-            ]+    table =+        [ (pg prms, grp)+        | grp <- availableFFGroups+        , let prms = fromJust $ dhParamsForGroup grp+        ] +findEllipticCurveGroup :: PubKeyEC -> Maybe Group+findEllipticCurveGroup ecPub =+    case ecPubKeyCurveName ecPub of+        Just ECC.SEC_p256r1 -> Just P256+        Just ECC.SEC_p384r1 -> Just P384+        Just ECC.SEC_p521r1 -> Just P521+        _ -> Nothing+ -- functions to use the hidden class. hashInit :: Hash -> HashContext-hashInit MD5      = HashContext $ ContextSimple (H.hashInit :: H.Context H.MD5)-hashInit SHA1     = HashContext $ ContextSimple (H.hashInit :: H.Context H.SHA1)-hashInit SHA224   = HashContext $ ContextSimple (H.hashInit :: H.Context H.SHA224)-hashInit SHA256   = HashContext $ ContextSimple (H.hashInit :: H.Context H.SHA256)-hashInit SHA384   = HashContext $ ContextSimple (H.hashInit :: H.Context H.SHA384)-hashInit SHA512   = HashContext $ ContextSimple (H.hashInit :: H.Context H.SHA512)+hashInit MD5 = HashContext $ ContextSimple (H.hashInit :: H.Context H.MD5)+hashInit SHA1 = HashContext $ ContextSimple (H.hashInit :: H.Context H.SHA1)+hashInit SHA224 = HashContext $ ContextSimple (H.hashInit :: H.Context H.SHA224)+hashInit SHA256 = HashContext $ ContextSimple (H.hashInit :: H.Context H.SHA256)+hashInit SHA384 = HashContext $ ContextSimple (H.hashInit :: H.Context H.SHA384)+hashInit SHA512 = HashContext $ ContextSimple (H.hashInit :: H.Context H.SHA512) hashInit SHA1_MD5 = HashContextSSL H.hashInit H.hashInit -hashUpdate :: HashContext -> B.ByteString -> HashCtx+hashUpdate :: HashContext -> ByteString -> HashCtx hashUpdate (HashContext (ContextSimple h)) b = HashContext $ ContextSimple (H.hashUpdate h b) hashUpdate (HashContextSSL sha1Ctx md5Ctx) b =     HashContextSSL (H.hashUpdate sha1Ctx b) (H.hashUpdate md5Ctx b) -hashUpdateSSL :: HashCtx-              -> (B.ByteString,B.ByteString) -- ^ (for the md5 context, for the sha1 context)-              -> HashCtx+hashUpdates :: HashContext -> [ByteString] -> HashCtx+hashUpdates (HashContext (ContextSimple h)) xs = HashContext $ ContextSimple (H.hashUpdates h xs)+hashUpdates (HashContextSSL sha1Ctx md5Ctx) xs =+    HashContextSSL (H.hashUpdates sha1Ctx xs) (H.hashUpdates md5Ctx xs)++hashChunks :: Hash -> [ByteString] -> ByteString+hashChunks h xs = hashFinal $ hashUpdates (hashInit h) xs++hashUpdateSSL+    :: HashCtx+    -> (ByteString, ByteString)+    -- ^ (for the md5 context, for the sha1 context)+    -> HashCtx hashUpdateSSL (HashContext _) _ = error "internal error: update SSL without a SSL Context"-hashUpdateSSL (HashContextSSL sha1Ctx md5Ctx) (b1,b2) =+hashUpdateSSL (HashContextSSL sha1Ctx md5Ctx) (b1, b2) =     HashContextSSL (H.hashUpdate sha1Ctx b2) (H.hashUpdate md5Ctx b1) -hashFinal :: HashCtx -> B.ByteString-hashFinal (HashContext (ContextSimple h)) = B.convert $ H.hashFinalize h+hashFinal :: HashCtx -> ByteString+hashFinal (HashContext (ContextSimple h)) = convert $ H.hashFinalize h hashFinal (HashContextSSL sha1Ctx md5Ctx) =-    B.concat [B.convert (H.hashFinalize md5Ctx), B.convert (H.hashFinalize sha1Ctx)]+    B.concat [convert (H.hashFinalize md5Ctx), convert (H.hashFinalize sha1Ctx)]  data Hash = MD5 | SHA1 | SHA224 | SHA256 | SHA384 | SHA512 | SHA1_MD5-    deriving (Show,Eq)+    deriving (Show, Eq) -data HashContext =-      HashContext ContextSimple+data HashContext+    = HashContext ContextSimple     | HashContextSSL (H.Context H.SHA1) (H.Context H.MD5)  instance Show HashContext where     show _ = "hash-context" -data ContextSimple = forall alg . H.HashAlgorithm alg => ContextSimple (H.Context alg)+data ContextSimple+    = forall alg. H.HashAlgorithm alg => ContextSimple (H.Context alg)  type HashCtx = HashContext -hash :: Hash -> B.ByteString -> B.ByteString-hash MD5 b      = B.convert . (H.hash :: B.ByteString -> H.Digest H.MD5) $ b-hash SHA1 b     = B.convert . (H.hash :: B.ByteString -> H.Digest H.SHA1) $ b-hash SHA224 b   = B.convert . (H.hash :: B.ByteString -> H.Digest H.SHA224) $ b-hash SHA256 b   = B.convert . (H.hash :: B.ByteString -> H.Digest H.SHA256) $ b-hash SHA384 b   = B.convert . (H.hash :: B.ByteString -> H.Digest H.SHA384) $ b-hash SHA512 b   = B.convert . (H.hash :: B.ByteString -> H.Digest H.SHA512) $ b-hash SHA1_MD5 b =-    B.concat [B.convert (md5Hash b), B.convert (sha1Hash b)]-  where-    sha1Hash :: B.ByteString -> H.Digest H.SHA1-    sha1Hash = H.hash-    md5Hash :: B.ByteString -> H.Digest H.MD5-    md5Hash = H.hash+hash :: (ByteArray ba, ByteArrayAccess ba) => Hash -> ba -> ba+hash MD5 b = convert (H.hash b :: H.Digest H.MD5)+hash SHA1 b = convert (H.hash b :: H.Digest H.SHA1)+hash SHA224 b = convert (H.hash b :: H.Digest H.SHA224)+hash SHA256 b = convert (H.hash b :: H.Digest H.SHA256)+hash SHA384 b = convert (H.hash b :: H.Digest H.SHA384)+hash SHA512 b = convert (H.hash b :: H.Digest H.SHA512)+hash SHA1_MD5 b = BA.concat [hash MD5 b, hash SHA1 b]  hashName :: Hash -> String hashName = show +-- | Digest size in bytes. hashDigestSize :: Hash -> Int-hashDigestSize MD5    = 16-hashDigestSize SHA1   = 20+hashDigestSize MD5 = 16+hashDigestSize SHA1 = 20 hashDigestSize SHA224 = 28 hashDigestSize SHA256 = 32 hashDigestSize SHA384 = 48@@ -155,8 +206,8 @@ hashDigestSize SHA1_MD5 = 36  hashBlockSize :: Hash -> Int-hashBlockSize MD5    = 64-hashBlockSize SHA1   = 64+hashBlockSize MD5 = 64+hashBlockSize SHA1 = 64 hashBlockSize SHA224 = 64 hashBlockSize SHA256 = 64 hashBlockSize SHA384 = 128@@ -166,153 +217,286 @@ {- key exchange methods encrypt and decrypt for each supported algorithm -}  generalizeRSAError :: Either RSA.Error a -> Either KxError a-generalizeRSAError (Left e)  = Left (RSAError e)+generalizeRSAError (Left e) = Left (RSAError e) generalizeRSAError (Right x) = Right x -kxEncrypt :: MonadRandom r => PublicKey -> ByteString -> r (Either KxError ByteString)+kxEncrypt+    :: MonadRandom r => PublicKey -> ScrubbedBytes -> r (Either KxError ByteString) kxEncrypt (PubKeyRSA pk) b = generalizeRSAError <$> RSA.encrypt pk b-kxEncrypt _              _ = return (Left KxUnsupported)+kxEncrypt _ _ = return (Left KxUnsupported) -kxDecrypt :: MonadRandom r => PrivateKey -> ByteString -> r (Either KxError ByteString)+kxDecrypt+    :: MonadRandom r => PrivateKey -> ByteString -> r (Either KxError ScrubbedBytes) kxDecrypt (PrivKeyRSA pk) b = generalizeRSAError <$> RSA.decryptSafer pk b-kxDecrypt _               _ = return (Left KxUnsupported)+kxDecrypt _ _ = return (Left KxUnsupported) -data RSAEncoding = RSApkcs1 | RSApss deriving (Show,Eq)+data RSAEncoding = RSApkcs1 | RSApss deriving (Show, Eq) +-- | Test the RSASSA-PKCS1 length condition described in RFC 8017 section 9.2,+-- i.e. @emLen >= tLen + 11@.  Lengths are in bytes.+kxCanUseRSApkcs1 :: RSA.PublicKey -> Hash -> Bool+kxCanUseRSApkcs1 pk h = RSA.public_size pk >= tLen + 11+  where+    tLen = prefixSize h + hashDigestSize h++    prefixSize MD5 = 18+    prefixSize SHA1 = 15+    prefixSize SHA224 = 19+    prefixSize SHA256 = 19+    prefixSize SHA384 = 19+    prefixSize SHA512 = 19+    prefixSize _ = error (show h ++ " is not supported for RSASSA-PKCS1")++-- | Test the RSASSA-PSS length condition described in RFC 8017 section 9.1.1,+-- i.e. @emBits >= 8hLen + 8sLen + 9@.  Lengths are in bits.+kxCanUseRSApss :: RSA.PublicKey -> Hash -> Bool+kxCanUseRSApss pk h = numBits (RSA.public_n pk) >= 16 * hashDigestSize h + 10+ -- Signature algorithm and associated parameters. ----- FIXME add RSAPSSParams, Ed25519Params, Ed448Params-data SignatureParams =-      RSAParams      Hash RSAEncoding-    | DSSParams-    | ECDSAParams    Hash-    deriving (Show,Eq)+-- FIXME add RSAPSSParams+data SignatureParams+    = RSAParams Hash RSAEncoding+    | DSAParams+    | ECDSAParams Hash+    | Ed25519Params+    | Ed448Params+    deriving (Show, Eq)  -- Verify that the signature matches the given message, using the -- public key. --  kxVerify :: PublicKey -> SignatureParams -> ByteString -> ByteString -> Bool-kxVerify (PubKeyRSA pk) (RSAParams alg RSApkcs1) msg sign   = rsaVerifyHash alg pk msg sign-kxVerify (PubKeyRSA pk) (RSAParams alg RSApss)   msg sign   = rsapssVerifyHash alg pk msg sign-kxVerify (PubKeyDSA pk) DSSParams                msg signBS =-+kxVerify (PubKeyRSA pk) (RSAParams alg RSApkcs1) msg sign = rsaVerifyHash alg pk msg sign+kxVerify (PubKeyRSA pk) (RSAParams alg RSApss) msg sign = rsapssVerifyHash alg pk msg sign+kxVerify (PubKeyDSA pk) DSAParams msg signBS =     case dsaToSignature signBS of         Just sig -> DSA.verify H.SHA1 pk sig msg-        _        -> False+        _ -> False   where-        dsaToSignature :: ByteString -> Maybe DSA.Signature-        dsaToSignature b =-            case decodeASN1' BER b of-                Left _     -> Nothing-                Right asn1 ->-                    case asn1 of-                        Start Sequence:IntVal r:IntVal s:End Sequence:_ ->-                            Just DSA.Signature { DSA.sign_r = r, DSA.sign_s = s }-                        _ ->-                            Nothing-kxVerify (PubKeyEC key) (ECDSAParams alg) msg sigBS = fromMaybe False $ do-    -- get the curve name and the public key data-    (curveName, pubBS) <- case key of-            PubKeyEC_Named curveName' pub -> Just (curveName',pub)-            PubKeyEC_Prime {}            ->-                case find matchPrimeCurve $ enumFrom $ toEnum 0 of-                    Nothing        -> Nothing-                    Just curveName' -> Just (curveName', pubkeyEC_pub key)-    -- decode the signature-    signature <- case decodeASN1' BER sigBS of-        Left _ -> Nothing-        Right [Start Sequence,IntVal r,IntVal s,End Sequence] -> Just $ ECDSA.Signature r s-        Right _ -> Nothing--    -- decode the public key related to the curve-    pubkey <- unserializePoint (ECC.getCurveByName curveName) pubBS--    verifyF <- case alg of-                    MD5    -> Just (ECDSA.verify H.MD5)-                    SHA1   -> Just (ECDSA.verify H.SHA1)-                    SHA224 -> Just (ECDSA.verify H.SHA224)-                    SHA256 -> Just (ECDSA.verify H.SHA256)-                    SHA384 -> Just (ECDSA.verify H.SHA384)-                    SHA512 -> Just (ECDSA.verify H.SHA512)-                    _      -> Nothing-    return $ verifyF pubkey signature msg+    dsaToSignature :: ByteString -> Maybe DSA.Signature+    dsaToSignature b =+        case decodeASN1' BER b of+            Left _ -> Nothing+            Right asn1 ->+                case asn1 of+                    Start Sequence : IntVal r : IntVal s : End Sequence : _ ->+                        Just DSA.Signature{DSA.sign_r = r, DSA.sign_s = s}+                    _ ->+                        Nothing+kxVerify (PubKeyEC key) (ECDSAParams alg) msg sigBS =+    fromMaybe False $+        join $+            withPubKeyEC key verifyProxy verifyClassic Nothing   where-        matchPrimeCurve c =-            case ECC.getCurveByName c of-                ECC.CurveFP (ECC.CurvePrime p cc) ->-                    ECC.ecc_a cc == pubkeyEC_a key     &&-                    ECC.ecc_b cc == pubkeyEC_b key     &&-                    ECC.ecc_n cc == pubkeyEC_order key &&-                    p            == pubkeyEC_prime key-                _                                 -> False--        unserializePoint curve (SerializedPoint bs) =-            case B.uncons bs of-                Nothing                -> Nothing-                Just (ptFormat, input) ->-                    case ptFormat of-                        4 -> if B.length input /= 2 * bytes-                                then Nothing-                                else-                                    let (x, y) = B.splitAt bytes input-                                        p      = ECC.Point (os2ip x) (os2ip y)-                                     in if ECC.isPointValid curve p-                                            then Just $ ECDSA.PublicKey curve p-                                            else Nothing-                        -- 2 and 3 for compressed format.-                        _ -> Nothing-          where bits  = ECC.curveSizeBits curve-                bytes = (bits + 7) `div` 8-kxVerify _              _         _   _    = False+    decodeSignatureASN1 buildRS =+        case decodeASN1' BER sigBS of+            Left _ -> Nothing+            Right [Start Sequence, IntVal r, IntVal s, End Sequence] ->+                Just (buildRS r s)+            Right _ -> Nothing+    verifyProxy prx pubkey = do+        rs <- decodeSignatureASN1 (,)+        signature <- maybeCryptoError $ ECDSA.signatureFromIntegers prx rs+        verifyF <- withAlg (ECDSA.verify prx)+        return $ verifyF pubkey signature msg+    verifyClassic pubkey = do+        signature <- decodeSignatureASN1 ECDSA_ECC.Signature+        verifyF <- withAlg ECDSA_ECC.verify+        return $ verifyF pubkey signature msg+    withAlg :: (forall hash. H.HashAlgorithm hash => hash -> a) -> Maybe a+    withAlg f = case alg of+        MD5 -> Just (f H.MD5)+        SHA1 -> Just (f H.SHA1)+        SHA224 -> Just (f H.SHA224)+        SHA256 -> Just (f H.SHA256)+        SHA384 -> Just (f H.SHA384)+        SHA512 -> Just (f H.SHA512)+        _ -> Nothing+kxVerify (PubKeyEd25519 key) Ed25519Params msg sigBS =+    case Ed25519.signature sigBS of+        CryptoPassed sig -> Ed25519.verify key msg sig+        _ -> False+kxVerify (PubKeyEd448 key) Ed448Params msg sigBS =+    case Ed448.signature sigBS of+        CryptoPassed sig -> Ed448.verify key msg sig+        _ -> False+kxVerify _ _ _ _ = False  -- Sign the given message using the private key. ---kxSign :: MonadRandom r-       => PrivateKey-       -> SignatureParams-       -> ByteString-       -> r (Either KxError ByteString)-kxSign (PrivKeyRSA pk) (RSAParams hashAlg RSApkcs1) msg =+kxSign+    :: MonadRandom r+    => PrivateKey+    -> PublicKey+    -> SignatureParams+    -> ByteString+    -> r (Either KxError ByteString)+kxSign (PrivKeyRSA pk) (PubKeyRSA _) (RSAParams hashAlg RSApkcs1) msg =     generalizeRSAError <$> rsaSignHash hashAlg pk msg-kxSign (PrivKeyRSA pk) (RSAParams hashAlg RSApss) msg =+kxSign (PrivKeyRSA pk) (PubKeyRSA _) (RSAParams hashAlg RSApss) msg =     generalizeRSAError <$> rsapssSignHash hashAlg pk msg-kxSign (PrivKeyDSA pk) DSSParams           msg = do+kxSign (PrivKeyDSA pk) (PubKeyDSA _) DSAParams msg = do     sign <- DSA.sign pk H.SHA1 msg     return (Right $ encodeASN1' DER $ dsaSequence sign)-  where dsaSequence sign = [Start Sequence,IntVal (DSA.sign_r sign),IntVal (DSA.sign_s sign),End Sequence]-kxSign _ _ _ =+  where+    dsaSequence sign =+        [ Start Sequence+        , IntVal (DSA.sign_r sign)+        , IntVal (DSA.sign_s sign)+        , End Sequence+        ]+kxSign (PrivKeyEC pk) (PubKeyEC _) (ECDSAParams hashAlg) msg =+    case withPrivKeyEC pk doSign (const unsupported) unsupported of+        Nothing -> unsupported+        Just run -> fmap encode <$> run+  where+    encode (r, s) =+        encodeASN1'+            DER+            [Start Sequence, IntVal r, IntVal s, End Sequence]+    doSign prx privkey = do+        msig <- ecdsaSignHash prx hashAlg privkey msg+        return $ case msig of+            Nothing -> Left KxUnsupported+            Just sign -> Right (ECDSA.signatureToIntegers prx sign)+    unsupported = return $ Left KxUnsupported+kxSign (PrivKeyEd25519 pk) (PubKeyEd25519 pub) Ed25519Params msg =+    return $ Right $ convert $ Ed25519.sign pk pub msg+kxSign (PrivKeyEd448 pk) (PubKeyEd448 pub) Ed448Params msg =+    return $ Right $ convert $ Ed448.sign pk pub msg+kxSign _ _ _ _ =     return (Left KxUnsupported) -rsaSignHash :: MonadRandom m => Hash -> RSA.PrivateKey -> ByteString -> m (Either RSA.Error ByteString)+rsaSignHash+    :: MonadRandom m+    => Hash+    -> RSA.PrivateKey+    -> ByteString+    -> m (Either RSA.Error ByteString) rsaSignHash SHA1_MD5 pk msg = RSA.signSafer noHash pk msg-rsaSignHash MD5 pk msg      = RSA.signSafer (Just H.MD5) pk msg-rsaSignHash SHA1 pk msg     = RSA.signSafer (Just H.SHA1) pk msg-rsaSignHash SHA224 pk msg   = RSA.signSafer (Just H.SHA224) pk msg-rsaSignHash SHA256 pk msg   = RSA.signSafer (Just H.SHA256) pk msg-rsaSignHash SHA384 pk msg   = RSA.signSafer (Just H.SHA384) pk msg-rsaSignHash SHA512 pk msg   = RSA.signSafer (Just H.SHA512) pk msg+rsaSignHash MD5 pk msg = RSA.signSafer (Just H.MD5) pk msg+rsaSignHash SHA1 pk msg = RSA.signSafer (Just H.SHA1) pk msg+rsaSignHash SHA224 pk msg = RSA.signSafer (Just H.SHA224) pk msg+rsaSignHash SHA256 pk msg = RSA.signSafer (Just H.SHA256) pk msg+rsaSignHash SHA384 pk msg = RSA.signSafer (Just H.SHA384) pk msg+rsaSignHash SHA512 pk msg = RSA.signSafer (Just H.SHA512) pk msg -rsapssSignHash :: MonadRandom m => Hash -> RSA.PrivateKey -> ByteString -> m (Either RSA.Error ByteString)+rsapssSignHash+    :: MonadRandom m+    => Hash+    -> RSA.PrivateKey+    -> ByteString+    -> m (Either RSA.Error ByteString) rsapssSignHash SHA256 pk msg = PSS.signSafer (PSS.defaultPSSParams H.SHA256) pk msg rsapssSignHash SHA384 pk msg = PSS.signSafer (PSS.defaultPSSParams H.SHA384) pk msg rsapssSignHash SHA512 pk msg = PSS.signSafer (PSS.defaultPSSParams H.SHA512) pk msg-rsapssSignHash _ _ _         = error "rsapssSignHash: unsupported hash"+rsapssSignHash _ _ _ = error "rsapssSignHash: unsupported hash"  rsaVerifyHash :: Hash -> RSA.PublicKey -> ByteString -> ByteString -> Bool rsaVerifyHash SHA1_MD5 = RSA.verify noHash-rsaVerifyHash MD5      = RSA.verify (Just H.MD5)-rsaVerifyHash SHA1     = RSA.verify (Just H.SHA1)-rsaVerifyHash SHA224   = RSA.verify (Just H.SHA224)-rsaVerifyHash SHA256   = RSA.verify (Just H.SHA256)-rsaVerifyHash SHA384   = RSA.verify (Just H.SHA384)-rsaVerifyHash SHA512   = RSA.verify (Just H.SHA512)+rsaVerifyHash MD5 = RSA.verify (Just H.MD5)+rsaVerifyHash SHA1 = RSA.verify (Just H.SHA1)+rsaVerifyHash SHA224 = RSA.verify (Just H.SHA224)+rsaVerifyHash SHA256 = RSA.verify (Just H.SHA256)+rsaVerifyHash SHA384 = RSA.verify (Just H.SHA384)+rsaVerifyHash SHA512 = RSA.verify (Just H.SHA512)  rsapssVerifyHash :: Hash -> RSA.PublicKey -> ByteString -> ByteString -> Bool rsapssVerifyHash SHA256 = PSS.verify (PSS.defaultPSSParams H.SHA256) rsapssVerifyHash SHA384 = PSS.verify (PSS.defaultPSSParams H.SHA384) rsapssVerifyHash SHA512 = PSS.verify (PSS.defaultPSSParams H.SHA512)-rsapssVerifyHash _      = error "rsapssVerifyHash: unsupported hash"+rsapssVerifyHash _ = error "rsapssVerifyHash: unsupported hash"  noHash :: Maybe H.MD5 noHash = Nothing++ecdsaSignHash+    :: (MonadRandom m, ECDSA.EllipticCurveECDSA curve)+    => proxy curve+    -> Hash+    -> ECDSA.Scalar curve+    -> ByteString+    -> m (Maybe (ECDSA.Signature curve))+ecdsaSignHash prx SHA1 pk msg = Just <$> ECDSA.sign prx pk H.SHA1 msg+ecdsaSignHash prx SHA224 pk msg = Just <$> ECDSA.sign prx pk H.SHA224 msg+ecdsaSignHash prx SHA256 pk msg = Just <$> ECDSA.sign prx pk H.SHA256 msg+ecdsaSignHash prx SHA384 pk msg = Just <$> ECDSA.sign prx pk H.SHA384 msg+ecdsaSignHash prx SHA512 pk msg = Just <$> ECDSA.sign prx pk H.SHA512 msg+ecdsaSignHash _ _ _ _ = return Nothing++-- Currently we generate ECDSA signatures in constant time for P256 only.+kxSupportedPrivKeyEC :: PrivKeyEC -> Bool+kxSupportedPrivKeyEC privkey =+    case ecPrivKeyCurveName privkey of+        Just ECC.SEC_p256r1 -> True+        Just ECC.SEC_p384r1 -> True+        Just ECC.SEC_p521r1 -> True+        _ -> False++-- Perform a public-key operation with a parameterized ECC implementation when+-- available, otherwise fallback to the classic ECC implementation.+withPubKeyEC+    :: PubKeyEC+    -> ( forall curve+          . ECDSA.EllipticCurveECDSA curve+         => Proxy curve+         -> ECDSA.PublicKey curve+         -> a+       )+    -> (ECDSA_ECC.PublicKey -> a)+    -> a+    -> Maybe a+withPubKeyEC pubkey withProxy withClassic whenUnknown =+    case ecPubKeyCurveName pubkey of+        Nothing -> Just whenUnknown+        Just ECC.SEC_p256r1 ->+            maybeCryptoError $ withProxy p256 <$> ECDSA.decodePublic p256 bs+        Just ECC.SEC_p384r1 ->+            maybeCryptoError $ withProxy p384 <$> ECDSA.decodePublic p384 bs+        Just ECC.SEC_p521r1 ->+            maybeCryptoError $ withProxy p521 <$> ECDSA.decodePublic p521 bs+        Just curveName ->+            let curve = ECC.getCurveByName curveName+                pub = unserializePoint curve pt+             in withClassic . ECDSA_ECC.PublicKey curve <$> pub+  where+    pt@(SerializedPoint bs) = pubkeyEC_pub pubkey++-- Perform a private-key operation with a parameterized ECC implementation when+-- available.  Calls for an unsupported curve can be prevented with+-- kxSupportedEcPrivKey.+withPrivKeyEC+    :: PrivKeyEC+    -> ( forall curve+          . ECDSA.EllipticCurveECDSA curve+         => Proxy curve+         -> ECDSA.PrivateKey curve+         -> a+       )+    -> (ECC.CurveName -> a)+    -> a+    -> Maybe a+withPrivKeyEC privkey withProxy withUnsupported whenUnknown =+    case ecPrivKeyCurveName privkey of+        Nothing -> Just whenUnknown+        Just ECC.SEC_p256r1 ->+            -- Private key should rather be stored as bytearray and converted+            -- using ECDSA.decodePrivate, unfortunately the data type chosen in+            -- x509 was Integer.+            maybeCryptoError $ withProxy p256 <$> ECDSA.scalarFromInteger p256 d+        Just ECC.SEC_p384r1 ->+            maybeCryptoError $ withProxy p384 <$> ECDSA.scalarFromInteger p384 d+        Just ECC.SEC_p521r1 ->+            maybeCryptoError $ withProxy p521 <$> ECDSA.scalarFromInteger p521 d+        Just curveName -> Just $ withUnsupported curveName+  where+    d = privkeyEC_priv privkey++p256 :: Proxy ECDSA.Curve_P256R1+p256 = Proxy+p384 :: Proxy ECDSA.Curve_P384R1+p384 = Proxy+p521 :: Proxy ECDSA.Curve_P521R1+p521 = Proxy
Network/TLS/Crypto/DH.hs view
@@ -1,34 +1,36 @@-module Network.TLS.Crypto.DH-    (+module Network.TLS.Crypto.DH (     -- * DH types-      DHParams-    , DHPublic-    , DHPrivate-    , DHKey+    DHParams,+    DHPublic,+    DHPrivate,+    DHKey,      -- * DH methods-    , dhPublic-    , dhPrivate-    , dhParams-    , dhParamsGetP-    , dhParamsGetG-    , dhGenerateKeyPair-    , dhGetShared-    , dhValid-    , dhUnwrap-    , dhUnwrapPublic-    ) where+    dhPublic,+    dhPrivate,+    dhParams,+    dhParamsGetP,+    dhParamsGetG,+    dhParamsGetBits,+    dhGenerateKeyPair,+    dhGetShared,+    dhValid,+    dhUnwrap,+    dhUnwrapPublic,+) where +import Crypto.Number.Basic (numBits) import qualified Crypto.PubKey.DH as DH-import           Crypto.Number.Basic (numBits)-import qualified Data.ByteArray as B-import           Network.TLS.RNG+import Data.ByteArray (ScrubbedBytes)+import qualified Data.ByteArray as BA -type DHPublic   = DH.PublicNumber-type DHPrivate  = DH.PrivateNumber-type DHParams   = DH.Params-type DHKey      = DH.SharedKey+import Network.TLS.RNG +type DHPublic = DH.PublicNumber+type DHPrivate = DH.PrivateNumber+type DHParams = DH.Params+type DHKey = ScrubbedBytes+ dhPublic :: Integer -> DHPublic dhPublic = DH.PublicNumber @@ -41,16 +43,16 @@ dhGenerateKeyPair :: MonadRandom r => DHParams -> r (DHPrivate, DHPublic) dhGenerateKeyPair params = do     priv <- DH.generatePrivate params-    let pub        = DH.calculatePublic params priv+    let pub = DH.calculatePublic params priv     return (priv, pub)  dhGetShared :: DHParams -> DHPrivate -> DHPublic -> DHKey-dhGetShared params priv pub =-    stripLeadingZeros (DH.getShared params priv pub)+dhGetShared params priv pub = stripLeadingZeros sec   where+    DH.SharedKey sec = DH.getShared params priv pub     -- strips leading zeros from the result of DH.getShared, as required-    -- for DH(E) premaster secret in SSL/TLS before version 1.3.-    stripLeadingZeros (DH.SharedKey sb) = DH.SharedKey (snd $ B.span (== 0) sb)+    -- for DH(E) pre-main secret in SSL/TLS before version 1.3.+    stripLeadingZeros sb = snd $ BA.span (== 0) sb  -- Check that group element in not in the 2-element subgroup { 1, p - 1 }. -- See RFC 7919 section 3 and NIST SP 56A rev 2 section 5.6.2.3.1.@@ -59,13 +61,16 @@ dhValid (DH.Params p _ _) y = 1 < y && y < p - 1  dhUnwrap :: DHParams -> DHPublic -> [Integer]-dhUnwrap (DH.Params p g _) (DH.PublicNumber y) = [p,g,y]+dhUnwrap (DH.Params p g _) (DH.PublicNumber y) = [p, g, y]  dhParamsGetP :: DHParams -> Integer dhParamsGetP (DH.Params p _ _) = p  dhParamsGetG :: DHParams -> Integer dhParamsGetG (DH.Params _ g _) = g++dhParamsGetBits :: DHParams -> Int+dhParamsGetBits (DH.Params _ _ b) = b  dhUnwrapPublic :: DHPublic -> Integer dhUnwrapPublic (DH.PublicNumber y) = y
Network/TLS/Crypto/IES.hs view
@@ -1,67 +1,116 @@--- |+-- | (Elliptic Curve) Integrated Encryption Scheme+--   KEM(Key Encapsulation Mechanism) based APIs+-- -- Module      : Network.TLS.Crypto.IES -- License     : BSD-style -- Maintainer  : Kazu Yamamoto <kazu@iij.ad.jp> -- Stability   : experimental -- Portability : unknown----module Network.TLS.Crypto.IES-    (-      GroupPublic-    , GroupPrivate-    , GroupKey+module Network.TLS.Crypto.IES (+    GroupPublicA,+    GroupPublicB,+    GroupPrivate,+    GroupKey,+     -- * Group methods-    , groupGenerateKeyPair-    , groupGetPubShared-    , groupGetShared-    , encodeGroupPublic-    , decodeGroupPublic+    groupGenerateKeyPair,+    groupEncapsulate,+    groupDecapsulate,+    groupEncodePublicA,+    groupDecodePublicA,+    groupEncodePublicB,+    groupDecodePublicB,+     -- * Compatibility with 'Network.TLS.Crypto.DH'-    , dhParamsForGroup-    , dhGroupGenerateKeyPair-    , dhGroupGetPubShared-    ) where+    dhParamsForGroup,+    dhGroupGenerateKeyPair,+    dhGroupGetPubShared,+) where  import Control.Arrow-import Crypto.ECC+import Crypto.ECC as ECC import Crypto.Error import Crypto.Number.Generate-import Crypto.PubKey.DH hiding (generateParams)+import Crypto.PubKey.DH (PrivateNumber (..), PublicNumber (..))+import qualified Crypto.PubKey.DH as DH import Crypto.PubKey.ECIES-import qualified Data.ByteArray as B+import Crypto.PubKey.ML_KEM (ML_KEM_1024, ML_KEM_512, ML_KEM_768)+import qualified Crypto.PubKey.ML_KEM as ML+import Data.ByteArray (ScrubbedBytes, convert)+import qualified Data.ByteArray as BA import Data.Proxy+ import Network.TLS.Crypto.Types import Network.TLS.Extra.FFDHE import Network.TLS.Imports import Network.TLS.RNG-import Network.TLS.Util.Serialization (os2ip,i2ospOf_)+import Network.TLS.Util.Serialization (i2ospOf_, os2ip) -data GroupPrivate = GroupPri_P256 (Scalar Curve_P256R1)-                  | GroupPri_P384 (Scalar Curve_P384R1)-                  | GroupPri_P521 (Scalar Curve_P521R1)-                  | GroupPri_X255 (Scalar Curve_X25519)-                  | GroupPri_X448 (Scalar Curve_X448)-                  | GroupPri_FFDHE2048 PrivateNumber-                  | GroupPri_FFDHE3072 PrivateNumber-                  | GroupPri_FFDHE4096 PrivateNumber-                  | GroupPri_FFDHE6144 PrivateNumber-                  | GroupPri_FFDHE8192 PrivateNumber-                  deriving (Eq, Show)+{- FOURMOLU_DISABLE -}+data GroupPrivate+    = GroupPri_P256 (Scalar Curve_P256R1)+    | GroupPri_P384 (Scalar Curve_P384R1)+    | GroupPri_P521 (Scalar Curve_P521R1)+    | GroupPri_X255 (Scalar Curve_X25519)+    | GroupPri_X448 (Scalar Curve_X448)+    | GroupPri_FFDHE2048 PrivateNumber+    | GroupPri_FFDHE3072 PrivateNumber+    | GroupPri_FFDHE4096 PrivateNumber+    | GroupPri_FFDHE6144 PrivateNumber+    | GroupPri_FFDHE8192 PrivateNumber+    | GroupPri_MLKEM512       (ML.DecapsulationKey ML_KEM_512)+    | GroupPri_MLKEM768       (ML.DecapsulationKey ML_KEM_768)+    | GroupPri_MLKEM1024      (ML.DecapsulationKey ML_KEM_1024)+    | GroupPri_X25519MLKEM768 (Scalar Curve_X25519, ML.DecapsulationKey ML_KEM_768)+    | GroupPri_P256MLKEM768   (Scalar Curve_P256R1, ML.DecapsulationKey ML_KEM_768)+    | GroupPri_P384MLKEM1024  (Scalar Curve_P384R1, ML.DecapsulationKey ML_KEM_1024)+    deriving (Eq, Show)+{- FOURMOLU_ENABLE -} -data GroupPublic = GroupPub_P256 (Point Curve_P256R1)-                 | GroupPub_P384 (Point Curve_P384R1)-                 | GroupPub_P521 (Point Curve_P521R1)-                 | GroupPub_X255 (Point Curve_X25519)-                 | GroupPub_X448 (Point Curve_X448)-                 | GroupPub_FFDHE2048 PublicNumber-                 | GroupPub_FFDHE3072 PublicNumber-                 | GroupPub_FFDHE4096 PublicNumber-                 | GroupPub_FFDHE6144 PublicNumber-                 | GroupPub_FFDHE8192 PublicNumber-                 deriving (Eq, Show)+{- FOURMOLU_DISABLE -}+data GroupPublicA+    = GroupPubA_P256 (Point Curve_P256R1)+    | GroupPubA_P384 (Point Curve_P384R1)+    | GroupPubA_P521 (Point Curve_P521R1)+    | GroupPubA_X255 (Point Curve_X25519)+    | GroupPubA_X448 (Point Curve_X448)+    | GroupPubA_FFDHE2048 PublicNumber+    | GroupPubA_FFDHE3072 PublicNumber+    | GroupPubA_FFDHE4096 PublicNumber+    | GroupPubA_FFDHE6144 PublicNumber+    | GroupPubA_FFDHE8192 PublicNumber+    | GroupPubA_MLKEM512       (ML.EncapsulationKey ML_KEM_512)+    | GroupPubA_MLKEM768       (ML.EncapsulationKey ML_KEM_768)+    | GroupPubA_MLKEM1024      (ML.EncapsulationKey ML_KEM_1024)+    | GroupPubA_X25519MLKEM768 (Point Curve_X25519, ML.EncapsulationKey ML_KEM_768)+    | GroupPubA_P256MLKEM768   (Point Curve_P256R1, ML.EncapsulationKey ML_KEM_768)+    | GroupPubA_P384MLKEM1024  (Point Curve_P384R1, ML.EncapsulationKey ML_KEM_1024)+    deriving (Eq, Show)+{- FOURMOLU_ENABLE -} -type GroupKey = SharedSecret+{- FOURMOLU_DISABLE -}+data GroupPublicB+    = GroupPubB_P256 (Point Curve_P256R1)+    | GroupPubB_P384 (Point Curve_P384R1)+    | GroupPubB_P521 (Point Curve_P521R1)+    | GroupPubB_X255 (Point Curve_X25519)+    | GroupPubB_X448 (Point Curve_X448)+    | GroupPubB_FFDHE2048 PublicNumber+    | GroupPubB_FFDHE3072 PublicNumber+    | GroupPubB_FFDHE4096 PublicNumber+    | GroupPubB_FFDHE6144 PublicNumber+    | GroupPubB_FFDHE8192 PublicNumber+    | GroupPubB_MLKEM512       (ML.Ciphertext ML_KEM_512)+    | GroupPubB_MLKEM768       (ML.Ciphertext ML_KEM_768)+    | GroupPubB_MLKEM1024      (ML.Ciphertext ML_KEM_1024)+    | GroupPubB_X25519MLKEM768 (Point Curve_X25519, ML.Ciphertext ML_KEM_768)+    | GroupPubB_P256MLKEM768   (Point Curve_P256R1, ML.Ciphertext ML_KEM_768)+    | GroupPubB_P384MLKEM1024  (Point Curve_P384R1, ML.Ciphertext ML_KEM_1024)+    deriving (Eq, Show)+{- FOURMOLU_ENABLE -} +type GroupKey = ScrubbedBytes+ p256 :: Proxy Curve_P256R1 p256 = Proxy @@ -77,172 +126,385 @@ x448 :: Proxy Curve_X448 x448 = Proxy -dhParamsForGroup :: Group -> Maybe Params+mlkem512 :: Proxy ML_KEM_512+mlkem512 = Proxy++mlkem768 :: Proxy ML_KEM_768+mlkem768 = Proxy++mlkem1024 :: Proxy ML_KEM_1024+mlkem1024 = Proxy++dhParamsForGroup :: Group -> Maybe DH.Params dhParamsForGroup FFDHE2048 = Just ffdhe2048 dhParamsForGroup FFDHE3072 = Just ffdhe3072 dhParamsForGroup FFDHE4096 = Just ffdhe4096 dhParamsForGroup FFDHE6144 = Just ffdhe6144 dhParamsForGroup FFDHE8192 = Just ffdhe8192-dhParamsForGroup _         = Nothing+dhParamsForGroup _ = Nothing -groupGenerateKeyPair :: MonadRandom r => Group -> r (GroupPrivate, GroupPublic)-groupGenerateKeyPair P256   =-    (GroupPri_P256,GroupPub_P256) `fs` curveGenerateKeyPair p256-groupGenerateKeyPair P384   =-    (GroupPri_P384,GroupPub_P384) `fs` curveGenerateKeyPair p384-groupGenerateKeyPair P521   =-    (GroupPri_P521,GroupPub_P521) `fs` curveGenerateKeyPair p521+groupGenerateKeyPair :: MonadRandom r => Group -> r (GroupPrivate, GroupPublicA)+groupGenerateKeyPair P256 =+    (GroupPri_P256, GroupPubA_P256) `fs` curveGenerateKeyPair p256+groupGenerateKeyPair P384 =+    (GroupPri_P384, GroupPubA_P384) `fs` curveGenerateKeyPair p384+groupGenerateKeyPair P521 =+    (GroupPri_P521, GroupPubA_P521) `fs` curveGenerateKeyPair p521 groupGenerateKeyPair X25519 =-    (GroupPri_X255,GroupPub_X255) `fs` curveGenerateKeyPair x25519+    (GroupPri_X255, GroupPubA_X255) `fs` curveGenerateKeyPair x25519 groupGenerateKeyPair X448 =-    (GroupPri_X448,GroupPub_X448) `fs` curveGenerateKeyPair x448-groupGenerateKeyPair FFDHE2048 = gen ffdhe2048 exp2048 GroupPri_FFDHE2048 GroupPub_FFDHE2048-groupGenerateKeyPair FFDHE3072 = gen ffdhe3072 exp3072 GroupPri_FFDHE3072 GroupPub_FFDHE3072-groupGenerateKeyPair FFDHE4096 = gen ffdhe4096 exp4096 GroupPri_FFDHE4096 GroupPub_FFDHE4096-groupGenerateKeyPair FFDHE6144 = gen ffdhe6144 exp6144 GroupPri_FFDHE6144 GroupPub_FFDHE6144-groupGenerateKeyPair FFDHE8192 = gen ffdhe8192 exp8192 GroupPri_FFDHE8192 GroupPub_FFDHE8192+    (GroupPri_X448, GroupPubA_X448) `fs` curveGenerateKeyPair x448+groupGenerateKeyPair FFDHE2048 = gen ffdhe2048 exp2048 GroupPri_FFDHE2048 GroupPubA_FFDHE2048+groupGenerateKeyPair FFDHE3072 = gen ffdhe3072 exp3072 GroupPri_FFDHE3072 GroupPubA_FFDHE3072+groupGenerateKeyPair FFDHE4096 = gen ffdhe4096 exp4096 GroupPri_FFDHE4096 GroupPubA_FFDHE4096+groupGenerateKeyPair FFDHE6144 = gen ffdhe6144 exp6144 GroupPri_FFDHE6144 GroupPubA_FFDHE6144+groupGenerateKeyPair FFDHE8192 = gen ffdhe8192 exp8192 GroupPri_FFDHE8192 GroupPubA_FFDHE8192+groupGenerateKeyPair MLKEM512 = do+    (e, d) <- ML.generate mlkem512+    return (GroupPri_MLKEM512 d, GroupPubA_MLKEM512 e)+groupGenerateKeyPair MLKEM768 = do+    (e, d) <- ML.generate mlkem768+    return (GroupPri_MLKEM768 d, GroupPubA_MLKEM768 e)+groupGenerateKeyPair MLKEM1024 = do+    (e, d) <- ML.generate mlkem1024+    return (GroupPri_MLKEM1024 d, GroupPubA_MLKEM1024 e)+groupGenerateKeyPair X25519MLKEM768 = do+    (d1, e1) <- fs' $ curveGenerateKeyPair x25519+    (e2, d2) <- ML.generate mlkem768+    return (GroupPri_X25519MLKEM768 (d1, d2), GroupPubA_X25519MLKEM768 (e1, e2))+groupGenerateKeyPair P256MLKEM768 = do+    (d1, e1) <- fs' $ curveGenerateKeyPair p256+    (e2, d2) <- ML.generate mlkem768+    return (GroupPri_P256MLKEM768 (d1, d2), GroupPubA_P256MLKEM768 (e1, e2))+groupGenerateKeyPair P384MLKEM1024 = do+    (d1, e1) <- fs' $ curveGenerateKeyPair p384+    (e2, d2) <- ML.generate mlkem1024+    return (GroupPri_P384MLKEM1024 (d1, d2), GroupPubA_P384MLKEM1024 (e1, e2))+groupGenerateKeyPair _ = error "groupGenerateKeyPair" -dhGroupGenerateKeyPair :: MonadRandom r => Group -> r (Params, PrivateNumber, PublicNumber)+dhGroupGenerateKeyPair+    :: MonadRandom r => Group -> r (DH.Params, PrivateNumber, PublicNumber) dhGroupGenerateKeyPair FFDHE2048 = addParams ffdhe2048 (gen' ffdhe2048 exp2048) dhGroupGenerateKeyPair FFDHE3072 = addParams ffdhe3072 (gen' ffdhe3072 exp3072) dhGroupGenerateKeyPair FFDHE4096 = addParams ffdhe4096 (gen' ffdhe4096 exp4096) dhGroupGenerateKeyPair FFDHE6144 = addParams ffdhe6144 (gen' ffdhe6144 exp6144) dhGroupGenerateKeyPair FFDHE8192 = addParams ffdhe8192 (gen' ffdhe8192 exp8192)-dhGroupGenerateKeyPair grp       = error ("invalid FFDHE group: " ++ show grp)+dhGroupGenerateKeyPair grp = error ("invalid FFDHE group: " ++ show grp) -addParams :: Functor f => Params -> f (a, b) -> f (Params, a, b)+addParams :: Functor f => DH.Params -> f (a, b) -> f (DH.Params, a, b) addParams params = fmap $ \(a, b) -> (params, a, b) -fs :: MonadRandom r-   => (Scalar a -> GroupPrivate, Point a -> GroupPublic)-   -> r (KeyPair a)-   -> r (GroupPrivate, GroupPublic)+fs+    :: MonadRandom r+    => (Scalar a -> GroupPrivate, Point a -> GroupPublicA)+    -> r (KeyPair a)+    -> r (GroupPrivate, GroupPublicA) (t1, t2) `fs` action = do     keypair <- action     let pub = keypairGetPublic keypair         pri = keypairGetPrivate keypair     return (t1 pri, t2 pub) -gen :: MonadRandom r-    => Params+fs' :: Monad m => m (KeyPair curve) -> m (Scalar curve, Point curve)+fs' action = do+    keypair <- action+    let pub = keypairGetPublic keypair+        pri = keypairGetPrivate keypair+    return (pri, pub)++gen+    :: MonadRandom r+    => DH.Params     -> Int     -> (PrivateNumber -> GroupPrivate)-    -> (PublicNumber -> GroupPublic)-    -> r (GroupPrivate, GroupPublic)+    -> (PublicNumber -> GroupPublicA)+    -> r (GroupPrivate, GroupPublicA) gen params expBits priTag pubTag = (priTag *** pubTag) <$> gen' params expBits -gen' :: MonadRandom r-     => Params-     -> Int-     -> r (PrivateNumber, PublicNumber)-gen' params expBits = (id &&& calculatePublic params) <$> generatePriv expBits+gen'+    :: MonadRandom r+    => DH.Params+    -> Int+    -> r (PrivateNumber, PublicNumber)+gen' params expBits = (id &&& DH.calculatePublic params) <$> generatePriv expBits -groupGetPubShared :: MonadRandom r => GroupPublic -> r (Maybe (GroupPublic, GroupKey))-groupGetPubShared (GroupPub_P256 pub) =-    fmap (first GroupPub_P256) . maybeCryptoError <$> deriveEncrypt p256 pub-groupGetPubShared (GroupPub_P384 pub) =-    fmap (first GroupPub_P384) . maybeCryptoError <$> deriveEncrypt p384 pub-groupGetPubShared (GroupPub_P521 pub) =-    fmap (first GroupPub_P521) . maybeCryptoError <$> deriveEncrypt p521 pub-groupGetPubShared (GroupPub_X255 pub) =-    fmap (first GroupPub_X255) . maybeCryptoError <$> deriveEncrypt x25519 pub-groupGetPubShared (GroupPub_X448 pub) =-    fmap (first GroupPub_X448) . maybeCryptoError <$> deriveEncrypt x448 pub-groupGetPubShared (GroupPub_FFDHE2048 pub) = getPubShared ffdhe2048 exp2048 pub GroupPub_FFDHE2048-groupGetPubShared (GroupPub_FFDHE3072 pub) = getPubShared ffdhe3072 exp3072 pub GroupPub_FFDHE3072-groupGetPubShared (GroupPub_FFDHE4096 pub) = getPubShared ffdhe4096 exp4096 pub GroupPub_FFDHE4096-groupGetPubShared (GroupPub_FFDHE6144 pub) = getPubShared ffdhe6144 exp6144 pub GroupPub_FFDHE6144-groupGetPubShared (GroupPub_FFDHE8192 pub) = getPubShared ffdhe8192 exp8192 pub GroupPub_FFDHE8192+groupEncapsulate+    :: MonadRandom r => GroupPublicA -> r (Maybe (GroupPublicB, GroupKey))+groupEncapsulate (GroupPubA_P256 pub) = getECDHPubShared GroupPubB_P256 p256 pub+groupEncapsulate (GroupPubA_P384 pub) = getECDHPubShared GroupPubB_P384 p384 pub+groupEncapsulate (GroupPubA_P521 pub) = getECDHPubShared GroupPubB_P521 p521 pub+groupEncapsulate (GroupPubA_X255 pub) = getECDHPubShared GroupPubB_X255 x25519 pub+groupEncapsulate (GroupPubA_X448 pub) = getECDHPubShared GroupPubB_X448 x448 pub+groupEncapsulate (GroupPubA_FFDHE2048 pub) = getDHPubShared ffdhe2048 exp2048 pub GroupPubB_FFDHE2048+groupEncapsulate (GroupPubA_FFDHE3072 pub) = getDHPubShared ffdhe3072 exp3072 pub GroupPubB_FFDHE3072+groupEncapsulate (GroupPubA_FFDHE4096 pub) = getDHPubShared ffdhe4096 exp4096 pub GroupPubB_FFDHE4096+groupEncapsulate (GroupPubA_FFDHE6144 pub) = getDHPubShared ffdhe6144 exp6144 pub GroupPubB_FFDHE6144+groupEncapsulate (GroupPubA_FFDHE8192 pub) = getDHPubShared ffdhe8192 exp8192 pub GroupPubB_FFDHE8192+groupEncapsulate (GroupPubA_MLKEM512 pub) = do+    (sec, ct) <- ML.encapsulate pub+    return $ Just (GroupPubB_MLKEM512 ct, convert sec)+groupEncapsulate (GroupPubA_MLKEM768 pub) = do+    (sec, ct) <- ML.encapsulate pub+    return $ Just (GroupPubB_MLKEM768 ct, convert sec)+groupEncapsulate (GroupPubA_MLKEM1024 pub) = do+    (sec, ct) <- ML.encapsulate pub+    return $ Just (GroupPubB_MLKEM1024 ct, convert sec)+groupEncapsulate (GroupPubA_X25519MLKEM768 (e1, e2)) = do+    (c1, k1) <- fromJust <$> getECDHPubShared' x25519 e1+    (k2, c2) <- ML.encapsulate e2+    -- Sec 4.1: Specifically, the order of shares in the concatenation+    -- has been reversed.+    return $ Just (GroupPubB_X25519MLKEM768 (c1, c2), convert k2 <> k1)+groupEncapsulate (GroupPubA_P256MLKEM768 (e1, e2)) = do+    (c1, k1) <- fromJust <$> getECDHPubShared' p256 e1+    (k2, c2) <- ML.encapsulate e2+    return $ Just (GroupPubB_P256MLKEM768 (c1, c2), k1 <> convert k2)+groupEncapsulate (GroupPubA_P384MLKEM1024 (e1, e2)) = do+    (c1, k1) <- fromJust <$> getECDHPubShared' p384 e1+    (k2, c2) <- ML.encapsulate e2+    return $ Just (GroupPubB_P384MLKEM1024 (c1, c2), k1 <> convert k2) -dhGroupGetPubShared :: MonadRandom r => Group -> PublicNumber -> r (Maybe (PublicNumber, SharedKey))-dhGroupGetPubShared FFDHE2048 pub = getPubShared' ffdhe2048 exp2048 pub-dhGroupGetPubShared FFDHE3072 pub = getPubShared' ffdhe3072 exp3072 pub-dhGroupGetPubShared FFDHE4096 pub = getPubShared' ffdhe4096 exp4096 pub-dhGroupGetPubShared FFDHE6144 pub = getPubShared' ffdhe6144 exp6144 pub-dhGroupGetPubShared FFDHE8192 pub = getPubShared' ffdhe8192 exp8192 pub-dhGroupGetPubShared _         _   = return Nothing+dhGroupGetPubShared+    :: MonadRandom r => Group -> PublicNumber -> r (Maybe (PublicNumber, GroupKey))+dhGroupGetPubShared FFDHE2048 pub = getDHPubShared' ffdhe2048 exp2048 pub+dhGroupGetPubShared FFDHE3072 pub = getDHPubShared' ffdhe3072 exp3072 pub+dhGroupGetPubShared FFDHE4096 pub = getDHPubShared' ffdhe4096 exp4096 pub+dhGroupGetPubShared FFDHE6144 pub = getDHPubShared' ffdhe6144 exp6144 pub+dhGroupGetPubShared FFDHE8192 pub = getDHPubShared' ffdhe8192 exp8192 pub+dhGroupGetPubShared _ _ = return Nothing -getPubShared :: MonadRandom r-             => Params-             -> Int-             -> PublicNumber-             -> (PublicNumber -> GroupPublic)-             -> r (Maybe (GroupPublic, GroupKey))-getPubShared params expBits pub pubTag | not (valid params pub) = return Nothing-                                       | otherwise = do-    mypri <- generatePriv expBits-    let mypub = calculatePublic params mypri-    let SharedKey share = getShared params mypri pub-    return $ Just (pubTag mypub, SharedSecret share)+getECDHPubShared+    :: (MonadRandom m, EllipticCurveDH curve)+    => (Point curve -> GroupPublicB)+    -> proxy curve+    -> Point curve+    -> m (Maybe (GroupPublicB, GroupKey))+getECDHPubShared tag proxy pub = do+    mx <- maybeCryptoError <$> deriveEncrypt proxy pub+    case mx of+        Nothing -> return Nothing+        Just (p, ECC.SharedSecret s) -> return $ Just (tag p, s) -getPubShared' :: MonadRandom r-              => Params-              -> Int-              -> PublicNumber-              -> r (Maybe (PublicNumber, SharedKey))-getPubShared' params expBits pub+getECDHPubShared'+    :: (MonadRandom m, EllipticCurveDH curve)+    => proxy curve+    -> Point curve+    -> m (Maybe (Point curve, GroupKey))+getECDHPubShared' proxy pub = do+    mx <- maybeCryptoError <$> deriveEncrypt proxy pub+    case mx of+        Nothing -> return Nothing+        Just (p, ECC.SharedSecret s) -> return $ Just (p, s)++getDHPubShared+    :: MonadRandom r+    => DH.Params+    -> Int+    -> PublicNumber+    -> (PublicNumber -> GroupPublicB)+    -> r (Maybe (GroupPublicB, GroupKey))+getDHPubShared params expBits pub pubTag     | not (valid params pub) = return Nothing     | otherwise = do         mypri <- generatePriv expBits-        let share = stripLeadingZeros (getShared params mypri pub)-        return $ Just (calculatePublic params mypri, SharedKey share)+        let mypub = DH.calculatePublic params mypri+            DH.SharedKey share = DH.getShared params mypri pub+        return $ Just (pubTag mypub, share) -groupGetShared ::  GroupPublic -> GroupPrivate -> Maybe GroupKey-groupGetShared (GroupPub_P256 pub) (GroupPri_P256 pri) = maybeCryptoError $ deriveDecrypt p256 pub pri-groupGetShared (GroupPub_P384 pub) (GroupPri_P384 pri) = maybeCryptoError $ deriveDecrypt p384 pub pri-groupGetShared (GroupPub_P521 pub) (GroupPri_P521 pri) = maybeCryptoError $ deriveDecrypt p521 pub pri-groupGetShared (GroupPub_X255 pub) (GroupPri_X255 pri) = maybeCryptoError $ deriveDecrypt x25519 pub pri-groupGetShared (GroupPub_X448 pub) (GroupPri_X448 pri) = maybeCryptoError $ deriveDecrypt x448 pub pri-groupGetShared (GroupPub_FFDHE2048 pub) (GroupPri_FFDHE2048 pri) = calcShared ffdhe2048 pub pri-groupGetShared (GroupPub_FFDHE3072 pub) (GroupPri_FFDHE3072 pri) = calcShared ffdhe3072 pub pri-groupGetShared (GroupPub_FFDHE4096 pub) (GroupPri_FFDHE4096 pri) = calcShared ffdhe4096 pub pri-groupGetShared (GroupPub_FFDHE6144 pub) (GroupPri_FFDHE6144 pri) = calcShared ffdhe6144 pub pri-groupGetShared (GroupPub_FFDHE8192 pub) (GroupPri_FFDHE8192 pri) = calcShared ffdhe8192 pub pri-groupGetShared _ _ = Nothing+getDHPubShared'+    :: MonadRandom r+    => DH.Params+    -> Int+    -> PublicNumber+    -> r (Maybe (PublicNumber, GroupKey))+getDHPubShared' params expBits pub+    | not (valid params pub) = return Nothing+    | otherwise = do+        mypri <- generatePriv expBits+        let share = stripLeadingZeros (DH.getShared params mypri pub)+        return $ Just (DH.calculatePublic params mypri, convert share) -calcShared :: Params -> PublicNumber -> PrivateNumber -> Maybe SharedSecret-calcShared params pub pri-    | valid params pub = Just $ SharedSecret share-    | otherwise        = Nothing+unwrap :: SharedSecret -> GroupKey+unwrap (ECC.SharedSecret sec) = sec++groupDecapsulate :: GroupPublicB -> GroupPrivate -> Maybe GroupKey+groupDecapsulate (GroupPubB_P256 pub) (GroupPri_P256 pri) = (unwrap <$>) . maybeCryptoError $ deriveDecrypt p256 pub pri+groupDecapsulate (GroupPubB_P384 pub) (GroupPri_P384 pri) = (unwrap <$>) . maybeCryptoError $ deriveDecrypt p384 pub pri+groupDecapsulate (GroupPubB_P521 pub) (GroupPri_P521 pri) = (unwrap <$>) . maybeCryptoError $ deriveDecrypt p521 pub pri+groupDecapsulate (GroupPubB_X255 pub) (GroupPri_X255 pri) = (unwrap <$>) . maybeCryptoError $ deriveDecrypt x25519 pub pri+groupDecapsulate (GroupPubB_X448 pub) (GroupPri_X448 pri) = (unwrap <$>) . maybeCryptoError $ deriveDecrypt x448 pub pri+groupDecapsulate (GroupPubB_FFDHE2048 pub) (GroupPri_FFDHE2048 pri) = calcDHShared ffdhe2048 pub pri+groupDecapsulate (GroupPubB_FFDHE3072 pub) (GroupPri_FFDHE3072 pri) = calcDHShared ffdhe3072 pub pri+groupDecapsulate (GroupPubB_FFDHE4096 pub) (GroupPri_FFDHE4096 pri) = calcDHShared ffdhe4096 pub pri+groupDecapsulate (GroupPubB_FFDHE6144 pub) (GroupPri_FFDHE6144 pri) = calcDHShared ffdhe6144 pub pri+groupDecapsulate (GroupPubB_FFDHE8192 pub) (GroupPri_FFDHE8192 pri) = calcDHShared ffdhe8192 pub pri+groupDecapsulate (GroupPubB_MLKEM512 p) (GroupPri_MLKEM512 s) =+    Just $ convert $ ML.decapsulate s p+groupDecapsulate (GroupPubB_MLKEM768 p) (GroupPri_MLKEM768 s) =+    Just $ convert $ ML.decapsulate s p+groupDecapsulate (GroupPubB_MLKEM1024 p) (GroupPri_MLKEM1024 s) =+    Just $ convert $ ML.decapsulate s p+groupDecapsulate (GroupPubB_X25519MLKEM768 (p1, p2)) (GroupPri_X25519MLKEM768 (s1, s2)) = do+    bs1 <- (unwrap <$>) . maybeCryptoError $ deriveDecrypt x25519 p1 s1+    let bs2 = convert $ ML.decapsulate s2 p2+    return (bs2 <> bs1)+groupDecapsulate (GroupPubB_P256MLKEM768 (p1, p2)) (GroupPri_P256MLKEM768 (s1, s2)) = do+    bs1 <- (unwrap <$>) . maybeCryptoError $ deriveDecrypt p256 p1 s1+    let bs2 = convert $ ML.decapsulate s2 p2+    return (bs1 <> bs2)+groupDecapsulate (GroupPubB_P384MLKEM1024 (p1, p2)) (GroupPri_P384MLKEM1024 (s1, s2)) = do+    bs1 <- (unwrap <$>) . maybeCryptoError $ deriveDecrypt p384 p1 s1+    let bs2 = convert $ ML.decapsulate s2 p2+    return (bs1 <> bs2)+groupDecapsulate _ _ = Nothing++calcDHShared :: DH.Params -> PublicNumber -> PrivateNumber -> Maybe GroupKey+calcDHShared params pub pri+    | valid params pub = Just $ convert share+    | otherwise = Nothing   where-    SharedKey share = getShared params pri pub+    share = DH.getShared params pri pub -encodeGroupPublic :: GroupPublic -> ByteString-encodeGroupPublic (GroupPub_P256 p) = encodePoint p256 p-encodeGroupPublic (GroupPub_P384 p) = encodePoint p384 p-encodeGroupPublic (GroupPub_P521 p) = encodePoint p521 p-encodeGroupPublic (GroupPub_X255 p) = encodePoint x25519 p-encodeGroupPublic (GroupPub_X448 p) = encodePoint x448 p-encodeGroupPublic (GroupPub_FFDHE2048 p) = enc ffdhe2048 p-encodeGroupPublic (GroupPub_FFDHE3072 p) = enc ffdhe3072 p-encodeGroupPublic (GroupPub_FFDHE4096 p) = enc ffdhe4096 p-encodeGroupPublic (GroupPub_FFDHE6144 p) = enc ffdhe6144 p-encodeGroupPublic (GroupPub_FFDHE8192 p) = enc ffdhe8192 p+groupEncodePublicA :: GroupPublicA -> ByteString+groupEncodePublicA (GroupPubA_P256 p) = encodePoint p256 p+groupEncodePublicA (GroupPubA_P384 p) = encodePoint p384 p+groupEncodePublicA (GroupPubA_P521 p) = encodePoint p521 p+groupEncodePublicA (GroupPubA_X255 p) = encodePoint x25519 p+groupEncodePublicA (GroupPubA_X448 p) = encodePoint x448 p+groupEncodePublicA (GroupPubA_FFDHE2048 p) = enc ffdhe2048 p+groupEncodePublicA (GroupPubA_FFDHE3072 p) = enc ffdhe3072 p+groupEncodePublicA (GroupPubA_FFDHE4096 p) = enc ffdhe4096 p+groupEncodePublicA (GroupPubA_FFDHE6144 p) = enc ffdhe6144 p+groupEncodePublicA (GroupPubA_FFDHE8192 p) = enc ffdhe8192 p+groupEncodePublicA (GroupPubA_MLKEM512 p) = ML.encode p+groupEncodePublicA (GroupPubA_MLKEM768 p) = ML.encode p+groupEncodePublicA (GroupPubA_MLKEM1024 p) = ML.encode p+groupEncodePublicA (GroupPubA_X25519MLKEM768 (p1, p2)) =+    ML.encode p2 <> encodePoint x25519 p1+groupEncodePublicA (GroupPubA_P256MLKEM768 (p1, p2)) =+    encodePoint p256 p1 <> ML.encode p2+groupEncodePublicA (GroupPubA_P384MLKEM1024 (p1, p2)) =+    encodePoint p384 p1 <> ML.encode p2 -enc :: Params -> PublicNumber -> ByteString-enc params (PublicNumber p) = i2ospOf_ ((params_bits params + 7) `div` 8) p+groupEncodePublicB :: GroupPublicB -> ByteString+groupEncodePublicB (GroupPubB_P256 p) = encodePoint p256 p+groupEncodePublicB (GroupPubB_P384 p) = encodePoint p384 p+groupEncodePublicB (GroupPubB_P521 p) = encodePoint p521 p+groupEncodePublicB (GroupPubB_X255 p) = encodePoint x25519 p+groupEncodePublicB (GroupPubB_X448 p) = encodePoint x448 p+groupEncodePublicB (GroupPubB_FFDHE2048 p) = enc ffdhe2048 p+groupEncodePublicB (GroupPubB_FFDHE3072 p) = enc ffdhe3072 p+groupEncodePublicB (GroupPubB_FFDHE4096 p) = enc ffdhe4096 p+groupEncodePublicB (GroupPubB_FFDHE6144 p) = enc ffdhe6144 p+groupEncodePublicB (GroupPubB_FFDHE8192 p) = enc ffdhe8192 p+groupEncodePublicB (GroupPubB_MLKEM512 p) = convert p+groupEncodePublicB (GroupPubB_MLKEM768 p) = convert p+groupEncodePublicB (GroupPubB_MLKEM1024 p) = convert p+groupEncodePublicB (GroupPubB_X25519MLKEM768 (p1, p2)) =+    convert p2 <> encodePoint x25519 p1+groupEncodePublicB (GroupPubB_P256MLKEM768 (p1, p2)) =+    encodePoint p256 p1 <> convert p2+groupEncodePublicB (GroupPubB_P384MLKEM1024 (p1, p2)) =+    encodePoint p384 p1 <> convert p2 -decodeGroupPublic :: Group -> ByteString -> Either CryptoError GroupPublic-decodeGroupPublic P256   bs = eitherCryptoError $ GroupPub_P256 <$> decodePoint p256 bs-decodeGroupPublic P384   bs = eitherCryptoError $ GroupPub_P384 <$> decodePoint p384 bs-decodeGroupPublic P521   bs = eitherCryptoError $ GroupPub_P521 <$> decodePoint p521 bs-decodeGroupPublic X25519 bs = eitherCryptoError $ GroupPub_X255 <$> decodePoint x25519 bs-decodeGroupPublic X448 bs = eitherCryptoError $ GroupPub_X448 <$> decodePoint x448 bs-decodeGroupPublic FFDHE2048 bs = Right . GroupPub_FFDHE2048 . PublicNumber $ os2ip bs-decodeGroupPublic FFDHE3072 bs = Right . GroupPub_FFDHE3072 . PublicNumber $ os2ip bs-decodeGroupPublic FFDHE4096 bs = Right . GroupPub_FFDHE4096 . PublicNumber $ os2ip bs-decodeGroupPublic FFDHE6144 bs = Right . GroupPub_FFDHE6144 . PublicNumber $ os2ip bs-decodeGroupPublic FFDHE8192 bs = Right . GroupPub_FFDHE8192 . PublicNumber $ os2ip bs+enc :: DH.Params -> PublicNumber -> ByteString+enc params (PublicNumber p) = i2ospOf_ ((DH.params_bits params + 7) `div` 8) p +groupDecodePublicA :: Group -> ByteString -> Either CryptoError GroupPublicA+groupDecodePublicA P256 bs = eitherCryptoError $ GroupPubA_P256 <$> decodePoint p256 bs+groupDecodePublicA P384 bs = eitherCryptoError $ GroupPubA_P384 <$> decodePoint p384 bs+groupDecodePublicA P521 bs = eitherCryptoError $ GroupPubA_P521 <$> decodePoint p521 bs+groupDecodePublicA X25519 bs = eitherCryptoError $ GroupPubA_X255 <$> decodePoint x25519 bs+groupDecodePublicA X448 bs = eitherCryptoError $ GroupPubA_X448 <$> decodePoint x448 bs+groupDecodePublicA FFDHE2048 bs = Right . GroupPubA_FFDHE2048 . PublicNumber $ os2ip bs+groupDecodePublicA FFDHE3072 bs = Right . GroupPubA_FFDHE3072 . PublicNumber $ os2ip bs+groupDecodePublicA FFDHE4096 bs = Right . GroupPubA_FFDHE4096 . PublicNumber $ os2ip bs+groupDecodePublicA FFDHE6144 bs = Right . GroupPubA_FFDHE6144 . PublicNumber $ os2ip bs+groupDecodePublicA FFDHE8192 bs = Right . GroupPubA_FFDHE8192 . PublicNumber $ os2ip bs+groupDecodePublicA MLKEM512 bs = case ML.decode mlkem512 bs of+    Nothing -> Left CryptoError_PointFormatInvalid+    Just p -> Right $ GroupPubA_MLKEM512 p+groupDecodePublicA MLKEM768 bs = case ML.decode mlkem768 bs of+    Nothing -> Left CryptoError_PointFormatInvalid+    Just p -> Right $ GroupPubA_MLKEM768 p+groupDecodePublicA MLKEM1024 bs = case ML.decode mlkem1024 bs of+    Nothing -> Left CryptoError_PointFormatInvalid+    Just p -> Right $ GroupPubA_MLKEM1024 p+groupDecodePublicA X25519MLKEM768 bs =+    let (bs1, bs2) = BA.splitAt 1184 bs+     in case ML.decode mlkem768 bs1 of+            Nothing -> Left CryptoError_PointFormatInvalid+            Just p1 -> case maybeCryptoError $ decodePoint x25519 bs2 of+                Nothing -> Left CryptoError_PointFormatInvalid+                Just p2 -> Right $ GroupPubA_X25519MLKEM768 (p2, p1)+groupDecodePublicA P256MLKEM768 bs =+    let (bs1, bs2) = BA.splitAt 65 bs+     in case ML.decode mlkem768 bs2 of+            Nothing -> Left CryptoError_PointFormatInvalid+            Just p1 -> case maybeCryptoError $ decodePoint p256 bs1 of+                Nothing -> Left CryptoError_PointFormatInvalid+                Just p2 -> Right $ GroupPubA_P256MLKEM768 (p2, p1)+groupDecodePublicA P384MLKEM1024 bs =+    let (bs1, bs2) = BA.splitAt 97 bs+     in case ML.decode mlkem1024 bs2 of+            Nothing -> Left CryptoError_PointFormatInvalid+            Just p1 -> case maybeCryptoError $ decodePoint p384 bs1 of+                Nothing -> Left CryptoError_PointFormatInvalid+                Just p2 -> Right $ GroupPubA_P384MLKEM1024 (p2, p1)+groupDecodePublicA _ _ = error "groupDecodePublicA"++groupDecodePublicB :: Group -> ByteString -> Either CryptoError GroupPublicB+groupDecodePublicB P256 bs = eitherCryptoError $ GroupPubB_P256 <$> decodePoint p256 bs+groupDecodePublicB P384 bs = eitherCryptoError $ GroupPubB_P384 <$> decodePoint p384 bs+groupDecodePublicB P521 bs = eitherCryptoError $ GroupPubB_P521 <$> decodePoint p521 bs+groupDecodePublicB X25519 bs = eitherCryptoError $ GroupPubB_X255 <$> decodePoint x25519 bs+groupDecodePublicB X448 bs = eitherCryptoError $ GroupPubB_X448 <$> decodePoint x448 bs+groupDecodePublicB FFDHE2048 bs = Right . GroupPubB_FFDHE2048 . PublicNumber $ os2ip bs+groupDecodePublicB FFDHE3072 bs = Right . GroupPubB_FFDHE3072 . PublicNumber $ os2ip bs+groupDecodePublicB FFDHE4096 bs = Right . GroupPubB_FFDHE4096 . PublicNumber $ os2ip bs+groupDecodePublicB FFDHE6144 bs = Right . GroupPubB_FFDHE6144 . PublicNumber $ os2ip bs+groupDecodePublicB FFDHE8192 bs = Right . GroupPubB_FFDHE8192 . PublicNumber $ os2ip bs+groupDecodePublicB MLKEM512 bs = case ML.decode mlkem512 bs of+    Nothing -> Left CryptoError_PointFormatInvalid+    Just p -> Right $ GroupPubB_MLKEM512 p+groupDecodePublicB MLKEM768 bs = case ML.decode mlkem768 bs of+    Nothing -> Left CryptoError_PointFormatInvalid+    Just p -> Right $ GroupPubB_MLKEM768 p+groupDecodePublicB MLKEM1024 bs = case ML.decode mlkem1024 bs of+    Nothing -> Left CryptoError_PointFormatInvalid+    Just p -> Right $ GroupPubB_MLKEM1024 p+groupDecodePublicB X25519MLKEM768 bs =+    let (bs1, bs2) = BA.splitAt 1088 bs+     in case ML.decode mlkem768 bs1 of+            Nothing -> Left CryptoError_PointFormatInvalid+            Just p1 -> case maybeCryptoError $ decodePoint x25519 bs2 of+                Nothing -> Left CryptoError_PointFormatInvalid+                Just p2 -> Right $ GroupPubB_X25519MLKEM768 (p2, p1)+groupDecodePublicB P256MLKEM768 bs =+    let (bs1, bs2) = BA.splitAt 65 bs+     in case ML.decode mlkem768 bs2 of+            Nothing -> Left CryptoError_PointFormatInvalid+            Just p1 -> case maybeCryptoError $ decodePoint p256 bs1 of+                Nothing -> Left CryptoError_PointFormatInvalid+                Just p2 -> Right $ GroupPubB_P256MLKEM768 (p2, p1)+groupDecodePublicB P384MLKEM1024 bs =+    let (bs1, bs2) = BA.splitAt 97 bs+     in case ML.decode mlkem1024 bs2 of+            Nothing -> Left CryptoError_PointFormatInvalid+            Just p1 -> case maybeCryptoError $ decodePoint p384 bs1 of+                Nothing -> Left CryptoError_PointFormatInvalid+                Just p2 -> Right $ GroupPubB_P384MLKEM1024 (p2, p1)+groupDecodePublicB _ _ = error "groupDecodePublicB"+ -- Check that group element in not in the 2-element subgroup { 1, p - 1 }. -- See RFC 7919 section 3 and NIST SP 56A rev 2 section 5.6.2.3.1.-valid :: Params -> PublicNumber -> Bool-valid (Params p _ _) (PublicNumber y) = 1 < y && y < p - 1+valid :: DH.Params -> PublicNumber -> Bool+valid (DH.Params p _ _) (PublicNumber y) = 1 < y && y < p - 1  -- strips leading zeros from the result of getShared, as required--- for DH(E) premaster secret in SSL/TLS before version 1.3.-stripLeadingZeros :: SharedKey -> B.ScrubbedBytes-stripLeadingZeros (SharedKey sb) = snd $ B.span (== 0) sb+-- for DH(E) pre-main secret in SSL/TLS before version 1.3.+stripLeadingZeros :: DH.SharedKey -> ScrubbedBytes+stripLeadingZeros (DH.SharedKey sb) = snd $ BA.span (== 0) sb  -- Use short exponents as optimization, see RFC 7919 section 5.2. generatePriv :: MonadRandom r => Int -> r PrivateNumber
Network/TLS/Crypto/Types.hs view
@@ -1,26 +1,143 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE PatternSynonyms #-}+ -- | -- Module      : Network.TLS.Crypto.Types -- License     : BSD-style -- Maintainer  : Kazu Yamamoto <kazu@iij.ad.jp> -- Stability   : experimental -- Portability : unknown----module Network.TLS.Crypto.Types where+module Network.TLS.Crypto.Types (+    Group (+        Group,+        P256,+        P384,+        P521,+        X25519,+        X448,+        FFDHE2048,+        FFDHE3072,+        FFDHE4096,+        FFDHE6144,+        FFDHE8192,+        MLKEM512,+        MLKEM768,+        MLKEM1024,+        X25519MLKEM768,+        P256MLKEM768,+        P384MLKEM1024+    ),+    availableFFGroups,+    availableECGroups,+    availableHybridGroups,+    supportedNamedGroups,+    supportedNamedGroupsTLS13,+    KeyExchangeSignatureAlg (..),+) where -data Group = P256 | P384 | P521 | X25519 | X448-           | FFDHE2048 | FFDHE3072 | FFDHE4096 | FFDHE6144 | FFDHE8192-           deriving (Eq, Show)+import Codec.Serialise+import Data.Word+import GHC.Generics +newtype Group = Group Word16 deriving (Eq, Generic)+instance Serialise Group++{- FOURMOLU_DISABLE -}+pattern P256      :: Group+pattern P256       = Group 23+pattern P384      :: Group+pattern P384       = Group 24+pattern P521      :: Group+pattern P521       = Group 25+pattern X25519    :: Group+pattern X25519     = Group 29+pattern X448      :: Group+pattern X448       = Group 30+pattern FFDHE2048 :: Group+pattern FFDHE2048  = Group 256+pattern FFDHE3072 :: Group+pattern FFDHE3072  = Group 257+pattern FFDHE4096 :: Group+pattern FFDHE4096  = Group 258+pattern FFDHE6144 :: Group+pattern FFDHE6144  = Group 259+pattern FFDHE8192 :: Group+pattern FFDHE8192  = Group 260+pattern MLKEM512  :: Group+pattern MLKEM512   = Group 512+pattern MLKEM768  :: Group+pattern MLKEM768   = Group 513+pattern MLKEM1024 :: Group+pattern MLKEM1024  = Group 514+pattern X25519MLKEM768 :: Group+pattern X25519MLKEM768  = Group 4588+pattern P256MLKEM768   :: Group+pattern P256MLKEM768    = Group 4587+pattern P384MLKEM1024  :: Group+pattern P384MLKEM1024   = Group 4589++instance Show Group where+    show P256      = "P256"+    show P384      = "P384"+    show P521      = "P521"+    show X25519    = "X25519"+    show X448      = "X448"+    show FFDHE2048 = "FFDHE2048"+    show FFDHE3072 = "FFDHE3072"+    show FFDHE4096 = "FFDHE4096"+    show FFDHE6144 = "FFDHE6144"+    show FFDHE8192 = "FFDHE8192"+    show MLKEM512  = "MLKEM512"+    show MLKEM768  = "MLKEM768"+    show MLKEM1024 = "MLKEM1024"+    show X25519MLKEM768 = "X25519MLKEM768"+    show P256MLKEM768   = "P256MLKEM768"+    show P384MLKEM1024  = "P384MLKEM1024"+    show (Group x) = "Group " ++ show x+{- FOURMOLU_ENABLE -}+ availableFFGroups :: [Group]-availableFFGroups = [FFDHE2048,FFDHE3072,FFDHE4096,FFDHE6144,FFDHE8192]+availableFFGroups = [FFDHE2048, FFDHE3072, FFDHE4096, FFDHE6144, FFDHE8192]  availableECGroups :: [Group]-availableECGroups = [P256,P384,P521,X25519,X448]+availableECGroups = [P256, P384, P521, X25519, X448] -availableGroups :: [Group]-availableGroups = availableECGroups ++ availableFFGroups+availableHybridGroups :: [Group]+availableHybridGroups = [X25519MLKEM768, P256MLKEM768, P384MLKEM1024] --- Digital signature algorithm, in close relation to public/private key types--- and cipher key exchange.-data DigitalSignatureAlg = RSA | DSS | ECDSA | Ed25519 | Ed448-                           deriving (Show, Eq)+-- | A list for named groups.  The ordering is for client preference+--   because server preference is not used in our server+--   implementation.+supportedNamedGroups :: [Group]+supportedNamedGroups =+    [ X25519 -- 128 bits security+    , P256 -- 128 bits security+    , P384 -- 192 bits security+    , X448 -- 224 bits security+    , P521 -- 256 bits security+    --    , FFDHE2048 -- 103 bits security+    , FFDHE3072 -- 125 bits security+    , FFDHE4096 -- 150 bits security+    , FFDHE6144 -- 175 bits security+    , FFDHE8192 -- 192 bits security+    , X25519MLKEM768+    , P256MLKEM768+    , P384MLKEM1024+    , -- , MLKEM512+      MLKEM768+    , MLKEM1024+    ]++supportedNamedGroupsTLS13 :: [[Group]]+supportedNamedGroupsTLS13 =+    [ [X25519MLKEM768, P256MLKEM768, P384MLKEM1024]+    , [X25519, P256]+    , [P384, X448, P521]+    , [FFDHE2048, FFDHE3072, FFDHE4096, FFDHE6144, FFDHE8192]+    , [MLKEM768, MLKEM1024]+    ]++-- Key-exchange signature algorithm, in close relation to ciphers+-- (before TLS 1.3).+data KeyExchangeSignatureAlg = KX_RSA | KX_DSA | KX_ECDSA+    deriving (Show, Eq)
Network/TLS/ErrT.hs view
@@ -1,29 +1,15 @@--- |--- Module      : Network.TLS.ErrT--- License     : BSD-style--- Maintainer  : Vincent Hanquez <vincent@snarc.org>--- Stability   : experimental--- Portability : unknown------ a simple compat ErrorT and other error stuff {-# LANGUAGE CPP #-}-module Network.TLS.ErrT-    ( runErrT-    , ErrT-    , Error(..)-    , MonadError(..)-    ) where -#if MIN_VERSION_mtl(2,2,1)-import Control.Monad.Except-import Control.Monad.Error.Class (Error(..))+-- | A simple compat ErrorT and other error stuff+module Network.TLS.ErrT (+    runErrT,+    ErrT,+    MonadError (..),+) where++import Control.Monad.Except (MonadError (..))+import Control.Monad.Trans.Except (ExceptT, runExceptT)+ runErrT :: ExceptT e m a -> m (Either e a) runErrT = runExceptT type ErrT = ExceptT-#else-import Control.Monad.Error-runErrT :: ErrorT e m a -> m (Either e a)-runErrT = runErrorT-type ErrT = ErrorT-#endif-
+ Network/TLS/Error.hs view
@@ -0,0 +1,198 @@+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE PatternSynonyms #-}++module Network.TLS.Error where++import Control.Exception (Exception (..))+import Data.Typeable++import Network.TLS.Imports++----------------------------------------------------------------++-- | TLSError that might be returned through the TLS stack.+--+-- Prior to version 1.8.0, this type had an @Exception@ instance.+-- In version 1.8.0, this instance was removed, and functions in+-- this library now only throw 'TLSException'.+data TLSError+    = -- | mainly for instance of Error+      Error_Misc String+    | -- | A fatal error condition was encountered at a low level.  The+      -- elements of the tuple give (freeform text description, structured+      -- error description).+      Error_Protocol String AlertDescription+    | -- | A non-fatal error condition was encountered at a low level at a low+      -- level.  The elements of the tuple give (freeform text description,+      -- structured error description).+      Error_Protocol_Warning String AlertDescription+    | Error_Certificate String+    | -- | handshake policy failed.+      Error_HandshakePolicy String+    | Error_EOF+    | Error_Packet String+    | Error_Packet_unexpected String String+    | Error_Packet_Parsing String+    | Error_TCP_Terminate+    deriving (Eq, Show, Typeable)++----------------------------------------------------------------++-- | TLS Exceptions. Some of the data constructors indicate incorrect use of+--   the library, and the documentation for those data constructors calls+--   this out. The others wrap 'TLSError' with some kind of context to explain+--   when the exception occurred.+data TLSException+    = -- | Early termination exception with the reason and the error associated+      Terminated Bool String TLSError+    | -- | Handshake failed for the reason attached.+      HandshakeFailed TLSError+    | -- | Failure occurred while sending or receiving data after the+      --   TLS handshake succeeded.+      PostHandshake TLSError+    | -- | Lifts a 'TLSError' into 'TLSException' without provided any context+      --   around when the error happened.+      Uncontextualized TLSError+    | -- | Usage error when the connection has not been established+      --   and the user is trying to send or receive data.+      --   Indicates that this library has been used incorrectly.+      ConnectionNotEstablished+    | -- | Expected that a TLS handshake had already taken place, but no TLS+      --   handshake had occurred.+      --   Indicates that this library has been used incorrectly.+      MissingHandshake+    deriving (Show, Eq, Typeable)++instance Exception TLSException++----------------------------------------------------------------++newtype AlertLevel = AlertLevel {fromAlertLevel :: Word8} deriving (Eq)++{- FOURMOLU_DISABLE -}+pattern AlertLevel_Warning :: AlertLevel+pattern AlertLevel_Warning  = AlertLevel 1+pattern AlertLevel_Fatal   :: AlertLevel+pattern AlertLevel_Fatal    = AlertLevel 2++instance Show AlertLevel where+    show AlertLevel_Warning = "AlertLevel_Warning"+    show AlertLevel_Fatal   = "AlertLevel_Fatal"+    show (AlertLevel x)     = "AlertLevel " ++ show x+{- FOURMOLU_ENABLE -}++----------------------------------------------------------------++newtype AlertDescription = AlertDescription {fromAlertDescription :: Word8}+    deriving (Eq)++{- FOURMOLU_DISABLE -}+pattern CloseNotify                  :: AlertDescription+pattern CloseNotify                   = AlertDescription 0+pattern UnexpectedMessage            :: AlertDescription+pattern UnexpectedMessage             = AlertDescription 10+pattern BadRecordMac                 :: AlertDescription+pattern BadRecordMac                  = AlertDescription 20+pattern DecryptionFailed             :: AlertDescription+pattern DecryptionFailed              = AlertDescription 21+pattern RecordOverflow               :: AlertDescription+pattern RecordOverflow                = AlertDescription 22+pattern DecompressionFailure         :: AlertDescription+pattern DecompressionFailure          = AlertDescription 30+pattern HandshakeFailure             :: AlertDescription+pattern HandshakeFailure              = AlertDescription 40+pattern BadCertificate               :: AlertDescription+pattern BadCertificate                = AlertDescription 42+pattern UnsupportedCertificate       :: AlertDescription+pattern UnsupportedCertificate        = AlertDescription 43+pattern CertificateRevoked           :: AlertDescription+pattern CertificateRevoked            = AlertDescription 44+pattern CertificateExpired           :: AlertDescription+pattern CertificateExpired            = AlertDescription 45+pattern CertificateUnknown           :: AlertDescription+pattern CertificateUnknown            = AlertDescription 46+pattern IllegalParameter             :: AlertDescription+pattern IllegalParameter              = AlertDescription 47+pattern UnknownCa                    :: AlertDescription+pattern UnknownCa                     = AlertDescription 48+pattern AccessDenied                 :: AlertDescription+pattern AccessDenied                  = AlertDescription 49+pattern DecodeError                  :: AlertDescription+pattern DecodeError                   = AlertDescription 50+pattern DecryptError                 :: AlertDescription+pattern DecryptError                  = AlertDescription 51+pattern ExportRestriction            :: AlertDescription+pattern ExportRestriction             = AlertDescription 60+pattern ProtocolVersion              :: AlertDescription+pattern ProtocolVersion               = AlertDescription 70+pattern InsufficientSecurity         :: AlertDescription+pattern InsufficientSecurity          = AlertDescription 71+pattern InternalError                :: AlertDescription+pattern InternalError                 = AlertDescription 80+pattern InappropriateFallback        :: AlertDescription+pattern InappropriateFallback         = AlertDescription 86  -- RFC7507+pattern UserCanceled                 :: AlertDescription+pattern UserCanceled                  = AlertDescription 90+pattern NoRenegotiation              :: AlertDescription+pattern NoRenegotiation               = AlertDescription 100+pattern MissingExtension             :: AlertDescription+pattern MissingExtension              = AlertDescription 109+pattern UnsupportedExtension         :: AlertDescription+pattern UnsupportedExtension          = AlertDescription 110+pattern CertificateUnobtainable      :: AlertDescription+pattern CertificateUnobtainable       = AlertDescription 111+pattern UnrecognizedName             :: AlertDescription+pattern UnrecognizedName              = AlertDescription 112+pattern BadCertificateStatusResponse :: AlertDescription+pattern BadCertificateStatusResponse  = AlertDescription 113+pattern BadCertificateHashValue      :: AlertDescription+pattern BadCertificateHashValue       = AlertDescription 114+pattern UnknownPskIdentity           :: AlertDescription+pattern UnknownPskIdentity            = AlertDescription 115+pattern CertificateRequired          :: AlertDescription+pattern CertificateRequired           = AlertDescription 116+pattern GeneralError                 :: AlertDescription+pattern GeneralError                  = AlertDescription 117+pattern NoApplicationProtocol        :: AlertDescription+pattern NoApplicationProtocol         = AlertDescription 120 -- RFC7301+pattern EchRequired                  :: AlertDescription+pattern EchRequired                   = AlertDescription 121 -- draft++instance Show AlertDescription where+    show CloseNotify                  = "CloseNotify"+    show UnexpectedMessage            = "UnexpectedMessage"+    show BadRecordMac                 = "BadRecordMac"+    show DecryptionFailed             = "DecryptionFailed"+    show RecordOverflow               = "RecordOverflow"+    show DecompressionFailure         = "DecompressionFailure"+    show HandshakeFailure             = "HandshakeFailure"+    show BadCertificate               = "BadCertificate"+    show UnsupportedCertificate       = "UnsupportedCertificate"+    show CertificateRevoked           = "CertificateRevoked"+    show CertificateExpired           = "CertificateExpired"+    show CertificateUnknown           = "CertificateUnknown"+    show IllegalParameter             = "IllegalParameter"+    show UnknownCa                    = "UnknownCa"+    show AccessDenied                 = "AccessDenied"+    show DecodeError                  = "DecodeError"+    show DecryptError                 = "DecryptError"+    show ExportRestriction            = "ExportRestriction"+    show ProtocolVersion              = "ProtocolVersion"+    show InsufficientSecurity         = "InsufficientSecurity"+    show InternalError                = "InternalError"+    show InappropriateFallback        = "InappropriateFallback"+    show UserCanceled                 = "UserCanceled"+    show NoRenegotiation              = "NoRenegotiation"+    show MissingExtension             = "MissingExtension"+    show UnsupportedExtension         = "UnsupportedExtension"+    show CertificateUnobtainable      = "CertificateUnobtainable"+    show UnrecognizedName             = "UnrecognizedName"+    show BadCertificateStatusResponse = "BadCertificateStatusResponse"+    show BadCertificateHashValue      = "BadCertificateHashValue"+    show UnknownPskIdentity           = "UnknownPskIdentity"+    show CertificateRequired          = "CertificateRequired"+    show GeneralError                 = "GeneralError"+    show NoApplicationProtocol        = "NoApplicationProtocol"+    show EchRequired                  = "EchRequired"+    show (AlertDescription x)         = "AlertDescription " ++ show x+{- FOURMOLU_ENABLE -}
Network/TLS/Extension.hs view
@@ -1,298 +1,1175 @@--- |--- Module      : Network.TLS.Extension--- License     : BSD-style--- Maintainer  : Vincent Hanquez <vincent@snarc.org>--- Stability   : experimental--- Portability : unknown------ basic extensions are defined in RFC 6066----module Network.TLS.Extension-    ( Extension(..)-    , supportedExtensions-    , definedExtensions-    -- all extensions ID supported-    , extensionID_ServerName-    , extensionID_MaxFragmentLength-    , extensionID_SecureRenegotiation-    , extensionID_ApplicationLayerProtocolNegotiation-    , extensionID_NegotiatedGroups-    , extensionID_EcPointFormats-    , extensionID_Heartbeat-    , extensionID_SignatureAlgorithms-    -- all implemented extensions-    , ServerNameType(..)-    , ServerName(..)-    , MaxFragmentLength(..)-    , MaxFragmentEnum(..)-    , SecureRenegotiation(..)-    , ApplicationLayerProtocolNegotiation(..)-    , NegotiatedGroups(..)-    , Group(..)-    , EcPointFormatsSupported(..)-    , EcPointFormat(..)-    , SessionTicket(..)-    , HeartBeat(..)-    , HeartBeatMode(..)-    , SignatureAlgorithms(..)-    ) where--import qualified Data.ByteString as B-import qualified Data.ByteString.Char8 as BC--import Network.TLS.Struct (ExtensionID, EnumSafe8(..), EnumSafe16(..), HashAndSignatureAlgorithm)-import Network.TLS.Crypto.Types-import Network.TLS.Wire-import Network.TLS.Imports-import Network.TLS.Packet (putSignatureHashAlgorithm, getSignatureHashAlgorithm)--type HostName = String---- central list defined in <http://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.txt>-extensionID_ServerName-  , extensionID_MaxFragmentLength-  , extensionID_ClientCertificateUrl-  , extensionID_TrustedCAKeys-  , extensionID_TruncatedHMAC-  , extensionID_StatusRequest-  , extensionID_UserMapping-  , extensionID_ClientAuthz-  , extensionID_ServerAuthz-  , extensionID_CertType-  , extensionID_NegotiatedGroups-  , extensionID_EcPointFormats-  , extensionID_SRP-  , extensionID_SignatureAlgorithms-  , extensionID_SRTP-  , extensionID_Heartbeat-  , extensionID_ApplicationLayerProtocolNegotiation-  , extensionID_StatusRequestv2-  , extensionID_SignedCertificateTimestamp-  , extensionID_ClientCertificateType-  , extensionID_ServerCertificateType-  , extensionID_Padding-  , extensionID_EncryptThenMAC-  , extensionID_ExtendedMasterSecret-  , extensionID_SessionTicket-  , extensionID_SecureRenegotiation :: ExtensionID-extensionID_ServerName                          = 0x0 -- RFC6066-extensionID_MaxFragmentLength                   = 0x1 -- RFC6066-extensionID_ClientCertificateUrl                = 0x2 -- RFC6066-extensionID_TrustedCAKeys                       = 0x3 -- RFC6066-extensionID_TruncatedHMAC                       = 0x4 -- RFC6066-extensionID_StatusRequest                       = 0x5 -- RFC6066-extensionID_UserMapping                         = 0x6 -- RFC4681-extensionID_ClientAuthz                         = 0x7 -- RFC5878-extensionID_ServerAuthz                         = 0x8 -- RFC5878-extensionID_CertType                            = 0x9 -- RFC6091-extensionID_NegotiatedGroups                    = 0xa -- RFC4492bis and TLS 1.3-extensionID_EcPointFormats                      = 0xb -- RFC4492-extensionID_SRP                                 = 0xc -- RFC5054-extensionID_SignatureAlgorithms                 = 0xd -- RFC5246-extensionID_SRTP                                = 0xe -- RFC5764-extensionID_Heartbeat                           = 0xf -- RFC6520-extensionID_ApplicationLayerProtocolNegotiation = 0x10 -- RFC7301-extensionID_StatusRequestv2                     = 0x11 -- RFC6961-extensionID_SignedCertificateTimestamp          = 0x12 -- RFC6962-extensionID_ClientCertificateType               = 0x13 -- RFC7250-extensionID_ServerCertificateType               = 0x14 -- RFC7250-extensionID_Padding                             = 0x15 -- draft-agl-tls-padding. expires 2015-03-12-extensionID_EncryptThenMAC                      = 0x16 -- RFC7366-extensionID_ExtendedMasterSecret                = 0x17 -- draft-ietf-tls-session-hash. expires 2015-09-26-extensionID_SessionTicket                       = 0x23 -- RFC4507-extensionID_SecureRenegotiation                 = 0xff01 -- RFC5746--definedExtensions :: [ExtensionID]-definedExtensions =-    [ extensionID_ServerName-    , extensionID_MaxFragmentLength-    , extensionID_ClientCertificateUrl-    , extensionID_TrustedCAKeys-    , extensionID_TruncatedHMAC-    , extensionID_StatusRequest-    , extensionID_UserMapping-    , extensionID_ClientAuthz-    , extensionID_ServerAuthz-    , extensionID_CertType-    , extensionID_NegotiatedGroups-    , extensionID_EcPointFormats-    , extensionID_SRP-    , extensionID_SignatureAlgorithms-    , extensionID_SRTP-    , extensionID_Heartbeat-    , extensionID_ApplicationLayerProtocolNegotiation-    , extensionID_StatusRequestv2-    , extensionID_SignedCertificateTimestamp-    , extensionID_ClientCertificateType-    , extensionID_ServerCertificateType-    , extensionID_Padding-    , extensionID_EncryptThenMAC-    , extensionID_ExtendedMasterSecret-    , extensionID_SessionTicket-    , extensionID_SecureRenegotiation-    ]---- | all supported extensions by the implementation-supportedExtensions :: [ExtensionID]-supportedExtensions = [ extensionID_ServerName-                      , extensionID_MaxFragmentLength-                      , extensionID_ApplicationLayerProtocolNegotiation-                      , extensionID_SecureRenegotiation-                      , extensionID_NegotiatedGroups-                      , extensionID_EcPointFormats-                      , extensionID_SignatureAlgorithms-                      ]---- | Extension class to transform bytes to and from a high level Extension type.-class Extension a where-    extensionID     :: a -> ExtensionID-    extensionDecode :: Bool -> ByteString -> Maybe a-    extensionEncode :: a -> ByteString---- | Server Name extension including the name type and the associated name.--- the associated name decoding is dependant of its name type.--- name type = 0 : hostname-newtype ServerName = ServerName [ServerNameType] deriving (Show,Eq)--data ServerNameType = ServerNameHostName HostName-                    | ServerNameOther    (Word8, ByteString)-                    deriving (Show,Eq)--instance Extension ServerName where-    extensionID _ = extensionID_ServerName-    extensionEncode (ServerName l) = runPut $ putOpaque16 (runPut $ mapM_ encodeNameType l)-        where encodeNameType (ServerNameHostName hn)       = putWord8 0  >> putOpaque16 (BC.pack hn) -- FIXME: should be puny code conversion-              encodeNameType (ServerNameOther (nt,opaque)) = putWord8 nt >> putBytes opaque-    extensionDecode _ = runGetMaybe (getWord16 >>= \len -> ServerName <$> getList (fromIntegral len) getServerName)-        where getServerName = do-                  ty    <- getWord8-                  sname <- getOpaque16-                  return (1+2+B.length sname, case ty of-                      0 -> ServerNameHostName $ BC.unpack sname -- FIXME: should be puny code conversion-                      _ -> ServerNameOther (ty, sname))---- | Max fragment extension with length from 512 bytes to 4096 bytes-newtype MaxFragmentLength = MaxFragmentLength MaxFragmentEnum deriving (Show,Eq)-data MaxFragmentEnum = MaxFragment512 | MaxFragment1024 | MaxFragment2048 | MaxFragment4096-    deriving (Show,Eq)--instance Extension MaxFragmentLength where-    extensionID _ = extensionID_MaxFragmentLength-    extensionEncode (MaxFragmentLength e) = B.singleton $ marshallSize e-        where marshallSize MaxFragment512  = 1-              marshallSize MaxFragment1024 = 2-              marshallSize MaxFragment2048 = 3-              marshallSize MaxFragment4096 = 4-    extensionDecode _ = runGetMaybe (MaxFragmentLength . unmarshallSize <$> getWord8)-        where unmarshallSize 1 = MaxFragment512-              unmarshallSize 2 = MaxFragment1024-              unmarshallSize 3 = MaxFragment2048-              unmarshallSize 4 = MaxFragment4096-              unmarshallSize n = error ("unknown max fragment size " ++ show n)---- | Secure Renegotiation-data SecureRenegotiation = SecureRenegotiation ByteString (Maybe ByteString)-    deriving (Show,Eq)--instance Extension SecureRenegotiation where-    extensionID _ = extensionID_SecureRenegotiation-    extensionEncode (SecureRenegotiation cvd svd) =-        runPut $ putOpaque8 (cvd `B.append` fromMaybe B.empty svd)-    extensionDecode isServerHello = runGetMaybe $ do-        opaque <- getOpaque8-        if isServerHello-           then let (cvd, svd) = B.splitAt (B.length opaque `div` 2) opaque-                 in return $ SecureRenegotiation cvd (Just svd)-           else return $ SecureRenegotiation opaque Nothing---- | Application Layer Protocol Negotiation (ALPN)-newtype ApplicationLayerProtocolNegotiation = ApplicationLayerProtocolNegotiation [ByteString] deriving (Show,Eq)--instance Extension ApplicationLayerProtocolNegotiation where-    extensionID _ = extensionID_ApplicationLayerProtocolNegotiation-    extensionEncode (ApplicationLayerProtocolNegotiation bytes) =-        runPut $ putOpaque16 $ runPut $ mapM_ putOpaque8 bytes-    extensionDecode _ = runGetMaybe (ApplicationLayerProtocolNegotiation <$> getALPN)-        where getALPN = do-                 _ <- getWord16-                 getALPN'-              getALPN' = do-                 avail <- remaining-                 case avail of-                     0 -> return []-                     _ -> (:) <$> getOpaque8 <*> getALPN'---newtype NegotiatedGroups = NegotiatedGroups [Group] deriving (Show,Eq)---- on decode, filter all unknown curves-instance Extension NegotiatedGroups where-    extensionID _ = extensionID_NegotiatedGroups-    extensionEncode (NegotiatedGroups groups) = runPut $ putWords16 $ map fromEnumSafe16 groups-    extensionDecode _ = runGetMaybe (NegotiatedGroups . mapMaybe toEnumSafe16 <$> getWords16)--newtype EcPointFormatsSupported = EcPointFormatsSupported [EcPointFormat] deriving (Show,Eq)--data EcPointFormat =-      EcPointFormat_Uncompressed-    | EcPointFormat_AnsiX962_compressed_prime-    | EcPointFormat_AnsiX962_compressed_char2-    deriving (Show,Eq)--instance EnumSafe8 EcPointFormat where-    fromEnumSafe8 EcPointFormat_Uncompressed = 0-    fromEnumSafe8 EcPointFormat_AnsiX962_compressed_prime = 1-    fromEnumSafe8 EcPointFormat_AnsiX962_compressed_char2 = 2--    toEnumSafe8 0 = Just EcPointFormat_Uncompressed-    toEnumSafe8 1 = Just EcPointFormat_AnsiX962_compressed_prime-    toEnumSafe8 2 = Just EcPointFormat_AnsiX962_compressed_char2-    toEnumSafe8 _ = Nothing---- on decode, filter all unknown formats-instance Extension EcPointFormatsSupported where-    extensionID _ = extensionID_EcPointFormats-    extensionEncode (EcPointFormatsSupported formats) = runPut $ putWords8 $ map fromEnumSafe8 formats-    extensionDecode _ = runGetMaybe (EcPointFormatsSupported . mapMaybe toEnumSafe8 <$> getWords8)--data SessionTicket = SessionTicket-    deriving (Show,Eq)--instance Extension SessionTicket where-    extensionID _ = extensionID_SessionTicket-    extensionEncode SessionTicket{} = runPut $ return ()-    extensionDecode _ = runGetMaybe (return SessionTicket)--newtype HeartBeat = HeartBeat HeartBeatMode deriving (Show,Eq)--data HeartBeatMode =-      HeartBeat_PeerAllowedToSend-    | HeartBeat_PeerNotAllowedToSend-    deriving (Show,Eq)--instance EnumSafe8 HeartBeatMode where-    fromEnumSafe8 HeartBeat_PeerAllowedToSend    = 1-    fromEnumSafe8 HeartBeat_PeerNotAllowedToSend = 2--    toEnumSafe8 1 = Just HeartBeat_PeerAllowedToSend-    toEnumSafe8 2 = Just HeartBeat_PeerNotAllowedToSend-    toEnumSafe8 _ = Nothing--instance Extension HeartBeat where-    extensionID _ = extensionID_Heartbeat-    extensionEncode (HeartBeat mode) = runPut $ putWord8 $ fromEnumSafe8 mode-    extensionDecode _ bs =-        case runGetMaybe (toEnumSafe8 <$> getWord8) bs of-            Just (Just mode) -> Just $ HeartBeat mode-            _                -> Nothing--newtype SignatureAlgorithms = SignatureAlgorithms [HashAndSignatureAlgorithm] deriving (Show,Eq)--instance Extension SignatureAlgorithms where-    extensionID _ = extensionID_SignatureAlgorithms-    extensionEncode (SignatureAlgorithms algs) =-        runPut $ putWord16 (fromIntegral (length algs * 2)) >> mapM_ putSignatureHashAlgorithm algs-    extensionDecode _ =-        runGetMaybe $ do-            len <- getWord16-            SignatureAlgorithms <$> getList (fromIntegral len) (getSignatureHashAlgorithm >>= \sh -> return (2, sh))+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE PatternSynonyms #-}+{-# LANGUAGE RecordWildCards #-}++-- | Basic extensions are defined in RFC 6066+module Network.TLS.Extension (+    -- * Extension identifiers+    ExtensionID (+        ..,+        EID_ServerName,+        EID_MaxFragmentLength,+        EID_ClientCertificateUrl,+        EID_TrustedCAKeys,+        EID_TruncatedHMAC,+        EID_StatusRequest,+        EID_UserMapping,+        EID_ClientAuthz,+        EID_ServerAuthz,+        EID_CertType,+        EID_SupportedGroups,+        EID_EcPointFormats,+        EID_SRP,+        EID_SignatureAlgorithms,+        EID_SRTP,+        EID_Heartbeat,+        EID_ApplicationLayerProtocolNegotiation,+        EID_StatusRequestv2,+        EID_SignedCertificateTimestamp,+        EID_ClientCertificateType,+        EID_ServerCertificateType,+        EID_Padding,+        EID_EncryptThenMAC,+        EID_ExtendedMainSecret,+        EID_CompressCertificate,+        EID_RecordSizeLimit,+        EID_SessionTicket,+        EID_PreSharedKey,+        EID_EarlyData,+        EID_SupportedVersions,+        EID_Cookie,+        EID_PskKeyExchangeModes,+        EID_CertificateAuthorities,+        EID_OidFilters,+        EID_PostHandshakeAuth,+        EID_SignatureAlgorithmsCert,+        EID_KeyShare,+        EID_QuicTransportParameters,+        EID_EchOuterExtensions,+        EID_EncryptedClientHello,+        EID_SecureRenegotiation+    ),+    definedExtensions,+    supportedExtensions,++    -- * Extension raw+    ExtensionRaw (..),+    toExtensionRaw,+    extensionLookup,+    lookupAndDecode,+    lookupAndDecodeAndDo,++    -- * Class+    Extension (..),++    -- * Extensions+    ServerNameType (..),+    ServerName (..),+    MaxFragmentLength (..),+    MaxFragmentEnum (..),+    SecureRenegotiation (..),+    ApplicationLayerProtocolNegotiation (..),+    ExtendedMainSecret (..),+    CertificateCompressionAlgorithm (.., CCA_Zlib, CCA_Brotli, CCA_Zstd),+    CompressCertificate (..),+    SupportedGroups (..),+    Group (..),+    EcPointFormatsSupported (..),+    EcPointFormat (+        EcPointFormat,+        EcPointFormat_Uncompressed,+        EcPointFormat_AnsiX962_compressed_prime,+        EcPointFormat_AnsiX962_compressed_char2+    ),+    RecordSizeLimit (..),+    SessionTicket (..),+    HeartBeat (..),+    HeartBeatMode (+        HeartBeatMode,+        HeartBeat_PeerAllowedToSend,+        HeartBeat_PeerNotAllowedToSend+    ),+    SignatureAlgorithms (..),+    SignatureAlgorithmsCert (..),+    SupportedVersions (..),+    KeyShare (..),+    KeyShareEntry (..),+    MessageType (..),+    PostHandshakeAuth (..),+    PskKexMode (PskKexMode, PSK_KE, PSK_DHE_KE),+    PskKeyExchangeModes (..),+    PskIdentity (..),+    PreSharedKey (..),+    EarlyDataIndication (..),+    Cookie (..),+    CertificateAuthorities (..),+    EchOuterExtensions (..),+    EncryptedClientHello (..),+) where++import qualified Control.Exception as E+import Crypto.HPKE+import qualified Data.ByteString as B+import qualified Data.ByteString.Char8 as BC+import Data.X509 (DistinguishedName)++import Network.TLS.ECH.Config++import Network.TLS.Crypto.Types+import Network.TLS.Error+import Network.TLS.HashAndSignature+import Network.TLS.Imports+import Network.TLS.Packet (+    getBinaryVersion,+    getDNames,+    getSignatureHashAlgorithm,+    putBinaryVersion,+    putDNames,+    putSignatureHashAlgorithm,+ )+import Network.TLS.Types (HostName, Ticket, Version)+import Network.TLS.Wire++----------------------------------------------------------------+-- Extension identifiers++-- | Identifier of a TLS extension.+--   <http://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.txt>+newtype ExtensionID = ExtensionID {fromExtensionID :: Word16} deriving (Eq)++{- FOURMOLU_DISABLE -}+pattern EID_ServerName                          :: ExtensionID -- RFC6066+pattern EID_ServerName                           = ExtensionID 0x0+pattern EID_MaxFragmentLength                   :: ExtensionID -- RFC6066+pattern EID_MaxFragmentLength                    = ExtensionID 0x1+pattern EID_ClientCertificateUrl                :: ExtensionID -- RFC6066+pattern EID_ClientCertificateUrl                 = ExtensionID 0x2+pattern EID_TrustedCAKeys                       :: ExtensionID -- RFC6066+pattern EID_TrustedCAKeys                        = ExtensionID 0x3+pattern EID_TruncatedHMAC                       :: ExtensionID -- RFC6066+pattern EID_TruncatedHMAC                        = ExtensionID 0x4+pattern EID_StatusRequest                       :: ExtensionID -- RFC6066+pattern EID_StatusRequest                        = ExtensionID 0x5+pattern EID_UserMapping                         :: ExtensionID -- RFC4681+pattern EID_UserMapping                          = ExtensionID 0x6+pattern EID_ClientAuthz                         :: ExtensionID -- RFC5878+pattern EID_ClientAuthz                          = ExtensionID 0x7+pattern EID_ServerAuthz                         :: ExtensionID -- RFC5878+pattern EID_ServerAuthz                          = ExtensionID 0x8+pattern EID_CertType                            :: ExtensionID -- RFC6091+pattern EID_CertType                             = ExtensionID 0x9+pattern EID_SupportedGroups                     :: ExtensionID -- RFC8422,8446+pattern EID_SupportedGroups                      = ExtensionID 0xa+pattern EID_EcPointFormats                      :: ExtensionID -- RFC4492+pattern EID_EcPointFormats                       = ExtensionID 0xb+pattern EID_SRP                                 :: ExtensionID -- RFC5054+pattern EID_SRP                                  = ExtensionID 0xc+pattern EID_SignatureAlgorithms                 :: ExtensionID -- RFC5246,8446+pattern EID_SignatureAlgorithms                  = ExtensionID 0xd+pattern EID_SRTP                                :: ExtensionID -- RFC5764+pattern EID_SRTP                                 = ExtensionID 0xe+pattern EID_Heartbeat                           :: ExtensionID -- RFC6520+pattern EID_Heartbeat                            = ExtensionID 0xf+pattern EID_ApplicationLayerProtocolNegotiation :: ExtensionID -- RFC7301+pattern EID_ApplicationLayerProtocolNegotiation  = ExtensionID 0x10+pattern EID_StatusRequestv2                     :: ExtensionID -- RFC6961+pattern EID_StatusRequestv2                      = ExtensionID 0x11+pattern EID_SignedCertificateTimestamp          :: ExtensionID -- RFC6962+pattern EID_SignedCertificateTimestamp           = ExtensionID 0x12+pattern EID_ClientCertificateType               :: ExtensionID -- RFC7250+pattern EID_ClientCertificateType                = ExtensionID 0x13+pattern EID_ServerCertificateType               :: ExtensionID -- RFC7250+pattern EID_ServerCertificateType                = ExtensionID 0x14+pattern EID_Padding                             :: ExtensionID -- RFC5246+pattern EID_Padding                              = ExtensionID 0x15+pattern EID_EncryptThenMAC                      :: ExtensionID -- RFC7366+pattern EID_EncryptThenMAC                       = ExtensionID 0x16+pattern EID_ExtendedMainSecret                  :: ExtensionID -- REF7627+pattern EID_ExtendedMainSecret                   = ExtensionID 0x17+pattern EID_CompressCertificate                 :: ExtensionID -- RFC8879+pattern EID_CompressCertificate                  = ExtensionID 0x1b+pattern EID_RecordSizeLimit                     :: ExtensionID -- RFC8449+pattern EID_RecordSizeLimit                      = ExtensionID 0x1c+pattern EID_SessionTicket                       :: ExtensionID -- RFC4507+pattern EID_SessionTicket                        = ExtensionID 0x23+pattern EID_PreSharedKey                        :: ExtensionID -- RFC8446+pattern EID_PreSharedKey                         = ExtensionID 0x29+pattern EID_EarlyData                           :: ExtensionID -- RFC8446+pattern EID_EarlyData                            = ExtensionID 0x2a+pattern EID_SupportedVersions                   :: ExtensionID -- RFC8446+pattern EID_SupportedVersions                    = ExtensionID 0x2b+pattern EID_Cookie                              :: ExtensionID -- RFC8446+pattern EID_Cookie                               = ExtensionID 0x2c+pattern EID_PskKeyExchangeModes                 :: ExtensionID -- RFC8446+pattern EID_PskKeyExchangeModes                  = ExtensionID 0x2d+pattern EID_CertificateAuthorities              :: ExtensionID -- RFC8446+pattern EID_CertificateAuthorities               = ExtensionID 0x2f+pattern EID_OidFilters                          :: ExtensionID -- RFC8446+pattern EID_OidFilters                           = ExtensionID 0x30+pattern EID_PostHandshakeAuth                   :: ExtensionID -- RFC8446+pattern EID_PostHandshakeAuth                    = ExtensionID 0x31+pattern EID_SignatureAlgorithmsCert             :: ExtensionID -- RFC8446+pattern EID_SignatureAlgorithmsCert              = ExtensionID 0x32+pattern EID_KeyShare                            :: ExtensionID -- RFC8446+pattern EID_KeyShare                             = ExtensionID 0x33+pattern EID_QuicTransportParameters             :: ExtensionID -- RFC9001+pattern EID_QuicTransportParameters              = ExtensionID 0x39+pattern EID_EchOuterExtensions                  :: ExtensionID -- draft+pattern EID_EchOuterExtensions                   = ExtensionID 0xfd00+pattern EID_EncryptedClientHello                :: ExtensionID -- draft+pattern EID_EncryptedClientHello                 = ExtensionID 0xfe0d+pattern EID_SecureRenegotiation                 :: ExtensionID -- RFC5746+pattern EID_SecureRenegotiation                  = ExtensionID 0xff01++instance Show ExtensionID where+    show EID_ServerName              = "ServerName"+    show EID_MaxFragmentLength       = "MaxFragmentLength"+    show EID_ClientCertificateUrl    = "ClientCertificateUrl"+    show EID_TrustedCAKeys           = "TrustedCAKeys"+    show EID_TruncatedHMAC           = "TruncatedHMAC"+    show EID_StatusRequest           = "StatusRequest"+    show EID_UserMapping             = "UserMapping"+    show EID_ClientAuthz             = "ClientAuthz"+    show EID_ServerAuthz             = "ServerAuthz"+    show EID_CertType                = "CertType"+    show EID_SupportedGroups         = "SupportedGroups"+    show EID_EcPointFormats          = "EcPointFormats"+    show EID_SRP                     = "SRP"+    show EID_SignatureAlgorithms     = "SignatureAlgorithms"+    show EID_SRTP                    = "SRTP"+    show EID_Heartbeat               = "Heartbeat"+    show EID_ApplicationLayerProtocolNegotiation = "ApplicationLayerProtocolNegotiation"+    show EID_StatusRequestv2         = "StatusRequestv2"+    show EID_SignedCertificateTimestamp = "SignedCertificateTimestamp"+    show EID_ClientCertificateType   = "ClientCertificateType"+    show EID_ServerCertificateType   = "ServerCertificateType"+    show EID_Padding                 = "Padding"+    show EID_EncryptThenMAC          = "EncryptThenMAC"+    show EID_ExtendedMainSecret      = "ExtendedMainSecret"+    show EID_CompressCertificate     = "CompressCertificate"+    show EID_RecordSizeLimit         = "RecordSizeLimit"+    show EID_SessionTicket           = "SessionTicket"+    show EID_PreSharedKey            = "PreSharedKey"+    show EID_EarlyData               = "EarlyData"+    show EID_SupportedVersions       = "SupportedVersions"+    show EID_Cookie                  = "Cookie"+    show EID_PskKeyExchangeModes     = "PskKeyExchangeModes"+    show EID_CertificateAuthorities  = "CertificateAuthorities"+    show EID_OidFilters              = "OidFilters"+    show EID_PostHandshakeAuth       = "PostHandshakeAuth"+    show EID_SignatureAlgorithmsCert = "SignatureAlgorithmsCert"+    show EID_KeyShare                = "KeyShare"+    show EID_QuicTransportParameters = "QuicTransportParameters"+    show EID_EchOuterExtensions      = "EchOuterExtensions"+    show EID_EncryptedClientHello    = "EncryptedClientHello"+    show EID_SecureRenegotiation     = "SecureRenegotiation"+    show (ExtensionID x)             = "ExtensionID " ++ show x+{- FOURMOLU_ENABLE -}++------------------------------------------------------------++definedExtensions :: [ExtensionID]+definedExtensions =+    [ EID_ServerName+    , EID_MaxFragmentLength+    , EID_ClientCertificateUrl+    , EID_TrustedCAKeys+    , EID_TruncatedHMAC+    , EID_StatusRequest+    , EID_UserMapping+    , EID_ClientAuthz+    , EID_ServerAuthz+    , EID_CertType+    , EID_SupportedGroups+    , EID_EcPointFormats+    , EID_SRP+    , EID_SignatureAlgorithms+    , EID_SRTP+    , EID_Heartbeat+    , EID_ApplicationLayerProtocolNegotiation+    , EID_StatusRequestv2+    , EID_SignedCertificateTimestamp+    , EID_ClientCertificateType+    , EID_ServerCertificateType+    , EID_Padding+    , EID_EncryptThenMAC+    , EID_ExtendedMainSecret+    , EID_CompressCertificate+    , EID_RecordSizeLimit+    , EID_SessionTicket+    , EID_PreSharedKey+    , EID_EarlyData+    , EID_SupportedVersions+    , EID_Cookie+    , EID_PskKeyExchangeModes+    , EID_CertificateAuthorities+    , EID_OidFilters+    , EID_PostHandshakeAuth+    , EID_SignatureAlgorithmsCert+    , EID_KeyShare+    , EID_QuicTransportParameters+    , EID_EchOuterExtensions+    , EID_EncryptedClientHello+    , EID_SecureRenegotiation+    ]++-- | all supported extensions by the implementation+{- FOURMOLU_DISABLE -}+supportedExtensions :: [ExtensionID]+supportedExtensions =+    [ EID_ServerName                          -- 0x00+    , EID_SupportedGroups                     -- 0x0a+    , EID_EcPointFormats                      -- 0x0b+    , EID_SignatureAlgorithms                 -- 0x0d+    , EID_ApplicationLayerProtocolNegotiation -- 0x10+    , EID_ExtendedMainSecret                  -- 0x17+    , EID_CompressCertificate                 -- 0x1b+    , EID_RecordSizeLimit                     -- 0x1c+    , EID_SessionTicket                       -- 0x23+    , EID_PreSharedKey                        -- 0x29+    , EID_EarlyData                           -- 0x2a+    , EID_SupportedVersions                   -- 0x2b+    , EID_Cookie                              -- 0x2c+    , EID_PskKeyExchangeModes                 -- 0x2d+    , EID_CertificateAuthorities              -- 0x2f+    , EID_PostHandshakeAuth                   -- 0x31+    , EID_SignatureAlgorithmsCert             -- 0x32+    , EID_KeyShare                            -- 0x33+    , EID_QuicTransportParameters             -- 0x39+    , EID_EchOuterExtensions                  -- 0xfd00+    , EID_EncryptedClientHello                -- 0xfe0d+    , EID_SecureRenegotiation                 -- 0xff01+    ]+{- FOURMOLU_ENABLE -}++----------------------------------------------------------------++-- | The raw content of a TLS extension.+data ExtensionRaw = ExtensionRaw ExtensionID ByteString+    deriving (Eq)++instance Show ExtensionRaw where+    show (ExtensionRaw eid@EID_ServerName bs) = showExtensionRaw eid bs decodeServerName+    show (ExtensionRaw eid@EID_MaxFragmentLength bs) = showExtensionRaw eid bs decodeMaxFragmentLength+    show (ExtensionRaw eid@EID_SupportedGroups bs) = showExtensionRaw eid bs decodeSupportedGroups+    show (ExtensionRaw eid@EID_EcPointFormats bs) = showExtensionRaw eid bs decodeEcPointFormatsSupported+    show (ExtensionRaw eid@EID_SignatureAlgorithms bs) = showExtensionRaw eid bs decodeSignatureAlgorithms+    show (ExtensionRaw eid@EID_Heartbeat bs) = showExtensionRaw eid bs decodeHeartBeat+    show (ExtensionRaw eid@EID_ApplicationLayerProtocolNegotiation bs) = showExtensionRaw eid bs decodeApplicationLayerProtocolNegotiation+    show (ExtensionRaw eid@EID_ExtendedMainSecret _) = show eid+    show (ExtensionRaw eid@EID_CompressCertificate bs) = showExtensionRaw eid bs decodeCompressCertificate+    show (ExtensionRaw eid@EID_RecordSizeLimit bs) = showExtensionRaw eid bs decodeRecordSizeLimit+    show (ExtensionRaw eid@EID_SessionTicket bs) = showExtensionRaw eid bs decodeSessionTicket+    show (ExtensionRaw eid@EID_PreSharedKey bs) = showExtensionRaw eid bs decodePreSharedKey+    show (ExtensionRaw eid@EID_EarlyData _) = show eid+    show (ExtensionRaw eid@EID_SupportedVersions bs) = showExtensionRaw eid bs decodeSupportedVersions+    show (ExtensionRaw eid@EID_Cookie bs) = show eid ++ " " ++ showBytesHex bs+    show (ExtensionRaw eid@EID_PskKeyExchangeModes bs) = showExtensionRaw eid bs decodePskKeyExchangeModes+    show (ExtensionRaw eid@EID_CertificateAuthorities bs) = showExtensionRaw eid bs decodeCertificateAuthorities+    show (ExtensionRaw eid@EID_PostHandshakeAuth _) = show eid+    show (ExtensionRaw eid@EID_SignatureAlgorithmsCert bs) = showExtensionRaw eid bs decodeSignatureAlgorithmsCert+    show (ExtensionRaw eid@EID_KeyShare bs) = showExtensionRaw eid bs decodeKeyShare+    show (ExtensionRaw eid@EID_EchOuterExtensions bs) = showExtensionRaw eid bs decodeEchOuterExtensions+    show (ExtensionRaw eid@EID_EncryptedClientHello bs) = showExtensionRaw eid bs decodeECH+    show (ExtensionRaw eid@EID_SecureRenegotiation bs) = show eid ++ " " ++ showBytesHex bs+    show (ExtensionRaw eid bs) = "ExtensionRaw " ++ show eid ++ " " ++ showBytesHex bs++showExtensionRaw+    :: Show a => ExtensionID -> ByteString -> (ByteString -> Maybe a) -> String+showExtensionRaw eid bs decode = case decode bs of+    Nothing -> show eid ++ " broken"+    Just x -> show x++toExtensionRaw :: Extension e => e -> ExtensionRaw+toExtensionRaw ext = ExtensionRaw (extensionID ext) (extensionEncode ext)++extensionLookup :: ExtensionID -> [ExtensionRaw] -> Maybe ByteString+extensionLookup toFind exts = extract <$> find idEq exts+  where+    extract (ExtensionRaw _ content) = content+    idEq (ExtensionRaw eid _) = eid == toFind++lookupAndDecode+    :: Extension e+    => ExtensionID+    -> MessageType+    -> [ExtensionRaw]+    -> a+    -> (e -> a)+    -> a+lookupAndDecode eid msgtyp exts defval conv = case extensionLookup eid exts of+    Nothing -> defval+    Just bs -> case extensionDecode msgtyp bs of+        Nothing ->+            E.throw $+                Uncontextualized $+                    Error_Protocol ("Illegal " ++ show eid) DecodeError+        Just val -> conv val++lookupAndDecodeAndDo+    :: Extension a+    => ExtensionID+    -> MessageType+    -> [ExtensionRaw]+    -> IO b+    -> (a -> IO b)+    -> IO b+lookupAndDecodeAndDo eid msgtyp exts defAction action = case extensionLookup eid exts of+    Nothing -> defAction+    Just bs -> case extensionDecode msgtyp bs of+        Nothing ->+            E.throwIO $+                Uncontextualized $+                    Error_Protocol ("Illegal " ++ show eid) DecodeError+        Just val -> action val++------------------------------------------------------------++-- | Extension class to transform bytes to and from a high level Extension type.+class Extension a where+    extensionID :: a -> ExtensionID+    extensionDecode :: MessageType -> ByteString -> Maybe a+    extensionEncode :: a -> ByteString++data MessageType+    = MsgTClientHello+    | MsgTServerHello+    | MsgTHelloRetryRequest+    | MsgTEncryptedExtensions+    | MsgTNewSessionTicket+    | MsgTCertificateRequest+    deriving (Eq, Show)++------------------------------------------------------------++-- | Server Name extension including the name type and the associated name.+-- the associated name decoding is dependent of its name type.+-- name type = 0 : hostname+newtype ServerName = ServerName [ServerNameType] deriving (Show, Eq)++data ServerNameType+    = ServerNameHostName HostName+    | ServerNameOther (Word8, ByteString)+    deriving (Eq)++instance Show ServerNameType where+    show (ServerNameHostName host) = "\"" ++ host ++ "\""+    show (ServerNameOther (w, _)) = "(" ++ show w ++ ", )"++instance Extension ServerName where+    extensionID _ = EID_ServerName++    -- dirty hack for servers+    extensionEncode (ServerName []) = ""+    -- for clients+    extensionEncode (ServerName l) = runPut $ putOpaque16 (runPut $ mapM_ encodeNameType l)+      where+        encodeNameType (ServerNameHostName hn) = putWord8 0 >> putOpaque16 (BC.pack hn) -- FIXME: should be puny code conversion+        encodeNameType (ServerNameOther (nt, opaque)) = putWord8 nt >> putBytes opaque+    extensionDecode MsgTClientHello = decodeServerName+    extensionDecode MsgTServerHello = decodeServerName+    extensionDecode MsgTEncryptedExtensions = decodeServerName+    extensionDecode _ = error "extensionDecode: ServerName"++decodeServerName :: ByteString -> Maybe ServerName+decodeServerName "" = Just $ ServerName [] -- dirty hack for servers+decodeServerName bs = runGetMaybe decode bs+  where+    decode = do+        len <- fromIntegral <$> getWord16+        ServerName <$> getList len getServerName+    getServerName = do+        ty <- getWord8+        snameParsed <- getOpaque16+        let sname = B.copy snameParsed+            name = case ty of+                0 -> ServerNameHostName $ BC.unpack sname -- FIXME: should be puny code conversion+                _ -> ServerNameOther (ty, sname)+        return (1 + 2 + B.length sname, name)++------------------------------------------------------------++-- | Max fragment extension with length from 512 bytes to 4096 bytes+--+-- RFC 6066 defines:+-- If a server receives a maximum fragment length negotiation request+-- for a value other than the allowed values, it MUST abort the+-- handshake with an "illegal_parameter" alert.+--+-- So, if a server receives MaxFragmentLengthOther, it must send the alert.+data MaxFragmentLength+    = MaxFragmentLength MaxFragmentEnum+    | MaxFragmentLengthOther Word8+    deriving (Show, Eq)++data MaxFragmentEnum+    = MaxFragment512+    | MaxFragment1024+    | MaxFragment2048+    | MaxFragment4096+    deriving (Show, Eq)++instance Extension MaxFragmentLength where+    extensionID _ = EID_MaxFragmentLength+    extensionEncode (MaxFragmentLength l) = runPut $ putWord8 $ fromMaxFragmentEnum l+      where+        fromMaxFragmentEnum MaxFragment512 = 1+        fromMaxFragmentEnum MaxFragment1024 = 2+        fromMaxFragmentEnum MaxFragment2048 = 3+        fromMaxFragmentEnum MaxFragment4096 = 4+    extensionEncode (MaxFragmentLengthOther l) = runPut $ putWord8 l+    extensionDecode MsgTClientHello = decodeMaxFragmentLength+    extensionDecode MsgTServerHello = decodeMaxFragmentLength+    extensionDecode MsgTEncryptedExtensions = decodeMaxFragmentLength+    extensionDecode _ = error "extensionDecode: MaxFragmentLength"++decodeMaxFragmentLength :: ByteString -> Maybe MaxFragmentLength+decodeMaxFragmentLength = runGetMaybe $ toMaxFragmentEnum <$> getWord8+  where+    toMaxFragmentEnum 1 = MaxFragmentLength MaxFragment512+    toMaxFragmentEnum 2 = MaxFragmentLength MaxFragment1024+    toMaxFragmentEnum 3 = MaxFragmentLength MaxFragment2048+    toMaxFragmentEnum 4 = MaxFragmentLength MaxFragment4096+    toMaxFragmentEnum n = MaxFragmentLengthOther n++------------------------------------------------------------++newtype SupportedGroups = SupportedGroups [Group] deriving (Show, Eq)++-- on decode, filter all unknown curves+instance Extension SupportedGroups where+    extensionID _ = EID_SupportedGroups+    extensionEncode (SupportedGroups groups) = runPut $ putWords16 $ map (\(Group g) -> g) groups+    extensionDecode MsgTClientHello = decodeSupportedGroups+    extensionDecode MsgTEncryptedExtensions = decodeSupportedGroups+    extensionDecode _ = error "extensionDecode: SupportedGroups"++decodeSupportedGroups :: ByteString -> Maybe SupportedGroups+decodeSupportedGroups =+    runGetMaybe (SupportedGroups . map Group <$> getWords16)++------------------------------------------------------------++newtype EcPointFormatsSupported = EcPointFormatsSupported [EcPointFormat]+    deriving (Show, Eq)++newtype EcPointFormat = EcPointFormat {fromEcPointFormat :: Word8}+    deriving (Eq)++{- FOURMOLU_DISABLE -}+pattern EcPointFormat_Uncompressed              :: EcPointFormat+pattern EcPointFormat_Uncompressed               = EcPointFormat 0+pattern EcPointFormat_AnsiX962_compressed_prime :: EcPointFormat+pattern EcPointFormat_AnsiX962_compressed_prime  = EcPointFormat 1+pattern EcPointFormat_AnsiX962_compressed_char2 :: EcPointFormat+pattern EcPointFormat_AnsiX962_compressed_char2  = EcPointFormat 2++instance Show EcPointFormat where+    show EcPointFormat_Uncompressed = "EcPointFormat_Uncompressed"+    show EcPointFormat_AnsiX962_compressed_prime = "EcPointFormat_AnsiX962_compressed_prime"+    show EcPointFormat_AnsiX962_compressed_char2 = "EcPointFormat_AnsiX962_compressed_char2"+    show (EcPointFormat x) = "EcPointFormat " ++ show x+{- FOURMOLU_ENABLE -}++-- on decode, filter all unknown formats+instance Extension EcPointFormatsSupported where+    extensionID _ = EID_EcPointFormats+    extensionEncode (EcPointFormatsSupported formats) = runPut $ putWords8 $ map fromEcPointFormat formats+    extensionDecode MsgTClientHello = decodeEcPointFormatsSupported+    extensionDecode MsgTServerHello = decodeEcPointFormatsSupported+    extensionDecode _ = error "extensionDecode: EcPointFormatsSupported"++decodeEcPointFormatsSupported :: ByteString -> Maybe EcPointFormatsSupported+decodeEcPointFormatsSupported =+    runGetMaybe (EcPointFormatsSupported . map EcPointFormat <$> getWords8)++------------------------------------------------------------++newtype SignatureAlgorithms = SignatureAlgorithms [HashAndSignatureAlgorithm]+    deriving (Show, Eq)++instance Extension SignatureAlgorithms where+    extensionID _ = EID_SignatureAlgorithms+    extensionEncode (SignatureAlgorithms algs) =+        runPut $+            putWord16 (fromIntegral (length algs * 2))+                >> mapM_ putSignatureHashAlgorithm algs+    extensionDecode MsgTClientHello = decodeSignatureAlgorithms+    extensionDecode MsgTCertificateRequest = decodeSignatureAlgorithms+    extensionDecode _ = error "extensionDecode: SignatureAlgorithms"++decodeSignatureAlgorithms :: ByteString -> Maybe SignatureAlgorithms+decodeSignatureAlgorithms = runGetMaybe $ do+    len <- getWord16+    sas <-+        getList (fromIntegral len) (getSignatureHashAlgorithm >>= \sh -> return (2, sh))+    leftoverLen <- remaining+    when (leftoverLen /= 0) $ fail "decodeSignatureAlgorithms: broken length"+    when (null sas) $ fail "signature algorithms are empty"+    return $ SignatureAlgorithms sas++------------------------------------------------------------++newtype HeartBeat = HeartBeat HeartBeatMode deriving (Show, Eq)++newtype HeartBeatMode = HeartBeatMode {fromHeartBeatMode :: Word8}+    deriving (Eq)++{- FOURMOLU_DISABLE -}+pattern HeartBeat_PeerAllowedToSend    :: HeartBeatMode+pattern HeartBeat_PeerAllowedToSend     = HeartBeatMode 1+pattern HeartBeat_PeerNotAllowedToSend :: HeartBeatMode+pattern HeartBeat_PeerNotAllowedToSend  = HeartBeatMode 2++instance Show HeartBeatMode where+    show HeartBeat_PeerAllowedToSend    = "HeartBeat_PeerAllowedToSend"+    show HeartBeat_PeerNotAllowedToSend = "HeartBeat_PeerNotAllowedToSend"+    show (HeartBeatMode x)              = "HeartBeatMode " ++ show x+{- FOURMOLU_ENABLE -}++instance Extension HeartBeat where+    extensionID _ = EID_Heartbeat+    extensionEncode (HeartBeat mode) = runPut $ putWord8 $ fromHeartBeatMode mode+    extensionDecode MsgTClientHello = decodeHeartBeat+    extensionDecode MsgTServerHello = decodeHeartBeat+    extensionDecode _ = error "extensionDecode: HeartBeat"++decodeHeartBeat :: ByteString -> Maybe HeartBeat+decodeHeartBeat = runGetMaybe $ HeartBeat . HeartBeatMode <$> getWord8++------------------------------------------------------------++-- | Application Layer Protocol Negotiation (ALPN)+newtype ApplicationLayerProtocolNegotiation+    = ApplicationLayerProtocolNegotiation [ByteString]+    deriving (Show, Eq)++instance Extension ApplicationLayerProtocolNegotiation where+    extensionID _ = EID_ApplicationLayerProtocolNegotiation+    extensionEncode (ApplicationLayerProtocolNegotiation bytes) =+        runPut $ putOpaque16 $ runPut $ mapM_ putOpaque8 bytes+    extensionDecode MsgTClientHello = decodeApplicationLayerProtocolNegotiation+    extensionDecode MsgTServerHello = decodeApplicationLayerProtocolNegotiation+    extensionDecode MsgTEncryptedExtensions = decodeApplicationLayerProtocolNegotiation+    extensionDecode _ = error "extensionDecode: ApplicationLayerProtocolNegotiation"++decodeApplicationLayerProtocolNegotiation+    :: ByteString -> Maybe ApplicationLayerProtocolNegotiation+decodeApplicationLayerProtocolNegotiation = runGetMaybe $ do+    len <- getWord16+    ApplicationLayerProtocolNegotiation <$> getList (fromIntegral len) getALPN+  where+    getALPN = do+        alpnParsed <- getOpaque8+        let alpn = B.copy alpnParsed+        return (B.length alpn + 1, alpn)++------------------------------------------------------------++-- | Extended Main Secret+data ExtendedMainSecret = ExtendedMainSecret deriving (Show, Eq)++instance Extension ExtendedMainSecret where+    extensionID _ = EID_ExtendedMainSecret+    extensionEncode ExtendedMainSecret = B.empty+    extensionDecode MsgTClientHello "" = Just ExtendedMainSecret+    extensionDecode MsgTServerHello "" = Just ExtendedMainSecret+    extensionDecode _ _ = error "extensionDecode: ExtendedMainSecret"++------------------------------------------------------------++newtype CertificateCompressionAlgorithm+    = CertificateCompressionAlgorithm Word16+    deriving (Eq)++{- FOURMOLU_DISABLE -}+pattern CCA_Zlib   :: CertificateCompressionAlgorithm+pattern CCA_Zlib    = CertificateCompressionAlgorithm 1+pattern CCA_Brotli :: CertificateCompressionAlgorithm+pattern CCA_Brotli  = CertificateCompressionAlgorithm 2+pattern CCA_Zstd   :: CertificateCompressionAlgorithm+pattern CCA_Zstd    = CertificateCompressionAlgorithm 3++instance Show CertificateCompressionAlgorithm where+    show CCA_Zlib   = "zlib"+    show CCA_Brotli = "brotli"+    show CCA_Zstd   = "zstd"+    show (CertificateCompressionAlgorithm n) = "CertificateCompressionAlgorithm " ++ show n+{- FOURMOLU_ENABLE -}++newtype CompressCertificate = CompressCertificate [CertificateCompressionAlgorithm]+    deriving (Show, Eq)++instance Extension CompressCertificate where+    extensionID _ = EID_CompressCertificate+    extensionEncode (CompressCertificate cs) = runPut $ do+        putWord8 $ fromIntegral (length cs * 2)+        mapM_ putCCA cs+      where+        putCCA (CertificateCompressionAlgorithm n) = putWord16 n+    extensionDecode _ = decodeCompressCertificate++decodeCompressCertificate :: ByteString -> Maybe CompressCertificate+decodeCompressCertificate = runGetMaybe $ do+    len <- fromIntegral <$> getWord8+    cs <- getList len getCCA+    when (null cs) $ fail "empty list of CertificateCompressionAlgorithm"+    leftoverLen <- remaining+    when (leftoverLen /= 0) $ fail "decodeCompressCertificate: broken length"+    return $ CompressCertificate cs+  where+    getCCA = do+        cca <- CertificateCompressionAlgorithm <$> getWord16+        return (2, cca)++------------------------------------------------------------++newtype RecordSizeLimit = RecordSizeLimit Word16 deriving (Eq, Show)++instance Extension RecordSizeLimit where+    extensionID _ = EID_RecordSizeLimit+    extensionEncode (RecordSizeLimit n) = runPut $ putWord16 n+    extensionDecode _ = decodeRecordSizeLimit++decodeRecordSizeLimit :: ByteString -> Maybe RecordSizeLimit+decodeRecordSizeLimit = runGetMaybe $ do+    r <- RecordSizeLimit <$> getWord16+    leftoverLen <- remaining+    when (leftoverLen /= 0) $ fail "decodeRecordSizeLimit: broken length"+    return r++------------------------------------------------------------++newtype SessionTicket = SessionTicket Ticket+    deriving (Show, Eq)++-- https://datatracker.ietf.org/doc/html/rfc5077#appendix-A+instance Extension SessionTicket where+    extensionID _ = EID_SessionTicket+    extensionEncode (SessionTicket ticket) = runPut $ putBytes ticket+    extensionDecode MsgTClientHello = decodeSessionTicket+    extensionDecode MsgTServerHello = decodeSessionTicket+    extensionDecode _ = error "extensionDecode: SessionTicket"++decodeSessionTicket :: ByteString -> Maybe SessionTicket+decodeSessionTicket = runGetMaybe $ SessionTicket <$> (remaining >>= getBytes)++------------------------------------------------------------++data PskIdentity = PskIdentity ByteString Word32 deriving (Eq)++instance Show PskIdentity where+    show (PskIdentity bs n) = "PskId " ++ showBytesHex bs ++ " " ++ show n++data PreSharedKey+    = PreSharedKeyClientHello [PskIdentity] [ByteString]+    | PreSharedKeyServerHello Int+    deriving (Eq)++instance Show PreSharedKey where+    show (PreSharedKeyClientHello ids bndrs) =+        "PreSharedKey "+            ++ show ids+            ++ " "+            ++ "["+            ++ intercalate ", " (map showBytesHex bndrs)+            ++ "]"+    show (PreSharedKeyServerHello n) = "PreSharedKey " ++ show n++instance Extension PreSharedKey where+    extensionID _ = EID_PreSharedKey+    extensionEncode (PreSharedKeyClientHello ids bds) = runPut $ do+        putOpaque16 $ runPut (mapM_ putIdentity ids)+        putOpaque16 $ runPut (mapM_ putBinder bds)+      where+        putIdentity (PskIdentity bs w) = do+            putOpaque16 bs+            putWord32 w+        putBinder = putOpaque8+    extensionEncode (PreSharedKeyServerHello w16) =+        runPut $+            putWord16 $+                fromIntegral w16+    extensionDecode MsgTClientHello = decodePreSharedKeyClientHello+    extensionDecode MsgTServerHello = decodePreSharedKeyServerHello+    extensionDecode _ = error "extensionDecode: PreShareKey"++decodePreSharedKeyClientHello :: ByteString -> Maybe PreSharedKey+decodePreSharedKeyClientHello = runGetMaybe $ do+    len1 <- fromIntegral <$> getWord16+    identities <- getList len1 getIdentity+    len2 <- fromIntegral <$> getWord16+    binders <- getList len2 getBinder+    return $ PreSharedKeyClientHello identities binders+  where+    getIdentity = do+        identity <- getOpaque16+        age <- getWord32+        let len = 2 + B.length identity + 4+        return (len, PskIdentity identity age)+    getBinder = do+        l <- fromIntegral <$> getWord8+        binder <- getBytes l+        let len = l + 1+        return (len, binder)++decodePreSharedKeyServerHello :: ByteString -> Maybe PreSharedKey+decodePreSharedKeyServerHello =+    runGetMaybe $+        PreSharedKeyServerHello . fromIntegral <$> getWord16++decodePreSharedKey :: ByteString -> Maybe PreSharedKey+decodePreSharedKey bs =+    decodePreSharedKeyClientHello bs+        <|> decodePreSharedKeyServerHello bs++------------------------------------------------------------++newtype EarlyDataIndication = EarlyDataIndication (Maybe Word32)+    deriving (Eq, Show)++instance Extension EarlyDataIndication where+    extensionID _ = EID_EarlyData+    extensionEncode (EarlyDataIndication Nothing) = runPut $ putBytes B.empty+    extensionEncode (EarlyDataIndication (Just w32)) = runPut $ putWord32 w32+    extensionDecode MsgTClientHello = return $ Just (EarlyDataIndication Nothing)+    extensionDecode MsgTEncryptedExtensions = return $ Just (EarlyDataIndication Nothing)+    extensionDecode MsgTNewSessionTicket =+        runGetMaybe $+            EarlyDataIndication . Just <$> getWord32+    extensionDecode _ = error "extensionDecode: EarlyDataIndication"++------------------------------------------------------------++data SupportedVersions+    = SupportedVersionsClientHello [Version]+    | SupportedVersionsServerHello Version+    deriving (Eq)++instance Show SupportedVersions where+    show (SupportedVersionsClientHello vers) = "Versions " ++ show vers+    show (SupportedVersionsServerHello ver) = "Versions " ++ show ver++instance Extension SupportedVersions where+    extensionID _ = EID_SupportedVersions+    extensionEncode (SupportedVersionsClientHello vers) = runPut $ do+        putWord8 (fromIntegral (length vers * 2))+        mapM_ putBinaryVersion vers+    extensionEncode (SupportedVersionsServerHello ver) =+        runPut $+            putBinaryVersion ver+    extensionDecode MsgTClientHello = decodeSupportedVersionsClientHello+    extensionDecode MsgTServerHello = decodeSupportedVersionsServerHello+    extensionDecode _ = error "extensionDecode: SupportedVersionsServerHello"++decodeSupportedVersionsClientHello :: ByteString -> Maybe SupportedVersions+decodeSupportedVersionsClientHello = runGetMaybe $ do+    len <- fromIntegral <$> getWord8+    SupportedVersionsClientHello <$> getList len getVer+  where+    getVer = do+        ver <- getBinaryVersion+        return (2, ver)++decodeSupportedVersionsServerHello :: ByteString -> Maybe SupportedVersions+decodeSupportedVersionsServerHello =+    runGetMaybe (SupportedVersionsServerHello <$> getBinaryVersion)++decodeSupportedVersions :: ByteString -> Maybe SupportedVersions+decodeSupportedVersions bs =+    decodeSupportedVersionsClientHello bs+        <|> decodeSupportedVersionsServerHello bs++------------------------------------------------------------++newtype Cookie = Cookie ByteString deriving (Eq, Show)++instance Extension Cookie where+    extensionID _ = EID_Cookie+    extensionEncode (Cookie opaque) = runPut $ putOpaque16 opaque+    extensionDecode MsgTServerHello = runGetMaybe (Cookie <$> getOpaque16)+    extensionDecode _ = error "extensionDecode: Cookie"++------------------------------------------------------------++newtype PskKexMode = PskKexMode {fromPskKexMode :: Word8} deriving (Eq)++{- FOURMOLU_DISABLE -}+pattern PSK_KE     :: PskKexMode+pattern PSK_KE      = PskKexMode 0+pattern PSK_DHE_KE :: PskKexMode+pattern PSK_DHE_KE  = PskKexMode 1++instance Show PskKexMode where+    show PSK_KE     = "PSK_KE"+    show PSK_DHE_KE = "PSK_DHE_KE"+    show (PskKexMode x) = "PskKexMode " ++ show x+{- FOURMOLU_ENABLE -}++newtype PskKeyExchangeModes = PskKeyExchangeModes [PskKexMode]+    deriving (Eq, Show)++instance Extension PskKeyExchangeModes where+    extensionID _ = EID_PskKeyExchangeModes+    extensionEncode (PskKeyExchangeModes pkms) =+        runPut $+            putWords8 $+                map fromPskKexMode pkms+    extensionDecode MsgTClientHello = decodePskKeyExchangeModes+    extensionDecode _ = error "extensionDecode: PskKeyExchangeModes"++decodePskKeyExchangeModes :: ByteString -> Maybe PskKeyExchangeModes+decodePskKeyExchangeModes =+    runGetMaybe $+        PskKeyExchangeModes . map PskKexMode <$> getWords8++------------------------------------------------------------++newtype CertificateAuthorities = CertificateAuthorities [DistinguishedName]+    deriving (Eq, Show)++instance Extension CertificateAuthorities where+    extensionID _ = EID_CertificateAuthorities+    extensionEncode (CertificateAuthorities names) =+        runPut $+            putDNames names+    extensionDecode MsgTClientHello = decodeCertificateAuthorities+    extensionDecode MsgTCertificateRequest = decodeCertificateAuthorities+    extensionDecode _ = error "extensionDecode: CertificateAuthorities"++decodeCertificateAuthorities :: ByteString -> Maybe CertificateAuthorities+decodeCertificateAuthorities =+    runGetMaybe (CertificateAuthorities <$> getDNames)++------------------------------------------------------------++data PostHandshakeAuth = PostHandshakeAuth deriving (Show, Eq)++instance Extension PostHandshakeAuth where+    extensionID _ = EID_PostHandshakeAuth+    extensionEncode _ = B.empty+    extensionDecode MsgTClientHello = runGetMaybe $ return PostHandshakeAuth+    extensionDecode _ = error "extensionDecode: PostHandshakeAuth"++------------------------------------------------------------++newtype SignatureAlgorithmsCert = SignatureAlgorithmsCert [HashAndSignatureAlgorithm]+    deriving (Show, Eq)++instance Extension SignatureAlgorithmsCert where+    extensionID _ = EID_SignatureAlgorithmsCert+    extensionEncode (SignatureAlgorithmsCert algs) =+        runPut $+            putWord16 (fromIntegral (length algs * 2))+                >> mapM_ putSignatureHashAlgorithm algs+    extensionDecode MsgTClientHello = decodeSignatureAlgorithmsCert+    extensionDecode MsgTCertificateRequest = decodeSignatureAlgorithmsCert+    extensionDecode _ = error "extensionDecode: SignatureAlgorithmsCert"++decodeSignatureAlgorithmsCert :: ByteString -> Maybe SignatureAlgorithmsCert+decodeSignatureAlgorithmsCert = runGetMaybe $ do+    len <- getWord16+    SignatureAlgorithmsCert+        <$> getList (fromIntegral len) (getSignatureHashAlgorithm >>= \sh -> return (2, sh))++------------------------------------------------------------++data KeyShareEntry = KeyShareEntry+    { keyShareEntryGroup :: Group+    , keyShareEntryKeyExchange :: ByteString+    }+    deriving (Eq)++instance Show KeyShareEntry where+    show kse = show $ keyShareEntryGroup kse++getKeyShareEntry :: Get (Int, Maybe KeyShareEntry)+getKeyShareEntry = do+    grp <- Group <$> getWord16+    l <- fromIntegral <$> getWord16+    key <- getBytes l+    let len = l + 4+    return (len, Just $ KeyShareEntry grp key)++putKeyShareEntry :: KeyShareEntry -> Put+putKeyShareEntry (KeyShareEntry (Group grp) key) = do+    putWord16 grp+    putWord16 $ fromIntegral $ B.length key+    putBytes key++data KeyShare+    = KeyShareClientHello [KeyShareEntry]+    | KeyShareServerHello KeyShareEntry+    | KeyShareHRR Group+    deriving (Eq)++{- FOURMOLU_DISABLE -}+instance Show KeyShare where+    show (KeyShareClientHello kses) = "KeyShare " ++ show kses+    show (KeyShareServerHello kse)  = "KeyShare " ++ show kse+    show (KeyShareHRR g)            = "KeyShareHRR " ++ show g+{- FOURMOLU_ENABLE -}++instance Extension KeyShare where+    extensionID _ = EID_KeyShare+    extensionEncode (KeyShareClientHello kses) = runPut $ do+        let len = sum [B.length key + 4 | KeyShareEntry _ key <- kses]+        putWord16 $ fromIntegral len+        mapM_ putKeyShareEntry kses+    extensionEncode (KeyShareServerHello kse) = runPut $ putKeyShareEntry kse+    extensionEncode (KeyShareHRR (Group grp)) = runPut $ putWord16 grp+    extensionDecode MsgTClientHello = decodeKeyShareClientHello+    extensionDecode MsgTServerHello = decodeKeyShareServerHello+    extensionDecode MsgTHelloRetryRequest = decodeKeyShareHRR+    extensionDecode _ = error "extensionDecode: KeyShare"++decodeKeyShareClientHello :: ByteString -> Maybe KeyShare+decodeKeyShareClientHello = runGetMaybe $ do+    len <- fromIntegral <$> getWord16+    --      len == 0 allows for HRR+    grps <- getList len getKeyShareEntry+    return $ KeyShareClientHello $ catMaybes grps++decodeKeyShareServerHello :: ByteString -> Maybe KeyShare+decodeKeyShareServerHello = runGetMaybe $ do+    (_, ment) <- getKeyShareEntry+    case ment of+        Nothing -> fail "decoding KeyShare for ServerHello"+        Just ent -> return $ KeyShareServerHello ent++decodeKeyShareHRR :: ByteString -> Maybe KeyShare+decodeKeyShareHRR =+    runGetMaybe $+        KeyShareHRR . Group <$> getWord16++decodeKeyShare :: ByteString -> Maybe KeyShare+decodeKeyShare bs =+    decodeKeyShareClientHello bs+        <|> decodeKeyShareServerHello bs+        <|> decodeKeyShareHRR bs++------------------------------------------------------------++newtype EchOuterExtensions = EchOuterExtensions [ExtensionID]+    deriving (Eq, Show)++instance Extension EchOuterExtensions where+    extensionID _ = EID_EchOuterExtensions+    extensionEncode (EchOuterExtensions ids) = runPut $ do+        putWord8 $ fromIntegral (length ids * 2)+        mapM_ (putWord16 . fromExtensionID) ids+    extensionDecode MsgTClientHello = decodeEchOuterExtensions+    extensionDecode _ = error "extensionDecode: EchOuterExtensions"++decodeEchOuterExtensions :: ByteString -> Maybe EchOuterExtensions+decodeEchOuterExtensions = runGetMaybe $ do+    len <- fromIntegral <$> getWord8+    eids <- getList len $ do+        eid <- ExtensionID <$> getWord16+        return (2, eid)+    return $ EchOuterExtensions eids++------------------------------------------------------------++-- | Encrypted Client Hello+data EncryptedClientHello+    = ECHClientHelloInner+    | ECHClientHelloOuter+        { echCipherSuite :: (KDF_ID, AEAD_ID)+        , echConfigId :: ConfigId+        , echEnc :: EncodedPublicKey+        , echPayload :: ByteString+        }+    | ECHEncryptedExtensions ECHConfigList+    | ECHHelloRetryRequest ByteString+    deriving (Eq)++instance Show EncryptedClientHello where+    show ECHClientHelloInner = "ECHClientHelloInner"+    show ECHClientHelloOuter{..} =+        "ECHClientHelloOuter {"+            ++ show (fst echCipherSuite)+            ++ " "+            ++ show (snd echCipherSuite)+            ++ " "+            ++ show echConfigId+            ++ " "+            ++ showBytesHex enc+            ++ " "+            ++ showBytesHex echPayload+            ++ "}"+      where+        EncodedPublicKey enc = echEnc+    show (ECHEncryptedExtensions cnflst) = "ECHEncryptedExtensions " ++ show cnflst+    show (ECHHelloRetryRequest cnfm) = "ECHHelloRetryRequest " ++ showBytesHex cnfm++instance Extension EncryptedClientHello where+    extensionID _ = EID_EncryptedClientHello+    extensionEncode ECHClientHelloInner = runPut $ putWord8 1+    extensionEncode ECHClientHelloOuter{..} = runPut $ do+        putWord8 0+        let (kdfid, aeadid) = echCipherSuite+        putWord16 $ fromKDF_ID kdfid+        putWord16 $ fromAEAD_ID aeadid+        putWord8 echConfigId+        let EncodedPublicKey enc = echEnc+        putOpaque16 enc+        putOpaque16 echPayload+    extensionEncode (ECHEncryptedExtensions cnflist) = encodeECHConfigList cnflist+    extensionEncode (ECHHelloRetryRequest cnfm) = runPut $ putBytes cnfm+    extensionDecode MsgTClientHello = decodeECHClientHello+    extensionDecode MsgTEncryptedExtensions = decodeECHEncryptedExtensions+    extensionDecode MsgTHelloRetryRequest = decodeECHHelloRetryRequest+    extensionDecode _ = error "extensionDecode: EncryptedClientHello"++decodeECH :: ByteString -> Maybe EncryptedClientHello+decodeECH bs =+    decodeECHClientHello bs+        <|> decodeECHEncryptedExtensions bs+        <|> decodeECHHelloRetryRequest bs++decodeECHClientHello :: ByteString -> Maybe EncryptedClientHello+decodeECHClientHello = runGetMaybe $ do+    typ <- getWord8+    if typ == 1+        then return ECHClientHelloInner+        else do+            kdfid <- KDF_ID <$> getWord16+            aeadid <- AEAD_ID <$> getWord16+            cnfid <- getWord8+            enc <- EncodedPublicKey <$> getOpaque16+            payload <- getOpaque16+            return $+                ECHClientHelloOuter+                    { echCipherSuite = (kdfid, aeadid)+                    , echConfigId = cnfid+                    , echEnc = enc+                    , echPayload = payload+                    }++decodeECHEncryptedExtensions :: ByteString -> Maybe EncryptedClientHello+decodeECHEncryptedExtensions bs =+    ECHEncryptedExtensions <$> decodeECHConfigList bs++decodeECHHelloRetryRequest :: ByteString -> Maybe EncryptedClientHello+decodeECHHelloRetryRequest = runGetMaybe $ do+    ECHHelloRetryRequest <$> getBytes 8++------------------------------------------------------------++-- | Secure Renegotiation+data SecureRenegotiation = SecureRenegotiation ByteString ByteString+    deriving (Show, Eq)++instance Extension SecureRenegotiation where+    extensionID _ = EID_SecureRenegotiation+    extensionEncode (SecureRenegotiation cvd svd) =+        runPut $ putOpaque8 (cvd `B.append` svd)+    extensionDecode MsgTClientHello = runGetMaybe $ do+        opaque <- getOpaque8+        return $ SecureRenegotiation opaque ""+    extensionDecode MsgTServerHello = runGetMaybe $ do+        opaque <- getOpaque8+        let (cvd, svd) = B.splitAt (B.length opaque `div` 2) opaque+        return $ SecureRenegotiation cvd svd+    extensionDecode _ = error "extensionDecode: SecureRenegotiation"
+ Network/TLS/Extension.hs-boot view
@@ -0,0 +1,22 @@+-- This is a breaker for cyclic imports:+--+-- - Network.TLS.Extension imports Network.TLS.Struct+-- - Network.TLS.Extension imports Network.TLS.Packet+--+-- - Network.TLS.Struct imports Network.TLS.Extension+--+-- - Network.TLS.Packet imports Network.TLS.Struct+--+-- Originally, ExtensionRaw was defined in Network.TLS.Struct and no+-- cyclic imports exist. It is moved into Network.TLS.Extension for+-- pretty-printing, so the cyclic imports happen.+module Network.TLS.Extension where++import Data.ByteString+import Data.Word++data ExtensionRaw = ExtensionRaw ExtensionID ByteString+instance Eq ExtensionRaw+instance Show ExtensionRaw++newtype ExtensionID = ExtensionID {fromExtensionID :: Word16}
Network/TLS/Extra.hs view
@@ -1,15 +1,8 @@--- |--- Module      : Network.TLS.Extra--- License     : BSD-style--- Maintainer  : Vincent Hanquez <vincent@snarc.org>--- Stability   : experimental--- Portability : unknown------ default values and ciphers-module Network.TLS.Extra-    ( module Network.TLS.Extra.Cipher-    , module Network.TLS.Extra.FFDHE-    ) where+-- | Default values and ciphers+module Network.TLS.Extra (+    module Network.TLS.Extra.Cipher,+    module Network.TLS.Extra.FFDHE,+) where  import Network.TLS.Extra.Cipher import Network.TLS.Extra.FFDHE
Network/TLS/Extra/Cipher.hs view
@@ -1,660 +1,808 @@--- |--- Module      : Network.TLS.Extra.Cipher--- License     : BSD-style--- Maintainer  : Vincent Hanquez <vincent@snarc.org>--- Stability   : experimental--- Portability : unknown----{-# LANGUAGE CPP #-}-module Network.TLS.Extra.Cipher-    (-    -- * cipher suite-      ciphersuite_default-    , ciphersuite_all-    , ciphersuite_medium-    , ciphersuite_strong-    , ciphersuite_unencrypted-    , ciphersuite_dhe_rsa-    , ciphersuite_dhe_dss-    -- * individual ciphers-    , cipher_null_SHA1-    , cipher_AES128_SHA1-    , cipher_AES256_SHA1-    , cipher_AES128_SHA256-    , cipher_AES256_SHA256-    , cipher_AES128GCM_SHA256-    , cipher_AES256GCM_SHA384-    , cipher_DHE_RSA_AES128_SHA1-    , cipher_DHE_RSA_AES256_SHA1-    , cipher_DHE_RSA_AES128_SHA256-    , cipher_DHE_RSA_AES256_SHA256-    , cipher_DHE_DSS_AES128_SHA1-    , cipher_DHE_DSS_AES256_SHA1-    , cipher_DHE_RSA_AES128GCM_SHA256-    , cipher_DHE_RSA_AES256GCM_SHA384-    , cipher_ECDHE_RSA_AES128GCM_SHA256-    , cipher_ECDHE_RSA_AES256GCM_SHA384-    , cipher_ECDHE_RSA_AES128CBC_SHA256-    , cipher_ECDHE_RSA_AES128CBC_SHA-    , cipher_ECDHE_RSA_AES256CBC_SHA-    , cipher_ECDHE_RSA_AES256CBC_SHA384-    , cipher_ECDHE_ECDSA_AES128CBC_SHA-    , cipher_ECDHE_ECDSA_AES256CBC_SHA-    , cipher_ECDHE_ECDSA_AES128CBC_SHA256-    , cipher_ECDHE_ECDSA_AES256CBC_SHA384-    , cipher_ECDHE_ECDSA_AES128GCM_SHA256-    , cipher_ECDHE_ECDSA_AES256GCM_SHA384-    -- * obsolete and non-standard ciphers-    , cipher_RSA_3DES_EDE_CBC_SHA1-    , cipher_RC4_128_MD5-    , cipher_RC4_128_SHA1-    , cipher_null_MD5-    , cipher_DHE_DSS_RC4_SHA1-    ) where--import qualified Data.ByteString as B--import Network.TLS.Types (Version(..))-import Network.TLS.Cipher-import Network.TLS.Imports-import Data.Tuple (swap)--import Crypto.Cipher.AES-import qualified Crypto.Cipher.RC4 as RC4-import Crypto.Cipher.TripleDES-import Crypto.Cipher.Types hiding (Cipher, cipherName)-import Crypto.Error--takelast :: Int -> B.ByteString -> B.ByteString-takelast i b = B.drop (B.length b - i) b--aes128cbc :: BulkDirection -> BulkKey -> BulkBlock-aes128cbc BulkEncrypt key =-    let ctx = noFail (cipherInit key) :: AES128-     in (\iv input -> let output = cbcEncrypt ctx (makeIV_ iv) input in (output, takelast 16 output))-aes128cbc BulkDecrypt key =-    let ctx = noFail (cipherInit key) :: AES128-     in (\iv input -> let output = cbcDecrypt ctx (makeIV_ iv) input in (output, takelast 16 input))--aes256cbc :: BulkDirection -> BulkKey -> BulkBlock-aes256cbc BulkEncrypt key =-    let ctx = noFail (cipherInit key) :: AES256-     in (\iv input -> let output = cbcEncrypt ctx (makeIV_ iv) input in (output, takelast 16 output))-aes256cbc BulkDecrypt key =-    let ctx = noFail (cipherInit key) :: AES256-     in (\iv input -> let output = cbcDecrypt ctx (makeIV_ iv) input in (output, takelast 16 input))--aes128gcm :: BulkDirection -> BulkKey -> BulkAEAD-aes128gcm BulkEncrypt key =-    let ctx = noFail (cipherInit key) :: AES128-     in (\nonce d ad ->-            let aeadIni = noFail (aeadInit AEAD_GCM ctx nonce)-             in swap $ aeadSimpleEncrypt aeadIni ad d 16)-aes128gcm BulkDecrypt key =-    let ctx = noFail (cipherInit key) :: AES128-     in (\nonce d ad ->-            let aeadIni = noFail (aeadInit AEAD_GCM ctx nonce)-             in simpleDecrypt aeadIni ad d)-  where-    simpleDecrypt aeadIni header input = (output, tag)-      where-            aead                = aeadAppendHeader aeadIni header-            (output, aeadFinal) = aeadDecrypt aead input-            tag                 = aeadFinalize aeadFinal 16--aes256gcm :: BulkDirection -> BulkKey -> BulkAEAD-aes256gcm BulkEncrypt key =-    let ctx = noFail (cipherInit key) :: AES256-     in (\nonce d ad ->-            let aeadIni = noFail (aeadInit AEAD_GCM ctx nonce)-             in swap $ aeadSimpleEncrypt aeadIni ad d 16)-aes256gcm BulkDecrypt key =-    let ctx = noFail (cipherInit key) :: AES256-     in (\nonce d ad ->-            let aeadIni = noFail (aeadInit AEAD_GCM ctx nonce)-             in simpleDecrypt aeadIni ad d)-  where-    simpleDecrypt aeadIni header input = (output, tag)-      where-            aead                = aeadAppendHeader aeadIni header-            (output, aeadFinal) = aeadDecrypt aead input-            tag                 = aeadFinalize aeadFinal 16--noFail :: CryptoFailable a -> a-noFail = throwCryptoError--makeIV_ :: BlockCipher a => B.ByteString -> IV a-makeIV_ = fromMaybe (error "makeIV_") . makeIV--tripledes_ede :: BulkDirection -> BulkKey -> BulkBlock-tripledes_ede BulkEncrypt key =-    let ctx = noFail $ cipherInit key-     in (\iv input -> let output = cbcEncrypt ctx (tripledes_iv iv) input in (output, takelast 8 output))-tripledes_ede BulkDecrypt key =-    let ctx = noFail $ cipherInit key-     in (\iv input -> let output = cbcDecrypt ctx (tripledes_iv iv) input in (output, takelast 8 input))--tripledes_iv :: BulkIV -> IV DES_EDE3-tripledes_iv iv = fromMaybe (error "tripledes cipher iv internal error") $ makeIV iv--rc4 :: BulkDirection -> BulkKey -> BulkStream-rc4 _ bulkKey = BulkStream (combineRC4 $ RC4.initialize bulkKey)-  where-    combineRC4 ctx input =-        let (ctx', output) = RC4.combine ctx input-         in (output, BulkStream (combineRC4 ctx'))---- | All AES ciphers supported ordered from strong to weak.  This choice--- of ciphersuites should satisfy most normal needs.  For otherwise strong--- ciphers we make little distinction between AES128 and AES256, and list--- each but the weakest of the AES128 ciphers ahead of the corresponding AES256--- ciphers.-ciphersuite_default :: [Cipher]-ciphersuite_default =-    [        -- First the PFS + GCM + SHA2 ciphers-      cipher_ECDHE_ECDSA_AES128GCM_SHA256, cipher_ECDHE_ECDSA_AES256GCM_SHA384-    , cipher_ECDHE_RSA_AES128GCM_SHA256, cipher_ECDHE_RSA_AES256GCM_SHA384-    , cipher_DHE_RSA_AES128GCM_SHA256, cipher_DHE_RSA_AES256GCM_SHA384-             -- Next the PFS + CBC + SHA2 ciphers-    , cipher_ECDHE_ECDSA_AES128CBC_SHA256, cipher_ECDHE_ECDSA_AES256CBC_SHA384-    , cipher_ECDHE_RSA_AES128CBC_SHA256, cipher_ECDHE_RSA_AES256CBC_SHA384-    , cipher_DHE_RSA_AES128_SHA256, cipher_DHE_RSA_AES256_SHA256-             -- Next the PFS + CBC + SHA1 ciphers-    , cipher_ECDHE_ECDSA_AES128CBC_SHA, cipher_ECDHE_ECDSA_AES256CBC_SHA-    , cipher_ECDHE_RSA_AES128CBC_SHA, cipher_ECDHE_RSA_AES256CBC_SHA-    , cipher_DHE_RSA_AES128_SHA1, cipher_DHE_RSA_AES256_SHA1-             -- Next the non-PFS + GCM + SHA2 ciphers-    , cipher_AES128GCM_SHA256, cipher_AES256GCM_SHA384-             -- Next the non-PFS + CBC + SHA2 ciphers-    , cipher_AES256_SHA256, cipher_AES128_SHA256-             -- Next the non-PFS + CBC + SHA1 ciphers-    , cipher_AES256_SHA1, cipher_AES128_SHA1-             -- Nobody uses or should use DSS, RC4,  3DES or MD5-    -- , cipher_DHE_DSS_AES256_SHA1, cipher_DHE_DSS_AES128_SHA1-    -- , cipher_DHE_DSS_RC4_SHA1, cipher_RC4_128_SHA1, cipher_RC4_128_MD5-    -- , cipher_RSA_3DES_EDE_CBC_SHA1-    ]--{-# WARNING ciphersuite_all "This ciphersuite list contains RC4. Use ciphersuite_strong or ciphersuite_default instead." #-}--- | The default ciphersuites + some not recommended last resort ciphers.-ciphersuite_all :: [Cipher]-ciphersuite_all = ciphersuite_default ++-    [ cipher_DHE_DSS_AES256_SHA1, cipher_DHE_DSS_AES128_SHA1-    , cipher_RSA_3DES_EDE_CBC_SHA1-    , cipher_RC4_128_SHA1-    ]--{-# DEPRECATED ciphersuite_medium "Use ciphersuite_strong or ciphersuite_default instead." #-}--- | list of medium ciphers.-ciphersuite_medium :: [Cipher]-ciphersuite_medium = [ cipher_RC4_128_SHA1-                     , cipher_AES128_SHA1-                     ]---- | The strongest ciphers supported.  For ciphers with PFS, AEAD and SHA2, we--- list each AES128 variant right after the corresponding AES256 variant.  For--- weaker constructs, we use just the AES256 form.-ciphersuite_strong :: [Cipher]-ciphersuite_strong =-    [        -- If we have PFS + AEAD + SHA2, then allow AES128, else just 256-      cipher_ECDHE_ECDSA_AES256GCM_SHA384, cipher_ECDHE_ECDSA_AES128GCM_SHA256-    , cipher_ECDHE_RSA_AES256GCM_SHA384, cipher_ECDHE_RSA_AES128GCM_SHA256-    , cipher_DHE_RSA_AES256GCM_SHA384, cipher_DHE_RSA_AES128GCM_SHA256-             -- No AEAD-    , cipher_ECDHE_ECDSA_AES256CBC_SHA384-    , cipher_ECDHE_RSA_AES256CBC_SHA384-    , cipher_DHE_RSA_AES256_SHA256-             -- No SHA2-    , cipher_ECDHE_ECDSA_AES256CBC_SHA-    , cipher_ECDHE_RSA_AES256CBC_SHA-    , cipher_DHE_RSA_AES256_SHA1-             -- No PFS-    , cipher_AES256GCM_SHA384-             -- Neither PFS nor AEAD, just SHA2-    , cipher_AES256_SHA256-             -- Last resort no PFS, AEAD or SHA2-    , cipher_AES256_SHA1-    ]---- | DHE-RSA cipher suite-ciphersuite_dhe_rsa :: [Cipher]-ciphersuite_dhe_rsa = [ cipher_DHE_RSA_AES256GCM_SHA384, cipher_DHE_RSA_AES128GCM_SHA256-                      , cipher_DHE_RSA_AES256_SHA256, cipher_DHE_RSA_AES128_SHA256-                      , cipher_DHE_RSA_AES256_SHA1, cipher_DHE_RSA_AES128_SHA1-                      ]--ciphersuite_dhe_dss :: [Cipher]-ciphersuite_dhe_dss = [cipher_DHE_DSS_AES256_SHA1, cipher_DHE_DSS_AES128_SHA1, cipher_DHE_DSS_RC4_SHA1]---- | all unencrypted ciphers, do not use on insecure network.-ciphersuite_unencrypted :: [Cipher]-ciphersuite_unencrypted = [cipher_null_MD5, cipher_null_SHA1]--bulk_null, bulk_rc4, bulk_aes128, bulk_aes256, bulk_tripledes_ede, bulk_aes128gcm, bulk_aes256gcm :: Bulk-bulk_null = Bulk-    { bulkName         = "null"-    , bulkKeySize      = 0-    , bulkIVSize       = 0-    , bulkExplicitIV   = 0-    , bulkAuthTagLen   = 0-    , bulkBlockSize    = 0-    , bulkF            = BulkStreamF passThrough-    }-  where-    passThrough _ _ = BulkStream go where go inp = (inp, BulkStream go)--bulk_rc4 = Bulk-    { bulkName         = "RC4-128"-    , bulkKeySize      = 16-    , bulkIVSize       = 0-    , bulkExplicitIV   = 0-    , bulkAuthTagLen   = 0-    , bulkBlockSize    = 0-    , bulkF            = BulkStreamF rc4-    }--bulk_aes128 = Bulk-    { bulkName         = "AES128"-    , bulkKeySize      = 16-    , bulkIVSize       = 16-    , bulkExplicitIV   = 0-    , bulkAuthTagLen   = 0-    , bulkBlockSize    = 16-    , bulkF            = BulkBlockF aes128cbc-    }--bulk_aes128gcm = Bulk-    { bulkName         = "AES128GCM"-    , bulkKeySize      = 16 -- RFC 5116 Sec 5.1: K_LEN-    , bulkIVSize       = 4  -- RFC 5288 GCMNonce.salt, fixed_iv_length-    , bulkExplicitIV   = 8-    , bulkAuthTagLen   = 16-    , bulkBlockSize    = 0  -- dummy, not used-    , bulkF            = BulkAeadF aes128gcm-    }--bulk_aes256gcm = Bulk-    { bulkName         = "AES256GCM"-    , bulkKeySize      = 32 -- RFC 5116 Sec 5.1: K_LEN-    , bulkIVSize       = 4  -- RFC 5288 GCMNonce.salt, fixed_iv_length-    , bulkExplicitIV   = 8-    , bulkAuthTagLen   = 16-    , bulkBlockSize    = 0  -- dummy, not used-    , bulkF            = BulkAeadF aes256gcm-    }--bulk_aes256 = Bulk-    { bulkName         = "AES256"-    , bulkKeySize      = 32-    , bulkIVSize       = 16-    , bulkExplicitIV   = 0-    , bulkAuthTagLen   = 0-    , bulkBlockSize    = 16-    , bulkF            = BulkBlockF aes256cbc-    }--bulk_tripledes_ede = Bulk-    { bulkName      = "3DES-EDE-CBC"-    , bulkKeySize   = 24-    , bulkIVSize    = 8-    , bulkExplicitIV = 0-    , bulkAuthTagLen = 0-    , bulkBlockSize = 8-    , bulkF         = BulkBlockF tripledes_ede-    }---- | unencrypted cipher using RSA for key exchange and MD5 for digest-cipher_null_MD5 :: Cipher-cipher_null_MD5 = Cipher-    { cipherID           = 0x0001-    , cipherName         = "RSA-null-MD5"-    , cipherBulk         = bulk_null-    , cipherHash         = MD5-    , cipherPRFHash      = Nothing-    , cipherKeyExchange  = CipherKeyExchange_RSA-    , cipherMinVer       = Nothing-    }---- | unencrypted cipher using RSA for key exchange and SHA1 for digest-cipher_null_SHA1 :: Cipher-cipher_null_SHA1 = Cipher-    { cipherID           = 0x0002-    , cipherName         = "RSA-null-SHA1"-    , cipherBulk         = bulk_null-    , cipherHash         = SHA1-    , cipherPRFHash      = Nothing-    , cipherKeyExchange  = CipherKeyExchange_RSA-    , cipherMinVer       = Nothing-    }---- | RC4 cipher, RSA key exchange and MD5 for digest-cipher_RC4_128_MD5 :: Cipher-cipher_RC4_128_MD5 = Cipher-    { cipherID           = 0x0004-    , cipherName         = "RSA-rc4-128-md5"-    , cipherBulk         = bulk_rc4-    , cipherHash         = MD5-    , cipherPRFHash      = Nothing-    , cipherKeyExchange  = CipherKeyExchange_RSA-    , cipherMinVer       = Nothing-    }---- | RC4 cipher, RSA key exchange and SHA1 for digest-cipher_RC4_128_SHA1 :: Cipher-cipher_RC4_128_SHA1 = Cipher-    { cipherID           = 0x0005-    , cipherName         = "RSA-rc4-128-sha1"-    , cipherBulk         = bulk_rc4-    , cipherHash         = SHA1-    , cipherPRFHash      = Nothing-    , cipherKeyExchange  = CipherKeyExchange_RSA-    , cipherMinVer       = Nothing-    }---- | 3DES cipher (168 bit key), RSA key exchange and SHA1 for digest-cipher_RSA_3DES_EDE_CBC_SHA1 :: Cipher-cipher_RSA_3DES_EDE_CBC_SHA1 = Cipher-    { cipherID           = 0x000A-    , cipherName         = "RSA-3DES-EDE-CBC-SHA1"-    , cipherBulk         = bulk_tripledes_ede-    , cipherHash         = SHA1-    , cipherPRFHash      = Nothing-    , cipherKeyExchange  = CipherKeyExchange_RSA-    , cipherMinVer       = Nothing-    }---- | AES cipher (128 bit key), RSA key exchange and SHA1 for digest-cipher_AES128_SHA1 :: Cipher-cipher_AES128_SHA1 = Cipher-    { cipherID           = 0x002F-    , cipherName         = "RSA-AES128-SHA1"-    , cipherBulk         = bulk_aes128-    , cipherHash         = SHA1-    , cipherPRFHash      = Nothing-    , cipherKeyExchange  = CipherKeyExchange_RSA-    , cipherMinVer       = Just SSL3-    }---- | AES cipher (128 bit key), DHE key exchanged signed by DSA and SHA1 for digest-cipher_DHE_DSS_AES128_SHA1 :: Cipher-cipher_DHE_DSS_AES128_SHA1 = Cipher-    { cipherID           = 0x0032-    , cipherName         = "DHE-DSA-AES128-SHA1"-    , cipherBulk         = bulk_aes128-    , cipherHash         = SHA1-    , cipherPRFHash      = Nothing-    , cipherKeyExchange  = CipherKeyExchange_DHE_DSS-    , cipherMinVer       = Nothing-    }---- | AES cipher (128 bit key), DHE key exchanged signed by RSA and SHA1 for digest-cipher_DHE_RSA_AES128_SHA1 :: Cipher-cipher_DHE_RSA_AES128_SHA1 = Cipher-    { cipherID           = 0x0033-    , cipherName         = "DHE-RSA-AES128-SHA1"-    , cipherBulk         = bulk_aes128-    , cipherHash         = SHA1-    , cipherPRFHash      = Nothing-    , cipherKeyExchange  = CipherKeyExchange_DHE_RSA-    , cipherMinVer       = Nothing-    }---- | AES cipher (256 bit key), RSA key exchange and SHA1 for digest-cipher_AES256_SHA1 :: Cipher-cipher_AES256_SHA1 = Cipher-    { cipherID           = 0x0035-    , cipherName         = "RSA-AES256-SHA1"-    , cipherBulk         = bulk_aes256-    , cipherHash         = SHA1-    , cipherPRFHash      = Nothing-    , cipherKeyExchange  = CipherKeyExchange_RSA-    , cipherMinVer       = Just SSL3-    }---- | AES cipher (256 bit key), DHE key exchanged signed by DSA and SHA1 for digest-cipher_DHE_DSS_AES256_SHA1 :: Cipher-cipher_DHE_DSS_AES256_SHA1 = cipher_DHE_DSS_AES128_SHA1-    { cipherID           = 0x0038-    , cipherName         = "DHE-DSA-AES256-SHA1"-    , cipherBulk         = bulk_aes256-    }---- | AES cipher (256 bit key), DHE key exchanged signed by RSA and SHA1 for digest-cipher_DHE_RSA_AES256_SHA1 :: Cipher-cipher_DHE_RSA_AES256_SHA1 = cipher_DHE_RSA_AES128_SHA1-    { cipherID           = 0x0039-    , cipherName         = "DHE-RSA-AES256-SHA1"-    , cipherBulk         = bulk_aes256-    }---- | AES cipher (128 bit key), RSA key exchange and SHA256 for digest-cipher_AES128_SHA256 :: Cipher-cipher_AES128_SHA256 = Cipher-    { cipherID           = 0x003C-    , cipherName         = "RSA-AES128-SHA256"-    , cipherBulk         = bulk_aes128-    , cipherHash         = SHA256-    , cipherPRFHash      = Just SHA256-    , cipherKeyExchange  = CipherKeyExchange_RSA-    , cipherMinVer       = Just TLS12-    }---- | AES cipher (256 bit key), RSA key exchange and SHA256 for digest-cipher_AES256_SHA256 :: Cipher-cipher_AES256_SHA256 = Cipher-    { cipherID           = 0x003D-    , cipherName         = "RSA-AES256-SHA256"-    , cipherBulk         = bulk_aes256-    , cipherHash         = SHA256-    , cipherPRFHash      = Just SHA256-    , cipherKeyExchange  = CipherKeyExchange_RSA-    , cipherMinVer       = Just TLS12-    }---- This is not registered in IANA.--- So, this will be removed in the next major release.-cipher_DHE_DSS_RC4_SHA1 :: Cipher-cipher_DHE_DSS_RC4_SHA1 = cipher_DHE_DSS_AES128_SHA1-    { cipherID           = 0x0066-    , cipherName         = "DHE-DSA-RC4-SHA1"-    , cipherBulk         = bulk_rc4-    }--cipher_DHE_RSA_AES128_SHA256 :: Cipher-cipher_DHE_RSA_AES128_SHA256 = cipher_DHE_RSA_AES128_SHA1-    { cipherID           = 0x0067-    , cipherName         = "DHE-RSA-AES128-SHA256"-    , cipherHash         = SHA256-    , cipherPRFHash      = Just SHA256-    , cipherMinVer       = Just TLS12-    }--cipher_DHE_RSA_AES256_SHA256 :: Cipher-cipher_DHE_RSA_AES256_SHA256 = cipher_DHE_RSA_AES128_SHA256-    { cipherID           = 0x006B-    , cipherName         = "DHE-RSA-AES256-SHA256"-    , cipherBulk         = bulk_aes256-    }---- | AESGCM cipher (128 bit key), RSA key exchange.--- The SHA256 digest is used as a PRF, not as a MAC.-cipher_AES128GCM_SHA256 :: Cipher-cipher_AES128GCM_SHA256 = Cipher-    { cipherID           = 0x009C-    , cipherName         = "RSA-AES128GCM-SHA256"-    , cipherBulk         = bulk_aes128gcm-    , cipherHash         = SHA256-    , cipherPRFHash      = Just SHA256-    , cipherKeyExchange  = CipherKeyExchange_RSA-    , cipherMinVer       = Just TLS12-    }---- | AESGCM cipher (256 bit key), RSA key exchange.--- The SHA384 digest is used as a PRF, not as a MAC.-cipher_AES256GCM_SHA384 :: Cipher-cipher_AES256GCM_SHA384 = Cipher-    { cipherID           = 0x009D-    , cipherName         = "RSA-AES256GCM-SHA384"-    , cipherBulk         = bulk_aes256gcm-    , cipherHash         = SHA384-    , cipherPRFHash      = Just SHA384-    , cipherKeyExchange  = CipherKeyExchange_RSA-    , cipherMinVer       = Just TLS12-    }--cipher_DHE_RSA_AES128GCM_SHA256 :: Cipher-cipher_DHE_RSA_AES128GCM_SHA256 = Cipher-    { cipherID           = 0x009E-    , cipherName         = "DHE-RSA-AES128GCM-SHA256"-    , cipherBulk         = bulk_aes128gcm-    , cipherHash         = SHA256-    , cipherPRFHash      = Just SHA256-    , cipherKeyExchange  = CipherKeyExchange_DHE_RSA-    , cipherMinVer       = Just TLS12 -- RFC 5288 Sec 4-    }--cipher_DHE_RSA_AES256GCM_SHA384 :: Cipher-cipher_DHE_RSA_AES256GCM_SHA384 = Cipher-    { cipherID           = 0x009F-    , cipherName         = "DHE-RSA-AES256GCM-SHA384"-    , cipherBulk         = bulk_aes256gcm-    , cipherHash         = SHA384-    , cipherPRFHash      = Just SHA384-    , cipherKeyExchange  = CipherKeyExchange_DHE_RSA-    , cipherMinVer       = Just TLS12-    }--cipher_ECDHE_ECDSA_AES128CBC_SHA :: Cipher-cipher_ECDHE_ECDSA_AES128CBC_SHA = Cipher-    { cipherID           = 0xC009-    , cipherName         = "ECDHE-ECDSA-AES128CBC-SHA"-    , cipherBulk         = bulk_aes128-    , cipherHash         = SHA1-    , cipherPRFHash      = Nothing-    , cipherKeyExchange  = CipherKeyExchange_ECDHE_ECDSA-    , cipherMinVer       = Just TLS10-    }--cipher_ECDHE_ECDSA_AES256CBC_SHA :: Cipher-cipher_ECDHE_ECDSA_AES256CBC_SHA = Cipher-    { cipherID           = 0xC00A-    , cipherName         = "ECDHE-ECDSA-AES256CBC-SHA"-    , cipherBulk         = bulk_aes256-    , cipherHash         = SHA1-    , cipherPRFHash      = Nothing-    , cipherKeyExchange  = CipherKeyExchange_ECDHE_ECDSA-    , cipherMinVer       = Just TLS10-    }--cipher_ECDHE_RSA_AES128CBC_SHA :: Cipher-cipher_ECDHE_RSA_AES128CBC_SHA = Cipher-    { cipherID           = 0xC013-    , cipherName         = "ECDHE-RSA-AES128CBC-SHA"-    , cipherBulk         = bulk_aes128-    , cipherHash         = SHA1-    , cipherPRFHash      = Nothing-    , cipherKeyExchange  = CipherKeyExchange_ECDHE_RSA-    , cipherMinVer       = Just TLS10-    }--cipher_ECDHE_RSA_AES256CBC_SHA :: Cipher-cipher_ECDHE_RSA_AES256CBC_SHA = Cipher-    { cipherID           = 0xC014-    , cipherName         = "ECDHE-RSA-AES256CBC-SHA"-    , cipherBulk         = bulk_aes256-    , cipherHash         = SHA1-    , cipherPRFHash      = Nothing-    , cipherKeyExchange  = CipherKeyExchange_ECDHE_RSA-    , cipherMinVer       = Just TLS10-    }--cipher_ECDHE_RSA_AES128CBC_SHA256 :: Cipher-cipher_ECDHE_RSA_AES128CBC_SHA256 = Cipher-    { cipherID           = 0xC027-    , cipherName         = "ECDHE-RSA-AES128CBC-SHA256"-    , cipherBulk         = bulk_aes128-    , cipherHash         = SHA256-    , cipherPRFHash      = Just SHA256-    , cipherKeyExchange  = CipherKeyExchange_ECDHE_RSA-    , cipherMinVer       = Just TLS12 -- RFC 5288 Sec 4-    }--cipher_ECDHE_RSA_AES256CBC_SHA384 :: Cipher-cipher_ECDHE_RSA_AES256CBC_SHA384 = Cipher-    { cipherID           = 0xC028-    , cipherName         = "ECDHE-RSA-AES256CBC-SHA384"-    , cipherBulk         = bulk_aes256-    , cipherHash         = SHA384-    , cipherPRFHash      = Just SHA384-    , cipherKeyExchange  = CipherKeyExchange_ECDHE_RSA-    , cipherMinVer       = Just TLS12 -- RFC 5288 Sec 4-    }--cipher_ECDHE_ECDSA_AES128CBC_SHA256 :: Cipher-cipher_ECDHE_ECDSA_AES128CBC_SHA256 = Cipher-    { cipherID           = 0xc023-    , cipherName         = "ECDHE-ECDSA-AES128CBC-SHA256"-    , cipherBulk         = bulk_aes128-    , cipherHash         = SHA256-    , cipherPRFHash      = Just SHA256-    , cipherKeyExchange  = CipherKeyExchange_ECDHE_ECDSA-    , cipherMinVer       = Just TLS12 -- RFC 5289-    }--cipher_ECDHE_ECDSA_AES256CBC_SHA384 :: Cipher-cipher_ECDHE_ECDSA_AES256CBC_SHA384 = Cipher-    { cipherID           = 0xC024-    , cipherName         = "ECDHE-ECDSA-AES256CBC-SHA384"-    , cipherBulk         = bulk_aes256-    , cipherHash         = SHA384-    , cipherPRFHash      = Just SHA384-    , cipherKeyExchange  = CipherKeyExchange_ECDHE_ECDSA-    , cipherMinVer       = Just TLS12 -- RFC 5289-    }--cipher_ECDHE_ECDSA_AES128GCM_SHA256 :: Cipher-cipher_ECDHE_ECDSA_AES128GCM_SHA256 = Cipher-    { cipherID           = 0xC02B-    , cipherName         = "ECDHE-ECDSA-AES128GCM-SHA256"-    , cipherBulk         = bulk_aes128gcm-    , cipherHash         = SHA256-    , cipherPRFHash      = Just SHA256-    , cipherKeyExchange  = CipherKeyExchange_ECDHE_ECDSA-    , cipherMinVer       = Just TLS12 -- RFC 5289-    }--cipher_ECDHE_ECDSA_AES256GCM_SHA384 :: Cipher-cipher_ECDHE_ECDSA_AES256GCM_SHA384 = Cipher-    { cipherID           = 0xC02C-    , cipherName         = "ECDHE-ECDSA-AES256GCM-SHA384"-    , cipherBulk         = bulk_aes256gcm-    , cipherHash         = SHA384-    , cipherPRFHash      = Just SHA384-    , cipherKeyExchange  = CipherKeyExchange_ECDHE_ECDSA-    , cipherMinVer       = Just TLS12 -- RFC 5289-    }--cipher_ECDHE_RSA_AES128GCM_SHA256 :: Cipher-cipher_ECDHE_RSA_AES128GCM_SHA256 = Cipher-    { cipherID           = 0xC02F-    , cipherName         = "ECDHE-RSA-AES128GCM-SHA256"-    , cipherBulk         = bulk_aes128gcm-    , cipherHash         = SHA256-    , cipherPRFHash      = Just SHA256-    , cipherKeyExchange  = CipherKeyExchange_ECDHE_RSA-    , cipherMinVer       = Just TLS12 -- RFC 5288 Sec 4-    }--cipher_ECDHE_RSA_AES256GCM_SHA384 :: Cipher-cipher_ECDHE_RSA_AES256GCM_SHA384 = Cipher-    { cipherID           = 0xC030-    , cipherName         = "ECDHE-RSA-AES256GCM-SHA384"-    , cipherBulk         = bulk_aes256gcm-    , cipherHash         = SHA384-    , cipherPRFHash      = Just SHA384-    , cipherKeyExchange  = CipherKeyExchange_ECDHE_RSA-    , cipherMinVer       = Just TLS12 -- RFC 5289-    }---- A list of cipher suite is found from:--- https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-4+module Network.TLS.Extra.Cipher (+    -- * Cipher suite+    ciphersuite_default,+    ciphersuite_default_det,+    ciphersuite_all,+    ciphersuite_all_det,+    ciphersuite_strong,+    ciphersuite_strong_det,+    ciphersuite_dhe_rsa,++    -- * Individual ciphers++    -- ** RFC 5288+    cipher_DHE_RSA_WITH_AES_128_GCM_SHA256,+    cipher_DHE_RSA_WITH_AES_256_GCM_SHA384,++    -- ** RFC 8446+    cipher13_AES_128_GCM_SHA256,+    cipher13_AES_256_GCM_SHA384,+    cipher13_CHACHA20_POLY1305_SHA256,+    cipher13_AES_128_CCM_SHA256,+    cipher13_AES_128_CCM_8_SHA256,++    -- ** RFC 5289+    cipher_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,+    cipher_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,+    cipher_ECDHE_RSA_WITH_AES_128_GCM_SHA256,+    cipher_ECDHE_RSA_WITH_AES_256_GCM_SHA384,++    -- ** RFC 7251+    cipher_ECDHE_ECDSA_WITH_AES_128_CCM,+    cipher_ECDHE_ECDSA_WITH_AES_256_CCM,+    cipher_ECDHE_ECDSA_WITH_AES_128_CCM_8,+    cipher_ECDHE_ECDSA_WITH_AES_256_CCM_8,++    -- ** RFC 7905+    cipher_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,+    cipher_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,+    cipher_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256,++    -- * Deprecated names++    -- ** RFC 5288+    cipher_DHE_RSA_AES128GCM_SHA256,+    cipher_DHE_RSA_AES256GCM_SHA384,++    -- ** RFC 8446+    cipher_TLS13_AES128GCM_SHA256,+    cipher_TLS13_AES256GCM_SHA384,+    cipher_TLS13_CHACHA20POLY1305_SHA256,+    cipher_TLS13_AES128CCM_SHA256,+    cipher_TLS13_AES128CCM8_SHA256,++    -- ** RFC 5289+    cipher_ECDHE_ECDSA_AES128GCM_SHA256,+    cipher_ECDHE_ECDSA_AES256GCM_SHA384,+    cipher_ECDHE_RSA_AES128GCM_SHA256,+    cipher_ECDHE_RSA_AES256GCM_SHA384,++    -- ** RFC 7251+    cipher_ECDHE_ECDSA_AES128CCM_SHA256,+    cipher_ECDHE_ECDSA_AES256CCM_SHA256,+    cipher_ECDHE_ECDSA_AES128CCM8_SHA256,+    cipher_ECDHE_ECDSA_AES256CCM8_SHA256,++    -- ** RFC 7905+    cipher_ECDHE_RSA_CHACHA20POLY1305_SHA256,+    cipher_ECDHE_ECDSA_CHACHA20POLY1305_SHA256,+    cipher_DHE_RSA_CHACHA20POLY1305_SHA256,+) where++import Crypto.Cipher.AES+import qualified Crypto.Cipher.ChaChaPoly1305 as ChaChaPoly1305+import Crypto.Cipher.Types hiding (Cipher, cipherName)+import Crypto.Error+import qualified Crypto.MAC.Poly1305 as Poly1305+import Crypto.System.CPU+import qualified Data.ByteString as B+import Data.Tuple (swap)++import Network.TLS.Cipher+import Network.TLS.Imports+import Network.TLS.Types++----------------------------------------------------------------++-- | All AES and ChaCha20-Poly1305 ciphers supported ordered from strong to+-- weak.  This choice of ciphersuites should satisfy most normal needs.  For+-- otherwise strong ciphers we make little distinction between AES128 and+-- AES256, and list each but the weakest of the AES128 ciphers ahead of the+-- corresponding AES256 ciphers.+--+-- AEAD ciphers with equivalent security properties are ordered based on CPU+-- hardware-acceleration support.  If this dynamic runtime behavior is not+-- desired, use 'ciphersuite_default_det' instead.+ciphersuite_default :: [Cipher]+ciphersuite_default = ciphersuite_strong++-- | Same as 'ciphersuite_default', but using deterministic preference not+-- influenced by the CPU.+ciphersuite_default_det :: [Cipher]+ciphersuite_default_det = ciphersuite_strong_det++----------------------------------------------------------------++-- | The default ciphersuites + some not recommended last resort ciphers.+--+-- AEAD ciphers with equivalent security properties are ordered based on CPU+-- hardware-acceleration support.  If this dynamic runtime behavior is not+-- desired, use 'ciphersuite_all_det' instead.+ciphersuite_all :: [Cipher]+ciphersuite_all = ciphersuite_default ++ complement_all++-- | Same as 'ciphersuite_all', but using deterministic preference not+-- influenced by the CPU.+ciphersuite_all_det :: [Cipher]+ciphersuite_all_det = ciphersuite_default_det ++ complement_all++complement_all :: [Cipher]+complement_all =+    [ cipher_ECDHE_ECDSA_WITH_AES_128_CCM_8+    , cipher_ECDHE_ECDSA_WITH_AES_256_CCM_8+    , cipher13_AES_128_CCM_8_SHA256+    ]++-- | The strongest ciphers supported.  For ciphers with PFS, AEAD and SHA2, we+-- list each AES128 variant after the corresponding AES256 and ChaCha20-Poly1305+-- variants.  For weaker constructs, we use just the AES256 form.+--+-- AEAD ciphers with equivalent security properties are ordered based on CPU+-- hardware-acceleration support.  If this dynamic runtime behavior is not+-- desired, use 'ciphersuite_strong_det' instead.+ciphersuite_strong :: [Cipher]+ciphersuite_strong = sortOptimized sets_strong++-- | Same as 'ciphersuite_strong', but using deterministic preference not+-- influenced by the CPU.+ciphersuite_strong_det :: [Cipher]+ciphersuite_strong_det = sortDeterministic sets_strong++sets_strong :: [CipherSet]+sets_strong =+    [ -- If we have PFS + AEAD + SHA2, then allow AES128, else just 256+      SetAead+        [cipher_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384]+        [cipher_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256]+        [cipher_ECDHE_ECDSA_WITH_AES_256_CCM]+    , SetAead+        [cipher_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256]+        []+        [cipher_ECDHE_ECDSA_WITH_AES_128_CCM]+    , SetAead+        [cipher_ECDHE_RSA_WITH_AES_256_GCM_SHA384]+        [cipher_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256]+        []+    , SetAead+        [cipher_ECDHE_RSA_WITH_AES_128_GCM_SHA256]+        []+        []+    , -- TLS13 (listed at the end but version is negotiated first)+      SetAead+        [cipher13_AES_256_GCM_SHA384]+        [cipher13_CHACHA20_POLY1305_SHA256]+        []+    , SetAead+        [cipher13_AES_128_GCM_SHA256]+        []+        [cipher13_AES_128_CCM_SHA256]+    ]++-- | DHE-RSA cipher suite.  This only includes ciphers bound specifically to+-- DHE-RSA so TLS 1.3 ciphers must be added separately.+--+-- @since 2.1.5+ciphersuite_dhe_rsa :: [Cipher]+ciphersuite_dhe_rsa =+    [ cipher_DHE_RSA_WITH_AES_256_GCM_SHA384+    , cipher_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256+    , cipher_DHE_RSA_WITH_AES_128_GCM_SHA256+    ]++----------------------------------------------------------------+----------------------------------------------------------------++-- A list of cipher suite is found from:+-- https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-4++----------------------------------------------------------------+-- RFC 5288++-- TLS_DHE_RSA_WITH_AES_128_GCM_SHA256+cipher_DHE_RSA_WITH_AES_128_GCM_SHA256 :: Cipher+cipher_DHE_RSA_WITH_AES_128_GCM_SHA256 =+    Cipher+        { cipherID = 0x009E+        , cipherName = "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256"+        , cipherBulk = bulk_aes128gcm+        , cipherHash = SHA256+        , cipherPRFHash = Just SHA256+        , cipherKeyExchange = CipherKeyExchange_DHE_RSA+        , cipherMinVer = Just TLS12 -- RFC 5288 Sec 4+        }++{-# DEPRECATED+    cipher_DHE_RSA_AES128GCM_SHA256+    "Use cipher_DHE_RSA_WITH_AES_128_GCM_SHA256 instead"+    #-}+cipher_DHE_RSA_AES128GCM_SHA256 :: Cipher+cipher_DHE_RSA_AES128GCM_SHA256 = cipher_DHE_RSA_WITH_AES_128_GCM_SHA256++-- TLS_DHE_RSA_WITH_AES_256_GCM_SHA384+cipher_DHE_RSA_WITH_AES_256_GCM_SHA384 :: Cipher+cipher_DHE_RSA_WITH_AES_256_GCM_SHA384 =+    Cipher+        { cipherID = 0x009F+        , cipherName = "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384"+        , cipherBulk = bulk_aes256gcm+        , cipherHash = SHA384+        , cipherPRFHash = Just SHA384+        , cipherKeyExchange = CipherKeyExchange_DHE_RSA+        , cipherMinVer = Just TLS12+        }++{-# DEPRECATED+    cipher_DHE_RSA_AES256GCM_SHA384+    "Use cipher_DHE_RSA_WITH_AES_256_GCM_SHA384 instead"+    #-}+cipher_DHE_RSA_AES256GCM_SHA384 :: Cipher+cipher_DHE_RSA_AES256GCM_SHA384 = cipher_DHE_RSA_WITH_AES_256_GCM_SHA384++----------------------------------------------------------------+-- RFC 8446++-- TLS_AES_128_GCM_SHA256+cipher13_AES_128_GCM_SHA256 :: Cipher+cipher13_AES_128_GCM_SHA256 =+    Cipher+        { cipherID = 0x1301+        , cipherName = "TLS_AES_128_GCM_SHA256"+        , cipherBulk = bulk_aes128gcm_13+        , cipherHash = SHA256+        , cipherPRFHash = Nothing+        , cipherKeyExchange = CipherKeyExchange_TLS13+        , cipherMinVer = Just TLS13+        }++cipher_TLS13_AES128GCM_SHA256 :: Cipher+cipher_TLS13_AES128GCM_SHA256 = cipher13_AES_128_GCM_SHA256+{-# DEPRECATED+    cipher_TLS13_AES128GCM_SHA256+    "Use cipher13_AES_128_GCM_SHA256 instead"+    #-}++-- TLS_AES_256_GCM_SHA384+cipher13_AES_256_GCM_SHA384 :: Cipher+cipher13_AES_256_GCM_SHA384 =+    Cipher+        { cipherID = 0x1302+        , cipherName = "TLS_AES_256_GCM_SHA384"+        , cipherBulk = bulk_aes256gcm_13+        , cipherHash = SHA384+        , cipherPRFHash = Nothing+        , cipherKeyExchange = CipherKeyExchange_TLS13+        , cipherMinVer = Just TLS13+        }++cipher_TLS13_AES256GCM_SHA384 :: Cipher+cipher_TLS13_AES256GCM_SHA384 = cipher13_AES_256_GCM_SHA384+{-# DEPRECATED+    cipher_TLS13_AES256GCM_SHA384+    "Use cipher13_AES_256_GCM_SHA384 instead"+    #-}++-- TLS_CHACHA20_POLY1305_SHA256+cipher13_CHACHA20_POLY1305_SHA256 :: Cipher+cipher13_CHACHA20_POLY1305_SHA256 =+    Cipher+        { cipherID = 0x1303+        , cipherName = "TLS_CHACHA20_POLY1305_SHA256"+        , cipherBulk = bulk_chacha20poly1305+        , cipherHash = SHA256+        , cipherPRFHash = Nothing+        , cipherKeyExchange = CipherKeyExchange_TLS13+        , cipherMinVer = Just TLS13+        }++cipher_TLS13_CHACHA20POLY1305_SHA256 :: Cipher+cipher_TLS13_CHACHA20POLY1305_SHA256 = cipher13_CHACHA20_POLY1305_SHA256+{-# DEPRECATED+    cipher_TLS13_CHACHA20POLY1305_SHA256+    "Use cipher13_CHACHA20_POLY1305_SHA256 instead"+    #-}++-- TLS_AES_128_CCM_SHA256+cipher13_AES_128_CCM_SHA256 :: Cipher+cipher13_AES_128_CCM_SHA256 =+    Cipher+        { cipherID = 0x1304+        , cipherName = "TLS_AES_128_CCM_SHA256"+        , cipherBulk = bulk_aes128ccm_13+        , cipherHash = SHA256+        , cipherPRFHash = Nothing+        , cipherKeyExchange = CipherKeyExchange_TLS13+        , cipherMinVer = Just TLS13+        }++cipher_TLS13_AES128CCM_SHA256 :: Cipher+cipher_TLS13_AES128CCM_SHA256 = cipher13_AES_128_CCM_SHA256+{-# DEPRECATED+    cipher_TLS13_AES128CCM_SHA256+    "Use cipher13_AES_128_CCM_SHA256 instead"+    #-}++-- TLS_AES_128_CCM_8_SHA256+cipher13_AES_128_CCM_8_SHA256 :: Cipher+cipher13_AES_128_CCM_8_SHA256 =+    Cipher+        { cipherID = 0x1305+        , cipherName = "TLS_AES_128_CCM_8_SHA256"+        , cipherBulk = bulk_aes128ccm8_13+        , cipherHash = SHA256+        , cipherPRFHash = Nothing+        , cipherKeyExchange = CipherKeyExchange_TLS13+        , cipherMinVer = Just TLS13+        }++cipher_TLS13_AES128CCM8_SHA256 :: Cipher+cipher_TLS13_AES128CCM8_SHA256 = cipher13_AES_128_CCM_8_SHA256+{-# DEPRECATED+    cipher_TLS13_AES128CCM8_SHA256+    "Use cipher13_AES_128_CCM_8_SHA256 instead"+    #-}++----------------------------------------------------------------+-- GCM: RFC 5289++-- TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256+cipher_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 :: Cipher+cipher_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 =+    Cipher+        { cipherID = 0xC02B+        , cipherName = "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"+        , cipherBulk = bulk_aes128gcm+        , cipherHash = SHA256+        , cipherPRFHash = Just SHA256+        , cipherKeyExchange = CipherKeyExchange_ECDHE_ECDSA+        , cipherMinVer = Just TLS12 -- RFC 5289+        }++cipher_ECDHE_ECDSA_AES128GCM_SHA256 :: Cipher+cipher_ECDHE_ECDSA_AES128GCM_SHA256 = cipher_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256+{-# DEPRECATED+    cipher_ECDHE_ECDSA_AES128GCM_SHA256+    "Use cipher_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 instead"+    #-}++-- TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384+cipher_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 :: Cipher+cipher_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 =+    Cipher+        { cipherID = 0xC02C+        , cipherName = "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"+        , cipherBulk = bulk_aes256gcm+        , cipherHash = SHA384+        , cipherPRFHash = Just SHA384+        , cipherKeyExchange = CipherKeyExchange_ECDHE_ECDSA+        , cipherMinVer = Just TLS12 -- RFC 5289+        }++cipher_ECDHE_ECDSA_AES256GCM_SHA384 :: Cipher+cipher_ECDHE_ECDSA_AES256GCM_SHA384 = cipher_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384+{-# DEPRECATED+    cipher_ECDHE_ECDSA_AES256GCM_SHA384+    "Use cipher_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 instead"+    #-}++-- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256+cipher_ECDHE_RSA_WITH_AES_128_GCM_SHA256 :: Cipher+cipher_ECDHE_RSA_WITH_AES_128_GCM_SHA256 =+    Cipher+        { cipherID = 0xC02F+        , cipherName = "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"+        , cipherBulk = bulk_aes128gcm+        , cipherHash = SHA256+        , cipherPRFHash = Just SHA256+        , cipherKeyExchange = CipherKeyExchange_ECDHE_RSA+        , cipherMinVer = Just TLS12 -- RFC 5288 Sec 4+        }++cipher_ECDHE_RSA_AES128GCM_SHA256 :: Cipher+cipher_ECDHE_RSA_AES128GCM_SHA256 = cipher_ECDHE_RSA_WITH_AES_128_GCM_SHA256+{-# DEPRECATED+    cipher_ECDHE_RSA_AES128GCM_SHA256+    "Use cipher_ECDHE_RSA_WITH_AES_128_GCM_SHA256 instead"+    #-}++-- TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384+cipher_ECDHE_RSA_WITH_AES_256_GCM_SHA384 :: Cipher+cipher_ECDHE_RSA_WITH_AES_256_GCM_SHA384 =+    Cipher+        { cipherID = 0xC030+        , cipherName = "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"+        , cipherBulk = bulk_aes256gcm+        , cipherHash = SHA384+        , cipherPRFHash = Just SHA384+        , cipherKeyExchange = CipherKeyExchange_ECDHE_RSA+        , cipherMinVer = Just TLS12 -- RFC 5289+        }++cipher_ECDHE_RSA_AES256GCM_SHA384 :: Cipher+cipher_ECDHE_RSA_AES256GCM_SHA384 = cipher_ECDHE_RSA_WITH_AES_256_GCM_SHA384+{-# DEPRECATED+    cipher_ECDHE_RSA_AES256GCM_SHA384+    "Use cipher_ECDHE_RSA_WITH_AES_256_GCM_SHA384 instead"+    #-}++----------------------------------------------------------------+-- CCM/ECC: RFC 7251++-- TLS_ECDHE_ECDSA_WITH_AES_128_CCM+cipher_ECDHE_ECDSA_WITH_AES_128_CCM :: Cipher+cipher_ECDHE_ECDSA_WITH_AES_128_CCM =+    Cipher+        { cipherID = 0xC0AC+        , cipherName = "TLS_ECDHE_ECDSA_WITH_AES_128_CCM"+        , cipherBulk = bulk_aes128ccm+        , cipherHash = SHA256+        , cipherPRFHash = Just SHA256+        , cipherKeyExchange = CipherKeyExchange_ECDHE_ECDSA+        , cipherMinVer = Just TLS12 -- RFC 7251+        }++cipher_ECDHE_ECDSA_AES128CCM_SHA256 :: Cipher+cipher_ECDHE_ECDSA_AES128CCM_SHA256 = cipher_ECDHE_ECDSA_WITH_AES_128_CCM+{-# DEPRECATED+    cipher_ECDHE_ECDSA_AES128CCM_SHA256+    "User cipher_ECDHE_ECDSA_WITH_AES_128_CCM instead"+    #-}++-- TLS_ECDHE_ECDSA_WITH_AES_256_CCM+cipher_ECDHE_ECDSA_WITH_AES_256_CCM :: Cipher+cipher_ECDHE_ECDSA_WITH_AES_256_CCM =+    Cipher+        { cipherID = 0xC0AD+        , cipherName = "TLS_ECDHE_ECDSA_WITH_AES_256_CCM"+        , cipherBulk = bulk_aes256ccm+        , cipherHash = SHA256+        , cipherPRFHash = Just SHA256+        , cipherKeyExchange = CipherKeyExchange_ECDHE_ECDSA+        , cipherMinVer = Just TLS12 -- RFC 7251+        }++cipher_ECDHE_ECDSA_AES256CCM_SHA256 :: Cipher+cipher_ECDHE_ECDSA_AES256CCM_SHA256 = cipher_ECDHE_ECDSA_WITH_AES_256_CCM+{-# DEPRECATED+    cipher_ECDHE_ECDSA_AES256CCM_SHA256+    "Use cipher_ECDHE_ECDSA_WITH_AES_256_CCM instead"+    #-}++-- TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8+cipher_ECDHE_ECDSA_WITH_AES_128_CCM_8 :: Cipher+cipher_ECDHE_ECDSA_WITH_AES_128_CCM_8 =+    Cipher+        { cipherID = 0xC0AE+        , cipherName = "TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8"+        , cipherBulk = bulk_aes128ccm8+        , cipherHash = SHA256+        , cipherPRFHash = Just SHA256+        , cipherKeyExchange = CipherKeyExchange_ECDHE_ECDSA+        , cipherMinVer = Just TLS12 -- RFC 7251+        }++cipher_ECDHE_ECDSA_AES128CCM8_SHA256 :: Cipher+cipher_ECDHE_ECDSA_AES128CCM8_SHA256 = cipher_ECDHE_ECDSA_WITH_AES_128_CCM_8+{-# DEPRECATED+    cipher_ECDHE_ECDSA_AES128CCM8_SHA256+    "Use cipher_ECDHE_ECDSA_WITH_AES_128_CCM_8 instead"+    #-}++-- TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8+cipher_ECDHE_ECDSA_WITH_AES_256_CCM_8 :: Cipher+cipher_ECDHE_ECDSA_WITH_AES_256_CCM_8 =+    Cipher+        { cipherID = 0xC0AF+        , cipherName = "TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8"+        , cipherBulk = bulk_aes256ccm8+        , cipherHash = SHA256+        , cipherPRFHash = Just SHA256+        , cipherKeyExchange = CipherKeyExchange_ECDHE_ECDSA+        , cipherMinVer = Just TLS12 -- RFC 7251+        }++cipher_ECDHE_ECDSA_AES256CCM8_SHA256 :: Cipher+cipher_ECDHE_ECDSA_AES256CCM8_SHA256 = cipher_ECDHE_ECDSA_WITH_AES_256_CCM_8+{-# DEPRECATED+    cipher_ECDHE_ECDSA_AES256CCM8_SHA256+    "Use cipher_ECDHE_ECDSA_WITH_AES_256_CCM_8 instead"+    #-}++----------------------------------------------------------------+-- RFC 7905++-- TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256+cipher_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 :: Cipher+cipher_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 =+    Cipher+        { cipherID = 0xCCA8+        , cipherName = "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256"+        , cipherBulk = bulk_chacha20poly1305+        , cipherHash = SHA256+        , cipherPRFHash = Just SHA256+        , cipherKeyExchange = CipherKeyExchange_ECDHE_RSA+        , cipherMinVer = Just TLS12+        }++cipher_ECDHE_RSA_CHACHA20POLY1305_SHA256 :: Cipher+cipher_ECDHE_RSA_CHACHA20POLY1305_SHA256 = cipher_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256+{-# DEPRECATED+    cipher_ECDHE_RSA_CHACHA20POLY1305_SHA256+    "Use cipher_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 instead"+    #-}++-- TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256+cipher_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 :: Cipher+cipher_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 =+    Cipher+        { cipherID = 0xCCA9+        , cipherName = "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256"+        , cipherBulk = bulk_chacha20poly1305+        , cipherHash = SHA256+        , cipherPRFHash = Just SHA256+        , cipherKeyExchange = CipherKeyExchange_ECDHE_ECDSA+        , cipherMinVer = Just TLS12+        }++cipher_ECDHE_ECDSA_CHACHA20POLY1305_SHA256 :: Cipher+cipher_ECDHE_ECDSA_CHACHA20POLY1305_SHA256 = cipher_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256+{-# DEPRECATED+    cipher_ECDHE_ECDSA_CHACHA20POLY1305_SHA256+    "Use cipher_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 instead"+    #-}++-- TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256+cipher_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 :: Cipher+cipher_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 =+    Cipher+        { cipherID = 0xCCAA+        , cipherName = "TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256"+        , cipherBulk = bulk_chacha20poly1305+        , cipherHash = SHA256+        , cipherPRFHash = Just SHA256+        , cipherKeyExchange = CipherKeyExchange_DHE_RSA+        , cipherMinVer = Just TLS12+        }++cipher_DHE_RSA_CHACHA20POLY1305_SHA256 :: Cipher+cipher_DHE_RSA_CHACHA20POLY1305_SHA256 = cipher_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256+{-# DEPRECATED+    cipher_DHE_RSA_CHACHA20POLY1305_SHA256+    "Use cipher_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 instead"+    #-}++----------------------------------------------------------------+----------------------------------------------------------------++data CipherSet+    = SetAead [Cipher] [Cipher] [Cipher] -- gcm, chacha, ccm+    | SetOther [Cipher]++-- Preference between AEAD ciphers having equivalent properties is based on+-- hardware-acceleration support in the crypton implementation.+sortOptimized :: [CipherSet] -> [Cipher]+sortOptimized = concatMap f+  where+    f (SetAead gcm chacha ccm)+        | AESNI `notElem` processorOptions = chacha ++ gcm ++ ccm+        | PCLMUL `notElem` processorOptions = ccm ++ chacha ++ gcm+        | otherwise = gcm ++ ccm ++ chacha+    f (SetOther ciphers) = ciphers++-- Order which is deterministic but not optimized for the CPU.+sortDeterministic :: [CipherSet] -> [Cipher]+sortDeterministic = concatMap f+  where+    f (SetAead gcm chacha ccm) = gcm ++ chacha ++ ccm+    f (SetOther ciphers) = ciphers++----------------------------------------------------------------++aes128ccm :: BulkDirection -> BulkKey -> BulkAEAD+aes128ccm BulkEncrypt key =+    let ctx = noFail (cipherInit key) :: AES128+     in ( \nonce d ad ->+            let mode = AEAD_CCM (B.length d) CCM_M16 CCM_L3+                aeadIni = noFail (aeadInit mode ctx nonce)+             in swap $ aeadSimpleEncrypt aeadIni ad d 16+        )+aes128ccm BulkDecrypt key =+    let ctx = noFail (cipherInit key) :: AES128+     in ( \nonce d ad ->+            let mode = AEAD_CCM (B.length d) CCM_M16 CCM_L3+                aeadIni = noFail (aeadInit mode ctx nonce)+             in simpleDecrypt aeadIni ad d 16+        )++aes128ccm8 :: BulkDirection -> BulkKey -> BulkAEAD+aes128ccm8 BulkEncrypt key =+    let ctx = noFail (cipherInit key) :: AES128+     in ( \nonce d ad ->+            let mode = AEAD_CCM (B.length d) CCM_M8 CCM_L3+                aeadIni = noFail (aeadInit mode ctx nonce)+             in swap $ aeadSimpleEncrypt aeadIni ad d 8+        )+aes128ccm8 BulkDecrypt key =+    let ctx = noFail (cipherInit key) :: AES128+     in ( \nonce d ad ->+            let mode = AEAD_CCM (B.length d) CCM_M8 CCM_L3+                aeadIni = noFail (aeadInit mode ctx nonce)+             in simpleDecrypt aeadIni ad d 8+        )++aes128gcm :: BulkDirection -> BulkKey -> BulkAEAD+aes128gcm BulkEncrypt key =+    let ctx = noFail (cipherInit key) :: AES128+     in ( \nonce d ad ->+            let aeadIni = noFail (aeadInit AEAD_GCM ctx nonce)+             in swap $ aeadSimpleEncrypt aeadIni ad d 16+        )+aes128gcm BulkDecrypt key =+    let ctx = noFail (cipherInit key) :: AES128+     in ( \nonce d ad ->+            let aeadIni = noFail (aeadInit AEAD_GCM ctx nonce)+             in simpleDecrypt aeadIni ad d 16+        )++aes256ccm :: BulkDirection -> BulkKey -> BulkAEAD+aes256ccm BulkEncrypt key =+    let ctx = noFail (cipherInit key) :: AES256+     in ( \nonce d ad ->+            let mode = AEAD_CCM (B.length d) CCM_M16 CCM_L3+                aeadIni = noFail (aeadInit mode ctx nonce)+             in swap $ aeadSimpleEncrypt aeadIni ad d 16+        )+aes256ccm BulkDecrypt key =+    let ctx = noFail (cipherInit key) :: AES256+     in ( \nonce d ad ->+            let mode = AEAD_CCM (B.length d) CCM_M16 CCM_L3+                aeadIni = noFail (aeadInit mode ctx nonce)+             in simpleDecrypt aeadIni ad d 16+        )++aes256ccm8 :: BulkDirection -> BulkKey -> BulkAEAD+aes256ccm8 BulkEncrypt key =+    let ctx = noFail (cipherInit key) :: AES256+     in ( \nonce d ad ->+            let mode = AEAD_CCM (B.length d) CCM_M8 CCM_L3+                aeadIni = noFail (aeadInit mode ctx nonce)+             in swap $ aeadSimpleEncrypt aeadIni ad d 8+        )+aes256ccm8 BulkDecrypt key =+    let ctx = noFail (cipherInit key) :: AES256+     in ( \nonce d ad ->+            let mode = AEAD_CCM (B.length d) CCM_M8 CCM_L3+                aeadIni = noFail (aeadInit mode ctx nonce)+             in simpleDecrypt aeadIni ad d 8+        )++aes256gcm :: BulkDirection -> BulkKey -> BulkAEAD+aes256gcm BulkEncrypt key =+    let ctx = noFail (cipherInit key) :: AES256+     in ( \nonce d ad ->+            let aeadIni = noFail (aeadInit AEAD_GCM ctx nonce)+             in swap $ aeadSimpleEncrypt aeadIni ad d 16+        )+aes256gcm BulkDecrypt key =+    let ctx = noFail (cipherInit key) :: AES256+     in ( \nonce d ad ->+            let aeadIni = noFail (aeadInit AEAD_GCM ctx nonce)+             in simpleDecrypt aeadIni ad d 16+        )++simpleDecrypt+    :: AEAD cipher -> ByteString -> ByteString -> Int -> (ByteString, AuthTag)+simpleDecrypt aeadIni header input taglen = (output, tag)+  where+    aead = aeadAppendHeader aeadIni header+    (output, aeadFinal) = aeadDecrypt aead input+    tag = aeadFinalize aeadFinal taglen++noFail :: CryptoFailable a -> a+noFail = throwCryptoError++chacha20poly1305 :: BulkDirection -> BulkKey -> BulkAEAD+chacha20poly1305 BulkEncrypt key nonce =+    let st = noFail (ChaChaPoly1305.nonce12 nonce >>= ChaChaPoly1305.initialize key)+     in ( \input ad ->+            let st2 = ChaChaPoly1305.finalizeAAD (ChaChaPoly1305.appendAAD ad st)+                (output, st3) = ChaChaPoly1305.encrypt input st2+                Poly1305.Auth tag = ChaChaPoly1305.finalize st3+             in (output, AuthTag tag)+        )+chacha20poly1305 BulkDecrypt key nonce =+    let st = noFail (ChaChaPoly1305.nonce12 nonce >>= ChaChaPoly1305.initialize key)+     in ( \input ad ->+            let st2 = ChaChaPoly1305.finalizeAAD (ChaChaPoly1305.appendAAD ad st)+                (output, st3) = ChaChaPoly1305.decrypt input st2+                Poly1305.Auth tag = ChaChaPoly1305.finalize st3+             in (output, AuthTag tag)+        )++----------------------------------------------------------------++bulk_aes128ccm :: Bulk+bulk_aes128ccm =+    Bulk+        { bulkName = "AES128CCM"+        , bulkKeySize = 16 -- RFC 5116 Sec 5.1: K_LEN+        , bulkIVSize = 4 -- RFC 6655 CCMNonce.salt, fixed_iv_length+        , bulkExplicitIV = 8+        , bulkAuthTagLen = 16+        , bulkBlockSize = 0 -- dummy, not used+        , bulkF = BulkAeadF aes128ccm+        }++bulk_aes128ccm8 :: Bulk+bulk_aes128ccm8 =+    Bulk+        { bulkName = "AES128CCM8"+        , bulkKeySize = 16 -- RFC 5116 Sec 5.1: K_LEN+        , bulkIVSize = 4 -- RFC 6655 CCMNonce.salt, fixed_iv_length+        , bulkExplicitIV = 8+        , bulkAuthTagLen = 8+        , bulkBlockSize = 0 -- dummy, not used+        , bulkF = BulkAeadF aes128ccm8+        }++bulk_aes128gcm :: Bulk+bulk_aes128gcm =+    Bulk+        { bulkName = "AES128GCM"+        , bulkKeySize = 16 -- RFC 5116 Sec 5.1: K_LEN+        , bulkIVSize = 4 -- RFC 5288 GCMNonce.salt, fixed_iv_length+        , bulkExplicitIV = 8+        , bulkAuthTagLen = 16+        , bulkBlockSize = 0 -- dummy, not used+        , bulkF = BulkAeadF aes128gcm+        }++bulk_aes256ccm :: Bulk+bulk_aes256ccm =+    Bulk+        { bulkName = "AES256CCM"+        , bulkKeySize = 32 -- RFC 5116 Sec 5.1: K_LEN+        , bulkIVSize = 4 -- RFC 6655 CCMNonce.salt, fixed_iv_length+        , bulkExplicitIV = 8+        , bulkAuthTagLen = 16+        , bulkBlockSize = 0 -- dummy, not used+        , bulkF = BulkAeadF aes256ccm+        }++bulk_aes256ccm8 :: Bulk+bulk_aes256ccm8 =+    Bulk+        { bulkName = "AES256CCM8"+        , bulkKeySize = 32 -- RFC 5116 Sec 5.1: K_LEN+        , bulkIVSize = 4 -- RFC 6655 CCMNonce.salt, fixed_iv_length+        , bulkExplicitIV = 8+        , bulkAuthTagLen = 8+        , bulkBlockSize = 0 -- dummy, not used+        , bulkF = BulkAeadF aes256ccm8+        }++bulk_aes256gcm :: Bulk+bulk_aes256gcm =+    Bulk+        { bulkName = "AES256GCM"+        , bulkKeySize = 32 -- RFC 5116 Sec 5.1: K_LEN+        , bulkIVSize = 4 -- RFC 5288 GCMNonce.salt, fixed_iv_length+        , bulkExplicitIV = 8+        , bulkAuthTagLen = 16+        , bulkBlockSize = 0 -- dummy, not used+        , bulkF = BulkAeadF aes256gcm+        }++bulk_chacha20poly1305 :: Bulk+bulk_chacha20poly1305 =+    Bulk+        { bulkName = "CHACHA20POLY1305"+        , bulkKeySize = 32+        , bulkIVSize = 12 -- RFC 7905 section 2, fixed_iv_length+        , bulkExplicitIV = 0+        , bulkAuthTagLen = 16+        , bulkBlockSize = 0 -- dummy, not used+        , bulkF = BulkAeadF chacha20poly1305+        }++-- TLS13 bulks are same as TLS12 except they never have explicit IV+bulk_aes128gcm_13 :: Bulk+bulk_aes128gcm_13 = bulk_aes128gcm{bulkIVSize = 12, bulkExplicitIV = 0}++bulk_aes256gcm_13 :: Bulk+bulk_aes256gcm_13 = bulk_aes256gcm{bulkIVSize = 12, bulkExplicitIV = 0}++bulk_aes128ccm_13 :: Bulk+bulk_aes128ccm_13 = bulk_aes128ccm{bulkIVSize = 12, bulkExplicitIV = 0}++bulk_aes128ccm8_13 :: Bulk+bulk_aes128ccm8_13 = bulk_aes128ccm8{bulkIVSize = 12, bulkExplicitIV = 0}
+ Network/TLS/Extra/CipherCBC.hs view
@@ -0,0 +1,201 @@+module Network.TLS.Extra.CipherCBC (+    -- * TLS 1.2 CBC ciphers with PFS and SHA2+    ciphersuite_pfs_sha2_cbc,+    ciphersuite_ecdhe_sha2_cbc,+    ciphersuite_dhe_rsa_sha2_cbc,++    -- ** Individual CBC ciphers+    cipher_DHE_RSA_AES128_SHA256,+    cipher_DHE_RSA_AES256_SHA256,+    cipher_ECDHE_RSA_AES128CBC_SHA256,+    cipher_ECDHE_RSA_AES256CBC_SHA384,+    cipher_ECDHE_ECDSA_AES128CBC_SHA256,+) where++import Crypto.Cipher.AES+import Crypto.Cipher.Types hiding (Cipher, cipherName)+import Crypto.Error+-- import Crypto.System.CPU+import qualified Data.ByteString as B++import Network.TLS.Cipher+import Network.TLS.Imports+import Network.TLS.Types hiding (IV)++----------------------------------------------------------------++-- | TLS 1.2 AES CBC ciphers with DHE or ECDHE key exchange, ECDSA or RSA+-- authentication and a SHA256 or SHA2384 MAC.+-- For legacy applications only, deprecated in HTTPS.+ciphersuite_pfs_sha2_cbc :: [Cipher]+ciphersuite_pfs_sha2_cbc =+    [ cipher_ECDHE_ECDSA_AES128CBC_SHA256+    , cipher_ECDHE_ECDSA_AES256CBC_SHA384+    , cipher_ECDHE_RSA_AES128CBC_SHA256+    , cipher_ECDHE_RSA_AES256CBC_SHA384+    , cipher_DHE_RSA_AES128_SHA256+    , cipher_DHE_RSA_AES256_SHA256+    ]++-- | TLS 1.2 AES CBC ciphers with ECDHE key exchange, ECDSA or RSA+-- authentication and a SHA256 or SHA2384 MAC.+-- For legacy applications only, deprecated in HTTPS.+ciphersuite_ecdhe_sha2_cbc :: [Cipher]+ciphersuite_ecdhe_sha2_cbc =+    [ cipher_ECDHE_ECDSA_AES128CBC_SHA256+    , cipher_ECDHE_ECDSA_AES256CBC_SHA384+    , cipher_ECDHE_RSA_AES128CBC_SHA256+    , cipher_ECDHE_RSA_AES256CBC_SHA384+    ]++-- | TLS 1.2 AES CBC ciphers with DHE key exchange, RSA authentication and a+-- SHA256 MAC.+-- For legacy applications only, deprecated in HTTPS.+ciphersuite_dhe_rsa_sha2_cbc :: [Cipher]+ciphersuite_dhe_rsa_sha2_cbc =+    [ cipher_DHE_RSA_AES256_SHA256+    , cipher_DHE_RSA_AES128_SHA256+    ]++----------------------------------------------------------------++-- | TLS 1.2 AES128 CBC, with DHE key exchange, RSA authentication and a SHA256 MAC.+-- For legacy applications only, deprecated in HTTPS.+cipher_DHE_RSA_AES128_SHA256 :: Cipher+cipher_DHE_RSA_AES128_SHA256 =+    Cipher+        { cipherID = 0x0067+        , cipherName = "DHE-RSA-AES128-SHA256"+        , cipherBulk = bulk_aes128+        , cipherHash = SHA256+        , cipherPRFHash = Just SHA256+        , cipherKeyExchange = CipherKeyExchange_DHE_RSA+        , cipherMinVer = Just TLS12 -- RFC 5288 Sec 4+        }++-- | TLS 1.2 AES256 CBC, with DHE key exchange, RSA authentication and a SHA256 MAC.+-- For legacy applications only, deprecated in HTTPS.+cipher_DHE_RSA_AES256_SHA256 :: Cipher+cipher_DHE_RSA_AES256_SHA256 =+    cipher_DHE_RSA_AES128_SHA256+        { cipherID = 0x006B+        , cipherName = "DHE-RSA-AES256-SHA256"+        , cipherBulk = bulk_aes256+        }++-- | TLS 1.2 AES128 CBC, with ECDHE key exchange, RSA authentication and a SHA256 MAC.+-- For legacy applications only, deprecated in HTTPS.+cipher_ECDHE_RSA_AES128CBC_SHA256 :: Cipher+cipher_ECDHE_RSA_AES128CBC_SHA256 =+    Cipher+        { cipherID = 0xC027+        , cipherName = "ECDHE-RSA-AES128CBC-SHA256"+        , cipherBulk = bulk_aes128+        , cipherHash = SHA256+        , cipherPRFHash = Just SHA256+        , cipherKeyExchange = CipherKeyExchange_ECDHE_RSA+        , cipherMinVer = Just TLS12 -- RFC 5288 Sec 4+        }++-- | TLS 1.2 AES256 CBC, with ECDHE key exchange, RSA authentication and a SHA384 MAC.+-- For legacy applications only, deprecated in HTTPS.+cipher_ECDHE_RSA_AES256CBC_SHA384 :: Cipher+cipher_ECDHE_RSA_AES256CBC_SHA384 =+    Cipher+        { cipherID = 0xC028+        , cipherName = "ECDHE-RSA-AES256CBC-SHA384"+        , cipherBulk = bulk_aes256+        , cipherHash = SHA384+        , cipherPRFHash = Just SHA384+        , cipherKeyExchange = CipherKeyExchange_ECDHE_RSA+        , cipherMinVer = Just TLS12 -- RFC 5288 Sec 4+        }++-- | TLS 1.2 AES128 CBC, with ECDHE key exchange, ECDSA authentication and a SHA256 MAC.+-- For legacy applications only, deprecated in HTTPS.+cipher_ECDHE_ECDSA_AES128CBC_SHA256 :: Cipher+cipher_ECDHE_ECDSA_AES128CBC_SHA256 =+    Cipher+        { cipherID = 0xc023+        , cipherName = "ECDHE-ECDSA-AES128CBC-SHA256"+        , cipherBulk = bulk_aes128+        , cipherHash = SHA256+        , cipherPRFHash = Just SHA256+        , cipherKeyExchange = CipherKeyExchange_ECDHE_ECDSA+        , cipherMinVer = Just TLS12 -- RFC 5289+        }++-- | TLS 1.2 AES256 CBC, with ECDHE key exchange, ECDSA authentication and a SHA384 MAC.+-- For legacy applications only, deprecated in HTTPS.+cipher_ECDHE_ECDSA_AES256CBC_SHA384 :: Cipher+cipher_ECDHE_ECDSA_AES256CBC_SHA384 =+    Cipher+        { cipherID = 0xC024+        , cipherName = "ECDHE-ECDSA-AES256CBC-SHA384"+        , cipherBulk = bulk_aes256+        , cipherHash = SHA384+        , cipherPRFHash = Just SHA384+        , cipherKeyExchange = CipherKeyExchange_ECDHE_ECDSA+        , cipherMinVer = Just TLS12 -- RFC 5289+        }++----------------------------------------------------------------++aes128cbc :: BulkDirection -> BulkKey -> BulkBlock+aes128cbc BulkEncrypt key =+    let ctx = noFail (cipherInit key) :: AES128+     in ( \iv input ->+            let output = cbcEncrypt ctx (makeIV_ iv) input in (output, takelast 16 output)+        )+aes128cbc BulkDecrypt key =+    let ctx = noFail (cipherInit key) :: AES128+     in ( \iv input ->+            let output = cbcDecrypt ctx (makeIV_ iv) input in (output, takelast 16 input)+        )++aes256cbc :: BulkDirection -> BulkKey -> BulkBlock+aes256cbc BulkEncrypt key =+    let ctx = noFail (cipherInit key) :: AES256+     in ( \iv input ->+            let output = cbcEncrypt ctx (makeIV_ iv) input in (output, takelast 16 output)+        )+aes256cbc BulkDecrypt key =+    let ctx = noFail (cipherInit key) :: AES256+     in ( \iv input ->+            let output = cbcDecrypt ctx (makeIV_ iv) input in (output, takelast 16 input)+        )++makeIV_ :: BlockCipher a => B.ByteString -> IV a+makeIV_ = fromMaybe (error "makeIV_") . makeIV++takelast :: Int -> B.ByteString -> B.ByteString+takelast i b = B.drop (B.length b - i) b++noFail :: CryptoFailable a -> a+noFail = throwCryptoError++----------------------------------------------------------------++bulk_aes128 :: Bulk+bulk_aes128 =+    Bulk+        { bulkName = "AES128"+        , bulkKeySize = 16+        , bulkIVSize = 16+        , bulkExplicitIV = 0+        , bulkAuthTagLen = 0+        , bulkBlockSize = 16+        , bulkF = BulkBlockF aes128cbc+        }++bulk_aes256 :: Bulk+bulk_aes256 =+    Bulk+        { bulkName = "AES256"+        , bulkKeySize = 32+        , bulkIVSize = 16+        , bulkExplicitIV = 0+        , bulkAuthTagLen = 0+        , bulkBlockSize = 16+        , bulkF = BulkBlockF aes256cbc+        }
Network/TLS/Extra/FFDHE.hs view
@@ -15,48 +15,58 @@ --   defined in RFC 7919. --   The estimated symmetric-equivalent strength is 103 bits. ffdhe2048 :: DHParams-ffdhe2048 = Params {-    params_p = 0xFFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF97D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD65612433F51F5F066ED0856365553DED1AF3B557135E7F57C935984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE73530ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FBB96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB190B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F619172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD733BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA886B423861285C97FFFFFFFFFFFFFFFF-  , params_g = 2-  , params_bits = 2048-  }+ffdhe2048 =+    Params+        { params_p =+            0xFFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF97D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD65612433F51F5F066ED0856365553DED1AF3B557135E7F57C935984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE73530ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FBB96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB190B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F619172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD733BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA886B423861285C97FFFFFFFFFFFFFFFF+        , params_g = 2+        , params_bits = 2048+        }  -- | 3072 bits finite field Diffie-Hellman ephemeral parameters --   defined in RFC 7919. --   The estimated symmetric-equivalent strength is 125 bits. ffdhe3072 :: DHParams-ffdhe3072 = Params {-    params_p = 0xFFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF97D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD65612433F51F5F066ED0856365553DED1AF3B557135E7F57C935984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE73530ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FBB96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB190B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F619172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD733BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C023861B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91CAEFE130985139270B4130C93BC437944F4FD4452E2D74DD364F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0DABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF3C1B20EE3FD59D7C25E41D2B66C62E37FFFFFFFFFFFFFFFF-  , params_g = 2-  , params_bits = 3072-  }+ffdhe3072 =+    Params+        { params_p =+            0xFFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF97D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD65612433F51F5F066ED0856365553DED1AF3B557135E7F57C935984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE73530ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FBB96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB190B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F619172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD733BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C023861B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91CAEFE130985139270B4130C93BC437944F4FD4452E2D74DD364F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0DABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF3C1B20EE3FD59D7C25E41D2B66C62E37FFFFFFFFFFFFFFFF+        , params_g = 2+        , params_bits = 3072+        }  -- | 4096 bits finite field Diffie-Hellman ephemeral parameters --   defined in RFC 7919. --   The estimated symmetric-equivalent strength is 150 bits. ffdhe4096 :: DHParams-ffdhe4096 = Params {-    params_p = 0xFFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF97D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD65612433F51F5F066ED0856365553DED1AF3B557135E7F57C935984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE73530ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FBB96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB190B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F619172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD733BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C023861B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91CAEFE130985139270B4130C93BC437944F4FD4452E2D74DD364F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0DABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF3C1B20EE3FD59D7C25E41D2B669E1EF16E6F52C3164DF4FB7930E9E4E58857B6AC7D5F42D69F6D187763CF1D5503400487F55BA57E31CC7A7135C886EFB4318AED6A1E012D9E6832A907600A918130C46DC778F971AD0038092999A333CB8B7A1A1DB93D7140003C2A4ECEA9F98D0ACC0A8291CDCEC97DCF8EC9B55A7F88A46B4DB5A851F44182E1C68A007E5E655F6AFFFFFFFFFFFFFFFF-  , params_g = 2-  , params_bits = 4096-  }+ffdhe4096 =+    Params+        { params_p =+            0xFFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF97D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD65612433F51F5F066ED0856365553DED1AF3B557135E7F57C935984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE73530ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FBB96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB190B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F619172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD733BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C023861B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91CAEFE130985139270B4130C93BC437944F4FD4452E2D74DD364F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0DABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF3C1B20EE3FD59D7C25E41D2B669E1EF16E6F52C3164DF4FB7930E9E4E58857B6AC7D5F42D69F6D187763CF1D5503400487F55BA57E31CC7A7135C886EFB4318AED6A1E012D9E6832A907600A918130C46DC778F971AD0038092999A333CB8B7A1A1DB93D7140003C2A4ECEA9F98D0ACC0A8291CDCEC97DCF8EC9B55A7F88A46B4DB5A851F44182E1C68A007E5E655F6AFFFFFFFFFFFFFFFF+        , params_g = 2+        , params_bits = 4096+        }  -- | 6144 bits finite field Diffie-Hellman ephemeral parameters --   defined in RFC 7919. --   The estimated symmetric-equivalent strength is 175 bits. ffdhe6144 :: DHParams-ffdhe6144 = Params {-    params_p = 0xFFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF97D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD65612433F51F5F066ED0856365553DED1AF3B557135E7F57C935984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE73530ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FBB96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB190B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F619172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD733BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C023861B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91CAEFE130985139270B4130C93BC437944F4FD4452E2D74DD364F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0DABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF3C1B20EE3FD59D7C25E41D2B669E1EF16E6F52C3164DF4FB7930E9E4E58857B6AC7D5F42D69F6D187763CF1D5503400487F55BA57E31CC7A7135C886EFB4318AED6A1E012D9E6832A907600A918130C46DC778F971AD0038092999A333CB8B7A1A1DB93D7140003C2A4ECEA9F98D0ACC0A8291CDCEC97DCF8EC9B55A7F88A46B4DB5A851F44182E1C68A007E5E0DD9020BFD64B645036C7A4E677D2C38532A3A23BA4442CAF53EA63BB454329B7624C8917BDD64B1C0FD4CB38E8C334C701C3ACDAD0657FCCFEC719B1F5C3E4E46041F388147FB4CFDB477A52471F7A9A96910B855322EDB6340D8A00EF092350511E30ABEC1FFF9E3A26E7FB29F8C183023C3587E38DA0077D9B4763E4E4B94B2BBC194C6651E77CAF992EEAAC0232A281BF6B3A739C1226116820AE8DB5847A67CBEF9C9091B462D538CD72B03746AE77F5E62292C311562A846505DC82DB854338AE49F5235C95B91178CCF2DD5CACEF403EC9D1810C6272B045B3B71F9DC6B80D63FDD4A8E9ADB1E6962A69526D43161C1A41D570D7938DAD4A40E329CD0E40E65FFFFFFFFFFFFFFFF-  , params_g = 2-  , params_bits = 6144-  }+ffdhe6144 =+    Params+        { params_p =+            0xFFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF97D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD65612433F51F5F066ED0856365553DED1AF3B557135E7F57C935984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE73530ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FBB96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB190B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F619172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD733BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C023861B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91CAEFE130985139270B4130C93BC437944F4FD4452E2D74DD364F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0DABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF3C1B20EE3FD59D7C25E41D2B669E1EF16E6F52C3164DF4FB7930E9E4E58857B6AC7D5F42D69F6D187763CF1D5503400487F55BA57E31CC7A7135C886EFB4318AED6A1E012D9E6832A907600A918130C46DC778F971AD0038092999A333CB8B7A1A1DB93D7140003C2A4ECEA9F98D0ACC0A8291CDCEC97DCF8EC9B55A7F88A46B4DB5A851F44182E1C68A007E5E0DD9020BFD64B645036C7A4E677D2C38532A3A23BA4442CAF53EA63BB454329B7624C8917BDD64B1C0FD4CB38E8C334C701C3ACDAD0657FCCFEC719B1F5C3E4E46041F388147FB4CFDB477A52471F7A9A96910B855322EDB6340D8A00EF092350511E30ABEC1FFF9E3A26E7FB29F8C183023C3587E38DA0077D9B4763E4E4B94B2BBC194C6651E77CAF992EEAAC0232A281BF6B3A739C1226116820AE8DB5847A67CBEF9C9091B462D538CD72B03746AE77F5E62292C311562A846505DC82DB854338AE49F5235C95B91178CCF2DD5CACEF403EC9D1810C6272B045B3B71F9DC6B80D63FDD4A8E9ADB1E6962A69526D43161C1A41D570D7938DAD4A40E329CD0E40E65FFFFFFFFFFFFFFFF+        , params_g = 2+        , params_bits = 6144+        }  -- | 8192 bits finite field Diffie-Hellman ephemeral parameters --   defined in RFC 7919. --   The estimated symmetric-equivalent strength is 192 bits. ffdhe8192 :: DHParams-ffdhe8192 = Params {-    params_p = 0xFFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF97D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD65612433F51F5F066ED0856365553DED1AF3B557135E7F57C935984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE73530ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FBB96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB190B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F619172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD733BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C023861B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91CAEFE130985139270B4130C93BC437944F4FD4452E2D74DD364F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0DABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF3C1B20EE3FD59D7C25E41D2B669E1EF16E6F52C3164DF4FB7930E9E4E58857B6AC7D5F42D69F6D187763CF1D5503400487F55BA57E31CC7A7135C886EFB4318AED6A1E012D9E6832A907600A918130C46DC778F971AD0038092999A333CB8B7A1A1DB93D7140003C2A4ECEA9F98D0ACC0A8291CDCEC97DCF8EC9B55A7F88A46B4DB5A851F44182E1C68A007E5E0DD9020BFD64B645036C7A4E677D2C38532A3A23BA4442CAF53EA63BB454329B7624C8917BDD64B1C0FD4CB38E8C334C701C3ACDAD0657FCCFEC719B1F5C3E4E46041F388147FB4CFDB477A52471F7A9A96910B855322EDB6340D8A00EF092350511E30ABEC1FFF9E3A26E7FB29F8C183023C3587E38DA0077D9B4763E4E4B94B2BBC194C6651E77CAF992EEAAC0232A281BF6B3A739C1226116820AE8DB5847A67CBEF9C9091B462D538CD72B03746AE77F5E62292C311562A846505DC82DB854338AE49F5235C95B91178CCF2DD5CACEF403EC9D1810C6272B045B3B71F9DC6B80D63FDD4A8E9ADB1E6962A69526D43161C1A41D570D7938DAD4A40E329CCFF46AAA36AD004CF600C8381E425A31D951AE64FDB23FCEC9509D43687FEB69EDD1CC5E0B8CC3BDF64B10EF86B63142A3AB8829555B2F747C932665CB2C0F1CC01BD70229388839D2AF05E454504AC78B7582822846C0BA35C35F5C59160CC046FD8251541FC68C9C86B022BB7099876A460E7451A8A93109703FEE1C217E6C3826E52C51AA691E0E423CFC99E9E31650C1217B624816CDAD9A95F9D5B8019488D9C0A0A1FE3075A577E23183F81D4A3F2FA4571EFC8CE0BA8A4FE8B6855DFE72B0A66EDED2FBABFBE58A30FAFABE1C5D71A87E2F741EF8C1FE86FEA6BBFDE530677F0D97D11D49F7A8443D0822E506A9F4614E011E2A94838FF88CD68C8BB7C5C6424CFFFFFFFFFFFFFFFF-  , params_g = 2-  , params_bits = 8192-  }+ffdhe8192 =+    Params+        { params_p =+            0xFFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF97D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD65612433F51F5F066ED0856365553DED1AF3B557135E7F57C935984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE73530ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FBB96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB190B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F619172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD733BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C023861B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91CAEFE130985139270B4130C93BC437944F4FD4452E2D74DD364F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0DABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF3C1B20EE3FD59D7C25E41D2B669E1EF16E6F52C3164DF4FB7930E9E4E58857B6AC7D5F42D69F6D187763CF1D5503400487F55BA57E31CC7A7135C886EFB4318AED6A1E012D9E6832A907600A918130C46DC778F971AD0038092999A333CB8B7A1A1DB93D7140003C2A4ECEA9F98D0ACC0A8291CDCEC97DCF8EC9B55A7F88A46B4DB5A851F44182E1C68A007E5E0DD9020BFD64B645036C7A4E677D2C38532A3A23BA4442CAF53EA63BB454329B7624C8917BDD64B1C0FD4CB38E8C334C701C3ACDAD0657FCCFEC719B1F5C3E4E46041F388147FB4CFDB477A52471F7A9A96910B855322EDB6340D8A00EF092350511E30ABEC1FFF9E3A26E7FB29F8C183023C3587E38DA0077D9B4763E4E4B94B2BBC194C6651E77CAF992EEAAC0232A281BF6B3A739C1226116820AE8DB5847A67CBEF9C9091B462D538CD72B03746AE77F5E62292C311562A846505DC82DB854338AE49F5235C95B91178CCF2DD5CACEF403EC9D1810C6272B045B3B71F9DC6B80D63FDD4A8E9ADB1E6962A69526D43161C1A41D570D7938DAD4A40E329CCFF46AAA36AD004CF600C8381E425A31D951AE64FDB23FCEC9509D43687FEB69EDD1CC5E0B8CC3BDF64B10EF86B63142A3AB8829555B2F747C932665CB2C0F1CC01BD70229388839D2AF05E454504AC78B7582822846C0BA35C35F5C59160CC046FD8251541FC68C9C86B022BB7099876A460E7451A8A93109703FEE1C217E6C3826E52C51AA691E0E423CFC99E9E31650C1217B624816CDAD9A95F9D5B8019488D9C0A0A1FE3075A577E23183F81D4A3F2FA4571EFC8CE0BA8A4FE8B6855DFE72B0A66EDED2FBABFBE58A30FAFABE1C5D71A87E2F741EF8C1FE86FEA6BBFDE530677F0D97D11D49F7A8443D0822E506A9F4614E011E2A94838FF88CD68C8BB7C5C6424CFFFFFFFFFFFFFFFF+        , params_g = 2+        , params_bits = 8192+        }
Network/TLS/Handshake.hs view
@@ -1,50 +1,33 @@--- |--- Module      : Network.TLS.Handshake--- License     : BSD-style--- Maintainer  : Vincent Hanquez <vincent@snarc.org>--- Stability   : experimental--- Portability : unknown----module Network.TLS.Handshake-    ( handshake-    , handshakeWith-    , handshakeClientWith-    , handshakeServerWith-    , handshakeClient-    , handshakeServer-    ) where+module Network.TLS.Handshake (+    handshake_,+    handshakeWith,+    handshakeClientWith,+    handshakeServerWith,+    handshakeClient,+    handshakeServer,+) where  import Network.TLS.Context.Internal-import Network.TLS.Struct-import Network.TLS.IO-import Network.TLS.Util (catchException)-import Network.TLS.Imports--import Network.TLS.Handshake.Common import Network.TLS.Handshake.Client+import Network.TLS.Handshake.Common import Network.TLS.Handshake.Server+import Network.TLS.Struct  import Control.Monad.State.Strict-import Control.Exception (IOException, catch, fromException) --- | Handshake for a new TLS connection--- This is to be called at the beginning of a connection, and during renegotiation-handshake :: MonadIO m => Context -> m ()-handshake ctx =-    liftIO $ handleException ctx $ withRWLock ctx (ctxDoHandshake ctx ctx)+handshake_ :: MonadIO m => Context -> m ()+handshake_ ctx =+    liftIO $+        withRWLock ctx $+            handleException ctx (doHandshake_ (ctxRoleParams ctx) ctx)  -- Handshake when requested by the remote end--- This is called automatically by 'recvData'-handshakeWith :: MonadIO m => Context -> Handshake -> m ()-handshakeWith ctx hs =-    liftIO $ handleException ctx $ withRWLock ctx $ ctxDoHandshakeWith ctx ctx hs--handleException :: Context -> IO () -> IO ()-handleException ctx f = catchException f $ \exception -> do-    let tlserror = fromMaybe (Error_Misc $ show exception) $ fromException exception-    setEstablished ctx False-    sendPacket ctx (errorToAlert tlserror) `catch` ignoreIOErr-    handshakeFailed tlserror-  where-    ignoreIOErr :: IOException -> IO ()-    ignoreIOErr _ = return ()+-- This is called automatically by 'recvData', in a context where the read lock+-- is already taken.  So contrary to 'handshake' above, here we only need to+-- call withWriteLock.+handshakeWith :: MonadIO m => Context -> HandshakeR -> m ()+handshakeWith ctx hsr =+    liftIO $+        withWriteLock ctx $+            handleException ctx $+                doHandshakeWith_ (ctxRoleParams ctx) ctx hsr
Network/TLS/Handshake/Certificate.hs view
@@ -1,31 +1,71 @@--- |--- Module      : Network.TLS.Handshake.Certificate--- License     : BSD-style--- Maintainer  : Vincent Hanquez <vincent@snarc.org>--- Stability   : experimental--- Portability : unknown----module Network.TLS.Handshake.Certificate-    ( certificateRejected-    , rejectOnException-    ) where+module Network.TLS.Handshake.Certificate (+    certificateRejected,+    badCertificate,+    rejectOnException,+    verifyLeafKeyUsage,+    verifyLeafKeyUsagePurpose,+    extractCAname,+) where +import Control.Exception (SomeException)+import Control.Monad (unless)+import Control.Monad.State.Strict+import Data.X509 (+    ExtExtendedKeyUsage (..),+    ExtKeyUsage (..),+    ExtKeyUsageFlag,+    ExtKeyUsagePurpose (..),+    extensionGet,+ )+ import Network.TLS.Context.Internal import Network.TLS.Struct import Network.TLS.X509-import Control.Monad.State.Strict-import Control.Exception (SomeException)  -- on certificate reject, throw an exception with the proper protocol alert error. certificateRejected :: MonadIO m => CertificateRejectReason -> m a certificateRejected CertificateRejectRevoked =-    throwCore $ Error_Protocol ("certificate is revoked", True, CertificateRevoked)+    throwCore $ Error_Protocol "certificate is revoked" CertificateRevoked certificateRejected CertificateRejectExpired =-    throwCore $ Error_Protocol ("certificate has expired", True, CertificateExpired)+    throwCore $ Error_Protocol "certificate has expired" CertificateExpired certificateRejected CertificateRejectUnknownCA =-    throwCore $ Error_Protocol ("certificate has unknown CA", True, UnknownCa)+    throwCore $ Error_Protocol "certificate has unknown CA" UnknownCa+certificateRejected CertificateRejectAbsent =+    throwCore $ Error_Protocol "certificate is missing" CertificateRequired certificateRejected (CertificateRejectOther s) =-    throwCore $ Error_Protocol ("certificate rejected: " ++ s, True, CertificateUnknown)+    throwCore $ Error_Protocol ("certificate rejected: " ++ s) CertificateUnknown +badCertificate :: MonadIO m => String -> m a+badCertificate msg = throwCore $ Error_Protocol msg BadCertificate+ rejectOnException :: SomeException -> IO CertificateUsage rejectOnException e = return $ CertificateUsageReject $ CertificateRejectOther $ show e++verifyLeafKeyUsage :: MonadIO m => [ExtKeyUsageFlag] -> CertificateChain -> m ()+verifyLeafKeyUsage _ (CertificateChain []) = return ()+verifyLeafKeyUsage validFlags (CertificateChain (signed : _)) =+    unless verified $+        badCertificate $+            "certificate is not allowed for any of " ++ show validFlags+  where+    cert = getCertificate signed+    verified =+        case extensionGet (certExtensions cert) of+            Nothing -> True -- unrestricted cert+            Just (ExtKeyUsage flags) -> any (`elem` validFlags) flags++verifyLeafKeyUsagePurpose+    :: MonadIO m => ExtKeyUsagePurpose -> CertificateChain -> m ()+verifyLeafKeyUsagePurpose _ (CertificateChain []) = return ()+verifyLeafKeyUsagePurpose validPurpose (CertificateChain (signed : _)) =+    unless verified $+        badCertificate $+            "certificate is not allowed for " ++ show validPurpose+  where+    cert = getCertificate signed+    verified = case extensionGet (certExtensions cert) of+        Nothing -> True+        Just (ExtExtendedKeyUsage purposes) -> validPurpose `elem` purposes++extractCAname :: SignedCertificate -> DistinguishedName+extractCAname cert = certSubjectDN $ getCertificate cert
Network/TLS/Handshake/Client.hs view
@@ -1,417 +1,177 @@ {-# LANGUAGE OverloadedStrings #-}--- |--- Module      : Network.TLS.Handshake.Client--- License     : BSD-style--- Maintainer  : Vincent Hanquez <vincent@snarc.org>--- Stability   : experimental--- Portability : unknown----module Network.TLS.Handshake.Client-    ( handshakeClient-    , handshakeClientWith-    ) where+{-# LANGUAGE RecordWildCards #-} -import Network.TLS.Crypto+module Network.TLS.Handshake.Client (+    handshakeClient,+    handshakeClientWith,+    postHandshakeAuthClientWith,+) where+ import Network.TLS.Context.Internal-import Network.TLS.Parameters-import Network.TLS.Struct-import Network.TLS.Cipher-import Network.TLS.Compression-import Network.TLS.Packet-import Network.TLS.ErrT+import Network.TLS.Crypto import Network.TLS.Extension+import Network.TLS.Handshake.Client.ClientHello+import Network.TLS.Handshake.Client.Common+import Network.TLS.Handshake.Client.ServerHello+import Network.TLS.Handshake.Client.TLS12+import Network.TLS.Handshake.Client.TLS13+import Network.TLS.Handshake.Common13+import Network.TLS.Handshake.State+import Network.TLS.Handshake.State13 import Network.TLS.IO import Network.TLS.Imports-import Network.TLS.State hiding (getNegotiatedProtocol) import Network.TLS.Measurement-import Network.TLS.Wire (encodeWord16)-import Network.TLS.Util (bytesEq, catchException)-import Network.TLS.Types-import Network.TLS.X509-import qualified Data.ByteString as B--import Control.Monad.State.Strict-import Control.Exception (SomeException)+import Network.TLS.Parameters+import Network.TLS.State+import Network.TLS.Struct -import Network.TLS.Handshake.Common-import Network.TLS.Handshake.Process-import Network.TLS.Handshake.Certificate-import Network.TLS.Handshake.Signature-import Network.TLS.Handshake.Key-import Network.TLS.Handshake.State+---------------------------------------------------------------- -handshakeClientWith :: ClientParams -> Context -> Handshake -> IO ()-handshakeClientWith cparams ctx HelloRequest = handshakeClient cparams ctx-handshakeClientWith _       _   _            = throwCore $ Error_Protocol ("unexpected handshake message received in handshakeClientWith", True, HandshakeFailure)+handshakeClientWith+    :: ClientParams -> Context -> HandshakeR -> IO ()+handshakeClientWith cparams ctx (HelloRequest, _b) = handshakeClient cparams ctx -- xxx+handshakeClientWith _ _ _ =+    throwCore $+        Error_Protocol+            "unexpected handshake message received in handshakeClientWith"+            HandshakeFailure  -- client part of handshake. send a bunch of handshake of client -- values intertwined with response from the server. handshakeClient :: ClientParams -> Context -> IO () handshakeClient cparams ctx = do-    updateMeasure ctx incrementNbHandshakes-    sentExtensions <- sendClientHello-    recvServerHello sentExtensions-    sessionResuming <- usingState_ ctx isSessionResuming-    if sessionResuming-        then sendChangeCipherAndFinish ctx ClientRole-        else do sendClientData cparams ctx-                sendChangeCipherAndFinish ctx ClientRole-                recvChangeCipherAndFinish ctx-    handshakeTerminate ctx-  where ciphers      = supportedCiphers $ ctxSupported ctx-        compressions = supportedCompressions $ ctxSupported ctx-        getExtensions = sequence [sniExtension-                                 ,secureReneg-                                 ,alpnExtension-                                 ,groupExtension-                                 ,ecPointExtension-                                 --,sessionTicketExtension-                                 ,signatureAlgExtension-                                 -- ,heartbeatExtension-                                 ]--        toExtensionRaw :: Extension e => e -> ExtensionRaw-        toExtensionRaw ext = ExtensionRaw (extensionID ext) (extensionEncode ext)--        secureReneg  =-                if supportedSecureRenegotiation $ ctxSupported ctx-                then usingState_ ctx (getVerifiedData ClientRole) >>= \vd -> return $ Just $ toExtensionRaw $ SecureRenegotiation vd Nothing-                else return Nothing-        alpnExtension = do-            mprotos <- onSuggestALPN $ clientHooks cparams-            case mprotos of-                Nothing -> return Nothing-                Just protos -> do-                    usingState_ ctx $ setClientALPNSuggest protos-                    return $ Just $ toExtensionRaw $ ApplicationLayerProtocolNegotiation protos-        sniExtension = if clientUseServerNameIndication cparams-                         then do let sni = fst $ clientServerIdentification cparams-                                 usingState_ ctx $ setClientSNI sni-                                 return $ Just $ toExtensionRaw $ ServerName [ServerNameHostName sni]-                         else return Nothing--        groupExtension = return $ Just $ toExtensionRaw $ NegotiatedGroups (supportedGroups $ ctxSupported ctx)-        ecPointExtension = return $ Just $ toExtensionRaw $ EcPointFormatsSupported [EcPointFormat_Uncompressed]-                                --[EcPointFormat_Uncompressed,EcPointFormat_AnsiX962_compressed_prime,EcPointFormat_AnsiX962_compressed_char2]-        --heartbeatExtension = return $ Just $ toExtensionRaw $ HeartBeat $ HeartBeat_PeerAllowedToSend-        --sessionTicketExtension = return $ Just $ toExtensionRaw $ SessionTicket--        signatureAlgExtension = return $ Just $ toExtensionRaw $ SignatureAlgorithms $ supportedHashSignatures $ clientSupported cparams--        sendClientHello = do-            crand <- ClientRandom <$> getStateRNG ctx 32-            let clientSession = Session . (fst <$>) $ clientWantSessionResume cparams-                highestVer = maximum $ supportedVersions $ ctxSupported ctx-            extensions <- catMaybes <$> getExtensions-            startHandshake ctx highestVer crand-            usingState_ ctx $ setVersionIfUnset highestVer-            sendPacket ctx $ Handshake-                [ ClientHello highestVer crand clientSession (map cipherID ciphers)-                              (map compressionID compressions) extensions Nothing-                ]-            return $ map (\(ExtensionRaw i _) -> i) extensions--        recvServerHello sentExts = runRecvState ctx recvState-          where recvState = RecvStateNext $ \p ->-                    case p of-                        Handshake hs -> onRecvStateHandshake ctx (RecvStateHandshake $ onServerHello ctx cparams sentExts) hs-                        Alert a      ->-                            case a of-                                [(AlertLevel_Warning, UnrecognizedName)] ->-                                    if clientUseServerNameIndication cparams-                                        then return recvState-                                        else throwAlert a-                                _ -> throwAlert a-                        _ -> fail ("unexepected type received. expecting handshake and got: " ++ show p)-                throwAlert a = usingState_ ctx $ throwError $ Error_Protocol ("expecting server hello, got alert : " ++ show a, True, HandshakeFailure)---- | send client Data after receiving all server data (hello/certificates/key).------       -> [certificate]---       -> client key exchange---       -> [cert verify]-sendClientData :: ClientParams -> Context -> IO ()-sendClientData cparams ctx = sendCertificate >> sendClientKeyXchg >> sendCertificateVerify+    grps <- case clientSessions cparams of+        [] ->+            return $+                Groups+                    { grpsSupported = groupsSupported+                    , grpsSelected = groupsSelected+                    }+        (_, sdata) : _ -> case sessionGroup sdata of+            Nothing ->+                -- TLS 1.2 or earlier+                return $+                    Groups+                        { grpsSupported = groupsSupported -- for ciphers+                        , grpsSelected = []+                        }+            Just grp+                | grp `elem` groupsSupported -> do+                    let supported = grp : filter (/= grp) groupsSupported+                    return $+                        Groups+                            { grpsSupported = supported+                            , grpsSelected = [grp]+                            }+                | otherwise -> throwCore $ Error_Misc "groupsSupported is incorrect"+    handshake cparams ctx grps Nothing   where-        -- When the server requests a client certificate, we-        -- fetch a certificate chain from the callback in the-        -- client parameters and send it to the server.-        -- Additionally, we store the private key associated-        -- with the first certificate in the chain for later-        -- use.-        ---        sendCertificate = do-            certRequested <- usingHState ctx getClientCertRequest-            case certRequested of-                Nothing ->-                    return ()--                Just req -> do-                    certChain <- liftIO $ (onCertificateRequest $ clientHooks cparams) req `catchException`-                                 throwMiscErrorOnException "certificate request callback failed"--                    usingHState ctx $ setClientCertSent False-                    case certChain of-                        Nothing                       -> sendPacket ctx $ Handshake [Certificates (CertificateChain [])]-                        Just (CertificateChain [], _) -> sendPacket ctx $ Handshake [Certificates (CertificateChain [])]-                        Just (cc@(CertificateChain (c:_)), pk) -> do-                            case certPubKey $ getCertificate c of-                                PubKeyRSA _ -> return ()-                                PubKeyDSA _ -> return ()-                                _           -> throwCore $ Error_Protocol ("no supported certificate type", True, HandshakeFailure)-                            usingHState ctx $ setPrivateKey pk-                            usingHState ctx $ setClientCertSent True-                            sendPacket ctx $ Handshake [Certificates cc]--        sendClientKeyXchg = do-            cipher <- usingHState ctx getPendingCipher-            ckx <- case cipherKeyExchange cipher of-                CipherKeyExchange_RSA -> do-                    clientVersion <- usingHState ctx $ gets hstClientVersion-                    (xver, prerand) <- usingState_ ctx $ (,) <$> getVersion <*> genRandom 46--                    let premaster = encodePreMasterSecret clientVersion prerand-                    usingHState ctx $ setMasterSecretFromPre xver ClientRole premaster-                    encryptedPreMaster <- 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 premaster-                        let extra = if xver < TLS10-                                        then B.empty-                                        else encodeWord16 $ fromIntegral $ B.length e-                        return $ extra `B.append` e-                    return $ CKX_RSA encryptedPreMaster-                CipherKeyExchange_DHE_RSA -> getCKX_DHE-                CipherKeyExchange_DHE_DSS -> getCKX_DHE-                CipherKeyExchange_ECDHE_RSA -> getCKX_ECDHE-                CipherKeyExchange_ECDHE_ECDSA -> getCKX_ECDHE-                _ -> throwCore $ Error_Protocol ("client key exchange unsupported type", True, HandshakeFailure)-            sendPacket ctx $ Handshake [ClientKeyXchg ckx]-          where getCKX_DHE = do-                    xver <- usingState_ ctx getVersion-                    serverParams <- usingHState ctx getServerDHParams--                    let params  = serverDHParamsToParams serverParams-                        ffGroup = findFiniteFieldGroup params-                        srvpub  = serverDHParamsToPublic serverParams--                    (clientDHPub, premaster) <--                        case ffGroup of-                             Nothing  -> 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", True, InsufficientSecurity)-                                     GroupUsageUnsupported reason -> throwCore $ Error_Protocol ("unsupported FFDHE group: " ++ reason, True, HandshakeFailure)-                                     GroupUsageInvalidPublic      -> throwCore $ Error_Protocol ("invalid server public key", True, HandshakeFailure)-                                     GroupUsageValid              -> do-                                         (clientDHPriv, clientDHPub) <- generateDHE ctx params-                                         let premaster = dhGetShared params clientDHPriv srvpub-                                         return (clientDHPub, premaster)-                             Just grp -> do-                                 dhePair <- generateFFDHEShared ctx grp srvpub-                                 case dhePair of-                                     Nothing   -> throwCore $ Error_Protocol ("invalid server public key", True, HandshakeFailure)-                                     Just pair -> return pair--                    usingHState ctx $ setMasterSecretFromPre xver ClientRole premaster--                    return $ CKX_DH clientDHPub--                getCKX_ECDHE = do-                    ServerECDHParams _grp srvpub <- usingHState ctx getServerECDHParams-                    ecdhePair <- generateECDHEShared ctx srvpub-                    case ecdhePair of-                        Nothing                  -> throwCore $ Error_Protocol ("invalid server public key", True, HandshakeFailure)-                        Just (clipub, premaster) -> do-                            xver <- usingState_ ctx getVersion-                            usingHState ctx $ setMasterSecretFromPre xver ClientRole premaster-                            return $ CKX_ECDH $ encodeGroupPublic clipub--        -- 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 = do-            usedVersion <- 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-                sigAlg <- getLocalSignatureAlg--                mhashSig <- case usedVersion of-                    TLS12 -> do-                        Just (_, Just hashSigs, _) <- usingHState ctx getClientCertRequest-                        -- The values in the "signature_algorithms" extension-                        -- are in descending order of preference.-                        -- However here the algorithms are selected according-                        -- to client preference in 'supportedHashSignatures'.-                        let suppHashSigs = supportedHashSignatures $ ctxSupported ctx-                            matchHashSigs = filter (sigAlg `signatureCompatible`) suppHashSigs-                            hashSigs' = filter (`elem` hashSigs) matchHashSigs--                        when (null hashSigs') $-                            throwCore $ Error_Protocol ("no " ++ show sigAlg ++ " hash algorithm in common with the server", True, HandshakeFailure)-                        return $ Just $ head hashSigs'-                    _     -> return Nothing--                -- Fetch all handshake messages up to now.-                msgs   <- usingHState ctx $ B.concat <$> getHandshakeMessages-                sigDig <- createCertificateVerify ctx usedVersion sigAlg mhashSig msgs-                sendPacket ctx $ Handshake [CertVerify sigDig]--        getLocalSignatureAlg = do-            pk <- usingHState ctx getLocalPrivateKey-            case pk of-                PrivKeyRSA _   -> return RSA-                PrivKeyDSA _   -> return DSS--processServerExtension :: ExtensionRaw -> TLSSt ()-processServerExtension (ExtensionRaw 0xff01 content) = do-    cv <- getVerifiedData ClientRole-    sv <- getVerifiedData ServerRole-    let bs = extensionEncode (SecureRenegotiation cv $ Just sv)-    unless (bs `bytesEq` content) $ throwError $ Error_Protocol ("server secure renegotiation data not matching", True, HandshakeFailure)-    return ()-processServerExtension _ = return ()--throwMiscErrorOnException :: String -> SomeException -> IO a-throwMiscErrorOnException msg e =-    throwCore $ Error_Misc $ msg ++ ": " ++ show e+    groupsSupported = supportedGroups (ctxSupported ctx)+    groupsSelected = onSelectKeyShareGroups (clientHooks cparams) groupsSupported --- | onServerHello process the ServerHello message on the client.------ 1) check the version chosen by the server is one allowed by parameters.--- 2) check that our compression and cipher algorithms are part of the list we sent--- 3) check extensions received are part of the one we sent--- 4) process the session parameter to see if the server want to start a new session or can resume--- 5) if no resume switch to processCertificate SM or in resume switch to expectChangeCipher+-- https://tools.ietf.org/html/rfc8446#section-4.1.2 says:+-- "The client will also send a+--  ClientHello when the server has responded to its ClientHello with a+--  HelloRetryRequest.  In that case, the client MUST send the same+--  ClientHello without modification, except as follows:" ---onServerHello :: Context -> ClientParams -> [ExtensionID] -> Handshake -> IO (RecvState IO)-onServerHello ctx cparams sentExts (ServerHello rver serverRan serverSession cipher compression exts) = do-    when (rver == SSL2) $ throwCore $ Error_Protocol ("ssl2 is not supported", True, ProtocolVersion)-    case find (== rver) (supportedVersions $ ctxSupported ctx) of-        Nothing -> throwCore $ Error_Protocol ("server version " ++ show rver ++ " is not supported", True, ProtocolVersion)-        Just _  -> return ()-    -- find the compression and cipher methods that the server want to use.-    cipherAlg <- case find ((==) cipher . cipherID) (supportedCiphers $ ctxSupported ctx) of-                     Nothing  -> throwCore $ Error_Protocol ("server choose unknown cipher", True, HandshakeFailure)-                     Just alg -> return alg-    compressAlg <- case find ((==) compression . compressionID) (supportedCompressions $ ctxSupported ctx) of-                       Nothing  -> throwCore $ Error_Protocol ("server choose unknown compression", True, HandshakeFailure)-                       Just alg -> return alg--    -- intersect sent extensions in client and the received extensions from server.-    -- if server returns extensions that we didn't request, fail.-    unless (null $ filter (not . flip elem sentExts . (\(ExtensionRaw i _) -> i)) exts) $-        throwCore $ Error_Protocol ("spurious extensions received", True, UnsupportedExtension)--    let resumingSession =-            case clientWantSessionResume cparams of-                Just (sessionId, sessionData) -> if serverSession == Session (Just sessionId) then Just sessionData else Nothing-                Nothing                       -> Nothing-    usingState_ ctx $ do-        setSession serverSession (isJust resumingSession)-        mapM_ processServerExtension exts-        setVersion rver-    usingHState ctx $ setServerHelloParameters rver serverRan cipherAlg compressAlg--    case extensionDecode False <$> extensionLookup extensionID_ApplicationLayerProtocolNegotiation exts of-        Just (Just (ApplicationLayerProtocolNegotiation [proto])) -> usingState_ ctx $ do-            mprotos <- getClientALPNSuggest-            case mprotos of-                Just protos -> when (proto `elem` protos) $ do-                    setExtensionALPN True-                    setNegotiatedProtocol proto-                _ -> return ()-        _ -> return ()--    case resumingSession of-        Nothing          -> return $ RecvStateHandshake (processCertificate cparams ctx)-        Just sessionData -> do-            usingHState ctx (setMasterSecret rver ClientRole $ sessionSecret sessionData)-            return $ RecvStateNext expectChangeCipher-onServerHello _ _ _ p = unexpected (show p) (Just "server hello")--processCertificate :: ClientParams -> Context -> Handshake -> IO (RecvState IO)-processCertificate cparams ctx (Certificates certs) = do-    -- run certificate recv hook-    ctxWithHooks ctx (\hooks -> hookRecvCertificates hooks certs)-    -- then run certificate validation-    usage <- catchException (wrapCertificateChecks <$> checkCert) rejectOnException-    case usage of-        CertificateUsageAccept        -> return ()-        CertificateUsageReject reason -> certificateRejected reason-    return $ RecvStateHandshake (processServerKeyExchange ctx)-  where shared = clientShared cparams-        checkCert = (onServerCertificate $ clientHooks cparams) (sharedCAStore shared)-                                                                (sharedValidationCache shared)-                                                                (clientServerIdentification cparams)-                                                                certs-processCertificate _ ctx p = processServerKeyExchange ctx p--expectChangeCipher :: Packet -> IO (RecvState IO)-expectChangeCipher ChangeCipherSpec = return $ RecvStateHandshake expectFinish-expectChangeCipher p                = unexpected (show p) (Just "change cipher")--expectFinish :: Handshake -> IO (RecvState IO)-expectFinish (Finished _) = return RecvStateDone-expectFinish p            = unexpected (show p) (Just "Handshake Finished")--processServerKeyExchange :: Context -> Handshake -> IO (RecvState IO)-processServerKeyExchange ctx (ServerKeyXchg origSkx) = do-    cipher <- usingHState ctx getPendingCipher-    processWithCipher cipher origSkx-    return $ RecvStateHandshake (processCertificateRequest ctx)-  where processWithCipher cipher skx =-            case (cipherKeyExchange cipher, skx) of-                (CipherKeyExchange_DHE_RSA, SKX_DHE_RSA dhparams signature) -> do-                    doDHESignature dhparams signature RSA-                (CipherKeyExchange_DHE_DSS, SKX_DHE_DSS dhparams signature) -> do-                    doDHESignature dhparams signature DSS-                (CipherKeyExchange_ECDHE_RSA, SKX_ECDHE_RSA ecdhparams signature) -> do-                    doECDHESignature ecdhparams signature RSA-                (CipherKeyExchange_ECDHE_ECDSA, SKX_ECDHE_ECDSA ecdhparams signature) -> do-                    doECDHESignature ecdhparams signature ECDSA-                (cke, SKX_Unparsed bytes) -> do-                    ver <- usingState_ ctx getVersion-                    case decodeReallyServerKeyXchgAlgorithmData ver cke bytes of-                        Left _        -> throwCore $ Error_Protocol ("unknown server key exchange received, expecting: " ++ show cke, True, HandshakeFailure)-                        Right realSkx -> processWithCipher cipher realSkx-                    -- we need to resolve the result. and recall processWithCipher ..-                (c,_)           -> throwCore $ Error_Protocol ("unknown server key exchange received, expecting: " ++ show c, True, HandshakeFailure)-        doDHESignature dhparams signature signatureType = do-            -- FIXME verify if FF group is one of supported groups-            verified <- digitallySignDHParamsVerify ctx dhparams signatureType signature-            unless verified $ throwCore $ Error_Protocol ("bad " ++ show signatureType ++ " signature for dhparams " ++ show dhparams, True, HandshakeFailure)-            usingHState ctx $ setServerDHParams dhparams--        doECDHESignature ecdhparams signature signatureType = do-            -- FIXME verify if EC group is one of supported groups-            verified <- digitallySignECDHParamsVerify ctx ecdhparams signatureType signature-            unless verified $ throwCore $ Error_Protocol ("bad " ++ show signatureType ++ " signature for ecdhparams", True, HandshakeFailure)-            usingHState ctx $ setServerECDHParams ecdhparams+-- So, the ClientRandom in the first client hello is necessary.+handshake+    :: ClientParams+    -> Context+    -> Groups+    -> Maybe (ClientRandom, Session, Version)+    -> IO ()+handshake cparams ctx grps@Groups{..} mparams = do+    --------------------------------+    -- Sending ClientHello+    pskinfo@(_, _, rtt0) <- getPreSharedKeyInfo cparams ctx+    when rtt0 $ modifyTLS13State ctx $ \st -> st{tls13st0RTT = True}+    let async = rtt0 && not (ctxQUICMode ctx)+    when async $ do+        chSentTime <- getCurrentTimeFromBase+        asyncServerHello13 cparams ctx grpsSelected chSentTime+    updateMeasure ctx incrementNbHandshakes+    crand <-+        sendClientHello cparams ctx grps mparams pskinfo+    --------------------------------+    -- Receiving ServerHello+    unless async $ do+        (ver, hbs, hrr) <- receiveServerHello cparams ctx mparams+        -- Switching to HRR, TLS 1.2 or TLS 1.3+        case ver of+            TLS13+                | hrr ->+                    helloRetry cparams ctx mparams ver crand grpsSupported grpsSelected+                | otherwise -> do+                    recvServerSecondFlight13 cparams ctx grpsSelected+                    sendClientSecondFlight13 cparams ctx+            _+                | rtt0 ->+                    throwCore $+                        Error_Protocol+                            "server denied TLS 1.3 when connecting with early data"+                            HandshakeFailure+                | otherwise -> do+                    recvServerFirstFlight12 cparams ctx hbs+                    sendClientSecondFlight12 cparams ctx+                    recvServerSecondFlight12 cparams ctx -processServerKeyExchange ctx p = processCertificateRequest ctx p+---------------------------------------------------------------- -processCertificateRequest :: Context -> Handshake -> IO (RecvState IO)-processCertificateRequest ctx (CertRequest cTypes sigAlgs dNames) = do-    -- When the server requests a client-    -- certificate, we simply store the-    -- information for later.-    ---    usingHState ctx $ setClientCertRequest (cTypes, sigAlgs, dNames)-    return $ RecvStateHandshake (processServerHelloDone ctx)-processCertificateRequest ctx p = processServerHelloDone ctx p+helloRetry+    :: ClientParams+    -> Context+    -> Maybe a+    -> Version+    -> ClientRandom+    -> [Group]+    -> [Group]+    -> IO ()+helloRetry cparams ctx mparams ver crand groupsSupported groupsSelected = do+    when (null groupsSupported) $+        throwCore $+            Error_Protocol "no supported groups on the client side" IllegalParameter+    when (isJust mparams) $+        throwCore $+            Error_Protocol "server sent too many hello retries" UnexpectedMessage+    mks <- usingState_ ctx getTLS13KeyShare+    case mks of+        Just (KeyShareHRR selectedGroup)+            -- RFC 8446 Sec 4.1.4: the selected_group MUST be in supported_groups+            -- and MUST NOT already have been offered in the initial key_share.+            | selectedGroup `elem` groupsSupported+                && selectedGroup `notElem` groupsSelected -> do+                usingHState ctx $ setTLS13HandshakeMode HelloRetryRequest+                clearTxRecordState ctx+                let cparams' = cparams{clientUseEarlyData = False}+                runPacketFlight ctx $ sendChangeCipherSpec13 ctx+                clientSession <- tls13stSession <$> getTLS13State ctx+                -- RFC 8446 Sec 4.1.2: the second ClientHello MUST be identical+                -- to the first except for the specific listed changes.+                -- supported_groups is NOT on that list, so it must be unchanged.+                let grps =+                        Groups+                            { grpsSupported = groupsSupported+                            , grpsSelected = [selectedGroup]+                            } -processServerHelloDone :: Context -> Handshake -> IO (RecvState m)-processServerHelloDone _ ServerHelloDone = return RecvStateDone-processServerHelloDone _ p = unexpected (show p) (Just "server hello data")+                handshake+                    cparams'+                    ctx+                    grps+                    (Just (crand, clientSession, ver))+            | selectedGroup `elem` groupsSelected ->+                throwCore $+                    Error_Protocol+                        "server selected a group already offered in key_share"+                        IllegalParameter+            | otherwise ->+                throwCore $+                    Error_Protocol "server-selected group is not supported" IllegalParameter+        Just _ -> error "handshake: invalid KeyShare value"+        Nothing ->+            throwCore $+                Error_Protocol+                    "key exchange not implemented in HRR, expected key_share extension"+                    HandshakeFailure
+ Network/TLS/Handshake/Client/ClientHello.hs view
@@ -0,0 +1,610 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}++module Network.TLS.Handshake.Client.ClientHello (+    sendClientHello,+    getPreSharedKeyInfo,+    Groups (..),+) where++import qualified Control.Exception as E+import Crypto.HPKE hiding (CipherText, PlainText)+import Data.ByteArray (convert)+import qualified Data.ByteArray as BA+import qualified Data.ByteString as B+import Network.TLS.ECH.Config+import System.Random++#if !MIN_VERSION_random(1,3,0)+import Data.ByteString.Internal (unsafeCreate)+import Foreign.Ptr+import Foreign.Storable+#endif++import Network.TLS.Cipher+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.Random+import Network.TLS.Handshake.State+import Network.TLS.Handshake.State13+import Network.TLS.Handshake.TranscriptHash+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++----------------------------------------------------------------++data Groups = Groups+    { grpsSupported :: [Group]+    -- ^ For supported_group, head is identical to key_share+    , grpsSelected :: [Group]+    -- ^ For key_share+    }+    deriving (Eq, Show)++----------------------------------------------------------------++sendClientHello+    :: ClientParams+    -> Context+    -> Groups+    -> Maybe (ClientRandom, Session, Version)+    -> PreSharedKeyInfo+    -> IO ClientRandom+sendClientHello cparams ctx grps mparams pskinfo = do+    crand <- generateClientHelloParams mparams -- Inner for ECH+    sendClientHello' cparams ctx grps 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+    -> Groups+    -> ClientRandom+    -> ( Maybe ([ByteString], SessionData, CipherChoice, Word32)+       , Maybe CipherChoice+       , Bool+       )+    -> 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+        mkClientHello exts =+            CH+                { chVersion = ver+                , chRandom = crand+                , chSession = clientSession+                , chCiphers = cipherIds+                , chComps = [0]+                , chExtensions = exts+                }+    setMyRecordLimit ctx $ limitRecordSize $ sharedLimit $ ctxShared ctx+    extensions0 <- catMaybes <$> getExtensions+    let extensions1 = sharedHelloExtensions (clientShared cparams) ++ extensions0+    extensions <- adjustPreSharedKeyExt extensions1 $ mkClientHello extensions1+    let ch0 = mkClientHello extensions+    updateTranscriptHashI ctx "ClientHelloI" $ encodeHandshake $ ClientHello ch0+    let nhpks = supportedHPKE $ clientSupported cparams+        echcnfs = sharedECHConfigList $ clientShared cparams+        mEchParams = lookupECHConfigList nhpks echcnfs+    ch <-+        if clientUseECH cparams+            then case mEchParams of+                Nothing -> do+                    if hrr+                        then do+                            (chI, _) <- fromJust <$> usingHState ctx getClientHello+                            let ch0' = ch0{chExtensions = take 1 (chExtensions chI) ++ drop 1 (chExtensions ch0)}+                            -- [] will be overridden via+                            -- encodeUpdateTranscriptHash12+                            usingHState ctx $ setClientHello ch0' []+                            return ch0'+                        else do+                            gEchExt <- greasingEchExt+                            let ch0' = ch0{chExtensions = gEchExt : drop 1 (chExtensions ch0)}+                            -- [] will be overridden via+                            -- encodeUpdateTranscriptHash12+                            usingHState ctx $ setClientHello ch0' []+                            return ch0'+                Just echParams -> do+                    let encoded = encodeHandshake $ ClientHello ch0+                    usingHState ctx $ setClientHello ch0 [encoded]+                    mcrandO <- usingHState ctx getOuterClientRandom+                    crandO <- case mcrandO of+                        Nothing -> clientRandom ctx+                        Just x -> return x+                    usingHState ctx $ do+                        setClientRandom crandO+                        setOuterClientRandom $ Just crandO+                    mpskExt <- randomPreSharedKeyExt+                    createEncryptedClientHello ctx ch0 echParams crandO mpskExt+            else do+                -- [] will be overridden via+                -- encodeUpdateTranscriptHash12+                usingHState ctx $ setClientHello ch0 []+                return ch0+    sendPacket12 ctx $ Handshake [ClientHello ch] []+    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+    highestVer = maximum $ supportedVersions $ ctxSupported ctx+    tls13 = highestVer >= TLS13+    ems = supportedExtendedMainSecret $ ctxSupported ctx++    -- 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+            [ {- 0xfe0d -} echExt+            , {- 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++    -- RFC 8446 Sec 4.2.8 says: Each KeyShareEntry value MUST correspond+    -- to a group offered in the "supported_groups" extension and MUST+    -- appear in the same order.+    groupExt = return $ Just $ toExtensionRaw $ SupportedGroups grpsSupported++    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 $ ctxShared ctx of+        Nothing -> return Nothing+        Just siz -> return $ Just $ toExtensionRaw $ RecordSizeLimit $ fromIntegral siz++    sessionTicketExt =+        case clientSessions cparams of+            (sidOrTkt, _) : _+                | isTicket sidOrTkt -> return $ Just $ toExtensionRaw $ SessionTicket sidOrTkt+            _   | clientWantTicket cparams -> return $ Just $ toExtensionRaw $ SessionTicket ""+                | otherwise -> return $ Nothing++    earlyDataExt+        | rtt0 = return $ Just $ toExtensionRaw (EarlyDataIndication Nothing)+        | otherwise = return Nothing++    versionExt+        | clientUseECH cparams = do+            let vers = supportedVersions $ ctxSupported ctx+            if TLS13 `elem` vers+                then+                    return $ Just $ toExtensionRaw $ SupportedVersionsClientHello [TLS13]+                else+                    throwCore $ Error_Misc "TLS 1.3 must be specified for Encrypted Client Hello"+        | 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++    keyShareExt+        | tls13 = do+            (grpCpris, ents) <- unzip <$> mapM (makeClientKeyShare ctx) grpsSelected+            usingHState ctx $ setGroupPrivate grpCpris+            return $ Just $ toExtensionRaw $ KeyShareClientHello ents+        | otherwise = return Nothing++    secureRenegExt =+        if supportedSecureRenegotiation $ ctxSupported ctx+            then do+                VerifyData cvd <- usingState_ ctx $ getVerifyData ClientRole+                return $ Just $ toExtensionRaw $ SecureRenegotiation cvd ""+            else return Nothing++    -- ECHClientHelloInner should be replaced if ECHConfigList is not available.+    echExt+        | clientUseECH cparams = return $ Just $ toExtensionRaw ECHClientHelloInner+        | otherwise = return Nothing++    preSharedKeyExt =+        case pskInfo of+            Nothing -> return Nothing+            Just (identities, _, choice, obfAge) -> do+                let zero = cZero choice+                    pskIdentities = map (\x -> PskIdentity x obfAge) identities+                    -- [zero] is a place holds.+                    -- adjustPreSharedKeyExt will replace them.+                    binders = replicate (length pskIdentities) $ convert zero+                    offeredPsks = PreSharedKeyClientHello pskIdentities binders+                return $ Just $ toExtensionRaw offeredPsks++    randomPreSharedKeyExt :: IO (Maybe ExtensionRaw)+    randomPreSharedKeyExt =+        case pskInfo of+            Nothing -> return Nothing+            Just (identities, _, choice, _) -> do+                let zero = cZero choice+                zeroR <- getStdRandom $ uniformByteString $ BA.length zero+                obfAgeR <- getStdRandom genWord32+                let genPskId x = do+                        xR <- getStdRandom $ uniformByteString $ B.length x+                        return $ PskIdentity xR obfAgeR+                pskIdentitiesR <- mapM genPskId identities+                let bindersR = replicate (length pskIdentitiesR) zeroR+                    offeredPsksR = PreSharedKeyClientHello pskIdentitiesR bindersR+                return $ Just $ toExtensionRaw offeredPsksR++    ----------------------------------------++    adjustPreSharedKeyExt 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 $ ClientHello ch+                    h = cHash choice+                    siz = (hashDigestSize h + 1) * length identities + 2+                    binder = makePSKBinder earlySecret h siz 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+        earlyKey <- calculateEarlySecret ctx choice (Right earlySecret)+        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++----------------------------------------------------------------++createEncryptedClientHello+    :: Context+    -> ClientHello+    -> (KDF_ID, AEAD_ID, ECHConfig)+    -> ClientRandom+    -> Maybe ExtensionRaw+    -> IO ClientHello+createEncryptedClientHello ctx ch0@CH{..} echParams@(kdfid, aeadid, conf) crO mpskExt = E.handle hpkeHandler $ do+    let (chExtsO, chExtsI) = dupCompExts (cnfPublicName conf) mpskExt chExtensions+        chI =+            ch0+                { chSession = Session Nothing+                , chExtensions = chExtsI+                }+    Just (func, enc, taglen) <- getHPKE ctx echParams+    let bsI = encodeHandshake' $ ClientHello chI+        padLen = 32 - (B.length bsI .&. 31)+        bsI' = bsI <> B.replicate padLen 0+    let outerZ =+            ECHClientHelloOuter+                { echCipherSuite = (kdfid, aeadid)+                , echConfigId = cnfConfigId conf+                , echEnc = enc+                , echPayload = B.replicate (B.length bsI' + taglen) 0+                }+        echOZ = extensionEncode outerZ+        chExtsOTail = drop 1 chExtsO+        chOZ =+            ch0+                { chRandom = crO+                , chExtensions =+                    ExtensionRaw EID_EncryptedClientHello echOZ : chExtsOTail+                }+        aad = encodeHandshake' $ ClientHello chOZ+    bsO <- func aad bsI'+    let outer =+            ECHClientHelloOuter+                { echCipherSuite = (kdfid, aeadid)+                , echConfigId = cnfConfigId conf+                , echEnc = enc+                , echPayload = bsO+                }+        echO = extensionEncode outer+        chO =+            chOZ+                { chExtensions =+                    ExtensionRaw EID_EncryptedClientHello echO : chExtsOTail+                }+    return chO+  where+    hpkeHandler :: HPKEError -> IO ClientHello+    hpkeHandler _ = return ch0++dupCompExts+    :: HostName+    -> Maybe ExtensionRaw+    -> [ExtensionRaw]+    -> ([ExtensionRaw], [ExtensionRaw]) -- Outer, inner+dupCompExts host mpskExt chExts = step1 chExts+  where+    step1 (echExtI@(ExtensionRaw EID_EncryptedClientHello _) : exts) =+        (echExtO : os, echExtI : is)+      where+        echExtO = ExtensionRaw EID_EncryptedClientHello ""+        (os, is) = step2 exts+    step1 _ = error "step1"+    step2 (sniExtI@(ExtensionRaw EID_ServerName _) : exts) =+        (sniExtO : os, sniExtI : is)+      where+        sniExtO = toExtensionRaw $ ServerName [ServerNameHostName host]+        (os, is) = step3 exts id+    step2 _ = error "step2"+    step3 [] build = ([], [echOuterExt])+      where+        echOuterExt = toExtensionRaw $ EchOuterExtensions $ build []+    step3 [pskExtI@(ExtensionRaw EID_PreSharedKey _)] build =+        ([pskExtO], [echOuterExt, pskExtI])+      where+        echOuterExt = toExtensionRaw $ EchOuterExtensions $ build []+        pskExtO = fromJust mpskExt+    step3 (i@(ExtensionRaw eid _) : is) build = (i : os', is')+      where+        (os', is') = step3 is (build . (eid :))++getHPKE+    :: Context+    -> (KDF_ID, AEAD_ID, ECHConfig)+    -> IO (Maybe (AAD -> PlainText -> IO CipherText, EncodedPublicKey, Int))+getHPKE ctx (kdfid, aeadid, conf) = do+    mfunc <- getTLS13HPKE ctx+    case mfunc of+        Nothing -> do+            let encodedConfig = encodeECHConfig conf+                info = "tls ech\x00" <> encodedConfig+            (pkSm, ctxS) <- setupBaseS kemid kdfid aeadid Nothing Nothing mpkR info+            let func = seal ctxS+            setTLS13HPKE ctx func 0+            return $ Just (func, pkSm, nT)+        Just (func, _) -> return $ Just (func, EncodedPublicKey "", nT)+  where+    mpkR = cnfEncodedPublicKey conf+    kemid = cnfKemId conf+    nT = nTag aeadid++----------------------------------------------------------------++lookupECHConfigList+    :: [(KEM_ID, KDF_ID, AEAD_ID)]+    -> ECHConfigList+    -> Maybe (KDF_ID, AEAD_ID, ECHConfig)+lookupECHConfigList [] _ = Nothing+lookupECHConfigList ((kemid, kdfid, aeadid) : xs) cnfs =+    case find (\cnf -> cnfKemId cnf == kemid) cnfs of+        Nothing -> lookupECHConfigList xs cnfs+        Just cnf+            | (kdfid, aeadid) `elem` cnfCipherSuite cnf ->+                Just (kdfid, aeadid, cnf)+            | otherwise -> lookupECHConfigList xs cnfs++cnfKemId :: ECHConfig -> KEM_ID+cnfKemId ECHConfig{..} = KEM_ID $ kem_id $ key_config contents++cnfCipherSuite :: ECHConfig -> [(KDF_ID, AEAD_ID)]+cnfCipherSuite ECHConfig{..} = map conv $ cipher_suites $ key_config contents+  where+    conv HpkeSymmetricCipherSuite{..} = (KDF_ID kdf_id, AEAD_ID aead_id)++cnfEncodedPublicKey :: ECHConfig -> EncodedPublicKey+cnfEncodedPublicKey ECHConfig{..} = EncodedPublicKey pk+  where+    EncodedServerPublicKey pk = public_key $ key_config contents++cnfPublicName :: ECHConfig -> HostName+cnfPublicName ECHConfig{..} = public_name contents++cnfConfigId :: ECHConfig -> ConfigId+cnfConfigId ECHConfig{..} = config_id $ key_config contents++----------------------------------------------------------------++-- Pretending X25519 is used because it is the de-facto and+-- its public key is easily created.+greasingEchExt :: IO ExtensionRaw+greasingEchExt = do+    cid <- getStdRandom genWord8+    enc <- getStdRandom $ uniformByteString 32+    n <- getStdRandom $ randomR (4, 6)+    payload <- getStdRandom $ uniformByteString (n * 32 + 16)+    let outer =+            ECHClientHelloOuter+                { echCipherSuite = (HKDF_SHA256, AES_128_GCM)+                , echConfigId = cid+                , echEnc = EncodedPublicKey enc+                , echPayload = payload+                }+    return $ toExtensionRaw outer++#if !MIN_VERSION_random(1,3,0)+uniformByteString :: RandomGen g => Int -> g -> (ByteString, g)+uniformByteString l g0 = (bs, g2)+  where+    (g1, g2) = split g0+    bs = unsafeCreate l $ go 0 g1+    go n g ptr+        | n == l = return ()+        | otherwise = do+            let (w, g') = genWord8 g+            poke ptr w+            go (n + 1) g' (plusPtr ptr 1)+#endif
+ Network/TLS/Handshake/Client/Common.hs view
@@ -0,0 +1,363 @@+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE RecordWildCards #-}++module Network.TLS.Handshake.Client.Common (+    throwMiscErrorOnException,+    doServerKeyExchange,+    doCertificate,+    getLocalHashSigAlg,+    clientChain,+    sigAlgsToCertTypes,+    setALPN,+    contextSync,+    clientSessions,+) where++import Control.Exception (SomeException)+import Control.Monad.State.Strict+import Data.X509 (ExtKeyUsageFlag (..))++import Network.TLS.Cipher+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.Control+import Network.TLS.Handshake.Key+import Network.TLS.Handshake.Signature+import Network.TLS.Handshake.State+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.Util (catchException)+import Network.TLS.X509++----------------------------------------------------------------++throwMiscErrorOnException :: String -> SomeException -> IO a+throwMiscErrorOnException msg e =+    throwCore $ Error_Misc $ msg ++ ": " ++ show e++----------------------------------------------------------------++doServerKeyExchange :: Context -> ServerKeyXchgAlgorithmData -> IO ()+doServerKeyExchange ctx origSkx = do+    cipher <- usingHState ctx getPendingCipher+    processWithCipher cipher origSkx+  where+    processWithCipher cipher skx =+        case (cipherKeyExchange cipher, skx) of+            (CipherKeyExchange_DHE_RSA, SKX_DHE_RSA dhparams signature) ->+                doDHESignature dhparams signature KX_RSA+            (CipherKeyExchange_DHE_DSA, SKX_DHE_DSA dhparams signature) ->+                doDHESignature dhparams signature KX_DSA+            (CipherKeyExchange_ECDHE_RSA, SKX_ECDHE_RSA ecdhparams signature) ->+                doECDHESignature ecdhparams signature KX_RSA+            (CipherKeyExchange_ECDHE_ECDSA, SKX_ECDHE_ECDSA ecdhparams signature) ->+                doECDHESignature ecdhparams signature KX_ECDSA+            (cke, SKX_Unparsed bytes) -> do+                ver <- usingState_ ctx getVersion+                case decodeReallyServerKeyXchgAlgorithmData ver cke bytes of+                    Left _ ->+                        throwCore $+                            Error_Protocol+                                ("unknown server key exchange received, expecting: " ++ show cke)+                                HandshakeFailure+                    Right realSkx -> processWithCipher cipher realSkx+            -- we need to resolve the result. and recall processWithCipher ..+            (c, _) ->+                throwCore $+                    Error_Protocol+                        ("unknown server key exchange received, expecting: " ++ show c)+                        HandshakeFailure+    doDHESignature dhparams signature kxsAlg = do+        -- FF group selected by the server is verified when generating CKX+        publicKey <- getSignaturePublicKey kxsAlg+        verified <- digitallySignDHParamsVerify ctx dhparams publicKey signature+        unless verified $+            decryptError+                ("bad " ++ pubkeyType publicKey ++ " signature for dhparams " ++ show dhparams)+        usingHState ctx $ setServerDHParams dhparams++    doECDHESignature ecdhparams signature kxsAlg = do+        -- EC group selected by the server is verified when generating CKX+        publicKey <- getSignaturePublicKey kxsAlg+        verified <- digitallySignECDHParamsVerify ctx ecdhparams publicKey signature+        unless verified $+            decryptError ("bad " ++ pubkeyType publicKey ++ " signature for ecdhparams")+        usingHState ctx $ setServerECDHParams ecdhparams++    getSignaturePublicKey kxsAlg = do+        publicKey <- usingHState ctx getRemotePublicKey+        unless (isKeyExchangeSignatureKey kxsAlg publicKey) $+            throwCore $+                Error_Protocol+                    ("server public key algorithm is incompatible with " ++ show kxsAlg)+                    HandshakeFailure+        ver <- usingState_ ctx getVersion+        unless (publicKey `versionCompatible` ver) $+            throwCore $+                Error_Protocol+                    (show ver ++ " has no support for " ++ pubkeyType publicKey)+                    IllegalParameter+        let groups = supportedGroups (ctxSupported ctx)+        unless (satisfiesEcPredicate (`elem` groups) publicKey) $+            throwCore $+                Error_Protocol+                    "server public key has unsupported elliptic curve"+                    IllegalParameter+        return publicKey++----------------------------------------------------------------++doCertificate :: ClientParams -> Context -> CertificateChain -> IO ()+doCertificate cparams ctx certs = do+    when (isNullCertificateChain certs) $+        throwCore $+            Error_Protocol "server certificate missing" DecodeError+    -- run certificate recv hook+    ctxWithHooks ctx (`hookRecvCertificates` certs)+    -- then run certificate validation+    usage <- catchException (wrapCertificateChecks <$> checkCert) rejectOnException+    case usage of+        CertificateUsageAccept -> checkLeafCertificateKeyUsage+        CertificateUsageReject reason -> certificateRejected reason+  where+    shared = clientShared cparams+    checkCert =+        onServerCertificate+            (clientHooks cparams)+            (sharedCAStore shared)+            (sharedValidationCache shared)+            (clientServerIdentification cparams)+            certs+    -- also verify that the certificate optional key usage is compatible+    -- with the intended key-exchange.  This check is not delegated to+    -- x509-validation 'checkLeafKeyUsage' because it depends on negotiated+    -- cipher, which is not available from onServerCertificate parameters.+    -- Additionally, with only one shared ValidationCache, x509-validation+    -- would cache validation result based on a key usage and reuse it with+    -- another key usage.+    checkLeafCertificateKeyUsage = do+        cipher <- usingHState ctx getPendingCipher+        case requiredCertKeyUsage cipher of+            [] -> return ()+            flags -> verifyLeafKeyUsage flags certs++-- Unless result is empty, server certificate must be allowed for at least one+-- of the returned values.  Constraints for RSA-based key exchange are relaxed+-- to avoid rejecting certificates having incomplete extension.+requiredCertKeyUsage :: Cipher -> [ExtKeyUsageFlag]+requiredCertKeyUsage cipher =+    case cipherKeyExchange cipher of+        CipherKeyExchange_RSA -> rsaCompatibility+        CipherKeyExchange_DH_Anon -> [] -- unrestricted+        CipherKeyExchange_DHE_RSA -> rsaCompatibility+        CipherKeyExchange_ECDHE_RSA -> rsaCompatibility+        CipherKeyExchange_DHE_DSA -> [KeyUsage_digitalSignature]+        CipherKeyExchange_DH_DSA -> [KeyUsage_keyAgreement]+        CipherKeyExchange_DH_RSA -> rsaCompatibility+        CipherKeyExchange_ECDH_ECDSA -> [KeyUsage_keyAgreement]+        CipherKeyExchange_ECDH_RSA -> rsaCompatibility+        CipherKeyExchange_ECDHE_ECDSA -> [KeyUsage_digitalSignature]+        CipherKeyExchange_TLS13 -> [KeyUsage_digitalSignature]+  where+    rsaCompatibility =+        [ KeyUsage_digitalSignature+        , KeyUsage_keyEncipherment+        , KeyUsage_keyAgreement+        ]++----------------------------------------------------------------++-- | Return the supported 'CertificateType' values that are+-- compatible with at least one supported signature algorithm.+supportedCtypes+    :: [HashAndSignatureAlgorithm]+    -> [CertificateType]+supportedCtypes hashAlgs =+    nub $ foldr ctfilter [] hashAlgs+  where+    ctfilter x acc = case hashSigToCertType x of+        Just cType+            | cType <= lastSupportedCertificateType ->+                cType : acc+        _ -> acc++clientSupportedCtypes+    :: Context+    -> [CertificateType]+clientSupportedCtypes ctx =+    supportedCtypes $ supportedHashSignatures $ ctxSupported ctx++sigAlgsToCertTypes+    :: Context+    -> [HashAndSignatureAlgorithm]+    -> [CertificateType]+sigAlgsToCertTypes ctx hashSigs =+    filter (`elem` supportedCtypes hashSigs) $ clientSupportedCtypes ctx++----------------------------------------------------------------++-- | When the server requests a client certificate, we try to+-- obtain a suitable certificate chain and private key via the+-- callback in the client parameters.  It is OK for the callback+-- to return an empty chain, in many cases the client certificate+-- is optional.  If the client wishes to abort the handshake for+-- lack of a suitable certificate, it can throw an exception in+-- the callback.+--+-- The return value is 'Nothing' when no @CertificateRequest@ was+-- received and no @Certificate@ message needs to be sent. An empty+-- chain means that an empty @Certificate@ message needs to be sent+-- to the server, naturally without a @CertificateVerify@.  A non-empty+-- 'CertificateChain' is the chain to send to the server along with+-- a corresponding 'CertificateVerify'.+--+-- With TLS < 1.2 the server's @CertificateRequest@ does not carry+-- a signature algorithm list.  It has a list of supported public+-- key signing algorithms in the @certificate_types@ field.  The+-- hash is implicit.  It is 'SHA1' for DSA and 'SHA1_MD5' for RSA.+--+-- With TLS == 1.2 the server's @CertificateRequest@ always has a+-- @supported_signature_algorithms@ list, as a fixed component of+-- the structure.  This list is (wrongly) overloaded to also limit+-- X.509 signatures in the client's certificate chain.  The BCP+-- strategy is to find a compatible chain if possible, but else+-- ignore the constraint, and let the server verify the chain as it+-- sees fit.  The @supported_signature_algorithms@ field is only+-- obligatory with respect to signatures on TLS messages, in this+-- case the @CertificateVerify@ message.  The @certificate_types@+-- field is still included.+--+-- With TLS 1.3 the server's @CertificateRequest@ has a mandatory+-- @signature_algorithms@ extension, the @signature_algorithms_cert@+-- extension, which is optional, carries a list of algorithms the+-- server promises to support in verifying the certificate chain.+-- As with TLS 1.2, the client's makes a /best-effort/ to deliver+-- a compatible certificate chain where all the CA signatures are+-- known to be supported, but it should not abort the connection+-- just because the chain might not work out, just send the best+-- chain you have and let the server worry about the rest.  The+-- supported public key algorithms are now inferred from the+-- @signature_algorithms@ extension and @certificate_types@ is+-- gone.+--+-- With TLS 1.3, we synthesize and store a @certificate_types@+-- field at the time that the server's @CertificateRequest@+-- message is received.  This is then present across all the+-- protocol versions, and can be used to determine whether+-- a @CertificateRequest@ was received or not.+--+-- If @signature_algorithms@ is 'Nothing', then we're doing+-- TLS 1.0 or 1.1.  The @signature_algorithms_cert@ extension+-- is optional in TLS 1.3, and so the application callback+-- will not be able to distinguish between TLS 1.[01] and+-- TLS 1.3 with no certificate algorithm hints, but this+-- just simplifies the chain selection process, all CA+-- signatures are OK.+clientChain :: ClientParams -> Context -> IO (Maybe CertificateChain)+clientChain cparams ctx =+    usingHState ctx getCertReqCBdata >>= \case+        Nothing -> return Nothing+        Just cbdata -> do+            let callback = onCertificateRequest $ clientHooks cparams+            chain <-+                liftIO $+                    callback cbdata+                        `catchException` throwMiscErrorOnException "certificate request callback failed"+            case chain of+                Nothing ->+                    return $ Just $ CertificateChain []+                Just (CertificateChain [], _) ->+                    return $ Just $ CertificateChain []+                Just cred@(cc, _) ->+                    do+                        let (cTypes, _, _) = cbdata+                        storePrivInfoClient ctx cTypes cred+                        return $ Just cc++-- | Store the keypair and check that it is compatible with the current protocol+-- version and a list of 'CertificateType' values.+storePrivInfoClient+    :: Context+    -> [CertificateType]+    -> Credential+    -> IO ()+storePrivInfoClient ctx cTypes (cc, privkey) = do+    pubkey <- storePrivInfo ctx cc privkey+    unless (certificateCompatible pubkey cTypes) $+        throwCore $+            Error_Protocol+                (pubkeyType pubkey ++ " credential does not match allowed certificate types")+                InternalError+    ver <- usingState_ ctx getVersion+    unless (pubkey `versionCompatible` ver) $+        throwCore $+            Error_Protocol+                (pubkeyType pubkey ++ " credential is not supported at version " ++ show ver)+                InternalError++----------------------------------------------------------------++-- | Return a most preferred 'HandAndSignatureAlgorithm' that is compatible with+-- the local key and server's signature algorithms (both already saved).  Must+-- only be called for TLS versions 1.2 and up, with compatibility function+-- 'signatureCompatible' or 'signatureCompatible13' based on version.+--+-- The values in the server's @signature_algorithms@ extension are+-- in descending order of preference.  However here the algorithms+-- are selected by client preference in @cHashSigs@.+getLocalHashSigAlg+    :: Context+    -> (PubKey -> HashAndSignatureAlgorithm -> Bool)+    -> [HashAndSignatureAlgorithm]+    -> PubKey+    -> IO HashAndSignatureAlgorithm+getLocalHashSigAlg ctx isCompatible cHashSigs pubKey = do+    -- Must be present with TLS 1.2 and up.+    (Just (_, Just hashSigs, _)) <- usingHState ctx getCertReqCBdata+    let want =+            (&&)+                <$> isCompatible pubKey+                <*> flip elem hashSigs+    case find want cHashSigs of+        Just best -> return best+        Nothing -> throwCore $ Error_Protocol (keyerr pubKey) HandshakeFailure+  where+    keyerr k = "no " ++ pubkeyType k ++ " hash algorithm in common with the server"++----------------------------------------------------------------++setALPN :: Context -> MessageType -> [ExtensionRaw] -> IO ()+setALPN ctx msgt exts =+    lookupAndDecodeAndDo+        EID_ApplicationLayerProtocolNegotiation+        msgt+        exts+        (return ())+        setAlpn+  where+    setAlpn (ApplicationLayerProtocolNegotiation [proto]) = usingState_ ctx $ do+        mprotos <- getClientALPNSuggest+        case mprotos of+            Just protos -> when (proto `elem` protos) $ do+                setExtensionALPN True+                setNegotiatedProtocol proto+            _ -> return ()+    setAlpn _ = return ()++----------------------------------------------------------------++contextSync :: Context -> ClientState -> IO ()+contextSync ctx ctl = case ctxHandshakeSync ctx of+    HandshakeSync sync _ -> sync ctx ctl++clientSessions :: ClientParams -> [(SessionID, SessionData)]+clientSessions ClientParams{..} = case clientWantSessionResume of+    Nothing -> clientWantSessionResumeList+    Just ent -> clientWantSessionResumeList ++ [ent]
+ Network/TLS/Handshake/Client/ServerHello.hs view
@@ -0,0 +1,306 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}++module Network.TLS.Handshake.Client.ServerHello (+    receiveServerHello,+    processServerHello13,+) where++import Data.ByteArray (convert)+import qualified Data.ByteString as B++import Network.TLS.Cipher+import Network.TLS.Compression+import Network.TLS.Context.Internal+import Network.TLS.ErrT+import Network.TLS.Extension+import Network.TLS.Handshake.Client.Common+import Network.TLS.Handshake.Common+import Network.TLS.Handshake.Common13+import Network.TLS.Handshake.Key+import Network.TLS.Handshake.Random+import Network.TLS.Handshake.State+import Network.TLS.Handshake.TranscriptHash+import Network.TLS.IO+import Network.TLS.Imports+import Network.TLS.Packet+import Network.TLS.Parameters+import Network.TLS.State+import Network.TLS.Struct+import Network.TLS.Struct13+import Network.TLS.Types++----------------------------------------------------------------++receiveServerHello+    :: ClientParams+    -> Context+    -> Maybe (ClientRandom, Session, Version)+    -> IO (Version, [HandshakeR], Bool)+receiveServerHello cparams ctx mparams = do+    chSentTime <- getCurrentTimeFromBase+    (shb@(sh, _), hbs) <- recvSH+    processServerHello cparams ctx sh+    updateTranscriptHash12 ctx shb+    setRTT ctx chSentTime+    ver <- usingState_ ctx getVersion+    unless (maybe True (\(_, _, v) -> v == ver) mparams) $+        throwCore $+            Error_Protocol "version changed after hello retry" IllegalParameter+    -- recvServerHello sets TLS13HRR according to the server random.+    -- For 1st server hello, getTLS13HR returns True if it is HRR and+    -- False otherwise.  For 2nd server hello, getTLS13HR returns+    -- False since it is NOT HRR.+    hrr <- usingState_ ctx getTLS13HRR+    return (ver, hbs, hrr)+  where+    recvSH = do+        epkt <- recvPacket12 ctx+        case epkt of+            Left e -> throwCore e+            Right pkt -> case pkt of+                Alert a -> throwAlert a+                Handshake (h : hs) (b : bs) -> return ((h, b), zip hs bs)+                _ -> unexpected (show pkt) (Just "handshake")+    throwAlert a =+        throwCore $+            Error_Protocol+                ("expecting server hello, got alert : " ++ show a)+                HandshakeFailure++----------------------------------------------------------------++processServerHello13+    :: ClientParams -> Context -> Handshake13 -> IO ()+processServerHello13 cparams ctx (ServerHello13 sh13) = do+    let sh12 = ServerHello sh13+    processServerHello cparams ctx sh12+processServerHello13 _ _ h = unexpected (show h) (Just "server hello")++-- | processServerHello processes the ServerHello message on the client.+--+-- 1) check the version chosen by the server is one allowed by+--    parameters.+-- 2) check that our compression and cipher algorithms are part of the+--    list we sent+-- 3) check extensions received are part of the one we sent+-- 4) process the session parameter to see if the server want to start+--    a new session or can resume+processServerHello+    :: ClientParams -> Context -> Handshake -> IO ()+processServerHello cparams ctx (ServerHello sh@SH{..}) = do+    -- A server which receives a legacy_version value not equal to+    -- 0x0303 MUST abort the handshake with an "illegal_parameter"+    -- alert.+    when (shVersion /= TLS12) $+        throwCore $+            Error_Protocol (show shVersion ++ " is not supported") IllegalParameter+    -- find the compression and cipher methods that the server want to use.+    clientSession <- tls13stSession <$> getTLS13State ctx+    chExts <- tls13stSentExtensions <$> getTLS13State ctx+    let clientCiphers = supportedCiphers $ ctxSupported ctx+    usedCipher <- case findCipher (fromCipherId shCipher) clientCiphers of+        Nothing -> throwCore $ Error_Protocol "server choose unknown cipher" IllegalParameter+        Just alg -> return alg+    compressAlg <- case find+        ((==) shComp . compressionID)+        (supportedCompressions $ ctxSupported ctx) of+        Nothing ->+            throwCore $ Error_Protocol "server choose unknown compression" IllegalParameter+        Just alg -> return alg+    ensureNullCompression shComp++    -- intersect sent extensions in client and the received extensions+    -- from server.  if server returns extensions that we didn't+    -- request, fail.+    let checkExt (ExtensionRaw i _)+            | i == EID_Cookie = False -- for HRR+            | otherwise = i `notElem` chExts+    when (any checkExt shExtensions) $+        throwCore $+            Error_Protocol "spurious extensions received" UnsupportedExtension++    let isHRR = isHelloRetryRequest shRandom+    usingState_ ctx $ do+        setTLS13HRR isHRR+        when isHRR $+            setTLS13Cookie $+                lookupAndDecode+                    EID_Cookie+                    MsgTServerHello+                    shExtensions+                    Nothing+                    (\cookie@(Cookie _) -> Just cookie)+        setVersion shVersion -- must be before processing supportedVersions ext+        mapM_ processServerExtension shExtensions++    setALPN ctx MsgTServerHello shExtensions++    ver <- usingState_ ctx getVersion++    when (ver == TLS12) $+        setServerHelloParameters12 ctx shVersion shRandom usedCipher compressAlg++    let supportedVers = supportedVersions $ clientSupported cparams++    when (ver == TLS13) $ do+        -- TLS 1.3 server MUST echo the session id+        when (clientSession /= shSession) $+            throwCore $+                Error_Protocol+                    "session is not matched in compatibility mode"+                    IllegalParameter+        when (ver `notElem` supportedVers) $+            throwCore $+                Error_Protocol+                    ("server version " ++ show ver ++ " is not supported")+                    ProtocolVersion++    -- Some servers set TLS 1.2 as the legacy server hello version,+    -- and TLS 1.3 in the supported_versions extension, *AND ALSO* set+    -- the TLS 1.2 downgrade signal in the server random.  If we+    -- support TLS 1.3 and actually negotiate TLS 1.3, we must ignore+    -- the server random downgrade signal.  Therefore, 'isDowngraded'+    -- needs to take into account the negotiated version and the+    -- server random, as well as the list of client-side enabled+    -- protocol versions.+    --+    when (isDowngraded ver supportedVers shRandom) $+        throwCore $+            Error_Protocol "version downgrade detected" IllegalParameter++    if ver == TLS13+        then do+            -- Session is dummy in TLS 1.3.+            usingState_ ctx $ setSession shSession+            processRecordSizeLimit ctx shExtensions True+            enableMyRecordLimit ctx+            enablePeerRecordLimit ctx+            let usedHash = cipherHash usedCipher+            transitTranscriptHashI ctx "transitI" usedHash isHRR+            accepted <- checkECHacceptance ctx isHRR usedHash sh+            when accepted $ do+                (CH{..}, _b) <- fromJust <$> usingHState ctx getClientHello+                usingHState ctx $ setClientRandom chRandom -- inner random+            when (accepted && not isHRR) $ do+                copyTranscriptHash ctx "copy"+                usingHState ctx $ setECHAccepted True+            updateContext13 ctx usedCipher isHRR+            updateTranscriptHashI ctx "ServerHelloI" $ encodeHandshake $ ServerHello sh+        else do+            let resumingSession = case clientSessions cparams of+                    (_, sessionData) : _ ->+                        if shSession == clientSession then Just sessionData else Nothing+                    _ -> Nothing++            usingState_ ctx $ do+                setSession shSession+                setTLS12SessionResuming $ isJust resumingSession+            processRecordSizeLimit ctx shExtensions False+            updateContext12 ctx shExtensions resumingSession+processServerHello _ _ p = unexpected (show p) (Just "server hello")++----------------------------------------------------------------++processServerExtension :: ExtensionRaw -> TLSSt ()+processServerExtension (ExtensionRaw extID content)+    | extID == EID_SecureRenegotiation = do+        VerifyData cvd <- getVerifyData ClientRole+        VerifyData svd <- getVerifyData ServerRole+        let bs = extensionEncode $ SecureRenegotiation cvd svd+        unless (bs == content) $+            throwError $+                Error_Protocol "server secure renegotiation data not matching" HandshakeFailure+    | extID == EID_SupportedVersions = case extensionDecode MsgTServerHello content of+        Just (SupportedVersionsServerHello ver) -> setVersion ver+        _ -> return ()+    | extID == EID_KeyShare = do+        hrr <- getTLS13HRR+        let msgt = if hrr then MsgTHelloRetryRequest else MsgTServerHello+        setTLS13KeyShare $ extensionDecode msgt content+    | extID == EID_PreSharedKey =+        setTLS13PreSharedKey $ extensionDecode MsgTServerHello content+    | extID == EID_SessionTicket = setTLS12SessionTicket "" -- empty ticket+processServerExtension _ = return ()++----------------------------------------------------------------++updateContext13 :: Context -> Cipher -> Bool -> IO ()+updateContext13 ctx usedCipher isHRR = do+    established <- ctxEstablished ctx+    eof <- ctxEOF ctx+    when (established == Established && not eof) $+        throwCore $+            Error_Protocol+                "renegotiation to TLS 1.3 or later is not allowed"+                ProtocolVersion+    failOnEitherError $ setServerHelloParameters13 ctx usedCipher isHRR++updateContext12 :: Context -> [ExtensionRaw] -> Maybe SessionData -> IO ()+updateContext12 ctx shExtensions resumingSession = do+    ems <- processExtendedMainSecret ctx TLS12 MsgTServerHello shExtensions+    case resumingSession of+        Nothing -> return ()+        Just sessionData -> do+            let emsSession = SessionEMS `elem` sessionFlags sessionData+            when (ems /= emsSession) $+                let err = "server resumes a session which is not EMS consistent"+                 in throwCore $ Error_Protocol err HandshakeFailure+            let mainSecret = convert $ sessionSecret sessionData+            usingHState ctx $ setMainSecret TLS12 ClientRole mainSecret+            logKey ctx (MainSecret mainSecret)++----------------------------------------------------------------++processRecordSizeLimit+    :: Context -> [ExtensionRaw] -> Bool -> IO ()+processRecordSizeLimit ctx shExtensions tls13 = do+    let mmylim = limitRecordSize $ sharedLimit $ ctxShared ctx+    case mmylim of+        Nothing -> return ()+        Just mylim -> do+            lookupAndDecodeAndDo+                EID_RecordSizeLimit+                MsgTClientHello+                shExtensions+                (return ())+                (setPeerRecordSizeLimit ctx tls13)+            ack <- checkPeerRecordLimit ctx+            -- When a client sends RecordSizeLimit, it does not know+            -- which TLS version the server selects.  RecordLimit is+            -- the length of plaintext.  But RecordSizeLimit also+            -- includes CT: and padding for TLS 1.3.  To convert+            -- RecordSizeLimit to RecordLimit, we should reduce the+            -- value by 1, which is the length of CT:.+            when (ack && tls13) $ setMyRecordLimit ctx $ Just (mylim - 1)++----------------------------------------------------------------++checkECHacceptance :: Context -> Bool -> Hash -> ServerHello -> IO Bool+checkECHacceptance ctx False usedHash sh@SH{..} = do+    let (prefix, confirm) = B.splitAt 24 $ unServerRandom shRandom+        sr' = ServerRandom (prefix <> "\x00\x00\x00\x00\x00\x00\x00\x00")+    verified <-+        computeConfirm ctx usedHash sh{shRandom = sr'} "ech accept confirmation"+    return (confirm == verified)+checkECHacceptance ctx True usedHash sh@SH{..} = do+    case replace shExtensions of+        Nothing -> return False+        Just (confirm, shExts') -> do+            verified <-+                computeConfirm+                    ctx+                    usedHash+                    sh{shExtensions = shExts'}+                    "hrr ech accept confirmation"+            return (confirm == verified)+  where+    replace [] = Nothing+    replace (ExtensionRaw EID_EncryptedClientHello confirm : es) =+        Just+            ( confirm+            , ExtensionRaw EID_EncryptedClientHello "\x00\x00\x00\x00\x00\x00\x00\x00" : es+            )+    replace (e : es) = case replace es of+        Nothing -> Nothing+        Just (confirm, es') -> Just (confirm, e : es')
+ Network/TLS/Handshake/Client/TLS12.hs view
@@ -0,0 +1,288 @@+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedStrings #-}++module Network.TLS.Handshake.Client.TLS12 (+    recvServerFirstFlight12,+    sendClientSecondFlight12,+    recvServerSecondFlight12,+) where++import Control.Monad.State.Strict+import Data.ByteArray (convert)+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 -> [HandshakeR] -> IO ()+recvServerFirstFlight12 cparams ctx hbs = do+    resuming <- usingState_ ctx getTLS12SessionResuming+    if resuming+        then recvNSTandCCSandFinished ctx+        else do+            let st = RecvStateHandshake (expectCertificate cparams ctx)+            runRecvStateHS ctx st hbs++expectCertificate :: ClientParams -> Context -> Handshake -> IO (RecvState IO)+expectCertificate cparams ctx (Certificate (CertificateChain_ 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)+    finishHandshake12 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 (CertificateChain_ 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 Secret)+getCKX_RSA ctx = do+    clientVersion <- usingHState ctx $ gets hstClientVersion+    (xver, prerand) <- usingState_ ctx $ (,) <$> getVersion <*> genRandom 46++    let preMain = convert $ 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 Secret)+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 Secret)+getCKX_ECDHE ctx = do+    ServerECDHParams grp srvpub <- usingHState ctx getServerECDHParams+    checkSupportedGroup ctx grp+    usingHState ctx $ setSupportedGroup grp+    ecdhePair <- encapsulateGroup 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 $ groupEncodePublicB 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] []
+ Network/TLS/Handshake/Client/TLS13.hs view
@@ -0,0 +1,425 @@+{-# LANGUAGE OverloadedStrings #-}++module Network.TLS.Handshake.Client.TLS13 (+    recvServerSecondFlight13,+    sendClientSecondFlight13,+    asyncServerHello13,+    postHandshakeAuthClientWith,+) where++import Control.Exception (bracket)+import Control.Monad.State.Strict+import qualified Data.ByteArray as BA+import Data.IORef++import Network.TLS.Cipher+import Network.TLS.Context.Internal+import Network.TLS.Crypto+import Network.TLS.Error+import Network.TLS.Extension+import Network.TLS.Handshake.Client.Common+import Network.TLS.Handshake.Client.ServerHello+import Network.TLS.Handshake.Common hiding (expectFinished)+import Network.TLS.Handshake.Common13+import Network.TLS.Handshake.Control+import Network.TLS.Handshake.Key+import Network.TLS.Handshake.Signature+import Network.TLS.Handshake.State+import Network.TLS.Handshake.State13+import Network.TLS.Handshake.TranscriptHash+import Network.TLS.IO+import Network.TLS.Imports+import Network.TLS.Parameters+import Network.TLS.State+import Network.TLS.Struct+import Network.TLS.Struct13+import Network.TLS.Types+import Network.TLS.X509++----------------------------------------------------------------+----------------------------------------------------------------++recvServerSecondFlight13 :: ClientParams -> Context -> [Group] -> IO ()+recvServerSecondFlight13 cparams ctx groupSent = do+    resuming <- prepareSecondFlight13 ctx groupSent+    runRecvHandshake13 $ do+        recvHandshake13 ctx $ expectEncryptedExtensions ctx+        unless resuming $ recvHandshake13 ctx $ expectCertRequest cparams ctx+        recvHandshake13hash ctx "Finished" $ expectFinished cparams ctx++----------------------------------------------------------------++prepareSecondFlight13+    :: Context -> [Group] -> IO Bool+prepareSecondFlight13 ctx groupSent = do+    choice <- makeCipherChoice TLS13 <$> usingHState ctx getPendingCipher+    prepareSecondFlight13' ctx groupSent choice++prepareSecondFlight13'+    :: Context+    -> [Group]+    -> CipherChoice+    -> IO Bool+prepareSecondFlight13' ctx groupSent choice = do+    (_, hkey, resuming) <- switchToHandshakeSecret+    let clientHandshakeSecret = triClient hkey+        serverHandshakeSecret = triServer hkey+        handSecInfo = HandshakeSecretInfo usedCipher (clientHandshakeSecret, serverHandshakeSecret)+    contextSync ctx $ RecvServerHello handSecInfo+    modifyTLS13State ctx $ \st ->+        st+            { tls13stChoice = choice+            , tls13stHsKey = Just hkey+            }+    return resuming+  where+    usedCipher = cCipher choice+    usedHash = cHash choice++    hashSize = hashDigestSize usedHash++    switchToHandshakeSecret = do+        ensureRecvComplete ctx+        ecdhe <- calcSharedKey+        (earlySecret, resuming) <- makeEarlySecret+        handKey <- calculateHandshakeSecret ctx choice earlySecret ecdhe+        let serverHandshakeSecret = triServer handKey+        setRxRecordState ctx usedHash usedCipher serverHandshakeSecret+        return (usedCipher, handKey, resuming)++    calcSharedKey = do+        serverKeyShare <- do+            mks <- usingState_ ctx getTLS13KeyShare+            case mks of+                Just (KeyShareServerHello ks) -> return ks+                Just _ ->+                    throwCore $ Error_Protocol "invalid key_share value" IllegalParameter+                Nothing ->+                    throwCore $+                        Error_Protocol+                            "key exchange not implemented, expected key_share extension"+                            HandshakeFailure+        let grp = keyShareEntryGroup serverKeyShare+        unless (checkServerKeyShareKeyLength serverKeyShare) $+            throwCore $+                Error_Protocol "broken key_share" IllegalParameter+        unless (grp `elem` groupSent) $+            throwCore $+                Error_Protocol "received incompatible group for (EC)DHE" IllegalParameter+        usingHState ctx $ setSupportedGroup grp+        usingHState ctx getGroupPrivate >>= fromServerKeyShare serverKeyShare++    makeEarlySecret = do+        mEarlySecretPSK <- usingHState ctx getTLS13EarlySecret+        case mEarlySecretPSK of+            Nothing -> return (initEarlySecret choice Nothing, False)+            Just earlySecretPSK@(BaseSecret sec) -> do+                mSelectedIdentity <- usingState_ ctx getTLS13PreSharedKey+                case mSelectedIdentity of+                    Nothing ->+                        return (initEarlySecret choice Nothing, False)+                    Just (PreSharedKeyServerHello 0) -> do+                        unless (BA.length sec == hashSize) $+                            throwCore $+                                Error_Protocol+                                    "selected cipher is incompatible with selected PSK"+                                    IllegalParameter+                        usingHState ctx $ setTLS13HandshakeMode PreSharedKey+                        return (earlySecretPSK, True)+                    Just _ ->+                        throwCore $ Error_Protocol "selected identity out of range" IllegalParameter++----------------------------------------------------------------++expectEncryptedExtensions+    :: MonadIO m => Context -> Handshake13 -> m ()+expectEncryptedExtensions ctx (EncryptedExtensions13 eexts) = do+    liftIO $ do+        setALPN ctx MsgTEncryptedExtensions eexts+        modifyTLS13State ctx $ \st -> st{tls13stClientExtensions = eexts}+    st13 <- usingHState ctx getTLS13RTT0Status+    when (st13 == RTT0Sent) $+        case extensionLookup EID_EarlyData eexts of+            Just _ -> do+                usingHState ctx $ setTLS13HandshakeMode RTT0+                usingHState ctx $ setTLS13RTT0Status RTT0Accepted+                liftIO $ modifyTLS13State ctx $ \st -> st{tls13st0RTTAccepted = True}+            Nothing -> do+                usingHState ctx $ setTLS13HandshakeMode PreSharedKey+                usingHState ctx $ setTLS13RTT0Status RTT0Rejected+expectEncryptedExtensions _ h = unexpected (show h) (Just "encrypted extensions")++----------------------------------------------------------------+-- not used in 0-RTT+expectCertRequest+    :: MonadIO m+    => ClientParams -> Context -> Handshake13 -> RecvHandshake13M m ()+expectCertRequest cparams ctx (CertRequest13 token exts) = do+    processCertRequest13 ctx token exts+    recvHandshake13 ctx $ expectCertAndVerify cparams ctx+expectCertRequest cparams ctx other = do+    usingHState ctx $ do+        setCertReqToken Nothing+        setCertReqCBdata Nothing+    -- setCertReqSigAlgsCert Nothing+    expectCertAndVerify cparams ctx other++processCertRequest13+    :: MonadIO m => Context -> CertReqContext -> [ExtensionRaw] -> m ()+processCertRequest13 ctx token exts = do+    let hsextID = EID_SignatureAlgorithms+    -- caextID = EID_SignatureAlgorithmsCert+    dNames <- canames+    -- The @signature_algorithms@ extension is mandatory.+    hsAlgs <- extalgs hsextID unsighash+    cTypes <- case hsAlgs of+        Just as ->+            let validAs = filter isHashSignatureValid13 as+             in return $ sigAlgsToCertTypes ctx validAs+        Nothing -> throwCore $ Error_Protocol "invalid certificate request" HandshakeFailure+    -- Unused:+    -- caAlgs <- extalgs caextID uncertsig+    let zlib =+            lookupAndDecode+                EID_CompressCertificate+                MsgTClientHello+                exts+                False+                (\(CompressCertificate ccas) -> CCA_Zlib `elem` ccas)+    usingHState ctx $ do+        setCertReqToken $ Just token+        setCertReqCBdata $ Just (cTypes, hsAlgs, dNames)+        setTLS13CertComp zlib+  where+    -- setCertReqSigAlgsCert caAlgs++    canames = case extensionLookup EID_CertificateAuthorities exts of+        Nothing -> return []+        Just ext -> case extensionDecode MsgTCertificateRequest ext of+            Just (CertificateAuthorities names) -> return names+            _ -> throwCore $ Error_Protocol "invalid certificate request" HandshakeFailure+    extalgs extID decons = case extensionLookup extID exts of+        Nothing -> return Nothing+        Just ext -> case extensionDecode MsgTCertificateRequest ext of+            Just e ->+                return $ decons e+            _ -> throwCore $ Error_Protocol "invalid certificate request" HandshakeFailure+    unsighash+        :: SignatureAlgorithms+        -> Maybe [HashAndSignatureAlgorithm]+    unsighash (SignatureAlgorithms a) = Just a++----------------------------------------------------------------+-- not used in 0-RTT+expectCertAndVerify+    :: MonadIO m+    => ClientParams -> Context -> Handshake13 -> RecvHandshake13M m ()+expectCertAndVerify cparams ctx (Certificate13 _ (CertificateChain_ cc) _) = processCertAndVerify cparams ctx cc+expectCertAndVerify cparams ctx (CompressedCertificate13 _ (CertificateChain_ cc) _) = processCertAndVerify cparams ctx cc+expectCertAndVerify _ _ h = unexpected (show h) (Just "server certificate")++processCertAndVerify+    :: MonadIO m+    => ClientParams -> Context -> CertificateChain -> RecvHandshake13M m ()+processCertAndVerify cparams ctx cc = do+    liftIO $ usingState_ ctx $ setServerCertificateChain cc+    liftIO $ doCertificate cparams ctx cc+    let pubkey = certPubKey $ getCertificate $ getCertificateChainLeaf cc+    ver <- liftIO $ usingState_ ctx getVersion+    checkDigitalSignatureKey ver pubkey+    usingHState ctx $ setPublicKey pubkey+    recvHandshake13hash ctx "CertVerify" $ expectCertVerify ctx pubkey++----------------------------------------------------------------++expectCertVerify+    :: MonadIO m+    => Context -> PubKey -> TranscriptHash -> Handshake13 -> m ()+expectCertVerify ctx pubkey (TranscriptHash hChSc) (CertVerify13 (DigitallySigned sigAlg sig)) = do+    ok <- checkCertVerify ctx pubkey sigAlg sig hChSc+    unless ok $ decryptError "cannot verify CertificateVerify"+expectCertVerify _ _ _ h = unexpected (show h) (Just "certificate verify")++----------------------------------------------------------------++expectFinished+    :: MonadIO m+    => ClientParams+    -> Context+    -> TranscriptHash+    -> Handshake13+    -> m ()+expectFinished cparams ctx hashValue (Finished13 verifyData) = do+    st <- liftIO $ getTLS13State ctx+    let usedHash = cHash $ tls13stChoice st+        ServerTrafficSecret baseKey = triServer $ fromJust $ tls13stHsKey st+    checkFinished ctx usedHash baseKey hashValue verifyData+    liftIO $ do+        minfo <- contextGetInformation ctx+        case minfo of+            Nothing -> return ()+            Just info -> onServerFinished (clientHooks cparams) info+    liftIO $ modifyTLS13State ctx $ \s -> s{tls13stRecvSF = True}+expectFinished _ _ _ p = unexpected (show p) (Just "server finished")++----------------------------------------------------------------+----------------------------------------------------------------++sendClientSecondFlight13 :: ClientParams -> Context -> IO ()+sendClientSecondFlight13 cparams ctx = do+    st <- getTLS13State ctx+    let choice = tls13stChoice st+        hkey = fromJust $ tls13stHsKey st+        rtt0accepted = tls13st0RTTAccepted st+        eexts = tls13stClientExtensions st+    sendClientSecondFlight13' cparams ctx choice hkey rtt0accepted eexts+    modifyTLS13State ctx $ \s -> s{tls13stSentCF = True}+    echAccepted <- usingHState ctx getECHAccepted+    when (clientUseECH cparams && not echAccepted) $+        throwCore $+            Error_Protocol "ECH is not accepted" EchRequired++sendClientSecondFlight13'+    :: ClientParams+    -> Context+    -> CipherChoice+    -> SecretTriple HandshakeSecret+    -> Bool+    -> [ExtensionRaw]+    -> IO ()+sendClientSecondFlight13' cparams ctx choice hkey rtt0accepted eexts = do+    hChSf <- transcriptHash ctx "CH..SF"+    unless (ctxQUICMode ctx) $+        runPacketFlight ctx $+            sendChangeCipherSpec13 ctx+    when (rtt0accepted && not (ctxQUICMode ctx)) $+        sendPacket13 ctx (Handshake13 [EndOfEarlyData13] [])+    let clientHandshakeSecret = triClient hkey+    setTxRecordState ctx usedHash usedCipher clientHandshakeSecret+    sendClientFlight13 cparams ctx usedHash clientHandshakeSecret+    appKey <- switchToApplicationSecret hChSf+    let applicationSecret = triBase appKey+    setResumptionSecret applicationSecret+    let appSecInfo = ApplicationSecretInfo (triClient appKey, triServer appKey)+    contextSync ctx $ SendClientFinished eexts appSecInfo+    modifyTLS13State ctx $ \st -> st{tls13stHsKey = Nothing}+    finishHandshake13 ctx+    rtt0 <- tls13st0RTT <$> getTLS13State ctx+    when rtt0 $ do+        builder <- tls13stPendingSentData <$> getTLS13State ctx+        modifyTLS13State ctx $ \st -> st{tls13stPendingSentData = id}+        unless rtt0accepted $+            mapM_ (sendPacket13 ctx . AppData13) $+                builder []+  where+    usedCipher = cCipher choice+    usedHash = cHash choice++    switchToApplicationSecret hChSf = do+        ensureRecvComplete ctx+        let handshakeSecret = triBase hkey+        appKey <- calculateApplicationSecret ctx choice handshakeSecret hChSf+        let serverApplicationSecret0 = triServer appKey+        let clientApplicationSecret0 = triClient appKey+        setTxRecordState ctx usedHash usedCipher clientApplicationSecret0+        setRxRecordState ctx usedHash usedCipher serverApplicationSecret0+        return appKey++    setResumptionSecret applicationSecret = do+        resumptionSecret <- calculateResumptionSecret ctx choice applicationSecret+        usingHState ctx $ setTLS13ResumptionSecret resumptionSecret++{- Unused for now+uncertsig :: SignatureAlgorithmsCert+          -> Maybe [HashAndSignatureAlgorithm]+uncertsig (SignatureAlgorithmsCert a) = Just a+-}++sendClientFlight13+    :: ClientParams -> Context -> Hash -> ClientTrafficSecret a -> IO ()+sendClientFlight13 cparams ctx usedHash (ClientTrafficSecret baseKey) = do+    mcc <- clientChain cparams ctx+    runPacketFlight ctx $ do+        case mcc of+            Nothing -> return ()+            Just cc -> do+                reqtoken <- usingHState ctx getCertReqToken+                certComp <- usingHState ctx getTLS13CertComp+                loadClientData13 cc reqtoken certComp+        rawFinished <- makeFinished ctx usedHash baseKey+        loadPacket13 ctx $ Handshake13 [rawFinished] []+    when (isJust mcc) $+        modifyTLS13State ctx $+            \st -> st{tls13stSentClientCert = True}+  where+    loadClientData13 chain (Just token) certComp = do+        let (CertificateChain certs) = chain+            certExts = replicate (length certs) []+            cHashSigs = filter isHashSignatureValid13 $ supportedHashSignatures $ ctxSupported ctx+        let certtag = if certComp then CompressedCertificate13 else Certificate13+        loadPacket13 ctx $+            Handshake13 [certtag token (CertificateChain_ chain) certExts] []+        case certs of+            [] -> return ()+            _ -> do+                hChSc <- transcriptHash ctx "CH..SC"+                pubKey <- getLocalPublicKey ctx+                sigAlg <-+                    liftIO $ getLocalHashSigAlg ctx signatureCompatible13 cHashSigs pubKey+                vfy <- makeCertVerify ctx pubKey sigAlg hChSc+                loadPacket13 ctx $ Handshake13 [vfy] []+    --+    loadClientData13 _ _ _ =+        throwCore $+            Error_Protocol "missing TLS 1.3 certificate request context token" InternalError++----------------------------------------------------------------+----------------------------------------------------------------++postHandshakeAuthClientWith+    :: ClientParams -> Context -> Handshake13 -> IO ()+postHandshakeAuthClientWith cparams ctx (CertRequest13 certReqCtx exts) =+    bracket (saveHState ctx) (restoreHState ctx) $ \_ -> do+        --        updateTranscriptHash13 ctx h b+        processCertRequest13 ctx certReqCtx exts+        (usedHash, _, level, applicationSecretN) <- getTxRecordState ctx+        unless (level == CryptApplicationSecret) $+            throwCore $+                Error_Protocol+                    "unexpected post-handshake authentication request"+                    UnexpectedMessage+        sendClientFlight13+            cparams+            ctx+            usedHash+            (ClientTrafficSecret applicationSecretN)+postHandshakeAuthClientWith _ _ _ =+    throwCore $+        Error_Protocol+            "unexpected handshake message received in postHandshakeAuthClientWith"+            UnexpectedMessage++----------------------------------------------------------------+----------------------------------------------------------------++asyncServerHello13+    :: ClientParams -> Context -> [Group] -> Millisecond -> IO ()+asyncServerHello13 cparams ctx groupSent chSentTime = do+    setPendingRecvActions+        ctx+        [ PendingRecvActionSelfUpdate True expectServerHello+        , PendingRecvAction True (expectEncryptedExtensions ctx)+        , PendingRecvActionHash True expectFinishedAndSet+        ]+  where+    expectServerHello shb@(sh, _) = do+        setRTT ctx chSentTime+        processServerHello13 cparams ctx sh+        updateTranscriptHash13 ctx shb -- update by myself+        void $ prepareSecondFlight13 ctx groupSent+    expectFinishedAndSet h sf = do+        expectFinished cparams ctx h sf+        liftIO $+            writeIORef (ctxPendingSendAction ctx) $+                Just $+                    sendClientSecondFlight13 cparams
Network/TLS/Handshake/Common.hs view
@@ -1,141 +1,442 @@+{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-}-module Network.TLS.Handshake.Common-    ( handshakeFailed-    , errorToAlert-    , unexpected-    , newSession-    , handshakeTerminate++module Network.TLS.Handshake.Common (+    handshakeFailed,+    handleException,+    unexpected,+    newSession,+    ensureNullCompression,+    ticketOrSessionID12,+     -- * sending packets-    , sendChangeCipherAndFinish+    sendCCSandFinished,+     -- * receiving packets-    , recvChangeCipherAndFinish-    , RecvState(..)-    , runRecvState-    , recvPacketHandshake-    , onRecvStateHandshake-    , extensionLookup-    ) where+    RecvState (..),+    runRecvState,+    runRecvStateHS,+    recvPacketHandshake,+    onRecvStateHandshake,+    ensureRecvComplete,+    processExtendedMainSecret,+    getSessionData,+    storePrivInfo,+    isSupportedGroup,+    checkSupportedGroup,+    errorToAlert,+    errorToAlertMessage,+    expectFinished,+    processCertificate,+    --+    setPeerRecordSizeLimit,+    generateFinished,+    encodeUpdateTranscriptHash12,+    updateTranscriptHash12,+    --+    startHandshake,+    finishHandshake12,+    setServerHelloParameters12,+) where  import Control.Concurrent.MVar+import Control.Exception (IOException, fromException, handle, throwIO)+import Control.Monad.State.Strict+import Data.ByteArray (convert)+import qualified Data.ByteString as B -import Network.TLS.Parameters+import Network.TLS.Cipher import Network.TLS.Compression import Network.TLS.Context.Internal-import Network.TLS.Session-import Network.TLS.Struct-import Network.TLS.IO-import Network.TLS.State hiding (getNegotiatedProtocol)-import Network.TLS.Handshake.Process+import Network.TLS.Crypto+import Network.TLS.Extension+import Network.TLS.Handshake.Key+import Network.TLS.Handshake.Signature import Network.TLS.Handshake.State-import Network.TLS.Record.State+import Network.TLS.Handshake.State13+import Network.TLS.Handshake.TranscriptHash+import Network.TLS.IO+import Network.TLS.IO.Encode+import Network.TLS.Imports import Network.TLS.Measurement+import Network.TLS.Packet+import Network.TLS.Parameters+import Network.TLS.State+import Network.TLS.Struct+import Network.TLS.Struct13 import Network.TLS.Types-import Network.TLS.Cipher import Network.TLS.Util-import Network.TLS.Imports--import Control.Monad.State.Strict-import Control.Exception (throwIO)+import Network.TLS.X509  handshakeFailed :: TLSError -> IO () handshakeFailed err = throwIO $ HandshakeFailed err -errorToAlert :: TLSError -> Packet-errorToAlert (Error_Protocol (_, _, ad)) = Alert [(AlertLevel_Fatal, ad)]-errorToAlert _                           = Alert [(AlertLevel_Fatal, InternalError)]+handleException :: Context -> IO () -> IO ()+handleException ctx f = catchException f $ \exception -> do+    debugError (ctxDebug ctx) $ show exception+    -- If the error was an Uncontextualized TLSException, we replace the+    -- context with HandshakeFailed. If it's anything else, we convert+    -- it to a string and wrap it with Error_Misc and HandshakeFailed.+    let tlserror = case fromException exception of+            Just e | Uncontextualized e' <- e -> e'+            _ -> Error_Misc (show exception)+    established <- ctxEstablished ctx+    setEstablished ctx NotEstablished+    handle ignoreIOErr $ do+        tls13 <- tls13orLater ctx+        if tls13+            then do+                when (established == EarlyDataSending) $ clearTxRecordState ctx+                when (tlserror /= Error_TCP_Terminate) $+                    sendPacket13 ctx $+                        Alert13 [errorToAlert tlserror]+            else sendPacket12 ctx $ Alert [errorToAlert tlserror]+    handshakeFailed tlserror+  where+    ignoreIOErr :: IOException -> IO ()+    ignoreIOErr _ = return () -unexpected :: String -> Maybe String -> IO a-unexpected msg expected = throwCore $ Error_Packet_unexpected msg (maybe "" (" expected: " ++) expected)+errorToAlert :: TLSError -> (AlertLevel, AlertDescription)+errorToAlert (Error_Protocol _ ad) = (AlertLevel_Fatal, ad)+errorToAlert (Error_Protocol_Warning _ ad) = (AlertLevel_Warning, ad)+errorToAlert (Error_Packet_unexpected _ _) = (AlertLevel_Fatal, UnexpectedMessage)+errorToAlert (Error_Packet_Parsing msg)+    | "invalid version" `isInfixOf` msg = (AlertLevel_Fatal, ProtocolVersion)+    | "request_update" `isInfixOf` msg = (AlertLevel_Fatal, IllegalParameter)+    | otherwise = (AlertLevel_Fatal, DecodeError)+errorToAlert _ = (AlertLevel_Fatal, InternalError) +-- | Return the message that a TLS endpoint can add to its local log for the+-- specified library error.+errorToAlertMessage :: TLSError -> String+errorToAlertMessage (Error_Protocol msg _) = msg+errorToAlertMessage (Error_Protocol_Warning msg _) = msg+errorToAlertMessage (Error_Packet_unexpected msg _) = msg+errorToAlertMessage (Error_Packet_Parsing msg) = msg+errorToAlertMessage e = show e++unexpected :: MonadIO m => String -> Maybe String -> m a+unexpected msg expected =+    throwCore $ Error_Packet_unexpected msg (maybe "" (" expected: " ++) expected)+ newSession :: Context -> IO Session newSession ctx     | supportedSession $ ctxSupported ctx = Session . Just <$> getStateRNG ctx 32-    | otherwise                           = return $ Session Nothing---- | when a new handshake is done, wrap up & clean up.-handshakeTerminate :: Context -> IO ()-handshakeTerminate ctx = do-    session <- usingState_ ctx getSession-    -- only callback the session established if we have a session-    case session of-        Session (Just sessionId) -> do-            sessionData <- getSessionData ctx-            liftIO $ sessionEstablish (sharedSessionManager $ ctxShared ctx) sessionId (fromJust "session-data" sessionData)-        _ -> return ()-    -- forget most handshake data and reset bytes counters.-    liftIO $ modifyMVar_ (ctxHandshake ctx) $ \ mhshake ->-        case mhshake of-            Nothing -> return Nothing-            Just hshake ->-                return $ Just (newEmptyHandshake (hstClientVersion hshake) (hstClientRandom hshake))-                    { hstServerRandom = hstServerRandom hshake-                    , hstMasterSecret = hstMasterSecret hshake-                    }-    updateMeasure ctx resetBytesCounters-    -- mark the secure connection up and running.-    setEstablished ctx True-    return ()--sendChangeCipherAndFinish :: Context-                          -> Role-                          -> IO ()-sendChangeCipherAndFinish ctx role = do-    sendPacket ctx ChangeCipherSpec-    liftIO $ contextFlush ctx-    cf <- usingState_ ctx getVersion >>= \ver -> usingHState ctx $ getHandshakeDigest ver role-    sendPacket ctx (Handshake [Finished cf])-    liftIO $ contextFlush ctx+    | otherwise = return $ Session Nothing -recvChangeCipherAndFinish :: Context -> IO ()-recvChangeCipherAndFinish ctx = runRecvState ctx (RecvStateNext expectChangeCipher)-  where expectChangeCipher ChangeCipherSpec = return $ RecvStateHandshake expectFinish-        expectChangeCipher p                = unexpected (show p) (Just "change cipher")-        expectFinish (Finished _) = return RecvStateDone-        expectFinish p            = unexpected (show p) (Just "Handshake Finished")+sendCCSandFinished+    :: Context+    -> Role+    -> IO ()+sendCCSandFinished ctx role = do+    sendPacket12 ctx ChangeCipherSpec+    contextFlush ctx+    enablePeerRecordLimit ctx+    ver <- usingState_ ctx getVersion+    verifyData <- VerifyData <$> generateFinished ctx ver role+    sendPacket12 ctx (Handshake [Finished verifyData] [])+    usingState_ ctx $ setVerifyDataForSend verifyData+    contextFlush ctx -data RecvState m =-      RecvStateNext (Packet -> m (RecvState m))+data RecvState m+    = RecvStatePacket (Packet -> m (RecvState m)) -- CCS is not Handshake     | RecvStateHandshake (Handshake -> m (RecvState m))     | RecvStateDone -recvPacketHandshake :: Context -> IO [Handshake]+recvPacketHandshake :: Context -> IO [HandshakeR] recvPacketHandshake ctx = do-    pkts <- recvPacket ctx+    pkts <- recvPacket12 ctx     case pkts of-        Right (Handshake l) -> return l-        Right x             -> fail ("unexpected type received. expecting handshake and got: " ++ show x)-        Left err            -> throwCore err+        Right (Handshake hss bss) -> return $ zip hss bss+        Right x@(AppData _) -> do+            -- If a TLS13 server decides to reject RTT0 data, the server should+            -- skip records for RTT0 data up to the maximum limit.+            established <- ctxEstablished ctx+            case established of+                EarlyDataNotAllowed n+                    | n > 0 -> do+                        setEstablished ctx $ EarlyDataNotAllowed (n - 1)+                        recvPacketHandshake ctx+                _ -> unexpected (show x) (Just "handshake")+        Right x -> unexpected (show x) (Just "handshake")+        Left err -> throwCore err  -- | process a list of handshakes message in the recv state machine.-onRecvStateHandshake :: Context -> RecvState IO -> [Handshake] -> IO (RecvState IO)-onRecvStateHandshake _   recvState [] = return recvState-onRecvStateHandshake ctx (RecvStateHandshake f) (x:xs) = do-    nstate <- f x-    processHandshake ctx x-    onRecvStateHandshake ctx nstate xs-onRecvStateHandshake _ _ _   = unexpected "spurious handshake" Nothing+onRecvStateHandshake+    :: Context -> RecvState IO -> [HandshakeR] -> IO (RecvState IO)+onRecvStateHandshake _ recvState [] = return recvState+onRecvStateHandshake _ (RecvStatePacket f) hbs = do+    let (hss, bss) = unzip hbs+    f (Handshake hss bss)+onRecvStateHandshake ctx (RecvStateHandshake f) (hb@(h, _) : hbs) = do+    let finished = isFinished h+    unless finished $ void $ updateTranscriptHash12 ctx hb+    nstate <- f h+    when finished $ void $ updateTranscriptHash12 ctx hb+    onRecvStateHandshake ctx nstate hbs+onRecvStateHandshake _ _ _ = unexpected "spurious handshake" Nothing +isFinished :: Handshake -> Bool+isFinished Finished{} = True+isFinished _ = False+ runRecvState :: Context -> RecvState IO -> IO ()-runRecvState _    RecvStateDone    = return ()-runRecvState ctx (RecvStateNext f) = recvPacket ctx >>= either throwCore f >>= runRecvState ctx-runRecvState ctx iniState          = recvPacketHandshake ctx >>= onRecvStateHandshake ctx iniState >>= runRecvState ctx+runRecvState _ RecvStateDone = return ()+runRecvState ctx (RecvStatePacket f) = recvPacket12 ctx >>= either throwCore f >>= runRecvState ctx+runRecvState ctx iniState =+    recvPacketHandshake ctx+        >>= onRecvStateHandshake ctx iniState+        >>= runRecvState ctx +runRecvStateHS+    :: Context -> RecvState IO -> [HandshakeR] -> IO ()+runRecvStateHS ctx iniState hbs = onRecvStateHandshake ctx iniState hbs >>= runRecvState ctx++ensureRecvComplete :: MonadIO m => Context -> m ()+ensureRecvComplete ctx = do+    complete <- liftIO $ isRecvComplete ctx+    unless complete $+        throwCore $+            Error_Protocol "received incomplete message at key change" UnexpectedMessage++processExtendedMainSecret+    :: MonadIO m => Context -> Version -> MessageType -> [ExtensionRaw] -> m Bool+processExtendedMainSecret ctx ver msgt exts+    | ver < TLS10 = return False+    | ver > TLS12 = error "EMS processing is not compatible with TLS 1.3"+    | ems == NoEMS = return False+    | otherwise =+        liftIO $+            lookupAndDecodeAndDo+                EID_ExtendedMainSecret+                msgt+                exts+                nonExistAction+                existAction+  where+    ems = supportedExtendedMainSecret $ ctxSupported ctx+    err = "peer does not support Extended Main Secret"+    nonExistAction =+        if ems == RequireEMS+            then throwCore $ Error_Protocol err HandshakeFailure+            else return False+    existAction ExtendedMainSecret = do+        usingHState ctx $ setExtendedMainSecret True+        return True+ getSessionData :: Context -> IO (Maybe SessionData) getSessionData ctx = do     ver <- usingState_ ctx getVersion     sni <- usingState_ ctx getClientSNI-    mms <- usingHState ctx (gets hstMasterSecret)-    tx  <- liftIO $ readMVar (ctxTxState ctx)+    mms <- usingHState ctx $ gets hstMainSecret+    ems <- usingHState ctx getExtendedMainSecret+    cipher <- cipherID <$> usingHState ctx getPendingCipher+    alpn <- usingState_ ctx getNegotiatedProtocol+    let compression = 0+        flags = [SessionEMS | ems]     case mms of         Nothing -> return Nothing-        Just ms -> return $ Just SessionData-                        { sessionVersion     = ver-                        , sessionCipher      = cipherID $ fromJust "cipher" $ stCipher tx-                        , sessionCompression = compressionID $ stCompression tx-                        , sessionClientSNI   = sni-                        , sessionSecret      = ms+        Just ms ->+            return $+                Just+                    SessionData+                        { sessionVersion = ver+                        , sessionCipher = cipher+                        , sessionCompression = compression+                        , sessionClientSNI = sni+                        , sessionSecret = convert ms+                        , sessionGroup = Nothing+                        , sessionTicketInfo = Nothing+                        , sessionALPN = alpn+                        , sessionMaxEarlyDataSize = 0+                        , sessionFlags = flags                         } -extensionLookup :: ExtensionID -> [ExtensionRaw] -> Maybe ByteString-extensionLookup toFind = fmap (\(ExtensionRaw _ content) -> content)-                       . find (\(ExtensionRaw eid _) -> eid == toFind)+-- | Store the specified keypair.  Whether the public key and private key+-- actually match is left for the peer to discover.  We're not presently+-- burning  CPU to detect that misconfiguration.  We verify only that the+-- types of keys match and that it does not include an algorithm that would+-- not be safe.+storePrivInfo+    :: MonadIO m+    => Context+    -> CertificateChain+    -> PrivKey+    -> m PubKey+storePrivInfo ctx cc privkey = do+    let c = fromCC cc+        pubkey = certPubKey $ getCertificate c+    unless (isDigitalSignaturePair (pubkey, privkey)) $+        throwCore $+            Error_Protocol "mismatched or unsupported private key pair" InternalError+    usingHState ctx $ setPublicPrivateKeys (pubkey, privkey)+    return pubkey+  where+    fromCC (CertificateChain (c : _)) = c+    fromCC _ = error "fromCC"++-- verify that the group selected by the peer is supported in the local+-- configuration+checkSupportedGroup :: Context -> Group -> IO ()+checkSupportedGroup ctx grp =+    unless (isSupportedGroup ctx grp) $+        let msg = "unsupported (EC)DHE group: " ++ show grp+         in throwCore $ Error_Protocol msg IllegalParameter++isSupportedGroup :: Context -> Group -> Bool+isSupportedGroup ctx grp = grp `elem` supportedGroups (ctxSupported ctx)++ensureNullCompression :: MonadIO m => CompressionID -> m ()+ensureNullCompression compression =+    when (compression /= compressionID nullCompression) $+        throwCore $+            Error_Protocol "compression is not allowed in TLS 1.3" IllegalParameter++expectFinished :: Context -> Handshake -> IO (RecvState IO)+expectFinished ctx (Finished verifyData) = do+    processFinished ctx verifyData+    return RecvStateDone+expectFinished _ p = unexpected (show p) (Just "Handshake Finished")++processFinished :: Context -> VerifyData -> IO ()+processFinished ctx verifyData = do+    (cc, ver) <- usingState_ ctx $ (,) <$> getRole <*> getVersion+    expected <- VerifyData <$> generateFinished ctx ver (invertRole cc)+    when (expected /= verifyData) $ decryptError "finished verification failed"+    usingState_ ctx $ setVerifyDataForRecv verifyData++processCertificate :: Context -> Role -> CertificateChain -> IO ()+processCertificate _ ServerRole (CertificateChain []) = return ()+processCertificate _ ClientRole (CertificateChain []) =+    throwCore $ Error_Protocol "server certificate missing" HandshakeFailure+processCertificate ctx _ (CertificateChain (c : _)) =+    usingHState ctx $ setPublicKey pubkey+  where+    pubkey = certPubKey $ getCertificate c++-- TLS 1.2 distinguishes session ID and session ticket.  session+-- ticket. Session ticket is prioritized over session ID.+ticketOrSessionID12+    :: Maybe Ticket -> Session -> Maybe SessionIDorTicket+ticketOrSessionID12 (Just ticket) _+    | ticket /= "" = Just $ B.copy ticket+ticketOrSessionID12 _ (Session (Just sessionId)) = Just $ B.copy sessionId+ticketOrSessionID12 _ _ = Nothing++setPeerRecordSizeLimit :: Context -> Bool -> RecordSizeLimit -> IO ()+setPeerRecordSizeLimit ctx tls13 (RecordSizeLimit n0) = do+    when (n0 < 64) $+        throwCore $+            Error_Protocol ("too small recode size limit: " ++ show n0) IllegalParameter++    -- RFC 8449 Section 4:+    -- Even if a larger record size limit is provided by a peer, an+    -- endpoint MUST NOT send records larger than the protocol-defined+    -- limit, unless explicitly allowed by a future TLS version or+    -- extension.+    let n1 = fromIntegral n0+        n2+            | n1 > protolim = protolim+            | otherwise = n1+    -- Even if peer's value is larger than the protocol-defined+    -- limitation, call "setPeerRecordLimit" to send+    -- "record_size_limit" as ACK.  In this case, the protocol-defined+    -- limitation is used.+    let lim = if tls13 then n2 - 1 else n2+    setPeerRecordLimit ctx $ Just lim+  where+    protolim+        | tls13 = defaultRecordSizeLimit + 1+        | otherwise = defaultRecordSizeLimit++----------------------------------------------------------------++generateFinished :: Context -> Version -> Role -> IO ByteString+generateFinished ctx ver role = do+    thash <- transcriptHash ctx "generateFinished"+    (mainSecret, cipher) <- usingHState ctx $ gets $ \hst ->+        (fromJust $ hstMainSecret hst, fromJust $ hstPendingCipher hst)+    return $+        if role == ClientRole+            then+                generateClientFinished ver cipher mainSecret thash+            else+                generateServerFinished ver cipher mainSecret thash++generateFinished'+    :: PRF -> ByteString -> Secret -> TranscriptHash -> ByteString+generateFinished' prf label mainSecret (TranscriptHash thash) = convert $ prf mainSecret seed 12+  where+    seed = label <> thash++generateClientFinished+    :: Version+    -> Cipher+    -> Secret+    -> TranscriptHash+    -> ByteString+generateClientFinished ver ciph =+    generateFinished' (getPRF ver ciph) "client finished"++generateServerFinished+    :: Version+    -> Cipher+    -> Secret+    -> TranscriptHash+    -> ByteString+generateServerFinished ver ciph =+    generateFinished' (getPRF ver ciph) "server finished"++----------------------------------------------------------------++-- initialize a new Handshake context (initial handshake or renegotiations)+startHandshake :: Context -> Version -> ClientRandom -> IO ()+startHandshake ctx ver crand =+    void $ swapMVar (ctxHandshakeState ctx) $ Just hs+  where+    hs = newEmptyHandshake ver crand++setServerHelloParameters12+    :: Context+    -> Version+    -- ^ chosen version+    -> ServerRandom+    -> Cipher+    -> Compression+    -> IO ()+setServerHelloParameters12 ctx ver sran cipher compression = do+    usingHState ctx $+        modify' $ \hst ->+            hst+                { hstServerRandom = Just sran+                , hstPendingCipher = Just cipher+                , hstPendingCompression = compression+                }+    transitTranscriptHash ctx "transit" (getHash ver cipher) False++-- The TLS12 Hash is cipher specific, and some TLS12 algorithms use SHA384+-- instead of the default SHA256.+getHash :: Version -> Cipher -> Hash+getHash ver ciph+    | ver < TLS12 = SHA1_MD5+    | maybe True (< TLS12) (cipherMinVer ciph) = SHA256+    | otherwise = cipherHash ciph++-- | when a new handshake is done, wrap up & clean up.+finishHandshake12 :: Context -> IO ()+finishHandshake12 ctx = do+    -- forget most handshake data and reset bytes counters.+    modifyMVar_ (ctxHandshakeState ctx) $ \case+        Nothing -> return Nothing+        Just hshake ->+            return $+                Just+                    (newEmptyHandshake (hstClientVersion hshake) (hstClientRandom hshake))+                        { hstServerRandom = hstServerRandom hshake+                        , hstMainSecret = hstMainSecret hshake+                        , hstExtendedMainSecret = hstExtendedMainSecret hshake+                        , hstSupportedGroup = hstSupportedGroup hshake+                        }+    updateMeasure ctx resetBytesCounters+    -- mark the secure connection up and running.+    setEstablished ctx Established
+ Network/TLS/Handshake/Common13.hs view
@@ -0,0 +1,693 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}++module Network.TLS.Handshake.Common13 (+    makeFinished,+    checkFinished,+    makeServerKeyShare,+    makeClientKeyShare,+    fromServerKeyShare,+    makeCertVerify,+    checkCertVerify,+    makePSKBinder,+    replacePSKBinder,+    sendChangeCipherSpec13,+    makeCertRequest,+    createTLS13TicketInfo,+    ageToObfuscatedAge,+    isAgeValid,+    getAge,+    checkFreshness,+    getCurrentTimeFromBase,+    getSessionData13,+    isHashSignatureValid13,+    safeNonNegative32,+    RecvHandshake13M,+    runRecvHandshake13,+    recvHandshake13,+    recvHandshake13hash,+    CipherChoice (..),+    makeCipherChoice,+    initEarlySecret,+    calculateEarlySecret,+    calculateHandshakeSecret,+    calculateApplicationSecret,+    calculateResumptionSecret,+    derivePSK,+    checkClientKeyShareKeyLength,+    checkServerKeyShareKeyLength,+    setRTT,+    computeConfirm,+    updateTranscriptHash13,+    setServerHelloParameters13,+    finishHandshake13,+) where++import Control.Concurrent.MVar+import Control.Monad.State.Strict+import Data.ByteArray (convert)+import qualified Data.ByteString as B+import Data.UnixTime+import Foreign.C.Types (CTime (..))+import Network.TLS.Cipher+import Network.TLS.Context.Internal+import Network.TLS.Crypto+import qualified Network.TLS.Crypto.IES as IES++import Network.TLS.Compression+import Network.TLS.Extension+import Network.TLS.Handshake.Certificate (extractCAname)+import Network.TLS.Handshake.Common (unexpected)+import Network.TLS.Handshake.Key+import Network.TLS.Handshake.Signature+import Network.TLS.Handshake.State+import Network.TLS.Handshake.TranscriptHash+import Network.TLS.IO+import Network.TLS.IO.Encode+import Network.TLS.Imports+import Network.TLS.KeySchedule+import Network.TLS.MAC+import Network.TLS.Packet13+import Network.TLS.Parameters+import Network.TLS.State+import Network.TLS.Struct+import Network.TLS.Struct13+import Network.TLS.Types+import Network.TLS.Wire++----------------------------------------------------------------++makeFinished :: MonadIO m => Context -> Hash -> Secret -> m Handshake13+makeFinished ctx usedHash baseKey = do+    verifyData <-+        VerifyData . makeVerifyData usedHash baseKey+            <$> transcriptHash ctx "makeFinished"+    liftIO $ usingState_ ctx $ setVerifyDataForSend verifyData+    pure $ Finished13 verifyData++checkFinished+    :: MonadIO m+    => Context -> Hash -> Secret -> TranscriptHash -> VerifyData -> m ()+checkFinished ctx usedHash baseKey (TranscriptHash hashValue) vd@(VerifyData verifyData) = do+    let verifyData' = makeVerifyData usedHash baseKey $ TranscriptHash hashValue+    when (B.length verifyData /= B.length verifyData') $+        throwCore $+            Error_Protocol "broken Finished" DecodeError+    unless (verifyData' == verifyData) $ decryptError "finished verification failed"+    liftIO $ usingState_ ctx $ setVerifyDataForRecv vd++makeVerifyData :: Hash -> Secret -> TranscriptHash -> ByteString+makeVerifyData usedHash baseKey (TranscriptHash th) =+    hmac usedHash finishedKey th+  where+    hashSize = hashDigestSize usedHash+    finishedKey = hkdfExpandLabel usedHash baseKey "finished" "" hashSize++----------------------------------------------------------------++makeClientKeyShare+    :: Context -> Group -> IO ((Group, IES.GroupPrivate), KeyShareEntry)+makeClientKeyShare ctx grp = do+    (cpri, cpub) <- generateGroup ctx grp+    let wcpub = IES.groupEncodePublicA cpub+        clientKeyShare = KeyShareEntry grp wcpub+    return ((grp, cpri), clientKeyShare)++makeServerKeyShare :: Context -> KeyShareEntry -> IO (Secret, KeyShareEntry)+makeServerKeyShare ctx (KeyShareEntry grp wcpub) = case ecpub of+    Left e -> throwCore $ Error_Protocol (show e) IllegalParameter+    Right cpub -> do+        ecdhePair <- encapsulateGroup ctx cpub+        case ecdhePair of+            Nothing -> throwCore $ Error_Protocol msgInvalidPublic IllegalParameter+            Just (spub, share) ->+                let wspub = IES.groupEncodePublicB spub+                    serverKeyShare = KeyShareEntry grp wspub+                 in return (share, serverKeyShare)+  where+    ecpub = IES.groupDecodePublicA grp wcpub+    msgInvalidPublic = "invalid client " ++ show grp ++ " public key"++fromServerKeyShare+    :: KeyShareEntry -> [(Group, IES.GroupPrivate)] -> IO Secret+fromServerKeyShare (KeyShareEntry grp wspub) grpCpris = case espub of+    Left e -> throwCore $ Error_Protocol (show e) IllegalParameter+    Right spub -> case lookup grp grpCpris of+        Nothing -> throwCore err+        Just cpri -> case IES.groupDecapsulate spub cpri of+            Just shared -> return shared+            Nothing -> throwCore err+  where+    err = Error_Protocol "cannot generate a shared secret on (EC)DH" IllegalParameter+    espub = IES.groupDecodePublicB grp wspub++----------------------------------------------------------------++serverContextString :: ByteString+serverContextString = "TLS 1.3, server CertificateVerify"++clientContextString :: ByteString+clientContextString = "TLS 1.3, client CertificateVerify"++makeCertVerify+    :: MonadIO m+    => Context+    -> PubKey+    -> HashAndSignatureAlgorithm+    -> TranscriptHash+    -> m Handshake13+makeCertVerify ctx pub hs (TranscriptHash hashValue) = do+    role <- liftIO $ usingState_ ctx getRole+    let ctxStr+            | role == ClientRole = clientContextString+            | otherwise = serverContextString+        target = makeTarget ctxStr hashValue+    CertVerify13 . DigitallySigned hs <$> sign ctx pub hs target++checkCertVerify+    :: MonadIO m+    => Context+    -> PubKey+    -> HashAndSignatureAlgorithm+    -> Signature+    -> ByteString+    -> m Bool+checkCertVerify ctx pub hs signature hashValue+    | pub `signatureCompatible13` hs = liftIO $ do+        role <- usingState_ ctx getRole+        let ctxStr+                | role == ClientRole = serverContextString -- opposite context+                | otherwise = clientContextString+            target = makeTarget ctxStr hashValue+            sigParams = signatureParams pub hs+        checkHashSignatureValid13 hs+        checkSupportedHashSignature ctx hs+        verifyPublic ctx sigParams target signature+    | otherwise = return False++makeTarget :: ByteString -> ByteString -> ByteString+makeTarget contextString hashValue = runPut $ do+    putBytes $ B.replicate 64 32+    putBytes contextString+    putWord8 0+    putBytes hashValue++sign+    :: MonadIO m+    => Context+    -> PubKey+    -> HashAndSignatureAlgorithm+    -> ByteString+    -> m Signature+sign ctx pub hs target = liftIO $ do+    role <- usingState_ ctx getRole+    let sigParams = signatureParams pub hs+    signPrivate ctx role sigParams target++----------------------------------------------------------------++makePSKBinder+    :: BaseSecret EarlySecret+    -> Hash+    -> Int+    -> ByteString+    -- ^ Encoded client hello+    -> ByteString+makePSKBinder (BaseSecret sec) usedHash truncLen ech =+    makeVerifyData usedHash binderKey hChTruncated+  where+    hChTruncated = TranscriptHash $ hash usedHash $ trunc ech+    th = TranscriptHash $ hash usedHash ""+    binderKey = deriveSecret usedHash sec "res binder" th+    trunc x = B.take takeLen x+      where+        totalLen = B.length x+        takeLen = totalLen - truncLen++replacePSKBinder :: ByteString -> [ByteString] -> ByteString+replacePSKBinder pskz bds = tLidentities <> binders+  where+    tLidentities = B.take (B.length pskz - B.length binders) pskz+    -- See instance Extension PreSharedKey+    binders = runPut $ putOpaque16 $ runPut (mapM_ putBinder bds)+    putBinder = putOpaque8++----------------------------------------------------------------++sendChangeCipherSpec13 :: Monoid b => Context -> PacketFlightM b ()+sendChangeCipherSpec13 ctx = do+    sent <- usingHState ctx $ do+        b <- getCCS13Sent+        unless b $ setCCS13Sent True+        return b+    unless sent $ loadPacket13 ctx ChangeCipherSpec13++----------------------------------------------------------------++makeCertRequest+    :: ServerParams -> Context -> CertReqContext -> Bool -> Handshake13+makeCertRequest sparams ctx certReqCtx zlib =+    let sigAlgs = SignatureAlgorithms $ supportedHashSignatures $ ctxSupported ctx+        signatureAlgExt = Just $ toExtensionRaw sigAlgs++        compCertExt+            | zlib = Just $ toExtensionRaw $ CompressCertificate [CCA_Zlib]+            | otherwise = Nothing++        caDns = map extractCAname $ serverCACertificates sparams+        caExt+            | null caDns = Nothing+            | otherwise = Just $ toExtensionRaw $ CertificateAuthorities caDns++        crexts =+            catMaybes+                [ {- 0x0d -} signatureAlgExt+                , {- 0x1b -} compCertExt+                , {- 0x2f -} caExt+                ]+     in CertRequest13 certReqCtx crexts++----------------------------------------------------------------++createTLS13TicketInfo+    :: Second -> Either Context Second -> Maybe Millisecond -> IO TLS13TicketInfo+createTLS13TicketInfo life ecw mrtt = do+    -- Left:  serverSendTime+    -- Right: clientReceiveTime+    bTime <- getCurrentTimeFromBase+    add <- case ecw of+        Left ctx -> B.foldl' (*+) 0 <$> getStateRNG ctx 4+        Right ad -> return ad+    return $+        TLS13TicketInfo+            { lifetime = life+            , ageAdd = add+            , txrxTime = bTime+            , estimatedRTT = mrtt+            }+  where+    x *+ y = x * 256 + fromIntegral y++ageToObfuscatedAge :: Second -> TLS13TicketInfo -> Second+ageToObfuscatedAge age TLS13TicketInfo{..} = obfage+  where+    obfage = age + ageAdd++obfuscatedAgeToAge :: Second -> TLS13TicketInfo -> Second+obfuscatedAgeToAge obfage TLS13TicketInfo{..} = age+  where+    age = obfage - ageAdd++isAgeValid :: Second -> TLS13TicketInfo -> Bool+isAgeValid age TLS13TicketInfo{..} = age <= lifetime * 1000++getAge :: TLS13TicketInfo -> IO Second+getAge TLS13TicketInfo{..} = do+    let clientReceiveTime = txrxTime+    clientSendTime <- getCurrentTimeFromBase+    return $ fromIntegral (clientSendTime - clientReceiveTime) -- milliseconds++checkFreshness :: TLS13TicketInfo -> Second -> IO Bool+checkFreshness tinfo@TLS13TicketInfo{..} obfAge = do+    serverReceiveTime <- getCurrentTimeFromBase+    let freshness =+            if expectedArrivalTime > serverReceiveTime+                then expectedArrivalTime - serverReceiveTime+                else serverReceiveTime - expectedArrivalTime+    -- Some implementations round age up to second.+    -- We take max of 2000 and rtt in the case where rtt is too small.+    let tolerance = max 2000 rtt+        isFresh = freshness < tolerance+    return $ isAlive && isFresh+  where+    serverSendTime = txrxTime+    rtt = fromJust estimatedRTT+    age = obfuscatedAgeToAge obfAge tinfo+    expectedArrivalTime = serverSendTime + rtt + fromIntegral age+    isAlive = isAgeValid age tinfo++getCurrentTimeFromBase :: IO Millisecond+getCurrentTimeFromBase = millisecondsFromBase <$> getUnixTime++millisecondsFromBase :: UnixTime -> Millisecond+millisecondsFromBase (UnixTime (CTime s) us) =+    fromIntegral ((s - base) * 1000) + fromIntegral (us `div` 1000)+  where+    base = 1483228800++-- UnixTime (CTime base) _= parseUnixTimeGMT webDateFormat "Sun, 01 Jan 2017 00:00:00 GMT"++----------------------------------------------------------------++getSessionData13+    :: Context -> Cipher -> TLS13TicketInfo -> Int -> ByteString -> IO SessionData+getSessionData13 ctx usedCipher tinfo maxSize psk = do+    ver <- usingState_ ctx getVersion+    malpn <- usingState_ ctx getNegotiatedProtocol+    sni <- usingState_ ctx getClientSNI+    mgrp <- usingHState ctx getSupportedGroup+    return+        SessionData+            { sessionVersion = ver+            , sessionCipher = cipherID usedCipher+            , sessionCompression = 0+            , sessionClientSNI = sni+            , sessionSecret = psk+            , sessionGroup = mgrp+            , sessionTicketInfo = Just tinfo+            , sessionALPN = malpn+            , sessionMaxEarlyDataSize = maxSize+            , sessionFlags = []+            }++----------------------------------------------------------------++-- Word32 is used in TLS 1.3 protocol.+-- Int is used for API for Haskell TLS because it is natural.+-- If Int is 64 bits, users can specify bigger number than Word32.+-- If Int is 32 bits, 2^31 or larger may be converted into minus numbers.+safeNonNegative32 :: (Num a, Ord a, FiniteBits a) => a -> a+safeNonNegative32 x+    | x <= 0 = 0+    | finiteBitSize x <= 32 = x+    | otherwise = x `min` fromIntegral (maxBound :: Word32)++----------------------------------------------------------------++newtype RecvHandshake13M m a+    = RecvHandshake13M (StateT [Handshake13R] m a)+    deriving (Functor, Applicative, Monad, MonadIO)++recvHandshake13+    :: MonadIO m+    => Context+    -> (Handshake13 -> RecvHandshake13M m a)+    -> RecvHandshake13M m a+recvHandshake13 ctx f = getHandshake13 ctx >>= \(h, _b) -> f h++recvHandshake13hash+    :: MonadIO m+    => Context+    -> String+    -> (TranscriptHash -> Handshake13 -> RecvHandshake13M m a)+    -> RecvHandshake13M m a+recvHandshake13hash ctx label f = do+    d <- transcriptHash ctx label+    getHandshake13 ctx >>= \(h, _b) -> f d h++getHandshake13+    :: MonadIO m => Context -> RecvHandshake13M m Handshake13R+getHandshake13 ctx = RecvHandshake13M $ do+    currentState <- get+    case currentState of+        hb : hbs -> found hb hbs+        _ -> recvLoop+  where+    found hb hbs = liftIO (updateTranscriptHash13 ctx hb) >> put hbs >> return hb+    recvLoop = do+        epkt <- liftIO (recvPacket13 ctx)+        case epkt of+            Right (Handshake13 [] _) -> error "invalid recvPacket13 result"+            Right (Handshake13 (h : hs) (b : bs)) -> found (h, b) $ zip hs bs+            Right ChangeCipherSpec13 -> do+                alreadyReceived <- liftIO $ usingHState ctx getCCS13Recv+                if alreadyReceived+                    then+                        liftIO $ throwCore $ Error_Protocol "multiple CSS in TLS 1.3" UnexpectedMessage+                    else do+                        liftIO $ usingHState ctx $ setCCS13Recv True+                        recvLoop+            Right (Alert13 _) -> throwCore Error_TCP_Terminate+            Right x -> unexpected (show x) (Just "handshake 13")+            Left err -> throwCore err++runRecvHandshake13 :: MonadIO m => RecvHandshake13M m a -> m a+runRecvHandshake13 (RecvHandshake13M f) = do+    (result, new) <- runStateT f []+    unless (null new) $ unexpected "spurious handshake 13" Nothing+    return result++----------------------------------------------------------------++-- some hash/signature combinations have been deprecated in TLS13 and should+-- not be used+checkHashSignatureValid13 :: HashAndSignatureAlgorithm -> IO ()+checkHashSignatureValid13 hs =+    unless (isHashSignatureValid13 hs) $+        let msg = "invalid TLS13 hash and signature algorithm: " ++ show hs+         in throwCore $ Error_Protocol msg IllegalParameter++isHashSignatureValid13 :: HashAndSignatureAlgorithm -> Bool+isHashSignatureValid13 hs = hs `elem` signatureSchemesForTLS13++{-+isHashSignatureValid13 (HashIntrinsic, s) =+    s+        `elem` [ SignatureRSApssRSAeSHA256+               , SignatureRSApssRSAeSHA384+               , SignatureRSApssRSAeSHA512+               , SignatureEd25519+               , SignatureEd448+               , SignatureRSApsspssSHA256+               , SignatureRSApsspssSHA384+               , SignatureRSApsspssSHA512+               ]+isHashSignatureValid13 (h, SignatureECDSA) =+    h `elem` [HashSHA256, HashSHA384, HashSHA512]+isHashSignatureValid13 _ = False+-}++----------------------------------------------------------------++calculateEarlySecret+    :: Context+    -> CipherChoice+    -> Either ByteString (BaseSecret EarlySecret)+    -> IO (SecretPair EarlySecret)+calculateEarlySecret ctx choice maux = do+    (_ch, b) <- fromJust <$> usingHState ctx getClientHello+    let hCh = TranscriptHash $ hashChunks usedHash b+    let earlySecret = case maux of+            Right (BaseSecret sec) -> sec+            Left psk -> hkdfExtract usedHash zero (convert psk)+        clientEarlySecret = deriveSecret usedHash earlySecret "c e traffic" hCh+        cets = ClientTrafficSecret clientEarlySecret :: ClientTrafficSecret EarlySecret+    logKey ctx cets+    return $ SecretPair (BaseSecret earlySecret) cets+  where+    usedHash = cHash choice+    zero = cZero choice++initEarlySecret :: CipherChoice -> Maybe ByteString -> BaseSecret EarlySecret+initEarlySecret choice mpsk = BaseSecret sec+  where+    sec = hkdfExtract usedHash zero zeroOrPSK+    usedHash = cHash choice+    zero = cZero choice+    zeroOrPSK = fromMaybe zero (convert <$> mpsk)++calculateHandshakeSecret+    :: Context+    -> CipherChoice+    -> BaseSecret EarlySecret+    -> Secret+    -> IO (SecretTriple HandshakeSecret)+calculateHandshakeSecret ctx choice (BaseSecret sec) ecdhe = do+    hChSh <- transcriptHash ctx "CH..SH"+    let th = TranscriptHash $ hash usedHash ""+        handshakeSecret =+            hkdfExtract+                usedHash+                (deriveSecret usedHash sec "derived" th)+                ecdhe+    let clientHandshakeSecret = deriveSecret usedHash handshakeSecret "c hs traffic" hChSh+        serverHandshakeSecret = deriveSecret usedHash handshakeSecret "s hs traffic" hChSh+    let shts =+            ServerTrafficSecret serverHandshakeSecret :: ServerTrafficSecret HandshakeSecret+        chts =+            ClientTrafficSecret clientHandshakeSecret :: ClientTrafficSecret HandshakeSecret+    logKey ctx shts+    logKey ctx chts+    return $ SecretTriple (BaseSecret handshakeSecret) chts shts+  where+    usedHash = cHash choice++calculateApplicationSecret+    :: Context+    -> CipherChoice+    -> BaseSecret HandshakeSecret+    -> TranscriptHash+    -> IO (SecretTriple ApplicationSecret)+calculateApplicationSecret ctx choice (BaseSecret sec) hChSf = do+    let th = TranscriptHash $ hash usedHash ""+        applicationSecret =+            hkdfExtract+                usedHash+                (deriveSecret usedHash sec "derived" th)+                zero+    let clientApplicationSecret0 = deriveSecret usedHash applicationSecret "c ap traffic" hChSf+        serverApplicationSecret0 = deriveSecret usedHash applicationSecret "s ap traffic" hChSf+        exporterSecret = deriveSecret usedHash applicationSecret "exp master" hChSf+    usingState_ ctx $ setTLS13ExporterSecret exporterSecret+    let sts0 =+            ServerTrafficSecret serverApplicationSecret0+                :: ServerTrafficSecret ApplicationSecret+    let cts0 =+            ClientTrafficSecret clientApplicationSecret0+                :: ClientTrafficSecret ApplicationSecret+    logKey ctx sts0+    logKey ctx cts0+    return $ SecretTriple (BaseSecret applicationSecret) cts0 sts0+  where+    usedHash = cHash choice+    zero = cZero choice++calculateResumptionSecret+    :: Context+    -> CipherChoice+    -> BaseSecret ApplicationSecret+    -> IO (BaseSecret ResumptionSecret)+calculateResumptionSecret ctx choice (BaseSecret sec) = do+    hChCf <- transcriptHash ctx "CH..CF"+    let resumptionSecret = deriveSecret usedHash sec "res master" hChCf+    return $ BaseSecret resumptionSecret+  where+    usedHash = cHash choice++derivePSK+    :: CipherChoice -> BaseSecret ResumptionSecret -> TicketNonce -> ByteString+derivePSK choice (BaseSecret sec) (TicketNonce nonce) =+    hkdfExpandLabel usedHash sec "resumption" nonce hashSize+  where+    usedHash = cHash choice+    hashSize = hashDigestSize usedHash++----------------------------------------------------------------++checkClientKeyShareKeyLength :: KeyShareEntry -> Bool+checkClientKeyShareKeyLength ks = clientKeyShareKeyLength grp == B.length key+  where+    grp = keyShareEntryGroup ks+    key = keyShareEntryKeyExchange ks++{- FOURMOLU_DISABLE -}+clientKeyShareKeyLength :: Group -> Int+clientKeyShareKeyLength P256   = 65  -- 32 * 2 + 1+clientKeyShareKeyLength P384   = 97  -- 48 * 2 + 1+clientKeyShareKeyLength P521   = 133 -- 66 * 2 + 1+clientKeyShareKeyLength X25519 = 32+clientKeyShareKeyLength X448   = 56+clientKeyShareKeyLength FFDHE2048 = 256+clientKeyShareKeyLength FFDHE3072 = 384+clientKeyShareKeyLength FFDHE4096 = 512+clientKeyShareKeyLength FFDHE6144 = 768+clientKeyShareKeyLength FFDHE8192 = 1024+clientKeyShareKeyLength MLKEM512  = 800+clientKeyShareKeyLength MLKEM768  = 1184+clientKeyShareKeyLength MLKEM1024 = 1568+clientKeyShareKeyLength X25519MLKEM768 = 1216+clientKeyShareKeyLength P256MLKEM768   = 1249+clientKeyShareKeyLength P384MLKEM1024  = 1665+clientKeyShareKeyLength _ = error "clientKeyShareKeyLength"+{- FOURMOLU_ENABLE -}++checkServerKeyShareKeyLength :: KeyShareEntry -> Bool+checkServerKeyShareKeyLength ks = serverKeyShareKeyLength grp == B.length key+  where+    grp = keyShareEntryGroup ks+    key = keyShareEntryKeyExchange ks++{- FOURMOLU_DISABLE -}+serverKeyShareKeyLength :: Group -> Int+serverKeyShareKeyLength P256   = 65  -- 32 * 2 + 1+serverKeyShareKeyLength P384   = 97  -- 48 * 2 + 1+serverKeyShareKeyLength P521   = 133 -- 66 * 2 + 1+serverKeyShareKeyLength X25519 = 32+serverKeyShareKeyLength X448   = 56+serverKeyShareKeyLength FFDHE2048 = 256+serverKeyShareKeyLength FFDHE3072 = 384+serverKeyShareKeyLength FFDHE4096 = 512+serverKeyShareKeyLength FFDHE6144 = 768+serverKeyShareKeyLength FFDHE8192 = 1024+serverKeyShareKeyLength MLKEM512  = 768+serverKeyShareKeyLength MLKEM768  = 1088+serverKeyShareKeyLength MLKEM1024 = 1568+serverKeyShareKeyLength X25519MLKEM768 = 1120+serverKeyShareKeyLength P256MLKEM768   = 1153+serverKeyShareKeyLength P384MLKEM1024  = 1665+serverKeyShareKeyLength _ = error "clientKeyShareKeyLength"+{- FOURMOLU_ENABLE -}++setRTT :: Context -> Millisecond -> IO ()+setRTT ctx chSentTime = do+    shRecvTime <- getCurrentTimeFromBase+    let rtt' = shRecvTime - chSentTime+        rtt = if rtt' == 0 then 10 else rtt'+    modifyTLS13State ctx $ \st -> st{tls13stRTT = rtt}++computeConfirm+    :: (MonadFail m, MonadIO m)+    => Context -> Hash -> ServerHello -> ByteString -> m ByteString+computeConfirm ctx usedHash sh label = do+    (CH{..}, _b) <- fromJust <$> liftIO (usingHState ctx getClientHello)+    TranscriptHash echConf <-+        transcriptHashWith ctx "ECH acceptance" $ encodeHandshake13 $ ServerHello13 sh+    let prk = hkdfExtract usedHash "" $ unClientRandom chRandom+    return $ hkdfExpandLabel usedHash (convert prk) label echConf 8++----------------------------------------------------------------++setServerHelloParameters13+    :: Context -> Cipher -> Bool -> IO (Either TLSError ())+setServerHelloParameters13 ctx cipher isHRR = do+    transitTranscriptHash ctx "transit" (cipherHash cipher) isHRR+    usingHState ctx $ do+        hst <- get+        case hstPendingCipher hst of+            Nothing -> do+                put+                    hst+                        { hstPendingCipher = Just cipher+                        , hstPendingCompression = nullCompression+                        }+                return $ Right ()+            Just oldcipher+                | cipher == oldcipher -> return $ Right ()+                | otherwise ->+                    return $+                        Left $+                            Error_Protocol "TLS 1.3 cipher changed after hello retry" IllegalParameter++-- | TLS13 handshake wrap up & clean up.  Contrary to+-- @finishHandshake12@, this does not handle session, which is managed+-- separately for TLS 1.3.  This does not reset byte counters because+-- renegotiation is not allowed.  And a few more state attributes are+-- preserved, necessary for TLS13 handshake modes, session tickets and+-- post-handshake authentication.+finishHandshake13 :: Context -> IO ()+finishHandshake13 ctx = do+    -- forget most handshake data+    modifyMVar_ (ctxHandshakeState ctx) $ \case+        Nothing -> return Nothing+        Just hshake ->+            return $+                Just+                    (newEmptyHandshake (hstClientVersion hshake) (hstClientRandom hshake))+                        { hstServerRandom = hstServerRandom hshake+                        , hstMainSecret = hstMainSecret hshake+                        , hstSupportedGroup = hstSupportedGroup hshake+                        , hstTransHashState = hstTransHashState hshake+                        , hstTLS13HandshakeMode = hstTLS13HandshakeMode hshake+                        , hstTLS13RTT0Status = hstTLS13RTT0Status hshake+                        , hstTLS13ResumptionSecret = hstTLS13ResumptionSecret hshake+                        , hstTLS13ECHAccepted = hstTLS13ECHAccepted hshake+                        }+    -- forget handshake data stored in TLS state+    usingState_ ctx $ do+        setTLS13KeyShare Nothing+        setTLS13PreSharedKey Nothing+    -- mark the secure connection up and running.+    setEstablished ctx Established
+ Network/TLS/Handshake/Control.hs view
@@ -0,0 +1,43 @@+module Network.TLS.Handshake.Control (+    ClientState (..),+    ServerState (..),+    EarlySecretInfo (..),+    HandshakeSecretInfo (..),+    ApplicationSecretInfo (..),+    NegotiatedProtocol,+) where++import Network.TLS.Cipher+import Network.TLS.Imports+import Network.TLS.Struct+import Network.TLS.Types++----------------------------------------------------------------++-- | ID of the application-level protocol negotiated between client and server.+-- See values listed in the <https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids IANA registry>.+type NegotiatedProtocol = ByteString++-- | Handshake information generated for traffic at 0-RTT level.+data EarlySecretInfo = EarlySecretInfo Cipher (ClientTrafficSecret EarlySecret)+    deriving (Show)++-- | Handshake information generated for traffic at handshake level.+data HandshakeSecretInfo+    = HandshakeSecretInfo Cipher (TrafficSecrets HandshakeSecret)+    deriving (Show)++-- | Handshake information generated for traffic at application level.+newtype ApplicationSecretInfo = ApplicationSecretInfo (TrafficSecrets ApplicationSecret)+    deriving (Show)++----------------------------------------------------------------++data ClientState+    = SendClientHello (Maybe EarlySecretInfo)+    | RecvServerHello HandshakeSecretInfo+    | SendClientFinished [ExtensionRaw] ApplicationSecretInfo++data ServerState+    = SendServerHello [ExtensionRaw] (Maybe EarlySecretInfo) HandshakeSecretInfo+    | SendServerFinished ApplicationSecretInfo
Network/TLS/Handshake/Key.hs view
@@ -1,78 +1,182 @@--- |--- Module      : Network.TLS.Handshake.Key--- License     : BSD-style--- Maintainer  : Vincent Hanquez <vincent@snarc.org>--- Stability   : experimental--- Portability : unknown------ functions for RSA operations----module Network.TLS.Handshake.Key-    ( encryptRSA-    , signPrivate-    , decryptRSA-    , verifyPublic-    , generateDHE-    , generateECDHE-    , generateECDHEShared-    , generateFFDHE-    , generateFFDHEShared-    ) where+{-# LANGUAGE FlexibleInstances #-} +-- | Functions for RSA operations+module Network.TLS.Handshake.Key (+    encryptRSA,+    signPrivate,+    decryptRSA,+    verifyPublic,+    generateDHE,+    generateGroup,+    encapsulateGroup,+    generateFFDHE,+    generateFFDHEShared,+    versionCompatible,+    isDigitalSignaturePair,+    checkDigitalSignatureKey,+    getLocalPublicKey,+    satisfiesEcPredicate,+    logKey,+) where++import Control.Monad.State.Strict+import Data.ByteArray (convert) import qualified Data.ByteString as B -import Network.TLS.Handshake.State-import Network.TLS.State (withRNG, getVersion)-import Network.TLS.Crypto-import Network.TLS.Types import Network.TLS.Context.Internal+import Network.TLS.Crypto+import Network.TLS.Handshake.State import Network.TLS.Imports+import Network.TLS.Parameters+import Network.TLS.State (withRNG)+import Network.TLS.Struct+import Network.TLS.Types+import Network.TLS.X509  {- if the RSA encryption fails we just return an empty bytestring, and let the protocol  - fail by itself; however it would be probably better to just report it since it's an internal problem.  -}-encryptRSA :: Context -> ByteString -> IO ByteString+encryptRSA :: Context -> Secret -> IO ByteString encryptRSA ctx content = do     publicKey <- usingHState ctx getRemotePublicKey     usingState_ ctx $ do         v <- withRNG $ kxEncrypt publicKey content         case v of-            Left err       -> fail ("rsa encrypt failed: " ++ show err)+            Left err -> error ("rsa encrypt failed: " ++ show err)             Right econtent -> return econtent  signPrivate :: Context -> Role -> SignatureParams -> ByteString -> IO ByteString signPrivate ctx _ params content = do-    privateKey <- usingHState ctx getLocalPrivateKey+    (publicKey, privateKey) <- usingHState ctx getLocalPublicPrivateKeys     usingState_ ctx $ do-        r <- withRNG $ kxSign privateKey params content+        r <- withRNG $ kxSign privateKey publicKey params content         case r of-            Left err       -> fail ("sign failed: " ++ show err)+            Left err -> error ("sign failed: " ++ show err)             Right econtent -> return econtent -decryptRSA :: Context -> ByteString -> IO (Either KxError ByteString)+decryptRSA :: Context -> ByteString -> IO (Either KxError Secret) decryptRSA ctx econtent = do-    privateKey <- usingHState ctx getLocalPrivateKey+    (_, privateKey) <- usingHState ctx getLocalPublicPrivateKeys     usingState_ ctx $ do-        ver <- getVersion-        let cipher = if ver < TLS10 then econtent else B.drop 2 econtent+        let cipher = B.drop 2 econtent         withRNG $ kxDecrypt privateKey cipher -verifyPublic :: Context -> Role -> SignatureParams -> ByteString -> ByteString -> IO Bool-verifyPublic ctx _ params econtent sign = do+verifyPublic+    :: Context -> SignatureParams -> ByteString -> ByteString -> IO Bool+verifyPublic ctx params econtent sign = do     publicKey <- usingHState ctx getRemotePublicKey     return $ kxVerify publicKey params econtent sign  generateDHE :: Context -> DHParams -> IO (DHPrivate, DHPublic) generateDHE ctx dhp = usingState_ ctx $ withRNG $ dhGenerateKeyPair dhp -generateECDHE :: Context -> Group -> IO (GroupPrivate, GroupPublic)-generateECDHE ctx grp = usingState_ ctx $ withRNG $ groupGenerateKeyPair grp+generateGroup :: Context -> Group -> IO (GroupPrivate, GroupPublicA)+generateGroup ctx grp = usingState_ ctx $ withRNG $ groupGenerateKeyPair grp -generateECDHEShared :: Context -> GroupPublic -> IO (Maybe (GroupPublic, GroupKey))-generateECDHEShared ctx pub = usingState_ ctx $ withRNG $ groupGetPubShared pub+encapsulateGroup+    :: Context -> GroupPublicA -> IO (Maybe (GroupPublicB, GroupKey))+encapsulateGroup ctx pub = usingState_ ctx $ withRNG $ groupEncapsulate pub  generateFFDHE :: Context -> Group -> IO (DHParams, DHPrivate, DHPublic) generateFFDHE ctx grp = usingState_ ctx $ withRNG $ dhGroupGenerateKeyPair grp -generateFFDHEShared :: Context -> Group -> DHPublic -> IO (Maybe (DHPublic, DHKey))+generateFFDHEShared+    :: Context -> Group -> DHPublic -> IO (Maybe (DHPublic, DHKey)) generateFFDHEShared ctx grp pub = usingState_ ctx $ withRNG $ dhGroupGetPubShared grp pub++{- FOURMOLU_DISABLE -}+isDigitalSignatureKey :: PubKey -> Bool+isDigitalSignatureKey (PubKeyRSA _)     = True+isDigitalSignatureKey (PubKeyDSA _)     = True+isDigitalSignatureKey (PubKeyEC _)      = True+isDigitalSignatureKey (PubKeyEd25519 _) = True+isDigitalSignatureKey (PubKeyEd448 _)   = True+isDigitalSignatureKey _                 = False++versionCompatible :: PubKey -> Version -> Bool+versionCompatible (PubKeyRSA _) _     = True+versionCompatible (PubKeyDSA _) v     = v <= TLS12+versionCompatible (PubKeyEC _) v      = v >= TLS10+versionCompatible (PubKeyEd25519 _) v = v >= TLS12+versionCompatible (PubKeyEd448 _) v   = v >= TLS12+versionCompatible _ _                 = False+{- FOURMOLU_ENABLE -}++-- | Test whether the argument is a public key supported for signature at the+-- specified TLS version.  This also accepts a key for RSA encryption.  This+-- test is performed by clients or servers before verifying a remote+-- Certificate Verify.+checkDigitalSignatureKey :: MonadIO m => Version -> PubKey -> m ()+checkDigitalSignatureKey usedVersion key = do+    unless (isDigitalSignatureKey key) $+        throwCore $+            Error_Protocol "unsupported remote public key type" HandshakeFailure+    unless (key `versionCompatible` usedVersion) $+        throwCore $+            Error_Protocol+                (show usedVersion ++ " has no support for " ++ pubkeyType key)+                IllegalParameter++-- | Test whether the argument is matching key pair supported for signature.+-- This also accepts material for RSA encryption.  This test is performed by+-- servers or clients before using a credential from the local configuration.+isDigitalSignaturePair :: (PubKey, PrivKey) -> Bool+isDigitalSignaturePair keyPair =+    case keyPair of+        (PubKeyRSA _, PrivKeyRSA _) -> True+        (PubKeyDSA _, PrivKeyDSA _) -> True+        (PubKeyEC _, PrivKeyEC k) -> kxSupportedPrivKeyEC k+        (PubKeyEd25519 _, PrivKeyEd25519 _) -> True+        (PubKeyEd448 _, PrivKeyEd448 _) -> True+        _ -> False++getLocalPublicKey :: MonadIO m => Context -> m PubKey+getLocalPublicKey ctx =+    usingHState ctx (fst <$> getLocalPublicPrivateKeys)++-- | Test whether the public key satisfies a predicate about the elliptic curve.+-- When the public key is not suitable for ECDSA, like RSA for instance, the+-- predicate is not used and the result is 'True'.+satisfiesEcPredicate :: (Group -> Bool) -> PubKey -> Bool+satisfiesEcPredicate p (PubKeyEC ecPub) =+    maybe False p $ findEllipticCurveGroup ecPub+satisfiesEcPredicate _ _ = True++----------------------------------------------------------------++class LogLabel a where+    labelAndKey :: a -> (String, Secret)++instance LogLabel MainSecret where+    labelAndKey (MainSecret key) = ("CLIENT_RANDOM", key)++instance LogLabel (ClientTrafficSecret EarlySecret) where+    labelAndKey (ClientTrafficSecret key) = ("CLIENT_EARLY_TRAFFIC_SECRET", key)++instance LogLabel (ServerTrafficSecret HandshakeSecret) where+    labelAndKey (ServerTrafficSecret key) = ("SERVER_HANDSHAKE_TRAFFIC_SECRET", key)++instance LogLabel (ClientTrafficSecret HandshakeSecret) where+    labelAndKey (ClientTrafficSecret key) = ("CLIENT_HANDSHAKE_TRAFFIC_SECRET", key)++instance LogLabel (ServerTrafficSecret ApplicationSecret) where+    labelAndKey (ServerTrafficSecret key) = ("SERVER_TRAFFIC_SECRET_0", key)++instance LogLabel (ClientTrafficSecret ApplicationSecret) where+    labelAndKey (ClientTrafficSecret key) = ("CLIENT_TRAFFIC_SECRET_0", key)++-- NSS Key Log Format+-- See https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format+logKey :: LogLabel a => Context -> a -> IO ()+logKey ctx logkey = do+    mhst <- getHState ctx+    case mhst of+        Nothing -> return ()+        Just hst -> do+            let crm = fromMaybe (hstClientRandom hst) (hstTLS13OuterClientRandom hst)+                cr = unClientRandom crm+                (label, key) = labelAndKey logkey+            debugKeyLogger (ctxDebug ctx) $+                label ++ " " ++ dump cr ++ " " ++ dump (convert key)+  where+    dump = init . drop 1 . showBytesHex
− Network/TLS/Handshake/Process.hs
@@ -1,130 +0,0 @@--- |--- Module      : Network.TLS.Handshake.Process--- License     : BSD-style--- Maintainer  : Vincent Hanquez <vincent@snarc.org>--- Stability   : experimental--- Portability : unknown------ process handshake message received----module Network.TLS.Handshake.Process-    ( processHandshake-    , startHandshake-    , getHandshakeDigest-    ) where--import Control.Concurrent.MVar-import Control.Monad.State.Strict (gets)-import Control.Monad.IO.Class (liftIO)--import Network.TLS.Types (Role(..), invertRole)-import Network.TLS.Util-import Network.TLS.Packet-import Network.TLS.ErrT-import Network.TLS.Struct-import Network.TLS.State-import Network.TLS.Context.Internal-import Network.TLS.Crypto-import Network.TLS.Imports-import Network.TLS.Handshake.State-import Network.TLS.Handshake.Key-import Network.TLS.Extension-import Network.TLS.Parameters-import Data.X509 (CertificateChain(..), Certificate(..), getCertificate)--processHandshake :: Context -> Handshake -> IO ()-processHandshake ctx hs = do-    role <- usingState_ ctx isClientContext-    case hs of-        ClientHello cver ran _ cids _ ex _ -> when (role == ServerRole) $ do-            mapM_ (usingState_ ctx . processClientExtension) ex-            -- RFC 5746: secure renegotiation-            -- TLS_EMPTY_RENEGOTIATION_INFO_SCSV: {0x00, 0xFF}-            when (secureRenegotiation && (0xff `elem` cids)) $-                usingState_ ctx $ setSecureRenegotiation True-            startHandshake ctx cver ran-        Certificates certs            -> processCertificates role certs-        ClientKeyXchg content         -> when (role == ServerRole) $ do-            processClientKeyXchg ctx content-        Finished fdata                -> processClientFinished ctx fdata-        _                             -> return ()-    let encoded = encodeHandshake hs-    when (certVerifyHandshakeMaterial hs) $ usingHState ctx $ addHandshakeMessage encoded-    when (finishHandshakeTypeMaterial $ typeOfHandshake hs) $ usingHState ctx $ updateHandshakeDigest encoded-  where secureRenegotiation = supportedSecureRenegotiation $ ctxSupported ctx-        -- RFC5746: secure renegotiation-        -- the renegotiation_info extension: 0xff01-        processClientExtension (ExtensionRaw 0xff01 content) | secureRenegotiation = do-            v <- getVerifiedData ClientRole-            let bs = extensionEncode (SecureRenegotiation v Nothing)-            unless (bs `bytesEq` content) $ throwError $ Error_Protocol ("client verified data not matching: " ++ show v ++ ":" ++ show content, True, HandshakeFailure)--            setSecureRenegotiation True-        -- unknown extensions-        processClientExtension _ = return ()--        processCertificates :: Role -> CertificateChain -> IO ()-        processCertificates ServerRole (CertificateChain []) = return ()-        processCertificates ClientRole (CertificateChain []) =-            throwCore $ Error_Protocol ("server certificate missing", True, HandshakeFailure)-        processCertificates _ (CertificateChain (c:_)) =-            usingHState ctx $ setPublicKey pubkey-          where pubkey = certPubKey $ getCertificate c---- process the client key exchange message. the protocol expects the initial--- client version received in ClientHello, not the negotiated version.--- in case the version mismatch, generate a random master secret-processClientKeyXchg :: Context -> ClientKeyXchgAlgorithmData -> IO ()-processClientKeyXchg ctx (CKX_RSA encryptedPremaster) = do-    (rver, role, random) <- usingState_ ctx $ do-        (,,) <$> getVersion <*> isClientContext <*> genRandom 48-    ePremaster <- decryptRSA ctx encryptedPremaster-    usingHState ctx $ do-        expectedVer <- gets hstClientVersion-        case ePremaster of-            Left _          -> setMasterSecretFromPre rver role random-            Right premaster -> case decodePreMasterSecret premaster of-                Left _                   -> setMasterSecretFromPre rver role random-                Right (ver, _)-                    | ver /= expectedVer -> setMasterSecretFromPre rver role random-                    | otherwise          -> setMasterSecretFromPre rver role premaster-processClientKeyXchg ctx (CKX_DH clientDHValue) = do-    rver <- usingState_ ctx getVersion-    role <- usingState_ ctx isClientContext--    serverParams <- usingHState ctx getServerDHParams-    let params = serverDHParamsToParams serverParams-    unless (dhValid params $ dhUnwrapPublic clientDHValue) $-        throwCore $ Error_Protocol ("invalid client public key", True, HandshakeFailure)--    dhpriv       <- usingHState ctx getDHPrivate-    let premaster = dhGetShared params dhpriv clientDHValue-    usingHState ctx $ setMasterSecretFromPre rver role premaster--processClientKeyXchg ctx (CKX_ECDH bytes) = do-    ServerECDHParams grp _ <- usingHState ctx getServerECDHParams-    case decodeGroupPublic grp bytes of-      Left _ -> throwCore $ Error_Protocol ("client public key cannot be decoded", True, HandshakeFailure)-      Right clipub -> do-          srvpri <- usingHState ctx getECDHPrivate-          case groupGetShared clipub srvpri of-              Just premaster -> do-                  rver <- usingState_ ctx getVersion-                  role <- usingState_ ctx isClientContext-                  usingHState ctx $ setMasterSecretFromPre rver role premaster-              Nothing -> throwCore $ Error_Protocol ("cannote generate a shared secret on ECDH", True, HandshakeFailure)--processClientFinished :: Context -> FinishedData -> IO ()-processClientFinished ctx fdata = do-    (cc,ver) <- usingState_ ctx $ (,) <$> isClientContext <*> getVersion-    expected <- usingHState ctx $ getHandshakeDigest ver $ invertRole cc-    when (expected /= fdata) $ do-        throwCore $ Error_Protocol("bad record mac", True, BadRecordMac)-    usingState_ ctx $ updateVerifiedData ServerRole fdata-    return ()---- initialize a new Handshake context (initial handshake or renegotiations)-startHandshake :: Context -> Version -> ClientRandom -> IO ()-startHandshake ctx ver crand =-    let hs = Just $ newEmptyHandshake ver crand-    in liftIO $ void $ swapMVar (ctxHandshake ctx) hs
+ Network/TLS/Handshake/Random.hs view
@@ -0,0 +1,73 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE PatternGuards #-}++module Network.TLS.Handshake.Random (+    serverRandom,+    serverRandomECH,+    replaceServerRandomECH,+    clientRandom,+    isDowngraded,+) where++import qualified Data.ByteString as B++import Network.TLS.Context.Internal+import Network.TLS.Imports+import Network.TLS.Struct++-- | Generate a server random suitable for the version selected by the server+-- and its supported versions.  We use an 8-byte downgrade suffix when the+-- selected version is lowered because of incomplete client support, but also+-- when a version downgrade has been forced with 'debugVersionForced'.  This+-- second part allows to test that the client implementation correctly detects+-- downgrades.  The suffix is not used when forcing TLS13 to a server not+-- officially supporting TLS13 (this is not a downgrade scenario but only the+-- consequence of our debug API allowing this).+serverRandom :: Context -> Version -> [Version] -> IO ServerRandom+serverRandom ctx chosenVer suppVers+    | TLS13 `elem` suppVers = case chosenVer of+        TLS13 -> ServerRandom <$> getStateRNG ctx 32+        TLS12 -> ServerRandom <$> genServRand suffix12+        _ -> ServerRandom <$> genServRand suffix11+    | TLS12 `elem` suppVers = case chosenVer of+        TLS13 -> ServerRandom <$> getStateRNG ctx 32+        TLS12 -> ServerRandom <$> getStateRNG ctx 32+        _ -> ServerRandom <$> genServRand suffix11+    | otherwise = ServerRandom <$> getStateRNG ctx 32+  where+    genServRand suff = do+        pref <- getStateRNG ctx 24+        return (pref `B.append` suff)++serverRandomECH :: Context -> IO ServerRandom+serverRandomECH ctx = do+    rnd <- getStateRNG ctx 24+    let zeros = "\x00\x00\x00\x00\x00\x00\x00\x00"+    return $ ServerRandom (rnd <> zeros)++replaceServerRandomECH :: ServerRandom -> ByteString -> ServerRandom+replaceServerRandomECH (ServerRandom rnd) bs = ServerRandom (rnd' <> bs)+  where+    rnd' = B.take 24 rnd++-- | Test if the negotiated version was artificially downgraded (that is, for+-- other reason than the versions supported by the client).+isDowngraded :: Version -> [Version] -> ServerRandom -> Bool+isDowngraded ver suppVers (ServerRandom sr)+    | ver <= TLS12+    , TLS13 `elem` suppVers =+        suffix12 `B.isSuffixOf` sr+            || suffix11 `B.isSuffixOf` sr+    | ver <= TLS11+    , TLS12 `elem` suppVers =+        suffix11 `B.isSuffixOf` sr+    | otherwise = False++suffix12 :: ByteString+suffix12 = "\x44\x4F\x57\x4E\x47\x52\x44\x01"++suffix11 :: ByteString+suffix11 = "\x44\x4F\x57\x4E\x47\x52\x44\x00"++clientRandom :: Context -> IO ClientRandom+clientRandom ctx = ClientRandom <$> getStateRNG ctx 32
Network/TLS/Handshake/Server.hs view
@@ -1,42 +1,29 @@ {-# LANGUAGE OverloadedStrings #-}--- |--- Module      : Network.TLS.Handshake.Server--- License     : BSD-style--- Maintainer  : Vincent Hanquez <vincent@snarc.org>--- Stability   : experimental--- Portability : unknown----module Network.TLS.Handshake.Server-    ( handshakeServer-    , handshakeServerWith-    ) where -import Network.TLS.Parameters-import Network.TLS.Imports-import Network.TLS.Context.Internal-import Network.TLS.Session-import Network.TLS.Struct-import Network.TLS.Cipher-import Network.TLS.Compression-import Network.TLS.Credentials-import Network.TLS.Crypto-import Network.TLS.Extension-import Network.TLS.Util (catchException, fromJust)-import Network.TLS.IO-import Network.TLS.Types-import Network.TLS.State hiding (getNegotiatedProtocol)-import Network.TLS.Handshake.State-import Network.TLS.Handshake.Process-import Network.TLS.Handshake.Key-import Network.TLS.Measurement-import qualified Data.ByteString as B+module Network.TLS.Handshake.Server (+    handshakeServer,+    handshakeServerWith,+    requestCertificateServer,+    keyUpdate,+    updateKey,+    KeyUpdateRequest (..),+) where  import Control.Monad.State.Strict+import Data.Maybe -import Network.TLS.Handshake.Signature+import Network.TLS.Context.Internal import Network.TLS.Handshake.Common-import Network.TLS.Handshake.Certificate-import Network.TLS.X509+import Network.TLS.Handshake.Common13+import Network.TLS.Handshake.Server.ClientHello+import Network.TLS.Handshake.Server.ClientHello12+import Network.TLS.Handshake.Server.ClientHello13+import Network.TLS.Handshake.Server.ServerHello12+import Network.TLS.Handshake.Server.ServerHello13+import Network.TLS.Handshake.Server.TLS12+import Network.TLS.Handshake.Server.TLS13+import Network.TLS.Imports+import Network.TLS.Struct  -- Put the server context in handshake mode. --@@ -44,594 +31,59 @@ -- -- This is just a helper to pop the next message from the recv layer, -- and call handshakeServerWith.-handshakeServer :: MonadIO m => ServerParams -> Context -> m ()+handshakeServer :: ServerParams -> Context -> IO () handshakeServer sparams ctx = liftIO $ do-    hss <- recvPacketHandshake ctx-    case hss of-        [ch] -> handshakeServerWith sparams ctx ch-        _    -> fail ("unexpected handshake received, excepting client hello and received " ++ show hss)+    hbs <- recvPacketHandshake ctx+    case hbs of+        chb : _ -> handshake sparams ctx chb+        _ -> unexpected (show $ fst $ unzip hbs) (Just "client hello") +handshakeServerWith+    :: ServerParams -> Context -> HandshakeR -> IO ()+handshakeServerWith = handshake+ -- | Put the server context in handshake mode. -- -- Expect a client hello message as parameter.--- This is useful when the client hello has been already poped from the recv layer to inspect the packet.+-- This is useful when the client hello has been already popped from the recv layer to inspect the packet. ----- When the function returns, a new handshake has been succesfully negociated.+-- When the function returns, a new handshake has been successfully negotiated. -- On any error, a HandshakeFailed exception is raised.------ handshake protocol (<- receiving, -> sending, [] optional):---    (no session)           (session resumption)---      <- client hello       <- client hello---      -> server hello       -> server hello---      -> [certificate]---      -> [server key xchg]---      -> [cert request]---      -> hello done---      <- [certificate]---      <- client key xchg---      <- [cert verify]---      <- change cipher      -> change cipher---      <- finish             -> finish---      -> change cipher      <- change cipher---      -> finish             <- finish----handshakeServerWith :: ServerParams -> Context -> Handshake -> IO ()-handshakeServerWith sparams ctx clientHello@(ClientHello clientVersion _ clientSession ciphers compressions exts _) = do-    -- rejecting client initiated renegotiation to prevent DOS.-    unless (supportedClientInitiatedRenegotiation (ctxSupported ctx)) $ do-        established <- ctxEstablished ctx-        eof <- ctxEOF ctx-        when (established && not eof) $-            throwCore $ Error_Protocol ("renegotiation is not allowed", False, NoRenegotiation)-    -- check if policy allow this new handshake to happens-    handshakeAuthorized <- withMeasure ctx (onNewHandshake $ serverHooks sparams)-    unless handshakeAuthorized (throwCore $ Error_HandshakePolicy "server: handshake denied")-    updateMeasure ctx incrementNbHandshakes--    -- Handle Client hello-    processHandshake ctx clientHello--    -- rejecting SSL2. RFC 6176-    when (clientVersion == SSL2) $ throwCore $ Error_Protocol ("SSL 2.0 is not supported", True, ProtocolVersion)-    -- rejecting SSL3. RFC 7568-    -- when (clientVersion == SSL3) $ throwCore $ Error_Protocol ("SSL 3.0 is not supported", True, ProtocolVersion)--    -- Fallback SCSV: RFC7507-    -- TLS_FALLBACK_SCSV: {0x56, 0x00}-    when (supportedFallbackScsv (ctxSupported ctx) &&-          (0x5600 `elem` ciphers) &&-          clientVersion /= maxBound) $-        throwCore $ Error_Protocol ("fallback is not allowed", True, InappropriateFallback)-    chosenVersion <- case findHighestVersionFrom clientVersion (supportedVersions $ ctxSupported ctx) of-                        Nothing -> throwCore $ Error_Protocol ("client version " ++ show clientVersion ++ " is not supported", True, ProtocolVersion)-                        Just v  -> return v--    -- If compression is null, commonCompressions should be [0].-    when (null commonCompressions) $ throwCore $-        Error_Protocol ("no compression in common with the client", True, HandshakeFailure)--    -- SNI (Server Name Indication)-    let serverName = case extensionLookup extensionID_ServerName exts >>= extensionDecode False of-            Just (ServerName ns) -> listToMaybe (mapMaybe toHostName ns)-                where toHostName (ServerNameHostName hostName) = Just hostName-                      toHostName (ServerNameOther _)           = Nothing-            _                    -> Nothing--    extraCreds <- (onServerNameIndication $ serverHooks sparams) serverName--    -- When selecting a cipher we must ensure that it is allowed for the-    -- TLS version but also that all its key-exchange requirements-    -- will be met.--    -- Some ciphers require a signature and a hash.  With TLS 1.2 the hash-    -- algorithm is selected from a combination of server configuration and-    -- the client "supported_signatures" extension.  So we cannot pick-    -- such a cipher if no hash is available for it.  It's best to skip this-    -- cipher and pick another one (with another key exchange).--    -- Cipher selection is performed in two steps: first server credentials-    -- are flagged as not suitable for signature if not compatible with-    -- negotiated signature parameters.  Then ciphers are evalutated from-    -- the resulting credentials.--    let possibleGroups   = negotiatedGroupsInCommon ctx exts-        possibleECGroups = possibleGroups `intersect` availableECGroups-        possibleFFGroups = possibleGroups `intersect` availableFFGroups-        hasCommonGroupForECDHE = not (null possibleECGroups)-        hasCommonGroupForFFDHE = not (null possibleFFGroups)-        hasCustomGroupForFFDHE = isJust (serverDHEParams sparams)-        canFFDHE = hasCustomGroupForFFDHE || hasCommonGroupForFFDHE-        hasCommonGroup cipher =-            case cipherKeyExchange cipher of-                CipherKeyExchange_DH_Anon      -> canFFDHE-                CipherKeyExchange_DHE_RSA      -> canFFDHE-                CipherKeyExchange_DHE_DSS      -> canFFDHE-                CipherKeyExchange_ECDHE_RSA    -> hasCommonGroupForECDHE-                CipherKeyExchange_ECDHE_ECDSA  -> hasCommonGroupForECDHE-                _                              -> True -- group not used--        -- Ciphers are selected according to TLS version, availability of-        -- (EC)DHE group and credential depending on key exchange.-        cipherAllowed cipher   = cipherAllowedForVersion chosenVersion cipher && hasCommonGroup cipher-        selectCipher credentials signatureCredentials = filter cipherAllowed (commonCiphers credentials signatureCredentials)--        allCreds = extraCreds `mappend` sharedCredentials (ctxShared ctx)-        (creds, signatureCreds, ciphersFilteredVersion)-            = case chosenVersion of-                  TLS12 -> let -- Build a list of all hash/signature algorithms in common between-                               -- client and server.-                               possibleHashSigAlgs = hashAndSignaturesInCommon ctx exts--                               -- Check that a candidate signature credential will be compatible with-                               -- client & server hash/signature algorithms.  This returns Just Int-                               -- in order to sort credentials according to server hash/signature-                               -- preference.  When the certificate has no matching hash/signature in-                               -- 'possibleHashSigAlgs' the result is Nothing, and the credential will-                               -- not be used to sign.  This avoids a failure later in 'decideHashSig'.-                               signingRank cred =-                                   case credentialDigitalSignatureAlg cred of-                                       Just sig -> findIndex (sig `signatureCompatible`) possibleHashSigAlgs-                                       Nothing  -> Nothing--                               -- Finally compute credential lists and resulting cipher list.-                               ---                               -- We try to keep certificates supported by the client, but-                               -- fallback to all credentials if this produces no suitable result-                               -- (see RFC 5246 section 7.4.2 and TLS 1.3 section 4.4.2.2).-                               -- The condition is based on resulting (EC)DHE ciphers so that-                               -- filtering credentials does not give advantage to a less secure-                               -- key exchange like CipherKeyExchange_RSA or CipherKeyExchange_DH_Anon.-                               cltCreds    = filterCredentialsWithHashSignatures exts allCreds-                               sigCltCreds = filterSortCredentials signingRank cltCreds-                               sigAllCreds = filterSortCredentials signingRank allCreds-                               cltCiphers  = selectCipher cltCreds sigCltCreds-                               allCiphers  = selectCipher allCreds sigAllCreds--                               resultTuple = if cipherListCredentialFallback cltCiphers-                                                 then (allCreds, sigAllCreds, allCiphers)-                                                 else (cltCreds, sigCltCreds, cltCiphers)-                            in resultTuple-                  _     -> (allCreds, allCreds, selectCipher allCreds allCreds)--    -- The shared cipherlist can become empty after filtering for compatible-    -- creds, check now before calling onCipherChoosing, which does not handle-    -- empty lists.-    when (null ciphersFilteredVersion) $ throwCore $-        Error_Protocol ("no cipher in common with the client", True, HandshakeFailure)--    let usedCipher = (onCipherChoosing $ serverHooks sparams) chosenVersion ciphersFilteredVersion--    cred <- case cipherKeyExchange usedCipher of-                CipherKeyExchange_RSA       -> return $ credentialsFindForDecrypting creds-                CipherKeyExchange_DH_Anon   -> return   Nothing-                CipherKeyExchange_DHE_RSA   -> return $ credentialsFindForSigning RSA signatureCreds-                CipherKeyExchange_DHE_DSS   -> return $ credentialsFindForSigning DSS signatureCreds-                CipherKeyExchange_ECDHE_RSA -> return $ credentialsFindForSigning RSA signatureCreds-                _                           -> throwCore $ Error_Protocol ("key exchange algorithm not implemented", True, HandshakeFailure)--    resumeSessionData <- case clientSession of-            (Session (Just clientSessionId)) ->-                let resume = liftIO $ sessionResume (sharedSessionManager $ ctxShared ctx) clientSessionId-                 in validateSession serverName <$> resume-            (Session Nothing)                -> return Nothing--    maybe (return ()) (usingState_ ctx . setClientSNI) serverName--    case extensionLookup extensionID_ApplicationLayerProtocolNegotiation exts >>= extensionDecode False of-        Just (ApplicationLayerProtocolNegotiation protos) -> usingState_ ctx $ setClientALPNSuggest protos-        _ -> return ()--    -- Currently, we don't send back EcPointFormats. In this case,-    -- the client chooses EcPointFormat_Uncompressed.-    case extensionLookup extensionID_EcPointFormats exts >>= extensionDecode False of-        Just (EcPointFormatsSupported fs) -> usingState_ ctx $ setClientEcPointFormatSuggest fs-        _ -> return ()--    doHandshake sparams cred ctx chosenVersion usedCipher usedCompression clientSession resumeSessionData exts--  where-        commonCiphers creds sigCreds = filter ((`elem` ciphers) . cipherID) (getCiphers sparams creds sigCreds)-        commonCompressions    = compressionIntersectID (supportedCompressions $ ctxSupported ctx) compressions-        usedCompression       = head commonCompressions--        validateSession _   Nothing                         = Nothing-        validateSession sni 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.-            | clientVersion < sessionVersion sd             = Nothing-            | sessionCipher sd `notElem` ciphers            = Nothing-            | sessionCompression sd `notElem` compressions  = Nothing-            | isJust sni && sessionClientSNI sd /= sni      = Nothing-            | otherwise                                     = m---handshakeServerWith _ _ _ = throwCore $ Error_Protocol ("unexpected handshake message received in handshakeServerWith", True, HandshakeFailure)--doHandshake :: ServerParams -> Maybe Credential -> Context -> Version -> Cipher-            -> Compression -> Session -> Maybe SessionData-            -> [ExtensionRaw] -> IO ()-doHandshake sparams mcred ctx chosenVersion usedCipher usedCompression clientSession resumeSessionData exts = do-    case resumeSessionData of-        Nothing -> do-            handshakeSendServerData-            liftIO $ contextFlush ctx-            -- Receive client info until client Finished.-            recvClientData sparams ctx-            sendChangeCipherAndFinish ctx ServerRole-        Just sessionData -> do-            usingState_ ctx (setSession clientSession True)-            serverhello <- makeServerHello clientSession-            sendPacket ctx $ Handshake [serverhello]-            usingHState ctx $ setMasterSecret chosenVersion ServerRole $ sessionSecret sessionData-            sendChangeCipherAndFinish ctx ServerRole-            recvChangeCipherAndFinish ctx-    handshakeTerminate ctx-  where-        clientALPNSuggest = isJust $ extensionLookup extensionID_ApplicationLayerProtocolNegotiation exts--        applicationProtocol | clientALPNSuggest = do-            suggest <- usingState_ ctx getClientALPNSuggest-            case (onALPNClientSuggest $ serverHooks sparams, suggest) of-                (Just io, Just protos) -> do-                    proto <- liftIO $ io protos-                    usingState_ ctx $ do-                        setExtensionALPN True-                        setNegotiatedProtocol proto-                    return [ ExtensionRaw extensionID_ApplicationLayerProtocolNegotiation-                                            (extensionEncode $ ApplicationLayerProtocolNegotiation [proto]) ]-                (_, _)                  -> return []-             | otherwise = return []--        ----        -- When the client sends a certificate, check whether-        -- it is acceptable for the application.-        ---        ----        makeServerHello session = do-            srand <- ServerRandom <$> getStateRNG ctx 32-            case mcred of-                Just (_, privkey) -> usingHState ctx $ setPrivateKey privkey-                _                 -> return () -- return a sensible error--            -- 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-            secRengExt <- if secReneg-                    then do-                            vf <- usingState_ ctx $ do-                                    cvf <- getVerifiedData ClientRole-                                    svf <- getVerifiedData ServerRole-                                    return $ extensionEncode (SecureRenegotiation cvf $ Just svf)-                            return [ ExtensionRaw extensionID_SecureRenegotiation vf ]-                    else return []-            protoExt <- applicationProtocol-            sniExt   <- do-                resuming <- usingState_ ctx isSessionResuming-                if resuming-                  then return []-                  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 [ ExtensionRaw extensionID_ServerName ""]-                      Nothing -> return []-            let extensions = secRengExt ++ protoExt ++ sniExt-            usingState_ ctx (setVersion chosenVersion)-            usingHState ctx $ setServerHelloParameters chosenVersion srand usedCipher usedCompression-            return $ ServerHello chosenVersion srand session (cipherID usedCipher)-                                               (compressionID usedCompression) extensions--        handshakeSendServerData = do-            serverSession <- newSession ctx-            usingState_ ctx (setSession serverSession False)-            serverhello   <- makeServerHello serverSession-            -- send ServerHello & Certificate & ServerKeyXchg & CertReq-            let certMsg = case mcred of-                            Just (srvCerts, _) -> Certificates srvCerts-                            _                  -> Certificates $ CertificateChain []-            sendPacket ctx $ Handshake [ serverhello, certMsg ]--            -- send server key exchange if needed-            skx <- case cipherKeyExchange usedCipher of-                        CipherKeyExchange_DH_Anon -> Just <$> generateSKX_DH_Anon-                        CipherKeyExchange_DHE_RSA -> Just <$> generateSKX_DHE RSA-                        CipherKeyExchange_DHE_DSS -> Just <$> generateSKX_DHE DSS-                        CipherKeyExchange_ECDHE_RSA -> Just <$> generateSKX_ECDHE RSA-                        _                         -> return Nothing-            maybe (return ()) (sendPacket ctx . Handshake . (:[]) . ServerKeyXchg) skx--            -- FIXME we don't do this on a Anonymous server--            -- When configured, send a certificate request-            -- with the DNs of all confgure CA-            -- certificates.-            ---            when (serverWantClientCert sparams) $ do-                usedVersion <- usingState_ ctx getVersion-                let certTypes = [ CertificateType_RSA_Sign ]-                    hashSigs = if usedVersion < TLS12-                                   then Nothing-                                   else Just (supportedHashSignatures $ ctxSupported ctx)-                    creq = CertRequest certTypes hashSigs-                               (map extractCAname $ serverCACertificates sparams)-                usingHState ctx $ setCertReqSent True-                sendPacket ctx (Handshake [creq])--            -- Send HelloDone-            sendPacket ctx (Handshake [ServerHelloDone])--        extractCAname :: SignedCertificate -> DistinguishedName-        extractCAname cert = certSubjectDN $ getCertificate cert--        setup_DHE = do-            let possibleFFGroups = negotiatedGroupsInCommon ctx exts `intersect` availableFFGroups-            (dhparams, priv, pub) <--                    case possibleFFGroups of-                        []  ->-                            let dhparams = fromJust "server DHE Params" $ serverDHEParams sparams-                             in case findFiniteFieldGroup dhparams of-                                    Just g  -> generateFFDHE ctx g-                                    Nothing -> do-                                        (priv, pub) <- generateDHE ctx dhparams-                                        return (dhparams, priv, pub)-                        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 sigAlg = do-            usedVersion <- usingState_ ctx getVersion-            case usedVersion of-              TLS12 -> do-                  let hashSigs = hashAndSignaturesInCommon ctx exts-                  case filter (sigAlg `signatureCompatible`) hashSigs of-                      []  -> error ("no hash signature for " ++ show sigAlg)-                      x:_ -> return $ Just x-              _     -> return Nothing--        generateSKX_DHE sigAlg = do-            serverParams  <- setup_DHE-            mhashSig <- decideHashSig sigAlg-            signed <- digitallySignDHParams ctx serverParams sigAlg mhashSig-            case sigAlg of-                RSA -> return $ SKX_DHE_RSA serverParams signed-                DSS -> return $ SKX_DHE_DSS serverParams signed-                _   -> error ("generate skx_dhe unsupported signature type: " ++ show sigAlg)--        generateSKX_DH_Anon = SKX_DH_Anon <$> setup_DHE--        setup_ECDHE grp = do-            (srvpri, srvpub) <- generateECDHE ctx grp-            let serverParams = ServerECDHParams grp srvpub-            usingHState ctx $ setServerECDHParams serverParams-            usingHState ctx $ setECDHPrivate srvpri-            return serverParams--        generateSKX_ECDHE sigAlg = do-            let possibleECGroups = negotiatedGroupsInCommon ctx exts `intersect` availableECGroups-            grp <- case possibleECGroups of-                     []  -> throwCore $ Error_Protocol ("no common group", True, HandshakeFailure)-                     g:_ -> return g-            serverParams <- setup_ECDHE grp-            mhashSig <- decideHashSig sigAlg-            signed <- digitallySignECDHParams ctx serverParams sigAlg mhashSig-            case sigAlg of-                RSA -> return $ SKX_ECDHE_RSA serverParams signed-                _   -> error ("generate skx_ecdhe unsupported signature type: " ++ show sigAlg)--        -- create a DigitallySigned objects for DHParams or ECDHParams.---- | receive Client data in handshake until the Finished handshake.------      <- [certificate]---      <- client key xchg---      <- [cert verify]---      <- change cipher---      <- finish----recvClientData :: ServerParams -> Context -> IO ()-recvClientData sparams ctx = runRecvState ctx (RecvStateHandshake processClientCertificate)-  where processClientCertificate (Certificates certs) = do-            -- run certificate recv hook-            ctxWithHooks ctx (\hooks -> hookRecvCertificates hooks certs)-            -- Call application callback to see whether the-            -- certificate chain is acceptable.-            ---            usage <- liftIO $ catchException (onClientCertificate (serverHooks sparams) certs) rejectOnException-            case usage of-                CertificateUsageAccept        -> return ()-                CertificateUsageReject reason -> certificateRejected reason--            -- Remember cert chain for later use.-            ---            usingHState ctx $ setClientCertChain certs--            -- FIXME: We should check whether the certificate-            -- matches our request and that we support-            -- verifying with that certificate.--            return $ RecvStateHandshake processClientKeyExchange--        processClientCertificate p = processClientKeyExchange p--        -- cannot use RecvStateHandshake, as the next message could be a ChangeCipher,-        -- so we must process any packet, and in case of handshake call processHandshake manually.-        processClientKeyExchange (ClientKeyXchg _) = return $ RecvStateNext processCertificateVerify-        processClientKeyExchange p                 = unexpected (show p) (Just "client key exchange")--        -- Check whether the client correctly signed the handshake.-        -- If not, ask the application on how to proceed.-        ---        processCertificateVerify (Handshake [hs@(CertVerify dsig)]) = do-            processHandshake ctx hs--            checkValidClientCertChain "change cipher message expected"--            usedVersion <- usingState_ ctx getVersion-            -- Fetch all handshake messages up to now.-            msgs  <- usingHState ctx $ B.concat <$> getHandshakeMessages--            sigAlgExpected <- getRemoteSignatureAlg--            -- FIXME should check certificate is allowed for signing--            verif <- checkCertificateVerify ctx usedVersion sigAlgExpected msgs dsig--            if verif then do-                -- When verification succeeds, commit the-                -- client certificate chain to the context.-                ---                Just certs <- usingHState ctx getClientCertChain-                usingState_ ctx $ setClientCertificateChain certs-                return ()-              else do-                -- Either verification failed because of an-                -- invalid format (with an error message), or-                -- the signature is wrong.  In either case,-                -- ask the application if it wants to-                -- proceed, we will do that.-                res <- liftIO $ onUnverifiedClientCert (serverHooks sparams)-                if res then do-                        -- When verification fails, but the-                        -- application callbacks accepts, we-                        -- also commit the client certificate-                        -- chain to the context.-                        Just certs <- usingHState ctx getClientCertChain-                        usingState_ ctx $ setClientCertificateChain certs-                    else throwCore $ Error_Protocol ("verification failed", True, BadCertificate)-            return $ RecvStateNext expectChangeCipher--        processCertificateVerify p = do-            chain <- usingHState ctx getClientCertChain-            case chain of-                Just cc | isNullCertificateChain cc -> return ()-                        | otherwise                 -> throwCore $ Error_Protocol ("cert verify message missing", True, UnexpectedMessage)-                Nothing -> return ()-            expectChangeCipher p--        getRemoteSignatureAlg = do-            pk <- usingHState ctx getRemotePublicKey-            case pk of-                PubKeyRSA _   -> return RSA-                PubKeyDSA _   -> return DSS-                PubKeyEC  _   -> return ECDSA-                _             -> throwCore $ Error_Protocol ("unsupported remote public key type", True, HandshakeFailure)--        expectChangeCipher ChangeCipherSpec = do-            return $ RecvStateHandshake expectFinish--        expectChangeCipher p                = unexpected (show p) (Just "change cipher")--        expectFinish (Finished _) = return RecvStateDone-        expectFinish p            = unexpected (show p) (Just "Handshake Finished")--        checkValidClientCertChain msg = do-            chain <- usingHState ctx getClientCertChain-            let throwerror = Error_Protocol (msg , True, UnexpectedMessage)-            case chain of-                Nothing -> throwCore throwerror-                Just cc | isNullCertificateChain cc -> throwCore throwerror-                        | otherwise                 -> return ()--hashAndSignaturesInCommon :: Context -> [ExtensionRaw] -> [HashAndSignatureAlgorithm]-hashAndSignaturesInCommon ctx exts =-    let cHashSigs = case extensionLookup extensionID_SignatureAlgorithms exts >>= extensionDecode False of-            -- See Section 7.4.1.4.1 of RFC 5246.-            Nothing -> [(HashSHA1, SignatureECDSA)-                       ,(HashSHA1, SignatureRSA)-                       ,(HashSHA1, SignatureDSS)]-            Just (SignatureAlgorithms sas) -> sas-        sHashSigs = supportedHashSignatures $ ctxSupported ctx-        -- The values in the "signature_algorithms" extension-        -- are in descending order of preference.-        -- However here the algorithms are selected according-        -- to server preference in 'supportedHashSignatures'.-     in sHashSigs `intersect` cHashSigs--negotiatedGroupsInCommon :: Context -> [ExtensionRaw] -> [Group]-negotiatedGroupsInCommon ctx exts = case extensionLookup extensionID_NegotiatedGroups exts >>= extensionDecode False of-    Just (NegotiatedGroups clientGroups) ->-        let serverGroups = supportedGroups (ctxSupported ctx)-        in serverGroups `intersect` clientGroups-    _                                    -> []--credentialDigitalSignatureAlg :: Credential -> Maybe DigitalSignatureAlg-credentialDigitalSignatureAlg cred =-    findDigitalSignatureAlg (credentialPublicPrivateKeys cred)--filterSortCredentials :: Ord a => (Credential -> Maybe a) -> Credentials -> Credentials-filterSortCredentials rankFun (Credentials creds) =-    let orderedPairs = sortOn fst [ (rankFun cred, cred) | cred <- creds ]-     in Credentials [ cred | (Just _, cred) <- orderedPairs ]--filterCredentialsWithHashSignatures :: [ExtensionRaw] -> Credentials -> Credentials-filterCredentialsWithHashSignatures exts =-    case extensionLookup extensionID_SignatureAlgorithms exts >>= extensionDecode False of-        Nothing                        -> id-        Just (SignatureAlgorithms sas) ->-            let filterCredentials p (Credentials l) = Credentials (filter p l)-             in filterCredentials (credentialMatchesHashSignatures sas)---- returns True if "signature_algorithms" certificate filtering produced no--- ephemeral D-H nor TLS13 cipher (so handshake with lower security)-cipherListCredentialFallback :: [Cipher] -> Bool-cipherListCredentialFallback xs = all nonDH xs-  where-    nonDH x = case cipherKeyExchange x of-        CipherKeyExchange_DHE_RSA     -> False-        CipherKeyExchange_DHE_DSS     -> False-        CipherKeyExchange_ECDHE_RSA   -> False-        CipherKeyExchange_ECDHE_ECDSA -> False-        --CipherKeyExchange_TLS13       -> False-        _                             -> True--findHighestVersionFrom :: Version -> [Version] -> Maybe Version-findHighestVersionFrom clientVersion allowedVersions =-    case filter (clientVersion >=) $ sortOn Down allowedVersions of-        []  -> Nothing-        v:_ -> Just v---- We filter our allowed ciphers here according to dynamic credential lists.--- Credentials 'creds' come from server parameters but also SNI callback.--- When the key exchange requires a signature, we use a--- subset of this list named 'sigCreds'.  This list has been filtered in order--- to remove certificates that are not compatible with hash/signature--- restrictions (TLS 1.2).-getCiphers :: ServerParams -> Credentials -> Credentials -> [Cipher]-getCiphers sparams creds sigCreds = filter authorizedCKE (supportedCiphers $ serverSupported sparams)-      where authorizedCKE cipher =-                case cipherKeyExchange cipher of-                    CipherKeyExchange_RSA         -> canEncryptRSA-                    CipherKeyExchange_DH_Anon     -> True-                    CipherKeyExchange_DHE_RSA     -> canSignRSA-                    CipherKeyExchange_DHE_DSS     -> canSignDSS-                    CipherKeyExchange_ECDHE_RSA   -> canSignRSA-                    -- unimplemented: EC-                    CipherKeyExchange_ECDHE_ECDSA -> False-                    -- unimplemented: non ephemeral DH & ECDH.-                    -- Note, these *should not* be implemented, and have-                    -- (for example) been removed in OpenSSL 1.1.0-                    ---                    CipherKeyExchange_DH_DSS      -> False-                    CipherKeyExchange_DH_RSA      -> False-                    CipherKeyExchange_ECDH_ECDSA  -> False-                    CipherKeyExchange_ECDH_RSA    -> False--            canSignDSS    = DSS `elem` signingAlgs-            canSignRSA    = RSA `elem` signingAlgs-            canEncryptRSA = isJust $ credentialsFindForDecrypting creds-            signingAlgs   = credentialsListSigningAlgorithms sigCreds+handshake :: ServerParams -> Context -> HandshakeR -> IO ()+handshake sparams ctx chb@(ClientHello ch, bs) = do+    (chosenVersion, chI, mcrnd) <- processClientHello sparams ctx ch bs+    if chosenVersion == TLS13+        then do+            -- fixme: we should check if the client random is the same as+            -- that in the first client hello in the case of hello retry.+            -- r0 :: Cipher, Hash, Bool+            (keyShareResult, r0, r1) <-+                processClientHello13 sparams ctx chI+            case keyShareResult of+                SelectKeyShareNotFound ->+                    throwCore $+                        Error_Protocol "no group in common with the client for HRR" HandshakeFailure+                SelectKeyShareHRR g -> do+                    sendHRR ctx g r0 chI $ isJust mcrnd+                    -- Don't reset ctxEstablished since 0-RTT data+                    -- would be coming, which should be ignored.+                    handshakeServer sparams ctx+                SelectKeyShareFound cliKeyShare -> do+                    unless (checkClientKeyShareKeyLength cliKeyShare) $+                        throwCore $+                            Error_Protocol "broken key_share" IllegalParameter+                    -- r2 :: ( SecretTriple ApplicationSecret+                    --       , ClientTrafficSecret HandshakeSecret+                    --       , Bool  -- authenticated+                    --       , Bool) -- rtt0OK+                    r2 <-+                        sendServerHello13 sparams ctx cliKeyShare r0 r1 chI mcrnd+                    recvClientSecondFlight13 sparams ctx r2 chI+        else do+            r <-+                processClientHello12 sparams ctx chI+            updateTranscriptHash12 ctx chb+            resumeSessionData <-+                sendServerHello12 sparams ctx r chI+            recvClientSecondFlight12 sparams ctx resumeSessionData+handshake _ _ _ = throwCore $ Error_Protocol "client Hello is expected" HandshakeFailure
+ Network/TLS/Handshake/Server/ClientHello.hs view
@@ -0,0 +1,305 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE TupleSections #-}++module Network.TLS.Handshake.Server.ClientHello (+    processClientHello,+) where++import qualified Control.Exception as E+import Crypto.HPKE+import qualified Data.ByteString as BS++import Network.TLS.ECH.Config++import Network.TLS.Compression+import Network.TLS.Context.Internal+import Network.TLS.Extension+import Network.TLS.Handshake.Common+import Network.TLS.Handshake.State+import Network.TLS.Imports+import Network.TLS.Measurement+import Network.TLS.Packet+import Network.TLS.Parameters+import Network.TLS.State+import Network.TLS.Struct+import Network.TLS.Types++processClientHello+    :: ServerParams+    -> Context+    -> ClientHello+    -> [ByteString]+    -> IO+        ( Version+        , ClientHello+        , Maybe ClientRandom -- Just for ECH to keep the outer one for key log+        )+processClientHello sparams ctx ch@CH{..} b = do+    established <- ctxEstablished ctx+    -- renego is not allowed in TLS 1.3+    when (established /= NotEstablished) $ do+        ver <- usingState_ ctx (getVersionWithDefault TLS12)+        when (ver == TLS13) $+            throwCore $+                Error_Protocol "renegotiation is not allowed in TLS 1.3" UnexpectedMessage+    -- rejecting client initiated renegotiation to prevent DOS.+    eof <- ctxEOF ctx+    let renegotiation = established == Established && not eof+    when+        (renegotiation && not (supportedClientInitiatedRenegotiation $ ctxSupported ctx))+        $ throwCore+        $ Error_Protocol_Warning "renegotiation is not allowed" NoRenegotiation+    -- check if policy allow this new handshake to happens+    handshakeAuthorized <- withMeasure ctx (onNewHandshake $ serverHooks sparams)+    unless+        handshakeAuthorized+        (throwCore $ Error_HandshakePolicy "server: handshake denied")+    updateMeasure ctx incrementNbHandshakes++    when (chVersion /= TLS12) $+        throwCore $+            Error_Protocol (show chVersion ++ " is not supported") ProtocolVersion++    -- Fallback SCSV: RFC7507+    -- TLS_FALLBACK_SCSV: {0x56, 0x00}+    when+        ( supportedFallbackScsv (ctxSupported ctx)+            && (CipherId 0x5600 `elem` chCiphers)+            && chVersion < TLS12+        )+        $ throwCore+        $ Error_Protocol "fallback is not allowed" InappropriateFallback++    -- choosing TLS version+    let extract (SupportedVersionsClientHello vers) = vers -- fixme: vers == []+        extract _ = []+        clientVersions =+            lookupAndDecode EID_SupportedVersions MsgTClientHello chExtensions [] extract+        clientVersion = min TLS12 chVersion+        serverVersions+            | renegotiation = filter (< TLS13) (supportedVersions $ ctxSupported ctx)+            | otherwise = supportedVersions $ ctxSupported ctx+        mVersion = debugVersionForced $ serverDebug sparams+    chosenVersion <- case mVersion of+        Just cver -> return cver+        Nothing ->+            if (TLS13 `elem` serverVersions) && clientVersions /= []+                then case findHighestVersionFrom13 clientVersions serverVersions of+                    Nothing ->+                        throwCore $+                            Error_Protocol+                                ("client versions " ++ show clientVersions ++ " is not supported")+                                ProtocolVersion+                    Just v -> return v+                else case findHighestVersionFrom clientVersion serverVersions of+                    Nothing ->+                        throwCore $+                            Error_Protocol+                                ("client version " ++ show clientVersion ++ " is not supported")+                                ProtocolVersion+                    Just v -> return v++    -- Checking compression+    let nullComp = compressionID nullCompression+    case chosenVersion of+        TLS13 ->+            when (chComps /= [nullComp]) $+                throwCore $+                    Error_Protocol "compression is not allowed in TLS 1.3" IllegalParameter+        _ -> case find (== nullComp) chComps of+            Nothing ->+                throwCore $+                    Error_Protocol+                        "compressions must include nullCompression in TLS 1.2"+                        IllegalParameter+            _ -> return ()++    -- Processing encrypted client hello+    (mClientHello', receivedECH) <-+        if chosenVersion == TLS13 && not (null (serverECHKey sparams))+            then do+                lookupAndDecodeAndDo+                    EID_EncryptedClientHello+                    MsgTClientHello+                    chExtensions+                    (return (Nothing, False))+                    (\bs -> (,True) <$> decryptECH sparams ctx ch bs)+            else return (Nothing, False)+    case mClientHello' of+        Just chI -> do+            -- chI is created from diff.+            -- encodeHandshake is a MUST.+            setupI ctx chI $ [encodeHandshake $ ClientHello chI]+            return (chosenVersion, chI, Just chRandom)+        _ -> do+            setupO ctx ch b+            when (chosenVersion == TLS13) $ do+                let hasECHConf = not (null (sharedECHConfigList (serverShared sparams)))+                when (hasECHConf && not receivedECH) $+                    usingHState ctx $+                        setECHEE True+                when receivedECH $+                    usingHState ctx $+                        setECHEE True+            return (chosenVersion, ch, Nothing)++setupI :: Context -> ClientHello -> [ByteString] -> IO ()+setupI ctx chI@CH{..} b = do+    hrr <- usingState_ ctx getTLS13HRR+    unless hrr $ startHandshake ctx TLS13 chRandom+    usingHState ctx $ setClientHello chI b+    let serverName = getServerName chExtensions+    maybe (return ()) (usingState_ ctx . setClientSNI) serverName++setupO :: Context -> ClientHello -> [ByteString] -> IO ()+setupO ctx ch@CH{..} b = do+    hrr <- usingState_ ctx getTLS13HRR+    unless hrr $ startHandshake ctx chVersion chRandom+    usingHState ctx $ setClientHello ch b+    let serverName = getServerName chExtensions+    maybe (return ()) (usingState_ ctx . setClientSNI) serverName++-- SNI (Server Name Indication)+getServerName :: [ExtensionRaw] -> Maybe HostName+getServerName chExts =+    lookupAndDecode+        EID_ServerName+        MsgTClientHello+        chExts+        Nothing+        extractServerName+  where+    extractServerName (ServerName ns) = listToMaybe (mapMaybe toHostName ns)+    toHostName (ServerNameHostName hostName) = Just hostName+    toHostName (ServerNameOther _) = Nothing++findHighestVersionFrom :: Version -> [Version] -> Maybe Version+findHighestVersionFrom clientVersion allowedVersions =+    case filter (clientVersion >=) $ sortOn Down allowedVersions of+        [] -> Nothing+        v : _ -> Just v++findHighestVersionFrom13 :: [Version] -> [Version] -> Maybe Version+findHighestVersionFrom13 clientVersions serverVersions = case svs `intersect` cvs of+    [] -> Nothing+    v : _ -> Just v+  where+    svs = sortOn Down serverVersions+    cvs = sortOn Down $ filter (>= TLS12) clientVersions++decryptECH+    :: ServerParams+    -> Context+    -> ClientHello+    -> EncryptedClientHello+    -> IO (Maybe ClientHello)+decryptECH _ _ _ ECHClientHelloInner = return Nothing+decryptECH sparams ctx chO ech@ECHClientHelloOuter{..} = E.handle hpkeHandler $ do+    mfunc <- getHPKE sparams ctx ech+    case mfunc of+        Nothing -> return Nothing+        Just (func, nenc) -> do+            hrr <- usingState_ ctx getTLS13HRR+            let nenc' = if hrr then 0 else nenc+            let aad = encodeHandshake' $ ClientHello $ fill0ClientHello nenc' chO+            plaintext <- func aad echPayload+            case decodeClientHello' plaintext of+                Right (ClientHello chI) -> do+                    case expandClientHello chI chO of+                        Nothing -> return Nothing+                        Just chI' -> return $ Just chI'+                _ -> return Nothing+  where+    hpkeHandler :: HPKEError -> IO (Maybe ClientHello)+    hpkeHandler _ = return Nothing+decryptECH _ _ _ _ = return Nothing++fill0ClientHello :: Int -> ClientHello -> ClientHello+fill0ClientHello nenc ch@CH{..} =+    ch{chExtensions = fill0Exts nenc chExtensions}++fill0Exts :: Int -> [ExtensionRaw] -> [ExtensionRaw]+fill0Exts nenc xs0 = loop xs0+  where+    loop [] = []+    loop (ExtensionRaw EID_EncryptedClientHello bs : xs) = x' : loop xs+      where+        (prefix, payload) = BS.splitAt (10 + nenc) bs+        bs' = prefix <> BS.replicate (BS.length payload) 0+        x' = ExtensionRaw EID_EncryptedClientHello bs'+    loop (x : xs) = x : loop xs++expandClientHello :: ClientHello -> ClientHello -> Maybe ClientHello+expandClientHello inner outer =+    case expand (chExtensions inner) (chExtensions outer) of+        Nothing -> Nothing+        Just exts ->+            Just $+                inner+                    { chSession = chSession outer+                    , chExtensions = exts+                    }+  where+    expand :: [ExtensionRaw] -> [ExtensionRaw] -> Maybe [ExtensionRaw]+    expand [] _ = Just []+    expand iis [] = chk iis+    expand (i : is) oos = do+        (rs, oos') <- case i of+            ExtensionRaw EID_EchOuterExtensions bs ->+                case extensionDecode MsgTClientHello bs of+                    Nothing -> Nothing+                    Just (EchOuterExtensions eids) -> expd eids oos+            _ -> Just ([i], oos)+        (rs ++) <$> expand is oos'+    expd+        :: [ExtensionID] -> [ExtensionRaw] -> Maybe ([ExtensionRaw], [ExtensionRaw])+    expd [] oos = Just ([], oos)+    expd _ [] = Nothing+    expd (i : is) oos = case fnd i oos of+        Nothing -> Nothing+        Just (ext, oos') -> do+            (exts, oos'') <- expd is oos'+            Just (ext : exts, oos'')+    fnd :: ExtensionID -> [ExtensionRaw] -> Maybe (ExtensionRaw, [ExtensionRaw])+    fnd _ [] = Nothing+    fnd EID_EncryptedClientHello _ = Nothing+    fnd i (o@(ExtensionRaw eid _) : os)+        | i == eid = Just (o, os)+        | otherwise = fnd i os+    chk :: [ExtensionRaw] -> Maybe [ExtensionRaw]+    chk [] = Just []+    chk (ExtensionRaw EID_EchOuterExtensions _ : _) = Nothing+    chk (i : is) = (i :) <$> chk is++getHPKE+    :: ServerParams+    -> Context+    -> EncryptedClientHello+    -> IO (Maybe (HPKEF, Int))+getHPKE ServerParams{..} ctx ECHClientHelloOuter{..} = do+    mfunc <- getTLS13HPKE ctx+    case mfunc of+        Nothing -> do+            let mconfig = findECHConfigById echConfigId $ sharedECHConfigList serverShared+                mskR = lookup echConfigId serverECHKey+            case (mconfig, mskR) of+                (Just config, Just skR') -> do+                    let kemid = KEM_ID $ kem_id $ key_config $ contents config+                        skR = EncodedSecretKey skR'+                        encodedConfig = encodeECHConfig config+                    let info = "tls ech\x00" <> encodedConfig+                        (kdfid, aeadid) = echCipherSuite+                    ctxR <- setupBaseR kemid kdfid aeadid skR Nothing echEnc info+                    let nenc = nEnc kemid+                        func = open ctxR+                    setTLS13HPKE ctx func nenc+                    return $ Just (func, nenc)+                _ -> return Nothing+        _ -> return mfunc+getHPKE _ _ _ = return Nothing++findECHConfigById :: ConfigId -> ECHConfigList -> Maybe ECHConfig+findECHConfigById cnfId echConfigList = find eqCfgId echConfigList+  where+    eqCfgId cnf = config_id (key_config (contents cnf)) == cnfId
+ Network/TLS/Handshake/Server/ClientHello12.hs view
@@ -0,0 +1,235 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}++module Network.TLS.Handshake.Server.ClientHello12 (+    processClientHello12,+) where++import Network.TLS.Cipher+import Network.TLS.Context.Internal+import Network.TLS.Credentials+import Network.TLS.Crypto+import Network.TLS.ErrT+import Network.TLS.Extension+import Network.TLS.Handshake.Server.Common+import Network.TLS.Handshake.Signature+import Network.TLS.Imports+import Network.TLS.Parameters+import Network.TLS.State+import Network.TLS.Struct+import Network.TLS.Types (CipherId (..), Role (..))++----------------------------------------------------------------++-- serverSupported sparams == ctxSupported ctx++-- TLS 1.2 or earlier+processClientHello12+    :: ServerParams+    -> Context+    -> ClientHello+    -> IO (Cipher, Maybe Credential)+processClientHello12 sparams ctx ch = do+    let secureRenegotiation = supportedSecureRenegotiation $ serverSupported sparams+    when secureRenegotiation $ checkSecureRenegotiation ctx ch+    serverName <- usingState_ ctx getClientSNI+    let hooks = serverHooks sparams+    extraCreds <- onServerNameIndication hooks serverName+    let (creds, signatureCreds, ciphersFilteredVersion) =+            credsTriple sparams ch extraCreds+    -- The shared cipherlist can become empty after filtering for compatible+    -- creds, check now before calling onCipherChoosing, which does not handle+    -- empty lists.+    when (null ciphersFilteredVersion) $+        throwCore $+            Error_Protocol "no cipher in common with the TLS 1.2 client" HandshakeFailure+    let usedCipher = onCipherChoosing hooks TLS12 ciphersFilteredVersion+    mcred <- chooseCreds usedCipher creds signatureCreds+    return (usedCipher, mcred)++checkSecureRenegotiation :: Context -> ClientHello -> IO ()+checkSecureRenegotiation ctx CH{..} = do+    -- RFC 5746: secure renegotiation+    -- TLS_EMPTY_RENEGOTIATION_INFO_SCSV: {0x00, 0xFF}+    when (CipherId 0xff `elem` chCiphers) $+        usingState_ ctx $+            setSecureRenegotiation True+    case extensionLookup EID_SecureRenegotiation chExtensions of+        Just content -> usingState_ ctx $ do+            VerifyData cvd <- getVerifyData ClientRole+            let bs = extensionEncode (SecureRenegotiation cvd "")+            unless (bs == content) $+                throwError $+                    Error_Protocol+                        ("client verified data not matching: " ++ show cvd ++ ":" ++ show content)+                        HandshakeFailure++            setSecureRenegotiation True+        _ -> return ()++----------------------------------------------------------------++credsTriple+    :: ServerParams+    -> ClientHello+    -> Credentials+    -> (Credentials, Credentials, [Cipher])+credsTriple sparams CH{..} extraCreds+    | cipherListCredentialFallback cltCiphers = (allCreds, sigAllCreds, allCiphers)+    | otherwise = (cltCreds, sigCltCreds, cltCiphers)+  where+    ciphers = supportedCiphers $ serverSupported sparams++    commonCiphers creds sigCreds = intersectCiphers chCiphers availableCiphers+      where+        availableCiphers = getCiphers ciphers creds sigCreds++    p = makeCredentialPredicate TLS12 chExtensions+    allCreds =+        filterCredentials (isCredentialAllowed TLS12 p) $+            extraCreds `mappend` sharedCredentials (serverShared sparams)++    -- When selecting a cipher we must ensure that it is allowed for the+    -- TLS version but also that all its key-exchange requirements+    -- will be met.++    -- Some ciphers require a signature and a hash.  With TLS 1.2 the hash+    -- algorithm is selected from a combination of server configuration and+    -- the client "supported_signatures" extension.  So we cannot pick+    -- such a cipher if no hash is available for it.  It's best to skip this+    -- cipher and pick another one (with another key exchange).++    -- Cipher selection is performed in two steps: first server credentials+    -- are flagged as not suitable for signature if not compatible with+    -- negotiated signature parameters.  Then ciphers are evaluated from+    -- the resulting credentials.++    supported = serverSupported sparams+    groups = supportedGroups supported+    possibleGroups = negotiatedGroupsInCommon groups chExtensions+    possibleECGroups = possibleGroups `intersect` availableECGroups+    possibleFFGroups = possibleGroups `intersect` availableFFGroups+    hasCommonGroupForECDHE = not (null possibleECGroups)+    hasCommonGroupForFFDHE = not (null possibleFFGroups)+    hasCustomGroupForFFDHE = isJust (serverDHEParams sparams)+    canFFDHE = hasCustomGroupForFFDHE || hasCommonGroupForFFDHE+    hasCommonGroup cipher =+        case cipherKeyExchange cipher of+            CipherKeyExchange_DH_Anon -> canFFDHE+            CipherKeyExchange_DHE_RSA -> canFFDHE+            CipherKeyExchange_DHE_DSA -> canFFDHE+            CipherKeyExchange_ECDHE_RSA -> hasCommonGroupForECDHE+            CipherKeyExchange_ECDHE_ECDSA -> hasCommonGroupForECDHE+            _ -> True -- group not used++    -- Ciphers are selected according to TLS version, availability of+    -- (EC)DHE group and credential depending on key exchange.+    cipherAllowed cipher = cipherAllowedForVersion TLS12 cipher && hasCommonGroup cipher+    selectCipher credentials signatureCredentials = filter cipherAllowed (commonCiphers credentials signatureCredentials)++    -- Build a list of all hash/signature algorithms in common between+    -- client and server.+    hashAndSignatures = supportedHashSignatures supported+    possibleHashSigAlgs = hashAndSignaturesInCommon hashAndSignatures chExtensions++    -- Check that a candidate signature credential will be compatible with+    -- client & server hash/signature algorithms.  This returns Just Int+    -- in order to sort credentials according to server hash/signature+    -- preference.  When the certificate has no matching hash/signature in+    -- 'possibleHashSigAlgs' the result is Nothing, and the credential will+    -- not be used to sign.  This avoids a failure later in 'decideHashSig'.+    signingRank cred =+        case credentialDigitalSignatureKey cred of+            Just pub -> findIndex (pub `signatureCompatible`) possibleHashSigAlgs+            Nothing -> Nothing++    -- Finally compute credential lists and resulting cipher list.+    --+    -- We try to keep certificates supported by the client, but+    -- fallback to all credentials if this produces no suitable result+    -- (see RFC 5246 section 7.4.2 and RFC 8446 section 4.4.2.2).+    -- The condition is based on resulting (EC)DHE ciphers so that+    -- filtering credentials does not give advantage to a less secure+    -- key exchange like CipherKeyExchange_RSA or CipherKeyExchange_DH_Anon.+    cltCreds = filterCredentialsWithHashSignatures chExtensions allCreds+    sigCltCreds = filterSortCredentials signingRank cltCreds+    sigAllCreds = filterSortCredentials signingRank allCreds+    cltCiphers = selectCipher cltCreds sigCltCreds+    allCiphers = selectCipher allCreds sigAllCreds++chooseCreds :: Cipher -> Credentials -> Credentials -> IO (Maybe Credential)+chooseCreds usedCipher creds signatureCreds = case cipherKeyExchange usedCipher of+    CipherKeyExchange_RSA -> return $ credentialsFindForDecrypting creds+    CipherKeyExchange_DH_Anon -> return Nothing+    CipherKeyExchange_DHE_RSA -> return $ credentialsFindForSigning KX_RSA signatureCreds+    CipherKeyExchange_DHE_DSA -> return $ credentialsFindForSigning KX_DSA signatureCreds+    CipherKeyExchange_ECDHE_RSA -> return $ credentialsFindForSigning KX_RSA signatureCreds+    CipherKeyExchange_ECDHE_ECDSA -> return $ credentialsFindForSigning KX_ECDSA signatureCreds+    _ ->+        throwCore $+            Error_Protocol "key exchange algorithm not implemented" HandshakeFailure++----------------------------------------------------------------++negotiatedGroupsInCommon :: [Group] -> [ExtensionRaw] -> [Group]+negotiatedGroupsInCommon serverGroups exts =+    lookupAndDecode+        EID_SupportedGroups+        MsgTClientHello+        exts+        []+        (\(SupportedGroups clientGroups) -> serverGroups `intersect` clientGroups)++----------------------------------------------------------------++filterSortCredentials+    :: Ord a => (Credential -> Maybe a) -> Credentials -> Credentials+filterSortCredentials rankFun (Credentials creds) =+    let orderedPairs = sortOn fst [(rankFun cred, cred) | cred <- creds]+     in Credentials [cred | (Just _, cred) <- orderedPairs]++-- returns True if certificate filtering with "signature_algorithms_cert" /+-- "signature_algorithms" produced no ephemeral D-H nor TLS13 cipher (so+-- handshake with lower security)+cipherListCredentialFallback :: [Cipher] -> Bool+cipherListCredentialFallback = all nonDH+  where+    nonDH x = case cipherKeyExchange x of+        CipherKeyExchange_DHE_RSA -> False+        CipherKeyExchange_DHE_DSA -> False+        CipherKeyExchange_ECDHE_RSA -> False+        CipherKeyExchange_ECDHE_ECDSA -> False+        CipherKeyExchange_TLS13 -> False+        _ -> True++-- We filter our allowed ciphers here according to dynamic credential lists.+-- Credentials 'creds' come from server parameters but also SNI callback.+-- When the key exchange requires a signature, we use a+-- subset of this list named 'sigCreds'.  This list has been filtered in order+-- to remove certificates that are not compatible with hash/signature+-- restrictions (TLS 1.2).+getCiphers :: [Cipher] -> Credentials -> Credentials -> [Cipher]+getCiphers ciphers creds sigCreds = filter authorizedCKE ciphers+  where+    authorizedCKE cipher =+        case cipherKeyExchange cipher of+            CipherKeyExchange_RSA -> canEncryptRSA+            CipherKeyExchange_DH_Anon -> True+            CipherKeyExchange_DHE_RSA -> canSignRSA+            CipherKeyExchange_DHE_DSA -> canSignDSA+            CipherKeyExchange_ECDHE_RSA -> canSignRSA+            CipherKeyExchange_ECDHE_ECDSA -> canSignECDSA+            -- unimplemented: non ephemeral DH & ECDH.+            -- Note, these *should not* be implemented, and have+            -- (for example) been removed in OpenSSL 1.1.0+            --+            CipherKeyExchange_DH_DSA -> False+            CipherKeyExchange_DH_RSA -> False+            CipherKeyExchange_ECDH_ECDSA -> False+            CipherKeyExchange_ECDH_RSA -> False+            CipherKeyExchange_TLS13 -> False -- not reached+    canSignDSA = KX_DSA `elem` signingAlgs+    canSignRSA = KX_RSA `elem` signingAlgs+    canSignECDSA = KX_ECDSA `elem` signingAlgs+    canEncryptRSA = isJust $ credentialsFindForDecrypting creds+    signingAlgs = credentialsListSigningAlgorithms sigCreds
+ Network/TLS/Handshake/Server/ClientHello13.hs view
@@ -0,0 +1,212 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}++module Network.TLS.Handshake.Server.ClientHello13 (+    processClientHello13,+    SelectKeyShareResult (..),+) where++import qualified Data.ByteString as B++import Network.TLS.Cipher+import Network.TLS.Context.Internal+import Network.TLS.Crypto+import Network.TLS.Extension+import Network.TLS.Handshake.Common13+import Network.TLS.Handshake.Signature+import Network.TLS.Handshake.State+import Network.TLS.IO.Encode+import Network.TLS.Imports+import Network.TLS.Parameters+import Network.TLS.Session+import Network.TLS.State+import Network.TLS.Struct+import Network.TLS.Types++limitSupportedGroups :: Int+limitSupportedGroups = 64++-- TLS 1.3 or later+processClientHello13+    :: ServerParams+    -> Context+    -> ClientHello+    -> IO+        ( SelectKeyShareResult+        , (Cipher, Hash, Bool) -- rtt0+        , (SecretPair EarlySecret, [ExtensionRaw], Bool, Bool) -- authenticated, is0RTTvalid+        )+processClientHello13 sparams ctx ch@CH{..} = do+    when+        (any (\(ExtensionRaw eid _) -> eid == EID_PreSharedKey) $ init chExtensions)+        $ throwCore+        $ Error_Protocol "extension pre_shared_key must be last" IllegalParameter+    -- Deciding cipher.+    -- The shared cipherlist can become empty after filtering for compatible+    -- creds, check now before calling onCipherChoosing, which does not handle+    -- empty lists.+    when (null ciphersFilteredVersion) $+        throwCore $+            Error_Protocol "no cipher in common with the TLS 1.3 client" HandshakeFailure+    let usedCipher = onCipherChoosing (serverHooks sparams) TLS13 ciphersFilteredVersion+        usedHash = cipherHash usedCipher+        rtt0 =+            lookupAndDecode+                EID_EarlyData+                MsgTClientHello+                chExtensions+                False+                (\(EarlyDataIndication _) -> True)+    if rtt0+        then+            -- mark a 0-RTT attempt before a possible HRR, and before updating the+            -- status again if 0-RTT successful+            setEstablished ctx (EarlyDataNotAllowed 3) -- hardcoding+        else+            -- In the case of HRR, EarlyDataNotAllowed is already set.+            -- It should be cleared here.+            setEstablished ctx NotEstablished+    -- Deciding key exchange from key shares+    let require =+            throwCore $+                Error_Protocol+                    "key exchange not implemented, expected key_share extension"+                    MissingExtension+        extract (KeyShareClientHello kses) = return kses+        extract _ = require+    keyShares <-+        lookupAndDecodeAndDo EID_KeyShare MsgTClientHello chExtensions require extract+    let clientGroups =+            take limitSupportedGroups $+                lookupAndDecode+                    EID_SupportedGroups+                    MsgTClientHello+                    chExtensions+                    []+                    (\(SupportedGroups gs) -> gs)+    (mgroup, doHRR) <-+        onSelectKeyShare+            (serverHooks sparams)+            serverGroups+            clientGroups+            $ map keyShareEntryGroup keyShares+    keyshareResult <- case mgroup of+        Nothing -> return SelectKeyShareNotFound+        Just g+            | doHRR -> return $ SelectKeyShareHRR g+            | otherwise -> case filter (\e -> keyShareEntryGroup e == g) keyShares of+                [] -> return SelectKeyShareNotFound+                [x] -> return $ SelectKeyShareFound x+                _ -> throwCore $ Error_Protocol "duplicated key_share" IllegalParameter++    let triple = (usedCipher, usedHash, rtt0)+    pskEarlySecret <- pskAndEarlySecret sparams ctx triple ch+    (ich, b) <- fromJust <$> usingHState ctx getClientHello+    updateTranscriptHash12 ctx (ClientHello ich, b)+    return (keyshareResult, triple, pskEarlySecret)+  where+    ciphersFilteredVersion = intersectCiphers chCiphers serverCiphers+    serverCiphers =+        filter+            (cipherAllowedForVersion TLS13)+            (supportedCiphers $ serverSupported sparams)+    serverGroups = supportedGroupsTLS13 $ serverSupported sparams++data SelectKeyShareResult+    = -- | Negotiation failure+      SelectKeyShareNotFound+    | -- | Send a hello retry request with this group+      SelectKeyShareHRR Group+    | -- | Use this key share+      SelectKeyShareFound KeyShareEntry+    deriving (Eq, Show)++pskAndEarlySecret+    :: ServerParams+    -> Context+    -> (Cipher, Hash, Bool) -- rtt0+    -> ClientHello+    -> IO (SecretPair EarlySecret, [ExtensionRaw], Bool, Bool) -- authenticated, is0RTTvalid+pskAndEarlySecret sparams ctx (usedCipher, usedHash, rtt0) CH{..} = do+    (psk, binderInfo, is0RTTvalid) <- choosePSK+    earlyKey <- calculateEarlySecret ctx choice (Left psk)+    let earlySecret = pairBase earlyKey+        authenticated = isJust binderInfo+    preSharedKeyExt <- checkBinder earlySecret binderInfo+    return (earlyKey, preSharedKeyExt, authenticated, is0RTTvalid)+  where+    choice = makeCipherChoice TLS13 usedCipher++    choosePSK =+        lookupAndDecodeAndDo+            EID_PreSharedKey+            MsgTClientHello+            chExtensions+            (return (zero, Nothing, False))+            selectPSK++    selectPSK (PreSharedKeyClientHello (PskIdentity identity obfAge : _) bnds@(bnd : _)) = do+        when (null dhModes) $+            throwCore $+                Error_Protocol "no psk_key_exchange_modes extension" MissingExtension+        if PSK_DHE_KE `elem` dhModes+            then do+                let len = sum (map (\x -> B.length x + 1) bnds) + 2+                    mgr = sharedSessionManager $ serverShared sparams+                -- sessionInvalidate is not used for TLS 1.3+                -- because PSK is always changed.+                -- So, identity is not stored in Context.+                msdata <-+                    if rtt0+                        then sessionResumeOnlyOnce mgr identity+                        else sessionResume mgr identity+                case msdata of+                    Just sdata -> do+                        let tinfo = fromJust $ sessionTicketInfo sdata+                            psk = sessionSecret sdata+                        isFresh <- checkFreshness tinfo obfAge+                        (isPSKvalid, is0RTTvalid) <- checkSessionEquality sdata+                        if isPSKvalid && isFresh+                            then return (psk, Just (bnd, 0 :: Int, len), is0RTTvalid)+                            else -- fall back to full handshake+                                return (zero, Nothing, False)+                    _ -> return (zero, Nothing, False)+            else return (zero, Nothing, False)+    selectPSK _ = return (zero, Nothing, False)++    checkBinder _ Nothing = return []+    checkBinder earlySecret (Just (binder, n, tlen)) = do+        (_, b) <- fromJust <$> usingHState ctx getClientHello+        let binder' = makePSKBinder earlySecret usedHash tlen $ B.concat b --- xxx+        unless (binder == binder') $+            decryptError "PSK binder validation failed"+        return [toExtensionRaw $ PreSharedKeyServerHello $ fromIntegral n]++    checkSessionEquality sdata = do+        msni <- usingState_ ctx getClientSNI+        -- ALPN should be checked.+        -- But it's an extension in EE, sigh.+        --        malpn <- usingState_ ctx getNegotiatedProtocol+        let isSameSNI = sessionClientSNI sdata == msni+            isSameCipher = sessionCipher sdata == cipherID usedCipher+            ciphers = supportedCiphers $ serverSupported sparams+            scid = sessionCipher sdata+            isSameKDF = case findCipher scid ciphers of+                Nothing -> False+                Just c -> cipherHash c == cipherHash usedCipher+            isSameVersion = TLS13 == sessionVersion sdata+            --            isSameALPN = sessionALPN sdata == malpn+            isPSKvalid = isSameKDF && isSameSNI -- fixme: SNI is not required+            is0RTTvalid = isSameVersion && isSameCipher -- && isSameALPN+        return (isPSKvalid, is0RTTvalid)++    dhModes =+        lookupAndDecode+            EID_PskKeyExchangeModes+            MsgTClientHello+            chExtensions+            []+            (\(PskKeyExchangeModes ms) -> ms)++    hashSize = hashDigestSize usedHash+    zero = B.replicate hashSize 0
+ Network/TLS/Handshake/Server/Common.hs view
@@ -0,0 +1,207 @@+{-# LANGUAGE OverloadedStrings #-}++module Network.TLS.Handshake.Server.Common (+    applicationProtocol,+    checkValidClientCertChain,+    clientCertificate,+    credentialDigitalSignatureKey,+    filterCredentials,+    filterCredentialsWithHashSignatures,+    makeCredentialPredicate,+    isCredentialAllowed,+    storePrivInfoServer,+    hashAndSignaturesInCommon,+    processRecordSizeLimit,+) where++import Control.Monad.State.Strict+import Data.X509 (ExtKeyUsageFlag (..), ExtKeyUsagePurpose (..))++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)++-- 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.+makeCredentialPredicate :: Version -> [ExtensionRaw] -> (Group -> Bool)+makeCredentialPredicate ver exts+    | ver >= TLS13 = const True+    | otherwise =+        lookupAndDecode+            EID_SupportedGroups+            MsgTClientHello+            exts+            (const True)+            (\(SupportedGroups sg) -> (`elem` sg))++isCredentialAllowed :: Version -> (Group -> Bool) -> Credential -> Bool+isCredentialAllowed ver p cred =+    pubkey `versionCompatible` ver && satisfiesEcPredicate p pubkey+  where+    (pubkey, _) = credentialPublicPrivateKeys cred++-- 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 =+    lookupAndDecode+        EID_SignatureAlgorithmsCert+        MsgTClientHello+        exts+        lookupSignatureAlgorithms+        (\(SignatureAlgorithmsCert sas) -> withAlgs sas)+  where+    lookupSignatureAlgorithms =+        lookupAndDecode+            EID_SignatureAlgorithms+            MsgTClientHello+            exts+            id+            (\(SignatureAlgorithms sas) -> withAlgs sas)+    withAlgs sas = filterCredentials (credentialMatchesHashSignatures sas)++storePrivInfoServer :: MonadIO m => Context -> Credential -> m ()+storePrivInfoServer ctx (cc, privkey) = void (storePrivInfo ctx cc privkey)++-- ALPN (Application Layer Protocol Negotiation)+applicationProtocol+    :: Context -> [ExtensionRaw] -> ServerParams -> IO (Maybe ExtensionRaw)+applicationProtocol ctx exts sparams = case onALPN of+    Nothing -> return Nothing+    Just io ->+        lookupAndDecodeAndDo+            EID_ApplicationLayerProtocolNegotiation+            MsgTClientHello+            exts+            (return Nothing)+            $ select io+  where+    onALPN = onALPNClientSuggest $ serverHooks sparams+    select io (ApplicationLayerProtocolNegotiation protos) = do+        proto <- io protos+        when (proto == "") $+            throwCore $+                Error_Protocol "no supported application protocols" NoApplicationProtocol+        usingState_ ctx $ do+            setExtensionALPN True+            setNegotiatedProtocol proto+        let alpn = ApplicationLayerProtocolNegotiation [proto]+        return $ Just $ toExtensionRaw alpn++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 -> do+            verifyLeafKeyUsage [KeyUsage_digitalSignature] certs+            verifyLeafKeyUsagePurpose KeyUsagePurpose_ClientAuth certs+        CertificateUsageReject reason -> certificateRejected reason++    -- Remember cert chain for later use.+    --+    usingHState ctx $ setClientCertChain certs++----------------------------------------------------------------++-- The values in the "signature_algorithms" extension+-- are in descending order of preference.+-- However here the algorithms are selected according+-- to server preference in 'supportedHashSignatures'.+hashAndSignaturesInCommon+    :: [HashAndSignatureAlgorithm] -> [ExtensionRaw] -> [HashAndSignatureAlgorithm]+hashAndSignaturesInCommon sHashSigs exts = sHashSigs `intersect` cHashSigs+  where+    -- See Section 7.4.1.4.1 of RFC 5246.+    defVal =+        [ (HashSHA1, SignatureECDSA)+        , (HashSHA1, SignatureRSA)+        , (HashSHA1, SignatureDSA)+        ]+    cHashSigs =+        lookupAndDecode+            EID_SignatureAlgorithms+            MsgTClientHello+            exts+            defVal+            (\(SignatureAlgorithms sas) -> sas)++processRecordSizeLimit+    :: Context -> [ExtensionRaw] -> Bool -> IO (Maybe ExtensionRaw)+processRecordSizeLimit ctx chExts tls13 = do+    let mmylim = limitRecordSize $ sharedLimit $ ctxShared ctx+    setMyRecordLimit ctx mmylim+    case mmylim of+        Nothing -> return Nothing+        Just mylim -> do+            lookupAndDecodeAndDo+                EID_RecordSizeLimit+                MsgTClientHello+                chExts+                (return ())+                (setPeerRecordSizeLimit ctx tls13)+            peerSentRSL <- checkPeerRecordLimit ctx+            if peerSentRSL+                then do+                    let mysiz = fromIntegral mylim + if tls13 then 1 else 0+                        rsl = RecordSizeLimit mysiz+                    return $ Just $ toExtensionRaw rsl+                else return Nothing
+ Network/TLS/Handshake/Server/ServerHello12.hs view
@@ -0,0 +1,325 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}++module Network.TLS.Handshake.Server.ServerHello12 (+    sendServerHello12,+) where++import Data.ByteArray (convert)++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)+    -> ClientHello+    -> 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+            sh <- makeServerHello sparams ctx usedCipher mcred chExtensions serverSession+            build <- sendServerFirstFlight sparams ctx usedCipher mcred chExtensions+            let ff = ServerHello sh : build [ServerHelloDone]+            sendPacket12 ctx $ Handshake ff []+            contextFlush ctx+        Just sessionData -> do+            usingState_ ctx $ do+                setSession chSession+                setTLS12SessionResuming True+            sh <-+                makeServerHello sparams ctx usedCipher mcred chExtensions chSession+            sendPacket12 ctx $ Handshake [ServerHello sh] []+            let mainSecret = convert $ sessionSecret sessionData+            usingHState ctx $ setMainSecret TLS12 ServerRole mainSecret+            logKey ctx $ MainSecret mainSecret+            sendCCSandFinished ctx ServerRole+    return resumeSessionData++recoverSessionData :: Context -> ClientHello -> 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 different 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 (CertificateChain_ 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) <- generateGroup ctx grp+        let serverParams = ServerECDHParams grp srvpub+        usingHState ctx $ setServerECDHParams serverParams+        usingHState ctx $ setGroupPrivate [(grp, 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 ServerHello+makeServerHello sparams ctx usedCipher mcred chExts session = do+    resuming <- usingState_ ctx getTLS12SessionResuming+    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++    srand <-+        serverRandom ctx TLS12 $ supportedVersions $ serverSupported sparams++    let shExts =+            sharedHelloExtensions (serverShared sparams)+                ++ catMaybes+                    [ {- 0x00 -} sniExt+                    , {- 0x0b -} ecPointExt+                    , {- 0x10 -} alpnExt+                    , {- 0x17 -} emsExt+                    , {- 0x1c -} recodeSizeLimitExt+                    , {- 0x23 -} sessionTicketExt+                    , {- 0xff01 -} secureRenegExt+                    ]+    usingState_ ctx $ setVersion TLS12+    setServerHelloParameters12 ctx TLS12 srand usedCipher nullCompression+    return $+        SH+            { shVersion = TLS12+            , shRandom = srand+            , shSession = session+            , shCipher = CipherId (cipherID usedCipher)+            , shComp = 0+            , shExtensions = shExts+            }++negotiatedGroupsInCommon :: [Group] -> [ExtensionRaw] -> [Group]+negotiatedGroupsInCommon serverGroups chExts =+    lookupAndDecode+        EID_SupportedGroups+        MsgTClientHello+        chExts+        []+        common+  where+    common (SupportedGroups clientGroups) = serverGroups `intersect` clientGroups
+ Network/TLS/Handshake/Server/ServerHello13.hs view
@@ -0,0 +1,367 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}++module Network.TLS.Handshake.Server.ServerHello13 (+    sendServerHello13,+    sendHRR,+) where++import Control.Monad.State.Strict++import Network.TLS.Cipher+import Network.TLS.Context.Internal+import Network.TLS.Credentials+import Network.TLS.Crypto+import Network.TLS.Extension+import Network.TLS.Handshake.Common+import Network.TLS.Handshake.Common13+import Network.TLS.Handshake.Control+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.Handshake.State13+import Network.TLS.Handshake.TranscriptHash+import Network.TLS.IO+import Network.TLS.Imports+import Network.TLS.Parameters+import Network.TLS.State+import Network.TLS.Struct+import Network.TLS.Struct13+import Network.TLS.Types+import Network.TLS.X509++sendServerHello13+    :: ServerParams+    -> Context+    -> KeyShareEntry+    -> (Cipher, Hash, Bool) -- rtt0+    -> (SecretPair EarlySecret, [ExtensionRaw], Bool, Bool) -- authenticated, is0RTTvalid+    -> ClientHello+    -> Maybe ClientRandom+    -> IO+        ( SecretTriple ApplicationSecret+        , ClientTrafficSecret HandshakeSecret+        , Bool -- authenticated+        , Bool -- rtt0OK+        )+sendServerHello13 sparams ctx clientKeyShare (usedCipher, usedHash, rtt0) (earlyKey, preSharedKeyExt, authenticated, is0RTTvalid) CH{..} mOuterClientRandom = do+    let clientEarlySecret = pairClient earlyKey+        earlySecret = pairBase earlyKey+    -- parse CompressCertificate to check if it is broken here+    let zlib =+            lookupAndDecode+                EID_CompressCertificate+                MsgTClientHello+                chExtensions+                False+                (\(CompressCertificate ccas) -> CCA_Zlib `elem` ccas)++    recodeSizeLimitExt <- processRecordSizeLimit ctx chExtensions True+    enableMyRecordLimit ctx++    newSession ctx >>= \ss -> usingState_ ctx $ do+        setSession ss+        setTLS13ClientSupportsPHA supportsPHA+    usingHState ctx $ do+        setSupportedGroup $ keyShareEntryGroup clientKeyShare+        setOuterClientRandom mOuterClientRandom+    hrr <- usingState_ ctx getTLS13HRR+    alpnExt <- applicationProtocol ctx chExtensions sparams+    setServerParameter+    let rtt0OK = authenticated && not hrr && rtt0 && rtt0accept && is0RTTvalid+    extraCreds <-+        usingState_ ctx getClientSNI >>= onServerNameIndication (serverHooks sparams)+    let p = makeCredentialPredicate TLS13 chExtensions+        allCreds =+            filterCredentials (isCredentialAllowed TLS13 p) $+                extraCreds `mappend` sharedCredentials (ctxShared ctx)+    ----------------------------------------------------------------+    established <- ctxEstablished ctx+    if established /= NotEstablished+        then+            if rtt0OK+                then do+                    usingHState ctx $ setTLS13HandshakeMode RTT0+                    usingHState ctx $ setTLS13RTT0Status RTT0Accepted+                else do+                    usingHState ctx $ setTLS13HandshakeMode PreSharedKey+                    usingHState ctx $ setTLS13RTT0Status RTT0Rejected+        else when authenticated $ usingHState ctx $ setTLS13HandshakeMode PreSharedKey+    -- else : FullHandshake or HelloRetryRequest+    mCredInfo <-+        if authenticated then return Nothing else decideCredentialInfo allCreds+    (ecdhe, keyShare) <- makeServerKeyShare ctx clientKeyShare+    ensureRecvComplete ctx+    (clientHandshakeSecret, handSecret) <- runPacketFlight ctx $ do+        sendServerHello keyShare+        sendChangeCipherSpec13 ctx+        ----------------------------------------------------------------+        handKey <- liftIO $ calculateHandshakeSecret ctx choice earlySecret ecdhe+        let serverHandshakeSecret = triServer handKey+            clientHandshakeSecret = triClient handKey+            handSecret = triBase handKey+        liftIO $ do+            if rtt0OK && not (ctxQUICMode ctx)+                then setRxRecordState ctx usedHash usedCipher clientEarlySecret+                else setRxRecordState ctx usedHash usedCipher clientHandshakeSecret+            setTxRecordState ctx usedHash usedCipher serverHandshakeSecret+            let mEarlySecInfo+                    | rtt0OK = Just $ EarlySecretInfo usedCipher clientEarlySecret+                    | otherwise = Nothing+                handSecInfo = HandshakeSecretInfo usedCipher (clientHandshakeSecret, serverHandshakeSecret)+            contextSync ctx $ SendServerHello chExtensions mEarlySecInfo handSecInfo+        ----------------------------------------------------------------+        liftIO $ enablePeerRecordLimit ctx+        sendExtensions rtt0OK alpnExt recodeSizeLimitExt+        case mCredInfo of+            Nothing -> return ()+            Just (cred, hashSig) -> sendCertAndVerify cred hashSig zlib+        let ServerTrafficSecret shs = serverHandshakeSecret+        rawFinished <- makeFinished ctx usedHash shs+        loadPacket13 ctx $ Handshake13 [rawFinished] []+        return (clientHandshakeSecret, handSecret)+    ----------------------------------------------------------------+    hChSf <- transcriptHash ctx "CH..SF"+    appKey <- calculateApplicationSecret ctx choice handSecret hChSf+    let clientApplicationSecret0 = triClient appKey+        serverApplicationSecret0 = triServer appKey+    setTxRecordState ctx usedHash usedCipher serverApplicationSecret0+    let appSecInfo = ApplicationSecretInfo (clientApplicationSecret0, serverApplicationSecret0)+    contextSync ctx $ SendServerFinished appSecInfo+    ----------------------------------------------------------------+    when rtt0OK $ setEstablished ctx (EarlyDataAllowed rtt0max)+    return (appKey, clientHandshakeSecret, authenticated, rtt0OK)+  where+    choice = makeCipherChoice TLS13 usedCipher++    setServerParameter = do+        usingState_ ctx $ setVersion TLS13+        failOnEitherError $ setServerHelloParameters13 ctx usedCipher False++    supportsPHA =+        lookupAndDecode+            EID_PostHandshakeAuth+            MsgTClientHello+            chExtensions+            False+            (\PostHandshakeAuth -> True)++    rtt0max = safeNonNegative32 $ serverEarlyDataSize sparams+    rtt0accept = serverEarlyDataSize sparams > 0++    decideCredentialInfo allCreds = do+        let err =+                throwCore $ Error_Protocol "broken signature_algorithms extension" DecodeError+        cHashSigs <-+            lookupAndDecodeAndDo+                EID_SignatureAlgorithms+                MsgTClientHello+                chExtensions+                err+                (\(SignatureAlgorithms sas) -> return sas)+        -- When deciding signature algorithm and certificate, we try to keep+        -- certificates supported by the client, but fallback to all credentials+        -- if this produces no suitable result (see RFC 5246 section 7.4.2 and+        -- RFC 8446 section 4.4.2.2).+        let sHashSigs = filter isHashSignatureValid13 $ supportedHashSignatures $ ctxSupported ctx+            hashSigs = sHashSigs `intersect` cHashSigs+            cltCreds = filterCredentialsWithHashSignatures chExtensions allCreds+        case credentialsFindForSigning13 hashSigs cltCreds of+            Nothing ->+                case credentialsFindForSigning13 hashSigs allCreds of+                    Nothing -> throwCore $ Error_Protocol "credential not found" HandshakeFailure+                    mcs -> return mcs+            mcs -> return mcs++    sendServerHello keyShare = do+        let keyShareExt = toExtensionRaw $ KeyShareServerHello keyShare+            versionExt = toExtensionRaw $ SupportedVersionsServerHello TLS13+            shExts = keyShareExt : versionExt : preSharedKeyExt+        if isJust mOuterClientRandom+            then do+                srand <- liftIO $ serverRandomECH ctx+                let cipherId = CipherId (cipherID usedCipher)+                    sh =+                        SH+                            { shVersion = TLS12+                            , shRandom = srand+                            , shSession = chSession+                            , shCipher = cipherId+                            , shComp = 0+                            , shExtensions = shExts+                            }+                suffix <- computeConfirm ctx usedHash sh "ech accept confirmation"+                let srand' = replaceServerRandomECH srand suffix+                    sh' =+                        SH+                            { shVersion = TLS12+                            , shRandom = srand'+                            , shSession = chSession+                            , shCipher = cipherId+                            , shComp = 0+                            , shExtensions = shExts+                            }+                usingHState ctx $ setECHAccepted True+                loadPacket13 ctx $ Handshake13 [ServerHello13 sh'] []+            else do+                srand <-+                    liftIO $+                        serverRandom ctx TLS13 $+                            supportedVersions $+                                serverSupported sparams+                let sh =+                        SH+                            { shVersion = TLS12+                            , shRandom = srand+                            , shSession = chSession+                            , shCipher = CipherId (cipherID usedCipher)+                            , shComp = 0+                            , shExtensions = shExts+                            }+                loadPacket13 ctx $ Handshake13 [ServerHello13 sh] []++    sendCertAndVerify cred@(certChain, _) hashSig zlib = do+        storePrivInfoServer ctx cred+        when (serverWantClientCert sparams) $ do+            let certReqCtx = "" -- this must be zero length here.+                certReq = makeCertRequest sparams ctx certReqCtx True+            loadPacket13 ctx $ Handshake13 [certReq] []+            usingHState ctx $ setCertReqSent True++        let CertificateChain cs = certChain+            ess = replicate (length cs) []+        let certtag = if zlib then CompressedCertificate13 else Certificate13+        loadPacket13 ctx $+            Handshake13 [certtag "" (CertificateChain_ certChain) ess] []+        liftIO $ usingState_ ctx $ setServerCertificateChain certChain+        hChSc <- transcriptHash ctx "CH..SC"+        pubkey <- getLocalPublicKey ctx+        vrfy <- makeCertVerify ctx pubkey hashSig hChSc+        loadPacket13 ctx $ Handshake13 [vrfy] []++    sendExtensions rtt0OK alpnExt recodeSizeLimitExt = do+        msni <- liftIO $ usingState_ ctx getClientSNI+        let sniExt = 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 _ -> Just $ toExtensionRaw $ ServerName []+                Nothing -> Nothing++        mgroup <- usingHState ctx getSupportedGroup+        let serverGroups = supportedGroups (ctxSupported ctx)+            groupExt = case serverGroups of+                [] -> Nothing+                rg : _ -> case mgroup of+                    Nothing -> Nothing+                    Just grp+                        | grp == rg -> Nothing+                        | otherwise -> Just $ toExtensionRaw $ SupportedGroups serverGroups+        let earlyDataExt+                | rtt0OK = Just $ toExtensionRaw $ EarlyDataIndication Nothing+                | otherwise = Nothing++        sendECH <- usingHState ctx getECHEE+        let echExt+                | sendECH =+                    Just $+                        toExtensionRaw $+                            ECHEncryptedExtensions $+                                sharedECHConfigList $+                                    serverShared sparams+                | otherwise = Nothing+        let eeExtensions =+                sharedHelloExtensions (serverShared sparams)+                    ++ catMaybes+                        [ {- 0x00 -} sniExt+                        , {- 0x0a -} groupExt+                        , {- 0x10 -} alpnExt+                        , {- 0x1c -} recodeSizeLimitExt+                        , {- 0x2a -} earlyDataExt+                        , {- 0xfe0d -} echExt+                        ]+        eeExtensions' <-+            liftIO $ onEncryptedExtensionsCreating (serverHooks sparams) eeExtensions+        loadPacket13 ctx $ Handshake13 [EncryptedExtensions13 eeExtensions'] []++credentialsFindForSigning13+    :: [HashAndSignatureAlgorithm]+    -> Credentials+    -> Maybe (Credential, HashAndSignatureAlgorithm)+credentialsFindForSigning13 hss0 creds = loop hss0+  where+    loop [] = Nothing+    loop (hs : hss) = case credentialsFindForSigning13' hs creds of+        Nothing -> loop hss+        Just cred -> Just (cred, hs)++-- See credentialsFindForSigning.+credentialsFindForSigning13'+    :: HashAndSignatureAlgorithm -> Credentials -> Maybe Credential+credentialsFindForSigning13' sigAlg (Credentials l) = find forSigning l+  where+    forSigning cred = case credentialDigitalSignatureKey cred of+        Nothing -> False+        Just pub -> pub `signatureCompatible13` sigAlg++contextSync :: Context -> ServerState -> IO ()+contextSync ctx ctl = case ctxHandshakeSync ctx of+    HandshakeSync _ sync -> sync ctx ctl++----------------------------------------------------------------++sendHRR :: Context -> Group -> (Cipher, Hash, c) -> ClientHello -> Bool -> IO ()+sendHRR ctx g (usedCipher, usedHash, _) CH{..} isEch = do+    twice <- usingState_ ctx getTLS13HRR+    when twice $+        throwCore $+            Error_Protocol "Hello retry not allowed again" HandshakeFailure+    usingState_ ctx $ setTLS13HRR True+    failOnEitherError $ setServerHelloParameters13 ctx usedCipher True+    hrr <- makeHRR ctx usedCipher usedHash chSession g isEch+    usingHState ctx $ setTLS13HandshakeMode HelloRetryRequest+    runPacketFlight ctx $ do+        loadPacket13 ctx $ Handshake13 [ServerHello13 hrr] []+        sendChangeCipherSpec13 ctx++makeHRR+    :: Context -> Cipher -> Hash -> Session -> Group -> Bool -> IO ServerHello+makeHRR _ usedCipher _ session g False = return hrr+  where+    keyShareExt = toExtensionRaw $ KeyShareHRR g+    versionExt = toExtensionRaw $ SupportedVersionsServerHello TLS13+    extensions = [keyShareExt, versionExt]+    cipherId = CipherId $ cipherID usedCipher+    hrr =+        SH+            { shVersion = TLS12+            , shRandom = hrrRandom+            , shSession = session+            , shCipher = cipherId+            , shComp = 0+            , shExtensions = extensions+            }+makeHRR ctx usedCipher usedHash session g True = do+    suffix <- computeConfirm ctx usedHash hrr "hrr ech accept confirmation"+    let echExt' = toExtensionRaw $ ECHHelloRetryRequest suffix+        extensions' = [keyShareExt, versionExt, echExt']+        hrr' = hrr{shExtensions = extensions'}+    return hrr'+  where+    keyShareExt = toExtensionRaw $ KeyShareHRR g+    versionExt = toExtensionRaw $ SupportedVersionsServerHello TLS13+    echExt = toExtensionRaw $ ECHHelloRetryRequest "\x00\x00\x00\x00\x00\x00\x00\x00"+    extensions = [keyShareExt, versionExt, echExt]+    cipherId = CipherId $ cipherID usedCipher+    hrr =+        SH+            { shVersion = TLS12+            , shRandom = hrrRandom+            , shSession = session+            , shCipher = cipherId+            , shComp = 0+            , shExtensions = extensions+            }
+ Network/TLS/Handshake/Server/TLS12.hs view
@@ -0,0 +1,216 @@+{-# LANGUAGE OverloadedStrings #-}++module Network.TLS.Handshake.Server.TLS12 (+    recvClientSecondFlight12,+) where++import Control.Monad.State.Strict (gets)+import Data.ByteArray (convert)+import qualified Data.ByteString as B++import Network.TLS.Context.Internal+import Network.TLS.Crypto+import Network.TLS.Handshake.Common+import Network.TLS.Handshake.Key+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.Packet hiding (getSession)+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)++----------------------------------------------------------------++recvClientSecondFlight12+    :: ServerParams+    -> Context+    -> Maybe SessionData+    -> IO ()+recvClientSecondFlight12 sparams ctx resumeSessionData = do+    case resumeSessionData of+        Nothing -> do+            recvClientCCC sparams ctx+            mticket <- sessionEstablished ctx+            case mticket of+                Nothing -> return ()+                Just ticket -> do+                    let life = adjustLifetime $ serverTicketLifetime sparams+                    sendPacket12 ctx $ Handshake [NewSessionTicket life ticket] []+            sendCCSandFinished ctx ServerRole+        Just _ -> do+            _ <- sessionEstablished ctx+            recvCCSandFinished ctx+    finishHandshake12 ctx+  where+    adjustLifetime i+        | i < 0 = 0+        | i > 604800 = 604800+        | otherwise = fromIntegral i++sessionEstablished :: Context -> IO (Maybe Ticket)+sessionEstablished ctx = do+    session <- usingState_ ctx getSession+    -- only callback the session established if we have a session+    case session of+        Session (Just sessionId) -> do+            sessionData <- getSessionData ctx+            let sessionId' = B.copy sessionId+            -- SessionID method: SessionID is used as key to store+            -- SessionData. Nothing is returned.+            --+            -- Session ticket method: SessionID is ignored. SessionData+            -- is encrypted and returned.+            sessionEstablish+                (sharedSessionManager $ ctxShared ctx)+                sessionId'+                (fromJust sessionData)+        _ -> return Nothing -- never reach++----------------------------------------------------------------++-- | receive Client data in handshake until the Finished handshake.+--+--      <- [certificate]+--      <- client key xchg+--      <- [cert verify]+--      <- change cipher+--      <- finish+recvClientCCC :: ServerParams -> Context -> IO ()+recvClientCCC sparams ctx = runRecvState ctx (RecvStateHandshake expectClientCertificate)+  where+    expectClientCertificate (Certificate (CertificateChain_ certs)) = do+        clientCertificate sparams ctx certs+        processCertificate ctx ServerRole certs++        -- FIXME: We should check whether the certificate+        -- matches our request and that we support+        -- verifying with that certificate.++        return $ RecvStateHandshake $ expectClientKeyExchange True+    expectClientCertificate p = expectClientKeyExchange False p++    -- cannot use RecvStateHandshake, as the next message could be a ChangeCipher,+    -- so we must process any packet, and in case of handshake call processHandshake manually.+    expectClientKeyExchange followedCertVerify (ClientKeyXchg ckx) = do+        processClientKeyXchg ctx ckx+        if followedCertVerify+            then return $ RecvStateHandshake expectCertificateVerify+            else return $ RecvStatePacket $ expectChangeCipherSpec ctx+    expectClientKeyExchange _ p = unexpected (show p) (Just "client key exchange")++    expectCertificateVerify (CertVerify dsig) = do+        certs <- checkValidClientCertChain ctx "change cipher message expected"++        usedVersion <- usingState_ ctx getVersion+        -- Fetch all handshake messages up to now.+        msgs <- usingHState ctx $ B.concat <$> getHandshakeMessages++        pubKey <- usingHState ctx getRemotePublicKey+        checkDigitalSignatureKey usedVersion pubKey++        verif <- checkCertificateVerify ctx usedVersion pubKey msgs dsig+        processClientCertVerify sparams ctx certs verif+        return $ RecvStatePacket $ expectChangeCipherSpec ctx+    expectCertificateVerify p = unexpected (show p) (Just "client certificate verify")++----------------------------------------------------------------++expectChangeCipherSpec :: Context -> Packet -> IO (RecvState IO)+expectChangeCipherSpec ctx ChangeCipherSpec = do+    enableMyRecordLimit ctx+    return $ RecvStateHandshake $ expectFinished ctx+expectChangeCipherSpec _ p = unexpected (show p) (Just "change cipher")++----------------------------------------------------------------++-- process the client key exchange message. the protocol expects the initial+-- client version received in ClientHello, not the negotiated version.+-- in case the version mismatch, generate a random main secret+processClientKeyXchg :: Context -> ClientKeyXchgAlgorithmData -> IO ()+processClientKeyXchg ctx (CKX_RSA encryptedPreMain) = do+    (rver, role, random) <- usingState_ ctx $ do+        (,,) <$> getVersion <*> getRole <*> genRandom 48+    ePreMain <- decryptRSA ctx encryptedPreMain+    expectedVer <- usingHState ctx $ gets hstClientVersion+    mainSecret <- case ePreMain of+        Left _ ->+            -- BadRecordMac is nonsense but for tlsfuzzer+            throwCore $+                Error_Protocol "invalid client public key" BadRecordMac+        Right preMain -> case decodePreMainSecret $ convert preMain of+            Left _ -> usingHState ctx $ setMainSecretFromPre rver role $ convert random+            Right (ver, _)+                | ver /= expectedVer ->+                    usingHState ctx $ setMainSecretFromPre rver role $ convert random+                | otherwise -> usingHState ctx $ setMainSecretFromPre rver role preMain+    logKey ctx (MainSecret mainSecret)+processClientKeyXchg ctx (CKX_DH clientDHValue) = do+    rver <- usingState_ ctx getVersion+    role <- usingState_ ctx getRole++    serverParams <- usingHState ctx getServerDHParams+    let params = serverDHParamsToParams serverParams+    unless (dhValid params $ dhUnwrapPublic clientDHValue) $+        throwCore $+            Error_Protocol "invalid client public key" IllegalParameter++    dhpriv <- usingHState ctx getDHPrivate+    let preMain = dhGetShared params dhpriv clientDHValue+    mainSecret <- usingHState ctx $ setMainSecretFromPre rver role preMain+    logKey ctx (MainSecret mainSecret)+processClientKeyXchg ctx (CKX_ECDH bytes) = do+    ServerECDHParams grp _ <- usingHState ctx getServerECDHParams+    case groupDecodePublicB grp bytes of+        Left _ ->+            throwCore $+                Error_Protocol "client public key cannot be decoded" IllegalParameter+        Right clipub -> do+            grpSpris <- usingHState ctx getGroupPrivate+            case lookup grp grpSpris of+                Nothing -> throwCore err+                Just srvpri -> case groupDecapsulate clipub srvpri of+                    Just preMain -> do+                        rver <- usingState_ ctx getVersion+                        role <- usingState_ ctx getRole+                        mainSecret <- usingHState ctx $ setMainSecretFromPre rver role preMain+                        logKey ctx (MainSecret mainSecret)+                    Nothing -> throwCore err+  where+    err = Error_Protocol "cannot generate a shared secret on ECDH" IllegalParameter++----------------------------------------------------------------++processClientCertVerify+    :: ServerParams -> Context -> CertificateChain -> Bool -> IO ()+processClientCertVerify _sparams ctx certs True = do+    -- When verification succeeds, commit the+    -- client certificate chain to the context.+    --+    usingState_ ctx $ setClientCertificateChain certs+    return ()+processClientCertVerify sparams ctx certs False = do+    -- Either verification failed because of an+    -- invalid format (with an error message), or+    -- the signature is wrong.  In either case,+    -- ask the application if it wants to+    -- proceed, we will do that.+    res <- onUnverifiedClientCert (serverHooks sparams)+    if res+        then do+            -- When verification fails, but the+            -- application callbacks accepts, we+            -- also commit the client certificate+            -- chain to the context.+            usingState_ ctx $ setClientCertificateChain certs+        else decryptError "verification failed"++----------------------------------------------------------------++recvCCSandFinished :: Context -> IO ()+recvCCSandFinished ctx = runRecvState ctx $ RecvStatePacket $ expectChangeCipherSpec ctx
+ Network/TLS/Handshake/Server/TLS13.hs view
@@ -0,0 +1,387 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}++module Network.TLS.Handshake.Server.TLS13 (+    recvClientSecondFlight13,+    requestCertificateServer,+    keyUpdate,+    updateKey,+    KeyUpdateRequest (..),+) where++import Control.Exception+import Control.Monad.State.Strict+import Data.IORef++import Network.TLS.Cipher+import Network.TLS.Context.Internal+import Network.TLS.Crypto+import Network.TLS.Extension+import Network.TLS.Handshake.Common hiding (expectFinished)+import Network.TLS.Handshake.Common13+import Network.TLS.Handshake.Key+import Network.TLS.Handshake.Server.Common+import Network.TLS.Handshake.Signature+import Network.TLS.Handshake.State+import Network.TLS.Handshake.State13+import Network.TLS.Handshake.TranscriptHash+import Network.TLS.IO+import Network.TLS.Imports+import Network.TLS.KeySchedule+import Network.TLS.Parameters+import Network.TLS.Session+import Network.TLS.State+import Network.TLS.Struct+import Network.TLS.Struct13+import Network.TLS.Types+import Network.TLS.Util+import Network.TLS.X509++----------------------------------------------------------------++recvClientSecondFlight13+    :: ServerParams+    -> Context+    -> ( SecretTriple ApplicationSecret+       , ClientTrafficSecret HandshakeSecret+       , Bool+       , Bool+       )+    -> ClientHello+    -> IO ()+recvClientSecondFlight13 sparams ctx (appKey, clientHandshakeSecret, authenticated, rtt0OK) CH{..} = do+    sfSentTime <- getCurrentTimeFromBase+    let expectFinished' =+            expectFinished sparams ctx chExtensions appKey clientHandshakeSecret sfSentTime+    if not authenticated && serverWantClientCert sparams+        then runRecvHandshake13 $ do+            -- RFC 8446 Sec 4.4.3: Clients MUST send this message+            -- whenever authenticating via a certificate (i.e., when the+            -- Certificate message is non-empty).  When sent, this message MUST+            -- appear immediately after the Certificate message and immediately+            -- prior to the Finished message.+            skip <- recvHandshake13 ctx $ expectCertificate sparams ctx+            unless skip $+                recvHandshake13hash ctx "CertVerify" (expectCertVerify sparams ctx)+            recvHandshake13hash ctx "Finished" expectFinished'+            ensureRecvComplete ctx+        else+            if rtt0OK && not (ctxQUICMode ctx)+                then+                    setPendingRecvActions+                        ctx+                        [ PendingRecvAction True $ expectEndOfEarlyData ctx clientHandshakeSecret+                        , PendingRecvActionHash True $+                            expectFinished sparams ctx chExtensions appKey clientHandshakeSecret sfSentTime+                        ]+                else runRecvHandshake13 $ do+                    recvHandshake13hash ctx "Finished" expectFinished'+                    ensureRecvComplete ctx++expectFinished+    :: MonadIO m+    => ServerParams+    -> Context+    -> [ExtensionRaw]+    -> SecretTriple ApplicationSecret+    -> ClientTrafficSecret HandshakeSecret+    -> Word64+    -> TranscriptHash+    -> Handshake13+    -> m ()+expectFinished sparams ctx exts appKey clientHandshakeSecret sfSentTime hChBeforeCf (Finished13 verifyData) = liftIO $ do+    modifyTLS13State ctx $ \st -> st{tls13stRecvCF = True}+    (usedHash, usedCipher, _, _) <- getRxRecordState ctx+    let ClientTrafficSecret chs = clientHandshakeSecret+    checkFinished ctx usedHash chs hChBeforeCf verifyData+    finishHandshake13 ctx+    setRxRecordState ctx usedHash usedCipher clientApplicationSecret0+    sendNewSessionTicket sparams ctx usedCipher exts applicationSecret sfSentTime+  where+    applicationSecret = triBase appKey+    clientApplicationSecret0 = triClient appKey+expectFinished _ _ _ _ _ _ _ hs = unexpected (show hs) (Just "finished 13")++expectEndOfEarlyData+    :: Context+    -> ClientTrafficSecret HandshakeSecret+    -> Handshake13+    -> IO ()+expectEndOfEarlyData ctx clientHandshakeSecret EndOfEarlyData13 = do+    (usedHash, usedCipher, _, _) <- getRxRecordState ctx+    setRxRecordState ctx usedHash usedCipher clientHandshakeSecret+expectEndOfEarlyData _ _ hs = unexpected (show hs) (Just "end of early data")++expectCertificate+    :: MonadIO m => ServerParams -> Context -> Handshake13 -> m Bool+expectCertificate sparams ctx (Certificate13 certCtx (CertificateChain_ certs) _ext) = liftIO $ do+    when (certCtx /= "") $+        throwCore $+            Error_Protocol "certificate request context MUST be empty" IllegalParameter+    -- fixme checking _ext+    clientCertificate sparams ctx certs+    return $ isNullCertificateChain certs+expectCertificate sparams ctx (CompressedCertificate13 certCtx (CertificateChain_ certs) _ext) = liftIO $ do+    when (certCtx /= "") $+        throwCore $+            Error_Protocol "certificate request context MUST be empty" IllegalParameter+    -- fixme checking _ext+    clientCertificate sparams ctx certs+    return $ isNullCertificateChain certs+expectCertificate _ _ hs = unexpected (show hs) (Just "certificate 13")++sendNewSessionTicket+    :: ServerParams+    -> Context+    -> Cipher+    -> [ExtensionRaw]+    -> BaseSecret ApplicationSecret+    -> Word64+    -> IO ()+sendNewSessionTicket sparams ctx usedCipher exts applicationSecret sfSentTime = when sendNST $ do+    cfRecvTime <- getCurrentTimeFromBase+    let rtt = cfRecvTime - sfSentTime+    nonce <- TicketNonce <$> getStateRNG ctx 32+    resumptionSecret <- calculateResumptionSecret ctx choice applicationSecret+    let life = adjustLifetime $ serverTicketLifetime sparams+        psk = derivePSK choice resumptionSecret nonce+    (identity, add) <- generateSession life psk rtt0max rtt+    let nst = createNewSessionTicket life add nonce identity rtt0max+    sendPacket13 ctx $ Handshake13 [nst] []+  where+    choice = makeCipherChoice TLS13 usedCipher+    rtt0max = safeNonNegative32 $ serverEarlyDataSize sparams+    sendNST = PSK_DHE_KE `elem` dhModes++    dhModes = case extensionLookup EID_PskKeyExchangeModes exts+        >>= extensionDecode MsgTClientHello of+        Just (PskKeyExchangeModes ms) -> ms+        Nothing -> []++    generateSession life psk maxSize rtt = do+        Session (Just sessionId) <- newSession ctx+        tinfo <- createTLS13TicketInfo life (Left ctx) (Just rtt)+        sdata <- getSessionData13 ctx usedCipher tinfo maxSize psk+        let mgr = sharedSessionManager $ serverShared sparams+        mticket <- sessionEstablish mgr sessionId sdata+        let identity = SessionIDorTicket_ $ fromMaybe sessionId mticket+        return (identity, ageAdd tinfo)++    createNewSessionTicket life add nonce identity maxSize =+        NewSessionTicket13 life add nonce identity nstExtensions+      where+        nstExtensions+            | maxSize == 0 = []+            | otherwise = [earlyDataExt]+          where+            earlyDataExt = toExtensionRaw $ EarlyDataIndication $ Just $ fromIntegral maxSize+    adjustLifetime i+        | i < 0 = 0+        | i > 604800 = 604800+        | otherwise = fromIntegral i++expectCertVerify+    :: MonadIO m+    => ServerParams -> Context -> TranscriptHash -> Handshake13 -> m ()+expectCertVerify sparams ctx (TranscriptHash hChCc) (CertVerify13 (DigitallySigned sigAlg sig)) = liftIO $ do+    certs@(CertificateChain cc) <-+        checkValidClientCertChain ctx "invalid client certificate chain"+    pubkey <- case cc of+        [] -> throwCore $ Error_Protocol "client certificate missing" HandshakeFailure+        c : _ -> return $ certPubKey $ getCertificate c+    ver <- usingState_ ctx getVersion+    checkDigitalSignatureKey ver pubkey+    usingHState ctx $ setPublicKey pubkey+    verif <- checkCertVerify ctx pubkey sigAlg sig hChCc+    clientCertVerify sparams ctx certs verif+expectCertVerify _ _ _ hs = unexpected (show hs) (Just "certificate verify 13")++clientCertVerify :: ServerParams -> Context -> CertificateChain -> Bool -> IO ()+clientCertVerify sparams ctx certs verif = do+    if verif+        then do+            -- When verification succeeds, commit the+            -- client certificate chain to the context.+            --+            usingState_ ctx $ setClientCertificateChain certs+            return ()+        else do+            -- Either verification failed because of an+            -- invalid format (with an error message), or+            -- the signature is wrong.  In either case,+            -- ask the application if it wants to+            -- proceed, we will do that.+            res <- liftIO $ onUnverifiedClientCert (serverHooks sparams)+            if res+                then do+                    -- When verification fails, but the+                    -- application callbacks accepts, we+                    -- also commit the client certificate+                    -- chain to the context.+                    usingState_ ctx $ setClientCertificateChain certs+                else decryptError "verification failed"++----------------------------------------------------------------++newCertReqContext :: Context -> IO CertReqContext+newCertReqContext ctx = getStateRNG ctx 32++requestCertificateServer :: ServerParams -> Context -> IO Bool+requestCertificateServer sparams ctx = handleEx ctx $ do+    tls13 <- tls13orLater ctx+    supportsPHA <- usingState_ ctx getTLS13ClientSupportsPHA+    let ok = tls13 && supportsPHA+    if ok+        then newIORef [] >>= sendCertReqAndRecv+        else return ok+  where+    sendCertReqAndRecv ref = do+        origCertReqCtx <- newCertReqContext ctx+        let certReq13 = makeCertRequest sparams ctx origCertReqCtx False+        _ <- withWriteLock ctx $ do+            bracket (saveHState ctx) (restoreHState ctx) $ \_ -> do+                sendPacket13 ctx $ Handshake13 [certReq13] []+        withReadLock ctx $ do+            (clientCert13, bClientCert13) <- getHandshake ctx ref+            emptyCert <- expectClientCertificate sparams ctx origCertReqCtx clientCert13+            baseHState <- saveHState ctx+            updateTranscriptHash13 ctx (clientCert13, bClientCert13)+            th <- transcriptHash ctx "CH..Cert"+            unless emptyCert $ do+                (certVerify13, bCertVerify13) <- getHandshake ctx ref+                expectCertVerify sparams ctx th certVerify13+                updateTranscriptHash13 ctx (certVerify13, bCertVerify13)+            (finished13, _bFinished13) <- getHandshake ctx ref+            expectClientFinished ctx finished13+            void $ restoreHState ctx baseHState -- fixme+        return True++-- saving appdata and key update?+-- error handling+getHandshake+    :: Context -> IORef [Handshake13R] -> IO Handshake13R+getHandshake ctx ref = do+    hhs <- readIORef ref+    if null hhs+        then do+            ex <- recvPacket13 ctx+            either (terminate ctx) process ex+        else chk hhs+  where+    process (Handshake13 hss bss) = chk $ zip hss bss+    process _ =+        terminate ctx $+            Error_Protocol "post handshake authenticated" UnexpectedMessage+    chk [] = getHandshake ctx ref+    chk ((KeyUpdate13 mode, _) : hbs) = do+        keyUpdate ctx getRxRecordState setRxRecordState+        -- Write lock wraps both actions because we don't want another+        -- packet to be sent by another thread before the Tx state is+        -- updated.+        when (mode == UpdateRequested) $ withWriteLock ctx $ do+            sendPacket13 ctx $ Handshake13 [KeyUpdate13 UpdateNotRequested] []+            keyUpdate ctx getTxRecordState setTxRecordState+        chk hbs+    chk (hb : hbs) = do+        writeIORef ref hbs+        return hb++expectClientCertificate+    :: ServerParams -> Context -> CertReqContext -> Handshake13 -> IO Bool+expectClientCertificate sparams ctx origCertReqCtx (Certificate13 certReqCtx (CertificateChain_ certs) _ext) = do+    expectClientCertificate' sparams ctx origCertReqCtx certReqCtx certs+    return $ isNullCertificateChain certs+expectClientCertificate sparams ctx origCertReqCtx (CompressedCertificate13 certReqCtx (CertificateChain_ certs) _ext) = do+    expectClientCertificate' sparams ctx origCertReqCtx certReqCtx certs+    return $ isNullCertificateChain certs+expectClientCertificate _ _ _ h = unexpected "Certificate" $ Just $ show h++expectClientCertificate'+    :: ServerParams+    -> Context+    -> CertReqContext+    -> CertReqContext+    -> CertificateChain+    -> IO ()+expectClientCertificate' sparams ctx origCertReqCtx certReqCtx certs = do+    when (origCertReqCtx /= certReqCtx) $+        throwCore $+            Error_Protocol "certificate context is wrong" IllegalParameter+    void $ clientCertificate sparams ctx certs++expectClientFinished :: Context -> Handshake13 -> IO ()+expectClientFinished ctx (Finished13 verifyData) = do+    (usedHash, _, level, applicationSecretN) <- getRxRecordState ctx+    unless (level == CryptApplicationSecret) $+        throwCore $+            Error_Protocol+                "tried post-handshake authentication without application traffic secret"+                InternalError+    hChBeforeCf <- transcriptHash ctx "CH..<CF"+    checkFinished ctx usedHash applicationSecretN hChBeforeCf verifyData+expectClientFinished _ h = unexpected "Finished" $ Just $ show h++terminate :: Context -> TLSError -> IO a+terminate ctx err = do+    let (level, desc) = errorToAlert err+        reason = errorToAlertMessage err+        send = sendPacket13 ctx . Alert13+    catchException (send [(level, desc)]) (\_ -> return ())+    setEOF ctx+    throwIO $ Terminated False reason err++handleEx :: Context -> IO Bool -> IO Bool+handleEx ctx f = catchException f $ \exception -> do+    -- If the error was an Uncontextualized TLSException, we replace the+    -- context with HandshakeFailed. If it's anything else, we convert+    -- it to a string and wrap it with Error_Misc and HandshakeFailed.+    let tlserror = case fromException exception of+            Just e | Uncontextualized e' <- e -> e'+            _ -> Error_Misc (show exception)+    sendPacket13 ctx $ Alert13 [errorToAlert tlserror]+    void $ throwIO $ PostHandshake tlserror+    return False++----------------------------------------------------------------++keyUpdate+    :: Context+    -> (Context -> IO (Hash, Cipher, CryptLevel, Secret))+    -> (Context -> Hash -> Cipher -> AnyTrafficSecret ApplicationSecret -> IO ())+    -> IO ()+keyUpdate ctx getState setState = do+    (usedHash, usedCipher, level, applicationSecretN) <- getState ctx+    unless (level == CryptApplicationSecret) $+        throwCore $+            Error_Protocol+                "tried key update without application traffic secret"+                InternalError+    let applicationSecretN1 =+            hkdfExpandLabel usedHash applicationSecretN "traffic upd" "" $+                hashDigestSize usedHash+    setState ctx usedHash usedCipher (AnyTrafficSecret applicationSecretN1)++-- | How to update keys in TLS 1.3+data KeyUpdateRequest+    = -- | Unidirectional key update+      OneWay+    | -- | Bidirectional key update (normal case)+      TwoWay+    deriving (Eq, Show)++-- | Updating application traffic secrets for TLS 1.3.+--   If this API is called for TLS 1.3, 'True' is returned.+--   Otherwise, 'False' is returned.+updateKey :: MonadIO m => Context -> KeyUpdateRequest -> m Bool+updateKey ctx way = liftIO $ do+    tls13 <- tls13orLater ctx+    when tls13 $ do+        let req = case way of+                OneWay -> UpdateNotRequested+                TwoWay -> UpdateRequested+        -- Write lock wraps both actions because we don't want another packet to+        -- be sent by another thread before the Tx state is updated.+        withWriteLock ctx $ do+            sendPacket13 ctx $ Handshake13 [KeyUpdate13 req] []+            keyUpdate ctx getTxRecordState setTxRecordState+    return tls13
Network/TLS/Handshake/Signature.hs view
@@ -1,71 +1,141 @@ {-# LANGUAGE OverloadedStrings #-}--- |--- Module      : Network.TLS.Handshake.Signature--- License     : BSD-style--- Maintainer  : Vincent Hanquez <vincent@snarc.org>--- Stability   : experimental--- Portability : unknown----module Network.TLS.Handshake.Signature-    (-      createCertificateVerify-    , checkCertificateVerify-    , digitallySignDHParams-    , digitallySignECDHParams-    , digitallySignDHParamsVerify-    , digitallySignECDHParamsVerify-    , signatureCompatible-    ) where -import Network.TLS.Crypto+module Network.TLS.Handshake.Signature (+    createCertificateVerify,+    checkCertificateVerify,+    digitallySignDHParams,+    digitallySignECDHParams,+    digitallySignDHParamsVerify,+    digitallySignECDHParamsVerify,+    checkSupportedHashSignature,+    certificateCompatible,+    signatureCompatible,+    signatureCompatible13,+    hashSigToCertType,+    signatureParams,+    decryptError,+) where++import Control.Monad.State.Strict+ import Network.TLS.Context.Internal-import Network.TLS.Struct+import Network.TLS.Crypto+import Network.TLS.Handshake.Key+import Network.TLS.Handshake.State import Network.TLS.Imports-import Network.TLS.Packet (generateCertificateVerify_SSL, generateCertificateVerify_SSL_DSS,-                           encodeSignedDHParams, encodeSignedECDHParams)+import Network.TLS.Packet (+    encodeSignedDHParams,+    encodeSignedECDHParams,+ )+import Network.TLS.Parameters import Network.TLS.State-import Network.TLS.Handshake.State-import Network.TLS.Handshake.Key-import Network.TLS.Util+import Network.TLS.Struct+import Network.TLS.X509 -import Control.Monad.State.Strict+decryptError :: MonadIO m => String -> m a+decryptError msg = throwCore $ Error_Protocol msg DecryptError -signatureCompatible :: DigitalSignatureAlg -> HashAndSignatureAlgorithm -> Bool-signatureCompatible RSA   (_, SignatureRSA)          = True-signatureCompatible RSA   (_, SignatureRSApssSHA256) = True-signatureCompatible RSA   (_, SignatureRSApssSHA384) = True-signatureCompatible RSA   (_, SignatureRSApssSHA512) = True-signatureCompatible DSS   (_, SignatureDSS)          = True-signatureCompatible ECDSA (_, SignatureECDSA)        = True-signatureCompatible _     (_, _)                     = False+-- | Check that the key is compatible with a list of 'CertificateType' values.+-- Ed25519 and Ed448 have no assigned code point and are checked with extension+-- "signature_algorithms" only.+certificateCompatible :: PubKey -> [CertificateType] -> Bool+certificateCompatible (PubKeyRSA _) cTypes = CertificateType_RSA_Sign `elem` cTypes+certificateCompatible (PubKeyDSA _) cTypes = CertificateType_DSA_Sign `elem` cTypes+certificateCompatible (PubKeyEC _) cTypes = CertificateType_ECDSA_Sign `elem` cTypes+certificateCompatible (PubKeyEd25519 _) _ = True+certificateCompatible (PubKeyEd448 _) _ = True+certificateCompatible _ _ = False -checkCertificateVerify :: Context-                       -> Version-                       -> DigitalSignatureAlg-                       -> ByteString-                       -> DigitallySigned-                       -> IO Bool-checkCertificateVerify ctx usedVersion sigAlgExpected msgs digSig@(DigitallySigned hashSigAlg _) =-    case (usedVersion, hashSigAlg) of-        (TLS12, Nothing)    -> return False-        (TLS12, Just hs) | sigAlgExpected `signatureCompatible` hs -> doVerify-                         | otherwise                               -> return False-        (_,     Nothing)    -> doVerify-        (_,     Just _)     -> return False+signatureCompatible :: PubKey -> HashAndSignatureAlgorithm -> Bool+signatureCompatible (PubKeyRSA pk) (HashSHA1, SignatureRSA) = kxCanUseRSApkcs1 pk SHA1+signatureCompatible (PubKeyRSA pk) (HashSHA256, SignatureRSA) = kxCanUseRSApkcs1 pk SHA256+signatureCompatible (PubKeyRSA pk) (HashSHA384, SignatureRSA) = kxCanUseRSApkcs1 pk SHA384+signatureCompatible (PubKeyRSA pk) (HashSHA512, SignatureRSA) = kxCanUseRSApkcs1 pk SHA512+signatureCompatible (PubKeyRSA pk) (_, SignatureRSApssRSAeSHA256) = kxCanUseRSApss pk SHA256+signatureCompatible (PubKeyRSA pk) (_, SignatureRSApssRSAeSHA384) = kxCanUseRSApss pk SHA384+signatureCompatible (PubKeyRSA pk) (_, SignatureRSApssRSAeSHA512) = kxCanUseRSApss pk SHA512+signatureCompatible (PubKeyDSA _) (_, SignatureDSA) = True+signatureCompatible (PubKeyEC _) (_, SignatureECDSA) = True+signatureCompatible (PubKeyEd25519 _) (_, SignatureEd25519) = True+signatureCompatible (PubKeyEd448 _) (_, SignatureEd448) = True+signatureCompatible _ (_, _) = False++-- Same as 'signatureCompatible' but for TLS13: for ECDSA this also checks the+-- relation between hash in the HashAndSignatureAlgorithm and elliptic curve+signatureCompatible13 :: PubKey -> HashAndSignatureAlgorithm -> Bool+signatureCompatible13 (PubKeyEC ecPub) (h, SignatureECDSA) =+    maybe False (\g -> findEllipticCurveGroup ecPub == Just g) (hashCurve h)   where+    hashCurve HashSHA256 = Just P256+    hashCurve HashSHA384 = Just P384+    hashCurve HashSHA512 = Just P521+    hashCurve _ = Nothing+signatureCompatible13 pub hs = signatureCompatible pub hs++-- | Translate a 'HashAndSignatureAlgorithm' to an acceptable 'CertificateType'.+-- Perhaps this needs to take supported groups into account, so that, for+-- example, if we don't support any shared ECDSA groups with the server, we+-- return 'Nothing' rather than 'CertificateType_ECDSA_Sign'.+--+-- Therefore, this interface is preliminary.  It gets us moving in the right+-- direction.  The interplay between all the various TLS extensions and+-- certificate selection is rather complex.+--+-- The goal is to ensure that the client certificate request callback only sees+-- 'CertificateType' values that are supported by the library and also+-- compatible with the server signature algorithms extension.+--+-- Since we don't yet support ECDSA private keys, the caller will use+-- 'lastSupportedCertificateType' to filter those out for now, leaving just+-- @RSA@ as the only supported client certificate algorithm for TLS 1.3.+--+-- FIXME: Add RSA_PSS_PSS signatures when supported.+hashSigToCertType :: HashAndSignatureAlgorithm -> Maybe CertificateType+--+hashSigToCertType (_, SignatureRSA) = Just CertificateType_RSA_Sign+--+hashSigToCertType (_, SignatureDSA) = Just CertificateType_DSA_Sign+--+hashSigToCertType (_, SignatureECDSA) = Just CertificateType_ECDSA_Sign+--+hashSigToCertType (HashIntrinsic, SignatureRSApssRSAeSHA256) =+    Just CertificateType_RSA_Sign+hashSigToCertType (HashIntrinsic, SignatureRSApssRSAeSHA384) =+    Just CertificateType_RSA_Sign+hashSigToCertType (HashIntrinsic, SignatureRSApssRSAeSHA512) =+    Just CertificateType_RSA_Sign+hashSigToCertType (HashIntrinsic, SignatureEd25519) =+    Just CertificateType_Ed25519_Sign+hashSigToCertType (HashIntrinsic, SignatureEd448) =+    Just CertificateType_Ed448_Sign+--+hashSigToCertType _ = Nothing++checkCertificateVerify+    :: Context+    -> Version+    -> PubKey+    -> ByteString+    -> DigitallySigned+    -> IO Bool+checkCertificateVerify ctx usedVersion pubKey msgs digSig@(DigitallySigned hashSigAlg _)+    | pubKey `signatureCompatible` hashSigAlg = doVerify+    | otherwise = return False+  where     doVerify =-        prepareCertificateVerifySignatureData ctx usedVersion sigAlgExpected hashSigAlg msgs >>=-        signatureVerifyWithCertVerifyData ctx digSig+        prepareCertificateVerifySignatureData ctx usedVersion pubKey hashSigAlg msgs+            >>= signatureVerifyWithCertVerifyData ctx digSig -createCertificateVerify :: Context-                        -> Version-                        -> DigitalSignatureAlg-                        -> Maybe HashAndSignatureAlgorithm -- TLS12 only-                        -> ByteString-                        -> IO DigitallySigned-createCertificateVerify ctx usedVersion sigAlg hashSigAlg msgs =-    prepareCertificateVerifySignatureData ctx usedVersion sigAlg hashSigAlg msgs >>=-    signatureCreateWithCertVerifyData ctx hashSigAlg+createCertificateVerify+    :: Context+    -> Version+    -> PubKey+    -> HashAndSignatureAlgorithm -- TLS12 only+    -> ByteString+    -> IO DigitallySigned+createCertificateVerify ctx usedVersion pubKey hashSigAlg msgs =+    prepareCertificateVerifySignatureData ctx usedVersion pubKey hashSigAlg msgs+        >>= signatureCreateWithCertVerifyData ctx hashSigAlg  type CertVerifyData = (SignatureParams, ByteString) @@ -73,128 +143,163 @@ -- the SHA1_MD5 algorithm expect an already digested data buildVerifyData :: SignatureParams -> ByteString -> CertVerifyData buildVerifyData (RSAParams SHA1_MD5 enc) bs = (RSAParams SHA1_MD5 enc, hashFinal $ hashUpdate (hashInit SHA1_MD5) bs)-buildVerifyData sigParam             bs = (sigParam, bs)+buildVerifyData sigParam bs = (sigParam, bs) -prepareCertificateVerifySignatureData :: Context-                                      -> Version-                                      -> DigitalSignatureAlg-                                      -> Maybe HashAndSignatureAlgorithm -- TLS12 only-                                      -> ByteString-                                      -> IO CertVerifyData-prepareCertificateVerifySignatureData ctx usedVersion sigAlg hashSigAlg msgs-    | usedVersion == SSL3 = do-        (hashCtx, params, generateCV_SSL) <--            case sigAlg of-                RSA -> return (hashInit SHA1_MD5, RSAParams SHA1_MD5 RSApkcs1, generateCertificateVerify_SSL)-                DSS -> return (hashInit SHA1, DSSParams, generateCertificateVerify_SSL_DSS)-                _   -> throwCore $ Error_Misc ("unsupported CertificateVerify signature for SSL3: " ++ show sigAlg)-        Just masterSecret <- usingHState ctx $ gets hstMasterSecret-        return (params, generateCV_SSL masterSecret $ hashUpdate hashCtx msgs)-    | usedVersion == TLS10 || usedVersion == TLS11 =-            return $ buildVerifyData (signatureParams sigAlg Nothing) msgs-    | otherwise = return (signatureParams sigAlg hashSigAlg, msgs)+prepareCertificateVerifySignatureData+    :: Context+    -> Version+    -> PubKey+    -> HashAndSignatureAlgorithm -- TLS12 only+    -> ByteString+    -> IO CertVerifyData+prepareCertificateVerifySignatureData _ctx _usedVersion pubKey hashSigAlg msgs =+    return (signatureParams pubKey hashSigAlg, msgs) -signatureParams :: DigitalSignatureAlg -> Maybe HashAndSignatureAlgorithm -> SignatureParams-signatureParams RSA hashSigAlg =+signatureParams :: PubKey -> HashAndSignatureAlgorithm -> SignatureParams+signatureParams (PubKeyRSA _) hashSigAlg =     case hashSigAlg of-        Just (HashSHA512, SignatureRSA) -> RSAParams SHA512   RSApkcs1-        Just (HashSHA384, SignatureRSA) -> RSAParams SHA384   RSApkcs1-        Just (HashSHA256, SignatureRSA) -> RSAParams SHA256   RSApkcs1-        Just (HashSHA1  , SignatureRSA) -> RSAParams SHA1     RSApkcs1-        Just (HashIntrinsic , SignatureRSApssSHA512) -> RSAParams SHA512 RSApss-        Just (HashIntrinsic , SignatureRSApssSHA384) -> RSAParams SHA384 RSApss-        Just (HashIntrinsic , SignatureRSApssSHA256) -> RSAParams SHA256 RSApss-        Nothing                         -> RSAParams SHA1_MD5 RSApkcs1-        Just (hsh       , SignatureRSA) -> error ("unimplemented RSA signature hash type: " ++ show hsh)-        Just (_         , sigAlg)       -> error ("signature algorithm is incompatible with RSA: " ++ show sigAlg)-signatureParams DSS hashSigAlg =+        (HashSHA512, SignatureRSA) -> RSAParams SHA512 RSApkcs1+        (HashSHA384, SignatureRSA) -> RSAParams SHA384 RSApkcs1+        (HashSHA256, SignatureRSA) -> RSAParams SHA256 RSApkcs1+        (HashSHA1, SignatureRSA) -> RSAParams SHA1 RSApkcs1+        (HashIntrinsic, SignatureRSApssRSAeSHA512) -> RSAParams SHA512 RSApss+        (HashIntrinsic, SignatureRSApssRSAeSHA384) -> RSAParams SHA384 RSApss+        (HashIntrinsic, SignatureRSApssRSAeSHA256) -> RSAParams SHA256 RSApss+        (hsh, SignatureRSA) -> error ("unimplemented RSA signature hash type: " ++ show hsh)+        (_, sigAlg) ->+            error ("signature algorithm is incompatible with RSA: " ++ show sigAlg)+signatureParams (PubKeyDSA _) hashSigAlg =     case hashSigAlg of-        Nothing                       -> DSSParams-        Just (HashSHA1, SignatureDSS) -> DSSParams-        Just (_       , SignatureDSS) -> error "invalid DSA hash choice, only SHA1 allowed"-        Just (_       , sigAlg)       -> error ("signature algorithm is incompatible with DSS: " ++ show sigAlg)-signatureParams ECDSA hashSigAlg =+        (HashSHA1, SignatureDSA) -> DSAParams+        (_, SignatureDSA) -> error "invalid DSA hash choice, only SHA1 allowed"+        (_, sigAlg) ->+            error ("signature algorithm is incompatible with DSA: " ++ show sigAlg)+signatureParams (PubKeyEC _) hashSigAlg =     case hashSigAlg of-        Just (HashSHA512, SignatureECDSA) -> ECDSAParams SHA512-        Just (HashSHA384, SignatureECDSA) -> ECDSAParams SHA384-        Just (HashSHA256, SignatureECDSA) -> ECDSAParams SHA256-        Just (HashSHA1  , SignatureECDSA) -> ECDSAParams SHA1-        Nothing                           -> ECDSAParams SHA1-        Just (hsh       , SignatureECDSA) -> error ("unimplemented ECDSA signature hash type: " ++ show hsh)-        Just (_         , sigAlg)         -> error ("signature algorithm is incompatible with ECDSA: " ++ show sigAlg)-signatureParams sig _ = error ("unimplemented signature type: " ++ show sig)+        (HashSHA512, SignatureECDSA) -> ECDSAParams SHA512+        (HashSHA384, SignatureECDSA) -> ECDSAParams SHA384+        (HashSHA256, SignatureECDSA) -> ECDSAParams SHA256+        (HashSHA1, SignatureECDSA) -> ECDSAParams SHA1+        (hsh, SignatureECDSA) -> error ("unimplemented ECDSA signature hash type: " ++ show hsh)+        (_, sigAlg) ->+            error ("signature algorithm is incompatible with ECDSA: " ++ show sigAlg)+signatureParams (PubKeyEd25519 _) hashSigAlg =+    case hashSigAlg of+        (HashIntrinsic, SignatureEd25519) -> Ed25519Params+        (hsh, SignatureEd25519) -> error ("unimplemented Ed25519 signature hash type: " ++ show hsh)+        (_, sigAlg) ->+            error ("signature algorithm is incompatible with Ed25519: " ++ show sigAlg)+signatureParams (PubKeyEd448 _) hashSigAlg =+    case hashSigAlg of+        (HashIntrinsic, SignatureEd448) -> Ed448Params+        (hsh, SignatureEd448) -> error ("unimplemented Ed448 signature hash type: " ++ show hsh)+        (_, sigAlg) ->+            error ("signature algorithm is incompatible with Ed448: " ++ show sigAlg)+signatureParams pk _ = error ("signatureParams: " ++ pubkeyType pk ++ " is not supported") -signatureCreateWithCertVerifyData :: Context-                                  -> Maybe HashAndSignatureAlgorithm-                                  -> CertVerifyData-                                  -> IO DigitallySigned+signatureCreateWithCertVerifyData+    :: Context+    -> HashAndSignatureAlgorithm+    -> CertVerifyData+    -> IO DigitallySigned signatureCreateWithCertVerifyData ctx malg (sigParam, toSign) = do-    cc <- usingState_ ctx isClientContext-    DigitallySigned malg <$> signPrivate ctx cc sigParam toSign+    role <- usingState_ ctx getRole+    DigitallySigned malg <$> signPrivate ctx role sigParam toSign -signatureVerify :: Context -> DigitallySigned -> DigitalSignatureAlg -> ByteString -> IO Bool-signatureVerify ctx digSig@(DigitallySigned hashSigAlg _) sigAlgExpected toVerifyData = do+signatureVerify :: Context -> DigitallySigned -> PubKey -> ByteString -> IO Bool+signatureVerify ctx digSig@(DigitallySigned hashSigAlg _) pubKey toVerifyData = do     usedVersion <- usingState_ ctx getVersion     let (sigParam, toVerify) =             case (usedVersion, hashSigAlg) of-                (TLS12, Nothing)    -> error "expecting hash and signature algorithm in a TLS12 digitally signed structure"-                (TLS12, Just hs) | sigAlgExpected `signatureCompatible` hs -> (signatureParams sigAlgExpected hashSigAlg, toVerifyData)-                                 | otherwise                               -> error "expecting different signature algorithm"-                (_,     Nothing)    -> buildVerifyData (signatureParams sigAlgExpected Nothing) toVerifyData-                (_,     Just _)     -> error "not expecting hash and signature algorithm in a < TLS12 digitially signed structure"+                (TLS12, hs)+                    | pubKey `signatureCompatible` hs ->+                        (signatureParams pubKey hashSigAlg, toVerifyData)+                    | otherwise ->+                        error "expecting different signature algorithm"+                _ ->+                    error+                        "not expecting hash and signature algorithm in a < TLS12 digitially signed structure"     signatureVerifyWithCertVerifyData ctx digSig (sigParam, toVerify) -signatureVerifyWithCertVerifyData :: Context-                                  -> DigitallySigned-                                  -> CertVerifyData-                                  -> IO Bool-signatureVerifyWithCertVerifyData ctx (DigitallySigned _ bs) (sigParam, toVerify) = do-    cc <- usingState_ ctx isClientContext-    verifyPublic ctx cc sigParam toVerify bs+signatureVerifyWithCertVerifyData+    :: Context+    -> DigitallySigned+    -> CertVerifyData+    -> IO Bool+signatureVerifyWithCertVerifyData ctx (DigitallySigned hs bs) (sigParam, toVerify) = do+    checkSupportedHashSignature ctx hs+    verifyPublic ctx sigParam toVerify bs -digitallySignParams :: Context -> ByteString -> DigitalSignatureAlg -> Maybe HashAndSignatureAlgorithm -> IO DigitallySigned-digitallySignParams ctx signatureData sigAlg hashSigAlg =-    let sigParam = signatureParams sigAlg hashSigAlg-     in signatureCreateWithCertVerifyData ctx hashSigAlg (buildVerifyData sigParam signatureData)+digitallySignParams+    :: Context+    -> ByteString+    -> PubKey+    -> HashAndSignatureAlgorithm+    -> IO DigitallySigned+digitallySignParams ctx signatureData pubKey hashSigAlg =+    let sigParam = signatureParams pubKey hashSigAlg+     in signatureCreateWithCertVerifyData+            ctx+            hashSigAlg+            (buildVerifyData sigParam signatureData) -digitallySignDHParams :: Context-                      -> ServerDHParams-                      -> DigitalSignatureAlg-                      -> Maybe HashAndSignatureAlgorithm -- TLS12 only-                      -> IO DigitallySigned-digitallySignDHParams ctx serverParams sigAlg mhash = do-    dhParamsData <- withClientAndServerRandom ctx $ encodeSignedDHParams serverParams-    digitallySignParams ctx dhParamsData sigAlg mhash+digitallySignDHParams+    :: Context+    -> ServerDHParams+    -> PubKey+    -> HashAndSignatureAlgorithm -- TLS12 only+    -> IO DigitallySigned+digitallySignDHParams ctx serverParams pubKey mhash = do+    dhParamsData <-+        withClientAndServerRandom ctx $ encodeSignedDHParams serverParams+    digitallySignParams ctx dhParamsData pubKey mhash -digitallySignECDHParams :: Context-                        -> ServerECDHParams-                        -> DigitalSignatureAlg-                        -> Maybe HashAndSignatureAlgorithm -- TLS12 only-                        -> IO DigitallySigned-digitallySignECDHParams ctx serverParams sigAlg mhash = do-    ecdhParamsData <- withClientAndServerRandom ctx $ encodeSignedECDHParams serverParams-    digitallySignParams ctx ecdhParamsData sigAlg mhash+digitallySignECDHParams+    :: Context+    -> ServerECDHParams+    -> PubKey+    -> HashAndSignatureAlgorithm -- TLS12 only+    -> IO DigitallySigned+digitallySignECDHParams ctx serverParams pubKey mhash = do+    ecdhParamsData <-+        withClientAndServerRandom ctx $ encodeSignedECDHParams serverParams+    digitallySignParams ctx ecdhParamsData pubKey mhash -digitallySignDHParamsVerify :: Context-                            -> ServerDHParams-                            -> DigitalSignatureAlg-                            -> DigitallySigned-                            -> IO Bool-digitallySignDHParamsVerify ctx dhparams sigAlg signature = do+digitallySignDHParamsVerify+    :: Context+    -> ServerDHParams+    -> PubKey+    -> DigitallySigned+    -> IO Bool+digitallySignDHParamsVerify ctx dhparams pubKey signature = do     expectedData <- withClientAndServerRandom ctx $ encodeSignedDHParams dhparams-    signatureVerify ctx signature sigAlg expectedData+    signatureVerify ctx signature pubKey expectedData -digitallySignECDHParamsVerify :: Context-                              -> ServerECDHParams-                              -> DigitalSignatureAlg-                              -> DigitallySigned-                              -> IO Bool-digitallySignECDHParamsVerify ctx dhparams sigAlg signature = do+digitallySignECDHParamsVerify+    :: Context+    -> ServerECDHParams+    -> PubKey+    -> DigitallySigned+    -> IO Bool+digitallySignECDHParamsVerify ctx dhparams pubKey signature = do     expectedData <- withClientAndServerRandom ctx $ encodeSignedECDHParams dhparams-    signatureVerify ctx signature sigAlg expectedData+    signatureVerify ctx signature pubKey expectedData -withClientAndServerRandom :: Context -> (ClientRandom -> ServerRandom -> b) -> IO b+withClientAndServerRandom+    :: Context -> (ClientRandom -> ServerRandom -> b) -> IO b withClientAndServerRandom ctx f = do-    (cran, sran) <- usingHState ctx $ (,) <$> gets hstClientRandom-                                          <*> (fromJust "withClientAndServer : server random" <$> gets hstServerRandom)+    (cran, sran) <-+        usingHState ctx $+            (,)+                <$> gets hstClientRandom+                <*> (fromJust <$> gets hstServerRandom)     return $ f cran sran++-- verify that the hash and signature selected by the peer is supported in+-- the local configuration+checkSupportedHashSignature+    :: Context -> HashAndSignatureAlgorithm -> IO ()+checkSupportedHashSignature ctx hs =+    unless (hs `elem` supportedHashSignatures (ctxSupported ctx)) $+        let msg = "unsupported hash and signature algorithm: " ++ show hs+         in throwCore $ Error_Protocol msg IllegalParameter
Network/TLS/Handshake/State.hs view
@@ -1,310 +1,569 @@ {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE CPP #-}--- |--- Module      : Network.TLS.Handshake.State--- License     : BSD-style--- Maintainer  : Vincent Hanquez <vincent@snarc.org>--- Stability   : experimental--- Portability : unknown----module Network.TLS.Handshake.State-    ( HandshakeState(..)-    , ClientCertRequestData-    , HandshakeM-    , newEmptyHandshake-    , runHandshake+{-# LANGUAGE OverloadedStrings #-}++module Network.TLS.Handshake.State (+    HandshakeState (..),+    TransHashState (..),+    HandshakeMode13 (..),+    RTT0Status (..),+    CertReqCBdata,+    HandshakeM,+    newEmptyHandshake,+    runHandshake,+     -- * key accessors-    , setPublicKey-    , setPrivateKey-    , getLocalPrivateKey-    , getRemotePublicKey-    , setServerDHParams-    , getServerDHParams-    , setServerECDHParams-    , getServerECDHParams-    , setDHPrivate-    , getDHPrivate-    , setECDHPrivate-    , getECDHPrivate+    setPublicKey,+    setPublicPrivateKeys,+    getLocalPublicPrivateKeys,+    getRemotePublicKey,+    setServerDHParams,+    getServerDHParams,+    setServerECDHParams,+    getServerECDHParams,+    setDHPrivate,+    getDHPrivate,+    setGroupPrivate,+    getGroupPrivate,+     -- * cert accessors-    , setClientCertSent-    , getClientCertSent-    , setCertReqSent-    , getCertReqSent-    , setClientCertChain-    , getClientCertChain-    , setClientCertRequest-    , getClientCertRequest+    setClientRandom,+    getClientRandom,+    setOuterClientRandom,+    getOuterClientRandom,+    setClientHello,+    getClientHello,+    setECHEE,+    getECHEE,+    setECHAccepted,+    getECHAccepted,+    setClientCertSent,+    getClientCertSent,+    setCertReqSent,+    getCertReqSent,+    setClientCertChain,+    getClientCertChain,+    setCertReqToken,+    getCertReqToken,+    setCertReqCBdata,+    getCertReqCBdata,+    setCertReqSigAlgsCert,+    getCertReqSigAlgsCert,+     -- * digest accessors-    , addHandshakeMessage-    , updateHandshakeDigest-    , getHandshakeMessages-    , getHandshakeDigest-    -- * master secret-    , setMasterSecret-    , setMasterSecretFromPre+    addHandshakeMessage,+    getHandshakeMessages,++    -- * main secret+    setMainSecret,+    setMainSecretFromPre,+     -- * misc accessor-    , getPendingCipher-    , setServerHelloParameters-    ) where+    getPendingCipher,+    setExtendedMainSecret,+    getExtendedMainSecret,+    setSupportedGroup,+    getSupportedGroup,+    setTLS13HandshakeMode,+    getTLS13HandshakeMode,+    setTLS13RTT0Status,+    getTLS13RTT0Status,+    setTLS13EarlySecret,+    getTLS13EarlySecret,+    setTLS13ResumptionSecret,+    getTLS13ResumptionSecret,+    setTLS13CertComp,+    getTLS13CertComp,+    setCCS13Sent,+    getCCS13Sent,+    setCCS13Recv,+    getCCS13Recv,+) where -import Network.TLS.Util-import Network.TLS.Struct-import Network.TLS.Record.State-import Network.TLS.Packet-import Network.TLS.Crypto+import Control.Monad.State.Strict+import Data.ByteArray (convert)+import Data.X509 (CertificateChain)+ import Network.TLS.Cipher import Network.TLS.Compression-import Network.TLS.Types+import Network.TLS.Crypto import Network.TLS.Imports-import Control.Monad.State.Strict-import Data.X509 (CertificateChain)-import Data.ByteArray (ByteArrayAccess)+import Network.TLS.Packet+import Network.TLS.Record.State+import Network.TLS.Struct+import Network.TLS.Types+import Network.TLS.Util  data HandshakeKeyState = HandshakeKeyState-    { hksRemotePublicKey :: !(Maybe PubKey)-    , hksLocalPrivateKey :: !(Maybe PrivKey)-    } deriving (Show)+    { hksRemotePublicKey :: Maybe PubKey+    , hksLocalPublicPrivateKeys :: Maybe (PubKey, PrivKey)+    }+    deriving (Show) +data TransHashState+    = -- | Initial state+      TransHashState0+    | -- | A raw CH is stored since hash algo is not chosen yet.+      TransHashState1 [ByteString]+    | -- | Hashed+      TransHashState2 HashCtx++{- FOURMOLU_DISABLE -}+instance Show TransHashState where+    show  TransHashState0       = "State0 "+    show (TransHashState1 _)    = "State1 CH"+    show (TransHashState2 hctx) = showBytesHex $ hashFinal hctx+{- FOURMOLU_ENABLE -}+ data HandshakeState = HandshakeState-    { hstClientVersion       :: !Version-    , hstClientRandom        :: !ClientRandom-    , hstServerRandom        :: !(Maybe ServerRandom)-    , hstMasterSecret        :: !(Maybe ByteString)-    , hstKeyState            :: !HandshakeKeyState-    , hstServerDHParams      :: !(Maybe ServerDHParams)-    , hstDHPrivate           :: !(Maybe DHPrivate)-    , hstServerECDHParams    :: !(Maybe ServerECDHParams)-    , hstECDHPrivate         :: !(Maybe GroupPrivate)-    , hstHandshakeDigest     :: !(Either [ByteString] HashCtx)-    , hstHandshakeMessages   :: [ByteString]-    , hstClientCertRequest   :: !(Maybe ClientCertRequestData) -- ^ Set to Just-value when certificate request was received-    , hstClientCertSent      :: !Bool -- ^ Set to true when a client certificate chain was sent-    , hstCertReqSent         :: !Bool -- ^ Set to true when a certificate request was sent-    , hstClientCertChain     :: !(Maybe CertificateChain)-    , hstPendingTxState      :: Maybe RecordState-    , hstPendingRxState      :: Maybe RecordState-    , hstPendingCipher       :: Maybe Cipher-    , hstPendingCompression  :: Compression-    } deriving (Show)+    { hstClientVersion :: Version+    , hstClientRandom :: ClientRandom+    -- ^ For ECH, inner client random.+    , hstServerRandom :: Maybe ServerRandom+    , hstMainSecret :: Maybe Secret+    , hstKeyState :: HandshakeKeyState+    , hstServerDHParams :: Maybe ServerDHParams+    , hstDHPrivate :: Maybe DHPrivate+    , hstServerECDHParams :: Maybe ServerECDHParams+    , hstGroupPrivate :: [(Group, GroupPrivate)]+    , hstTransHashState :: TransHashState+    , hstTransHashStateI :: TransHashState -- Inner CH for client ECH+    , hstHandshakeMessages :: [ByteString]+    -- ^ To create certificate verify for TLS 1.2.+    --   This should be removed when TLS 1.2 is dropped.+    , hstCertReqToken :: Maybe ByteString+    -- ^ Set to Just-value when a TLS13 certificate request is received+    , hstCertReqCBdata :: Maybe CertReqCBdata+    -- ^ Set to Just-value when a certificate request is received+    , hstCertReqSigAlgsCert :: Maybe [HashAndSignatureAlgorithm]+    -- ^ In TLS 1.3, these are separate from the certificate+    -- issuer signature algorithm hints in the callback data.+    -- In TLS 1.2 the same list is overloaded for both purposes.+    -- Not present in TLS 1.1 and earlier+    , hstClientCertSent :: Bool+    -- ^ Set to true when a client certificate chain was sent+    , hstCertReqSent :: Bool+    -- ^ Set to true when a certificate request was sent.  This applies+    -- only to requests sent during handshake (not post-handshake).+    , hstClientCertChain :: Maybe CertificateChain+    , hstPendingTxState :: Maybe RecordState+    , hstPendingRxState :: Maybe RecordState+    , hstPendingCipher :: Maybe Cipher+    , hstPendingCompression :: Compression+    , hstExtendedMainSecret :: Bool+    , hstSupportedGroup :: Maybe Group+    , hstTLS13HandshakeMode :: HandshakeMode13+    , hstTLS13RTT0Status :: RTT0Status+    , hstTLS13EarlySecret :: Maybe (BaseSecret EarlySecret) -- xxx+    , hstTLS13ResumptionSecret :: Maybe (BaseSecret ResumptionSecret)+    , hstTLS13CertComp :: Bool+    , hstCCS13Sent :: Bool+    , hstCCS13Recv :: Bool+    , hstTLS13OuterClientRandom :: Maybe ClientRandom+    -- ^ Used for key logging in the case of ECH.+    , hstTLS13ClientHello :: Maybe (ClientHello, [ByteString])+    -- ^ Inner client hello in the case of ECH.+    , hstTLS13ECHAccepted :: Bool+    , hstTLS13ECHEE :: Bool+    }+    deriving (Show) -type ClientCertRequestData = ([CertificateType],-                              Maybe [(HashAlgorithm, SignatureAlgorithm)],-                              [DistinguishedName])+-- | When we receive a CertificateRequest from a server, a just-in-time+--    callback is issued to the application to obtain a suitable certificate.+--    Somewhat unfortunately, the callback parameters don't abstract away the+--    details of the TLS 1.2 Certificate Request message, which combines the+--    legacy @certificate_types@ and new @supported_signature_algorithms@+--    parameters is a rather subtle way.+--+--    TLS 1.2 also (again unfortunately, in the opinion of the author of this+--    comment) overloads the signature algorithms parameter to constrain not only+--    the algorithms used in TLS, but also the algorithms used by issuing CAs in+--    the X.509 chain.  Best practice is to NOT treat such that restriction as a+--    MUST, but rather take it as merely a preference, when a choice exists.  If+--    the best chain available does not match the provided signature algorithm+--    list, go ahead and use it anyway, it will probably work, and the server may+--    not even care about the issuer CAs at all, it may be doing DANE or have+--    explicit mappings for the client's public key, ...+--+--    The TLS 1.3 @CertificateRequest@ message, drops @certificate_types@ and no+--    longer overloads @supported_signature_algorithms@ to cover X.509.  It also+--    includes a new opaque context token that the client must echo back, which+--    makes certain client authentication replay attacks more difficult.  We will+--    store that context separately, it does not need to be presented in the user+--    callback.  The certificate signature algorithms preferred by the peer are+--    now in the separate @signature_algorithms_cert@ extension, but we cannot+--    report these to the application callback without an API change.  The good+--    news is that filtering the X.509 signature types is generally unnecessary,+--    unwise and difficult.  So we just ignore this extension.+--+--    As a result, the information we provide to the callback is no longer a+--    verbatim copy of the certificate request payload.  In the case of TLS 1.3+--    The 'CertificateType' list is synthetically generated from the server's+--    @signature_algorithms@ extension, and the @signature_algorithms_certs@+--    extension is ignored.+--+--    Since the original TLS 1.2 'CertificateType' has no provision for the newer+--    certificate types that have appeared in TLS 1.3 we're adding some synthetic+--    values that have no equivalent values in the TLS 1.2 'CertificateType' as+--    defined in the IANA+--    <https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-2+--    TLS ClientCertificateType Identifiers> registry.  These values are inferred+--    from the TLS 1.3 @signature_algorithms@ extension, and will allow clients to+--    present Ed25519 and Ed448 certificates when these become supported.+type CertReqCBdata =+    ( [CertificateType]+    , Maybe [HashAndSignatureAlgorithm]+    , [DistinguishedName]+    ) -newtype HandshakeM a = HandshakeM { runHandshakeM :: State HandshakeState a }+newtype HandshakeM a = HandshakeM {runHandshakeM :: State HandshakeState a}     deriving (Functor, Applicative, Monad)  instance MonadState HandshakeState HandshakeM where     put x = HandshakeM (put x)-    get   = HandshakeM get-#if MIN_VERSION_mtl(2,1,0)+    get = HandshakeM get     state f = HandshakeM (state f)-#endif  -- create a new empty handshake state newEmptyHandshake :: Version -> ClientRandom -> HandshakeState-newEmptyHandshake ver crand = HandshakeState-    { hstClientVersion       = ver-    , hstClientRandom        = crand-    , hstServerRandom        = Nothing-    , hstMasterSecret        = Nothing-    , hstKeyState            = HandshakeKeyState Nothing Nothing-    , hstServerDHParams      = Nothing-    , hstDHPrivate           = Nothing-    , hstServerECDHParams    = Nothing-    , hstECDHPrivate         = Nothing-    , hstHandshakeDigest     = Left []-    , hstHandshakeMessages   = []-    , hstClientCertRequest   = Nothing-    , hstClientCertSent      = False-    , hstCertReqSent         = False-    , hstClientCertChain     = Nothing-    , hstPendingTxState      = Nothing-    , hstPendingRxState      = Nothing-    , hstPendingCipher       = Nothing-    , hstPendingCompression  = nullCompression-    }+newEmptyHandshake ver crand =+    HandshakeState+        { hstClientVersion = ver+        , hstClientRandom = crand+        , hstServerRandom = Nothing+        , hstMainSecret = Nothing+        , hstKeyState = HandshakeKeyState Nothing Nothing+        , hstServerDHParams = Nothing+        , hstDHPrivate = Nothing+        , hstServerECDHParams = Nothing+        , hstGroupPrivate = []+        , hstTransHashState = TransHashState0+        , hstTransHashStateI = TransHashState0+        , hstHandshakeMessages = []+        , hstCertReqToken = Nothing+        , hstCertReqCBdata = Nothing+        , hstCertReqSigAlgsCert = Nothing+        , hstClientCertSent = False+        , hstCertReqSent = False+        , hstClientCertChain = Nothing+        , hstPendingTxState = Nothing+        , hstPendingRxState = Nothing+        , hstPendingCipher = Nothing+        , hstPendingCompression = nullCompression+        , hstExtendedMainSecret = False+        , hstSupportedGroup = Nothing+        , hstTLS13HandshakeMode = FullHandshake+        , hstTLS13RTT0Status = RTT0None+        , hstTLS13EarlySecret = Nothing+        , hstTLS13ResumptionSecret = Nothing+        , hstTLS13CertComp = False+        , hstCCS13Sent = False+        , hstCCS13Recv = False+        , hstTLS13OuterClientRandom = Nothing+        , hstTLS13ClientHello = Nothing+        , hstTLS13ECHAccepted = False+        , hstTLS13ECHEE = False+        }  runHandshake :: HandshakeState -> HandshakeM a -> (a, HandshakeState) runHandshake hst f = runState (runHandshakeM f) hst  setPublicKey :: PubKey -> HandshakeM ()-setPublicKey pk = modify (\hst -> hst { hstKeyState = setPK (hstKeyState hst) })-  where setPK hks = hks { hksRemotePublicKey = Just pk }+setPublicKey pk = modify' (\hst -> hst{hstKeyState = setPK (hstKeyState hst)})+  where+    setPK hks = hks{hksRemotePublicKey = Just pk} -setPrivateKey :: PrivKey -> HandshakeM ()-setPrivateKey pk = modify (\hst -> hst { hstKeyState = setPK (hstKeyState hst) })-  where setPK hks = hks { hksLocalPrivateKey = Just pk }+setPublicPrivateKeys :: (PubKey, PrivKey) -> HandshakeM ()+setPublicPrivateKeys keys = modify' (\hst -> hst{hstKeyState = setKeys (hstKeyState hst)})+  where+    setKeys hks = hks{hksLocalPublicPrivateKeys = Just keys}  getRemotePublicKey :: HandshakeM PubKey-getRemotePublicKey = fromJust "remote public key" <$> gets (hksRemotePublicKey . hstKeyState)+getRemotePublicKey = fromJust <$> gets (hksRemotePublicKey . hstKeyState) -getLocalPrivateKey :: HandshakeM PrivKey-getLocalPrivateKey = fromJust "local private key" <$> gets (hksLocalPrivateKey . hstKeyState)+getLocalPublicPrivateKeys :: HandshakeM (PubKey, PrivKey)+getLocalPublicPrivateKeys =+    fromJust <$> gets (hksLocalPublicPrivateKeys . hstKeyState) +setServerDHParams :: ServerDHParams -> HandshakeM ()+setServerDHParams shp = modify' (\hst -> hst{hstServerDHParams = Just shp})+ getServerDHParams :: HandshakeM ServerDHParams-getServerDHParams = fromJust "server DH params" <$> gets hstServerDHParams+getServerDHParams = fromJust <$> gets hstServerDHParams -getServerECDHParams :: HandshakeM ServerECDHParams-getServerECDHParams = fromJust "server ECDH params" <$> gets hstServerECDHParams+setServerECDHParams :: ServerECDHParams -> HandshakeM ()+setServerECDHParams shp = modify' (\hst -> hst{hstServerECDHParams = Just shp}) -setServerDHParams :: ServerDHParams -> HandshakeM ()-setServerDHParams shp = modify (\hst -> hst { hstServerDHParams = Just shp })+getServerECDHParams :: HandshakeM ServerECDHParams+getServerECDHParams = fromJust <$> gets hstServerECDHParams -setServerECDHParams :: ServerECDHParams -> HandshakeM ()-setServerECDHParams shp = modify (\hst -> hst { hstServerECDHParams = Just shp })+setDHPrivate :: DHPrivate -> HandshakeM ()+setDHPrivate shp = modify' (\hst -> hst{hstDHPrivate = Just shp})  getDHPrivate :: HandshakeM DHPrivate-getDHPrivate = fromJust "server DH private" <$> gets hstDHPrivate+getDHPrivate = fromJust <$> gets hstDHPrivate -getECDHPrivate :: HandshakeM GroupPrivate-getECDHPrivate = fromJust "server ECDH private" <$> gets hstECDHPrivate+getGroupPrivate :: HandshakeM [(Group, GroupPrivate)]+getGroupPrivate = gets hstGroupPrivate -setDHPrivate :: DHPrivate -> HandshakeM ()-setDHPrivate shp = modify (\hst -> hst { hstDHPrivate = Just shp })+setGroupPrivate :: [(Group, GroupPrivate)] -> HandshakeM ()+setGroupPrivate shp = modify' (\hst -> hst{hstGroupPrivate = shp}) -setECDHPrivate :: GroupPrivate -> HandshakeM ()-setECDHPrivate shp = modify (\hst -> hst { hstECDHPrivate = Just shp })+setExtendedMainSecret :: Bool -> HandshakeM ()+setExtendedMainSecret b = modify' (\hst -> hst{hstExtendedMainSecret = b}) +getExtendedMainSecret :: HandshakeM Bool+getExtendedMainSecret = gets hstExtendedMainSecret++setSupportedGroup :: Group -> HandshakeM ()+setSupportedGroup g = modify' (\hst -> hst{hstSupportedGroup = Just g})++getSupportedGroup :: HandshakeM (Maybe Group)+getSupportedGroup = gets hstSupportedGroup++-- | Type to show which handshake mode is used in TLS 1.3.+data HandshakeMode13+    = -- | Full handshake is used.+      FullHandshake+    | -- | Full handshake is used with hello retry request.+      HelloRetryRequest+    | -- | Server authentication is skipped.+      PreSharedKey+    | -- | Server authentication is skipped and early data is sent.+      RTT0+    deriving (Show, Eq)++setTLS13HandshakeMode :: HandshakeMode13 -> HandshakeM ()+setTLS13HandshakeMode s = modify' (\hst -> hst{hstTLS13HandshakeMode = s})++getTLS13HandshakeMode :: HandshakeM HandshakeMode13+getTLS13HandshakeMode = gets hstTLS13HandshakeMode++data RTT0Status+    = RTT0None+    | RTT0Sent+    | RTT0Accepted+    | RTT0Rejected+    deriving (Show, Eq)++setTLS13RTT0Status :: RTT0Status -> HandshakeM ()+setTLS13RTT0Status s = modify' (\hst -> hst{hstTLS13RTT0Status = s})++getTLS13RTT0Status :: HandshakeM RTT0Status+getTLS13RTT0Status = gets hstTLS13RTT0Status++setTLS13EarlySecret :: BaseSecret EarlySecret -> HandshakeM ()+setTLS13EarlySecret secret = modify' (\hst -> hst{hstTLS13EarlySecret = Just secret})++getTLS13EarlySecret :: HandshakeM (Maybe (BaseSecret EarlySecret))+getTLS13EarlySecret = gets hstTLS13EarlySecret++setTLS13ResumptionSecret :: BaseSecret ResumptionSecret -> HandshakeM ()+setTLS13ResumptionSecret secret = modify' (\hst -> hst{hstTLS13ResumptionSecret = Just secret})++getTLS13ResumptionSecret :: HandshakeM (Maybe (BaseSecret ResumptionSecret))+getTLS13ResumptionSecret = gets hstTLS13ResumptionSecret++setTLS13CertComp :: Bool -> HandshakeM ()+setTLS13CertComp comp = modify' (\hst -> hst{hstTLS13CertComp = comp})++getTLS13CertComp :: HandshakeM Bool+getTLS13CertComp = gets hstTLS13CertComp++setCCS13Sent :: Bool -> HandshakeM ()+setCCS13Sent sent = modify' (\hst -> hst{hstCCS13Sent = sent})++getCCS13Sent :: HandshakeM Bool+getCCS13Sent = gets hstCCS13Sent++setCCS13Recv :: Bool -> HandshakeM ()+setCCS13Recv sent = modify' (\hst -> hst{hstCCS13Recv = sent})++getCCS13Recv :: HandshakeM Bool+getCCS13Recv = gets hstCCS13Recv+ setCertReqSent :: Bool -> HandshakeM ()-setCertReqSent b = modify (\hst -> hst { hstCertReqSent = b })+setCertReqSent b = modify' (\hst -> hst{hstCertReqSent = b})  getCertReqSent :: HandshakeM Bool getCertReqSent = gets hstCertReqSent  setClientCertSent :: Bool -> HandshakeM ()-setClientCertSent b = modify (\hst -> hst { hstClientCertSent = b })+setClientCertSent b = modify' (\hst -> hst{hstClientCertSent = b}) +getClientRandom :: HandshakeM ClientRandom+getClientRandom = gets hstClientRandom++setClientRandom :: ClientRandom -> HandshakeM ()+setClientRandom cr = modify' $ \hst -> hst{hstClientRandom = cr}++getOuterClientRandom :: HandshakeM (Maybe ClientRandom)+getOuterClientRandom = gets hstTLS13OuterClientRandom++setOuterClientRandom :: Maybe ClientRandom -> HandshakeM ()+setOuterClientRandom mcr = modify' (\hst -> hst{hstTLS13OuterClientRandom = mcr})++getClientHello :: HandshakeM (Maybe (ClientHello, [ByteString]))+getClientHello = gets hstTLS13ClientHello++setClientHello :: ClientHello -> [ByteString] -> HandshakeM ()+setClientHello ch b = modify' $ \hst -> hst{hstTLS13ClientHello = Just (ch, b)}++getECHAccepted :: HandshakeM Bool+getECHAccepted = gets hstTLS13ECHAccepted++setECHAccepted :: Bool -> HandshakeM ()+setECHAccepted b = modify' $ \hst -> hst{hstTLS13ECHAccepted = b}++getECHEE :: HandshakeM Bool+getECHEE = gets hstTLS13ECHEE++setECHEE :: Bool -> HandshakeM ()+setECHEE b = modify' $ \hst -> hst{hstTLS13ECHEE = b}+ getClientCertSent :: HandshakeM Bool getClientCertSent = gets hstClientCertSent  setClientCertChain :: CertificateChain -> HandshakeM ()-setClientCertChain b = modify (\hst -> hst { hstClientCertChain = Just b })+setClientCertChain b = modify' (\hst -> hst{hstClientCertChain = Just b})  getClientCertChain :: HandshakeM (Maybe CertificateChain) getClientCertChain = gets hstClientCertChain -setClientCertRequest :: ClientCertRequestData -> HandshakeM ()-setClientCertRequest d = modify (\hst -> hst { hstClientCertRequest = Just d })+--+setCertReqToken :: Maybe ByteString -> HandshakeM ()+setCertReqToken token = modify' $ \hst -> hst{hstCertReqToken = token} -getClientCertRequest :: HandshakeM (Maybe ClientCertRequestData)-getClientCertRequest = gets hstClientCertRequest+getCertReqToken :: HandshakeM (Maybe ByteString)+getCertReqToken = gets hstCertReqToken +--+setCertReqCBdata :: Maybe CertReqCBdata -> HandshakeM ()+setCertReqCBdata d = modify' (\hst -> hst{hstCertReqCBdata = d})++getCertReqCBdata :: HandshakeM (Maybe CertReqCBdata)+getCertReqCBdata = gets hstCertReqCBdata++-- Dead code, until we find some use for the extension+setCertReqSigAlgsCert :: Maybe [HashAndSignatureAlgorithm] -> HandshakeM ()+setCertReqSigAlgsCert as = modify' $ \hst -> hst{hstCertReqSigAlgsCert = as}++getCertReqSigAlgsCert :: HandshakeM (Maybe [HashAndSignatureAlgorithm])+getCertReqSigAlgsCert = gets hstCertReqSigAlgsCert++-- getPendingCipher :: HandshakeM Cipher-getPendingCipher = fromJust "pending cipher" <$> gets hstPendingCipher+getPendingCipher = fromJust <$> gets hstPendingCipher  addHandshakeMessage :: ByteString -> HandshakeM ()-addHandshakeMessage content = modify $ \hs -> hs { hstHandshakeMessages = content : hstHandshakeMessages hs}+addHandshakeMessage content = modify' $ \hs -> hs{hstHandshakeMessages = content : hstHandshakeMessages hs}  getHandshakeMessages :: HandshakeM [ByteString] getHandshakeMessages = gets (reverse . hstHandshakeMessages) -updateHandshakeDigest :: ByteString -> HandshakeM ()-updateHandshakeDigest content = modify $ \hs -> hs-    { hstHandshakeDigest = case hstHandshakeDigest hs of-                                Left bytes    -> Left (content:bytes)-                                Right hashCtx -> Right $ hashUpdate hashCtx content }--getHandshakeDigest :: Version -> Role -> HandshakeM ByteString-getHandshakeDigest ver role = gets gen-  where gen hst = case hstHandshakeDigest hst of-                      Right hashCtx ->-                         let msecret = fromJust "master secret" $ hstMasterSecret hst-                             cipher  = fromJust "cipher" $ hstPendingCipher hst-                          in generateFinish ver cipher msecret hashCtx-                      Left _        ->-                         error "un-initialized handshake digest"-        generateFinish | role == ClientRole = generateClientFinished-                       | otherwise          = generateServerFinished+-- | Generate the main secret from the pre-main secret.+setMainSecretFromPre+    :: Version+    -- ^ chosen transmission version+    -> Role+    -- ^ the role (Client or Server) of the generating side+    -> Secret+    -- ^ the pre-main secret+    -> HandshakeM Secret+setMainSecretFromPre ver role preMainSecret = do+    ems <- getExtendedMainSecret+    secret <- if ems then get >>= genExtendedSecret else genSecret <$> get+    setMainSecret ver role secret+    return secret+  where+    genSecret hst =+        generateMainSecret+            ver+            (fromJust $ hstPendingCipher hst)+            preMainSecret+            (hstClientRandom hst)+            (fromJust $ hstServerRandom hst)+    genExtendedSecret hst =+        generateExtendedMainSecret+            ver+            (fromJust $ hstPendingCipher hst)+            preMainSecret+            <$> getSessionHash --- | Generate the master secret from the pre master secret.-setMasterSecretFromPre :: ByteArrayAccess preMaster-                       => Version   -- ^ chosen transmission version-                       -> Role      -- ^ the role (Client or Server) of the generating side-                       -> preMaster -- ^ the pre master secret-                       -> HandshakeM ()-setMasterSecretFromPre ver role premasterSecret = do-    secret <- genSecret <$> get-    setMasterSecret ver role secret-  where genSecret hst =-            generateMasterSecret ver (fromJust "cipher" $ hstPendingCipher hst)-                                 premasterSecret-                                 (hstClientRandom hst)-                                 (fromJust "server random" $ hstServerRandom hst)+getSessionHash :: HandshakeM ByteString+getSessionHash = gets $ \hst ->+    case hstTransHashState hst of+        TransHashState2 hashCtx -> hashFinal hashCtx+        _ -> error "un-initialized session hash" --- | Set master secret and as a side effect generate the key block+-- | Set main secret and as a side effect generate the key block -- with all the right parameters, and setup the pending tx/rx state.-setMasterSecret :: Version -> Role -> ByteString -> HandshakeM ()-setMasterSecret ver role masterSecret = modify $ \hst ->-    let (pendingTx, pendingRx) = computeKeyBlock hst masterSecret ver role-     in hst { hstMasterSecret   = Just masterSecret+setMainSecret :: Version -> Role -> Secret -> HandshakeM ()+setMainSecret ver role mainSecret = modify' $ \hst ->+    let (pendingTx, pendingRx) = computeKeyBlock hst mainSecret ver role+     in hst+            { hstMainSecret = Just mainSecret             , hstPendingTxState = Just pendingTx-            , hstPendingRxState = Just pendingRx }--computeKeyBlock :: HandshakeState -> ByteString -> Version -> Role -> (RecordState, RecordState)-computeKeyBlock hst masterSecret ver cc = (pendingTx, pendingRx)-  where cipher       = fromJust "cipher" $ hstPendingCipher hst-        keyblockSize = cipherKeyBlockSize cipher--        bulk         = cipherBulk cipher-        digestSize   = if hasMAC (bulkF bulk) then hashDigestSize (cipherHash cipher)-                                              else 0-        keySize      = bulkKeySize bulk-        ivSize       = bulkIVSize bulk-        kb           = generateKeyBlock ver cipher (hstClientRandom hst)-                                        (fromJust "server random" $ hstServerRandom hst)-                                        masterSecret keyblockSize--        (cMACSecret, sMACSecret, cWriteKey, sWriteKey, cWriteIV, sWriteIV) =-                    fromJust "p6" $ partition6 kb (digestSize, digestSize, keySize, keySize, ivSize, ivSize)+            , hstPendingRxState = Just pendingRx+            } -        cstClient = CryptState { cstKey        = bulkInit bulk (BulkEncrypt `orOnServer` BulkDecrypt) cWriteKey-                               , cstIV         = cWriteIV-                               , cstMacSecret  = cMACSecret }-        cstServer = CryptState { cstKey        = bulkInit bulk (BulkDecrypt `orOnServer` BulkEncrypt) sWriteKey-                               , cstIV         = sWriteIV-                               , cstMacSecret  = sMACSecret }-        msClient = MacState { msSequence = 0 }-        msServer = MacState { msSequence = 0 }+computeKeyBlock+    :: HandshakeState -> Secret -> Version -> Role -> (RecordState, RecordState)+computeKeyBlock hst mainSecret ver cc = (pendingTx, pendingRx)+  where+    cipher = fromJust $ hstPendingCipher hst+    keyblockSize = cipherKeyBlockSize cipher -        pendingTx = RecordState-                  { stCryptState  = if cc == ClientRole then cstClient else cstServer-                  , stMacState    = if cc == ClientRole then msClient else msServer-                  , stCipher      = Just cipher-                  , stCompression = hstPendingCompression hst-                  }-        pendingRx = RecordState-                  { stCryptState  = if cc == ClientRole then cstServer else cstClient-                  , stMacState    = if cc == ClientRole then msServer else msClient-                  , stCipher      = Just cipher-                  , stCompression = hstPendingCompression hst-                  }+    bulk = cipherBulk cipher+    digestSize =+        if hasMAC (bulkF bulk)+            then hashDigestSize (cipherHash cipher)+            else 0+    keySize = bulkKeySize bulk+    ivSize = bulkIVSize bulk+    kb =+        generateKeyBlock+            ver+            cipher+            (hstClientRandom hst)+            (fromJust $ hstServerRandom hst)+            mainSecret+            keyblockSize -        orOnServer f g = if cc == ClientRole then f else g+    (cMACSecret, sMACSecret, cWriteKey, sWriteKey, cWriteIV, sWriteIV) =+        fromJust $+            partition6 kb (digestSize, digestSize, keySize, keySize, ivSize, ivSize) +    cstClient =+        CryptState+            { cstKey = bulkInit bulk (BulkEncrypt `orOnServer` BulkDecrypt) cWriteKey+            , cstIV = convert $ cWriteIV+            , cstMacSecret = cMACSecret+            }+    cstServer =+        CryptState+            { cstKey = bulkInit bulk (BulkDecrypt `orOnServer` BulkEncrypt) sWriteKey+            , cstIV = convert $ sWriteIV+            , cstMacSecret = sMACSecret+            }+    msClient = MacState{msSequence = 0}+    msServer = MacState{msSequence = 0} -setServerHelloParameters :: Version      -- ^ chosen version-                         -> ServerRandom-                         -> Cipher-                         -> Compression-                         -> HandshakeM ()-setServerHelloParameters ver sran cipher compression = do-    modify $ \hst -> hst-                { hstServerRandom       = Just sran-                , hstPendingCipher      = Just cipher-                , hstPendingCompression = compression-                , hstHandshakeDigest    = updateDigest $ hstHandshakeDigest hst-                }-  where hashAlg = getHash ver cipher-        updateDigest (Left bytes) = Right $ foldl hashUpdate (hashInit hashAlg) $ reverse bytes-        updateDigest (Right _)    = error "cannot initialize digest with another digest"+    pendingTx =+        RecordState+            { stCryptState = if cc == ClientRole then cstClient else cstServer+            , stMacState = if cc == ClientRole then msClient else msServer+            , stCryptLevel = CryptMainSecret+            , stCipher = Just cipher+            , stCompression = hstPendingCompression hst+            }+    pendingRx =+        RecordState+            { stCryptState = if cc == ClientRole then cstServer else cstClient+            , stMacState = if cc == ClientRole then msServer else msClient+            , stCryptLevel = CryptMainSecret+            , stCipher = Just cipher+            , stCompression = hstPendingCompression hst+            } --- The TLS12 Hash is cipher specific, and some TLS12 algorithms use SHA384--- instead of the default SHA256.-getHash :: Version -> Cipher -> Hash-getHash ver ciph-    | ver < TLS12                              = SHA1_MD5-    | maybe True (< TLS12) (cipherMinVer ciph) = SHA256-    | otherwise                                = cipherHash ciph+    orOnServer f g = if cc == ClientRole then f else g
+ Network/TLS/Handshake/State13.hs view
@@ -0,0 +1,150 @@+{-# LANGUAGE OverloadedStrings #-}++module Network.TLS.Handshake.State13 (+    CryptLevel (+        CryptEarlySecret,+        CryptHandshakeSecret,+        CryptApplicationSecret+    ),+    TrafficSecret,+    getTxRecordState,+    getRxRecordState,+    setTxRecordState,+    setRxRecordState,+    getTxLevel,+    getRxLevel,+    clearTxRecordState,+    clearRxRecordState,+    PendingRecvAction (..),+    setPendingRecvActions,+    popPendingRecvAction,+) where++import Control.Concurrent.MVar+import Data.IORef++import Network.TLS.Cipher+import Network.TLS.Compression+import Network.TLS.Context.Internal+import Network.TLS.Imports+import Network.TLS.KeySchedule (hkdfExpandLabel)+import Network.TLS.Record.State+import Network.TLS.Types++getTxRecordState :: Context -> IO (Hash, Cipher, CryptLevel, Secret)+getTxRecordState ctx = getXState ctx ctxTxRecordState++getRxRecordState :: Context -> IO (Hash, Cipher, CryptLevel, Secret)+getRxRecordState ctx = getXState ctx ctxRxRecordState++getXState+    :: Context+    -> (Context -> MVar RecordState)+    -> IO (Hash, Cipher, CryptLevel, Secret)+getXState ctx func = do+    tx <- readMVar (func ctx)+    let usedCipher = fromJust $ stCipher tx+        usedHash = cipherHash usedCipher+        level = stCryptLevel tx+        secret = cstMacSecret $ stCryptState tx+    return (usedHash, usedCipher, level, secret)++-- In the case of QUIC, stCipher is Nothing.+-- So, fromJust causes an error.+getTxLevel :: Context -> IO CryptLevel+getTxLevel ctx = getXLevel ctx ctxTxRecordState++getRxLevel :: Context -> IO CryptLevel+getRxLevel ctx = getXLevel ctx ctxRxRecordState++getXLevel+    :: Context+    -> (Context -> MVar RecordState)+    -> IO CryptLevel+getXLevel ctx func = do+    tx <- readMVar (func ctx)+    return $ stCryptLevel tx++class TrafficSecret ty where+    fromTrafficSecret :: ty -> (CryptLevel, Secret)++instance HasCryptLevel a => TrafficSecret (AnyTrafficSecret a) where+    fromTrafficSecret prx@(AnyTrafficSecret s) = (getCryptLevel prx, s)++instance HasCryptLevel a => TrafficSecret (ClientTrafficSecret a) where+    fromTrafficSecret prx@(ClientTrafficSecret s) = (getCryptLevel prx, s)++instance HasCryptLevel a => TrafficSecret (ServerTrafficSecret a) where+    fromTrafficSecret prx@(ServerTrafficSecret s) = (getCryptLevel prx, s)++setTxRecordState :: TrafficSecret ty => Context -> Hash -> Cipher -> ty -> IO ()+setTxRecordState = setXState ctxTxRecordState BulkEncrypt++setRxRecordState :: TrafficSecret ty => Context -> Hash -> Cipher -> ty -> IO ()+setRxRecordState = setXState ctxRxRecordState BulkDecrypt++setXState+    :: TrafficSecret ty+    => (Context -> MVar RecordState)+    -> BulkDirection+    -> Context+    -> Hash+    -> Cipher+    -> ty+    -> IO ()+setXState func encOrDec ctx h cipher ts =+    let (lvl, secret) = fromTrafficSecret ts+     in setXState' func encOrDec ctx h cipher lvl secret++setXState'+    :: (Context -> MVar RecordState)+    -> BulkDirection+    -> Context+    -> Hash+    -> Cipher+    -> CryptLevel+    -> Secret+    -> IO ()+setXState' func encOrDec ctx h cipher lvl secret =+    modifyMVar_ (func ctx) (\_ -> return rt)+  where+    bulk = cipherBulk cipher+    keySize = bulkKeySize bulk+    ivSize = max 8 (bulkIVSize bulk + bulkExplicitIV bulk)+    key = hkdfExpandLabel h secret "key" "" keySize+    iv = hkdfExpandLabel h secret "iv" "" ivSize+    cst =+        CryptState+            { cstKey = bulkInit bulk encOrDec key+            , cstIV = iv+            , cstMacSecret = secret+            }+    rt =+        RecordState+            { stCryptState = cst+            , stMacState = MacState{msSequence = 0}+            , stCryptLevel = lvl+            , stCipher = Just cipher+            , stCompression = nullCompression+            }++clearTxRecordState :: Context -> IO ()+clearTxRecordState = clearXState ctxTxRecordState++clearRxRecordState :: Context -> IO ()+clearRxRecordState = clearXState ctxRxRecordState++clearXState :: (Context -> MVar RecordState) -> Context -> IO ()+clearXState func ctx =+    modifyMVar_ (func ctx) (\rt -> return rt{stCipher = Nothing})++setPendingRecvActions :: Context -> [PendingRecvAction] -> IO ()+setPendingRecvActions ctx = writeIORef (ctxPendingRecvActions ctx)++popPendingRecvAction :: Context -> IO (Maybe PendingRecvAction)+popPendingRecvAction ctx = do+    let ref = ctxPendingRecvActions ctx+    actions <- readIORef ref+    case actions of+        bs : bss -> writeIORef ref bss >> return (Just bs)+        [] -> return Nothing
+ Network/TLS/Handshake/TranscriptHash.hs view
@@ -0,0 +1,131 @@+{-# LANGUAGE OverloadedStrings #-}++module Network.TLS.Handshake.TranscriptHash (+    transcriptHash,+    transcriptHashWith,+    transitTranscriptHashI,+    updateTranscriptHash,+    updateTranscriptHashI,+    transitTranscriptHash,+    copyTranscriptHash,+    TranscriptHash (..),+) where++import Control.Monad.State+import qualified Data.ByteString as B++import Network.TLS.Cipher+import Network.TLS.Context.Internal+import Network.TLS.Crypto+import Network.TLS.Handshake.State+import Network.TLS.Imports+import Network.TLS.Parameters+import Network.TLS.State+import Network.TLS.Types++----------------------------------------------------------------++transitTranscriptHash :: Context -> String -> Hash -> Bool -> IO ()+transitTranscriptHash ctx label hashAlg isHRR = do+    usingHState ctx $ modify' $ \hst ->+        hst{hstTransHashState = transit label hashAlg isHRR $ hstTransHashState hst}+    traceTranscriptHash ctx label hstTransHashState++transitTranscriptHashI :: Context -> String -> Hash -> Bool -> IO ()+transitTranscriptHashI ctx label hashAlg isHRR = do+    usingHState ctx $ modify' $ \hst ->+        hst{hstTransHashStateI = transit label hashAlg isHRR $ hstTransHashStateI hst}+    traceTranscriptHash ctx label hstTransHashStateI++transit :: String -> Hash -> Bool -> TransHashState -> TransHashState+transit label _ _ st0@TransHashState0 = error $ "transitTranscriptHash " ++ label ++ " " ++ show st0+transit _ _ _ st2@(TransHashState2 _) = st2+transit _ hashAlg isHRR (TransHashState1 chs)+    | isHRR = TransHashState2 $ hashUpdate (hashInit hashAlg) hsMsg+    | otherwise = TransHashState2 $ hashUpdates (hashInit hashAlg) ch+  where+    ch = reverse chs+    hsMsg =+        -- Handshake message:+        -- typ <-len-> body+        -- 254 0 0 len hash(CH1)+        B.concat+            [ "\254\0\0"+            , B.singleton len+            , hashedCH+            ]+      where+        hashedCH = hashChunks hashAlg ch+        len = fromIntegral $ B.length hashedCH++----------------------------------------------------------------++updateTranscriptHash :: Context -> String -> ByteString -> IO ()+updateTranscriptHash ctx label eh = do+    usingHState ctx $ modify' $ \hst ->+        hst{hstTransHashState = update eh label $ hstTransHashState hst}+    traceTranscriptHash ctx label hstTransHashState++updateTranscriptHashI :: Context -> String -> ByteString -> IO ()+updateTranscriptHashI ctx label eh = do+    usingHState ctx $ modify' $ \hst ->+        hst{hstTransHashStateI = update eh label $ hstTransHashStateI hst}+    traceTranscriptHash ctx label hstTransHashStateI++update :: ByteString -> String -> TransHashState -> TransHashState+update eh _ TransHashState0 = TransHashState1 [eh]+update eh _ (TransHashState1 bss) = TransHashState1 (eh : bss)+update eh _ (TransHashState2 hctx) = TransHashState2 $ hashUpdate hctx eh++----------------------------------------------------------------++transcriptHash :: MonadIO m => Context -> String -> m TranscriptHash+transcriptHash ctx label = do+    hst <- fromJust <$> getHState ctx+    let th = calc label $ hstTransHashState hst+    liftIO $ debugTraceKey (ctxDebug ctx) $ adjustLabel label ++ showBytesHex th+    return $ TranscriptHash th++calc :: String -> TransHashState -> ByteString+calc _ (TransHashState2 hashCtx) = hashFinal hashCtx+calc label st = error $ "transcriptHash " ++ label ++ " " ++ show st++----------------------------------------------------------------++transcriptHashWith+    :: MonadIO m => Context -> String -> ByteString -> m TranscriptHash+transcriptHashWith ctx label bs = do+    role <- liftIO $ usingState_ ctx getRole+    let isClient = role == ClientRole+    hst <- fromJust <$> getHState ctx+    let st+            | isClient = hstTransHashStateI hst+            | otherwise = hstTransHashState hst+    let th = calcWith bs label st+    liftIO $ debugTraceKey (ctxDebug ctx) $ adjustLabel label ++ showBytesHex th+    return $ TranscriptHash th++calcWith :: ByteString -> String -> TransHashState -> ByteString+calcWith bs _ (TransHashState2 hashCtx) = hashFinal $ hashUpdate hashCtx bs+calcWith _ label st = error $ "transcriptHashWith " ++ label ++ " " ++ show st++----------------------------------------------------------------++copyTranscriptHash :: Context -> String -> IO ()+copyTranscriptHash ctx label = do+    usingHState ctx $ modify' $ \hst ->+        hst+            { hstTransHashState = hstTransHashStateI hst+            }+    traceTranscriptHash ctx label hstTransHashState++----------------------------------------------------------------++traceTranscriptHash+    :: Context -> String -> (HandshakeState -> TransHashState) -> IO ()+traceTranscriptHash ctx label getField = do+    hst <- fromJust <$> getHState ctx+    debugTraceKey (ctxDebug ctx) $ adjustLabel label ++ show (getField hst)++adjustLabel :: String -> String+adjustLabel label = take 24 (label ++ "                      ")
+ Network/TLS/HashAndSignature.hs view
@@ -0,0 +1,181 @@+{-# LANGUAGE PatternSynonyms #-}++module Network.TLS.HashAndSignature (+    HashAlgorithm (+        ..,+        HashNone,+        HashMD5,+        HashSHA1,+        HashSHA224,+        HashSHA256,+        HashSHA384,+        HashSHA512,+        HashIntrinsic+    ),+    SignatureAlgorithm (+        ..,+        SignatureAnonymous,+        SignatureRSA,+        SignatureDSA,+        SignatureECDSA,+        SignatureRSApssRSAeSHA256,+        SignatureRSApssRSAeSHA384,+        SignatureRSApssRSAeSHA512,+        SignatureEd25519,+        SignatureEd448,+        SignatureRSApsspssSHA256,+        SignatureRSApsspssSHA384,+        SignatureRSApsspssSHA512,+        SignatureBrainpoolP256,+        SignatureBrainpoolP384,+        SignatureBrainpoolP512+    ),+    HashAndSignatureAlgorithm,+    supportedSignatureSchemes,+    signatureSchemesForTLS13,+) where++import Network.TLS.Imports++------------------------------------------------------------++newtype HashAlgorithm = HashAlgorithm {fromHashAlgorithm :: Word8}+    deriving (Eq)++{- FOURMOLU_DISABLE -}+pattern HashNone      :: HashAlgorithm+pattern HashNone       = HashAlgorithm 0+pattern HashMD5       :: HashAlgorithm+pattern HashMD5        = HashAlgorithm 1+pattern HashSHA1      :: HashAlgorithm+pattern HashSHA1       = HashAlgorithm 2+pattern HashSHA224    :: HashAlgorithm+pattern HashSHA224     = HashAlgorithm 3+pattern HashSHA256    :: HashAlgorithm+pattern HashSHA256     = HashAlgorithm 4+pattern HashSHA384    :: HashAlgorithm+pattern HashSHA384     = HashAlgorithm 5+pattern HashSHA512    :: HashAlgorithm+pattern HashSHA512     = HashAlgorithm 6+pattern HashIntrinsic :: HashAlgorithm+pattern HashIntrinsic  = HashAlgorithm 8++instance Show HashAlgorithm where+    show HashNone          = "None"+    show HashMD5           = "MD5"+    show HashSHA1          = "SHA1"+    show HashSHA224        = "SHA224"+    show HashSHA256        = "SHA256"+    show HashSHA384        = "SHA384"+    show HashSHA512        = "SHA512"+    show HashIntrinsic     = "TLS13"+    show (HashAlgorithm x) = "Hash " ++ show x+{- FOURMOLU_ENABLE -}++------------------------------------------------------------++newtype SignatureAlgorithm = SignatureAlgorithm {fromSignatureAlgorithm :: Word8}+    deriving (Eq)++{- FOURMOLU_DISABLE -}+pattern SignatureAnonymous        :: SignatureAlgorithm+pattern SignatureAnonymous         = SignatureAlgorithm 0+pattern SignatureRSA              :: SignatureAlgorithm+pattern SignatureRSA               = SignatureAlgorithm 1+pattern SignatureDSA              :: SignatureAlgorithm+pattern SignatureDSA               = SignatureAlgorithm 2+pattern SignatureECDSA            :: SignatureAlgorithm+pattern SignatureECDSA             = SignatureAlgorithm 3+-- TLS 1.3 from here+pattern SignatureRSApssRSAeSHA256 :: SignatureAlgorithm+pattern SignatureRSApssRSAeSHA256  = SignatureAlgorithm 4+pattern SignatureRSApssRSAeSHA384 :: SignatureAlgorithm+pattern SignatureRSApssRSAeSHA384  = SignatureAlgorithm 5+pattern SignatureRSApssRSAeSHA512 :: SignatureAlgorithm+pattern SignatureRSApssRSAeSHA512  = SignatureAlgorithm 6+pattern SignatureEd25519          :: SignatureAlgorithm+pattern SignatureEd25519           = SignatureAlgorithm 7+pattern SignatureEd448            :: SignatureAlgorithm+pattern SignatureEd448             = SignatureAlgorithm 8+pattern SignatureRSApsspssSHA256  :: SignatureAlgorithm+pattern SignatureRSApsspssSHA256   = SignatureAlgorithm 9+pattern SignatureRSApsspssSHA384  :: SignatureAlgorithm+pattern SignatureRSApsspssSHA384   = SignatureAlgorithm 10+pattern SignatureRSApsspssSHA512  :: SignatureAlgorithm+pattern SignatureRSApsspssSHA512   = SignatureAlgorithm 11+pattern SignatureBrainpoolP256    :: SignatureAlgorithm -- RFC8734+pattern SignatureBrainpoolP256     = SignatureAlgorithm 26+pattern SignatureBrainpoolP384    :: SignatureAlgorithm+pattern SignatureBrainpoolP384     = SignatureAlgorithm 27+pattern SignatureBrainpoolP512    :: SignatureAlgorithm+pattern SignatureBrainpoolP512     = SignatureAlgorithm 28++instance Show SignatureAlgorithm where+    show SignatureAnonymous        = "Anonymous"+    show SignatureRSA              = "RSA"+    show SignatureDSA              = "DSA"+    show SignatureECDSA            = "ECDSA"+    show SignatureRSApssRSAeSHA256 = "RSApssRSAeSHA256"+    show SignatureRSApssRSAeSHA384 = "RSApssRSAeSHA384"+    show SignatureRSApssRSAeSHA512 = "RSApssRSAeSHA512"+    show SignatureEd25519          = "Ed25519"+    show SignatureEd448            = "Ed448"+    show SignatureRSApsspssSHA256  = "RSApsspssSHA256"+    show SignatureRSApsspssSHA384  = "RSApsspssSHA384"+    show SignatureRSApsspssSHA512  = "RSApsspssSHA512"+    show SignatureBrainpoolP256    = "BrainpoolP256"+    show SignatureBrainpoolP384    = "BrainpoolP384"+    show SignatureBrainpoolP512    = "BrainpoolP512"+    show (SignatureAlgorithm x)    = "Signature " ++ show x+{- FOURMOLU_ENABLE -}++------------------------------------------------------------++type HashAndSignatureAlgorithm = (HashAlgorithm, SignatureAlgorithm)++{- FOURMOLU_DISABLE -}+supportedSignatureSchemes :: [HashAndSignatureAlgorithm]+supportedSignatureSchemes =+    -- EdDSA algorithms+    [ (HashIntrinsic, SignatureEd448)   -- ed448  (0x0808)+    , (HashIntrinsic, SignatureEd25519) -- ed25519(0x0807)+    -- ECDSA algorithms+    , (HashSHA512,    SignatureECDSA)   -- ecdsa_secp512r1_sha512(0x0603)+    , (HashSHA384,    SignatureECDSA)   -- ecdsa_secp384r1_sha384(0x0503)+    , (HashSHA256,    SignatureECDSA)   -- ecdsa_secp256r1_sha256(0x0403)+    -- RSASSA-PSS RSAE algorithms+    , (HashIntrinsic, SignatureRSApssRSAeSHA512) -- rsa_pss_rsae_sha512(0x0806)+    , (HashIntrinsic, SignatureRSApssRSAeSHA384) -- rsa_pss_rsae_sha384(0x0805)+    , (HashIntrinsic, SignatureRSApssRSAeSHA256) -- rsa_pss_rsae_sha256(0x0804)+    -- RSASSA-PSS PSS algorithms with+    , (HashIntrinsic, SignatureRSApsspssSHA512)  -- rsa_pss_pss_sha512(0x080b)+    , (HashIntrinsic, SignatureRSApsspssSHA384)  -- rsa_pss_pss_sha384(0x080a)+    , (HashIntrinsic, SignatureRSApsspssSHA256)  -- rsa_pss_pss_sha256(0x0809)+    -- RSASSA-PKCS1-v1_5 algorithms+    , (HashSHA512,    SignatureRSA)    -- rsa_pkcs1_sha512(0x0601)+    , (HashSHA384,    SignatureRSA)    -- rsa_pkcs1_sha384(0x0501)+    , (HashSHA256,    SignatureRSA)    -- rsa_pkcs1_sha256(0x0401)+    -- Legacy algorithms+    , (HashSHA1,      SignatureRSA)    -- rsa_pkcs1_sha1  (0x0201)+    , (HashSHA1,      SignatureECDSA)  -- ecdsa_sha1      (0x0203)+    ]++signatureSchemesForTLS13 :: [(HashAlgorithm, SignatureAlgorithm)]+signatureSchemesForTLS13 =+    -- EdDSA algorithms+    [ (HashIntrinsic, SignatureEd448)   -- ed448  (0x0808)+    , (HashIntrinsic, SignatureEd25519) -- ed25519(0x0807)+    -- ECDSA algorithms+    , (HashSHA512,    SignatureECDSA)   -- ecdsa_secp512r1_sha512(0x0603)+    , (HashSHA384,    SignatureECDSA)   -- ecdsa_secp384r1_sha384(0x0503)+    , (HashSHA256,    SignatureECDSA)   -- ecdsa_secp256r1_sha256(0x0403)+    -- RSASSA-PSS RSAE algorithms+    , (HashIntrinsic, SignatureRSApssRSAeSHA512) -- rsa_pss_rsae_sha512(0x0806)+    , (HashIntrinsic, SignatureRSApssRSAeSHA384) -- rsa_pss_rsae_sha384(0x0805)+    , (HashIntrinsic, SignatureRSApssRSAeSHA256) -- rsa_pss_rsae_sha256(0x0804)+    -- RSASSA-PSS PSS algorithms with+    , (HashIntrinsic, SignatureRSApsspssSHA512)  -- rsa_pss_pss_sha512(0x080b)+    , (HashIntrinsic, SignatureRSApsspssSHA384)  -- rsa_pss_pss_sha384(0x080a)+    , (HashIntrinsic, SignatureRSApsspssSHA256)  -- rsa_pss_pss_sha256(0x0809)+    ]+{- FOURMOLU_ENABLE -}
Network/TLS/Hooks.hs view
@@ -1,20 +1,16 @@--- |--- Module      : Network.TLS.Context--- License     : BSD-style--- Maintainer  : Vincent Hanquez <vincent@snarc.org>--- Stability   : experimental--- Portability : unknown----module Network.TLS.Hooks-    ( Logging(..)-    , Hooks(..)-    , defaultHooks-    ) where+module Network.TLS.Hooks (+    Logging (..),+    defaultLogging,+    Hooks (..),+    defaultHooks,+) where -import qualified Data.ByteString as B-import Network.TLS.Struct (Header, Handshake(..))+import Data.Default (Default (def))++import Network.TLS.Imports+import Network.TLS.Struct (Handshake, Header)+import Network.TLS.Struct13 (Handshake13) import Network.TLS.X509 (CertificateChain)-import Data.Default.Class  -- | Hooks for logging --@@ -22,37 +18,42 @@ data Logging = Logging     { loggingPacketSent :: String -> IO ()     , loggingPacketRecv :: String -> IO ()-    , loggingIOSent     :: B.ByteString -> IO ()-    , loggingIORecv     :: Header -> B.ByteString -> IO ()+    , loggingIOSent :: ByteString -> IO ()+    , loggingIORecv :: Header -> ByteString -> IO ()     }  defaultLogging :: Logging-defaultLogging = Logging-    { loggingPacketSent = \_ -> return ()-    , loggingPacketRecv = \_ -> return ()-    , loggingIOSent     = \_ -> return ()-    , loggingIORecv     = \_ _ -> return ()-    }+defaultLogging =+    Logging+        { loggingPacketSent = \_ -> return ()+        , loggingPacketRecv = \_ -> return ()+        , loggingIOSent = \_ -> return ()+        , loggingIORecv = \_ _ -> return ()+        }  instance Default Logging where     def = defaultLogging  -- | A collection of hooks actions. data Hooks = Hooks-    { -- | called at each handshake message received-      hookRecvHandshake    :: Handshake -> IO Handshake-      -- | called at each certificate chain message received+    { hookRecvHandshake :: Handshake -> IO Handshake+    -- ^ called at each handshake message received+    , hookRecvHandshake13 :: Handshake13 -> IO Handshake13+    -- ^ called at each handshake message received for TLS 1.3     , hookRecvCertificates :: CertificateChain -> IO ()-      -- | hooks on IO and packets, receiving and sending.-    , hookLogging          :: Logging+    -- ^ called at each certificate chain message received+    , hookLogging :: Logging+    -- ^ hooks on IO and packets, receiving and sending.     }  defaultHooks :: Hooks-defaultHooks = Hooks-    { hookRecvHandshake    = return-    , hookRecvCertificates = return . const ()-    , hookLogging          = def-    }+defaultHooks =+    Hooks+        { hookRecvHandshake = return+        , hookRecvHandshake13 = return+        , hookRecvCertificates = return . const ()+        , hookLogging = def+        }  instance Default Hooks where     def = defaultHooks
Network/TLS/IO.hs view
@@ -1,132 +1,240 @@-{-# LANGUAGE CPP #-}--- |--- Module      : Network.TLS.IO--- License     : BSD-style--- Maintainer  : Vincent Hanquez <vincent@snarc.org>--- Stability   : experimental--- Portability : unknown----module Network.TLS.IO-    ( checkValid-    , sendPacket-    , recvPacket-    ) where+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE RankNTypes #-} +module Network.TLS.IO (+    sendPacket12,+    sendPacket13,+    recvPacket12,+    recvPacket13,+    --+    isRecvComplete,+    checkValid,++    -- * Grouping multiple packets in the same flight+    PacketFlightM,+    runPacketFlight,+    loadPacket13,+) where++import Control.Exception (finally, throwIO)+import Control.Monad.Reader+import Control.Monad.State.Strict+import qualified Data.ByteString as B+import Data.IORef+ import Network.TLS.Context.Internal-import Network.TLS.Struct-import Network.TLS.Record-import Network.TLS.Packet import Network.TLS.Hooks-import Network.TLS.Sending-import Network.TLS.Receiving+import Network.TLS.IO.Decode+import Network.TLS.IO.Encode import Network.TLS.Imports-import qualified Data.ByteString as B--import Data.IORef-import Control.Monad.State.Strict-import Control.Exception (throwIO)-import System.IO.Error (mkIOError, eofErrorType)+import Network.TLS.Parameters+import Network.TLS.Record+import Network.TLS.State+import Network.TLS.Struct+import Network.TLS.Struct13 -checkValid :: Context -> IO ()-checkValid ctx = do-    established <- ctxEstablished ctx-    unless established $ throwIO ConnectionNotEstablished-    eofed <- ctxEOF ctx-    when eofed $ throwIO $ mkIOError eofErrorType "data" Nothing Nothing+---------------------------------------------------------------- -readExact :: Context -> Int -> IO (Either TLSError ByteString)-readExact ctx sz = do-    hdrbs <- contextRecv ctx sz-    if B.length hdrbs == sz-        then return $ Right hdrbs-        else do-            setEOF ctx-            return . Left $-                if B.null hdrbs-                    then Error_EOF-                    else Error_Packet ("partial packet: expecting " ++ show sz ++ " bytes, got: " ++ show (B.length hdrbs))+-- | Send one packet to the context+sendPacket12 :: Context -> Packet -> IO ()+sendPacket12 ctx@Context{ctxRecordLayer = recordLayer} pkt = do+    -- in ver <= TLS1.0, block ciphers using CBC are using CBC residue+    -- as IV, which can be guessed by an attacker. Hence, an empty+    -- packet is sent before a normal data packet, to prevent+    -- guessability.+    when (isNonNullAppData pkt) $ do+        withEmptyPacket <- readIORef $ ctxNeedEmptyPacket ctx+        when withEmptyPacket $+            writePacketBytes12 ctx recordLayer (AppData B.empty)+                >>= recordSendBytes recordLayer ctx +    writePacketBytes12 ctx recordLayer pkt >>= recordSendBytes recordLayer ctx+  where+    isNonNullAppData (AppData b) = not $ B.null b+    isNonNullAppData _ = False --- | recvRecord receive a full TLS record (header + data), from the other side.------ The record is disengaged from the record layer-recvRecord :: Bool    -- ^ flag to enable SSLv2 compat ClientHello reception-           -> Context -- ^ TLS context-           -> IO (Either TLSError (Record Plaintext))-recvRecord compatSSLv2 ctx-#ifdef SSLV2_COMPATIBLE-    | compatSSLv2 = readExact ctx 2 >>= either (return . Left) sslv2Header-#endif-    | otherwise = readExact ctx 5 >>= either (return . Left) (recvLengthE . decodeHeader)+writePacketBytes12+    :: Monoid bytes+    => Context+    -> RecordLayer bytes+    -> Packet+    -> IO bytes+writePacketBytes12 ctx recordLayer pkt = do+    withLog ctx $ \logging -> loggingPacketSent logging (show pkt)+    edataToSend <- encodePacket12 ctx recordLayer pkt+    either throwCore return edataToSend -        where recvLengthE = either (return . Left) recvLength+---------------------------------------------------------------- -              recvLength header@(Header _ _ readlen)-                | readlen > 16384 + 2048 = return $ Left maximumSizeExceeded-                | otherwise              =-                    readExact ctx (fromIntegral readlen) >>=-                        either (return . Left) (getRecord header)-#ifdef SSLV2_COMPATIBLE-              sslv2Header header =-                if B.head header >= 0x80-                    then either (return . Left) recvDeprecatedLength $ decodeDeprecatedHeaderLength header-                    else readExact ctx 3 >>=-                            either (return . Left) (recvLengthE . decodeHeader . B.append header)+sendPacket13 :: Context -> Packet13 -> IO ()+sendPacket13 ctx@Context{ctxRecordLayer = recordLayer} pkt =+    writePacketBytes13 ctx recordLayer pkt >>= recordSendBytes recordLayer ctx -              recvDeprecatedLength readlen-                | readlen > 1024 * 4     = return $ Left maximumSizeExceeded-                | otherwise              = do-                    res <- readExact ctx (fromIntegral readlen)-                    case res of-                      Left e -> return $ Left e-                      Right content ->-                        either (return . Left) (flip getRecord content) $ decodeDeprecatedHeader readlen content-#endif-              maximumSizeExceeded = Error_Protocol ("record exceeding maximum size", True, RecordOverflow)-              getRecord :: Header -> ByteString -> IO (Either TLSError (Record Plaintext))-              getRecord header content = do-                    withLog ctx $ \logging -> loggingIORecv logging header content-                    runRxState ctx $ disengageRecord $ rawToRecord header (fragmentCiphertext content)+writePacketBytes13+    :: Monoid bytes+    => Context+    -> RecordLayer bytes+    -> Packet13+    -> IO bytes+writePacketBytes13 ctx recordLayer pkt = do+    withLog ctx $ \logging -> loggingPacketSent logging (show pkt)+    edataToSend <- encodePacket13 ctx recordLayer pkt+    either throwCore return edataToSend +----------------------------------------------------------------  -- | receive one packet from the context that contains 1 or -- many messages (many only in case of handshake). if will returns a -- TLSError if the packet is unexpected or malformed-recvPacket :: MonadIO m => Context -> m (Either TLSError Packet)-recvPacket ctx = liftIO $ do-    compatSSLv2 <- ctxHasSSLv2ClientHello ctx-    erecord     <- recvRecord compatSSLv2 ctx-    case erecord of-        Left err     -> return $ Left err-        Right record -> do-            pktRecv <- processPacket ctx record-            pkt <- case pktRecv of-                    Right (Handshake hss) ->-                        ctxWithHooks ctx $ \hooks ->-                            Right . Handshake <$> mapM (hookRecvHandshake hooks) hss-                    _                     -> return pktRecv-            case pkt of-                Right p -> withLog ctx $ \logging -> loggingPacketRecv logging $ show p-                _       -> return ()-            when compatSSLv2 $ ctxDisableSSLv2ClientHello ctx-            return pkt+recvPacket12 :: Context -> IO (Either TLSError Packet)+recvPacket12 ctx@Context{ctxRecordLayer = recordLayer} = loop 0+  where+    lim = limitHandshakeFragment $ sharedLimit $ ctxShared ctx+    loop count+        | count > lim = do+            let err = Error_Packet "too many handshake fragment"+            logPacket ctx $ show err+            return $ Left err+    loop count = do+        hrr <- usingState_ ctx getTLS13HRR+        erecord <- recordRecv12 recordLayer ctx+        case erecord of+            Left err -> do+                logPacket ctx $ show err+                return $ Left err+            Right record+                | hrr && isCCS record -> loop (count + 1)+                | otherwise -> do+                    pktRecv <- decodePacket12 ctx record+                    if isEmptyHandshake pktRecv+                        then do+                            logPacket ctx "Handshake fragment"+                            -- When a handshake record is fragmented+                            -- we continue receiving in order to feed+                            -- stHandshakeRecordCont+                            loop (count + 1)+                        else case pktRecv of+                            Right (Handshake hss bss) -> do+                                pktRecv'@(Right pkt) <- ctxWithHooks ctx $ \hooks -> do+                                    hss' <- mapM (hookRecvHandshake hooks) hss+                                    return $ Right $ Handshake hss' bss+                                logPacket ctx $ show pkt+                                return pktRecv'+                            Right pkt -> do+                                logPacket ctx $ show pkt+                                return pktRecv+                            Left err -> do+                                logPacket ctx $ show err+                                return pktRecv --- | Send one packet to the context-sendPacket :: MonadIO m => Context -> Packet -> m ()-sendPacket ctx pkt = do-    -- in ver <= TLS1.0, block ciphers using CBC are using CBC residue as IV, which can be guessed-    -- by an attacker. Hence, an empty packet is sent before a normal data packet, to-    -- prevent guessability.-    withEmptyPacket <- liftIO $ readIORef $ ctxNeedEmptyPacket ctx-    when (isNonNullAppData pkt && withEmptyPacket) $ sendPacket ctx $ AppData B.empty+isCCS :: Record a -> Bool+isCCS (Record ProtocolType_ChangeCipherSpec _ _) = True+isCCS _ = False -    edataToSend <- liftIO $ do-                        withLog ctx $ \logging -> loggingPacketSent logging (show pkt)-                        writePacket ctx pkt-    case edataToSend of-        Left err         -> throwCore err-        Right dataToSend -> liftIO $ do-            withLog ctx $ \logging -> loggingIOSent logging dataToSend-            contextSend ctx dataToSend-  where isNonNullAppData (AppData b) = not $ B.null b-        isNonNullAppData _           = False+isEmptyHandshake :: Either TLSError Packet -> Bool+isEmptyHandshake (Right (Handshake [] _)) = True+isEmptyHandshake _ = False++logPacket :: Context -> String -> IO ()+logPacket ctx msg = withLog ctx $ \logging -> loggingPacketRecv logging msg++----------------------------------------------------------------++recvPacket13 :: Context -> IO (Either TLSError Packet13)+recvPacket13 ctx@Context{ctxRecordLayer = recordLayer} = loop 0+  where+    lim = limitHandshakeFragment $ sharedLimit $ ctxShared ctx+    loop count+        | count > lim =+            return $ Left $ Error_Packet "too many handshake fragment"+    loop count = do+        erecord <- recordRecv13 recordLayer ctx+        case erecord of+            Left err@(Error_Protocol _ BadRecordMac) -> do+                -- If the server decides to reject RTT0 data but accepts RTT1+                -- data, the server should skip all records for RTT0 data.+                logPacket ctx $ show err+                established <- ctxEstablished ctx+                case established of+                    EarlyDataNotAllowed n+                        | n > 0 -> do+                            setEstablished ctx $ EarlyDataNotAllowed (n - 1)+                            loop (count + 1)+                    _ -> return $ Left err+            Left err -> do+                logPacket ctx $ show err+                return $ Left err+            Right record -> do+                pktRecv <- decodePacket13 ctx record+                if isEmptyHandshake13 pktRecv+                    then do+                        logPacket ctx "Handshake fragment"+                        -- When a handshake record is fragmented we+                        -- continue receiving in order to feed+                        -- stHandshakeRecordCont13+                        loop (count + 1)+                    else do+                        case pktRecv of+                            Right (Handshake13 hss bss) -> do+                                pktRecv'@(Right pkt) <- ctxWithHooks ctx $ \hooks -> do+                                    hss' <- mapM (hookRecvHandshake13 hooks) hss+                                    return $ Right $ Handshake13 hss' bss+                                logPacket ctx $ show pkt+                                return pktRecv'+                            Right pkt -> do+                                logPacket ctx $ show pkt+                                return pktRecv+                            Left err -> do+                                logPacket ctx $ show err+                                return pktRecv++isEmptyHandshake13 :: Either TLSError Packet13 -> Bool+isEmptyHandshake13 (Right (Handshake13 [] _)) = True+isEmptyHandshake13 _ = False++----------------------------------------------------------------++isRecvComplete :: Context -> IO Bool+isRecvComplete ctx = usingState_ ctx $ do+    cont12 <- gets stHandshakeRecordCont12+    cont13 <- gets stHandshakeRecordCont13+    return $ isNothing (fst cont12) && isNothing (fst cont13)++checkValid :: Context -> IO ()+checkValid ctx = do+    established <- ctxEstablished ctx+    when (established == NotEstablished) $ throwIO ConnectionNotEstablished+    eofed <- ctxEOF ctx+    when eofed $ throwIO $ PostHandshake Error_EOF++----------------------------------------------------------------++type Builder b = [b] -> [b]++-- | State monad used to group several packets together and send them on wire as+-- single flight.  When packets are loaded in the monad, they are logged+-- immediately, update the context digest and transcript, but actual sending is+-- deferred.  Packets are sent all at once when the monadic computation ends+-- (normal termination but also if interrupted by an exception).+newtype PacketFlightM b a+    = PacketFlightM (ReaderT (RecordLayer b, IORef (Builder b)) IO a)+    deriving (Functor, Applicative, Monad, MonadFail, MonadIO)++runPacketFlight :: Context -> (forall b. Monoid b => PacketFlightM b a) -> IO a+runPacketFlight ctx@Context{ctxRecordLayer = recordLayer} (PacketFlightM f) = do+    ref <- newIORef id+    runReaderT f (recordLayer, ref) `finally` sendPendingFlight ctx recordLayer ref++sendPendingFlight+    :: Monoid b => Context -> RecordLayer b -> IORef (Builder b) -> IO ()+sendPendingFlight ctx recordLayer ref = do+    build <- readIORef ref+    let bss = build []+    unless (null bss) $ recordSendBytes recordLayer ctx $ mconcat bss++loadPacket13 :: Monoid b => Context -> Packet13 -> PacketFlightM b ()+loadPacket13 ctx pkt = PacketFlightM $ do+    (recordLayer, ref) <- ask+    liftIO $ do+        bs <- writePacketBytes13 ctx recordLayer pkt+        modifyIORef' ref (. (bs :))
+ Network/TLS/IO/Decode.hs view
@@ -0,0 +1,109 @@+{-# LANGUAGE FlexibleContexts #-}++module Network.TLS.IO.Decode (+    decodePacket12,+    decodePacket13,+) where++import Control.Concurrent.MVar+import Control.Monad.State.Strict+import qualified Data.ByteString as BS++import Network.TLS.Cipher+import Network.TLS.Context.Internal+import Network.TLS.ErrT+import Network.TLS.Handshake.State+import Network.TLS.Imports+import Network.TLS.Packet+import Network.TLS.Packet13+import Network.TLS.Record+import Network.TLS.State+import Network.TLS.Struct+import Network.TLS.Struct13+import Network.TLS.Util+import Network.TLS.Wire++decodePacket12 :: Context -> Record Plaintext -> IO (Either TLSError Packet)+decodePacket12 _ (Record ProtocolType_AppData _ fragment) = return $ Right $ AppData $ fragmentGetBytes fragment+decodePacket12 _ (Record ProtocolType_Alert _ fragment) = return (Alert `fmapEither` decodeAlerts (fragmentGetBytes fragment))+decodePacket12 ctx (Record ProtocolType_ChangeCipherSpec _ fragment) =+    case decodeChangeCipherSpec $ fragmentGetBytes fragment of+        Left err -> return $ Left err+        Right _ -> do+            switchRxEncryption ctx+            return $ Right ChangeCipherSpec+decodePacket12 ctx (Record ProtocolType_Handshake ver fragment) = do+    keyxchg <-+        getHState ctx >>= \hs -> return (hs >>= hstPendingCipher >>= Just . cipherKeyExchange)+    usingState ctx $ do+        let currentParams =+                CurrentParams+                    { cParamsVersion = ver+                    , cParamsKeyXchgType = keyxchg+                    }+        -- get back the optional continuation, and parse as many handshake record as possible.+        (mCont, wirebytes) <- gets stHandshakeRecordCont12+        modify' (\st -> st{stHandshakeRecordCont12 = (Nothing, [])})+        (hss, bss) <-+            unzip <$> parseMany currentParams mCont wirebytes (fragmentGetBytes fragment)+        return $ Handshake hss bss+  where+    parseMany currentParams mCont wirebytes bs =+        case fromMaybe decodeHandshakeRecord mCont bs of+            GotError err -> throwError err+            GotPartial cont -> do+                modify' (\st -> st{stHandshakeRecordCont12 = (Just cont, bs : wirebytes)})+                return []+            GotSuccess (ty, content) ->+                case decodeHandshake currentParams ty content of+                    Left err -> throwError err+                    Right h -> return [(h, reverse (bs : wirebytes))]+            GotSuccessRemaining (ty, content) left ->+                case decodeHandshake currentParams ty content of+                    Left err -> throwError err+                    Right h -> do+                        hbs <- parseMany currentParams Nothing [] left+                        let len = BS.length bs - BS.length left+                            bs' = BS.take len bs+                        return ((h, reverse (bs' : wirebytes)) : hbs)+decodePacket12 _ _ = return $ Left (Error_Packet_Parsing "unknown protocol type")++switchRxEncryption :: Context -> IO ()+switchRxEncryption ctx =+    usingHState ctx (gets hstPendingRxState) >>= \rx ->+        modifyMVar_ (ctxRxRecordState ctx) (\_ -> return $ fromJust rx)++----------------------------------------------------------------++decodePacket13 :: Context -> Record Plaintext -> IO (Either TLSError Packet13)+decodePacket13 _ (Record ProtocolType_ChangeCipherSpec _ fragment) =+    case decodeChangeCipherSpec $ fragmentGetBytes fragment of+        Left err -> return $ Left err+        Right _ -> return $ Right ChangeCipherSpec13+decodePacket13 _ (Record ProtocolType_AppData _ fragment) = return $ Right $ AppData13 $ fragmentGetBytes fragment+decodePacket13 _ (Record ProtocolType_Alert _ fragment) = return (Alert13 `fmapEither` decodeAlerts (fragmentGetBytes fragment))+decodePacket13 ctx (Record ProtocolType_Handshake _ fragment) = usingState ctx $ do+    (mCont, wirebytes) <- gets stHandshakeRecordCont13+    modify' (\st -> st{stHandshakeRecordCont13 = (Nothing, [])})+    (hss, bss) <- unzip <$> parseMany mCont wirebytes (fragmentGetBytes fragment)+    return $ Handshake13 hss bss+  where+    parseMany mCont wirebytes bs =+        case fromMaybe decodeHandshakeRecord13 mCont bs of+            GotError err -> throwError err+            GotPartial cont -> do+                modify' (\st -> st{stHandshakeRecordCont13 = (Just cont, bs : wirebytes)})+                return []+            GotSuccess (ty, content) ->+                case decodeHandshake13 ty content of+                    Left err -> throwError err+                    Right h -> return [(h, reverse (bs : wirebytes))]+            GotSuccessRemaining (ty, content) left ->+                case decodeHandshake13 ty content of+                    Left err -> throwError err+                    Right h -> do+                        hbs <- parseMany Nothing [] left+                        let len = BS.length bs - BS.length left+                            bs' = BS.take len bs+                        return ((h, reverse (bs' : wirebytes)) : hbs)+decodePacket13 _ _ = return $ Left (Error_Packet_Parsing "unknown protocol type")
+ Network/TLS/IO/Encode.hs view
@@ -0,0 +1,148 @@+module Network.TLS.IO.Encode (+    encodePacket12,+    encodePacket13,+    updateTranscriptHash12,+    encodeUpdateTranscriptHash12,+    updateTranscriptHash13,+    encodeUpdateTranscriptHash13,+) where++import Control.Concurrent.MVar+import Control.Monad.State.Strict+import qualified Data.ByteString as B+import Data.IORef++import Network.TLS.Cipher+import Network.TLS.Context.Internal+import Network.TLS.Handshake.State+import Network.TLS.Handshake.TranscriptHash+import Network.TLS.Imports+import Network.TLS.Packet+import Network.TLS.Packet13+import Network.TLS.Parameters+import Network.TLS.Record+import Network.TLS.State+import Network.TLS.Struct+import Network.TLS.Struct13+import Network.TLS.Types (Role (..))+import Network.TLS.Util++-- | encodePacket transform a packet into marshalled data related to current state+-- and updating state on the go+encodePacket12+    :: Monoid bytes+    => Context+    -> RecordLayer bytes+    -> Packet+    -> IO (Either TLSError bytes)+encodePacket12 ctx recordLayer pkt = do+    (ver, _) <- decideRecordVersion ctx+    let pt = packetType pkt+        mkRecord bs = Record pt ver (fragmentPlaintext bs)+    mlen <- getPeerRecordLimit ctx+    records <- map mkRecord <$> packetToFragments12 ctx mlen pkt+    bs <- fmap mconcat <$> forEitherM records (recordEncode12 recordLayer ctx)+    when (pkt == ChangeCipherSpec) $ switchTxEncryption ctx+    return bs++-- Decompose handshake packets into fragments of the specified length.  AppData+-- packets are not fragmented here but by callers of sendPacket, so that the+-- empty-packet countermeasure may be applied to each fragment independently.+packetToFragments12 :: Context -> Maybe Int -> Packet -> IO [ByteString]+packetToFragments12 ctx mlen (Handshake hss _) =+    getChunks mlen . B.concat <$> mapM (encodeUpdateTranscriptHash12 ctx) hss+packetToFragments12 _ _ (Alert a) = return [encodeAlerts a]+packetToFragments12 _ _ ChangeCipherSpec = return [encodeChangeCipherSpec]+packetToFragments12 _ _ (AppData x) = return [x]++switchTxEncryption :: Context -> IO ()+switchTxEncryption ctx = do+    tx <- usingHState ctx (fromJust <$> gets hstPendingTxState)+    (ver, role) <- usingState_ ctx $ do+        v <- getVersion+        r <- getRole+        return (v, r)+    liftIO $ modifyMVar_ (ctxTxRecordState ctx) (\_ -> return tx)+    -- set empty packet counter measure if condition are met+    when+        ( ver <= TLS10+            && role == ClientRole+            && isCBC tx+            && supportedEmptyPacket (ctxSupported ctx)+        )+        $ liftIO+        $ writeIORef (ctxNeedEmptyPacket ctx) True+  where+    isCBC tx = maybe False (\c -> bulkBlockSize (cipherBulk c) > 0) (stCipher tx)++encodeUpdateTranscriptHash12 :: Context -> Handshake -> IO ByteString+encodeUpdateTranscriptHash12 ctx hs = do+    when (certVerifyHandshakeMaterial hs) $+        usingHState ctx $+            addHandshakeMessage encoded+    let label = show $ typeOfHandshake hs+    when (finishedHandshakeMaterial hs) $ updateTranscriptHash ctx label encoded+    when (isClientHello hs) $ do+        usingHState ctx $ do+            (ch, b) <- fromJust <$> getClientHello+            when (null b) $ setClientHello ch [encoded]+    return encoded+  where+    encoded = encodeHandshake hs+    isClientHello (ClientHello _) = True+    isClientHello _ = False++updateTranscriptHash12 :: Context -> HandshakeR -> IO ()+updateTranscriptHash12 ctx (hs, bss) = do+    when (certVerifyHandshakeMaterial hs) $+        usingHState ctx $+            mapM_ addHandshakeMessage bss+    let label = show $ typeOfHandshake hs+    when (finishedHandshakeMaterial hs) $ do+        mapM_ (updateTranscriptHash ctx label) bss++----------------------------------------------------------------++encodePacket13+    :: Monoid bytes+    => Context+    -> RecordLayer bytes+    -> Packet13+    -> IO (Either TLSError bytes)+encodePacket13 ctx recordLayer pkt = do+    let pt = contentType pkt+        mkRecord bs = Record pt TLS12 (fragmentPlaintext bs)+    mlen <- getPeerRecordLimit ctx+    records <- map mkRecord <$> packetToFragments13 ctx mlen pkt+    fmap mconcat <$> forEitherM records (recordEncode13 recordLayer ctx)++packetToFragments13 :: Context -> Maybe Int -> Packet13 -> IO [ByteString]+packetToFragments13 ctx mlen (Handshake13 hss _) =+    getChunks mlen . B.concat <$> mapM (encodeUpdateTranscriptHash13 ctx) hss+packetToFragments13 _ _ (Alert13 a) = return [encodeAlerts a]+packetToFragments13 _ _ (AppData13 x) = return [x]+packetToFragments13 _ _ ChangeCipherSpec13 = return [encodeChangeCipherSpec]++encodeUpdateTranscriptHash13 :: Context -> Handshake13 -> IO ByteString+encodeUpdateTranscriptHash13 ctx hs+    | isIgnored hs = return encoded+    | otherwise = do+        let label = show $ typeOfHandshake13 hs+        updateTranscriptHash ctx label encoded+        usingHState ctx $ addHandshakeMessage encoded+        return encoded+  where+    encoded = encodeHandshake13 hs++updateTranscriptHash13 :: Context -> Handshake13R -> IO ()+updateTranscriptHash13 ctx (hs, bss)+    | isIgnored hs = return ()+    | otherwise = do+        let label = show $ typeOfHandshake13 hs+        mapM_ (updateTranscriptHash ctx label) bss+        usingHState ctx $ mapM_ addHandshakeMessage bss++isIgnored :: Handshake13 -> Bool+isIgnored NewSessionTicket13{} = True+isIgnored KeyUpdate13{} = True+isIgnored _ = False
Network/TLS/Imports.hs view
@@ -1,65 +1,34 @@-{-# LANGUAGE CPP #-} {-# LANGUAGE NoImplicitPrelude #-}-{-# OPTIONS_GHC -fno-warn-dodgy-exports #-} -- Char8 --- |--- Module      : Network.TLS.Imports--- License     : BSD-style--- Maintainer  : Vincent Hanquez <vincent@snarc.org>--- Stability   : experimental--- Portability : unknown----module Network.TLS.Imports-    (+module Network.TLS.Imports (     -- generic exports-      ByteString-    , module Data.ByteString.Char8 -- instance-    , module Control.Applicative-    , module Control.Monad-    , module Data.Bits-    , module Data.List-    , module Data.Maybe-#if MIN_VERSION_base(4,9,0)-    , module Data.Semigroup-#else-    , module Data.Monoid-#endif-    , module Data.Ord-    , module Data.Word-#if !MIN_VERSION_base(4,8,0)-    , sortOn-#endif+    ByteString,+    (<&>),+    module Control.Applicative,+    module Control.Monad,+    module Data.Bits,+    module Data.List,+    module Data.Maybe,+    module Data.Semigroup,+    module Data.Ord,+    module Data.Word,     -- project definition-    , showBytesHex-    ) where--import Data.ByteString (ByteString)-import Data.ByteString.Char8 ()+    showBytesHex,+) where  import Control.Applicative import Control.Monad import Data.Bits+import Data.ByteArray.Encoding as B+import Data.ByteString (ByteString)+import Data.ByteString.Char8 ()+import Data.Functor import Data.List-import Data.Maybe hiding (fromJust)-#if MIN_VERSION_base(4,9,0)-import Data.Semigroup-#else-import Data.Monoid-#endif+import Data.Maybe import Data.Ord+import Data.Semigroup import Data.Word--import Data.ByteArray.Encoding as B import qualified Prelude as P -#if !MIN_VERSION_base(4,8,0)-import Prelude ((.))--sortOn :: Ord b => (a -> b) -> [a] -> [a]-sortOn f =-  map P.snd . sortBy (comparing P.fst) . map (\x -> let y = f x in y `P.seq` (y, x))-#endif- showBytesHex :: ByteString -> P.String showBytesHex bs = P.show (B.convertToBase B.Base16 bs :: ByteString)-
Network/TLS/Internal.hs view
@@ -1,24 +1,37 @@ {-# OPTIONS_HADDOCK hide #-}--- |--- Module      : Network.TLS.Internal--- License     : BSD-style--- Maintainer  : Vincent Hanquez <vincent@snarc.org>--- Stability   : experimental--- Portability : unknown----module Network.TLS.Internal-    ( module Network.TLS.Struct-    , module Network.TLS.Packet-    , module Network.TLS.Receiving-    , module Network.TLS.Sending-    , module Network.TLS.Wire-    , sendPacket-    , recvPacket-    ) where -import Network.TLS.Struct+module Network.TLS.Internal (+    module Network.TLS.Extension,+    module Network.TLS.IO.Decode,+    module Network.TLS.IO.Encode,+    module Network.TLS.Packet,+    module Network.TLS.Packet13,+    module Network.TLS.Struct,+    module Network.TLS.Struct13,+    module Network.TLS.Types,+    module Network.TLS.Wire,+    module Network.TLS.X509,+    sendPacket12,+    recvPacket12,+    makeCipherShowPretty,+) where++import Data.IORef++import Network.TLS.Core (recvPacket12, sendPacket12)+import Network.TLS.Extension+import Network.TLS.Extra.Cipher+import Network.TLS.IO.Decode+import Network.TLS.IO.Encode import Network.TLS.Packet-import Network.TLS.Receiving-import Network.TLS.Sending+import Network.TLS.Packet13+import Network.TLS.Struct+import Network.TLS.Struct13+import Network.TLS.Types import Network.TLS.Wire-import Network.TLS.Core (sendPacket, recvPacket)+import Network.TLS.X509 hiding (Certificate)++----------------------------------------------------------------++makeCipherShowPretty :: IO ()+makeCipherShowPretty = writeIORef globalCipherDict ciphersuite_all
+ Network/TLS/KeySchedule.hs view
@@ -0,0 +1,66 @@+{-# LANGUAGE OverloadedStrings #-}++module Network.TLS.KeySchedule (+    hkdfExtract,+    hkdfExpandLabel,+    deriveSecret,+) where++import qualified Crypto.Hash as H+import Crypto.KDF.HKDF+import Data.ByteArray (ByteArray, ByteArrayAccess, convert)+import qualified Data.ByteString as BS++import Network.TLS.Crypto+import Network.TLS.Imports+import Network.TLS.Types+import Network.TLS.Wire++----------------------------------------------------------------++-- | @HKDF-Extract@ function.  Returns the pseudorandom key (PRK) from salt and+-- input keying material (IKM).+hkdfExtract+    :: (ByteArray ba, ByteArrayAccess ba) => Hash -> ba -> ba -> ba+hkdfExtract SHA1 salt ikm = convert (extract salt ikm :: PRK H.SHA1)+hkdfExtract SHA256 salt ikm = convert (extract salt ikm :: PRK H.SHA256)+hkdfExtract SHA384 salt ikm = convert (extract salt ikm :: PRK H.SHA384)+hkdfExtract SHA512 salt ikm = convert (extract salt ikm :: PRK H.SHA512)+hkdfExtract _ _ _ = error "hkdfExtract: unsupported hash"++----------------------------------------------------------------++deriveSecret :: Hash -> Secret -> ByteString -> TranscriptHash -> Secret+deriveSecret h secret label (TranscriptHash hashedMsgs) =+    hkdfExpandLabel h secret label hashedMsgs outlen+  where+    outlen = hashDigestSize h++----------------------------------------------------------------++-- | @HKDF-Expand-Label@ function.  Returns output keying material of the+-- specified length from the PRK, customized for a TLS label and context.+hkdfExpandLabel+    :: (ByteArray ba, ByteArrayAccess ba)+    => Hash+    -> Secret+    -> ByteString+    -> ByteString+    -> Int+    -> ba+hkdfExpandLabel h secret label ctx outlen = expand' h secret hkdfLabel outlen+  where+    hkdfLabel = runPut $ do+        putWord16 $ fromIntegral outlen+        putOpaque8 ("tls13 " `BS.append` label)+        putOpaque8 ctx++expand'+    :: (ByteArray ba, ByteArrayAccess ba) => Hash -> Secret -> ByteString -> Int -> ba+expand' SHA1 secret label len = expand (extractSkip secret :: PRK H.SHA1) label len+expand' SHA256 secret label len = expand (extractSkip secret :: PRK H.SHA256) label len+expand' SHA384 secret label len = expand (extractSkip secret :: PRK H.SHA384) label len+expand' SHA512 secret label len = expand (extractSkip secret :: PRK H.SHA512) label len+expand' _ _ _ _ = error "expand'"++----------------------------------------------------------------
Network/TLS/MAC.hs view
@@ -1,80 +1,83 @@--- |--- Module      : Network.TLS.MAC--- License     : BSD-style--- Maintainer  : Vincent Hanquez <vincent@snarc.org>--- Stability   : experimental--- Portability : unknown----module Network.TLS.MAC-    ( macSSL-    , hmac-    , prf_MD5-    , prf_SHA1-    , prf_SHA256-    , prf_TLS-    , prf_MD5SHA1-    ) where+module Network.TLS.MAC (+    macSSL,+    hmac,+    prf_MD5,+    prf_SHA1,+    prf_SHA256,+    prf_TLS,+    prf_MD5SHA1,+    PRF,+) where +import Data.ByteArray (ByteArray, ByteArrayAccess)+import qualified Data.ByteArray as BA+ import Network.TLS.Crypto-import Network.TLS.Types import Network.TLS.Imports-import qualified Data.ByteString as B+import Network.TLS.Types -type HMAC = ByteString -> ByteString -> ByteString+type HMAC = Secret -> ByteString -> Secret  macSSL :: Hash -> HMAC macSSL alg secret msg =-    f $! B.concat-        [ secret-        , B.replicate padLen 0x5c-        , f $! B.concat [ secret, B.replicate padLen 0x36, msg ]-        ]+    f $+        BA.concat+            [ secret+            , BA.replicate padLen 0x5c+            , f $ BA.concat [secret, BA.replicate padLen 0x36, BA.convert msg]+            ]   where     padLen = case alg of-        MD5  -> 48+        MD5 -> 48         SHA1 -> 40-        _    -> error ("internal error: macSSL called with " ++ show alg)+        _ -> error ("internal error: macSSL called with " ++ show alg)     f = hash alg -hmac :: Hash -> HMAC-hmac alg secret msg = f $! B.append opad (f $! B.append ipad msg)-  where opad = B.map (xor 0x5c) k'-        ipad = B.map (xor 0x36) k'+hmac :: (ByteArray ba, ByteArrayAccess ba) => Hash -> ba -> ByteString -> ba+hmac alg secret msg = f $ BA.append opad (f $ BA.append ipad $ BA.convert msg)+  where+    opad = BA.map (0x5c `xor`) k'+    ipad = BA.map (0x36 `xor`) k' -        f = hash alg-        bl = hashBlockSize alg+    f = hash alg+    bl = hashBlockSize alg -        k' = B.append kt pad-          where kt  = if B.length secret > fromIntegral bl then f secret else secret-                pad = B.replicate (fromIntegral bl - B.length kt) 0+    k' = BA.append kt pad+      where+        kt = if BA.length secret > fromIntegral bl then f secret else secret+        pad = BA.replicate (fromIntegral bl - BA.length kt) 0 -hmacIter :: HMAC -> ByteString -> ByteString -> ByteString -> Int -> [ByteString]+hmacIter+    :: HMAC -> Secret -> ByteString -> ByteString -> Int -> [Secret] hmacIter f secret seed aprev len =-    let an = f secret aprev in-    let out = f secret (B.concat [an, seed]) in-    let digestsize = fromIntegral $ B.length out in-    if digestsize >= len-        then [ B.take (fromIntegral len) out ]-        else out : hmacIter f secret seed an (len - digestsize)+    let an = f secret aprev+     in let out = f secret (BA.concat [an, BA.convert seed])+         in let digestsize = BA.length out+             in if digestsize >= len+                    then [BA.take (fromIntegral len) out]+                    else out : hmacIter f secret seed (BA.convert an) (len - digestsize) -prf_SHA1 :: ByteString -> ByteString -> Int -> ByteString-prf_SHA1 secret seed len = B.concat $ hmacIter (hmac SHA1) secret seed seed len+type PRF = Secret -> ByteString -> Int -> Secret -prf_MD5 :: ByteString -> ByteString -> Int -> ByteString-prf_MD5 secret seed len = B.concat $ hmacIter (hmac MD5) secret seed seed len+prf_SHA1 :: PRF+prf_SHA1 secret seed len = BA.concat $ hmacIter (hmac SHA1) secret seed seed len -prf_MD5SHA1 :: ByteString -> ByteString -> Int -> ByteString+prf_MD5 :: PRF+prf_MD5 secret seed len = BA.concat $ hmacIter (hmac MD5) secret seed seed len++prf_MD5SHA1 :: PRF prf_MD5SHA1 secret seed len =-    B.pack $ B.zipWith xor (prf_MD5 s1 seed len) (prf_SHA1 s2 seed len)-  where slen  = B.length secret-        s1    = B.take (slen `div` 2 + slen `mod` 2) secret-        s2    = B.drop (slen `div` 2) secret+    BA.xor (prf_MD5 s1 seed len) (prf_SHA1 s2 seed len)+  where+    slen = BA.length secret+    s1 = BA.take (slen `div` 2 + slen `mod` 2) secret+    s2 = BA.drop (slen `div` 2) secret -prf_SHA256 :: ByteString -> ByteString -> Int -> ByteString-prf_SHA256 secret seed len = B.concat $ hmacIter (hmac SHA256) secret seed seed len+prf_SHA256 :: PRF+prf_SHA256 secret seed len = BA.concat $ hmacIter (hmac SHA256) secret seed seed len  -- | For now we ignore the version, but perhaps some day the PRF will depend -- not only on the cipher PRF algorithm, but also on the protocol version.-prf_TLS :: Version -> Hash -> ByteString -> ByteString -> Int -> ByteString+prf_TLS :: Version -> Hash -> PRF prf_TLS _ halg secret seed len =-    B.concat $ hmacIter (hmac halg) secret seed seed len+    BA.concat $ hmacIter (hmac halg) secret seed seed len
Network/TLS/Measurement.hs view
@@ -1,46 +1,44 @@--- |--- Module      : Network.TLS.Measurement--- License     : BSD-style--- Maintainer  : Vincent Hanquez <vincent@snarc.org>--- Stability   : experimental--- Portability : unknown----module Network.TLS.Measurement-        ( Measurement(..)-        , newMeasurement-        , addBytesReceived-        , addBytesSent-        , resetBytesCounters-        , incrementNbHandshakes-        ) where+module Network.TLS.Measurement (+    Measurement (..),+    newMeasurement,+    addBytesReceived,+    addBytesSent,+    resetBytesCounters,+    incrementNbHandshakes,+) where  import Network.TLS.Imports  -- | record some data about this connection. data Measurement = Measurement-        { nbHandshakes  :: !Word32 -- ^ number of handshakes on this context-        , bytesReceived :: !Word32 -- ^ bytes received since last handshake-        , bytesSent     :: !Word32 -- ^ bytes sent since last handshake-        } deriving (Show,Eq)+    { nbHandshakes :: Word32+    -- ^ number of handshakes on this context+    , bytesReceived :: Word32+    -- ^ bytes received since last handshake+    , bytesSent :: Word32+    -- ^ bytes sent since last handshake+    }+    deriving (Show, Eq)  newMeasurement :: Measurement-newMeasurement = Measurement-        { nbHandshakes  = 0+newMeasurement =+    Measurement+        { nbHandshakes = 0         , bytesReceived = 0-        , bytesSent     = 0+        , bytesSent = 0         }  addBytesReceived :: Int -> Measurement -> Measurement addBytesReceived sz measure =-        measure { bytesReceived = bytesReceived measure + fromIntegral sz }+    measure{bytesReceived = bytesReceived measure + fromIntegral sz}  addBytesSent :: Int -> Measurement -> Measurement addBytesSent sz measure =-        measure { bytesSent = bytesSent measure + fromIntegral sz }+    measure{bytesSent = bytesSent measure + fromIntegral sz}  resetBytesCounters :: Measurement -> Measurement-resetBytesCounters measure = measure { bytesReceived = 0, bytesSent = 0 }+resetBytesCounters measure = measure{bytesReceived = 0, bytesSent = 0}  incrementNbHandshakes :: Measurement -> Measurement incrementNbHandshakes measure =-        measure { nbHandshakes = nbHandshakes measure + 1 }+    measure{nbHandshakes = nbHandshakes measure + 1}
Network/TLS/Packet.hs view
@@ -1,289 +1,297 @@ {-# LANGUAGE OverloadedStrings #-}--- |--- Module      : Network.TLS.Packet--- License     : BSD-style--- Maintainer  : Vincent Hanquez <vincent@snarc.org>--- Stability   : experimental--- Portability : unknown------ the Packet module contains everything necessary to serialize and deserialize things--- with only explicit parameters, no TLS state is involved here.----module Network.TLS.Packet-    (+{-# LANGUAGE RecordWildCards #-}++-- | The Packet module contains everything necessary to serialize and+--  deserialize things with only explicit parameters, no TLS state is+--  involved here.+module Network.TLS.Packet (     -- * params for encoding and decoding-      CurrentParams(..)+    CurrentParams (..),+     -- * marshall functions for header messages-    , decodeHeader-    , decodeDeprecatedHeaderLength-    , decodeDeprecatedHeader-    , encodeHeader-    , encodeHeaderNoVer -- use for SSL3+    decodeHeader,+    encodeHeader,      -- * marshall functions for alert messages-    , decodeAlert-    , decodeAlerts-    , encodeAlerts+    decodeAlert,+    decodeAlerts,+    encodeAlerts,      -- * marshall functions for handshake messages-    , decodeHandshakeRecord-    , decodeHandshake-    , decodeDeprecatedHandshake-    , encodeHandshake-    , encodeHandshakes-    , encodeHandshakeHeader-    , encodeHandshakeContent+    decodeHandshakeRecord,+    decodeHandshake,+    encodeHandshake,+    encodeHandshake',+    encodeCertificate,+    decodeClientHello',      -- * marshall functions for change cipher spec message-    , decodeChangeCipherSpec-    , encodeChangeCipherSpec--    , decodePreMasterSecret-    , encodePreMasterSecret-    , encodeSignedDHParams-    , encodeSignedECDHParams--    , decodeReallyServerKeyXchgAlgorithmData+    decodeChangeCipherSpec,+    encodeChangeCipherSpec,+    decodePreMainSecret,+    encodePreMainSecret,+    encodeSignedDHParams,+    encodeSignedECDHParams,+    decodeReallyServerKeyXchgAlgorithmData,      -- * generate things for packet content-    , generateMasterSecret-    , generateKeyBlock-    , generateClientFinished-    , generateServerFinished--    , generateCertificateVerify_SSL-    , generateCertificateVerify_SSL_DSS+    generateMainSecret,+    generateExtendedMainSecret,+    generateKeyBlock,      -- * for extensions parsing-    , getSignatureHashAlgorithm-    , putSignatureHashAlgorithm-    ) where+    getSignatureHashAlgorithm,+    putSignatureHashAlgorithm,+    getBinaryVersion,+    putBinaryVersion,+    getClientRandom32,+    putClientRandom32,+    getServerRandom32,+    putServerRandom32,+    getExtensions,+    putExtension,+    getSession,+    putSession,+    putDNames,+    getDNames,+    getHandshakeType, +    -- * PRF+    PRF,+    getPRF,+) where++import Data.ByteArray (ByteArrayAccess, convert)+import qualified Data.ByteString as B+import Data.X509 (+    CertificateChain,+    CertificateChainRaw (..),+    decodeCertificateChain,+    encodeCertificateChain,+ )++import Network.TLS.Crypto import Network.TLS.Imports+import Network.TLS.MAC import Network.TLS.Struct+import Network.TLS.Types+import Network.TLS.Util.ASN1 import Network.TLS.Wire-import Network.TLS.Cap-import Data.ASN1.Types (fromASN1, toASN1)-import Data.ASN1.Encoding (decodeASN1', encodeASN1')-import Data.ASN1.BinaryEncoding (DER(..))-import Data.X509 (CertificateChainRaw(..), encodeCertificateChain, decodeCertificateChain)-import Network.TLS.Crypto-import Network.TLS.MAC-import Network.TLS.Cipher (CipherKeyExchangeType(..), Cipher(..))-import qualified Data.ByteString as B-import qualified Data.ByteString.Char8 as BC-import           Data.ByteArray (ByteArrayAccess)-import qualified Data.ByteArray as B (convert) +----------------------------------------------------------------+-- Header+ data CurrentParams = CurrentParams-    { cParamsVersion     :: Version                     -- ^ current protocol version-    , cParamsKeyXchgType :: Maybe CipherKeyExchangeType -- ^ current key exchange type-    } deriving (Show,Eq)+    { cParamsVersion :: Version+    -- ^ current protocol version+    , cParamsKeyXchgType :: Maybe CipherKeyExchangeType+    -- ^ current key exchange type+    }+    deriving (Show, Eq) -{- marshall helpers -}-getVersion :: Get Version-getVersion = do-    major <- getWord8-    minor <- getWord8-    case verOfNum (major, minor) of-        Nothing -> fail ("invalid version : " ++ show major ++ "," ++ show minor)-        Just v  -> return v+-- marshall helpers+getBinaryVersion :: Get Version+getBinaryVersion = Version <$> getWord16 -putVersion :: Version -> Put-putVersion ver = putWord8 major >> putWord8 minor-  where (major, minor) = numericalVer ver+putBinaryVersion :: Version -> Put+putBinaryVersion (Version ver) = putWord16 ver  getHeaderType :: Get ProtocolType-getHeaderType = do-    ty <- getWord8-    case valToType ty of-        Nothing -> fail ("invalid header type: " ++ show ty)-        Just t  -> return t+getHeaderType = ProtocolType <$> getWord8  putHeaderType :: ProtocolType -> Put-putHeaderType = putWord8 . valOfType+putHeaderType (ProtocolType pt) = putWord8 pt  getHandshakeType :: Get HandshakeType-getHandshakeType = do-    ty <- getWord8-    case valToType ty of-        Nothing -> fail ("invalid handshake type: " ++ show ty)-        Just t  -> return t+getHandshakeType = HandshakeType <$> getWord8 -{-- - decode and encode headers- -}+-- decode and encode headers decodeHeader :: ByteString -> Either TLSError Header-decodeHeader = runGetErr "header" $ liftM3 Header getHeaderType getVersion getWord16+decodeHeader =+    runGetErr "header" $ Header <$> getHeaderType <*> getBinaryVersion <*> getWord16 -decodeDeprecatedHeaderLength :: ByteString -> Either TLSError Word16-decodeDeprecatedHeaderLength = runGetErr "deprecatedheaderlength" $ subtract 0x8000 <$> getWord16+encodeHeader :: Header -> ByteString+encodeHeader (Header pt ver len) = runPut (putHeaderType pt >> putBinaryVersion ver >> putWord16 len) -decodeDeprecatedHeader :: Word16 -> ByteString -> Either TLSError Header-decodeDeprecatedHeader size =-    runGetErr "deprecatedheader" $ do-        1 <- getWord8-        version <- getVersion-        return $ Header ProtocolType_DeprecatedHandshake version size+-- FIXME check len <= 2^14 -encodeHeader :: Header -> ByteString-encodeHeader (Header pt ver len) = runPut (putHeaderType pt >> putVersion ver >> putWord16 len)-        {- FIXME check len <= 2^14 -}+------------------------------------------------------------+-- CCS -encodeHeaderNoVer :: Header -> ByteString-encodeHeaderNoVer (Header pt _ len) = runPut (putHeaderType pt >> putWord16 len)-        {- FIXME check len <= 2^14 -}+decodeChangeCipherSpec :: ByteString -> Either TLSError ()+decodeChangeCipherSpec = runGetErr "changecipherspec" $ do+    x <- getWord8+    when (x /= 1) $ fail "unknown change cipher spec content"+    len <- remaining+    when (len /= 0) $ fail "the length of CSS must be 1" -{-- - decode and encode ALERT- -}+encodeChangeCipherSpec :: ByteString+encodeChangeCipherSpec = runPut (putWord8 1)++----------------------------------------------------------------+-- Alert+ decodeAlert :: Get (AlertLevel, AlertDescription) decodeAlert = do-    al <- getWord8-    ad <- getWord8-    case (valToType al, valToType ad) of-        (Just a, Just d) -> return (a, d)-        (Nothing, _)     -> fail "cannot decode alert level"-        (_, Nothing)     -> fail "cannot decode alert description"+    al <- AlertLevel <$> getWord8+    ad <- AlertDescription <$> getWord8+    return (al, ad)  decodeAlerts :: ByteString -> Either TLSError [(AlertLevel, AlertDescription)] decodeAlerts = runGetErr "alerts" loop-  where loop = do-            r <- remaining-            if r == 0-                then return []-                else liftM2 (:) decodeAlert loop+  where+    loop = do+        r <- remaining+        if r == 0+            then return []+            else (:) <$> decodeAlert <*> loop  encodeAlerts :: [(AlertLevel, AlertDescription)] -> ByteString encodeAlerts l = runPut $ mapM_ encodeAlert l-  where encodeAlert (al, ad) = putWord8 (valOfType al) >> putWord8 (valOfType ad)+  where+    encodeAlert (al, ad) = putWord8 (fromAlertLevel al) >> putWord8 (fromAlertDescription ad) -{- decode and encode HANDSHAKE -}+----------------------------------------------------------------+-- decode HANDSHAKE+ decodeHandshakeRecord :: ByteString -> GetResult (HandshakeType, ByteString) decodeHandshakeRecord = runGet "handshake-record" $ do-    ty      <- getHandshakeType+    ty <- getHandshakeType     content <- getOpaque24     return (ty, content) -decodeHandshake :: CurrentParams -> HandshakeType -> ByteString -> Either TLSError Handshake+{- FOURMOLU_DISABLE -}+decodeHandshake+    :: CurrentParams -> HandshakeType -> ByteString -> Either TLSError Handshake decodeHandshake cp ty = runGetErr ("handshake[" ++ show ty ++ "]") $ case ty of-    HandshakeType_HelloRequest    -> decodeHelloRequest-    HandshakeType_ClientHello     -> decodeClientHello-    HandshakeType_ServerHello     -> decodeServerHello-    HandshakeType_Certificate     -> decodeCertificates-    HandshakeType_ServerKeyXchg   -> decodeServerKeyXchg cp-    HandshakeType_CertRequest     -> decodeCertRequest cp-    HandshakeType_ServerHelloDone -> decodeServerHelloDone-    HandshakeType_CertVerify      -> decodeCertVerify cp-    HandshakeType_ClientKeyXchg   -> decodeClientKeyXchg cp-    HandshakeType_Finished        -> decodeFinished--decodeDeprecatedHandshake :: ByteString -> Either TLSError Handshake-decodeDeprecatedHandshake b = runGetErr "deprecatedhandshake" getDeprecated b-  where getDeprecated = do-            1 <- getWord8-            ver <- getVersion-            cipherSpecLen <- fromEnum <$> getWord16-            sessionIdLen <- fromEnum <$> getWord16-            challengeLen <- fromEnum <$> getWord16-            ciphers <- getCipherSpec cipherSpecLen-            session <- getSessionId sessionIdLen-            random <- getChallenge challengeLen-            let compressions = [0]-            return $ ClientHello ver random session ciphers compressions [] (Just b)-        getCipherSpec len | len < 3 = return []-        getCipherSpec len = do-            [c0,c1,c2] <- map fromEnum <$> replicateM 3 getWord8-            ([ toEnum $ c1 * 0x100 + c2 | c0 == 0 ] ++) <$> getCipherSpec (len - 3)-        getSessionId 0 = return $ Session Nothing-        getSessionId len = Session . Just <$> getBytes len-        getChallenge len | 32 < len = getBytes (len - 32) >> getChallenge 32-        getChallenge len = ClientRandom . B.append (B.replicate (32 - len) 0) <$> getBytes len+    HandshakeType_HelloRequest     -> decodeHelloRequest+    HandshakeType_ClientHello      -> decodeClientHello False+    HandshakeType_ServerHello      -> decodeServerHello+    HandshakeType_NewSessionTicket -> decodeNewSessionTicket+    HandshakeType_Certificate      -> decodeCertificate+    HandshakeType_ServerKeyXchg    -> decodeServerKeyXchg cp+    HandshakeType_CertRequest      -> decodeCertRequest cp+    HandshakeType_ServerHelloDone  -> decodeServerHelloDone+    HandshakeType_CertVerify       -> decodeCertVerify cp+    HandshakeType_ClientKeyXchg    -> decodeClientKeyXchg cp+    HandshakeType_Finished         -> decodeFinished+    x -> fail $ "Unsupported HandshakeType " ++ show x+{- FOURMOLU_ENABLE -}  decodeHelloRequest :: Get Handshake decodeHelloRequest = return HelloRequest -decodeClientHello :: Get Handshake-decodeClientHello = do-    ver          <- getVersion-    random       <- getClientRandom32-    session      <- getSession-    ciphers      <- getWords16+decodeClientHello' :: ByteString -> Either TLSError Handshake+decodeClientHello' = runGetErr "decodeClientHello'" $ decodeClientHello True++decodeClientHello :: Bool -> Get Handshake+decodeClientHello inner = do+    ver <- getBinaryVersion+    random <- getClientRandom32+    session <- getSession+    ciphers <- map CipherId <$> getWords16     compressions <- getWords8-    r            <- remaining-    exts <- if hasHelloExtensions ver && r > 0-            then fromIntegral <$> getWord16 >>= getExtensions+    r <- remaining+    exts <-+        if r > 0+            then getWord16 >>= getExtensions . fromIntegral             else return []-    return $ ClientHello ver random session ciphers compressions exts Nothing+    r1 <- remaining+    if inner+        then+            checkAndSkip r1+        else when (r1 /= 0) $ fail "Client hello has garbage"+    return $+        ClientHello $+            CH+                { chVersion = ver+                , chRandom = random+                , chSession = session+                , chCiphers = ciphers+                , chComps = compressions+                , chExtensions = exts+                }+  where+    checkAndSkip 0 = return ()+    checkAndSkip r = do+        zero <- getWord8+        when (zero /= 0) $ fail "Inner client hello has garbage"+        checkAndSkip (r - 1)  decodeServerHello :: Get Handshake decodeServerHello = do-    ver           <- getVersion-    random        <- getServerRandom32-    session       <- getSession-    cipherid      <- getWord16+    ver <- getBinaryVersion+    random <- getServerRandom32+    session <- getSession+    cipherid <- CipherId <$> getWord16     compressionid <- getWord8-    r             <- remaining-    exts <- if hasHelloExtensions ver && r > 0-            then fromIntegral <$> getWord16 >>= getExtensions+    r <- remaining+    exts <-+        if r > 0+            then getWord16 >>= getExtensions . fromIntegral             else return []-    return $ ServerHello ver random session cipherid compressionid exts+    return $+        ServerHello $+            SH+                { shVersion = ver+                , shRandom = random+                , shSession = session+                , shCipher = cipherid+                , shComp = compressionid+                , shExtensions = exts+                } -decodeServerHelloDone :: Get Handshake-decodeServerHelloDone = return ServerHelloDone+decodeNewSessionTicket :: Get Handshake+decodeNewSessionTicket = NewSessionTicket <$> getWord32 <*> getOpaque16 -decodeCertificates :: Get Handshake-decodeCertificates = do-    certsRaw <- CertificateChainRaw <$> (getWord24 >>= \len -> getList (fromIntegral len) getCertRaw)+decodeCertificate :: Get Handshake+decodeCertificate = do+    certsRaw <-+        CertificateChainRaw+            <$> (getWord24 >>= \len -> getList (fromIntegral len) getCertRaw)     case decodeCertificateChain certsRaw of         Left (i, s) -> fail ("error certificate parsing " ++ show i ++ ":" ++ s)-        Right cc    -> return $ Certificates cc-  where getCertRaw = getOpaque24 >>= \cert -> return (3 + B.length cert, cert)--decodeFinished :: Get Handshake-decodeFinished = Finished <$> (remaining >>= getBytes)--decodeCertRequest :: CurrentParams -> Get Handshake-decodeCertRequest cp = do-    mcertTypes <- map (valToType . fromIntegral) <$> getWords8-    certTypes <- mapM (fromJustM "decodeCertRequest") mcertTypes-    sigHashAlgs <- if cParamsVersion cp >= TLS12-                       then Just <$> (getWord16 >>= getSignatureHashAlgorithms)-                       else return Nothing-    dNameLen <- getWord16-    -- FIXME: Decide whether to remove this check completely or to make it an option.-    -- when (cParamsVersion cp < TLS12 && dNameLen < 3) $ fail "certrequest distinguishname not of the correct size"-    dNames <- getList (fromIntegral dNameLen) getDName-    return $ CertRequest certTypes sigHashAlgs dNames-  where getSignatureHashAlgorithms len = getList (fromIntegral len) (getSignatureHashAlgorithm >>= \sh -> return (2, sh))-        getDName = do-            dName <- getOpaque16-            when (B.length dName == 0) $ fail "certrequest: invalid DN length"-            dn <- case decodeASN1' DER dName of-                    Left e      -> fail ("cert request decoding DistinguishedName ASN1 failed: " ++ show e)-                    Right asn1s -> case fromASN1 asn1s of-                                        Left e      -> fail ("cert request parsing DistinguishedName ASN1 failed: " ++ show e)-                                        Right (d,_) -> return d-            return (2 + B.length dName, dn)+        Right cc -> return $ Certificate $ CertificateChain_ cc+  where+    getCertRaw = getOpaque24 >>= \cert -> return (3 + B.length cert, cert) -decodeCertVerify :: CurrentParams -> Get Handshake-decodeCertVerify cp = CertVerify <$> getDigitallySigned (cParamsVersion cp)+---- -decodeClientKeyXchg :: CurrentParams -> Get Handshake-decodeClientKeyXchg cp = -- case  ClientKeyXchg <$> (remaining >>= getBytes)+decodeServerKeyXchg :: CurrentParams -> Get Handshake+decodeServerKeyXchg cp =     case cParamsKeyXchgType cp of-        Nothing  -> error "no client key exchange type"-        Just cke -> ClientKeyXchg <$> parseCKE cke-  where parseCKE CipherKeyExchange_RSA     = CKX_RSA <$> (remaining >>= getBytes)-        parseCKE CipherKeyExchange_DHE_RSA = parseClientDHPublic-        parseCKE CipherKeyExchange_DHE_DSS = parseClientDHPublic-        parseCKE CipherKeyExchange_DH_Anon = parseClientDHPublic-        parseCKE CipherKeyExchange_ECDHE_RSA   = parseClientECDHPublic-        parseCKE CipherKeyExchange_ECDHE_ECDSA = parseClientECDHPublic-        parseCKE _                         = error "unsupported client key exchange type"-        parseClientDHPublic = CKX_DH . dhPublic <$> getInteger16-        parseClientECDHPublic = CKX_ECDH <$> getOpaque8+        Just cke -> ServerKeyXchg <$> decodeServerKeyXchgAlgorithmData (cParamsVersion cp) cke+        Nothing -> ServerKeyXchg . SKX_Unparsed <$> (remaining >>= getBytes) +decodeServerKeyXchgAlgorithmData+    :: Version+    -> CipherKeyExchangeType+    -> Get ServerKeyXchgAlgorithmData+decodeServerKeyXchgAlgorithmData ver cke = toCKE+  where+    toCKE = case cke of+        CipherKeyExchange_RSA -> SKX_RSA . Just <$> decodeServerKeyXchg_RSA+        CipherKeyExchange_DH_Anon -> SKX_DH_Anon <$> decodeServerKeyXchg_DH+        CipherKeyExchange_DHE_RSA -> do+            dhparams <- getServerDHParams+            signature <- getDigitallySigned ver+            return $ SKX_DHE_RSA dhparams signature+        CipherKeyExchange_DHE_DSA -> do+            dhparams <- getServerDHParams+            signature <- getDigitallySigned ver+            return $ SKX_DHE_DSA dhparams signature+        CipherKeyExchange_ECDHE_RSA -> do+            ecdhparams <- getServerECDHParams+            signature <- getDigitallySigned ver+            return $ SKX_ECDHE_RSA ecdhparams signature+        CipherKeyExchange_ECDHE_ECDSA -> do+            ecdhparams <- getServerECDHParams+            signature <- getDigitallySigned ver+            return $ SKX_ECDHE_ECDSA ecdhparams signature+        _ -> do+            bs <- remaining >>= getBytes+            return $ SKX_Unknown bs+ decodeServerKeyXchg_DH :: Get ServerDHParams decodeServerKeyXchg_DH = getServerDHParams @@ -291,117 +299,140 @@ -- decodeServerKeyXchg_ECDH :: Get ServerECDHParams  decodeServerKeyXchg_RSA :: Get ServerRSAParams-decodeServerKeyXchg_RSA = ServerRSAParams <$> getInteger16 -- modulus-                                          <*> getInteger16 -- exponent+decodeServerKeyXchg_RSA =+    ServerRSAParams+        <$> getInteger16 -- modulus+        <*> getInteger16 -- exponent -decodeServerKeyXchgAlgorithmData :: Version-                                 -> CipherKeyExchangeType-                                 -> Get ServerKeyXchgAlgorithmData-decodeServerKeyXchgAlgorithmData ver cke = toCKE-  where toCKE = case cke of-            CipherKeyExchange_RSA     -> SKX_RSA . Just <$> decodeServerKeyXchg_RSA-            CipherKeyExchange_DH_Anon -> SKX_DH_Anon <$> decodeServerKeyXchg_DH-            CipherKeyExchange_DHE_RSA -> do-                dhparams  <- getServerDHParams-                signature <- getDigitallySigned ver-                return $ SKX_DHE_RSA dhparams signature-            CipherKeyExchange_DHE_DSS -> do-                dhparams  <- getServerDHParams-                signature <- getDigitallySigned ver-                return $ SKX_DHE_DSS dhparams signature-            CipherKeyExchange_ECDHE_RSA -> do-                ecdhparams  <- getServerECDHParams-                signature <- getDigitallySigned ver-                return $ SKX_ECDHE_RSA ecdhparams signature-            CipherKeyExchange_ECDHE_ECDSA -> do-                ecdhparams  <- getServerECDHParams-                signature <- getDigitallySigned ver-                return $ SKX_ECDHE_ECDSA ecdhparams signature-            _ -> do-                bs <- remaining >>= getBytes-                return $ SKX_Unknown bs+---- -decodeServerKeyXchg :: CurrentParams -> Get Handshake-decodeServerKeyXchg cp =-    case cParamsKeyXchgType cp of-        Just cke -> ServerKeyXchg <$> decodeServerKeyXchgAlgorithmData (cParamsVersion cp) cke-        Nothing  -> ServerKeyXchg . SKX_Unparsed <$> (remaining >>= getBytes)+decodeCertRequest :: CurrentParams -> Get Handshake+decodeCertRequest _cp = do+    certTypes <- map CertificateType <$> getWords8+    sigHashAlgs <- getWord16 >>= getSignatureHashAlgorithms+    CertRequest certTypes sigHashAlgs <$> getDNames+  where+    getSignatureHashAlgorithms len =+        getList (fromIntegral len) (getSignatureHashAlgorithm >>= \sh -> return (2, sh)) -encodeHandshake :: Handshake -> ByteString-encodeHandshake o =-    let content = runPut $ encodeHandshakeContent o in-    let len = fromIntegral $ B.length content in-    let header = case o of-                    ClientHello _ _ _ _ _ _ (Just _) -> "" -- SSLv2 ClientHello message-                    _ -> runPut $ encodeHandshakeHeader (typeOfHandshake o) len in-    B.concat [ header, content ]+---- -encodeHandshakes :: [Handshake] -> ByteString-encodeHandshakes hss = B.concat $ map encodeHandshake hss+decodeServerHelloDone :: Get Handshake+decodeServerHelloDone = return ServerHelloDone -encodeHandshakeHeader :: HandshakeType -> Int -> Put-encodeHandshakeHeader ty len = putWord8 (valOfType ty) >> putWord24 len+decodeCertVerify :: CurrentParams -> Get Handshake+decodeCertVerify cp = CertVerify <$> getDigitallySigned (cParamsVersion cp) -encodeHandshakeContent :: Handshake -> Put+decodeClientKeyXchg :: CurrentParams -> Get Handshake+decodeClientKeyXchg cp =+    -- case  ClientKeyXchg <$> (remaining >>= getBytes)+    case cParamsKeyXchgType cp of+        Nothing -> fail "no client key exchange type"+        Just cke -> ClientKeyXchg <$> parseCKE cke+  where+    parseCKE CipherKeyExchange_RSA = CKX_RSA <$> (remaining >>= getBytes)+    parseCKE CipherKeyExchange_DHE_RSA = parseClientDHPublic+    parseCKE CipherKeyExchange_DHE_DSA = parseClientDHPublic+    parseCKE CipherKeyExchange_DH_Anon = parseClientDHPublic+    parseCKE CipherKeyExchange_ECDHE_RSA = parseClientECDHPublic+    parseCKE CipherKeyExchange_ECDHE_ECDSA = parseClientECDHPublic+    parseCKE _ = fail "unsupported client key exchange type"+    parseClientDHPublic = CKX_DH . dhPublic <$> getInteger16+    parseClientECDHPublic = CKX_ECDH <$> getOpaque8 -encodeHandshakeContent (ClientHello _ _ _ _ _ _ (Just deprecated)) = do-    putBytes deprecated-encodeHandshakeContent (ClientHello version random session cipherIDs compressionIDs exts Nothing) = do-    putVersion version-    putClientRandom32 random-    putSession session-    putWords16 cipherIDs-    putWords8 compressionIDs-    putExtensions exts-    return ()+decodeFinished :: Get Handshake+decodeFinished = Finished . VerifyData <$> (remaining >>= getBytes) -encodeHandshakeContent (ServerHello version random session cipherid compressionID exts) =-    putVersion version >> putServerRandom32 random >> putSession session-                       >> putWord16 cipherid >> putWord8 compressionID-                       >> putExtensions exts >> return ()+----------------------------------------------------------------+-- encode HANDSHAKE -encodeHandshakeContent (Certificates cc) = putOpaque24 (runPut $ mapM_ putOpaque24 certs)-  where (CertificateChainRaw certs) = encodeCertificateChain cc+encodeHandshake :: Handshake -> ByteString+encodeHandshake o =+    let content = encodeHandshake' o+     in let len = B.length content+         in let header = runPut $ encodeHandshakeHeader (typeOfHandshake o) len+             in B.concat [header, content] -encodeHandshakeContent (ClientKeyXchg ckx) = do-    case ckx of-        CKX_RSA encryptedPreMaster -> putBytes encryptedPreMaster-        CKX_DH clientDHPublic      -> putInteger16 $ dhUnwrapPublic clientDHPublic-        CKX_ECDH bytes             -> putOpaque8 bytes+encodeHandshakeHeader :: HandshakeType -> Int -> Put+encodeHandshakeHeader ty len = putWord8 (fromHandshakeType ty) >> putWord24 len -encodeHandshakeContent (ServerKeyXchg skg) =+encodeHandshake' :: Handshake -> ByteString+encodeHandshake' HelloRequest = ""+encodeHandshake' (ClientHello CH{..}) = runPut $ do+    putBinaryVersion chVersion+    putClientRandom32 chRandom+    putSession chSession+    putWords16 $ map fromCipherId chCiphers+    putWords8 chComps+    putExtensions chExtensions+encodeHandshake' (ServerHello SH{..}) = runPut $ do+    putBinaryVersion shVersion+    putServerRandom32 shRandom+    putSession shSession+    putWord16 $ fromCipherId shCipher+    putWord8 shComp+    putExtensions shExtensions+encodeHandshake' (NewSessionTicket life ticket) = runPut $ do+    putWord32 life+    putOpaque16 ticket+encodeHandshake' (Certificate (CertificateChain_ cc)) = encodeCertificate cc+encodeHandshake' (ServerKeyXchg skg) = runPut $     case skg of-        SKX_RSA _              -> error "encodeHandshakeContent SKX_RSA not implemented"-        SKX_DH_Anon params     -> putServerDHParams params+        SKX_RSA _ -> error "encodeHandshake' SKX_RSA not implemented"+        SKX_DH_Anon params -> putServerDHParams params         SKX_DHE_RSA params sig -> putServerDHParams params >> putDigitallySigned sig-        SKX_DHE_DSS params sig -> putServerDHParams params >> putDigitallySigned sig+        SKX_DHE_DSA params sig -> putServerDHParams params >> putDigitallySigned sig         SKX_ECDHE_RSA params sig -> putServerECDHParams params >> putDigitallySigned sig         SKX_ECDHE_ECDSA params sig -> putServerECDHParams params >> putDigitallySigned sig-        SKX_Unparsed bytes     -> putBytes bytes-        _                      -> error ("encodeHandshakeContent: cannot handle: " ++ show skg)--encodeHandshakeContent HelloRequest    = return ()-encodeHandshakeContent ServerHelloDone = return ()+        SKX_Unparsed bytes -> putBytes bytes+        _ -> error ("encodeHandshake': cannot handle: " ++ show skg)+encodeHandshake' (CertRequest certTypes sigAlgs certAuthorities) = runPut $ do+    putWords8 (map fromCertificateType certTypes)+    putWords16 $+        map+            ( \(HashAlgorithm x, SignatureAlgorithm y) -> fromIntegral x * 256 + fromIntegral y+            )+            sigAlgs+    putDNames certAuthorities+encodeHandshake' ServerHelloDone = ""+encodeHandshake' (CertVerify digitallySigned) = runPut $ putDigitallySigned digitallySigned+encodeHandshake' (ClientKeyXchg ckx) = runPut $ do+    case ckx of+        CKX_RSA encryptedPreMain -> putBytes encryptedPreMain+        CKX_DH clientDHPublic -> putInteger16 $ dhUnwrapPublic clientDHPublic+        CKX_ECDH bytes -> putOpaque8 bytes+encodeHandshake' (Finished (VerifyData opaque)) = runPut $ putBytes opaque -encodeHandshakeContent (CertRequest certTypes sigAlgs certAuthorities) = do-    putWords8 (map valOfType certTypes)-    case sigAlgs of-        Nothing -> return ()-        Just l  -> putWords16 $ map (\(x,y) -> fromIntegral (valOfType x) * 256 + fromIntegral (valOfType y)) l-    encodeCertAuthorities certAuthorities-  where -- Convert a distinguished name to its DER encoding.-        encodeCA dn = return $ encodeASN1' DER (toASN1 dn []) --B.concat $ L.toChunks $ encodeDN dn+------------------------------------------------------------+-- CA distinguished names -        -- Encode a list of distinguished names.-        encodeCertAuthorities certAuths = do-            enc <- mapM encodeCA certAuths-            let totLength = sum $ map ((+) 2 . B.length) enc-            putWord16 (fromIntegral totLength)-            mapM_ (\ b -> putWord16 (fromIntegral (B.length b)) >> putBytes b) enc+-- | Decode a list CA distinguished names+getDNames :: Get [DistinguishedName]+getDNames = do+    dNameLen <- getWord16+    -- FIXME: Decide whether to remove this check completely or to make it an option.+    -- when (cParamsVersion cp < TLS12 && dNameLen < 3) $ fail "certrequest distinguishname not of the correct size"+    getList (fromIntegral dNameLen) getDName+  where+    getDName = do+        dName <- getOpaque16+        when (B.length dName == 0) $ fail "certrequest: invalid DN length"+        dn <-+            either fail return $ decodeASN1Object "cert request DistinguishedName" dName+        return (2 + B.length dName, dn) -encodeHandshakeContent (CertVerify digitallySigned) = putDigitallySigned digitallySigned+-- | Encode a list of distinguished names.+putDNames :: [DistinguishedName] -> Put+putDNames dnames = do+    enc <- mapM encodeCA dnames+    let totLength = sum $ map ((+) 2 . B.length) enc+    putWord16 (fromIntegral totLength)+    mapM_ (\b -> putWord16 (fromIntegral (B.length b)) >> putBytes b) enc+  where+    -- Convert a distinguished name to its DER encoding.+    encodeCA dn = return $ encodeASN1Object dn -encodeHandshakeContent (Finished opaque) = putBytes opaque+------------------------------------------------------------  {- FIXME make sure it return error if not 32 available -} getRandom32 :: Get ByteString@@ -422,117 +453,107 @@ putServerRandom32 :: ServerRandom -> Put putServerRandom32 (ServerRandom r) = putRandom32 r +------------------------------------------------------------+ getSession :: Get Session getSession = do     len8 <- getWord8     case fromIntegral len8 of-        0   -> return $ Session Nothing-        len -> Session . Just <$> getBytes len+        0 -> return $ Session Nothing+        len+            | len > 32 -> fail "the length of session id must be <= 32"+            | otherwise -> Session . Just <$> getBytes len  putSession :: Session -> Put-putSession (Session Nothing)  = putWord8 0+putSession (Session Nothing) = putWord8 0 putSession (Session (Just s)) = putOpaque8 s +------------------------------------------------------------+ getExtensions :: Int -> Get [ExtensionRaw]-getExtensions 0   = return []+getExtensions 0 = return [] getExtensions len = do-    extty <- getWord16+    extty <- ExtensionID <$> getWord16     extdatalen <- getWord16     extdata <- getBytes $ fromIntegral extdatalen     extxs <- getExtensions (len - fromIntegral extdatalen - 4)     return $ ExtensionRaw extty extdata : extxs  putExtension :: ExtensionRaw -> Put-putExtension (ExtensionRaw ty l) = putWord16 ty >> putOpaque16 l+putExtension (ExtensionRaw (ExtensionID ty) l) = putWord16 ty >> putOpaque16 l  putExtensions :: [ExtensionRaw] -> Put putExtensions [] = return () putExtensions es = putOpaque16 (runPut $ mapM_ putExtension es) +------------------------------------------------------------+ getSignatureHashAlgorithm :: Get HashAndSignatureAlgorithm getSignatureHashAlgorithm = do-    h <- (valToType <$> getWord8) >>= fromJustM "getSignatureHashAlgorithm"-    s <- (valToType <$> getWord8) >>= fromJustM "getSignatureHashAlgorithm"-    return (h,s)+    h <- HashAlgorithm <$> getWord8+    s <- SignatureAlgorithm <$> getWord8+    return (h, s)  putSignatureHashAlgorithm :: HashAndSignatureAlgorithm -> Put-putSignatureHashAlgorithm (h,s) =-    putWord8 (valOfType h) >> putWord8 (valOfType s)+putSignatureHashAlgorithm (HashAlgorithm h, SignatureAlgorithm s) =+    putWord8 h >> putWord8 s +------------------------------------------------------------+ getServerDHParams :: Get ServerDHParams getServerDHParams = ServerDHParams <$> getBigNum16 <*> getBigNum16 <*> getBigNum16  putServerDHParams :: ServerDHParams -> Put-putServerDHParams (ServerDHParams p g y) = mapM_ putBigNum16 [p,g,y]+putServerDHParams (ServerDHParams p g y) = mapM_ putBigNum16 [p, g, y] +------------------------------------------------------------++-- RFC 4492 Section 5.4 Server Key Exchange getServerECDHParams :: Get ServerECDHParams getServerECDHParams = do     curveType <- getWord8     case curveType of-        3 -> do               -- ECParameters ECCurveType: curve name type-            mgrp <- toEnumSafe16 <$> getWord16  -- ECParameters NamedCurve-            case mgrp of-              Nothing -> error "getServerECDHParams: unknown group"-              Just grp -> do-                  mxy <- getOpaque8 -- ECPoint-                  case decodeGroupPublic grp mxy of-                    Left e       -> error $ "getServerECDHParams: " ++ show e-                    Right grppub -> return $ ServerECDHParams grp grppub-        _ ->-            error "getServerECDHParams: unknown type for ECDH Params"+        3 -> do+            -- ECParameters ECCurveType: curve name type+            grp <- Group <$> getWord16 -- ECParameters NamedCurve+            mxy <- getOpaque8 -- ECPoint+            case groupDecodePublicA grp mxy of+                Left e -> fail $ "getServerECDHParams: " ++ show e+                Right grppub -> return $ ServerECDHParams grp grppub+        _ -> fail "getServerECDHParams: unknown type for ECDH Params" +-- RFC 4492 Section 5.4 Server Key Exchange putServerECDHParams :: ServerECDHParams -> Put-putServerECDHParams (ServerECDHParams grp grppub) = do-    putWord8 3                            -- ECParameters ECCurveType-    putWord16 $ fromEnumSafe16 grp        -- ECParameters NamedCurve-    putOpaque8 $ encodeGroupPublic grppub -- ECPoint+putServerECDHParams (ServerECDHParams (Group grp) grppub) = do+    putWord8 3 -- ECParameters ECCurveType+    putWord16 grp -- ECParameters NamedCurve+    putOpaque8 $ groupEncodePublicA grppub -- ECPoint +------------------------------------------------------------+ getDigitallySigned :: Version -> Get DigitallySigned-getDigitallySigned ver-    | ver >= TLS12 = DigitallySigned <$> (Just <$> getSignatureHashAlgorithm)-                                     <*> getOpaque16-    | otherwise    = DigitallySigned Nothing <$> getOpaque16+getDigitallySigned _ver =+    DigitallySigned+        <$> getSignatureHashAlgorithm+        <*> getOpaque16  putDigitallySigned :: DigitallySigned -> Put-putDigitallySigned (DigitallySigned mhash sig) =-    maybe (return ()) putSignatureHashAlgorithm mhash >> putOpaque16 sig--{-- - decode and encode ALERT- -}--decodeChangeCipherSpec :: ByteString -> Either TLSError ()-decodeChangeCipherSpec = runGetErr "changecipherspec" $ do-    x <- getWord8-    when (x /= 1) (fail "unknown change cipher spec content")--encodeChangeCipherSpec :: ByteString-encodeChangeCipherSpec = runPut (putWord8 1)---- rsa pre master secret-decodePreMasterSecret :: ByteString -> Either TLSError (Version, ByteString)-decodePreMasterSecret = runGetErr "pre-master-secret" $ do-    liftM2 (,) getVersion (getBytes 46)+putDigitallySigned (DigitallySigned h sig) =+    putSignatureHashAlgorithm h >> putOpaque16 sig -encodePreMasterSecret :: Version -> ByteString -> ByteString-encodePreMasterSecret version bytes = runPut (putVersion version >> putBytes bytes)+------------------------------------------------------------ --- | in certain cases, we haven't manage to decode ServerKeyExchange properly,--- because the decoding was too eager and the cipher wasn't been set yet.--- we keep the Server Key Exchange in it unparsed format, and this function is--- able to really decode the server key xchange if it's unparsed.-decodeReallyServerKeyXchgAlgorithmData :: Version-                                       -> CipherKeyExchangeType-                                       -> ByteString-                                       -> Either TLSError ServerKeyXchgAlgorithmData-decodeReallyServerKeyXchgAlgorithmData ver cke =-    runGetErr "server-key-xchg-algorithm-data" (decodeServerKeyXchgAlgorithmData ver cke)+-- RSA pre-main secret+decodePreMainSecret :: ByteString -> Either TLSError (Version, ByteString)+decodePreMainSecret =+    runGetErr "pre-main-secret" $+        (,) <$> getBinaryVersion <*> getBytes 46 +encodePreMainSecret :: Version -> ByteString -> ByteString+encodePreMainSecret version bytes = runPut (putBinaryVersion version >> putBytes bytes) -{-- - generate things for packet content- -}-type PRF = ByteString -> ByteString -> Int -> ByteString+------------------------------------------------------------+-- generate things for packet content  -- | The TLS12 PRF is cipher specific, and some TLS12 algorithms use SHA384 -- instead of the default SHA256.@@ -542,108 +563,89 @@     | maybe True (< TLS12) (cipherMinVer ciph) = prf_SHA256     | otherwise = prf_TLS ver $ fromMaybe SHA256 $ cipherPRFHash ciph -generateMasterSecret_SSL :: ByteArrayAccess preMaster => preMaster -> ClientRandom -> ServerRandom -> ByteString-generateMasterSecret_SSL premasterSecret (ClientRandom c) (ServerRandom s) =-    B.concat $ map computeMD5 ["A","BB","CCC"]-  where computeMD5  label = hash MD5 $ B.concat [ B.convert premasterSecret, computeSHA1 label ]-        computeSHA1 label = hash SHA1 $ B.concat [ label, B.convert premasterSecret, c, s ]--generateMasterSecret_TLS :: ByteArrayAccess preMaster => PRF -> preMaster -> ClientRandom -> ServerRandom -> ByteString-generateMasterSecret_TLS prf premasterSecret (ClientRandom c) (ServerRandom s) =-    prf (B.convert premasterSecret) seed 48-  where seed = B.concat [ "master secret", c, s ]--generateMasterSecret :: ByteArrayAccess preMaster-                     => Version-                     -> Cipher-                     -> preMaster-                     -> ClientRandom-                     -> ServerRandom-                     -> ByteString-generateMasterSecret SSL2 _ = generateMasterSecret_SSL-generateMasterSecret SSL3 _ = generateMasterSecret_SSL-generateMasterSecret v    c = generateMasterSecret_TLS $ getPRF v c--generateKeyBlock_TLS :: PRF -> ClientRandom -> ServerRandom -> ByteString -> Int -> ByteString-generateKeyBlock_TLS prf (ClientRandom c) (ServerRandom s) mastersecret kbsize =-    prf mastersecret seed kbsize where seed = B.concat [ "key expansion", s, c ]--generateKeyBlock_SSL :: ClientRandom -> ServerRandom -> ByteString -> Int -> ByteString-generateKeyBlock_SSL (ClientRandom c) (ServerRandom s) mastersecret kbsize =-    B.concat $ map computeMD5 $ take ((kbsize `div` 16) + 1) labels-  where labels            = [ uncurry BC.replicate x | x <- zip [1..] ['A'..'Z'] ]-        computeMD5  label = hash MD5 $ B.concat [ mastersecret, computeSHA1 label ]-        computeSHA1 label = hash SHA1 $ B.concat [ label, mastersecret, s, c ]--generateKeyBlock :: Version-                 -> Cipher-                 -> ClientRandom-                 -> ServerRandom-                 -> ByteString-                 -> Int-                 -> ByteString-generateKeyBlock SSL2 _ = generateKeyBlock_SSL-generateKeyBlock SSL3 _ = generateKeyBlock_SSL-generateKeyBlock v    c = generateKeyBlock_TLS $ getPRF v c--generateFinished_TLS :: PRF -> ByteString -> ByteString -> HashCtx -> ByteString-generateFinished_TLS prf label mastersecret hashctx = prf mastersecret seed 12-  where seed = B.concat [ label, hashFinal hashctx ]--generateFinished_SSL :: ByteString -> ByteString -> HashCtx -> ByteString-generateFinished_SSL sender mastersecret hashctx = B.concat [md5hash, sha1hash]-  where md5hash  = hash MD5 $ B.concat [ mastersecret, pad2, md5left ]-        sha1hash = hash SHA1 $ B.concat [ mastersecret, B.take 40 pad2, sha1left ]--        lefthash = hashFinal $ flip hashUpdateSSL (pad1, B.take 40 pad1)-                             $ foldl hashUpdate hashctx [sender,mastersecret]-        (md5left,sha1left) = B.splitAt 16 lefthash-        pad2     = B.replicate 48 0x5c-        pad1     = B.replicate 48 0x36+generateMainSecret_TLS+    :: PRF+    -> Secret+    -> ClientRandom+    -> ServerRandom+    -> Secret+generateMainSecret_TLS prf preMainSecret (ClientRandom c) (ServerRandom s) =+    prf preMainSecret seed 48+  where+    seed = B.concat ["master secret", c, s] -generateClientFinished :: Version-                       -> Cipher-                       -> ByteString-                       -> HashCtx-                       -> ByteString-generateClientFinished ver ciph-    | ver < TLS10 = generateFinished_SSL "CLNT"-    | otherwise   = generateFinished_TLS (getPRF ver ciph) "client finished"+generateMainSecret+    :: Version+    -> Cipher+    -> Secret+    -> ClientRandom+    -> ServerRandom+    -> Secret+generateMainSecret v c = generateMainSecret_TLS $ getPRF v c -generateServerFinished :: Version-                       -> Cipher-                       -> ByteString-                       -> HashCtx-                       -> ByteString-generateServerFinished ver ciph-    | ver < TLS10 = generateFinished_SSL "SRVR"-    | otherwise   = generateFinished_TLS (getPRF ver ciph) "server finished"+generateExtendedMainSecret+    :: ByteArrayAccess preMain+    => Version+    -> Cipher+    -> preMain+    -> ByteString+    -> Secret+generateExtendedMainSecret v c preMainSecret sessionHash =+    getPRF v c (convert preMainSecret) seed 48+  where+    seed = B.append "extended master secret" sessionHash -{- returns *output* after final MD5/SHA1 -}-generateCertificateVerify_SSL :: ByteString -> HashCtx -> ByteString-generateCertificateVerify_SSL = generateFinished_SSL ""+generateKeyBlock_TLS+    :: PRF -> ClientRandom -> ServerRandom -> Secret -> Int -> Secret+generateKeyBlock_TLS prf (ClientRandom c) (ServerRandom s) mainSecret kbsize =+    prf mainSecret seed kbsize+  where+    seed = B.concat ["key expansion", s, c] -{- returns *input* before final SHA1 -}-generateCertificateVerify_SSL_DSS :: ByteString -> HashCtx -> ByteString-generateCertificateVerify_SSL_DSS mastersecret hashctx = toHash-  where toHash = B.concat [ mastersecret, pad2, sha1left ]+generateKeyBlock+    :: Version+    -> Cipher+    -> ClientRandom+    -> ServerRandom+    -> Secret+    -> Int+    -> Secret+generateKeyBlock v c = generateKeyBlock_TLS $ getPRF v c -        sha1left = hashFinal $ flip hashUpdate pad1-                             $ hashUpdate hashctx mastersecret-        pad2     = B.replicate 40 0x5c-        pad1     = B.replicate 40 0x36+------------------------------------------------------------ -encodeSignedDHParams :: ServerDHParams -> ClientRandom -> ServerRandom -> ByteString-encodeSignedDHParams dhparams cran sran = runPut $-    putClientRandom32 cran >> putServerRandom32 sran >> putServerDHParams dhparams+encodeSignedDHParams+    :: ServerDHParams -> ClientRandom -> ServerRandom -> ByteString+encodeSignedDHParams dhparams cran sran =+    runPut $+        putClientRandom32 cran >> putServerRandom32 sran >> putServerDHParams dhparams  -- Combination of RFC 5246 and 4492 is ambiguous. -- Let's assume ecdhe_rsa and ecdhe_dss are identical to -- dhe_rsa and dhe_dss.-encodeSignedECDHParams :: ServerECDHParams -> ClientRandom -> ServerRandom -> ByteString-encodeSignedECDHParams dhparams cran sran = runPut $-    putClientRandom32 cran >> putServerRandom32 sran >> putServerECDHParams dhparams+encodeSignedECDHParams+    :: ServerECDHParams -> ClientRandom -> ServerRandom -> ByteString+encodeSignedECDHParams dhparams cran sran =+    runPut $+        putClientRandom32 cran >> putServerRandom32 sran >> putServerECDHParams dhparams -fromJustM :: Monad m => String -> Maybe a -> m a-fromJustM what Nothing  = fail ("fromJust " ++ what ++ ": Nothing")-fromJustM _    (Just x) = return x+encodeCertificate :: CertificateChain -> ByteString+encodeCertificate cc = runPut $ putOpaque24 (runPut $ mapM_ putOpaque24 certs)+  where+    (CertificateChainRaw certs) = encodeCertificateChain cc++------------------------------------------------------------++-- | in certain cases, we haven't manage to decode ServerKeyExchange properly,+-- because the decoding was too eager and the cipher wasn't been set yet.+-- we keep the Server Key Exchange in it unparsed format, and this function is+-- able to really decode the server key xchange if it's unparsed.+decodeReallyServerKeyXchgAlgorithmData+    :: Version+    -> CipherKeyExchangeType+    -> ByteString+    -> Either TLSError ServerKeyXchgAlgorithmData+decodeReallyServerKeyXchgAlgorithmData ver cke =+    runGetErr+        "server-key-xchg-algorithm-data"+        (decodeServerKeyXchgAlgorithmData ver cke)
+ Network/TLS/Packet13.hs view
@@ -0,0 +1,233 @@+{-# 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)
Network/TLS/Parameters.hs view
@@ -1,364 +1,899 @@--- |--- Module      : Network.TLS.Parameters--- License     : BSD-style--- Maintainer  : Vincent Hanquez <vincent@snarc.org>--- Stability   : experimental--- Portability : unknown----module Network.TLS.Parameters-    (-      ClientParams(..)-    , ServerParams(..)-    , CommonParams-    , DebugParams(..)-    , ClientHooks(..)-    , ServerHooks(..)-    , Supported(..)-    , Shared(..)-    -- * special default-    , defaultParamsClient-    -- * Parameters-    , MaxFragmentEnum(..)-    , GroupUsage(..)-    , CertificateUsage(..)-    , CertificateRejectReason(..)-    ) where--import Network.TLS.Extension-import Network.TLS.Struct-import qualified Network.TLS.Struct as Struct-import Network.TLS.Session-import Network.TLS.Cipher-import Network.TLS.Measurement-import Network.TLS.Compression-import Network.TLS.Crypto-import Network.TLS.Credentials-import Network.TLS.X509-import Network.TLS.RNG (Seed)-import Network.TLS.Imports-import Data.Default.Class-import qualified Data.ByteString as B--type HostName = String--type CommonParams = (Supported, Shared, DebugParams)---- | All settings should not be used in production-data DebugParams = DebugParams-    {-      -- | Disable the true randomness in favor of deterministic seed that will produce-      -- a deterministic random from. This is useful for tests and debugging purpose.-      -- Do not use in production-      debugSeed :: Maybe Seed-      -- | Add a way to print the seed that was randomly generated. re-using the same seed-      -- will reproduce the same randomness with 'debugSeed'-    , debugPrintSeed :: Seed -> IO ()-    }--defaultDebugParams :: DebugParams-defaultDebugParams = DebugParams-    { debugSeed = Nothing-    , debugPrintSeed = const (return ())-    }--instance Show DebugParams where-    show _ = "DebugParams"-instance Default DebugParams where-    def = defaultDebugParams--data ClientParams = ClientParams-    { clientUseMaxFragmentLength    :: Maybe MaxFragmentEnum-      -- | Define the name of the server, along with an extra service identification blob.-      -- this is important that the hostname part is properly filled for security reason,-      -- as it allow to properly associate the remote side with the given certificate-      -- during a handshake.-      ---      -- The extra blob is useful to differentiate services running on the same host, but that-      -- might have different certificates given. It's only used as part of the X509 validation-      -- infrastructure.-    , clientServerIdentification      :: (HostName, ByteString)-      -- | Allow the use of the Server Name Indication TLS extension during handshake, which allow-      -- the client to specify which host name, it's trying to access. This is useful to distinguish-      -- CNAME aliasing (e.g. web virtual host).-    , clientUseServerNameIndication   :: Bool-      -- | try to establish a connection using this session.-    , clientWantSessionResume         :: Maybe (SessionID, SessionData)-    , clientShared                    :: Shared-    , clientHooks                     :: ClientHooks-      -- | In this element, you'll  need to override the default empty value of-      -- of 'supportedCiphers' with a suitable cipherlist.-    , clientSupported                 :: Supported-    , clientDebug                     :: DebugParams-    } deriving (Show)--defaultParamsClient :: HostName -> ByteString -> ClientParams-defaultParamsClient serverName serverId = ClientParams-    { clientWantSessionResume    = Nothing-    , clientUseMaxFragmentLength = Nothing-    , clientServerIdentification = (serverName, serverId)-    , clientUseServerNameIndication = True-    , clientShared               = def-    , clientHooks                = def-    , clientSupported            = def-    , clientDebug                = defaultDebugParams-    }--data ServerParams = ServerParams-    { -- | request a certificate from client.-      serverWantClientCert    :: Bool--      -- | This is a list of certificates from which the-      -- disinguished names are sent in certificate request-      -- messages.  For TLS1.0, it should not be empty.-    , serverCACertificates :: [SignedCertificate]--      -- | Server Optional Diffie Hellman parameters.  Setting parameters is-      -- necessary for FFDHE key exchange when clients are not compatible-      -- with RFC 7919.-      ---      -- Value can be one of the standardized groups from module-      -- "Network.TLS.Extra.FFDHE" or custom parameters generated with-      -- 'Crypto.PubKey.DH.generateParams'.-    , serverDHEParams         :: Maybe DHParams--    , serverShared            :: Shared-    , serverHooks             :: ServerHooks-    , serverSupported         :: Supported-    , serverDebug             :: DebugParams-    } deriving (Show)--defaultParamsServer :: ServerParams-defaultParamsServer = ServerParams-    { serverWantClientCert   = False-    , serverCACertificates   = []-    , serverDHEParams        = Nothing-    , serverHooks            = def-    , serverShared           = def-    , serverSupported        = def-    , serverDebug            = defaultDebugParams-    }--instance Default ServerParams where-    def = defaultParamsServer---- | List all the supported algorithms, versions, ciphers, etc supported.-data Supported = Supported-    {-      -- | Supported Versions by this context-      -- On the client side, the highest version will be used to establish the connection.-      -- On the server side, the highest version that is less or equal than the client version will be chosed.-      supportedVersions       :: [Version]-      -- | Supported cipher methods.  The default is empty, specify a suitable-      -- cipher list.  'Network.TLS.Extra.Cipher.ciphersuite_default' is often-      -- a good choice.-    , supportedCiphers        :: [Cipher]-      -- | supported compressions methods-    , supportedCompressions   :: [Compression]-      -- | All supported hash/signature algorithms pair for client-      -- certificate verification and server signature in (EC)DHE,-      -- ordered by decreasing priority.-      ---      -- This list is sent to the peer as part of the signature_algorithms-      -- extension.  It is also used to restrict the choice of server-      -- credential, signature and hash algorithm, but only when the TLS-      -- version is 1.2 or above.  In order to disable SHA-1 one must then-      -- also disable earlier protocol versions in 'supportedVersions'.-    , supportedHashSignatures :: [HashAndSignatureAlgorithm]-      -- | Secure renegotiation defined in RFC5746.-      --   If 'True', clients send the renegotiation_info extension.-      --   If 'True', servers handle the extension or the renegotiation SCSV-      --   then send the renegotiation_info extension.-    , supportedSecureRenegotiation :: Bool-      -- | If 'True', renegotiation is allowed from the client side.-      --   This is vulnerable to DOS attacks.-      --   If 'False', renegotiation is allowed only from the server side-      --   via HelloRequest.-    , supportedClientInitiatedRenegotiation :: Bool-      -- | Set if we support session.-    , supportedSession             :: Bool-      -- | Support for fallback SCSV defined in RFC7507.-      --   If 'True', servers reject handshakes which suggest-      --   a lower protocol than the highest protocol supported.-    , supportedFallbackScsv        :: Bool-      -- | In ver <= TLS1.0, block ciphers using CBC are using CBC residue as IV, which can be guessed-      -- by an attacker. Hence, an empty packet is normally sent before a normal data packet, to-      -- prevent guessability. Some Microsoft TLS-based protocol implementations, however,-      -- consider these empty packets as a protocol violation and disconnect. If this parameter is-      -- 'False', empty packets will never be added, which is less secure, but might help in rare-      -- cases.-    , supportedEmptyPacket         :: Bool-      -- | A list of supported elliptic curves and finite-field groups in the-      --   preferred order.-      --   The default value is ['X25519','P256','P384','P521'].-      --   'X25519' and 'P256' provide 128-bit security which is strong-      --   enough until 2030.  Both curves are fast because their-      --   backends are written in C.-    , supportedGroups              :: [Group]-    } deriving (Show,Eq)--defaultSupported :: Supported-defaultSupported = Supported-    { supportedVersions       = [TLS12,TLS11,TLS10]-    , supportedCiphers        = []-    , supportedCompressions   = [nullCompression]-    , supportedHashSignatures = [ (Struct.HashSHA512, SignatureRSA)-                                , (Struct.HashSHA512, SignatureECDSA)-                                , (Struct.HashSHA384, SignatureRSA)-                                , (Struct.HashSHA384, SignatureECDSA)-                                , (Struct.HashSHA256, SignatureRSA)-                                , (Struct.HashSHA256, SignatureECDSA)-                                , (Struct.HashSHA1,   SignatureRSA)-                                , (Struct.HashSHA1,   SignatureDSS)-                                ]-    , supportedSecureRenegotiation = True-    , supportedClientInitiatedRenegotiation = False-    , supportedSession             = True-    , supportedFallbackScsv        = True-    , supportedEmptyPacket         = True-    , supportedGroups              = [X25519,P256,P384,P521]-    }--instance Default Supported where-    def = defaultSupported--data Shared = Shared-    { sharedCredentials     :: Credentials-    , sharedSessionManager  :: SessionManager-    , sharedCAStore         :: CertificateStore-    , sharedValidationCache :: ValidationCache-    }--instance Show Shared where-    show _ = "Shared"-instance Default Shared where-    def = Shared-            { sharedCAStore         = mempty-            , sharedCredentials     = mempty-            , sharedSessionManager  = noSessionManager-            , sharedValidationCache = def-            }---- | Group usage callback possible return values.-data GroupUsage =-          GroupUsageValid                 -- ^ usage of group accepted-        | GroupUsageInsecure              -- ^ usage of group provides insufficient security-        | GroupUsageUnsupported String    -- ^ usage of group rejected for other reason (specified as string)-        | GroupUsageInvalidPublic         -- ^ usage of group with an invalid public value-        deriving (Show,Eq)--defaultGroupUsage :: DHParams -> DHPublic -> IO GroupUsage-defaultGroupUsage params public-    | even $ dhParamsGetP params                   = return $ GroupUsageUnsupported "invalid odd prime"-    | not $ dhValid params (dhParamsGetG params)   = return $ GroupUsageUnsupported "invalid generator"-    | not $ dhValid params (dhUnwrapPublic public) = return   GroupUsageInvalidPublic-    | otherwise                                    = return   GroupUsageValid---- | A set of callbacks run by the clients for various corners of TLS establishment-data ClientHooks = ClientHooks-    { -- | This action is called when the server sends a-      -- certificate request.  The parameter is the information-      -- from the request.  The action should select a certificate-      -- chain of one of the given certificate types where the-      -- last certificate in the chain should be signed by one of-      -- the given distinguished names.  Each certificate should-      -- be signed by the following one, except for the last.  At-      -- least the first of the certificates in the chain must-      -- have a corresponding private key, because that is used-      -- for signing the certificate verify message.-      ---      -- Note that is is the responsibility of this action to-      -- select a certificate matching one of the requested-      -- certificate types.  Returning a non-matching one will-      -- lead to handshake failure later.-      ---      -- Returning a certificate chain not matching the-      -- distinguished names may lead to problems or not,-      -- depending whether the server accepts it.-      onCertificateRequest :: ([CertificateType],-                               Maybe [HashAndSignatureAlgorithm],-                               [DistinguishedName]) -> IO (Maybe (CertificateChain, PrivKey))-      -- | Used by the client to validate the server certificate.  The default-      -- implementation calls 'validateDefault' which validates according to the-      -- default hooks and checks provided by "Data.X509.Validation".  This can-      -- be replaced with a custom validation function using different settings.-    , onServerCertificate  :: CertificateStore -> ValidationCache -> ServiceID -> CertificateChain -> IO [FailedReason]-      -- | This action is called when the client sends ClientHello-      --   to determine ALPN values such as '["h2", "http/1.1"]'.-    , onSuggestALPN :: IO (Maybe [B.ByteString])-      -- | This action is called to validate DHE parameters when-      --   the server selected a finite-field group not part of-      --   the "Supported Groups Registry".-      --   See RFC 7919 section 3.1 for recommandations.-    , onCustomFFDHEGroup :: DHParams -> DHPublic -> IO GroupUsage-    }--defaultClientHooks :: ClientHooks-defaultClientHooks = ClientHooks-    { onCertificateRequest = \ _ -> return Nothing-    , onServerCertificate  = validateDefault-    , onSuggestALPN        = return Nothing-    , onCustomFFDHEGroup   = defaultGroupUsage-    }--instance Show ClientHooks where-    show _ = "ClientHooks"-instance Default ClientHooks where-    def = defaultClientHooks---- | A set of callbacks run by the server for various corners of the TLS establishment-data ServerHooks = ServerHooks-    {-      -- | This action is called when a client certificate chain-      -- is received from the client.  When it returns a-      -- CertificateUsageReject value, the handshake is aborted.-      onClientCertificate :: CertificateChain -> IO CertificateUsage--      -- | This action is called when the client certificate-      -- cannot be verified. Return 'True' to accept the certificate-      -- anyway, or 'False' to fail verification.-    , onUnverifiedClientCert :: IO Bool--      -- | Allow the server to choose the cipher relative to the-      -- the client version and the client list of ciphers.-      ---      -- This could be useful with old clients and as a workaround-      -- to the BEAST (where RC4 is sometimes prefered with TLS < 1.1)-      ---      -- The client cipher list cannot be empty.-    , onCipherChoosing        :: Version -> [Cipher] -> Cipher--      -- | Allow the server to indicate additional credentials-      -- to be used depending on the host name indicated by the-      -- client.-      ---      -- This is most useful for transparent proxies where-      -- credentials must be generated on the fly according to-      -- the host the client is trying to connect to.-      ---      -- Returned credentials may be ignored if a client does not support-      -- the signature algorithms used in the certificate chain.-    , onServerNameIndication  :: Maybe HostName -> IO Credentials--      -- | at each new handshake, we call this hook to see if we allow handshake to happens.-    , onNewHandshake          :: Measurement -> IO Bool--      -- | Allow the server to choose an application layer protocol-      --   suggested from the client through the ALPN-      --   (Application Layer Protocol Negotiation) extensions.-    , onALPNClientSuggest     :: Maybe ([B.ByteString] -> IO B.ByteString)-    }--defaultServerHooks :: ServerHooks-defaultServerHooks = ServerHooks-    { onCipherChoosing       = \_ -> head-    , onClientCertificate    = \_ -> return $ CertificateUsageReject $ CertificateRejectOther "no client certificates expected"-    , onUnverifiedClientCert = return False-    , onServerNameIndication = \_ -> return mempty-    , onNewHandshake         = \_ -> return True-    , onALPNClientSuggest    = Nothing-    }--instance Show ServerHooks where-    show _ = "ServerHooks"-instance Default ServerHooks where-    def = defaultServerHooks+{-# LANGUAGE Strict #-}++module Network.TLS.Parameters (+    ClientParams (..),+    defaultParamsClient,+    ServerParams (..),+    defaultParamsServer,+    CommonParams,+    DebugParams (..),+    defaultDebugParams,+    defaultKeyLogger,+    ClientHooks (..),+    defaultClientHooks,+    OnCertificateRequest,+    OnServerCertificate,+    ServerHooks (..),+    defaultServerHooks,+    Supported (..),+    defaultSupported,+    Shared (..),+    defaultShared,+    Limit (..),+    defaultLimit,++    -- * Parameters+    MaxFragmentEnum (..),+    EMSMode (..),+    GroupUsage (..),+    CertificateUsage (..),+    CertificateRejectReason (..),+    Information (..),+) where++import Control.Concurrent (MVar, newMVar, withMVar)+import Crypto.HPKE+import Data.Default (Default (def))+import System.Environment (lookupEnv)+import System.IO.Unsafe (unsafePerformIO)++import Network.TLS.Cipher+import Network.TLS.Compression+import Network.TLS.Credentials+import Network.TLS.Crypto+import Network.TLS.ECH.Config+import Network.TLS.Extension+import Network.TLS.Extra.Cipher+import Network.TLS.Handshake.State+import Network.TLS.Imports+import Network.TLS.Measurement+import Network.TLS.RNG (Seed)+import Network.TLS.Session+import Network.TLS.Struct+import qualified Network.TLS.Struct as Struct+import Network.TLS.Types (HostName)+import Network.TLS.X509++type CommonParams = (Supported, Shared, DebugParams)++-- | All settings should not be used in production+data DebugParams = DebugParams+    { debugSeed :: Maybe Seed+    -- ^ Disable the true randomness in favor of deterministic seed that will produce+    -- a deterministic random from. This is useful for tests and debugging purpose.+    -- Do not use in production+    --+    -- Default: 'Nothing'+    , debugPrintSeed :: Seed -> IO ()+    -- ^ Add a way to print the seed that was randomly generated. reusing the same seed+    -- will reproduce the same randomness with 'debugSeed'+    --+    -- Default: no printing+    , debugVersionForced :: Maybe Version+    -- ^ Force to choose this version in the server side.+    --+    -- Default: 'Nothing'+    , debugKeyLogger :: String -> IO ()+    -- ^ Printing main keys.+    --+    -- Default: no printing+    , debugError :: String -> IO ()+    , debugTraceKey :: String -> IO ()+    }++{-# NOINLINE keyLogLock #-}+keyLogLock :: MVar ()+keyLogLock = unsafePerformIO $ newMVar ()++{-# NOINLINE keyLogFile #-}+keyLogFile :: Maybe FilePath+keyLogFile = unsafePerformIO $ lookupEnv "SSLKEYLOGFILE"++-- | Key logger with the SSLKEYLOGFILE environment variable.+defaultKeyLogger :: String -> IO ()+defaultKeyLogger ~msg = case keyLogFile of+    Nothing -> return ()+    Just file -> withMVar keyLogLock $ \_ -> appendFile file (msg ++ "\n")++-- | Default value for 'DebugParams'+defaultDebugParams :: DebugParams+defaultDebugParams =+    DebugParams+        { debugSeed = Nothing+        , debugPrintSeed = const (return ())+        , debugVersionForced = Nothing+        , debugKeyLogger = defaultKeyLogger+        , debugError = \ ~_ -> return ()+        , debugTraceKey = \ ~_ -> return ()+        }++instance Show DebugParams where+    show _ = "DebugParams"+instance Default DebugParams where+    def = defaultDebugParams++{-# DEPRECATED clientUseMaxFragmentLength "UseMaxFragmentLength is deprecated" #-}++data ClientParams = ClientParams+    { clientUseMaxFragmentLength :: Maybe MaxFragmentEnum+    -- ^+    --+    -- Default: 'Nothing'+    , clientServerIdentification :: (HostName, ByteString)+    -- ^ Define the name of the server, along with an extra service+    -- identification blob.  this is important that the hostname part+    -- is properly filled for security reason, as it allow to properly+    -- associate the remote side with the given certificate during a+    -- handshake.+    --+    -- The extra blob is useful to differentiate services running on+    -- the same host, but that might have different certificates+    -- given. It's only used as part of the X509 validation+    -- infrastructure.+    --+    -- This value is typically set by 'defaultParamsClient'.+    , clientUseServerNameIndication :: Bool+    -- ^ Allow the use of the Server Name Indication TLS extension+    -- during handshake, which allow the client to specify which host+    -- name, it's trying to access. This is useful to distinguish+    -- CNAME aliasing (e.g. web virtual host).+    --+    -- Default: 'True'+    , clientWantSessionResume :: Maybe (SessionID, SessionData)+    -- ^ try to establish a connection using this session for TLS+    -- 1.2/TLS 1.3.  This can be used for TLS 1.3 but for backward+    -- compatibility purpose only.  Use 'clientWantSessionResume13'+    -- instead for TLS 1.3.+    --+    -- Default: 'Nothing'+    , clientWantSessionResumeList :: [(SessionID, SessionData)]+    -- ^ try to establish a connection using one of this sessions+    -- especially for TLS 1.3.  This take precedence over+    -- 'clientWantSessionResume'.  For convenience, this can be+    -- specified for TLS 1.2 but only the first entry is used.+    --+    -- Default: '[]'+    , clientWantTicket :: Bool+    -- ^ Whether to solicit TLS 1.2 session tickets (or TLS 1.3+    -- resumption PSKs) from servers.  With a 'False' setting,+    -- stateless clients that never do resumption can avoid+    -- wasting server and client resources used to generate,+    -- transmit and process tickets that will never be used.+    --+    -- Default: 'True'+    --+    -- @since 2.4.1+    , clientShared :: Shared+    -- ^ See the default value of 'Shared'.+    , clientHooks :: ClientHooks+    -- ^ See the default value of 'ClientHooks'.+    , clientSupported :: Supported+    -- ^ In this element, you'll need to override the default empty+    -- value of of 'supportedCiphers' with a suitable cipherlist.+    --+    -- See the default value of 'Supported'.+    , clientDebug :: DebugParams+    -- ^ See the default value of 'DebugParams'.+    , clientUseEarlyData :: Bool+    -- ^ Client tries to send early data in TLS 1.3 via 'sendData' if+    -- possible.  If not accepted by the server, the early data is+    -- automatically re-sent.+    --+    -- Default: 'False'+    , clientUseECH :: Bool+    -- ^ Enabling Encrypted Client Hello.+    -- If 'sharedECHConfigList' is null, a greasing ECH extension is sent.+    -- Otherwise, a valid ECH is sent.+    -- If the server rejects ECH in Server Hello,+    -- the client sends an alert after negotiation.+    --+    -- Default: 'False'+    --+    -- @since 2.1.9+    }+    deriving (Show)++-- | Default value for 'ClientParams'+defaultParamsClient :: HostName -> ByteString -> ClientParams+defaultParamsClient serverName serverId =+    ClientParams+        { clientUseMaxFragmentLength = Nothing+        , clientServerIdentification = (serverName, serverId)+        , clientUseServerNameIndication = True+        , clientWantSessionResume = Nothing+        , clientWantSessionResumeList = []+        , clientWantTicket = True+        , clientShared = def+        , clientHooks = def+        , clientSupported = def+        , clientDebug = defaultDebugParams+        , clientUseEarlyData = False+        , clientUseECH = False+        }++data ServerParams = ServerParams+    { serverWantClientCert :: Bool+    -- ^ Request a certificate from client.+    --+    -- Default: 'False'+    , serverCACertificates :: [SignedCertificate]+    -- ^ This is a list of certificates from which the disinguished+    -- names are sent in certificate request messages.  For TLS1.0, it+    -- should not be empty.+    --+    -- Default: '[]'+    , serverDHEParams :: Maybe DHParams+    -- ^ Server Optional Diffie Hellman parameters.  Setting+    -- parameters is necessary for FFDHE key exchange when clients are+    -- not compatible with RFC 7919.+    --+    -- Value can be one of the standardized groups from module+    -- "Network.TLS.Extra.FFDHE" or custom parameters generated with+    -- 'Crypto.PubKey.DH.generateParams'.+    --+    -- Default: 'Nothing'+    , serverHooks :: ServerHooks+    -- ^ See the default value of 'ServerHooks'.+    , serverShared :: Shared+    -- ^ See the default value of 'Shared'.+    , serverSupported :: Supported+    -- ^ See the default value of 'Supported'.+    , serverDebug :: DebugParams+    -- ^ See the default value of 'DebugParams'.+    , serverEarlyDataSize :: Int+    -- ^ Server accepts this size of early data in TLS 1.3.  0 (or+    -- lower) means that the server does not accept early data.+    --+    -- Default: 0+    , serverTicketLifetime :: Int+    -- ^ Lifetime in seconds for session tickets generated by the+    -- server.  Acceptable value range is 0 to 604800 (7 days).+    --+    -- Default: 7200 (2 hours)+    , serverECHKey :: [(ConfigId, ByteString)]+    -- ^ ECH secret keys.+    --+    -- Default: '[]'+    --+    -- @since 2.1.9+    }+    deriving (Show)++defaultParamsServer :: ServerParams+defaultParamsServer =+    ServerParams+        { serverWantClientCert = False+        , serverCACertificates = []+        , serverDHEParams = Nothing+        , serverHooks = def+        , serverShared = def+        , serverSupported = def+        , serverDebug = defaultDebugParams+        , serverEarlyDataSize = 0+        , serverTicketLifetime = 7200+        , serverECHKey = []+        }++instance Default ServerParams where+    def = defaultParamsServer++-- | List all the supported algorithms, versions, ciphers, etc supported.+data Supported = Supported+    { supportedVersions :: [Version]+    -- ^ Supported versions by this context.  On the client side, the+    -- highest version will be used to establish the connection.  On+    -- the server side, the highest version that is less or equal than+    -- the client version will be chosen.+    --+    -- Versions should be listed in preferred order, i.e. higher+    -- versions first.+    --+    -- Default: @[TLS13,TLS12]@+    , supportedCiphers :: [Cipher]+    -- ^ Supported cipher methods.  The default is empty, specify a+    -- suitable cipher list.+    -- 'Network.TLS.Extra.Cipher.ciphersuite_default' is often a good+    -- choice.+    --+    -- Default: @[]@+    , supportedCompressions :: [Compression]+    -- ^ Supported compressions methods.  By default only the "null"+    -- compression is supported, which means no compression will be+    -- performed.  Allowing other compression method is not advised as+    -- it causes a connection failure when TLS 1.3 is negotiated.+    --+    -- Default: @[nullCompression]@+    , supportedHashSignatures :: [HashAndSignatureAlgorithm]+    -- ^ All supported hash/signature algorithms pair for client+    -- certificate verification and server signature in (EC)DHE,+    -- ordered by decreasing priority.+    --+    -- This list is sent to the peer as part of the+    -- "signature_algorithms" extension.  It is used to restrict+    -- accepted signatures received from the peer at TLS level (not in+    -- X.509 certificates), but only when the TLS version is 1.2 or+    -- above.  In order to disable SHA-1 one must then also disable+    -- earlier protocol versions in 'supportedVersions'.+    --+    -- The list also impacts the selection of possible algorithms when+    -- generating signatures.+    --+    -- Note: with TLS 1.3 some algorithms have been deprecated and+    -- will not be used even when listed in the parameter: MD5, SHA-1,+    -- SHA-224, RSA PKCS#1, DSA.+    --+    -- Default:+    --+    -- @+    --   [ (HashIntrinsic,     SignatureEd448)+    --   , (HashIntrinsic,     SignatureEd25519)+    --   , (Struct.HashSHA256, SignatureECDSA)+    --   , (Struct.HashSHA384, SignatureECDSA)+    --   , (Struct.HashSHA512, SignatureECDSA)+    --   , (HashIntrinsic,     SignatureRSApssRSAeSHA512)+    --   , (HashIntrinsic,     SignatureRSApssRSAeSHA384)+    --   , (HashIntrinsic,     SignatureRSApssRSAeSHA256)+    --   , (Struct.HashSHA512, SignatureRSA)+    --   , (Struct.HashSHA384, SignatureRSA)+    --   , (Struct.HashSHA256, SignatureRSA)+    --   , (Struct.HashSHA1,   SignatureRSA)+    --   , (Struct.HashSHA1,   SignatureDSA)+    --   ]+    -- @+    , supportedSecureRenegotiation :: Bool+    -- ^ Secure renegotiation defined in RFC5746.  If 'True', clients+    --   send the renegotiation_info extension.  If 'True', servers+    --   handle the extension or the renegotiation SCSV then send the+    --   renegotiation_info extension.+    --+    --   Default: 'True'+    , supportedClientInitiatedRenegotiation :: Bool+    -- ^ If 'True', renegotiation is allowed from the client side.+    --   This is vulnerable to DOS attacks.  If 'False', renegotiation+    --   is allowed only from the server side via HelloRequest.+    --+    --   Default: 'False'+    , supportedExtendedMainSecret :: EMSMode+    -- ^ The mode regarding extended main secret.  Enabling this+    -- extension provides better security for TLS versions 1.2.  TLS+    -- 1.3 provides the security properties natively and does not need+    -- the extension.+    --+    -- By default the extension is 'RequireEMS'.  So, the handshake+    -- will fail when the peer does not support the extension.+    --+    -- Default: 'RequireEMS'+    , supportedSession :: Bool+    -- ^ Set if we support session.+    --+    --   Default: 'True'+    , supportedFallbackScsv :: Bool+    -- ^ Support for fallback SCSV defined in RFC7507.  If 'True',+    --   servers reject handshakes which suggest a lower protocol than+    --   the highest protocol supported.+    --+    --   Default: 'True'+    , supportedEmptyPacket :: Bool+    -- ^ In ver <= TLS1.0, block ciphers using CBC are using CBC+    -- residue as IV, which can be guessed by an attacker. Hence, an+    -- empty packet is normally sent before a normal data packet, to+    -- prevent guessability. Some Microsoft TLS-based protocol+    -- implementations, however, consider these empty packets as a+    -- protocol violation and disconnect. If this parameter is+    -- 'False', empty packets will never be added, which is less+    -- secure, but might help in rare cases.+    --+    --   Default: 'True'+    , supportedHPKE :: [(KEM_ID, KDF_ID, AEAD_ID)]+    -- ^ Client only.+    --+    -- @since 2.1.9+    , supportedGroups :: [Group]+    -- ^ A list of supported elliptic curves and finite-field groups+    --   in preferred order.+    --+    --   * TLS 1.3 client: this list is used as the 1st argument to+    --     'onSelectKeyShareGroups' to select groups in "key_share".+    --     This list is also used as values of "supported_groups".+    --   * TLS 1.3 server: this list is not used.+    --   * TLS 1.2 client: this list is also used as values of+    --     "supported_groups".+    --   * TLS 1.2 server: this list is used to select a key exchange+    --     mechanism.+    --+    --   The list is sent to the server as part of the+    --   "supported_groups" extension.  It is used in both clients and+    --   servers to restrict accepted groups in DH key exchange.  Up+    --   until TLS v1.2, it is also used by a client to restrict+    --   accepted elliptic curves in ECDSA signatures.+    --+    --   The default value includes all groups with security strength+    --   of 128 bits or more.+    --+    --   Default: @[X25519,P256,P384,X448,P521,FFDHE3072,FFDHE4096,FFDHE6144,FFDHE8192,X25519MLKEM768,P256MLKEM768,P384MLKEM1024,MLKEM768,MLKEM1024]@+    , supportedGroupsTLS13 :: [[Group]]+    -- ^ The inside @[Group]@ is the list of @Group@ at the same level+    -- in preferred order.  The inside @[Group]@s are also listed in+    -- preferred order.+    --+    -- TLS 1.3 server: this is used as the 1st argument to+    -- 'onSelectKeyShare'.+    --+    -- Default: @[[X25519MLKEM768,P256MLKEM768,P384MLKEM1024],[X25519,P256],[P384,X448,P521],[FFDHE2048,FFDHE3072,FFDHE4096,FFDHE6144,FFDHE8192],[MLKEM768,MLKEM1024]]@+    --+    -- @since 2.2.3+    }+    deriving (Show, Eq)++-- | Client or server policy regarding Extended Main Secret+data EMSMode+    = -- | Extended Main Secret is not used+      NoEMS+    | -- | Extended Main Secret is allowed+      AllowEMS+    | -- | Extended Main Secret is required+      RequireEMS+    deriving (Show, Eq)++defaultHPKE :: [(KEM_ID, KDF_ID, AEAD_ID)]+defaultHPKE =+    [ (DHKEM_X25519_HKDF_SHA256, HKDF_SHA256, AES_128_GCM)+    , (DHKEM_X25519_HKDF_SHA256, HKDF_SHA256, ChaCha20Poly1305)+    , (DHKEM_P256_HKDF_SHA256, HKDF_SHA256, AES_128_GCM)+    , (DHKEM_P256_HKDF_SHA256, HKDF_SHA512, AES_128_GCM)+    , (DHKEM_P256_HKDF_SHA256, HKDF_SHA256, ChaCha20Poly1305)+    , (DHKEM_P521_HKDF_SHA512, HKDF_SHA512, AES_256_GCM)+    ]++defaultSupported :: Supported+defaultSupported =+    Supported+        { supportedVersions = [TLS13, TLS12]+        , supportedCiphers = ciphersuite_default+        , supportedCompressions = [nullCompression]+        , supportedHashSignatures = Struct.supportedSignatureSchemes+        , supportedSecureRenegotiation = True+        , supportedClientInitiatedRenegotiation = False+        , supportedExtendedMainSecret = RequireEMS+        , supportedSession = True+        , supportedFallbackScsv = True+        , supportedEmptyPacket = True+        , supportedHPKE = defaultHPKE+        , supportedGroups = supportedNamedGroups+        , supportedGroupsTLS13 = supportedNamedGroupsTLS13+        }++instance Default Supported where+    def = defaultSupported++-- | Parameters that are common to clients and servers.+data Shared = Shared+    { sharedCredentials :: Credentials+    -- ^ The list of certificates and private keys that a server will+    -- use as part of authentication to clients.  Actual credentials+    -- that are used are selected dynamically from this list based on+    -- client capabilities.  Additional credentials returned by+    -- 'onServerNameIndication' are also considered.+    --+    -- When credential list is left empty (the default value), no key+    -- exchange can take place.+    --+    -- Default: 'mempty'+    , sharedSessionManager :: SessionManager+    -- ^ Callbacks used by clients and servers in order to resume TLS+    -- sessions.  The default implementation never resumes sessions.+    -- Package+    -- <https://hackage.haskell.org/package/tls-session-manager+    -- tls-session-manager> provides an in-memory implementation.+    --+    -- Default: 'noSessionManager'+    , sharedCAStore :: CertificateStore+    -- ^ A collection of trust anchors to be used by a client as part+    -- of validation of server certificates.  This is set as first+    -- argument to function 'onServerCertificate'.  Package+    -- <https://hackage.haskell.org/package/crypton-x509-system+    -- crypton-x509-system> gives access to a default certificate+    -- store configured in the system.+    --+    -- Default: 'mempty'+    , sharedValidationCache :: ValidationCache+    -- ^ Callbacks that may be used by a client to cache certificate+    -- validation results (positive or negative) and avoid expensive+    -- signature check.  The default implementation does not have any+    -- caching.+    --+    -- See the default value of 'ValidationCache'.+    , sharedHelloExtensions :: [ExtensionRaw]+    -- ^ Additional extensions to be sent during the Hello sequence.+    --+    -- For a client this is always included in message ClientHello.+    -- For a server, this is sent in messages ServerHello or+    -- EncryptedExtensions based on the TLS version.+    --+    -- Default: @[]@+    , sharedECHConfigList :: ECHConfigList+    -- ^ ECH configuration.+    --+    -- @since 2.1.9+    , sharedLimit :: Limit+    -- ^ Limitation parameters.+    --+    -- @since 2.1.8+    }++instance Show Shared where+    show _ = "Shared"+instance Default Shared where+    def = defaultShared++defaultShared :: Shared+defaultShared =+    Shared+        { sharedCredentials = mempty+        , sharedSessionManager = noSessionManager+        , sharedCAStore = mempty+        , sharedValidationCache = def+        , sharedHelloExtensions = []+        , sharedECHConfigList = []+        , sharedLimit = defaultLimit+        }++-- | Group usage callback possible return values.+data GroupUsage+    = -- | usage of group accepted+      GroupUsageValid+    | -- | usage of group provides insufficient security+      GroupUsageInsecure+    | -- | usage of group rejected for other reason (specified as string)+      GroupUsageUnsupported String+    | -- | usage of group with an invalid public value+      GroupUsageInvalidPublic+    deriving (Show, Eq)++defaultGroupUsage :: Int -> DHParams -> DHPublic -> IO GroupUsage+defaultGroupUsage minBits params public+    | even $ dhParamsGetP params =+        return $ GroupUsageUnsupported "invalid odd prime"+    | not $ dhValid params (dhParamsGetG params) =+        return $ GroupUsageUnsupported "invalid generator"+    | not $ dhValid params (dhUnwrapPublic public) =+        return GroupUsageInvalidPublic+    -- To prevent Logjam attack+    | dhParamsGetBits params < minBits = return GroupUsageInsecure+    | otherwise = return GroupUsageValid++-- | Type for 'onCertificateRequest'. This type synonym is to make+--   document readable.+type OnCertificateRequest =+    ( [CertificateType]+    , Maybe [HashAndSignatureAlgorithm]+    , [DistinguishedName]+    )+    -> IO (Maybe (CertificateChain, PrivKey))++-- | Type for 'onServerCertificate'. This type synonym is to make+--   document readable.+type OnServerCertificate =+    CertificateStore+    -> ValidationCache+    -> ServiceID+    -> CertificateChain+    -> IO [FailedReason]++-- | A set of callbacks run by the clients for various corners of TLS establishment+data ClientHooks = ClientHooks+    { onCertificateRequest :: OnCertificateRequest+    -- ^ This action is called when the a certificate request is+    -- received from the server. The callback argument is the+    -- information from the request.  The server, at its discretion,+    -- may be willing to continue the handshake without a client+    -- certificate.  Therefore, the callback is free to return+    -- 'Nothing' to indicate that no client certificate should be+    -- sent, despite the server's request.  In some cases it may be+    -- appropriate to get user consent before sending the certificate;+    -- the content of the user's certificate may be sensitive and+    -- intended only for specific servers.+    --+    -- The action should select a certificate chain of one of the+    -- given certificate types and one of the certificates in the+    -- chain should (if possible) be signed by one of the given+    -- distinguished names.  Some servers, that don't have a narrow+    -- set of preferred issuer CAs, will send an empty+    -- 'DistinguishedName' list, rather than send all the names from+    -- their trusted CA bundle.  If the client does not have a+    -- certificate chaining to a matching CA, it may choose a default+    -- certificate instead.+    --+    -- Each certificate except the last should be signed by the+    -- following one.  The returned private key must be for the first+    -- certificates in the chain.  This key will be used to signing+    -- the certificate verify message.+    --+    -- The public key in the first certificate, and the matching+    -- returned private key must be compatible with one of the list of+    -- 'HashAndSignatureAlgorithm' value when provided.  TLS 1.3+    -- changes the meaning of the list elements, adding explicit code+    -- points for each supported pair of hash and signature (public+    -- key) algorithms, rather than combining separate codes for the+    -- hash and key.  For details see+    -- <https://tools.ietf.org/html/rfc8446#section-4.2.3 RFC 8446>+    -- section 4.2.3.  When no compatible certificate chain is+    -- available, return 'Nothing' if it is OK to continue without a+    -- client certificate.  Returning a non-matching certificate+    -- should result in a handshake failure.+    --+    -- While the TLS version is not provided to the callback, the+    -- content of the @signature_algorithms@ list provides a strong+    -- hint, since TLS 1.3 servers will generally list RSA pairs with+    -- a hash component of 'Intrinsic' (@0x08@).+    --+    -- Note that is is the responsibility of this action to select a+    -- certificate matching one of the requested certificate types+    -- (public key algorithms).  Returning a non-matching one will+    -- lead to handshake failure later.+    --+    -- Default: returns 'Nothing' anyway.+    , onServerCertificate :: OnServerCertificate+    -- ^ Used by the client to validate the server certificate.  The+    -- default implementation calls 'validateDefault' which validates+    -- according to the default hooks and checks provided by+    -- "Data.X509.Validation".  This can be replaced with a custom+    -- validation function using different settings.+    --+    -- The function is not expected to verify the key-usage extension+    -- of the end-entity certificate, as this depends on the+    -- dynamically-selected cipher and this part should not be cached.+    -- Key-usage verification is performed by the library internally.+    --+    -- Default: 'validateDefault'+    , onSuggestALPN :: IO (Maybe [ByteString])+    -- ^ This action is called when the client sends ClientHello+    --   to determine ALPN values such as '["h2", "http/1.1"]'.+    --+    -- Default: returns 'Nothing'+    , onCustomFFDHEGroup :: DHParams -> DHPublic -> IO GroupUsage+    -- ^ This action is called to validate DHE parameters when the+    --   server selected a finite-field group not part of the+    --   "Supported Groups Registry" or not part of 'supportedGroups'+    --   list.+    --+    --   With TLS 1.3 custom groups have been removed from the+    --   protocol, so this callback is only used when the version+    --   negotiated is 1.2 or below.+    --+    --   The default behavior with (dh_p, dh_g, dh_size) and pub as+    --   follows:+    --+    --   (1) rejecting if dh_p is even+    --   (2) rejecting unless 1 < dh_g && dh_g < dh_p - 1+    --   (3) rejecting unless 1 < dh_p && pub < dh_p - 1+    --   (4) rejecting if dh_size < 1024 (to prevent Logjam attack)+    --+    --   See RFC 7919 section 3.1 for recommendations.+    , onServerFinished :: Information -> IO ()+    -- ^ When a handshake is done, this hook can check `Information`.+    , onSelectKeyShareGroups :: [Group] -> [Group]+    -- ^ A function to select groups in "key_share" by TLS 1.3 client.+    --+    --   Client's 'supportedGroups' is passed as the 1st argument.+    --+    --   The default function specifies a pair of hybrid (classical ++    --   post quantum) group and classical group to transit from+    --   classical key exchange to hybrid key exchange. With the+    --   default value of 'supportedGroups', X25519MLKEM and X25519+    --   are chosen.+    --+    --   Middleboxes may drop a ClientHello that contains large+    --   X2219MLKEM. In such environment, @take 1@, which selects+    --   X22519 only with the default value, is maybe a good+    --   candidate.+    --+    --   In the case where X22519 is only contained in "key_share", a+    --   wise-server without nasty middleboxes may ask the client to+    --   send X2219MLKEM via HelloRetryRequest as X2219MLKEM is+    --   specified in "supported_groups".+    --+    --   @since 2.2.3+    }++defaultOnSelectKeyShareGroups :: [Group] -> [Group]+defaultOnSelectKeyShareGroups groups = take 1 hs ++ take 1 es+  where+    (hs, es) = partition isHybrid groups++isHybrid :: Group -> Bool+isHybrid X25519MLKEM768 = True+isHybrid P256MLKEM768 = True+isHybrid P384MLKEM1024 = True+isHybrid _ = False++defaultClientHooks :: ClientHooks+defaultClientHooks =+    ClientHooks+        { onCertificateRequest = \_ -> return Nothing+        , onServerCertificate = validateDefault+        , onSuggestALPN = return Nothing+        , onCustomFFDHEGroup = defaultGroupUsage 1024+        , onServerFinished = \_ -> return ()+        , onSelectKeyShareGroups = defaultOnSelectKeyShareGroups+        }++instance Show ClientHooks where+    show _ = "ClientHooks"+instance Default ClientHooks where+    def = defaultClientHooks++-- | A set of callbacks run by the server for various corners of the TLS establishment+data ServerHooks = ServerHooks+    { onClientCertificate :: CertificateChain -> IO CertificateUsage+    -- ^ This action is called when a client certificate chain is+    -- received from the client.  When it returns a+    -- CertificateUsageReject value, the handshake is aborted.+    --+    -- The function is not expected to verify the key-usage extension+    -- of the certificate.  This verification is performed by the+    -- library internally.+    --+    -- Default: returns the following:+    --+    -- @+    -- CertificateUsageReject (CertificateRejectOther "no client certificates expected")+    -- @+    , onUnverifiedClientCert :: IO Bool+    -- ^ This action is called when the client certificate cannot be+    -- verified. Return 'True' to accept the certificate anyway, or+    -- 'False' to fail verification.+    --+    -- Default: returns 'False'+    , onCipherChoosing :: Version -> [Cipher] -> Cipher+    -- ^ Allow the server to choose the cipher relative to the the+    -- client version and the client list of ciphers.+    --+    -- This could be useful with old clients and as a workaround to+    -- the BEAST (where RC4 is sometimes preferred with TLS < 1.1)+    --+    -- The client cipher list cannot be empty.+    --+    -- Default: taking the head of ciphers.+    , onServerNameIndication :: Maybe HostName -> IO Credentials+    -- ^ Allow the server to indicate additional credentials to be+    -- used depending on the host name indicated by the client.+    --+    -- This is most useful for transparent proxies where credentials+    -- must be generated on the fly according to the host the client+    -- is trying to connect to.+    --+    -- Returned credentials may be ignored if a client does not+    -- support the signature algorithms used in the certificate chain.+    --+    -- Default: returns 'mempty'+    , onNewHandshake :: Measurement -> IO Bool+    -- ^ At each new handshake, we call this hook to see if we allow+    -- handshake to happens.+    --+    -- Default: returns 'True'+    , onALPNClientSuggest :: Maybe ([ByteString] -> IO ByteString)+    -- ^ Allow the server to choose an application layer protocol+    --   suggested from the client through the ALPN (Application Layer+    --   Protocol Negotiation) extensions.  If the server supports no+    --   protocols that the client advertises an empty 'ByteString'+    --   should be returned.+    --+    -- Default: 'Nothing'+    , onEncryptedExtensionsCreating :: [ExtensionRaw] -> IO [ExtensionRaw]+    -- ^ Allow to modify extensions to be sent in EncryptedExtensions+    --  of TLS 1.3.+    --+    -- Default: 'return'+    , onSelectKeyShare+        :: [[Group]]+        -> [Group]+        -> [Group]+        -> IO (Maybe Group, Bool)+    -- ^ A function to select one key share by TLS 1.3 server.+    --+    -- The 1st argument is server's 'supportedGroupsTLS13'.+    -- The 2nd arguments is client's groups in "supported_groups"+    -- The 3rd arguments is client's groups in "key_share".+    --+    -- 'True' in the result indicates sending a hello retry request+    -- with this group.+    --+    -- The default function targets @[Group]@ in the first argument in+    -- order.  If there is a common group among the "key_share"+    -- groups, it will use that group for key exchange.+    -- Alternatively, if there is a common group among the+    -- "supported_groups" groups, it will instruct to send the+    -- HelloRetryRequest using that group.  Otherwise, it will check+    -- the next @[Group]@.+    --+    -- @since 2.2.3+    }++-- | Default value for 'ServerHooks'+defaultServerHooks :: ServerHooks+defaultServerHooks =+    ServerHooks+        { onClientCertificate = \_ ->+            return $+                CertificateUsageReject $+                    CertificateRejectOther "no client certificates expected"+        , onUnverifiedClientCert = return False+        , onCipherChoosing = \_ ccs -> case ccs of+            [] -> error "onCipherChoosing"+            c : _ -> c+        , onServerNameIndication = \_ -> return mempty+        , onNewHandshake = \_ -> return True+        , onALPNClientSuggest = Nothing+        , onEncryptedExtensionsCreating = return+        , onSelectKeyShare = defaultOnSelectKeyShare+        }++instance Show ServerHooks where+    show _ = "ServerHooks"+instance Default ServerHooks where+    def = defaultServerHooks++defaultOnSelectKeyShare+    :: [[Group]] -- Server groups+    -> [Group] -- Client's groups in "supported_groups"+    -> [Group] -- Client's groups in "key_share"+    -> IO (Maybe Group, Bool)+defaultOnSelectKeyShare serverSupportedLoL clientSupportedGroups clientKeyShareGroups = go serverSupportedLoL+  where+    go [] = return (Nothing, False)+    go (gs : gss) = case gs `intersect` clientKeyShareGroups of+        [] -> case gs `intersect` clientSupportedGroups of+            [] -> go gss+            h : _ -> return (Just h, True)+        g : _ -> return (Just g, False)++-- | Information related to a running context, e.g. current cipher+data Information = Information+    { infoVersion :: Version+    , infoCipher :: Cipher+    , infoCompression :: Compression+    , infoMainSecret :: Maybe ByteString+    , infoExtendedMainSecret :: Bool+    , infoClientRandom :: Maybe ClientRandom+    , infoServerRandom :: Maybe ServerRandom+    , infoSupportedGroup :: Maybe Group+    , infoTLS12Resumption :: Bool+    , infoTLS13HandshakeMode :: Maybe HandshakeMode13+    , infoIsEarlyDataAccepted :: Bool+    , infoIsECHAccepted :: Bool+    }+    deriving (Show, Eq)++-- | Limitations for security.+--+-- @since 2.1.7+data Limit = Limit+    { limitRecordSize :: Maybe Int+    -- ^ Record size limit defined in RFC 8449.+    --+    -- If 'Nothing', the "record_size_limit" extension is not used.+    --+    -- In the case of 'Just': A client sends the "record_size_limit"+    -- extension with this value to the server. A server sends back+    -- this extension with its own value if a client sends the+    -- extension. When negotiated, both my limit and peer's limit are+    -- enabled for protected communication.+    --+    -- Default: Nothing+    , limitHandshakeFragment :: Int+    -- ^ The limit to accept the number of each handshake message.+    -- For instance, a nasty client may send many fragments of client+    -- certificate.+    --+    -- Default: 32+    }+    deriving (Eq, Show)++-- | Default value for 'Limit'.+defaultLimit :: Limit+defaultLimit =+    Limit+        { limitRecordSize = Nothing+        , limitHandshakeFragment = 32+        }
+ Network/TLS/PostHandshake.hs view
@@ -0,0 +1,33 @@+module Network.TLS.PostHandshake (+    requestCertificate,+    requestCertificateServer,+    postHandshakeAuthWith,+    postHandshakeAuthClientWith,+) where++import Network.TLS.Context.Internal+import Network.TLS.Handshake.Client+import Network.TLS.Handshake.Common+import Network.TLS.Handshake.Server+import Network.TLS.IO+import Network.TLS.Struct13++----------------------------------------------------------------++-- | Post-handshake certificate request with TLS 1.3.  Returns 'False'+-- if the request was impossible, i.e. the remote client supports+-- post-handshake authentication or the connection is established in+-- TLS 1.2. Returns 'True' if the client authentication succeeds. An+-- exception is thrown if the authentication fails. Server only.+requestCertificate :: Context -> IO Bool+requestCertificate ctx =+    checkValid ctx >> doRequestCertificate_ (ctxRoleParams ctx) ctx++-- | Handle a post-handshake authentication flight with TLS 1.3.  This+-- is called automatically by 'recvData', in a context where the read+-- lock is already taken. Client only.+postHandshakeAuthWith :: Context -> Handshake13 -> IO ()+postHandshakeAuthWith ctx hs =+    withWriteLock ctx $+        handleException ctx $+            doPostHandshakeAuthWith_ (ctxRoleParams ctx) ctx hs
+ Network/TLS/QUIC.hs view
@@ -0,0 +1,265 @@+{-# LANGUAGE OverloadedStrings #-}++-- |+-- API to run the TLS handshake establishing a QUIC connection.+--+-- On the northbound API:+--+-- * QUIC starts a TLS client or server thread with 'tlsQUICClient' or+--   'tlsQUICServer'.+--+--  TLS invokes QUIC callbacks to use the QUIC transport+--+-- * TLS uses 'quicSend' and 'quicRecv' to send and receive handshake message+--   fragments.+--+-- * TLS calls 'quicInstallKeys' to provide to QUIC the traffic secrets it+--   should use for encryption/decryption.+--+-- * TLS calls 'quicNotifyExtensions' to notify to QUIC the transport parameters+--   exchanged through the handshake protocol.+--+-- * TLS calls 'quicDone' when the handshake is done.+module Network.TLS.QUIC (+    -- * Handshakers+    tlsQUICClient,+    tlsQUICServer,++    -- * Callback+    QUICCallbacks (..),+    CryptLevel (..),+    KeyScheduleEvent (..),++    -- * Secrets+    EarlySecretInfo (..),+    HandshakeSecretInfo (..),+    ApplicationSecretInfo (..),+    EarlySecret,+    HandshakeSecret,+    ApplicationSecret,+    TrafficSecrets,+    ServerTrafficSecret (..),+    ClientTrafficSecret (..),++    -- * Negotiated parameters+    NegotiatedProtocol,+    HandshakeMode13 (..),++    -- * Extensions+    ExtensionRaw (..),+    ExtensionID (ExtensionID, EID_QuicTransportParameters),++    -- * Errors+    errorTLS,+    errorToAlertDescription,+    errorToAlertMessage,+    fromAlertDescription,+    toAlertDescription,++    -- * Hash+    hkdfExpandLabel,+    hkdfExtract,+    hashDigestSize,++    -- * Constants+    quicMaxEarlyDataSize,++    -- * Supported+    defaultSupported,+) where++import Data.Default (def)++import Network.TLS.Backend+import Network.TLS.Context+import Network.TLS.Context.Internal+import Network.TLS.Core+import Network.TLS.Crypto (hashDigestSize)+import Network.TLS.Crypto.Types+import Network.TLS.Extension+import Network.TLS.Extra.Cipher+import Network.TLS.Handshake.Common+import Network.TLS.Handshake.Control+import Network.TLS.Handshake.State+import Network.TLS.Handshake.State13+import Network.TLS.Imports+import Network.TLS.KeySchedule (hkdfExpandLabel, hkdfExtract)+import Network.TLS.Parameters hiding (defaultSupported)+import Network.TLS.Record.Layer+import Network.TLS.Record.State+import Network.TLS.Struct+import Network.TLS.Types++nullBackend :: Backend+nullBackend =+    Backend+        { backendFlush = return ()+        , backendClose = return ()+        , backendSend = \_ -> return ()+        , backendRecv = \_ -> return ""+        }++-- | Argument given to 'quicInstallKeys' when encryption material is available.+data KeyScheduleEvent+    = -- | Key material and parameters for traffic at 0-RTT level+      InstallEarlyKeys (Maybe EarlySecretInfo)+    | -- | Key material and parameters for traffic at handshake level+      InstallHandshakeKeys HandshakeSecretInfo+    | -- | Key material and parameters for traffic at application level+      InstallApplicationKeys ApplicationSecretInfo++-- | Callbacks implemented by QUIC and to be called by TLS at specific points+-- during the handshake.  TLS may invoke them from external threads but calls+-- are not concurrent.  Only a single callback function is called at a given+-- point in time.+data QUICCallbacks = QUICCallbacks+    { quicSend :: [(CryptLevel, ByteString)] -> IO ()+    -- ^ Called by TLS so that QUIC sends one or more handshake fragments. The+    -- content transiting on this API is the plaintext of the fragments and+    -- QUIC responsibility is to encrypt this payload with the key material+    -- given for the specified level and an appropriate encryption scheme.+    --+    -- The size of the fragments may exceed QUIC datagram limits so QUIC may+    -- break them into smaller fragments.+    --+    -- The handshake protocol sometimes combines content at two levels in a+    -- single flight.  The TLS library does its best to provide this in the+    -- same @quicSend@ call and with a multi-valued argument.  QUIC can then+    -- decide how to transmit this optimally.+    , quicRecv :: CryptLevel -> IO (Either TLSError ByteString)+    -- ^ Called by TLS to receive from QUIC the next plaintext handshake+    -- fragment.  The argument specifies with which encryption level the+    -- fragment should be decrypted.+    --+    -- QUIC may return partial fragments to TLS.  TLS will then call+    -- @quicRecv@ again as long as necessary.  Note however that fragments+    -- must be returned in the correct sequence, i.e. the order the TLS peer+    -- emitted them.+    --+    -- The function may return an error to TLS if end of stream is reached or+    -- if a protocol error has been received, believing the handshake cannot+    -- proceed any longer.  If the TLS handshake protocol cannot recover from+    -- this error, the failure condition will be reported back to QUIC through+    -- the control interface.+    , quicInstallKeys :: Context -> KeyScheduleEvent -> IO ()+    -- ^ Called by TLS when new encryption material is ready to be used in the+    -- handshake.  The next 'quicSend' or 'quicRecv' may now use the+    -- associated encryption level (although the previous level is also+    -- possible: directions Send/Recv do not change at the same time).+    , quicNotifyExtensions :: Context -> [ExtensionRaw] -> IO ()+    -- ^ Called by TLS when QUIC-specific extensions have been received from+    -- the peer.+    , quicDone :: Context -> IO ()+    -- ^ Called when 'handshake' is done. 'tlsQUICServer' is+    -- finished after calling this hook. 'tlsQUICClient' calls+    -- 'recvData' after calling this hook to wait for new session+    -- tickets.+    }++newRecordLayer+    :: QUICCallbacks+    -> RecordLayer [(CryptLevel, ByteString)]+newRecordLayer callbacks = newTransparentRecordLayer get send recv+  where+    get = getTxLevel+    send = quicSend callbacks+    recv ctx = getRxLevel ctx >>= quicRecv callbacks++-- | Start a TLS handshake thread for a QUIC client.  The client will use the+-- specified TLS parameters and call the provided callback functions to send and+-- receive handshake data.+tlsQUICClient :: ClientParams -> QUICCallbacks -> IO ()+tlsQUICClient cparams callbacks = do+    ctx0 <- contextNew nullBackend cparams+    mylimref <- newRecordLimitRef Nothing+    peerlimref <- newRecordLimitRef Nothing+    let ctx1 =+            ctx0+                { ctxHandshakeSync = HandshakeSync sync (\_ _ -> return ())+                , ctxMyRecordLimit = mylimref+                , ctxPeerRecordLimit = peerlimref+                , ctxQUICMode = True+                }+        rl = newRecordLayer callbacks+        ctx2 = updateRecordLayer rl ctx1+    handshake ctx2+    quicDone callbacks ctx2+    void $ recvData ctx2 -- waiting for new session tickets+  where+    sync ctx (SendClientHello mEarlySecInfo) =+        quicInstallKeys callbacks ctx (InstallEarlyKeys mEarlySecInfo)+    sync ctx (RecvServerHello handSecInfo) =+        quicInstallKeys callbacks ctx (InstallHandshakeKeys handSecInfo)+    sync ctx (SendClientFinished exts appSecInfo) = do+        let qexts = filterQTP exts+        when (null qexts) $ do+            throwCore $+                Error_Protocol "QUIC transport parameters are missing" MissingExtension+        quicNotifyExtensions callbacks ctx qexts+        quicInstallKeys callbacks ctx (InstallApplicationKeys appSecInfo)++-- | Start a TLS handshake thread for a QUIC server.  The server will use the+-- specified TLS parameters and call the provided callback functions to send and+-- receive handshake data.+tlsQUICServer :: ServerParams -> QUICCallbacks -> IO ()+tlsQUICServer sparams callbacks = do+    ctx0 <- contextNew nullBackend sparams+    mylimref <- newRecordLimitRef Nothing+    peerlimref <- newRecordLimitRef Nothing+    let ctx1 =+            ctx0+                { ctxHandshakeSync = HandshakeSync (\_ _ -> return ()) sync+                , ctxMyRecordLimit = mylimref+                , ctxPeerRecordLimit = peerlimref+                , ctxQUICMode = True+                }+        rl = newRecordLayer callbacks+        ctx2 = updateRecordLayer rl ctx1+    handshake ctx2+    quicDone callbacks ctx2+  where+    sync ctx (SendServerHello exts mEarlySecInfo handSecInfo) = do+        let qexts = filterQTP exts+        when (null qexts) $ do+            throwCore $+                Error_Protocol "QUIC transport parameters are missing" MissingExtension+        quicNotifyExtensions callbacks ctx qexts+        quicInstallKeys callbacks ctx (InstallEarlyKeys mEarlySecInfo)+        quicInstallKeys callbacks ctx (InstallHandshakeKeys handSecInfo)+    sync ctx (SendServerFinished appSecInfo) =+        quicInstallKeys callbacks ctx (InstallApplicationKeys appSecInfo)++filterQTP :: [ExtensionRaw] -> [ExtensionRaw]+filterQTP =+    filter+        (\(ExtensionRaw eid _) -> eid == EID_QuicTransportParameters)++-- | Can be used by callbacks to signal an unexpected condition.  This will then+-- generate an "internal_error" alert in the TLS stack.+errorTLS :: String -> IO a+errorTLS msg = throwCore $ Error_Protocol msg InternalError++-- | Return the alert that a TLS endpoint would send to the peer for the+-- specified library error.+errorToAlertDescription :: TLSError -> AlertDescription+errorToAlertDescription = snd . errorToAlert++-- | Decode an alert from the assigned value.+toAlertDescription :: Word8 -> AlertDescription+toAlertDescription = AlertDescription++defaultSupported :: Supported+defaultSupported =+    def+        { supportedVersions = [TLS13]+        , supportedCiphers =+            [ cipher13_AES_256_GCM_SHA384+            , cipher13_AES_128_GCM_SHA256+            , cipher13_AES_128_CCM_SHA256+            ]+        , supportedGroups = [X25519, X448, P256, P384, P521]+        }++-- | Max early data size for QUIC.+quicMaxEarlyDataSize :: Int+quicMaxEarlyDataSize = 0xffffffff
Network/TLS/RNG.hs view
@@ -1,17 +1,17 @@ {-# LANGUAGE GeneralizedNewtypeDeriving #-}-module Network.TLS.RNG-    ( StateRNG(..)-    , Seed-    , seedNew-    , seedToInteger-    , seedFromInteger-    , withTLSRNG-    , newStateRNG-    , MonadRandom-    , getRandomBytes-    ) where -import Crypto.Random.Types+module Network.TLS.RNG (+    StateRNG (..),+    Seed,+    seedNew,+    seedToInteger,+    seedFromInteger,+    withTLSRNG,+    newStateRNG,+    MonadRandom,+    getRandomBytes,+) where+ import Crypto.Random  newtype StateRNG = StateRNG ChaChaDRG@@ -20,9 +20,10 @@ instance Show StateRNG where     show _ = "rng[..]" -withTLSRNG :: StateRNG-           -> MonadPseudoRandom StateRNG a-           -> (a, StateRNG)+withTLSRNG+    :: StateRNG+    -> MonadPseudoRandom StateRNG a+    -> (a, StateRNG) withTLSRNG rng f = withDRG rng f  newStateRNG :: Seed -> StateRNG
− Network/TLS/Receiving.hs
@@ -1,75 +0,0 @@--- |--- Module      : Network.TLS.Receiving--- License     : BSD-style--- Maintainer  : Vincent Hanquez <vincent@snarc.org>--- Stability   : experimental--- Portability : unknown------ the Receiving module contains calls related to unmarshalling packets according--- to the TLS state----{-# LANGUAGE FlexibleContexts #-}--module Network.TLS.Receiving-    ( processPacket-    ) where--import Control.Monad.State.Strict-import Control.Concurrent.MVar--import Network.TLS.Context.Internal-import Network.TLS.Struct-import Network.TLS.ErrT-import Network.TLS.Record-import Network.TLS.Packet-import Network.TLS.Wire-import Network.TLS.State-import Network.TLS.Handshake.State-import Network.TLS.Cipher-import Network.TLS.Util-import Network.TLS.Imports--processPacket :: Context -> Record Plaintext -> IO (Either TLSError Packet)--processPacket _ (Record ProtocolType_AppData _ fragment) = return $ Right $ AppData $ fragmentGetBytes fragment--processPacket _ (Record ProtocolType_Alert _ fragment) = return (Alert `fmapEither` decodeAlerts (fragmentGetBytes fragment))--processPacket ctx (Record ProtocolType_ChangeCipherSpec _ fragment) =-    case decodeChangeCipherSpec $ fragmentGetBytes fragment of-        Left err -> return $ Left err-        Right _  -> do switchRxEncryption ctx-                       return $ Right ChangeCipherSpec--processPacket ctx (Record ProtocolType_Handshake ver fragment) = do-    keyxchg <- getHState ctx >>= \hs -> return (hs >>= hstPendingCipher >>= Just . cipherKeyExchange)-    usingState ctx $ do-        let currentParams = CurrentParams-                            { cParamsVersion     = ver-                            , cParamsKeyXchgType = keyxchg-                            }-        -- get back the optional continuation, and parse as many handshake record as possible.-        mCont <- gets stHandshakeRecordCont-        modify (\st -> st { stHandshakeRecordCont = Nothing })-        hss   <- parseMany currentParams mCont (fragmentGetBytes fragment)-        return $ Handshake hss-  where parseMany currentParams mCont bs =-            case fromMaybe decodeHandshakeRecord mCont bs of-                GotError err                -> throwError err-                GotPartial cont             -> modify (\st -> st { stHandshakeRecordCont = Just cont }) >> return []-                GotSuccess (ty,content)     ->-                    either throwError (return . (:[])) $ decodeHandshake currentParams ty content-                GotSuccessRemaining (ty,content) left ->-                    case decodeHandshake currentParams ty content of-                        Left err -> throwError err-                        Right hh -> (hh:) <$> parseMany currentParams Nothing left--processPacket _ (Record ProtocolType_DeprecatedHandshake _ fragment) =-    case decodeDeprecatedHandshake $ fragmentGetBytes fragment of-        Left err -> return $ Left err-        Right hs -> return $ Right $ Handshake [hs]--switchRxEncryption :: Context -> IO ()-switchRxEncryption ctx =-    usingHState ctx (gets hstPendingRxState) >>= \rx ->-    liftIO $ modifyMVar_ (ctxRxState ctx) (\_ -> return $ fromJust "rx-state" rx)
Network/TLS/Record.hs view
@@ -1,42 +1,36 @@--- |--- Module      : Network.TLS.Record--- License     : BSD-style--- Maintainer  : Vincent Hanquez <vincent@snarc.org>--- Stability   : experimental--- Portability : unknown------ The Record Protocol takes messages to be transmitted, fragments the--- data into manageable blocks, optionally compresses the data, applies--- a MAC, encrypts, and transmits the result.  Received data is--- decrypted, verified, decompressed, reassembled, and then delivered to--- higher-level clients.----module Network.TLS.Record-    ( Record(..)+-- | The Record Protocol takes messages to be transmitted, fragments+-- the data into manageable blocks, optionally compresses the data,+-- applies a MAC, encrypts, and transmits the result.  Received data+-- is decrypted, verified, decompressed, reassembled, and then+-- delivered to higher-level clients.+module Network.TLS.Record (+    Record (..),+     -- * Fragment manipulation types-    , Fragment-    , fragmentGetBytes-    , fragmentPlaintext-    , fragmentCiphertext-    , recordToRaw-    , rawToRecord-    , recordToHeader-    , Plaintext-    , Compressed-    , Ciphertext-    -- * Engage and disengage from the record layer-    , engageRecord-    , disengageRecord+    Fragment,+    fragmentGetBytes,+    fragmentPlaintext,+    fragmentCiphertext,+    recordToRaw,+    rawToRecord,+    recordToHeader,+    Plaintext,+    Ciphertext,++    -- * Encrypt and decrypt from the record layer+    encryptRecord,+    decryptRecord,+     -- * State tracking-    , RecordM-    , runRecordM-    , RecordState(..)-    , newRecordState-    , getRecordVersion-    , setRecordIV-    ) where+    RecordM,+    runRecordM,+    RecordState (..),+    newRecordState,+    getRecordVersion,+    setRecordIV,+) where -import Network.TLS.Record.Types-import Network.TLS.Record.Engage-import Network.TLS.Record.Disengage+import Network.TLS.Record.Decrypt+import Network.TLS.Record.Encrypt import Network.TLS.Record.State+import Network.TLS.Record.Types
+ Network/TLS/Record/Decrypt.hs view
@@ -0,0 +1,207 @@+{-# LANGUAGE FlexibleContexts #-}++module Network.TLS.Record.Decrypt (+    decryptRecord,+) where++import Control.Monad.State.Strict+import Crypto.Cipher.Types (AuthTag (..))+import Data.ByteArray (convert)+import qualified Data.ByteArray as BA+import qualified Data.ByteString as B++import Network.TLS.Cipher+import Network.TLS.Crypto+import Network.TLS.ErrT+import Network.TLS.Imports+import Network.TLS.Packet+import Network.TLS.Record.State+import Network.TLS.Record.Types+import Network.TLS.Struct+import Network.TLS.Util+import Network.TLS.Wire++decryptRecord :: Record Ciphertext -> Int -> RecordM (Record Plaintext)+decryptRecord record@(Record ct ver fragment) lim = do+    st <- get+    case stCipher st of+        Nothing -> noDecryption+        _ -> do+            recOpts <- getRecordOptions+            let mver = recordVersion recOpts+            if recordTLS13 recOpts+                then decryptData13 mver (fragmentGetBytes fragment) st+                else onRecordFragment record $ fragmentUncipher $ \e ->+                    decryptData mver record e st lim+  where+    noDecryption = onRecordFragment record $ fragmentUncipher $ checkPlainLimit lim+    decryptData13 mver e st = case ct of+        ProtocolType_AppData -> do+            inner <- decryptData mver record e st (lim + 1)+            case unInnerPlaintext inner of+                Left message -> throwError $ Error_Protocol message UnexpectedMessage+                Right (ct', d) -> return $ Record ct' ver $ fragmentPlaintext d+        ProtocolType_ChangeCipherSpec -> noDecryption+        ProtocolType_Alert -> noDecryption+        _ ->+            throwError $ Error_Protocol "illegal plain text" UnexpectedMessage++unInnerPlaintext :: ByteString -> Either String (ProtocolType, ByteString)+unInnerPlaintext inner =+    case B.unsnoc dc of+        Nothing -> Left $ unknownContentType13 (0 :: Word8)+        Just (bytes, c)+            | B.null bytes && ProtocolType c `elem` nonEmptyContentTypes ->+                Left ("empty " ++ show (ProtocolType c) ++ " record disallowed")+            | otherwise -> Right (ProtocolType c, bytes)+  where+    (dc, _pad) = B.spanEnd (== 0) inner+    nonEmptyContentTypes = [ProtocolType_Handshake, ProtocolType_Alert]+    unknownContentType13 c = "unknown TLS 1.3 content type: " ++ show c++getCipherData :: Record a -> CipherData -> RecordM ByteString+getCipherData (Record pt ver _) cdata = do+    -- check if the MAC is valid.+    macValid <- case cipherDataMAC cdata of+        Nothing -> return True+        Just digest -> do+            let new_hdr = Header pt ver (fromIntegral $ B.length $ cipherDataContent cdata)+            expected_digest <- makeDigest new_hdr $ cipherDataContent cdata+            return (expected_digest == digest)++    -- check if the padding is filled with the correct pattern if it exists+    -- (before TLS10 this checks instead that the padding length is minimal)+    paddingValid <- case cipherDataPadding cdata of+        Nothing -> return True+        Just (pad, _blksz) -> do+            let b = B.length pad - 1+            return $ B.replicate (B.length pad) (fromIntegral b) == pad++    unless (macValid &&! paddingValid) $+        throwError $+            Error_Protocol "bad record mac Stream/Block" BadRecordMac++    return $ cipherDataContent cdata++checkPlainLimit :: Int -> ByteString -> RecordM ByteString+checkPlainLimit lim plain+    | len > lim =+        throwError $+            Error_Protocol+                ( "plaintext exceeding record size limit: "+                    ++ show len+                    ++ " > "+                    ++ show lim+                )+                RecordOverflow+    | otherwise = return plain+  where+    len = B.length plain++decryptData+    :: Version+    -> Record Ciphertext+    -> ByteString+    -> RecordState+    -> Int+    -> RecordM ByteString+decryptData ver record econtent tst lim =+    decryptOf (cstKey cst) >>= checkPlainLimit lim+  where+    cipher = fromJust $ stCipher tst+    bulk = cipherBulk cipher+    cst = stCryptState tst+    macSize = hashDigestSize $ cipherHash cipher+    blockSize = bulkBlockSize bulk+    econtentLen = B.length econtent++    sanityCheckError =+        throwError+            (Error_Packet "encrypted content too small for encryption parameters")++    decryptOf :: BulkState -> RecordM ByteString+    decryptOf (BulkStateBlock decryptF) = do+        let minContent = bulkIVSize bulk + max (macSize + 1) blockSize++        -- check if we have enough bytes to cover the minimum for this cipher+        when+            ((econtentLen `mod` blockSize) /= 0 || econtentLen < minContent)+            sanityCheckError++        {- update IV -}+        (iv, econtent') <-+            get2o econtent (bulkIVSize bulk, econtentLen - bulkIVSize bulk)+        let (content', iv') = decryptF iv econtent'+        modify' $ \txs -> txs{stCryptState = cst{cstIV = iv'}}++        let paddinglength = fromIntegral (B.last content') + 1+        let contentlen = B.length content' - paddinglength - macSize+        (content, mac, padding) <- get3i content' (contentlen, macSize, paddinglength)+        getCipherData+            record+            CipherData+                { cipherDataContent = content+                , cipherDataMAC = Just mac+                , cipherDataPadding = Just (padding, blockSize)+                }+    decryptOf (BulkStateStream (BulkStream decryptF)) = do+        -- check if we have enough bytes to cover the minimum for this cipher+        when (econtentLen < macSize) sanityCheckError++        let (content', bulkStream') = decryptF econtent+        {- update Ctx -}+        let contentlen = B.length content' - macSize+        (content, mac) <- get2i content' (contentlen, macSize)+        modify' $ \txs -> txs{stCryptState = cst{cstKey = BulkStateStream bulkStream'}}+        getCipherData+            record+            CipherData+                { cipherDataContent = content+                , cipherDataMAC = Just mac+                , cipherDataPadding = Nothing+                }+    decryptOf (BulkStateAEAD decryptF) = do+        let authTagLen = bulkAuthTagLen bulk+            nonceExpLen = bulkExplicitIV bulk+            cipherLen = econtentLen - authTagLen - nonceExpLen++        -- check if we have enough bytes to cover the minimum for this cipher+        when (econtentLen < (authTagLen + nonceExpLen)) sanityCheckError++        (enonce, econtent', authTag) <-+            get3o econtent (nonceExpLen, cipherLen, authTagLen)+        let encodedSeq = encodeWord64 $ msSequence $ stMacState tst+            iv = cstIV (stCryptState tst)+            ivlen = B.length iv+            Header typ v _ = recordToHeader record+            hdrLen = if ver >= TLS13 then econtentLen else cipherLen+            hdr = Header typ v $ fromIntegral hdrLen+            ad+                | ver >= TLS13 = encodeHeader hdr+                | otherwise = B.concat [encodedSeq, encodeHeader hdr]+            sqnc = B.replicate (ivlen - 8) 0 `B.append` encodedSeq+            nonce+                | nonceExpLen == 0 = BA.xor iv sqnc+                | otherwise = iv `B.append` enonce+            (content, authTag2) = decryptF nonce econtent' ad++        when (AuthTag (convert authTag) /= authTag2) $+            throwError $+                Error_Protocol "bad record mac on AEAD" BadRecordMac++        modify' incrRecordState+        return content+    decryptOf BulkStateUninitialized =+        throwError $ Error_Protocol "decrypt state uninitialized" InternalError++    -- handling of outer format can report errors with Error_Packet+    get3o s ls =+        maybe (throwError $ Error_Packet "record bad format") return $ partition3 s ls+    get2o s (d1, d2) = get3o s (d1, d2, 0) >>= \(r1, r2, _) -> return (r1, r2)++    -- all format errors related to decrypted content are reported+    -- externally as integrity failures, i.e. BadRecordMac+    get3i s ls =+        maybe (throwError $ Error_Protocol "record bad format" BadRecordMac) return $+            partition3 s ls+    get2i s (d1, d2) = get3i s (d1, d2, 0) >>= \(r1, r2, _) -> return (r1, r2)
− Network/TLS/Record/Disengage.hs
@@ -1,149 +0,0 @@--- |--- Module      : Network.TLS.Record.Disengage--- License     : BSD-style--- Maintainer  : Vincent Hanquez <vincent@snarc.org>--- Stability   : experimental--- Portability : unknown------ Disengage a record from the Record layer.--- The record is decrypted, checked for integrity and then decompressed.----{-# LANGUAGE FlexibleContexts #-}--module Network.TLS.Record.Disengage-        ( disengageRecord-        ) where--import Control.Monad.State.Strict--import Crypto.Cipher.Types (AuthTag(..))-import Network.TLS.Struct-import Network.TLS.ErrT-import Network.TLS.Cap-import Network.TLS.Record.State-import Network.TLS.Record.Types-import Network.TLS.Cipher-import Network.TLS.Crypto-import Network.TLS.Compression-import Network.TLS.Util-import Network.TLS.Wire-import Network.TLS.Packet-import Network.TLS.Imports-import qualified Data.ByteString as B-import qualified Data.ByteArray as B (convert)--disengageRecord :: Record Ciphertext -> RecordM (Record Plaintext)-disengageRecord = decryptRecord >=> uncompressRecord--uncompressRecord :: Record Compressed -> RecordM (Record Plaintext)-uncompressRecord record = onRecordFragment record $ fragmentUncompress $ \bytes ->-    withCompression $ compressionInflate bytes--decryptRecord :: Record Ciphertext -> RecordM (Record Compressed)-decryptRecord record = onRecordFragment record $ fragmentUncipher $ \e -> do-    st <- get-    case stCipher st of-        Nothing -> return e-        _       -> getRecordVersion >>= \ver -> decryptData ver record e st--getCipherData :: Record a -> CipherData -> RecordM ByteString-getCipherData (Record pt ver _) cdata = do-    -- check if the MAC is valid.-    macValid <- case cipherDataMAC cdata of-        Nothing     -> return True-        Just digest -> do-            let new_hdr = Header pt ver (fromIntegral $ B.length $ cipherDataContent cdata)-            expected_digest <- makeDigest new_hdr $ cipherDataContent cdata-            return (expected_digest `bytesEq` digest)--    -- check if the padding is filled with the correct pattern if it exists-    paddingValid <- case cipherDataPadding cdata of-        Nothing  -> return True-        Just pad -> do-            cver <- getRecordVersion-            let b = B.length pad - 1-            return (cver < TLS10 || B.replicate (B.length pad) (fromIntegral b) `bytesEq` pad)--    unless (macValid &&! paddingValid) $ do-        throwError $ Error_Protocol ("bad record mac", True, BadRecordMac)--    return $ cipherDataContent cdata--decryptData :: Version -> Record Ciphertext -> ByteString -> RecordState -> RecordM ByteString-decryptData ver record econtent tst = decryptOf (cstKey cst)-  where cipher     = fromJust "cipher" $ stCipher tst-        bulk       = cipherBulk cipher-        cst        = stCryptState tst-        macSize    = hashDigestSize $ cipherHash cipher-        blockSize  = bulkBlockSize bulk-        econtentLen = B.length econtent--        explicitIV = hasExplicitBlockIV ver--        sanityCheckError = throwError (Error_Packet "encrypted content too small for encryption parameters")--        decryptOf :: BulkState -> RecordM ByteString-        decryptOf (BulkStateBlock decryptF) = do-            let minContent = (if explicitIV then bulkIVSize bulk else 0) + max (macSize + 1) blockSize--            -- check if we have enough bytes to cover the minimum for this cipher-            when ((econtentLen `mod` blockSize) /= 0 || econtentLen < minContent) sanityCheckError--            {- update IV -}-            (iv, econtent') <- if explicitIV-                                  then get2 econtent (bulkIVSize bulk, econtentLen - bulkIVSize bulk)-                                  else return (cstIV cst, econtent)-            let (content', iv') = decryptF iv econtent'-            modify $ \txs -> txs { stCryptState = cst { cstIV = iv' } }--            let paddinglength = fromIntegral (B.last content') + 1-            let contentlen = B.length content' - paddinglength - macSize-            (content, mac, padding) <- get3 content' (contentlen, macSize, paddinglength)-            getCipherData record CipherData-                    { cipherDataContent = content-                    , cipherDataMAC     = Just mac-                    , cipherDataPadding = Just padding-                    }--        decryptOf (BulkStateStream (BulkStream decryptF)) = do-            -- check if we have enough bytes to cover the minimum for this cipher-            when (econtentLen < macSize) sanityCheckError--            let (content', bulkStream') = decryptF econtent-            {- update Ctx -}-            let contentlen        = B.length content' - macSize-            (content, mac) <- get2 content' (contentlen, macSize)-            modify $ \txs -> txs { stCryptState = cst { cstKey = BulkStateStream bulkStream' } }-            getCipherData record CipherData-                    { cipherDataContent = content-                    , cipherDataMAC     = Just mac-                    , cipherDataPadding = Nothing-                    }--        decryptOf (BulkStateAEAD decryptF) = do-            let authTagLen  = bulkAuthTagLen bulk-                nonceExpLen = bulkExplicitIV bulk-                cipherLen   = econtentLen - authTagLen - nonceExpLen--            -- check if we have enough bytes to cover the minimum for this cipher-            when (econtentLen < (authTagLen + nonceExpLen)) sanityCheckError--            (enonce, econtent', authTag) <- get3 econtent (nonceExpLen, cipherLen, authTagLen)-            let encodedSeq = encodeWord64 $ msSequence $ stMacState tst-                Header typ v _ = recordToHeader record-                hdr = Header typ v $ fromIntegral cipherLen-                ad = B.concat [ encodedSeq, encodeHeader hdr ]-                nonce = cstIV (stCryptState tst) `B.append` enonce-                (content, authTag2) = decryptF nonce econtent' ad--            when (AuthTag (B.convert authTag) /= authTag2) $-                throwError $ Error_Protocol ("bad record mac", True, BadRecordMac)--            modify incrRecordState-            return content--        decryptOf BulkStateUninitialized =-            throwError $ Error_Protocol ("decrypt state uninitialized", True, InternalError)--        get3 s ls = maybe (throwError $ Error_Packet "record bad format") return $ partition3 s ls-        get2 s (d1,d2) = get3 s (d1,d2,0) >>= \(r1,r2,_) -> return (r1,r2)
+ Network/TLS/Record/Encrypt.hs view
@@ -0,0 +1,131 @@+{-# LANGUAGE BangPatterns #-}++-- |+-- Engage a record into the Record layer.+-- The record is compressed, added some integrity field, then encrypted.+--+-- Starting with TLS v1.3, only the "null" compression method is negotiated in+-- the handshake, so the compression step will be a no-op.  Integrity and+-- encryption are performed using an AEAD cipher only.+module Network.TLS.Record.Encrypt (+    encryptRecord,+) where++import Control.Monad.State.Strict+import Crypto.Cipher.Types (AuthTag (..))+import Data.ByteArray (convert)+import qualified Data.ByteArray as BA+import qualified Data.ByteString as B++import Network.TLS.Cipher+import Network.TLS.Imports+import Network.TLS.Packet+import Network.TLS.Record.State+import Network.TLS.Record.Types+import Network.TLS.Wire++-- when Tx Encrypted is set, we pass the data through encryptContent, otherwise+-- we just return the compress payload directly as the ciphered one+--+encryptRecord :: Record Plaintext -> RecordM (Record Ciphertext)+encryptRecord record@(Record ct ver fragment) = do+    st <- get+    case stCipher st of+        Nothing -> noEncryption+        _ -> do+            recOpts <- getRecordOptions+            if recordTLS13 recOpts+                then encryptContent13+                else onRecordFragment record $ fragmentCipher (encryptContent False record)+  where+    noEncryption = onRecordFragment record $ fragmentCipher return+    encryptContent13+        | ct == ProtocolType_ChangeCipherSpec = noEncryption+        | otherwise = do+            let bytes = fragmentGetBytes fragment+                fragment' = fragmentPlaintext $ innerPlaintext ct bytes+                record' = Record ProtocolType_AppData ver fragment'+            onRecordFragment record' $ fragmentCipher (encryptContent True record')++innerPlaintext :: ProtocolType -> ByteString -> ByteString+innerPlaintext (ProtocolType c) bytes = runPut $ do+    putBytes bytes+    putWord8 c -- non zero!+    -- fixme: zeros padding++encryptContent :: Bool -> Record Plaintext -> ByteString -> RecordM ByteString+encryptContent tls13 record content = do+    cst <- getCryptState+    bulk <- getBulk+    case cstKey cst of+        BulkStateBlock encryptF -> do+            digest <- makeDigest (recordToHeader record) content+            let content' = B.concat [content, digest]+            encryptBlock encryptF content' bulk+        BulkStateStream encryptF -> do+            digest <- makeDigest (recordToHeader record) content+            let content' = B.concat [content, digest]+            encryptStream encryptF content'+        BulkStateAEAD encryptF ->+            encryptAead tls13 bulk encryptF content record+        BulkStateUninitialized ->+            return content++encryptBlock :: BulkBlock -> ByteString -> Bulk -> RecordM ByteString+encryptBlock encryptF content bulk = do+    cst <- getCryptState+    let blockSize = fromIntegral $ bulkBlockSize bulk+    let msg_len = B.length content+    let padding =+            if blockSize > 0+                then+                    let padbyte = blockSize - (msg_len `mod` blockSize)+                     in let padbyte' = if padbyte == 0 then blockSize else padbyte+                         in B.replicate padbyte' (fromIntegral (padbyte' - 1))+                else B.empty++    let (e, _iv') = encryptF (cstIV cst) $ B.concat [content, padding]++    return $ B.concat [cstIV cst, e]++encryptStream :: BulkStream -> ByteString -> RecordM ByteString+encryptStream (BulkStream encryptF) content = do+    cst <- getCryptState+    let (!e, !newBulkStream) = encryptF content+    modify' $ \tstate -> tstate{stCryptState = cst{cstKey = BulkStateStream newBulkStream}}+    return e++encryptAead+    :: Bool+    -> Bulk+    -> BulkAEAD+    -> ByteString+    -> Record Plaintext+    -> RecordM ByteString+encryptAead tls13 bulk encryptF content record = do+    let authTagLen = bulkAuthTagLen bulk+        nonceExpLen = bulkExplicitIV bulk+    cst <- getCryptState+    encodedSeq <- encodeWord64 <$> getMacSequence++    let iv = cstIV cst+        ivlen = B.length iv+        Header typ v plainLen = recordToHeader record+        hdrLen = if tls13 then plainLen + fromIntegral authTagLen else plainLen+        hdr = Header typ v hdrLen+        ad+            | tls13 = encodeHeader hdr+            | otherwise = B.concat [encodedSeq, encodeHeader hdr]+        sqnc = B.replicate (ivlen - 8) 0 `B.append` encodedSeq+        nonce+            | nonceExpLen == 0 = BA.xor iv sqnc+            | otherwise = B.concat [iv, encodedSeq]+        (e, AuthTag authtag) = encryptF nonce content ad+        econtent+            | nonceExpLen == 0 = e `B.append` convert authtag+            | otherwise = B.concat [encodedSeq, e, convert authtag]+    modify' incrRecordState+    return econtent++getCryptState :: RecordM CryptState+getCryptState = stCryptState <$> get
− Network/TLS/Record/Engage.hs
@@ -1,109 +0,0 @@--- |--- Module      : Network.TLS.Record.Engage--- License     : BSD-style--- Maintainer  : Vincent Hanquez <vincent@snarc.org>--- Stability   : experimental--- Portability : unknown------ Engage a record into the Record layer.--- The record is compressed, added some integrity field, then encrypted.----{-# LANGUAGE BangPatterns #-}-module Network.TLS.Record.Engage-        ( engageRecord-        ) where--import Control.Monad.State.Strict-import Crypto.Cipher.Types (AuthTag(..))--import Network.TLS.Cap-import Network.TLS.Record.State-import Network.TLS.Record.Types-import Network.TLS.Cipher-import Network.TLS.Compression-import Network.TLS.Wire-import Network.TLS.Packet-import Network.TLS.Imports-import qualified Data.ByteString as B-import qualified Data.ByteArray as B (convert)--engageRecord :: Record Plaintext -> RecordM (Record Ciphertext)-engageRecord = compressRecord >=> encryptRecord--compressRecord :: Record Plaintext -> RecordM (Record Compressed)-compressRecord record =-    onRecordFragment record $ fragmentCompress $ \bytes -> do-        withCompression $ compressionDeflate bytes---- when Tx Encrypted is set, we pass the data through encryptContent, otherwise--- we just return the compress payload directly as the ciphered one----encryptRecord :: Record Compressed -> RecordM (Record Ciphertext)-encryptRecord record = onRecordFragment record $ fragmentCipher $ \bytes -> do-    st <- get-    case stCipher st of-        Nothing -> return bytes-        _       -> encryptContent record bytes--encryptContent :: Record Compressed -> ByteString -> RecordM ByteString-encryptContent record content = do-    cst  <- getCryptState-    bulk <- getBulk-    case cstKey cst of-        BulkStateBlock encryptF -> do-            digest <- makeDigest (recordToHeader record) content-            let content' =  B.concat [content, digest]-            encryptBlock encryptF content' bulk-        BulkStateStream encryptF -> do-            digest <- makeDigest (recordToHeader record) content-            let content' =  B.concat [content, digest]-            encryptStream encryptF content'-        BulkStateAEAD encryptF ->-            encryptAead encryptF content record-        BulkStateUninitialized ->-            return content--encryptBlock :: BulkBlock -> ByteString -> Bulk -> RecordM ByteString-encryptBlock encryptF content bulk = do-    cst <- getCryptState-    ver <- getRecordVersion-    let blockSize = fromIntegral $ bulkBlockSize bulk-    let msg_len = B.length content-    let padding = if blockSize > 0-                  then-                      let padbyte = blockSize - (msg_len `mod` blockSize) in-                      let padbyte' = if padbyte == 0 then blockSize else padbyte in B.replicate padbyte' (fromIntegral (padbyte' - 1))-                  else-                      B.empty--    let (e, iv') = encryptF (cstIV cst) $ B.concat [ content, padding ]--    if hasExplicitBlockIV ver-        then return $ B.concat [cstIV cst,e]-        else do-            modify $ \tstate -> tstate { stCryptState = cst { cstIV = iv' } }-            return e--encryptStream :: BulkStream -> ByteString -> RecordM ByteString-encryptStream (BulkStream encryptF) content = do-    cst <- getCryptState-    let (!e, !newBulkStream) = encryptF content-    modify $ \tstate -> tstate { stCryptState = cst { cstKey = BulkStateStream newBulkStream } }-    return e--encryptAead :: BulkAEAD-            -> ByteString -> Record Compressed-            -> RecordM ByteString-encryptAead encryptF content record = do-    cst        <- getCryptState-    encodedSeq <- encodeWord64 <$> getMacSequence--    let hdr   = recordToHeader record-        ad    = B.concat [encodedSeq, encodeHeader hdr]-        nonce = B.concat [cstIV cst, encodedSeq]-        (e, AuthTag authtag) = encryptF nonce content ad-    modify incrRecordState-    return $ B.concat [encodedSeq, e, B.convert authtag]--getCryptState :: RecordM CryptState-getCryptState = stCryptState <$> get
+ Network/TLS/Record/Layer.hs view
@@ -0,0 +1,64 @@+module Network.TLS.Record.Layer (+    RecordLayer (..),+    newTransparentRecordLayer,+) where++import Network.TLS.Context+import Network.TLS.Imports+import Network.TLS.Record+import Network.TLS.Struct++import qualified Data.ByteString as B++newTransparentRecordLayer+    :: Eq ann+    => (Context -> IO ann)+    -> ([(ann, ByteString)] -> IO ())+    -> (Context -> IO (Either TLSError ByteString))+    -> RecordLayer [(ann, ByteString)]+newTransparentRecordLayer get send recv =+    RecordLayer+        { recordEncode12 = transparentEncodeRecord get+        , recordEncode13 = transparentEncodeRecord get+        , recordSendBytes = transparentSendBytes send+        , recordRecv12 = transparentRecvRecord recv+        , recordRecv13 = transparentRecvRecord recv+        }++transparentEncodeRecord+    :: (Context -> IO ann)+    -> Context+    -> Record Plaintext+    -> IO (Either TLSError [(ann, ByteString)])+transparentEncodeRecord _ _ (Record ProtocolType_ChangeCipherSpec _ _) =+    return $ Right []+transparentEncodeRecord _ _ (Record ProtocolType_Alert _ _) =+    -- all alerts are silent and must be transported externally based on+    -- TLS exceptions raised by the library+    return $ Right []+transparentEncodeRecord get ctx (Record _ _ frag) =+    get ctx >>= \a -> return $ Right [(a, fragmentGetBytes frag)]++transparentSendBytes+    :: Eq ann+    => ([(ann, ByteString)] -> IO ())+    -> Context+    -> [(ann, ByteString)]+    -> IO ()+transparentSendBytes send _ input =+    send+        [ (a, bs) | (a, frgs) <- compress input, let bs = B.concat frgs, not (B.null bs)+        ]++transparentRecvRecord+    :: (Context -> IO (Either TLSError ByteString))+    -> Context+    -> IO (Either TLSError (Record Plaintext))+transparentRecvRecord recv ctx =+    fmap (Record ProtocolType_Handshake TLS12 . fragmentPlaintext) <$> recv ctx++compress :: Eq ann => [(ann, val)] -> [(ann, [val])]+compress [] = []+compress ((a, v) : xs) =+    let (ys, zs) = span ((== a) . fst) xs+     in (a, v : map snd ys) : compress zs
+ Network/TLS/Record/Recv.hs view
@@ -0,0 +1,112 @@+-- | TLS record layer in Rx direction+module Network.TLS.Record.Recv (+    recvRecord12,+    recvRecord13,+) where++import qualified Data.ByteString as B++import Network.TLS.Context.Internal+import Network.TLS.Hooks+import Network.TLS.Imports+import Network.TLS.Packet+import Network.TLS.Record+import Network.TLS.Struct+import Network.TLS.Types++----------------------------------------------------------------++getMyPlainLimit :: Context -> IO Int+getMyPlainLimit ctx = do+    msiz <- getMyRecordLimit ctx+    return $ case msiz of+        Nothing -> defaultRecordSizeLimit+        Just siz -> siz++getRecord+    :: Context+    -> Header+    -> ByteString+    -> IO (Either TLSError (Record Plaintext))+getRecord ctx header content = do+    withLog ctx $ \logging -> loggingIORecv logging header content+    lim <- getMyPlainLimit ctx+    runRxRecordState ctx $ do+        let erecord = rawToRecord header $ fragmentCiphertext content+        decryptRecord erecord lim++----------------------------------------------------------------++exceedsTLSCiphertext :: Int -> Word16 -> Bool+exceedsTLSCiphertext overhead actual =+    -- In TLS 1.3, overhead is included one more byte for content type.+    fromIntegral actual > defaultRecordSizeLimit + overhead++-- | recvRecord receive a full TLS record (header + data), from the other side.+--+-- The record is disengaged from the record layer+recvRecord12+    :: Context+    -- ^ TLS context+    -> IO (Either TLSError (Record Plaintext))+recvRecord12 ctx =+    readExactBytes ctx 5 >>= either (return . Left) (recvLengthE . decodeHeader)+  where+    recvLengthE = either (return . Left) recvLength++    recvLength header@(Header _ _ readlen) = do+        -- RFC 5246 Section 7.2.2+        -- A TLSCiphertext record was received that had a length more+        -- than 2^14+2048 bytes, or a record decrypted to a+        -- TLSCompressed record with more than 2^14+1024 bytes.  This+        -- message is always fatal and should never be observed in+        -- communication between proper implementations (except when+        -- messages were corrupted in the network).+        if exceedsTLSCiphertext 2048 readlen+            then return $ Left maximumSizeExceeded+            else+                readExactBytes ctx (fromIntegral readlen)+                    >>= either (return . Left) (getRecord ctx header)++recvRecord13 :: Context -> IO (Either TLSError (Record Plaintext))+recvRecord13 ctx = readExactBytes ctx 5 >>= either (return . Left) (recvLengthE . decodeHeader)+  where+    recvLengthE = either (return . Left) recvLength+    recvLength header@(Header _ _ readlen) = do+        -- RFC 8446 Section 5.2:+        -- An AEAD algorithm used in TLS 1.3 MUST NOT produce an+        -- expansion greater than 255 octets.  An endpoint that+        -- receives a record from its peer with TLSCiphertext.length+        -- larger than 2^14 + 256 octets MUST terminate the connection+        -- with a "record_overflow" alert.  This limit is derived from+        -- the maximum TLSInnerPlaintext length of 2^14 octets + 1+        -- octet for ContentType + the maximum AEAD expansion of 255+        -- octets.+        if exceedsTLSCiphertext 256 readlen+            then return $ Left maximumSizeExceeded+            else+                readExactBytes ctx (fromIntegral readlen)+                    >>= either (return . Left) (getRecord ctx header)++maximumSizeExceeded :: TLSError+maximumSizeExceeded = Error_Protocol "record exceeding maximum size" RecordOverflow++----------------------------------------------------------------++readExactBytes :: Context -> Int -> IO (Either TLSError ByteString)+readExactBytes ctx sz = do+    hdrbs <- contextRecv ctx sz+    if B.length hdrbs == sz+        then return $ Right hdrbs+        else do+            setEOF ctx+            return . Left $+                if B.null hdrbs+                    then Error_EOF+                    else+                        Error_Packet+                            ( "partial packet: expecting "+                                ++ show sz+                                ++ " bytes, got: "+                                ++ show (B.length hdrbs)+                            )
+ Network/TLS/Record/Send.hs view
@@ -0,0 +1,61 @@+-- | TLS record layer in Tx direction+module Network.TLS.Record.Send (+    encodeRecord12,+    encodeRecord13,+    sendBytes,+) where++import Control.Concurrent.MVar+import Control.Monad.State.Strict+import qualified Data.ByteString as B++import Network.TLS.Cipher+import Network.TLS.Context.Internal+import Network.TLS.Hooks+import Network.TLS.Imports+import Network.TLS.Packet+import Network.TLS.Record+import Network.TLS.Struct++encodeRecordM :: Record Plaintext -> RecordM ByteString+encodeRecordM record = do+    erecord <- encryptRecord record+    let (hdr, content) = recordToRaw erecord+    return $ B.concat [encodeHeader hdr, content]++----------------------------------------------------------------++encodeRecord12 :: Context -> Record Plaintext -> IO (Either TLSError ByteString)+encodeRecord12 ctx = prepareRecord12 ctx . encodeRecordM++-- before TLS 1.1, the block cipher IV is made of the residual of the previous block,+-- so we use cstIV as is, however in other case we generate an explicit IV+prepareRecord12 :: Context -> RecordM a -> IO (Either TLSError a)+prepareRecord12 ctx f = do+    txState <- readMVar $ ctxTxRecordState ctx+    let sz = case stCipher txState of+            Nothing -> 0+            Just cipher ->+                if hasRecordIV $ bulkF $ cipherBulk cipher+                    then bulkIVSize $ cipherBulk cipher+                    else 0 -- to not generate IV+    if sz > 0+        then do+            newIV <- getStateRNG ctx sz+            runTxRecordState ctx (modify' (setRecordIV newIV) >> f)+        else runTxRecordState ctx f++----------------------------------------------------------------++encodeRecord13 :: Context -> Record Plaintext -> IO (Either TLSError ByteString)+encodeRecord13 ctx = prepareRecord13 ctx . encodeRecordM++prepareRecord13 :: Context -> RecordM a -> IO (Either TLSError a)+prepareRecord13 = runTxRecordState++----------------------------------------------------------------++sendBytes :: Context -> ByteString -> IO ()+sendBytes ctx dataToSend = do+    withLog ctx $ \logging -> loggingIOSent logging dataToSend+    contextSend ctx dataToSend
Network/TLS/Record/State.hs view
@@ -1,131 +1,166 @@ {-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE CPP #-}--- |--- Module      : Network.TLS.Record.State--- License     : BSD-style--- Maintainer  : Vincent Hanquez <vincent@snarc.org>--- Stability   : experimental--- Portability : unknown----module Network.TLS.Record.State-    ( CryptState(..)-    , MacState(..)-    , RecordState(..)-    , newRecordState-    , incrRecordState-    , RecordM-    , runRecordM-    , getRecordVersion-    , setRecordIV-    , withCompression-    , computeDigest-    , makeDigest-    , getBulk-    , getMacSequence-    ) where +module Network.TLS.Record.State (+    CryptState (..),+    CryptLevel (..),+    HasCryptLevel (..),+    MacState (..),+    RecordOptions (..),+    RecordState (..),+    newRecordState,+    incrRecordState,+    RecordM,+    runRecordM,+    getRecordOptions,+    getRecordVersion,+    setRecordIV,+    withCompression,+    computeDigest,+    makeDigest,+    getBulk,+    getMacSequence,+) where+ import Control.Monad.State.Strict-import Network.TLS.Compression+import qualified Data.ByteArray as BA+import qualified Data.ByteString as B+ import Network.TLS.Cipher+import Network.TLS.Compression import Network.TLS.ErrT+import Network.TLS.Imports+import Network.TLS.MAC+import Network.TLS.Packet import Network.TLS.Struct+import Network.TLS.Types import Network.TLS.Wire -import Network.TLS.Packet-import Network.TLS.MAC-import Network.TLS.Util-import Network.TLS.Imports--import qualified Data.ByteString as B- data CryptState = CryptState-    { cstKey        :: !BulkState-    , cstIV         :: !ByteString-    , cstMacSecret  :: !ByteString-    } deriving (Show)+    { cstKey :: BulkState+    , cstIV :: IV+    , -- In TLS 1.2 or earlier, this holds mac secret.+      -- In TLS 1.3, this holds application traffic secret N.+      cstMacSecret :: Secret+    }+    deriving (Show)  newtype MacState = MacState     { msSequence :: Word64-    } deriving (Show)+    }+    deriving (Show) +data RecordOptions = RecordOptions+    { recordVersion :: Version -- version to use when sending/receiving+    , recordTLS13 :: Bool -- TLS13 record processing+    }++-- | TLS encryption level.+data CryptLevel+    = -- | Unprotected traffic+      CryptInitial+    | -- | Protected with main secret (TLS < 1.3)+      CryptMainSecret+    | -- | Protected with early traffic secret (TLS 1.3)+      CryptEarlySecret+    | -- | Protected with handshake traffic secret (TLS 1.3)+      CryptHandshakeSecret+    | -- | Protected with application traffic secret (TLS 1.3)+      CryptApplicationSecret+    deriving (Eq, Show)++class HasCryptLevel a where getCryptLevel :: proxy a -> CryptLevel+instance HasCryptLevel EarlySecret where getCryptLevel _ = CryptEarlySecret+instance HasCryptLevel HandshakeSecret where+    getCryptLevel _ = CryptHandshakeSecret+instance HasCryptLevel ApplicationSecret where+    getCryptLevel _ = CryptApplicationSecret+ data RecordState = RecordState-    { stCipher      :: Maybe Cipher+    { stCipher :: Maybe Cipher     , stCompression :: Compression-    , stCryptState  :: !CryptState-    , stMacState    :: !MacState-    } deriving (Show)+    , stCryptLevel :: CryptLevel+    , stCryptState :: CryptState+    , stMacState :: MacState+    }+    deriving (Show) -newtype RecordM a = RecordM { runRecordM :: Version-                                         -> RecordState-                                         -> Either TLSError (a, RecordState) }+newtype RecordM a = RecordM+    { runRecordM+        :: RecordOptions+        -> RecordState+        -> Either TLSError (a, RecordState)+    }  instance Applicative RecordM where-    pure = return+    pure a = RecordM $ \_ st -> Right (a, st)     (<*>) = ap  instance Monad RecordM where-    return a  = RecordM $ \_ st  -> Right (a, st)-    m1 >>= m2 = RecordM $ \ver st -> do-                    case runRecordM m1 ver st of-                        Left err       -> Left err-                        Right (a, st2) -> runRecordM (m2 a) ver st2+    m1 >>= m2 = RecordM $ \opt st ->+        case runRecordM m1 opt st of+            Left err -> Left err+            Right (a, st2) -> runRecordM (m2 a) opt st2  instance Functor RecordM where-    fmap f m = RecordM $ \ver st ->-                case runRecordM m ver st of-                    Left err       -> Left err-                    Right (a, st2) -> Right (f a, st2)+    fmap f m = RecordM $ \opt st ->+        case runRecordM m opt st of+            Left err -> Left err+            Right (a, st2) -> Right (f a, st2) +getRecordOptions :: RecordM RecordOptions+getRecordOptions = RecordM $ \opt st -> Right (opt, st)+ getRecordVersion :: RecordM Version-getRecordVersion = RecordM $ \ver st -> Right (ver, st)+getRecordVersion = recordVersion <$> getRecordOptions  instance MonadState RecordState RecordM where-    put x = RecordM $ \_  _  -> Right ((), x)-    get   = RecordM $ \_  st -> Right (st, st)-#if MIN_VERSION_mtl(2,1,0)+    put x = RecordM $ \_ _ -> Right ((), x)+    get = RecordM $ \_ st -> Right (st, st)     state f = RecordM $ \_ st -> Right (f st)-#endif  instance MonadError TLSError RecordM where-    throwError e   = RecordM $ \_ _ -> Left e-    catchError m f = RecordM $ \ver st ->-                        case runRecordM m ver st of-                            Left err -> runRecordM (f err) ver st-                            r        -> r+    throwError e = RecordM $ \_ _ -> Left e+    catchError m f = RecordM $ \opt st ->+        case runRecordM m opt st of+            Left err -> runRecordM (f err) opt st+            r -> r  newRecordState :: RecordState-newRecordState = RecordState-    { stCipher      = Nothing-    , stCompression = nullCompression-    , stCryptState  = CryptState BulkStateUninitialized B.empty B.empty-    , stMacState    = MacState 0-    }+newRecordState =+    RecordState+        { stCipher = Nothing+        , stCompression = nullCompression+        , stCryptLevel = CryptInitial+        , stCryptState = CryptState BulkStateUninitialized B.empty BA.empty+        , stMacState = MacState 0+        }  incrRecordState :: RecordState -> RecordState-incrRecordState ts = ts { stMacState = MacState (ms + 1) }-  where (MacState ms) = stMacState ts+incrRecordState ts = ts{stMacState = MacState (ms + 1)}+  where+    (MacState ms) = stMacState ts  setRecordIV :: ByteString -> RecordState -> RecordState-setRecordIV iv st = st { stCryptState = (stCryptState st) { cstIV = iv } }+setRecordIV iv st = st{stCryptState = (stCryptState st){cstIV = iv}}  withCompression :: (Compression -> (Compression, a)) -> RecordM a withCompression f = do     st <- get     let (nc, a) = f $ stCompression st-    put $ st { stCompression = nc }+    put $ st{stCompression = nc}     return a -computeDigest :: Version -> RecordState -> Header -> ByteString -> (ByteString, RecordState)-computeDigest ver tstate hdr content = (digest, incrRecordState tstate)-  where digest = macF (cstMacSecret cst) msg-        cst    = stCryptState tstate-        cipher = fromJust "cipher" $ stCipher tstate-        hashA  = cipherHash cipher-        encodedSeq = encodeWord64 $ msSequence $ stMacState tstate+computeDigest+    :: Version -> RecordState -> Header -> ByteString -> (ByteString, RecordState)+computeDigest _ver tstate hdr content = (digest, incrRecordState tstate)+  where+    digest = BA.convert $ macF (cstMacSecret cst) msg+    cst = stCryptState tstate+    cipher = fromJust $ stCipher tstate+    hashA = cipherHash cipher+    encodedSeq = encodeWord64 $ msSequence $ stMacState tstate -        (macF, msg)-            | ver < TLS10 = (macSSL hashA, B.concat [ encodedSeq, encodeHeaderNoVer hdr, content ])-            | otherwise   = (hmac hashA, B.concat [ encodedSeq, encodeHeader hdr, content ])+    (macF, msg) = (hmac hashA, B.concat [encodedSeq, encodeHeader hdr, content])  makeDigest :: Header -> ByteString -> RecordM ByteString makeDigest hdr content = do@@ -136,7 +171,7 @@     return digest  getBulk :: RecordM Bulk-getBulk = cipherBulk . fromJust "cipher" . stCipher <$> get+getBulk = cipherBulk . fromJust . stCipher <$> get  getMacSequence :: RecordM Word64 getMacSequence = msSequence . stMacState <$> get
Network/TLS/Record/Types.hs view
@@ -1,55 +1,49 @@ {-# LANGUAGE EmptyDataDecls #-}--- |--- Module      : Network.TLS.Record.Types--- License     : BSD-style--- Maintainer  : Vincent Hanquez <vincent@snarc.org>--- Stability   : experimental--- Portability : unknown------ The Record Protocol takes messages to be transmitted, fragments the--- data into manageable blocks, optionally compresses the data, applies--- a MAC, encrypts, and transmits the result.  Received data is--- decrypted, verified, decompressed, reassembled, and then delivered to--- higher-level clients.----module Network.TLS.Record.Types-    ( Header(..)-    , ProtocolType(..)-    , packetType++-- | The Record Protocol takes messages to be transmitted, fragments+-- the data into manageable blocks.  applies a MAC, encrypts, and+-- transmits the result.  Received data is decrypted, verified,+-- reassembled, and then delivered to higher-level clients.+module Network.TLS.Record.Types (+    Header (..),+    ProtocolType (..),+    packetType,+     -- * TLS Records-    , Record(..)+    Record (..),+     -- * TLS Record fragment and constructors-    , Fragment-    , fragmentGetBytes-    , fragmentPlaintext-    , fragmentCiphertext-    , Plaintext-    , Compressed-    , Ciphertext+    Fragment,+    fragmentGetBytes,+    fragmentPlaintext,+    fragmentCiphertext,+    Plaintext,+    Ciphertext,+     -- * manipulate record-    , onRecordFragment-    , fragmentCompress-    , fragmentCipher-    , fragmentUncipher-    , fragmentUncompress+    onRecordFragment,+    fragmentCipher,+    fragmentUncipher,+     -- * serialize record-    , rawToRecord-    , recordToRaw-    , recordToHeader-    ) where+    rawToRecord,+    recordToRaw,+    recordToHeader,+) where -import Network.TLS.Struct+import qualified Data.ByteString as B+ import Network.TLS.Imports import Network.TLS.Record.State-import qualified Data.ByteString as B+import Network.TLS.Struct  -- | Represent a TLS record.-data Record a = Record !ProtocolType !Version !(Fragment a) deriving (Show,Eq)+data Record a = Record ProtocolType Version (Fragment a) deriving (Show, Eq) -newtype Fragment a = Fragment { fragmentGetBytes :: ByteString } deriving (Show,Eq)+newtype Fragment a = Fragment {fragmentGetBytes :: ByteString}+    deriving (Show, Eq)  data Plaintext-data Compressed data Ciphertext  fragmentPlaintext :: ByteString -> Fragment Plaintext@@ -58,27 +52,27 @@ fragmentCiphertext :: ByteString -> Fragment Ciphertext fragmentCiphertext bytes = Fragment bytes -onRecordFragment :: Record a -> (Fragment a -> RecordM (Fragment b)) -> RecordM (Record b)+onRecordFragment+    :: Record a -> (Fragment a -> RecordM (Fragment b)) -> RecordM (Record b) onRecordFragment (Record pt ver frag) f = Record pt ver <$> f frag -fragmentMap :: (ByteString -> RecordM ByteString) -> Fragment a -> RecordM (Fragment b)+fragmentMap+    :: (ByteString -> RecordM ByteString) -> Fragment a -> RecordM (Fragment b) fragmentMap f (Fragment b) = Fragment <$> f b --- | turn a plaintext record into a compressed record using the compression function supplied-fragmentCompress :: (ByteString -> RecordM ByteString) -> Fragment Plaintext -> RecordM (Fragment Compressed)-fragmentCompress f = fragmentMap f- -- | turn a compressed record into a ciphertext record using the cipher function supplied-fragmentCipher :: (ByteString -> RecordM ByteString) -> Fragment Compressed -> RecordM (Fragment Ciphertext)+fragmentCipher+    :: (ByteString -> RecordM ByteString)+    -> Fragment Plaintext+    -> RecordM (Fragment Ciphertext) fragmentCipher f = fragmentMap f --- | turn a ciphertext fragment into a compressed fragment using the cipher function supplied-fragmentUncipher :: (ByteString -> RecordM ByteString) -> Fragment Ciphertext -> RecordM (Fragment Compressed)+-- | turn a ciphertext fragment into a plaintext fragment using the cipher function supplied+fragmentUncipher+    :: (ByteString -> RecordM ByteString)+    -> Fragment Ciphertext+    -> RecordM (Fragment Plaintext) fragmentUncipher f = fragmentMap f---- | turn a compressed fragment into a plaintext fragment using the decompression function supplied-fragmentUncompress :: (ByteString -> RecordM ByteString) -> Fragment Compressed -> RecordM (Fragment Plaintext)-fragmentUncompress f = fragmentMap f  -- | turn a record into an header and bytes recordToRaw :: Record a -> (Header, ByteString)
− Network/TLS/Sending.hs
@@ -1,91 +0,0 @@--- |--- Module      : Network.TLS.Sending--- License     : BSD-style--- Maintainer  : Vincent Hanquez <vincent@snarc.org>--- Stability   : experimental--- Portability : unknown------ the Sending module contains calls related to marshalling packets according--- to the TLS state----module Network.TLS.Sending (writePacket) where--import Control.Monad.State.Strict-import Control.Concurrent.MVar-import Data.IORef--import qualified Data.ByteString as B--import Network.TLS.Types (Role(..))-import Network.TLS.Cap-import Network.TLS.Struct-import Network.TLS.Record-import Network.TLS.Packet-import Network.TLS.Context.Internal-import Network.TLS.Parameters-import Network.TLS.State-import Network.TLS.Handshake.State-import Network.TLS.Cipher-import Network.TLS.Util-import Network.TLS.Imports---- | 'makePacketData' create a Header and a content bytestring related to a packet--- this doesn't change any state-makeRecord :: Packet -> RecordM (Record Plaintext)-makeRecord pkt = do-    ver <- getRecordVersion-    return $ Record (packetType pkt) ver (fragmentPlaintext $ writePacketContent pkt)-  where writePacketContent (Handshake hss)    = encodeHandshakes hss-        writePacketContent (Alert a)          = encodeAlerts a-        writePacketContent  ChangeCipherSpec  = encodeChangeCipherSpec-        writePacketContent (AppData x)        = x---- | marshall packet data-encodeRecord :: Record Ciphertext -> RecordM ByteString-encodeRecord record = return $ B.concat [ encodeHeader hdr, content ]-  where (hdr, content) = recordToRaw record---- | writePacket transform a packet into marshalled data related to current state--- and updating state on the go-writePacket :: Context -> Packet -> IO (Either TLSError ByteString)-writePacket ctx pkt@(Handshake hss) = do-    forM_ hss $ \hs -> do-        case hs of-            Finished fdata -> usingState_ ctx $ updateVerifiedData ClientRole fdata-            _              -> return ()-        let encoded = encodeHandshake hs-        usingHState ctx $ do-            when (certVerifyHandshakeMaterial hs) $ addHandshakeMessage encoded-            when (finishHandshakeTypeMaterial $ typeOfHandshake hs) $ updateHandshakeDigest encoded-    prepareRecord ctx (makeRecord pkt >>= engageRecord >>= encodeRecord)-writePacket ctx pkt = do-    d <- prepareRecord ctx (makeRecord pkt >>= engageRecord >>= encodeRecord)-    when (pkt == ChangeCipherSpec) $ switchTxEncryption ctx-    return d---- before TLS 1.1, the block cipher IV is made of the residual of the previous block,--- so we use cstIV as is, however in other case we generate an explicit IV-prepareRecord :: Context -> RecordM a -> IO (Either TLSError a)-prepareRecord ctx f = do-    ver     <- usingState_ ctx (getVersionWithDefault $ maximum $ supportedVersions $ ctxSupported ctx)-    txState <- readMVar $ ctxTxState ctx-    let sz = case stCipher txState of-                  Nothing     -> 0-                  Just cipher -> if hasRecordIV $ bulkF $ cipherBulk cipher-                                    then bulkIVSize $ cipherBulk cipher-                                    else 0 -- to not generate IV-    if hasExplicitBlockIV ver && sz > 0-        then do newIV <- getStateRNG ctx sz-                runTxState ctx (modify (setRecordIV newIV) >> f)-        else runTxState ctx f--switchTxEncryption :: Context -> IO ()-switchTxEncryption ctx = do-    tx  <- usingHState ctx (fromJust "tx-state" <$> gets hstPendingTxState)-    (ver, cc) <- usingState_ ctx $ do v <- getVersion-                                      c <- isClientContext-                                      return (v, c)-    liftIO $ modifyMVar_ (ctxTxState ctx) (\_ -> return tx)-    -- set empty packet counter measure if condition are met-    when (ver <= TLS10 && cc == ClientRole && isCBC tx && supportedEmptyPacket (ctxSupported ctx)) $ liftIO $ writeIORef (ctxNeedEmptyPacket ctx) True-  where isCBC tx = maybe False (\c -> bulkBlockSize (cipherBulk c) > 0) (stCipher tx)
Network/TLS/Session.hs view
@@ -1,30 +1,35 @@--- |--- Module      : Network.TLS.Session--- License     : BSD-style--- Maintainer  : Vincent Hanquez <vincent@snarc.org>--- Stability   : experimental--- Portability : unknown----module Network.TLS.Session-    ( SessionManager(..)-    , noSessionManager-    ) where+module Network.TLS.Session (+    SessionManager (..),+    noSessionManager,+) where  import Network.TLS.Types --- | A session manager+-- | A session manager.+-- In the server side, all fields are used.+-- In the client side, only 'sessionEstablish' is used. data SessionManager = SessionManager-    { -- | used on server side to decide whether to resume a client session.-      sessionResume     :: SessionID -> IO (Maybe SessionData)-      -- | used when a session is established.-    , sessionEstablish  :: SessionID -> SessionData -> IO ()-      -- | used when a session is invalidated.-    , sessionInvalidate :: SessionID -> IO ()+    { sessionResume :: SessionIDorTicket -> IO (Maybe SessionData)+    -- ^ Used on TLS 1.2\/1.3 servers to lookup 'SessionData' with 'SessionID' or to decrypt 'Ticket' to get 'SessionData'.+    , sessionResumeOnlyOnce :: SessionIDorTicket -> IO (Maybe SessionData)+    -- ^ Used for 0RTT on TLS 1.3 servers to lookup 'SessionData' with 'SessionID' or to decrypt 'Ticket' to get 'SessionData'.+    , sessionEstablish :: SessionIDorTicket -> SessionData -> IO (Maybe Ticket)+    -- ^ Used on TLS 1.2\/1.3 servers to store 'SessionData' with 'SessionID' or to encrypt 'SessionData' to get 'Ticket' ignoring 'SessionID'. Used on TLS 1.2\/1.3 clients to store 'SessionData' with 'SessionIDorTicket' and then return 'Nothing'. For clients, only this field should be set with 'noSessionManager'.+    , sessionInvalidate :: SessionIDorTicket -> IO ()+    -- ^ Used TLS 1.2 servers to delete 'SessionData' with 'SessionID' on errors.+    , sessionUseTicket :: Bool+    -- ^ Used on TLS 1.2 servers to decide to use 'SessionID' or 'Ticket'. Note that 'SessionID' and 'Ticket' are integrated as identity in TLS 1.3.     } +-- | The session manager to do nothing. noSessionManager :: SessionManager-noSessionManager = SessionManager-    { sessionResume     = \_   -> return Nothing-    , sessionEstablish  = \_ _ -> return ()-    , sessionInvalidate = \_   -> return ()-    }+noSessionManager =+    SessionManager+        { sessionResume = \_ -> return Nothing+        , sessionResumeOnlyOnce = \_ -> return Nothing+        , sessionEstablish = \_ _ -> return Nothing+        , sessionInvalidate = \_ -> return ()+        , -- Don't send NewSessionTicket in TLS 1.2 by default.+          -- Send NewSessionTicket with SessionID in TLS 1.3 by default.+          sessionUseTicket = False+        }
Network/TLS/State.hs view
@@ -1,231 +1,325 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE Rank2Types #-}-{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-}--- |--- Module      : Network.TLS.State--- License     : BSD-style--- Maintainer  : Vincent Hanquez <vincent@snarc.org>--- Stability   : experimental--- Portability : unknown------ the State module contains calls related to state initialization/manipulation--- which is use by the Receiving module and the Sending module.----module Network.TLS.State-    ( TLSState(..)-    , TLSSt-    , runTLSState-    , newTLSState-    , withTLSRNG-    , updateVerifiedData-    , finishHandshakeTypeMaterial-    , finishHandshakeMaterial-    , certVerifyHandshakeTypeMaterial-    , certVerifyHandshakeMaterial-    , setVersion-    , setVersionIfUnset-    , getVersion-    , getVersionWithDefault-    , setSecureRenegotiation-    , getSecureRenegotiation-    , setExtensionALPN-    , getExtensionALPN-    , setNegotiatedProtocol-    , getNegotiatedProtocol-    , setClientALPNSuggest-    , getClientALPNSuggest-    , setClientEcPointFormatSuggest-    , getClientEcPointFormatSuggest-    , getClientCertificateChain-    , setClientCertificateChain-    , setClientSNI-    , getClientSNI-    , getVerifiedData-    , setSession-    , getSession-    , isSessionResuming-    , isClientContext+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE Rank2Types #-}++-- | the State module contains calls related to state+-- initialization/manipulation which is use by the Receiving module+-- and the Sending module.+module Network.TLS.State (+    TLSState (..),+    TLSSt,+    runTLSState,+    newTLSState,+    withTLSRNG,+    setVerifyDataForSend,+    setVerifyDataForRecv,+    getVerifyData,+    getMyVerifyData,+    getPeerVerifyData,+    getFirstVerifyData,+    finishedHandshakeTypeMaterial,+    finishedHandshakeMaterial,+    certVerifyHandshakeTypeMaterial,+    certVerifyHandshakeMaterial,+    setVersion,+    setVersionIfUnset,+    getVersion,+    getVersionWithDefault,+    setSecureRenegotiation,+    getSecureRenegotiation,+    setExtensionALPN,+    getExtensionALPN,+    setNegotiatedProtocol,+    getNegotiatedProtocol,+    setClientALPNSuggest,+    getClientALPNSuggest,+    setClientEcPointFormatSuggest,+    getClientEcPointFormatSuggest,+    setClientSNI,+    clearClientSNI,+    getClientSNI,+    getClientCertificateChain,+    setClientCertificateChain,+    getServerCertificateChain,+    setServerCertificateChain,+    setSession,+    getSession,+    getRole,+    --+    setTLS12SessionResuming,+    getTLS12SessionResuming,+    --+    setTLS13ExporterSecret,+    getTLS13ExporterSecret,+    setTLS13KeyShare,+    getTLS13KeyShare,+    setTLS13PreSharedKey,+    getTLS13PreSharedKey,+    setTLS13HRR,+    getTLS13HRR,+    setTLS13Cookie,+    getTLS13Cookie,+    setTLS13ClientSupportsPHA,+    getTLS13ClientSupportsPHA,+    setTLS12SessionTicket,+    getTLS12SessionTicket,+     -- * random-    , genRandom-    , withRNG-    ) where+    genRandom,+    withRNG,+) where -import Network.TLS.Imports-import Network.TLS.Struct-import Network.TLS.RNG-import Network.TLS.Types (Role(..))-import Network.TLS.Wire (GetContinuation)-import Network.TLS.Extension-import qualified Data.ByteString as B import Control.Monad.State.Strict-import Network.TLS.ErrT import Crypto.Random import Data.X509 (CertificateChain) -type HostName = String+import Network.TLS.ErrT+import Network.TLS.Extension+import Network.TLS.Imports+import Network.TLS.RNG+import Network.TLS.Struct+import Network.TLS.Types (HostName, Role (..), Secret, Ticket, WireBytes)+import Network.TLS.Wire (GetContinuation)  data TLSState = TLSState-    { stSession             :: Session-    , stSessionResuming     :: Bool-    , stSecureRenegotiation :: Bool  -- RFC 5746-    , stClientVerifiedData  :: ByteString -- RFC 5746-    , stServerVerifiedData  :: ByteString -- RFC 5746-    , stExtensionALPN       :: Bool  -- RFC 7301-    , stHandshakeRecordCont :: Maybe (GetContinuation (HandshakeType, ByteString))-    , stNegotiatedProtocol  :: Maybe B.ByteString -- ALPN protocol-    , stClientALPNSuggest   :: Maybe [B.ByteString]-    , stClientGroupSuggest  :: Maybe [Group]+    { stSession :: Session+    , -- RFC 5746, Renegotiation Indication Extension+      -- RFC 5929, Channel Bindings for TLS, "tls-unique"+      stSecureRenegotiation :: Bool+    , stClientVerifyData :: Maybe VerifyData+    , stServerVerifyData :: Maybe VerifyData+    , -- RFC 5929, Channel Bindings for TLS, "tls-server-end-point"+      stServerCertificateChain :: Maybe CertificateChain+    , stExtensionALPN :: Bool -- RFC 7301+    , stNegotiatedProtocol :: Maybe ByteString -- ALPN protocol+    , stHandshakeRecordCont12+        :: (Maybe (GetContinuation (HandshakeType, ByteString)), WireBytes)+    , stHandshakeRecordCont13+        :: (Maybe (GetContinuation (HandshakeType, ByteString)), WireBytes)+    , stClientALPNSuggest :: Maybe [ByteString]+    , stClientGroupSuggest :: Maybe [Group]     , stClientEcPointFormatSuggest :: Maybe [EcPointFormat]     , stClientCertificateChain :: Maybe CertificateChain-    , stClientSNI           :: Maybe HostName-    , stRandomGen           :: StateRNG-    , stVersion             :: Maybe Version-    , stClientContext       :: Role+    , stClientSNI :: Maybe HostName+    , stRandomGen :: StateRNG+    , stClientContext :: Role+    , stVersion :: Maybe Version+    , --+      stTLS12SessionResuming :: Bool+    , stTLS12SessionTicket :: Maybe Ticket+    , --+      stTLS13KeyShare :: Maybe KeyShare+    , stTLS13PreSharedKey :: Maybe PreSharedKey+    , stTLS13HRR :: Bool+    , stTLS13Cookie :: Maybe Cookie+    , stTLS13ExporterSecret :: Maybe Secret+    , stTLS13ClientSupportsPHA :: Bool -- Post-Handshake Authentication     } -newtype TLSSt a = TLSSt { runTLSSt :: ErrT TLSError (State TLSState) a }+newtype TLSSt a = TLSSt {runTLSSt :: ErrT TLSError (State TLSState) a}     deriving (Monad, MonadError TLSError, Functor, Applicative)  instance MonadState TLSState TLSSt where     put x = TLSSt (lift $ put x)-    get   = TLSSt (lift get)-#if MIN_VERSION_mtl(2,1,0)+    get = TLSSt (lift get)     state f = TLSSt (lift $ state f)-#endif  runTLSState :: TLSSt a -> TLSState -> (Either TLSError a, TLSState) runTLSState f st = runState (runErrT (runTLSSt f)) st  newTLSState :: StateRNG -> Role -> TLSState-newTLSState rng clientContext = TLSState-    { stSession             = Session Nothing-    , stSessionResuming     = False-    , stSecureRenegotiation = False-    , stClientVerifiedData  = B.empty-    , stServerVerifiedData  = B.empty-    , stExtensionALPN       = False-    , stHandshakeRecordCont = Nothing-    , stNegotiatedProtocol  = Nothing-    , stClientALPNSuggest   = Nothing-    , stClientGroupSuggest = Nothing-    , stClientEcPointFormatSuggest = Nothing-    , stClientCertificateChain = Nothing-    , stClientSNI           = Nothing-    , stRandomGen           = rng-    , stVersion             = Nothing-    , stClientContext       = clientContext-    }+newTLSState rng clientContext =+    TLSState+        { stSession = Session Nothing+        , stSecureRenegotiation = False+        , stClientVerifyData = Nothing+        , stServerVerifyData = Nothing+        , stServerCertificateChain = Nothing+        , stExtensionALPN = False+        , stNegotiatedProtocol = Nothing+        , stHandshakeRecordCont12 = (Nothing, [])+        , stHandshakeRecordCont13 = (Nothing, [])+        , stClientALPNSuggest = Nothing+        , stClientGroupSuggest = Nothing+        , stClientEcPointFormatSuggest = Nothing+        , stClientCertificateChain = Nothing+        , stClientSNI = Nothing+        , stRandomGen = rng+        , stClientContext = clientContext+        , stVersion = Nothing+        , stTLS12SessionResuming = False+        , stTLS12SessionTicket = Nothing+        , stTLS13KeyShare = Nothing+        , stTLS13PreSharedKey = Nothing+        , stTLS13HRR = False+        , stTLS13Cookie = Nothing+        , stTLS13ExporterSecret = Nothing+        , stTLS13ClientSupportsPHA = False+        } -updateVerifiedData :: Role -> ByteString -> TLSSt ()-updateVerifiedData sending bs = do-    cc <- isClientContext-    if cc /= sending-        then modify (\st -> st { stServerVerifiedData = bs })-        else modify (\st -> st { stClientVerifiedData = bs })+setVerifyDataForSend :: VerifyData -> TLSSt ()+setVerifyDataForSend bs = do+    role <- getRole+    case role of+        ClientRole -> modify' (\st -> st{stClientVerifyData = Just bs})+        ServerRole -> modify' (\st -> st{stServerVerifyData = Just bs}) -finishHandshakeTypeMaterial :: HandshakeType -> Bool-finishHandshakeTypeMaterial HandshakeType_ClientHello     = True-finishHandshakeTypeMaterial HandshakeType_ServerHello     = True-finishHandshakeTypeMaterial HandshakeType_Certificate     = True-finishHandshakeTypeMaterial HandshakeType_HelloRequest    = False-finishHandshakeTypeMaterial HandshakeType_ServerHelloDone = True-finishHandshakeTypeMaterial HandshakeType_ClientKeyXchg   = True-finishHandshakeTypeMaterial HandshakeType_ServerKeyXchg   = True-finishHandshakeTypeMaterial HandshakeType_CertRequest     = True-finishHandshakeTypeMaterial HandshakeType_CertVerify      = True-finishHandshakeTypeMaterial HandshakeType_Finished        = True+setVerifyDataForRecv :: VerifyData -> TLSSt ()+setVerifyDataForRecv bs = do+    role <- getRole+    case role of+        ClientRole -> modify' (\st -> st{stServerVerifyData = Just bs})+        ServerRole -> modify' (\st -> st{stClientVerifyData = Just bs}) -finishHandshakeMaterial :: Handshake -> Bool-finishHandshakeMaterial = finishHandshakeTypeMaterial . typeOfHandshake+finishedHandshakeTypeMaterial :: HandshakeType -> Bool+finishedHandshakeTypeMaterial HandshakeType_ClientHello = True+finishedHandshakeTypeMaterial HandshakeType_ServerHello = True+finishedHandshakeTypeMaterial HandshakeType_Certificate = True+-- finishedHandshakeTypeMaterial HandshakeType_HelloRequest = False+finishedHandshakeTypeMaterial HandshakeType_ServerHelloDone = True+finishedHandshakeTypeMaterial HandshakeType_ClientKeyXchg = True+finishedHandshakeTypeMaterial HandshakeType_ServerKeyXchg = True+finishedHandshakeTypeMaterial HandshakeType_CertRequest = True+finishedHandshakeTypeMaterial HandshakeType_CertVerify = True+finishedHandshakeTypeMaterial HandshakeType_NewSessionTicket = True+finishedHandshakeTypeMaterial HandshakeType_Finished = True+finishedHandshakeTypeMaterial _ = False +finishedHandshakeMaterial :: Handshake -> Bool+finishedHandshakeMaterial = finishedHandshakeTypeMaterial . typeOfHandshake+ certVerifyHandshakeTypeMaterial :: HandshakeType -> Bool-certVerifyHandshakeTypeMaterial HandshakeType_ClientHello     = True-certVerifyHandshakeTypeMaterial HandshakeType_ServerHello     = True-certVerifyHandshakeTypeMaterial HandshakeType_Certificate     = True-certVerifyHandshakeTypeMaterial HandshakeType_HelloRequest    = False+certVerifyHandshakeTypeMaterial HandshakeType_ClientHello = True+certVerifyHandshakeTypeMaterial HandshakeType_ServerHello = True+certVerifyHandshakeTypeMaterial HandshakeType_Certificate = True+-- certVerifyHandshakeTypeMaterial HandshakeType_HelloRequest = False certVerifyHandshakeTypeMaterial HandshakeType_ServerHelloDone = True-certVerifyHandshakeTypeMaterial HandshakeType_ClientKeyXchg   = True-certVerifyHandshakeTypeMaterial HandshakeType_ServerKeyXchg   = True-certVerifyHandshakeTypeMaterial HandshakeType_CertRequest     = True-certVerifyHandshakeTypeMaterial HandshakeType_CertVerify      = False-certVerifyHandshakeTypeMaterial HandshakeType_Finished        = False+certVerifyHandshakeTypeMaterial HandshakeType_ClientKeyXchg = True+certVerifyHandshakeTypeMaterial HandshakeType_ServerKeyXchg = True+certVerifyHandshakeTypeMaterial HandshakeType_CertRequest = True+-- certVerifyHandshakeTypeMaterial HandshakeType_CertVerify = False+-- certVerifyHandshakeTypeMaterial HandshakeType_Finished = False+certVerifyHandshakeTypeMaterial _ = False  certVerifyHandshakeMaterial :: Handshake -> Bool certVerifyHandshakeMaterial = certVerifyHandshakeTypeMaterial . typeOfHandshake -setSession :: Session -> Bool -> TLSSt ()-setSession session resuming = modify (\st -> st { stSession = session, stSessionResuming = resuming })+setSession :: Session -> TLSSt ()+setSession session = modify' (\st -> st{stSession = session})  getSession :: TLSSt Session getSession = gets stSession -isSessionResuming :: TLSSt Bool-isSessionResuming = gets stSessionResuming+setTLS12SessionResuming :: Bool -> TLSSt ()+setTLS12SessionResuming b = modify' (\st -> st{stTLS12SessionResuming = b}) +getTLS12SessionResuming :: TLSSt Bool+getTLS12SessionResuming = gets stTLS12SessionResuming+ setVersion :: Version -> TLSSt ()-setVersion ver = modify (\st -> st { stVersion = Just ver })+setVersion ver = modify' (\st -> st{stVersion = Just ver})  setVersionIfUnset :: Version -> TLSSt ()-setVersionIfUnset ver = modify maybeSet-  where maybeSet st = case stVersion st of-                           Nothing -> st { stVersion = Just ver }-                           Just _  -> st+setVersionIfUnset ver = modify' maybeSet+  where+    maybeSet st = case stVersion st of+        Nothing -> st{stVersion = Just ver}+        Just _ -> st  getVersion :: TLSSt Version-getVersion = fromMaybe (error "internal error: version hasn't been set yet") <$> gets stVersion+getVersion =+    fromMaybe (error "internal error: version hasn't been set yet")+        <$> gets stVersion  getVersionWithDefault :: Version -> TLSSt Version getVersionWithDefault defaultVer = fromMaybe defaultVer <$> gets stVersion  setSecureRenegotiation :: Bool -> TLSSt ()-setSecureRenegotiation b = modify (\st -> st { stSecureRenegotiation = b })+setSecureRenegotiation b = modify' (\st -> st{stSecureRenegotiation = b})  getSecureRenegotiation :: TLSSt Bool getSecureRenegotiation = gets stSecureRenegotiation  setExtensionALPN :: Bool -> TLSSt ()-setExtensionALPN b = modify (\st -> st { stExtensionALPN = b })+setExtensionALPN b = modify' (\st -> st{stExtensionALPN = b})  getExtensionALPN :: TLSSt Bool getExtensionALPN = gets stExtensionALPN -setNegotiatedProtocol :: B.ByteString -> TLSSt ()-setNegotiatedProtocol s = modify (\st -> st { stNegotiatedProtocol = Just s })+setNegotiatedProtocol :: ByteString -> TLSSt ()+setNegotiatedProtocol s = modify' (\st -> st{stNegotiatedProtocol = Just s}) -getNegotiatedProtocol :: TLSSt (Maybe B.ByteString)+getNegotiatedProtocol :: TLSSt (Maybe ByteString) getNegotiatedProtocol = gets stNegotiatedProtocol -setClientALPNSuggest :: [B.ByteString] -> TLSSt ()-setClientALPNSuggest ps = modify (\st -> st { stClientALPNSuggest = Just ps})+setClientALPNSuggest :: [ByteString] -> TLSSt ()+setClientALPNSuggest ps = modify' (\st -> st{stClientALPNSuggest = Just ps}) -getClientALPNSuggest :: TLSSt (Maybe [B.ByteString])+getClientALPNSuggest :: TLSSt (Maybe [ByteString]) getClientALPNSuggest = gets stClientALPNSuggest  setClientEcPointFormatSuggest :: [EcPointFormat] -> TLSSt ()-setClientEcPointFormatSuggest epf = modify (\st -> st { stClientEcPointFormatSuggest = Just epf})+setClientEcPointFormatSuggest epf = modify' (\st -> st{stClientEcPointFormatSuggest = Just epf})  getClientEcPointFormatSuggest :: TLSSt (Maybe [EcPointFormat]) getClientEcPointFormatSuggest = gets stClientEcPointFormatSuggest  setClientCertificateChain :: CertificateChain -> TLSSt ()-setClientCertificateChain s = modify (\st -> st { stClientCertificateChain = Just s })+setClientCertificateChain s = modify' (\st -> st{stClientCertificateChain = Just s})  getClientCertificateChain :: TLSSt (Maybe CertificateChain) getClientCertificateChain = gets stClientCertificateChain +setServerCertificateChain :: CertificateChain -> TLSSt ()+setServerCertificateChain s = modify' (\st -> st{stServerCertificateChain = Just s})++getServerCertificateChain :: TLSSt (Maybe CertificateChain)+getServerCertificateChain = gets stServerCertificateChain+ setClientSNI :: HostName -> TLSSt ()-setClientSNI hn = modify (\st -> st { stClientSNI = Just hn })+setClientSNI hn = modify' (\st -> st{stClientSNI = Just hn}) +clearClientSNI :: TLSSt ()+clearClientSNI = modify' (\st -> st{stClientSNI = Nothing})+ getClientSNI :: TLSSt (Maybe HostName) getClientSNI = gets stClientSNI -getVerifiedData :: Role -> TLSSt ByteString-getVerifiedData client = gets (if client == ClientRole then stClientVerifiedData else stServerVerifiedData)+getVerifyData :: Role -> TLSSt VerifyData+getVerifyData client = do+    mVerifyData <-+        gets (if client == ClientRole then stClientVerifyData else stServerVerifyData)+    return $ fromMaybe (VerifyData "") mVerifyData -isClientContext :: TLSSt Role-isClientContext = gets stClientContext+getMyVerifyData :: TLSSt (Maybe VerifyData)+getMyVerifyData = do+    role <- getRole+    if role == ClientRole+        then gets stClientVerifyData+        else gets stServerVerifyData +getPeerVerifyData :: TLSSt (Maybe VerifyData)+getPeerVerifyData = do+    role <- getRole+    if role == ClientRole+        then gets stServerVerifyData+        else gets stClientVerifyData++getFirstVerifyData :: TLSSt (Maybe VerifyData)+getFirstVerifyData = do+    ver <- getVersion+    case ver of+        TLS13 -> gets stServerVerifyData+        _ -> do+            resuming <- getTLS12SessionResuming+            if resuming+                then gets stServerVerifyData+                else gets stClientVerifyData++getRole :: TLSSt Role+getRole = gets stClientContext+ genRandom :: Int -> TLSSt ByteString genRandom n = do     withRNG (getRandomBytes n)@@ -233,6 +327,48 @@ withRNG :: MonadPseudoRandom StateRNG a -> TLSSt a withRNG f = do     st <- get-    let (a,rng') = withTLSRNG (stRandomGen st) f-    put (st { stRandomGen = rng' })+    let (a, rng') = withTLSRNG (stRandomGen st) f+    put (st{stRandomGen = rng'})     return a++setTLS12SessionTicket :: Ticket -> TLSSt ()+setTLS12SessionTicket t = modify' (\st -> st{stTLS12SessionTicket = Just t})++getTLS12SessionTicket :: TLSSt (Maybe Ticket)+getTLS12SessionTicket = gets stTLS12SessionTicket++setTLS13ExporterSecret :: Secret -> TLSSt ()+setTLS13ExporterSecret key = modify' (\st -> st{stTLS13ExporterSecret = Just key})++getTLS13ExporterSecret :: TLSSt (Maybe Secret)+getTLS13ExporterSecret = gets stTLS13ExporterSecret++setTLS13KeyShare :: Maybe KeyShare -> TLSSt ()+setTLS13KeyShare mks = modify' (\st -> st{stTLS13KeyShare = mks})++getTLS13KeyShare :: TLSSt (Maybe KeyShare)+getTLS13KeyShare = gets stTLS13KeyShare++setTLS13PreSharedKey :: Maybe PreSharedKey -> TLSSt ()+setTLS13PreSharedKey mpsk = modify' (\st -> st{stTLS13PreSharedKey = mpsk})++getTLS13PreSharedKey :: TLSSt (Maybe PreSharedKey)+getTLS13PreSharedKey = gets stTLS13PreSharedKey++setTLS13HRR :: Bool -> TLSSt ()+setTLS13HRR b = modify' (\st -> st{stTLS13HRR = b})++getTLS13HRR :: TLSSt Bool+getTLS13HRR = gets stTLS13HRR++setTLS13Cookie :: Maybe Cookie -> TLSSt ()+setTLS13Cookie mcookie = modify' (\st -> st{stTLS13Cookie = mcookie})++getTLS13Cookie :: TLSSt (Maybe Cookie)+getTLS13Cookie = gets stTLS13Cookie++setTLS13ClientSupportsPHA :: Bool -> TLSSt ()+setTLS13ClientSupportsPHA b = modify' (\st -> st{stTLS13ClientSupportsPHA = b})++getTLS13ClientSupportsPHA :: TLSSt Bool+getTLS13ClientSupportsPHA = gets stTLS13ClientSupportsPHA
Network/TLS/Struct.hs view
@@ -1,578 +1,501 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE PatternSynonyms #-} {-# OPTIONS_HADDOCK hide #-}-{-# LANGUAGE DeriveDataTypeable #-}--- |--- Module      : Network.TLS.Struct--- License     : BSD-style--- Maintainer  : Vincent Hanquez <vincent@snarc.org>--- Stability   : experimental--- Portability : unknown------ the Struct module contains all definitions and values of the TLS protocol----{-# LANGUAGE CPP #-}-module Network.TLS.Struct-    ( Version(..)-    , ConnectionEnd(..)-    , CipherType(..)-    , CipherData(..)-    , ExtensionID-    , ExtensionRaw(..)-    , CertificateType(..)-    , HashAlgorithm(..)-    , SignatureAlgorithm(..)-    , HashAndSignatureAlgorithm-    , DigitallySigned(..)-    , ProtocolType(..)-    , TLSError(..)-    , TLSException(..)-    , DistinguishedName-    , BigNum(..)-    , bigNumToInteger-    , bigNumFromInteger-    , ServerDHParams(..)-    , serverDHParamsToParams-    , serverDHParamsToPublic-    , serverDHParamsFrom-    , ServerECDHParams(..)-    , ServerRSAParams(..)-    , ServerKeyXchgAlgorithmData(..)-    , ClientKeyXchgAlgorithmData(..)-    , Packet(..)-    , Header(..)-    , ServerRandom(..)-    , ClientRandom(..)-    , serverRandom-    , clientRandom-    , FinishedData-    , SessionID-    , Session(..)-    , SessionData(..)-    , AlertLevel(..)-    , AlertDescription(..)-    , HandshakeType(..)-    , Handshake(..)-    , numericalVer-    , verOfNum-    , TypeValuable, valOfType, valToType-    , EnumSafe8(..)-    , EnumSafe16(..)-    , packetType-    , typeOfHandshake-    ) where -import qualified Data.ByteString as B (length)-import Data.X509 (CertificateChain, DistinguishedName)-import Data.Typeable-import Control.Exception (Exception(..))-import Network.TLS.Types+-- | The Struct module contains all definitions and values of the TLS+-- protocol.+module Network.TLS.Struct (+    Version (..),+    CipherData (..),+    CertificateType (+        CertificateType,+        CertificateType_RSA_Sign,+        CertificateType_DSA_Sign,+        CertificateType_ECDSA_Sign,+        CertificateType_Ed25519_Sign,+        CertificateType_Ed448_Sign+    ),+    fromCertificateType,+    lastSupportedCertificateType,+    DigitallySigned (..),+    Signature,+    ProtocolType (+        ..,+        ProtocolType_ChangeCipherSpec,+        ProtocolType_Alert,+        ProtocolType_Handshake,+        ProtocolType_AppData+    ),+    TLSError (..),+    TLSException (..),+    DistinguishedName,+    ServerDHParams (..),+    serverDHParamsToParams,+    serverDHParamsToPublic,+    serverDHParamsFrom,+    ServerECDHParams (..),+    ServerRSAParams (..),+    ServerKeyXchgAlgorithmData (..),+    ClientKeyXchgAlgorithmData (..),+    Packet (..),+    Header (..),+    ServerRandom (..),+    ClientRandom (..),+    FinishedData,+    VerifyData (..),+    SessionID,+    Session (..),+    SessionData (..),+    AlertLevel (+        ..,+        AlertLevel_Warning,+        AlertLevel_Fatal+    ),+    AlertDescription (+        ..,+        CloseNotify,+        UnexpectedMessage,+        BadRecordMac,+        DecryptionFailed,+        RecordOverflow,+        DecompressionFailure,+        HandshakeFailure,+        BadCertificate,+        UnsupportedCertificate,+        CertificateRevoked,+        CertificateExpired,+        CertificateUnknown,+        IllegalParameter,+        UnknownCa,+        AccessDenied,+        DecodeError,+        DecryptError,+        ExportRestriction,+        ProtocolVersion,+        InsufficientSecurity,+        InternalError,+        InappropriateFallback,+        UserCanceled,+        NoRenegotiation,+        MissingExtension,+        UnsupportedExtension,+        CertificateUnobtainable,+        UnrecognizedName,+        BadCertificateStatusResponse,+        BadCertificateHashValue,+        UnknownPskIdentity,+        CertificateRequired,+        NoApplicationProtocol+    ),+    HandshakeType (+        ..,+        HandshakeType_HelloRequest,+        HandshakeType_ClientHello,+        HandshakeType_ServerHello,+        HandshakeType_NewSessionTicket,+        HandshakeType_EndOfEarlyData,+        HandshakeType_EncryptedExtensions,+        HandshakeType_Certificate,+        HandshakeType_ServerKeyXchg,+        HandshakeType_CertRequest,+        HandshakeType_ServerHelloDone,+        HandshakeType_CertVerify,+        HandshakeType_ClientKeyXchg,+        HandshakeType_Finished,+        HandshakeType_KeyUpdate,+        HandshakeType_CompressedCertificate+    ),+    CertificateChain_ (..),+    emptyCertificateChain_,+    Handshake (..),+    packetType,+    typeOfHandshake,+    module Network.TLS.HashAndSignature,+    ExtensionRaw (..),+    ExtensionID (..),+    showCertificateChain,+    isHelloRetryRequest,+    hrrRandom,+    ClientHello (..),+    ServerHello (..),+    HandshakeR,+) where++import Data.X509 (+    CertificateChain (..),+    DistinguishedName,+    certSubjectDN,+    getCharacterStringRawData,+    getDistinguishedElements,+    getSigned,+    signedObject,+ )+ import Network.TLS.Crypto-import Network.TLS.Util.Serialization+import Network.TLS.Error+import {-# SOURCE #-} Network.TLS.Extension+import Network.TLS.HashAndSignature import Network.TLS.Imports-#if MIN_VERSION_mtl(2,2,1)-#else-import Control.Monad.Error-#endif+import Network.TLS.Types -data ConnectionEnd = ConnectionServer | ConnectionClient-data CipherType = CipherStream | CipherBlock | CipherAEAD+----------------------------------------------------------------  data CipherData = CipherData     { cipherDataContent :: ByteString-    , cipherDataMAC     :: Maybe ByteString-    , cipherDataPadding :: Maybe ByteString-    } deriving (Show,Eq)+    , cipherDataMAC :: Maybe ByteString+    , cipherDataPadding :: Maybe (ByteString, Int)+    }+    deriving (Show, Eq) -data CertificateType =-      CertificateType_RSA_Sign         -- TLS10-    | CertificateType_DSS_Sign         -- TLS10-    | CertificateType_RSA_Fixed_DH     -- TLS10-    | CertificateType_DSS_Fixed_DH     -- TLS10-    | CertificateType_RSA_Ephemeral_DH -- TLS12-    | CertificateType_DSS_Ephemeral_DH -- TLS12-    | CertificateType_fortezza_dms     -- TLS12-    | CertificateType_Unknown Word8-    deriving (Show,Eq)+---------------------------------------------------------------- -data HashAlgorithm =-      HashNone-    | HashMD5-    | HashSHA1-    | HashSHA224-    | HashSHA256-    | HashSHA384-    | HashSHA512-    | HashIntrinsic-    | HashOther Word8-    deriving (Show,Eq)+-- | Some of the IANA registered code points for 'CertificateType' are not+-- currently supported by the library.  Nor should they be, they're are either+-- unwise, obsolete or both.  There's no point in conveying these to the user+-- in the client certificate request callback.  The request callback will be+-- filtered to exclude unsupported values.  If the user cannot find a certificate+-- for a supported code point, we'll go ahead without a client certificate and+-- hope for the best, unless the user's callback decides to throw an exception.+newtype CertificateType = CertificateType {fromCertificateType :: Word8}+    deriving (Eq, Ord) -data SignatureAlgorithm =-      SignatureAnonymous-    | SignatureRSA-    | SignatureDSS-    | SignatureECDSA-    | SignatureRSApssSHA256-    | SignatureRSApssSHA384-    | SignatureRSApssSHA512-    | SignatureEd25519-    | SignatureEd448-    | SignatureOther Word8-    deriving (Show,Eq)+{- FOURMOLU_DISABLE -}+-- | TLS10 and up, RFC5246+pattern CertificateType_RSA_Sign     :: CertificateType+pattern CertificateType_RSA_Sign      = CertificateType 1+-- | TLS10 and up, RFC5246+pattern CertificateType_DSA_Sign     :: CertificateType+pattern CertificateType_DSA_Sign      = CertificateType 2+-- | TLS10 and up, RFC8422+pattern CertificateType_ECDSA_Sign   :: CertificateType+pattern CertificateType_ECDSA_Sign    = CertificateType 64+-- \| There are no code points that map to the below synthetic types, these+-- are inferred indirectly from the @signature_algorithms@ extension of the+-- TLS 1.3 @CertificateRequest@ message.  the value assignments are there+-- only to avoid partial function warnings.+pattern CertificateType_Ed25519_Sign :: CertificateType+pattern CertificateType_Ed25519_Sign  = CertificateType 254 -- fixme: dummy value+pattern CertificateType_Ed448_Sign   :: CertificateType+pattern CertificateType_Ed448_Sign    = CertificateType 255 -- fixme:  dummy value -type HashAndSignatureAlgorithm = (HashAlgorithm, SignatureAlgorithm)+instance Show CertificateType where+    show CertificateType_RSA_Sign     = "rsa_sign"+    show CertificateType_DSA_Sign     = "dss_sign"+    show CertificateType_ECDSA_Sign   = "ecdsa_sign"+    show CertificateType_Ed25519_Sign = "ed25519_sign"+    show CertificateType_Ed448_Sign   = "ed448_sign"+    show (CertificateType x)          = "CertificateType " ++ show x+{- FOURMOLU_ENABLE -} +-- | Last supported certificate type, no 'CertificateType that+-- compares greater than this one (based on the 'Ord' instance,+-- not on the wire code point) will be reported to the application+-- via the client certificate request callback.+lastSupportedCertificateType :: CertificateType+lastSupportedCertificateType = CertificateType_ECDSA_Sign++------------------------------------------------------------+ type Signature = ByteString -data DigitallySigned = DigitallySigned (Maybe HashAndSignatureAlgorithm) Signature-    deriving (Show,Eq)+data DigitallySigned = DigitallySigned HashAndSignatureAlgorithm Signature+    deriving (Eq) -data ProtocolType =-      ProtocolType_ChangeCipherSpec-    | ProtocolType_Alert-    | ProtocolType_Handshake-    | ProtocolType_AppData-    | ProtocolType_DeprecatedHandshake-    deriving (Eq, Show)+instance Show DigitallySigned where+    show (DigitallySigned hs _sig) = "DigitallySigned " ++ show hs ++ " \"...\"" --- | TLSError that might be returned through the TLS stack-data TLSError =-      Error_Misc String        -- ^ mainly for instance of Error-    | Error_Protocol (String, Bool, AlertDescription)-    | Error_Certificate String-    | Error_HandshakePolicy String -- ^ handshake policy failed.-    | Error_EOF-    | Error_Packet String-    | Error_Packet_unexpected String String-    | Error_Packet_Parsing String-    deriving (Eq, Show, Typeable)+---------------------------------------------------------------- -#if MIN_VERSION_mtl(2,2,1)-#else-instance Error TLSError where-    noMsg  = Error_Misc ""-    strMsg = Error_Misc-#endif+newtype ProtocolType = ProtocolType {fromProtocolType :: Word8} deriving (Eq) -instance Exception TLSError+{- FOURMOLU_DISABLE -}+pattern ProtocolType_ChangeCipherSpec :: ProtocolType+pattern ProtocolType_ChangeCipherSpec  = ProtocolType 20 --- | TLS Exceptions related to bad user usage or--- asynchronous errors-data TLSException =-      Terminated Bool String TLSError -- ^ Early termination exception with the reason-                                      --   and the error associated-    | HandshakeFailed TLSError        -- ^ Handshake failed for the reason attached-    | ConnectionNotEstablished        -- ^ Usage error when the connection has not been established-                                      --   and the user is trying to send or receive data-    deriving (Show,Eq,Typeable)+pattern ProtocolType_Alert            :: ProtocolType+pattern ProtocolType_Alert             = ProtocolType 21 -instance Exception TLSException+pattern ProtocolType_Handshake        :: ProtocolType+pattern ProtocolType_Handshake         = ProtocolType 22 -data Packet =-      Handshake [Handshake]+pattern ProtocolType_AppData          :: ProtocolType+pattern ProtocolType_AppData           = ProtocolType 23++instance Show ProtocolType where+    show ProtocolType_ChangeCipherSpec = "ChangeCipherSpec"+    show ProtocolType_Alert            = "Alert"+    show ProtocolType_Handshake        = "Handshake"+    show ProtocolType_AppData          = "AppData"+    show (ProtocolType x)              = "ProtocolType " ++ show x+{- FOURMOLU_ENABLE -}++----------------------------------------------------------------++data Packet+    = Handshake [Handshake] [WireBytes]     | Alert [(AlertLevel, AlertDescription)]     | ChangeCipherSpec     | AppData ByteString-    deriving (Show,Eq)--data Header = Header ProtocolType Version Word16 deriving (Show,Eq)+    deriving (Eq) -newtype ServerRandom = ServerRandom { unServerRandom :: ByteString } deriving (Show, Eq)-newtype ClientRandom = ClientRandom { unClientRandom :: ByteString } deriving (Show, Eq)-newtype Session = Session (Maybe SessionID) deriving (Show, Eq)+instance Show Packet where+    show (Handshake hs _) = "Handshake " ++ show hs+    show (Alert as) = "Alert " ++ show as+    show ChangeCipherSpec = "ChangeCipherSpec"+    show (AppData bs) = "AppData " ++ showBytesHex bs -type FinishedData = ByteString-type ExtensionID  = Word16+data Header = Header ProtocolType Version Word16 deriving (Show, Eq) -data ExtensionRaw = ExtensionRaw ExtensionID ByteString+newtype ServerRandom = ServerRandom {unServerRandom :: ByteString}     deriving (Eq)+instance Show ServerRandom where+    show sr@(ServerRandom bs)+        | isHelloRetryRequest sr = "HelloRetryReqest"+        | otherwise = "ServerRandom " ++ showBytesHex bs -instance Show ExtensionRaw where-    show (ExtensionRaw eid bs) = "ExtensionRaw " ++ show eid ++ " " ++ showBytesHex bs ++ ""+hrrRandom :: ServerRandom+hrrRandom =+    ServerRandom+        "\xCF\x21\xAD\x74\xE5\x9A\x61\x11\xBE\x1D\x8C\x02\x1E\x65\xB8\x91\xC2\xA2\x11\x16\x7A\xBB\x8C\x5E\x07\x9E\x09\xE2\xC8\xA8\x33\x9C" -constrRandom32 :: (ByteString -> a) -> ByteString -> Maybe a-constrRandom32 constr l = if B.length l == 32 then Just (constr l) else Nothing+isHelloRetryRequest :: ServerRandom -> Bool+isHelloRetryRequest = (== hrrRandom) -serverRandom :: ByteString -> Maybe ServerRandom-serverRandom l = constrRandom32 ServerRandom l+newtype ClientRandom = ClientRandom {unClientRandom :: ByteString}+    deriving (Eq) -clientRandom :: ByteString -> Maybe ClientRandom-clientRandom l = constrRandom32 ClientRandom l+instance Show ClientRandom where+    show (ClientRandom bs) = "ClientRandom " ++ showBytesHex bs -data AlertLevel =-      AlertLevel_Warning-    | AlertLevel_Fatal-    deriving (Show,Eq)+newtype Session = Session (Maybe SessionID) deriving (Eq)+instance Show Session where+    show (Session Nothing) = "Session \"\""+    show (Session (Just bs)) = "Session " ++ showBytesHex bs -data AlertDescription =-      CloseNotify-    | UnexpectedMessage-    | BadRecordMac-    | DecryptionFailed       -- ^ deprecated alert, should never be sent by compliant implementation-    | RecordOverflow-    | DecompressionFailure-    | HandshakeFailure-    | BadCertificate-    | UnsupportedCertificate-    | CertificateRevoked-    | CertificateExpired-    | CertificateUnknown-    | IllegalParameter-    | UnknownCa-    | AccessDenied-    | DecodeError-    | DecryptError-    | ExportRestriction-    | ProtocolVersion-    | InsufficientSecurity-    | InternalError-    | InappropriateFallback -- RFC7507-    | UserCanceled-    | NoRenegotiation-    | UnsupportedExtension-    | CertificateUnobtainable-    | UnrecognizedName-    | BadCertificateStatusResponse-    | BadCertificateHashValue-    deriving (Show,Eq)+{-# DEPRECATED FinishedData "use VerifyData" #-}+type FinishedData = ByteString -data HandshakeType =-      HandshakeType_HelloRequest-    | HandshakeType_ClientHello-    | HandshakeType_ServerHello-    | HandshakeType_Certificate-    | HandshakeType_ServerKeyXchg-    | HandshakeType_CertRequest-    | HandshakeType_ServerHelloDone-    | HandshakeType_CertVerify-    | HandshakeType_ClientKeyXchg-    | HandshakeType_Finished-    deriving (Show,Eq)+newtype VerifyData = VerifyData ByteString deriving (Eq)+instance Show VerifyData where+    show (VerifyData bs) = showBytesHex bs -newtype BigNum = BigNum ByteString-    deriving (Show,Eq)+---------------------------------------------------------------- -bigNumToInteger :: BigNum -> Integer-bigNumToInteger (BigNum b) = os2ip b+newtype HandshakeType = HandshakeType {fromHandshakeType :: Word8}+    deriving (Eq) -bigNumFromInteger :: Integer -> BigNum-bigNumFromInteger i = BigNum $ i2osp i+{- FOURMOLU_DISABLE -}+pattern HandshakeType_HelloRequest          :: HandshakeType+pattern HandshakeType_HelloRequest           = HandshakeType 0+pattern HandshakeType_ClientHello           :: HandshakeType+pattern HandshakeType_ClientHello            = HandshakeType 1+pattern HandshakeType_ServerHello           :: HandshakeType+pattern HandshakeType_ServerHello            = HandshakeType 2+pattern HandshakeType_NewSessionTicket      :: HandshakeType+pattern HandshakeType_NewSessionTicket       = HandshakeType 4+pattern HandshakeType_EndOfEarlyData        :: HandshakeType+pattern HandshakeType_EndOfEarlyData         = HandshakeType 5+pattern HandshakeType_EncryptedExtensions   :: HandshakeType+pattern HandshakeType_EncryptedExtensions    = HandshakeType 8+pattern HandshakeType_Certificate           :: HandshakeType+pattern HandshakeType_Certificate            = HandshakeType 11+pattern HandshakeType_ServerKeyXchg         :: HandshakeType+pattern HandshakeType_ServerKeyXchg          = HandshakeType 12+pattern HandshakeType_CertRequest           :: HandshakeType+pattern HandshakeType_CertRequest            = HandshakeType 13+pattern HandshakeType_ServerHelloDone       :: HandshakeType+pattern HandshakeType_ServerHelloDone        = HandshakeType 14+pattern HandshakeType_CertVerify            :: HandshakeType+pattern HandshakeType_CertVerify             = HandshakeType 15+pattern HandshakeType_ClientKeyXchg         :: HandshakeType+pattern HandshakeType_ClientKeyXchg          = HandshakeType 16+pattern HandshakeType_Finished              :: HandshakeType+pattern HandshakeType_Finished               = HandshakeType 20+pattern HandshakeType_KeyUpdate             :: HandshakeType+pattern HandshakeType_KeyUpdate              = HandshakeType 24+pattern HandshakeType_CompressedCertificate :: HandshakeType+pattern HandshakeType_CompressedCertificate  = HandshakeType 25 +instance Show HandshakeType where+    show HandshakeType_HelloRequest          = "HelloRequest"+    show HandshakeType_ClientHello           = "ClientHello"+    show HandshakeType_ServerHello           = "ServerHello"+    show HandshakeType_NewSessionTicket      = "NewSessionTicket"+    show HandshakeType_EndOfEarlyData        = "EndOfEarlyData"+    show HandshakeType_EncryptedExtensions   = "EncryptedExtensions"+    show HandshakeType_Certificate           = "Certificate"+    show HandshakeType_ServerKeyXchg         = "ServerKeyXchg"+    show HandshakeType_CertRequest           = "CertRequest"+    show HandshakeType_ServerHelloDone       = "ServerHelloDone"+    show HandshakeType_CertVerify            = "CertVerify"+    show HandshakeType_ClientKeyXchg         = "ClientKeyXchg"+    show HandshakeType_Finished              = "Finished"+    show HandshakeType_KeyUpdate             = "KeyUpdate"+    show HandshakeType_CompressedCertificate = "CompressedCertificate"+    show (HandshakeType x)                   = "HandshakeType " ++ show x+{- FOURMOLU_ENABLE -}++----------------------------------------------------------------+ data ServerDHParams = ServerDHParams     { serverDHParams_p :: BigNum     , serverDHParams_g :: BigNum     , serverDHParams_y :: BigNum-    } deriving (Show,Eq)+    }+    deriving (Show, Eq)  serverDHParamsFrom :: DHParams -> DHPublic -> ServerDHParams serverDHParamsFrom params dhPub =-    ServerDHParams (bigNumFromInteger $ dhParamsGetP params)-                   (bigNumFromInteger $ dhParamsGetG params)-                   (bigNumFromInteger $ dhUnwrapPublic dhPub)+    ServerDHParams+        (bigNumFromInteger $ dhParamsGetP params)+        (bigNumFromInteger $ dhParamsGetG params)+        (bigNumFromInteger $ dhUnwrapPublic dhPub)  serverDHParamsToParams :: ServerDHParams -> DHParams serverDHParamsToParams serverParams =-    dhParams (bigNumToInteger $ serverDHParams_p serverParams)-             (bigNumToInteger $ serverDHParams_g serverParams)+    dhParams+        (bigNumToInteger $ serverDHParams_p serverParams)+        (bigNumToInteger $ serverDHParams_g serverParams)  serverDHParamsToPublic :: ServerDHParams -> DHPublic serverDHParamsToPublic serverParams =     dhPublic (bigNumToInteger $ serverDHParams_y serverParams) -data ServerECDHParams = ServerECDHParams Group GroupPublic-    deriving (Show,Eq)+---------------------------------------------------------------- +data ServerECDHParams = ServerECDHParams Group GroupPublicA+    deriving (Show, Eq)++----------------------------------------------------------------+ data ServerRSAParams = ServerRSAParams-    { rsa_modulus  :: Integer+    { rsa_modulus :: Integer     , rsa_exponent :: Integer-    } deriving (Show,Eq)+    }+    deriving (Show, Eq) -data ServerKeyXchgAlgorithmData =-      SKX_DH_Anon ServerDHParams-    | SKX_DHE_DSS ServerDHParams DigitallySigned+----------------------------------------------------------------++data ServerDSAParams = ServerDSAParams deriving (Show, Eq)++----------------------------------------------------------------++data ServerKeyXchgAlgorithmData+    = SKX_DH_Anon ServerDHParams+    | SKX_DHE_DSA ServerDHParams DigitallySigned     | SKX_DHE_RSA ServerDHParams DigitallySigned     | SKX_ECDHE_RSA ServerECDHParams DigitallySigned     | SKX_ECDHE_ECDSA ServerECDHParams DigitallySigned     | SKX_RSA (Maybe ServerRSAParams)-    | SKX_DH_DSS (Maybe ServerRSAParams)+    | SKX_DH_DSA (Maybe ServerDSAParams)     | SKX_DH_RSA (Maybe ServerRSAParams)     | SKX_Unparsed ByteString -- if we parse the server key xchg before knowing the actual cipher, we end up with this structure.     | SKX_Unknown ByteString-    deriving (Show,Eq)+    deriving (Eq) -data ClientKeyXchgAlgorithmData =-      CKX_RSA ByteString+{- FOURMOLU_DISABLE -}+instance Show ServerKeyXchgAlgorithmData where+    show (SKX_DH_Anon _)       = "SKX_DH_Anon"+    show (SKX_DHE_DSA _ _)     = "SKX_DHE_DSA"+    show (SKX_DHE_RSA _ _)     = "SKX_DHE_RSA"+    show (SKX_ECDHE_RSA _ _)   = "SKX_ECDHE_RSA"+    show (SKX_ECDHE_ECDSA _ _) = "SKX_ECDHE_ECDSA"+    show (SKX_RSA _)           = "SKX_RSA"+    show (SKX_DH_DSA _)        = "SKX_DH_DSA"+    show (SKX_DH_RSA _)        = "SKX_DH_RSA"+    show (SKX_Unparsed _)      = "SKX_Unparsed"+    show (SKX_Unknown _)       = "SKX_Unknown"+{- FOURMOLU_ENABLE -}++----------------------------------------------------------------++data ClientKeyXchgAlgorithmData+    = CKX_RSA ByteString     | CKX_DH DHPublic     | CKX_ECDH ByteString-    deriving (Show,Eq)+    deriving (Eq) -type DeprecatedRecord = ByteString+instance Show ClientKeyXchgAlgorithmData where+    show (CKX_RSA _bs) = "CKX_RSA \"...\""+    show (CKX_DH pub) = "CKX_DH " ++ show pub+    show (CKX_ECDH _bs) = "CKX_ECDH \"...\"" -data Handshake =-      ClientHello !Version !ClientRandom !Session ![CipherID] ![CompressionID] [ExtensionRaw] (Maybe DeprecatedRecord)-    | ServerHello !Version !ServerRandom !Session !CipherID !CompressionID [ExtensionRaw]-    | Certificates CertificateChain+----------------------------------------------------------------++newtype CertificateChain_ = CertificateChain_ CertificateChain deriving (Eq)+instance Show CertificateChain_ where+    show (CertificateChain_ cc) = showCertificateChain cc++emptyCertificateChain_ :: CertificateChain_+emptyCertificateChain_ = CertificateChain_ (CertificateChain [])++showCertificateChain :: CertificateChain -> String+showCertificateChain (CertificateChain xs) = show $ map getName xs+  where+    getName =+        maybe "" getCharacterStringRawData+            . lookup [2, 5, 4, 3]+            . getDistinguishedElements+            . certSubjectDN+            . signedObject+            . getSigned++data ClientHello = CH+    { chVersion :: Version+    , chRandom :: ClientRandom+    , chSession :: Session+    , chCiphers :: [CipherId]+    , chComps :: [CompressionID]+    , chExtensions :: [ExtensionRaw]+    }+    deriving (Eq, Show)++data ServerHello = SH+    { shVersion :: Version+    , shRandom :: ServerRandom+    , shSession :: Session+    , shCipher :: CipherId+    , shComp :: CompressionID+    , shExtensions :: [ExtensionRaw]+    }+    deriving (Eq, Show)++data Handshake+    = ClientHello ClientHello+    | ServerHello ServerHello+    | Certificate CertificateChain_     | HelloRequest     | ServerHelloDone     | ClientKeyXchg ClientKeyXchgAlgorithmData     | ServerKeyXchg ServerKeyXchgAlgorithmData-    | CertRequest [CertificateType] (Maybe [HashAndSignatureAlgorithm]) [DistinguishedName]+    | CertRequest+        [CertificateType]+        [HashAndSignatureAlgorithm]+        [DistinguishedName]     | CertVerify DigitallySigned-    | Finished FinishedData-    deriving (Show,Eq)+    | Finished VerifyData+    | NewSessionTicket Second Ticket+    deriving (Show, Eq) +{- FOURMOLU_DISABLE -} packetType :: Packet -> ProtocolType-packetType (Handshake _)    = ProtocolType_Handshake+packetType (Handshake _ _)  = ProtocolType_Handshake packetType (Alert _)        = ProtocolType_Alert packetType ChangeCipherSpec = ProtocolType_ChangeCipherSpec packetType (AppData _)      = ProtocolType_AppData  typeOfHandshake :: Handshake -> HandshakeType-typeOfHandshake ClientHello{}             = HandshakeType_ClientHello-typeOfHandshake ServerHello{}             = HandshakeType_ServerHello-typeOfHandshake Certificates{}            = HandshakeType_Certificate-typeOfHandshake HelloRequest              = HandshakeType_HelloRequest-typeOfHandshake ServerHelloDone           = HandshakeType_ServerHelloDone-typeOfHandshake ClientKeyXchg{}           = HandshakeType_ClientKeyXchg-typeOfHandshake ServerKeyXchg{}           = HandshakeType_ServerKeyXchg-typeOfHandshake CertRequest{}             = HandshakeType_CertRequest-typeOfHandshake CertVerify{}              = HandshakeType_CertVerify-typeOfHandshake Finished{}                = HandshakeType_Finished--numericalVer :: Version -> (Word8, Word8)-numericalVer SSL2  = (2, 0)-numericalVer SSL3  = (3, 0)-numericalVer TLS10 = (3, 1)-numericalVer TLS11 = (3, 2)-numericalVer TLS12 = (3, 3)--verOfNum :: (Word8, Word8) -> Maybe Version-verOfNum (2, 0) = Just SSL2-verOfNum (3, 0) = Just SSL3-verOfNum (3, 1) = Just TLS10-verOfNum (3, 2) = Just TLS11-verOfNum (3, 3) = Just TLS12-verOfNum _      = Nothing--class TypeValuable a where-    valOfType :: a -> Word8-    valToType :: Word8 -> Maybe a---- a better name for TypeValuable-class EnumSafe8 a where-    fromEnumSafe8 :: a -> Word8-    toEnumSafe8   :: Word8 -> Maybe a--class EnumSafe16 a where-    fromEnumSafe16 :: a -> Word16-    toEnumSafe16   :: Word16 -> Maybe a--instance TypeValuable ConnectionEnd where-    valOfType ConnectionServer = 0-    valOfType ConnectionClient = 1--    valToType 0 = Just ConnectionServer-    valToType 1 = Just ConnectionClient-    valToType _ = Nothing--instance TypeValuable CipherType where-    valOfType CipherStream = 0-    valOfType CipherBlock  = 1-    valOfType CipherAEAD   = 2--    valToType 0 = Just CipherStream-    valToType 1 = Just CipherBlock-    valToType 2 = Just CipherAEAD-    valToType _ = Nothing--instance TypeValuable ProtocolType where-    valOfType ProtocolType_ChangeCipherSpec    = 20-    valOfType ProtocolType_Alert               = 21-    valOfType ProtocolType_Handshake           = 22-    valOfType ProtocolType_AppData             = 23-    valOfType ProtocolType_DeprecatedHandshake = 128 -- unused--    valToType 20 = Just ProtocolType_ChangeCipherSpec-    valToType 21 = Just ProtocolType_Alert-    valToType 22 = Just ProtocolType_Handshake-    valToType 23 = Just ProtocolType_AppData-    valToType _  = Nothing--instance TypeValuable HandshakeType where-    valOfType HandshakeType_HelloRequest    = 0-    valOfType HandshakeType_ClientHello     = 1-    valOfType HandshakeType_ServerHello     = 2-    valOfType HandshakeType_Certificate     = 11-    valOfType HandshakeType_ServerKeyXchg   = 12-    valOfType HandshakeType_CertRequest     = 13-    valOfType HandshakeType_ServerHelloDone = 14-    valOfType HandshakeType_CertVerify      = 15-    valOfType HandshakeType_ClientKeyXchg   = 16-    valOfType HandshakeType_Finished        = 20--    valToType 0  = Just HandshakeType_HelloRequest-    valToType 1  = Just HandshakeType_ClientHello-    valToType 2  = Just HandshakeType_ServerHello-    valToType 11 = Just HandshakeType_Certificate-    valToType 12 = Just HandshakeType_ServerKeyXchg-    valToType 13 = Just HandshakeType_CertRequest-    valToType 14 = Just HandshakeType_ServerHelloDone-    valToType 15 = Just HandshakeType_CertVerify-    valToType 16 = Just HandshakeType_ClientKeyXchg-    valToType 20 = Just HandshakeType_Finished-    valToType _  = Nothing--instance TypeValuable AlertLevel where-    valOfType AlertLevel_Warning = 1-    valOfType AlertLevel_Fatal   = 2--    valToType 1 = Just AlertLevel_Warning-    valToType 2 = Just AlertLevel_Fatal-    valToType _ = Nothing--instance TypeValuable AlertDescription where-    valOfType CloseNotify            = 0-    valOfType UnexpectedMessage      = 10-    valOfType BadRecordMac           = 20-    valOfType DecryptionFailed       = 21-    valOfType RecordOverflow         = 22-    valOfType DecompressionFailure   = 30-    valOfType HandshakeFailure       = 40-    valOfType BadCertificate         = 42-    valOfType UnsupportedCertificate = 43-    valOfType CertificateRevoked     = 44-    valOfType CertificateExpired     = 45-    valOfType CertificateUnknown     = 46-    valOfType IllegalParameter       = 47-    valOfType UnknownCa              = 48-    valOfType AccessDenied           = 49-    valOfType DecodeError            = 50-    valOfType DecryptError           = 51-    valOfType ExportRestriction      = 60-    valOfType ProtocolVersion        = 70-    valOfType InsufficientSecurity   = 71-    valOfType InternalError          = 80-    valOfType InappropriateFallback  = 86-    valOfType UserCanceled           = 90-    valOfType NoRenegotiation        = 100-    valOfType UnsupportedExtension   = 110-    valOfType CertificateUnobtainable = 111-    valOfType UnrecognizedName        = 112-    valOfType BadCertificateStatusResponse = 113-    valOfType BadCertificateHashValue = 114--    valToType 0   = Just CloseNotify-    valToType 10  = Just UnexpectedMessage-    valToType 20  = Just BadRecordMac-    valToType 21  = Just DecryptionFailed-    valToType 22  = Just RecordOverflow-    valToType 30  = Just DecompressionFailure-    valToType 40  = Just HandshakeFailure-    valToType 42  = Just BadCertificate-    valToType 43  = Just UnsupportedCertificate-    valToType 44  = Just CertificateRevoked-    valToType 45  = Just CertificateExpired-    valToType 46  = Just CertificateUnknown-    valToType 47  = Just IllegalParameter-    valToType 48  = Just UnknownCa-    valToType 49  = Just AccessDenied-    valToType 50  = Just DecodeError-    valToType 51  = Just DecryptError-    valToType 60  = Just ExportRestriction-    valToType 70  = Just ProtocolVersion-    valToType 71  = Just InsufficientSecurity-    valToType 80  = Just InternalError-    valToType 86  = Just InappropriateFallback-    valToType 90  = Just UserCanceled-    valToType 100 = Just NoRenegotiation-    valToType 110 = Just UnsupportedExtension-    valToType 111 = Just CertificateUnobtainable-    valToType 112 = Just UnrecognizedName-    valToType 113 = Just BadCertificateStatusResponse-    valToType 114 = Just BadCertificateHashValue-    valToType _   = Nothing--instance TypeValuable CertificateType where-    valOfType CertificateType_RSA_Sign         = 1-    valOfType CertificateType_DSS_Sign         = 2-    valOfType CertificateType_RSA_Fixed_DH     = 3-    valOfType CertificateType_DSS_Fixed_DH     = 4-    valOfType CertificateType_RSA_Ephemeral_DH = 5-    valOfType CertificateType_DSS_Ephemeral_DH = 6-    valOfType CertificateType_fortezza_dms     = 20-    valOfType (CertificateType_Unknown i)      = i--    valToType 1  = Just CertificateType_RSA_Sign-    valToType 2  = Just CertificateType_DSS_Sign-    valToType 3  = Just CertificateType_RSA_Fixed_DH-    valToType 4  = Just CertificateType_DSS_Fixed_DH-    valToType 5  = Just CertificateType_RSA_Ephemeral_DH-    valToType 6  = Just CertificateType_DSS_Ephemeral_DH-    valToType 20 = Just CertificateType_fortezza_dms-    valToType i  = Just (CertificateType_Unknown i)--instance TypeValuable HashAlgorithm where-    valOfType HashNone      = 0-    valOfType HashMD5       = 1-    valOfType HashSHA1      = 2-    valOfType HashSHA224    = 3-    valOfType HashSHA256    = 4-    valOfType HashSHA384    = 5-    valOfType HashSHA512    = 6-    valOfType HashIntrinsic = 8-    valOfType (HashOther i) = i--    valToType 0 = Just HashNone-    valToType 1 = Just HashMD5-    valToType 2 = Just HashSHA1-    valToType 3 = Just HashSHA224-    valToType 4 = Just HashSHA256-    valToType 5 = Just HashSHA384-    valToType 6 = Just HashSHA512-    valToType 8 = Just HashIntrinsic-    valToType i = Just (HashOther i)--instance TypeValuable SignatureAlgorithm where-    valOfType SignatureAnonymous    = 0-    valOfType SignatureRSA          = 1-    valOfType SignatureDSS          = 2-    valOfType SignatureECDSA        = 3-    valOfType SignatureRSApssSHA256 = 4-    valOfType SignatureRSApssSHA384 = 5-    valOfType SignatureRSApssSHA512 = 6-    valOfType SignatureEd25519      = 7-    valOfType SignatureEd448        = 8-    valOfType (SignatureOther i)    = i--    valToType 0 = Just SignatureAnonymous-    valToType 1 = Just SignatureRSA-    valToType 2 = Just SignatureDSS-    valToType 3 = Just SignatureECDSA-    valToType 4 = Just SignatureRSApssSHA256-    valToType 5 = Just SignatureRSApssSHA384-    valToType 6 = Just SignatureRSApssSHA512-    valToType 7 = Just SignatureEd25519-    valToType 8 = Just SignatureEd448-    valToType i = Just (SignatureOther i)--instance EnumSafe16 Group where-    fromEnumSafe16 P256      =  23-    fromEnumSafe16 P384      =  24-    fromEnumSafe16 P521      =  25-    fromEnumSafe16 X25519    =  29-    fromEnumSafe16 X448      =  30-    fromEnumSafe16 FFDHE2048 = 256-    fromEnumSafe16 FFDHE3072 = 257-    fromEnumSafe16 FFDHE4096 = 258-    fromEnumSafe16 FFDHE6144 = 259-    fromEnumSafe16 FFDHE8192 = 260+typeOfHandshake ClientHello{}      = HandshakeType_ClientHello+typeOfHandshake ServerHello{}      = HandshakeType_ServerHello+typeOfHandshake Certificate{}      = HandshakeType_Certificate+typeOfHandshake HelloRequest       = HandshakeType_HelloRequest+typeOfHandshake ServerHelloDone    = HandshakeType_ServerHelloDone+typeOfHandshake ClientKeyXchg{}    = HandshakeType_ClientKeyXchg+typeOfHandshake ServerKeyXchg{}    = HandshakeType_ServerKeyXchg+typeOfHandshake CertRequest{}      = HandshakeType_CertRequest+typeOfHandshake CertVerify{}       = HandshakeType_CertVerify+typeOfHandshake Finished{}         = HandshakeType_Finished+typeOfHandshake NewSessionTicket{} = HandshakeType_NewSessionTicket+{- FOURMOLU_ENABLE -} -    toEnumSafe16  23 = Just P256-    toEnumSafe16  24 = Just P384-    toEnumSafe16  25 = Just P521-    toEnumSafe16  29 = Just X25519-    toEnumSafe16  30 = Just X448-    toEnumSafe16 256 = Just FFDHE2048-    toEnumSafe16 257 = Just FFDHE3072-    toEnumSafe16 258 = Just FFDHE4096-    toEnumSafe16 259 = Just FFDHE6144-    toEnumSafe16 260 = Just FFDHE8192-    toEnumSafe16 _   = Nothing+type HandshakeR = (Handshake, WireBytes)
+ Network/TLS/Struct13.hs view
@@ -0,0 +1,81 @@+module Network.TLS.Struct13 (+    Packet13 (..),+    Handshake13 (..),+    typeOfHandshake13,+    contentType,+    KeyUpdate (..),+    CertReqContext,+    isKeyUpdate13,+    TicketNonce (..),+    SessionIDorTicket_ (..),+    Handshake13R,+) where++import Network.TLS.Imports+import Network.TLS.Struct+import Network.TLS.Types++data Packet13+    = Handshake13 [Handshake13] [WireBytes]+    | Alert13 [(AlertLevel, AlertDescription)]+    | ChangeCipherSpec13+    | AppData13 ByteString+    deriving (Show, Eq)++data KeyUpdate+    = UpdateNotRequested+    | UpdateRequested+    deriving (Show, Eq)++newtype TicketNonce = TicketNonce ByteString deriving (Eq)++instance Show TicketNonce where+    show (TicketNonce bs) = showBytesHex bs++newtype SessionIDorTicket_ = SessionIDorTicket_ ByteString deriving (Eq)++instance Show SessionIDorTicket_ where+    show (SessionIDorTicket_ bs) = showBytesHex bs++-- fixme: convert Word32 to proper data type+data Handshake13+    = ServerHello13 ServerHello+    | NewSessionTicket13 Second Word32 TicketNonce SessionIDorTicket_ [ExtensionRaw]+    | EndOfEarlyData13+    | EncryptedExtensions13 [ExtensionRaw]+    | Certificate13 CertReqContext CertificateChain_ [[ExtensionRaw]]+    | CertRequest13 CertReqContext [ExtensionRaw]+    | CertVerify13 DigitallySigned+    | Finished13 VerifyData+    | KeyUpdate13 KeyUpdate+    | CompressedCertificate13 CertReqContext CertificateChain_ [[ExtensionRaw]]+    deriving (Show, Eq)++-- | Certificate request context for TLS 1.3.+type CertReqContext = ByteString++{- FOURMOLU_DISABLE -}+typeOfHandshake13 :: Handshake13 -> HandshakeType+typeOfHandshake13 ServerHello13{}           = HandshakeType_ServerHello+typeOfHandshake13 NewSessionTicket13{}      = HandshakeType_NewSessionTicket+typeOfHandshake13 EndOfEarlyData13{}        = HandshakeType_EndOfEarlyData+typeOfHandshake13 EncryptedExtensions13{}   = HandshakeType_EncryptedExtensions+typeOfHandshake13 Certificate13{}           = HandshakeType_Certificate+typeOfHandshake13 CertRequest13{}           = HandshakeType_CertRequest+typeOfHandshake13 CertVerify13{}            = HandshakeType_CertVerify+typeOfHandshake13 Finished13{}              = HandshakeType_Finished+typeOfHandshake13 KeyUpdate13{}             = HandshakeType_KeyUpdate+typeOfHandshake13 CompressedCertificate13{} = HandshakeType_CompressedCertificate++contentType :: Packet13 -> ProtocolType+contentType ChangeCipherSpec13 = ProtocolType_ChangeCipherSpec+contentType Handshake13{}      = ProtocolType_Handshake+contentType Alert13{}          = ProtocolType_Alert+contentType AppData13{}        = ProtocolType_AppData+{- FOURMOLU_ENABLE -}++isKeyUpdate13 :: Handshake13 -> Bool+isKeyUpdate13 (KeyUpdate13 _) = True+isKeyUpdate13 _ = False++type Handshake13R = (Handshake13, WireBytes)
Network/TLS/Types.hs view
@@ -1,56 +1,71 @@--- |--- Module      : Network.TLS.Types--- License     : BSD-style--- Maintainer  : Vincent Hanquez <vincent@snarc.org>--- Stability   : experimental--- Portability : unknown----module Network.TLS.Types-    ( Version(..)-    , SessionID-    , SessionData(..)-    , CipherID-    , CompressionID-    , Role(..)-    , invertRole-    , Direction(..)-    ) where+module Network.TLS.Types (+    module Network.TLS.Types.Cipher,+    module Network.TLS.Types.Secret,+    module Network.TLS.Types.Session,+    module Network.TLS.Types.Version,+    HostName,+    Role (..),+    invertRole,+    Direction (..),+    BigNum (..),+    bigNumToInteger,+    bigNumFromInteger,+    defaultRecordSizeLimit,+    TranscriptHash (..),+    WireBytes,+) where +import Network.Socket (HostName)+ import Network.TLS.Imports+import Network.TLS.Types.Cipher+import Network.TLS.Types.Secret+import Network.TLS.Types.Session+import Network.TLS.Types.Version+import Network.TLS.Util.Serialization -type HostName = String+---------------------------------------------------------------- --- | Versions known to TLS------ SSL2 is just defined, but this version is and will not be supported.-data Version = SSL2 | SSL3 | TLS10 | TLS11 | TLS12 deriving (Show, Eq, Ord, Bounded)+-- | Role+data Role = ClientRole | ServerRole+    deriving (Show, Eq) --- | A session ID-type SessionID = ByteString+invertRole :: Role -> Role+invertRole ClientRole = ServerRole+invertRole ServerRole = ClientRole --- | Session data to resume-data SessionData = SessionData-    { sessionVersion     :: Version-    , sessionCipher      :: CipherID-    , sessionCompression :: CompressionID-    , sessionClientSNI   :: Maybe HostName-    , sessionSecret      :: ByteString-    } deriving (Show,Eq)+---------------------------------------------------------------- --- | Cipher identification-type CipherID = Word16+-- | Direction+data Direction = Tx | Rx+    deriving (Show, Eq) --- | Compression identification-type CompressionID = Word8+---------------------------------------------------------------- --- | Role-data Role = ClientRole | ServerRole-    deriving (Show,Eq)+newtype BigNum = BigNum ByteString+    deriving (Show, Eq) --- | Direction-data Direction = Tx | Rx-    deriving (Show,Eq)+bigNumToInteger :: BigNum -> Integer+bigNumToInteger (BigNum b) = os2ip b -invertRole :: Role -> Role-invertRole ClientRole = ServerRole-invertRole ServerRole = ClientRole+bigNumFromInteger :: Integer -> BigNum+bigNumFromInteger i = BigNum $ i2osp i++----------------------------------------------------------------++-- For plaintext+-- 2^14 for TLS 1.2+-- 2^14 + 1 for TLS 1.3+defaultRecordSizeLimit :: Int+defaultRecordSizeLimit = 16384++----------------------------------------------------------------++newtype TranscriptHash = TranscriptHash ByteString++instance Show TranscriptHash where+    show (TranscriptHash bs) = showBytesHex bs++----------------------------------------------------------------++type WireBytes = [ByteString]
+ Network/TLS/Types/Cipher.hs view
@@ -0,0 +1,130 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}++module Network.TLS.Types.Cipher where++import Crypto.Cipher.Types (AuthTag)+import Data.ByteArray (ScrubbedBytes)+import Data.IORef+import GHC.Generics+import System.IO.Unsafe (unsafePerformIO)+import Text.Printf++import Network.TLS.Crypto (Hash (..))+import Network.TLS.Imports+import Network.TLS.Types.Version++----------------------------------------------------------------++type PlainText = ByteString+type CipherText = ByteString+type Secret = ScrubbedBytes+type Key = ScrubbedBytes+type IV = ByteString+type Nonce = ByteString -- aka IV+type AddDat = ByteString++----------------------------------------------------------------++-- | Cipher identification+type CipherID = Word16++newtype CipherId = CipherId {fromCipherId :: Word16}+    deriving (Eq, Ord, Enum, Num, Integral, Real, Read, Generic)++instance Show CipherId where+    show (CipherId 0x00FF) = "TLS_EMPTY_RENEGOTIATION_INFO_SCSV"+    show (CipherId n) = case find eqID dict of+        Just c -> cipherName c+        Nothing -> printf "0x%04X" n+      where+        eqID c = cipherID c == n+        dict = unsafePerformIO $ readIORef globalCipherDict++-- "ciphersuite" is designed extensible.+-- So, it's not available from internal modules.+-- This is a compromise to gule "ciphersuite" to Show CipherID.++{-# NOINLINE globalCipherDict #-}+globalCipherDict :: IORef [Cipher]+globalCipherDict = unsafePerformIO $ newIORef []++----------------------------------------------------------------++-- | Cipher algorithm+data Cipher = Cipher+    { cipherID :: CipherID+    , cipherName :: String+    , cipherHash :: Hash+    , cipherBulk :: Bulk+    , cipherKeyExchange :: CipherKeyExchangeType+    , cipherMinVer :: Maybe Version+    , cipherPRFHash :: Maybe Hash+    }++instance Show Cipher where+    show c = cipherName c++instance Eq Cipher where+    (==) c1 c2 = cipherID c1 == cipherID c2++----------------------------------------------------------------++data CipherKeyExchangeType+    = CipherKeyExchange_RSA+    | CipherKeyExchange_DH_Anon+    | CipherKeyExchange_DHE_RSA+    | CipherKeyExchange_ECDHE_RSA+    | CipherKeyExchange_DHE_DSA+    | CipherKeyExchange_DH_DSA+    | CipherKeyExchange_DH_RSA+    | CipherKeyExchange_ECDH_ECDSA+    | CipherKeyExchange_ECDH_RSA+    | CipherKeyExchange_ECDHE_ECDSA+    | CipherKeyExchange_TLS13 -- not expressed in cipher suite+    deriving (Show, Eq)++----------------------------------------------------------------++data Bulk = Bulk+    { bulkName :: String+    , bulkKeySize :: Int+    , bulkIVSize :: Int+    , bulkExplicitIV :: Int -- Explicit size for IV for AEAD Cipher, 0 otherwise+    , bulkAuthTagLen :: Int -- Authentication tag length in bytes for AEAD Cipher, 0 otherwise+    , bulkBlockSize :: Int+    , bulkF :: BulkFunctions+    }++instance Show Bulk where+    show bulk = bulkName bulk+instance Eq Bulk where+    b1 == b2 =+        and+            [ bulkName b1 == bulkName b2+            , bulkKeySize b1 == bulkKeySize b2+            , bulkIVSize b1 == bulkIVSize b2+            , bulkBlockSize b1 == bulkBlockSize b2+            ]++----------------------------------------------------------------++data BulkFunctions+    = BulkBlockF (BulkDirection -> BulkKey -> BulkBlock)+    | BulkStreamF (BulkDirection -> BulkKey -> BulkStream)+    | BulkAeadF (BulkDirection -> BulkKey -> BulkAEAD)++data BulkDirection = BulkEncrypt | BulkDecrypt+    deriving (Show, Eq)++type BulkKey = Secret+type BulkIV = Nonce+type BulkNonce = Nonce+type BulkAdditionalData = ByteString++type BulkBlock = BulkIV -> ByteString -> (ByteString, BulkIV)++newtype BulkStream = BulkStream (ByteString -> (ByteString, BulkStream))++type BulkAEAD =+    BulkNonce -> ByteString -> BulkAdditionalData -> (ByteString, AuthTag)
+ Network/TLS/Types/Secret.hs view
@@ -0,0 +1,62 @@+module Network.TLS.Types.Secret where++import Data.ByteArray (convert)+import Network.TLS.Imports+import Network.TLS.Types.Cipher++-- | Phantom type indicating early traffic secret.+data EarlySecret++-- | Phantom type indicating handshake traffic secrets.+data HandshakeSecret++-- | Phantom type indicating application traffic secrets.+data ApplicationSecret++data ResumptionSecret++newtype BaseSecret a = BaseSecret Secret++instance Show (BaseSecret a) where+    show (BaseSecret bs) = showBytesHex $ convert bs++newtype AnyTrafficSecret a = AnyTrafficSecret Secret++instance Show (AnyTrafficSecret a) where+    show (AnyTrafficSecret bs) = showBytesHex $ convert bs++-- | A client traffic secret, typed with a parameter indicating a step in the+-- TLS key schedule.+newtype ClientTrafficSecret a = ClientTrafficSecret Secret++instance Show (ClientTrafficSecret a) where+    show (ClientTrafficSecret bs) = showBytesHex $ convert bs++-- | A server traffic secret, typed with a parameter indicating a step in the+-- TLS key schedule.+newtype ServerTrafficSecret a = ServerTrafficSecret Secret++instance Show (ServerTrafficSecret a) where+    show (ServerTrafficSecret bs) = showBytesHex $ convert bs++data SecretTriple a = SecretTriple+    { triBase :: BaseSecret a+    , triClient :: ClientTrafficSecret a+    , triServer :: ServerTrafficSecret a+    }+    deriving (Show)++data SecretPair a = SecretPair+    { pairBase :: BaseSecret a+    , pairClient :: ClientTrafficSecret a+    }+    deriving (Show)++-- | Hold both client and server traffic secrets at the same step.+type TrafficSecrets a = (ClientTrafficSecret a, ServerTrafficSecret a)++-- Main secret for TLS 1.2 or earlier.+newtype MainSecret = MainSecret Secret++instance Show MainSecret where+    show (MainSecret bs) = showBytesHex $ convert bs
+ Network/TLS/Types/Session.hs view
@@ -0,0 +1,73 @@+{-# LANGUAGE DeriveGeneric #-}++module Network.TLS.Types.Session where++import Codec.Serialise+import qualified Data.ByteString as B+import GHC.Generics+import Network.Socket (HostName)++import Network.TLS.Crypto (Group, Hash (..), hash)+import Network.TLS.Imports+import Network.TLS.Types.Cipher+import Network.TLS.Types.Version++-- | A session ID+type SessionID = ByteString++-- | Identity+type SessionIDorTicket = ByteString++-- | Encrypted session ticket (encrypt(encode 'SessionData')).+type Ticket = ByteString++isTicket :: SessionIDorTicket -> Bool+isTicket x+    | B.length x > 32 = True+    | otherwise = False++toSessionID :: Ticket -> SessionID+toSessionID = hash SHA256++-- | Compression identification+type CompressionID = Word8++-- | Session data to resume+data SessionData = SessionData+    { sessionVersion :: Version+    , sessionCipher :: CipherID+    , sessionCompression :: CompressionID+    , sessionClientSNI :: Maybe HostName+    , -- ScrubbedBytes is not an instance of Generic, sigh.+      sessionSecret :: ByteString+    , sessionGroup :: Maybe Group+    , sessionTicketInfo :: Maybe TLS13TicketInfo+    , sessionALPN :: Maybe ByteString+    , sessionMaxEarlyDataSize :: Int+    , sessionFlags :: [SessionFlag]+    } -- sessionFromTicket :: Bool+    deriving (Show, Eq, Generic)++is0RTTPossible :: SessionData -> Bool+is0RTTPossible sd = sessionMaxEarlyDataSize sd > 0++-- | Some session flags+data SessionFlag+    = -- | Session created with Extended Main Secret+      SessionEMS+    deriving (Show, Eq, Enum, Generic)++type Second = Word32+type Millisecond = Word64++data TLS13TicketInfo = TLS13TicketInfo+    { lifetime :: Second -- NewSessionTicket.ticket_lifetime in seconds+    , ageAdd :: Second -- NewSessionTicket.ticket_age_add+    , txrxTime :: Millisecond -- serverSendTime or clientReceiveTime+    , estimatedRTT :: Maybe Millisecond+    }+    deriving (Show, Eq, Generic)++instance Serialise TLS13TicketInfo+instance Serialise SessionFlag+instance Serialise SessionData
+ Network/TLS/Types/Version.hs view
@@ -0,0 +1,40 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE PatternSynonyms #-}++module Network.TLS.Types.Version (+    Version (Version, SSL2, SSL3, TLS10, TLS11, TLS12, TLS13),+) where++import Codec.Serialise+import GHC.Generics++import Network.TLS.Imports++-- | Versions known to TLS+newtype Version = Version Word16 deriving (Eq, Ord, Generic)++{- FOURMOLU_DISABLE -}+pattern SSL2  :: Version+pattern SSL2   = Version 0x0002+pattern SSL3  :: Version+pattern SSL3   = Version 0x0300+pattern TLS10 :: Version+pattern TLS10  = Version 0x0301+pattern TLS11 :: Version+pattern TLS11  = Version 0x0302+pattern TLS12 :: Version+pattern TLS12  = Version 0x0303+pattern TLS13 :: Version+pattern TLS13  = Version 0x0304++instance Show Version where+    show SSL2  = "SSL2"+    show SSL3  = "SSL3"+    show TLS10 = "TLS1.0"+    show TLS11 = "TLS1.1"+    show TLS12 = "TLS1.2"+    show TLS13 = "TLS1.3"+    show (Version x) = "Version " ++ show x+{- FOURMOLU_ENABLE -}++instance Serialise Version
Network/TLS/Util.hs view
@@ -1,78 +1,125 @@ {-# LANGUAGE ScopedTypeVariables #-}-module Network.TLS.Util-        ( sub-        , takelast-        , partition3-        , partition6-        , fromJust-        , and'-        , (&&!)-        , bytesEq-        , fmapEither-        , catchException-        ) where+{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-} +module Network.TLS.Util (+    sub,+    takelast,+    partition3,+    partition6,+    (&&!),+    fmapEither,+    catchException,+    forEitherM,+    mapChunks_,+    getChunks,+    Saved,+    saveMVar,+    restoreMVar,+) where++import Control.Concurrent.MVar+import Control.Exception (SomeAsyncException (..))+import qualified Control.Exception as E+import Data.ByteArray (ScrubbedBytes)+import qualified Data.ByteArray as BA import qualified Data.ByteString as B-import Network.TLS.Imports -import Control.Exception (SomeException)-import Control.Concurrent.Async+import Network.TLS.Imports  sub :: ByteString -> Int -> Int -> Maybe ByteString sub b offset len     | B.length b < offset + len = Nothing-    | otherwise                 = Just $ B.take len $ snd $ B.splitAt offset b+    | otherwise = Just $ B.take len $ snd $ B.splitAt offset b  takelast :: Int -> ByteString -> Maybe ByteString takelast i b     | B.length b >= i = sub b (B.length b - i) i-    | otherwise       = Nothing+    | otherwise = Nothing -partition3 :: ByteString -> (Int,Int,Int) -> Maybe (ByteString, ByteString, ByteString)-partition3 bytes (d1,d2,d3)-    | any (< 0) l             = Nothing+partition3+    :: ByteString -> (Int, Int, Int) -> Maybe (ByteString, ByteString, ByteString)+partition3 bytes (d1, d2, d3)+    | any (< 0) l = Nothing     | sum l /= B.length bytes = Nothing-    | otherwise               = Just (p1,p2,p3)-        where l        = [d1,d2,d3]-              (p1, r1) = B.splitAt d1 bytes-              (p2, r2) = B.splitAt d2 r1-              (p3, _)  = B.splitAt d3 r2--partition6 :: ByteString -> (Int,Int,Int,Int,Int,Int) -> Maybe (ByteString, ByteString, ByteString, ByteString, ByteString, ByteString)-partition6 bytes (d1,d2,d3,d4,d5,d6) = if B.length bytes < s then Nothing else Just (p1,p2,p3,p4,p5,p6)-  where s        = sum [d1,d2,d3,d4,d5,d6]-        (p1, r1) = B.splitAt d1 bytes-        (p2, r2) = B.splitAt d2 r1-        (p3, r3) = B.splitAt d3 r2-        (p4, r4) = B.splitAt d4 r3-        (p5, r5) = B.splitAt d5 r4-        (p6, _)  = B.splitAt d6 r5--fromJust :: String -> Maybe a -> a-fromJust what Nothing  = error ("fromJust " ++ what ++ ": Nothing") -- yuck-fromJust _    (Just x) = x+    | otherwise = Just (p1, p2, p3)+  where+    l = [d1, d2, d3]+    (p1, r1) = B.splitAt d1 bytes+    (p2, r2) = B.splitAt d2 r1+    (p3, _) = B.splitAt d3 r2 --- | This is a strict version of and-and' :: [Bool] -> Bool-and' l = foldl' (&&!) True l+partition6+    :: ScrubbedBytes+    -> (Int, Int, Int, Int, Int, Int)+    -> Maybe+        ( ScrubbedBytes+        , ScrubbedBytes+        , ScrubbedBytes+        , ScrubbedBytes+        , ScrubbedBytes+        , ScrubbedBytes+        )+partition6 bytes (d1, d2, d3, d4, d5, d6) = if BA.length bytes < s then Nothing else Just (p1, p2, p3, p4, p5, p6)+  where+    slice' (beg, len) = BA.unsafeSlice bytes beg len+    lens = [d1, d2, d3, d4, d5, d6]+    s = sum lens+    begs = scanl (+) 0 lens+    ys = zip begs lens+    [p1, p2, p3, p4, p5, p6] = map slice' ys  -- | This is a strict version of &&. (&&!) :: Bool -> Bool -> Bool-True  &&! True  = True-True  &&! False = False-False &&! True  = False+True &&! True = True+True &&! False = False+False &&! True = False False &&! False = False --- | verify that 2 bytestrings are equals.--- it's a non lazy version, that will compare every bytes.--- arguments with different length will bail out early-bytesEq :: ByteString -> ByteString -> Bool-bytesEq b1 b2-    | B.length b1 /= B.length b2 = False-    | otherwise                  = and' $ B.zipWith (==) b1 b2- fmapEither :: (a -> b) -> Either l a -> Either l b fmapEither f = fmap f -catchException :: IO a -> (SomeException -> IO a) -> IO a-catchException action handler = withAsync action waitCatch >>= either handler return+catchException :: IO a -> (E.SomeException -> IO a) -> IO a+catchException f handler = E.catchJust filterExn f handler+  where+    filterExn :: E.SomeException -> Maybe E.SomeException+    filterExn e = case E.fromException (E.toException e) of+        Just (SomeAsyncException _) -> Nothing+        Nothing -> Just e++forEitherM :: Monad m => [a] -> (a -> m (Either l b)) -> m (Either l [b])+forEitherM [] _ = return (pure [])+forEitherM (x : xs) f = f x >>= doTail+  where+    doTail (Right b) = fmap (b :) <$> forEitherM xs f+    doTail (Left e) = return (Left e)++mapChunks_+    :: Monad m+    => Maybe Int+    -> (ByteString -> m a)+    -> ByteString+    -> m ()+mapChunks_ len f = mapM_ f . getChunks len++getChunks :: Maybe Int -> ByteString -> [ByteString]+getChunks Nothing = (: [])+getChunks (Just len) = go+  where+    go bs+        | B.length bs > len =+            let (chunk, remain) = B.splitAt len bs+             in chunk : go remain+        | otherwise = [bs]++-- | An opaque newtype wrapper to prevent from poking inside content that has+-- been saved.+newtype Saved a = Saved a++-- | Save the content of an 'MVar' to restore it later.+saveMVar :: MVar a -> IO (Saved a)+saveMVar ref = Saved <$> readMVar ref++-- | Restore the content of an 'MVar' to a previous saved value and return the+-- content that has just been replaced.+restoreMVar :: MVar a -> Saved a -> IO (Saved a)+restoreMVar ref (Saved val) = Saved <$> swapMVar ref val
Network/TLS/Util/ASN1.hs view
@@ -1,37 +1,32 @@--- |--- Module      : Network.TLS.Util.ASN1--- License     : BSD-style--- Maintainer  : Vincent Hanquez <vincent@snarc.org>--- Stability   : experimental--- Portability : unknown------ ASN1 utils for TLS----module Network.TLS.Util.ASN1-    ( decodeASN1Object-    , encodeASN1Object-    ) where+-- | ASN1 utils for TLS+module Network.TLS.Util.ASN1 (+    decodeASN1Object,+    encodeASN1Object,+) where -import Network.TLS.Imports-import Data.ASN1.Types (fromASN1, toASN1, ASN1Object)+import Data.ASN1.BinaryEncoding (DER (..)) import Data.ASN1.Encoding (decodeASN1', encodeASN1')-import Data.ASN1.BinaryEncoding (DER(..))+import Data.ASN1.Types (ASN1Object, fromASN1, toASN1) +import Network.TLS.Imports+ -- | Attempt to decode a bytestring representing -- an DER ASN.1 serialized object into the object.-decodeASN1Object :: ASN1Object a-                 => String-                 -> ByteString-                 -> Either String a+decodeASN1Object+    :: ASN1Object a+    => String+    -> ByteString+    -> Either String a decodeASN1Object name bs =     case decodeASN1' DER bs of-        Left e     -> Left (name ++ ": cannot decode ASN1: " ++ show e)+        Left e -> Left (name ++ ": cannot decode ASN1: " ++ show e)         Right asn1 -> case fromASN1 asn1 of-                            Left e      -> Left (name ++ ": cannot parse ASN1: " ++ show e)-                            Right (d,_) -> Right d+            Left e -> Left (name ++ ": cannot parse ASN1: " ++ show e)+            Right (d, _) -> Right d  -- | Encode an ASN.1 Object to the DER serialized bytestring-encodeASN1Object :: ASN1Object a-                 => a-                 -> ByteString+encodeASN1Object+    :: ASN1Object a+    => a+    -> ByteString encodeASN1Object obj = encodeASN1' DER $ toASN1 obj []
Network/TLS/Util/Serialization.hs view
@@ -1,12 +1,7 @@-module Network.TLS.Util.Serialization-    ( os2ip-    , i2osp-    , i2ospOf_-    , lengthBytes-    ) where--import Crypto.Number.Basic      (numBytes)-import Crypto.Number.Serialize (os2ip, i2osp, i2ospOf_)+module Network.TLS.Util.Serialization (+    os2ip,+    i2osp,+    i2ospOf_,+) where -lengthBytes :: Integer -> Int-lengthBytes = numBytes+import Crypto.Number.Serialize (i2osp, i2ospOf_, os2ip)
Network/TLS/Wire.hs view
@@ -1,85 +1,84 @@--- |--- Module      : Network.TLS.Wire--- License     : BSD-style--- Maintainer  : Vincent Hanquez <vincent@snarc.org>--- Stability   : experimental--- Portability : unknown------ the Wire module is a specialized marshalling/unmarshalling package related to the TLS protocol.--- all multibytes values are written as big endian.----module Network.TLS.Wire-    ( Get-    , GetResult(..)-    , GetContinuation-    , runGet-    , runGetErr-    , runGetMaybe-    , tryGet-    , remaining-    , getWord8-    , getWords8-    , getWord16-    , getWords16-    , getWord24-    , getWord32-    , getBytes-    , getOpaque8-    , getOpaque16-    , getOpaque24-    , getInteger16-    , getBigNum16-    , getList-    , processBytes-    , isEmpty-    , Put-    , runPut-    , putWord8-    , putWords8-    , putWord16-    , putWords16-    , putWord24-    , putWord32-    , putBytes-    , putOpaque8-    , putOpaque16-    , putOpaque24-    , putInteger16-    , putBigNum16-    , encodeWord16-    , encodeWord32-    , encodeWord64-    ) where+-- | The Wire module is a specialized marshalling/unmarshalling+-- package related to the TLS protocol.  All multibytes values are+-- written as big endian.+module Network.TLS.Wire (+    Get,+    GetResult (..),+    GetContinuation,+    runGet,+    runGetErr,+    runGetMaybe,+    tryGet,+    remaining,+    getWord8,+    getWords8,+    getWord16,+    getWords16,+    getWord24,+    getWord32,+    getWord64,+    getBytes,+    getOpaque8,+    getOpaque16,+    getOpaque24,+    getInteger16,+    getBigNum16,+    getList,+    processBytes,+    isEmpty,+    Put,+    runPut,+    putWord8,+    putWords8,+    putWord16,+    putWords16,+    putWord24,+    putWord32,+    putWord64,+    putBytes,+    putOpaque8,+    putOpaque16,+    putOpaque24,+    putInteger16,+    putBigNum16,+    encodeWord16,+    encodeWord32,+    encodeWord64,+) where +import qualified Data.ByteString as B import Data.Serialize.Get hiding (runGet) import qualified Data.Serialize.Get as G import Data.Serialize.Put-import qualified Data.ByteString as B-import Network.TLS.Struct++import Network.TLS.Error import Network.TLS.Imports+import Network.TLS.Types import Network.TLS.Util.Serialization  type GetContinuation a = ByteString -> GetResult a-data GetResult a =-      GotError TLSError+data GetResult a+    = GotError TLSError     | GotPartial (GetContinuation a)     | GotSuccess a     | GotSuccessRemaining a ByteString  runGet :: String -> Get a -> ByteString -> GetResult a runGet lbl f = toGetResult <$> G.runGetPartial (label lbl f)-  where toGetResult (G.Fail err _)    = GotError (Error_Packet_Parsing err)-        toGetResult (G.Partial cont)  = GotPartial (toGetResult <$> cont)-        toGetResult (G.Done r bsLeft)-            | B.null bsLeft = GotSuccess r-            | otherwise     = GotSuccessRemaining r bsLeft+  where+    toGetResult (G.Fail err _) = GotError (Error_Packet_Parsing err)+    toGetResult (G.Partial cont) = GotPartial (toGetResult <$> cont)+    toGetResult (G.Done r bsLeft)+        | B.null bsLeft = GotSuccess r+        | otherwise = GotSuccessRemaining r bsLeft  runGetErr :: String -> Get a -> ByteString -> Either TLSError a runGetErr lbl getter b = toSimple $ runGet lbl getter b-  where toSimple (GotError err) = Left err-        toSimple (GotPartial _) = Left (Error_Packet_Parsing (lbl ++ ": parsing error: partial packet"))-        toSimple (GotSuccessRemaining _ _) = Left (Error_Packet_Parsing (lbl ++ ": parsing error: remaining bytes"))-        toSimple (GotSuccess r) = Right r+  where+    toSimple (GotError err) = Left err+    toSimple (GotPartial _) = Left (Error_Packet_Parsing (lbl ++ ": parsing error: partial packet"))+    toSimple (GotSuccessRemaining _ _) = Left (Error_Packet_Parsing (lbl ++ ": parsing error: remaining bytes"))+    toSimple (GotSuccess r) = Right r  runGetMaybe :: Get a -> ByteString -> Maybe a runGetMaybe f = either (const Nothing) Just . G.runGet f@@ -94,7 +93,10 @@ getWord16 = getWord16be  getWords16 :: Get [Word16]-getWords16 = getWord16 >>= \lenb -> replicateM (fromIntegral lenb `div` 2) getWord16+getWords16 = do+    lenb <- getWord16+    when (odd lenb) $ fail "length for ciphers must be even"+    replicateM (fromIntegral lenb `shiftR` 1) getWord16  getWord24 :: Get Int getWord24 = do@@ -106,6 +108,9 @@ getWord32 :: Get Word32 getWord32 = getWord32be +getWord64 :: Get Word64+getWord64 = getWord64be+ getOpaque8 :: Get ByteString getOpaque8 = getWord8 >>= getBytes . fromIntegral @@ -123,10 +128,13 @@  getList :: Int -> Get (Int, a) -> Get [a] getList totalLen getElement = isolate totalLen (getElements totalLen)-  where getElements len-            | len < 0     = error "list consumed too much data. should never happen with isolate."-            | len == 0    = return []-            | otherwise   = getElement >>= \(elementLen, a) -> (:) a <$> getElements (len - elementLen)+  where+    getElements len+        | len < 0 =+            error "list consumed too much data. should never happen with isolate."+        | len == 0 = return []+        | otherwise =+            getElement >>= \(elementLen, a) -> (:) a <$> getElements (len - elementLen)  processBytes :: Int -> Get a -> Get a processBytes i f = isolate i f@@ -142,6 +150,9 @@ putWord32 :: Word32 -> Put putWord32 = putWord32be +putWord64 :: Word64 -> Put+putWord64 = putWord64be+ putWords16 :: [Word16] -> Put putWords16 l = do     putWord16 $ 2 * fromIntegral (length l)@@ -152,7 +163,7 @@     let a = fromIntegral ((i `shiftR` 16) .&. 0xff)     let b = fromIntegral ((i `shiftR` 8) .&. 0xff)     let c = fromIntegral (i .&. 0xff)-    mapM_ putWord8 [a,b,c]+    mapM_ putWord8 [a, b, c]  putBytes :: ByteString -> Put putBytes = putByteString
Network/TLS/X509.hs view
@@ -1,59 +1,82 @@--- |--- Module      : Network.TLS.X509--- License     : BSD-style--- Maintainer  : Vincent Hanquez <vincent@snarc.org>--- Stability   : experimental--- Portability : unknown------ X509 helpers----module Network.TLS.X509-    ( CertificateChain(..)-    , Certificate(..)-    , SignedCertificate-    , getCertificate-    , isNullCertificateChain-    , getCertificateChainLeaf-    , CertificateRejectReason(..)-    , CertificateUsage(..)-    , CertificateStore-    , ValidationCache-    , exceptionValidationCache-    , validateDefault-    , FailedReason-    , ServiceID-    , wrapCertificateChecks-    ) where+-- | X509 helpers+module Network.TLS.X509 (+    CertificateChain (..),+    Certificate (..),+    SignedCertificate,+    getCertificate,+    isNullCertificateChain,+    getCertificateChainLeaf,+    CertificateRejectReason (..),+    CertificateUsage (..),+    CertificateStore,+    ValidationCache,+    defaultValidationCache,+    exceptionValidationCache,+    validateDefault,+    FailedReason,+    ServiceID,+    wrapCertificateChecks,+    pubkeyType,+    validateClientCertificate,+) where  import Data.X509-import Data.X509.Validation import Data.X509.CertificateStore+import Data.X509.Validation  isNullCertificateChain :: CertificateChain -> Bool isNullCertificateChain (CertificateChain l) = null l  getCertificateChainLeaf :: CertificateChain -> SignedExact Certificate-getCertificateChainLeaf (CertificateChain [])    = error "empty certificate chain"-getCertificateChainLeaf (CertificateChain (x:_)) = x+getCertificateChainLeaf (CertificateChain []) = error "empty certificate chain"+getCertificateChainLeaf (CertificateChain (x : _)) = x  -- | Certificate and Chain rejection reason-data CertificateRejectReason =-          CertificateRejectExpired-        | CertificateRejectRevoked-        | CertificateRejectUnknownCA-        | CertificateRejectOther String-        deriving (Show,Eq)+data CertificateRejectReason+    = CertificateRejectExpired+    | CertificateRejectRevoked+    | CertificateRejectUnknownCA+    | CertificateRejectAbsent+    | CertificateRejectOther String+    deriving (Show, Eq)  -- | Certificate Usage callback possible returns values.-data CertificateUsage =-          CertificateUsageAccept                         -- ^ usage of certificate accepted-        | CertificateUsageReject CertificateRejectReason -- ^ usage of certificate rejected-        deriving (Show,Eq)+data CertificateUsage+    = -- | usage of certificate accepted+      CertificateUsageAccept+    | -- | usage of certificate rejected+      CertificateUsageReject CertificateRejectReason+    deriving (Show, Eq)  wrapCertificateChecks :: [FailedReason] -> CertificateUsage wrapCertificateChecks [] = CertificateUsageAccept wrapCertificateChecks l-    | Expired `elem` l   = CertificateUsageReject   CertificateRejectExpired-    | InFuture `elem` l  = CertificateUsageReject   CertificateRejectExpired-    | UnknownCA `elem` l = CertificateUsageReject   CertificateRejectUnknownCA-    | otherwise          = CertificateUsageReject $ CertificateRejectOther (show l)+    | Expired `elem` l = CertificateUsageReject CertificateRejectExpired+    | InFuture `elem` l = CertificateUsageReject CertificateRejectExpired+    | UnknownCA `elem` l = CertificateUsageReject CertificateRejectUnknownCA+    | SelfSigned `elem` l = CertificateUsageReject CertificateRejectUnknownCA+    | EmptyChain `elem` l = CertificateUsageReject CertificateRejectAbsent+    | otherwise = CertificateUsageReject $ CertificateRejectOther (show l)++pubkeyType :: PubKey -> String+pubkeyType = show . pubkeyToAlg++-- | A utility function for client authentication which can be used+-- `onClientCertificate`.+--+-- Since: 2.1.7+validateClientCertificate+    :: CertificateStore+    -> ValidationCache+    -> CertificateChain+    -> IO CertificateUsage+validateClientCertificate store cache cc =+    wrapCertificateChecks+        <$> validate+            HashSHA256+            defaultHooks+            defaultChecks{checkFQHN = False}+            store+            cache+            ("", mempty)+            cc
Setup.hs view
@@ -1,2 +1,3 @@ import Distribution.Simple+ main = defaultMain
− Tests/Certificate.hs
@@ -1,104 +0,0 @@-{-# LANGUAGE BangPatterns #-}-{-# LANGUAGE OverloadedStrings #-}-{-# OPTIONS_GHC -fno-warn-orphans #-}-module Certificate-    ( arbitraryX509-    , arbitraryX509WithKey-    , simpleCertificate-    , simpleX509-    ) where--import Control.Applicative-import Test.Tasty.QuickCheck-import Data.ASN1.OID-import Data.X509-import Data.Hourglass-import qualified Data.ByteString as B--import PubKey--arbitraryDN :: Gen DistinguishedName-arbitraryDN = return $ DistinguishedName []--instance Arbitrary Date where-    arbitrary = do-        y <- choose (1971, 2035)-        m <- elements [ January .. December]-        d <- choose (1, 30)-        return $ normalizeDate $ Date y m d--normalizeDate :: Date -> Date-normalizeDate d = timeConvert (timeConvert d :: Elapsed)--instance Arbitrary TimeOfDay where-    arbitrary = do-        h    <- choose (0, 23)-        mi   <- choose (0, 59)-        se   <- choose (0, 59)-        nsec <- return 0-        return $ TimeOfDay (Hours h) (Minutes mi) (Seconds se) nsec--instance Arbitrary DateTime where-    arbitrary = DateTime <$> arbitrary <*> arbitrary--maxSerial :: Integer-maxSerial = 16777216--arbitraryCertificate :: PubKey -> Gen Certificate-arbitraryCertificate pubKey = do-    serial    <- choose (0,maxSerial)-    subjectdn <- arbitraryDN-    validity  <- (,) <$> arbitrary <*> arbitrary-    let sigalg = SignatureALG HashSHA1 (pubkeyToAlg pubKey)-    return $ Certificate-            { certVersion      = 3-            , certSerial       = serial-            , certSignatureAlg = sigalg-            , certIssuerDN     = issuerdn-            , certSubjectDN    = subjectdn-            , certValidity     = validity-            , certPubKey       = pubKey-            , certExtensions   = Extensions $ Just-                [ extensionEncode True $ ExtKeyUsage [KeyUsage_digitalSignature,KeyUsage_keyEncipherment,KeyUsage_keyCertSign]-                ]-            }-  where issuerdn = DistinguishedName [(getObjectID DnCommonName, "Root CA")]--simpleCertificate :: PubKey -> Certificate-simpleCertificate pubKey =-    Certificate-        { certVersion = 3-        , certSerial = 0-        , certSignatureAlg = SignatureALG HashSHA1 (pubkeyToAlg pubKey)-        , certIssuerDN     = simpleDN-        , certSubjectDN    = simpleDN-        , certValidity     = (time1, time2)-        , certPubKey       = pubKey-        , certExtensions   = Extensions $ Just-                [ extensionEncode True $ ExtKeyUsage [KeyUsage_digitalSignature,KeyUsage_keyEncipherment]-                ]-        }-  where time1 = DateTime (Date 1999 January 1) (TimeOfDay 0 0 0 0)-        time2 = DateTime (Date 2049 January 1) (TimeOfDay 0 0 0 0)-        simpleDN = DistinguishedName []--simpleX509 :: PubKey -> SignedCertificate-simpleX509 pubKey = do-    let cert = simpleCertificate pubKey-        sig  = replicate 40 1-        sigalg = SignatureALG HashSHA1 (pubkeyToAlg pubKey)-        (signedExact, ()) = objectToSignedExact (\_ -> (B.pack sig,sigalg,())) cert-     in signedExact--arbitraryX509WithKey :: (PubKey, t) -> Gen SignedCertificate-arbitraryX509WithKey (pubKey, _) = do-    cert <- arbitraryCertificate pubKey-    sig  <- resize 40 $ listOf1 arbitrary-    let sigalg = SignatureALG HashSHA1 (pubkeyToAlg pubKey)-    let (signedExact, ()) = objectToSignedExact (\(!(_)) -> (B.pack sig,sigalg,())) cert-    return signedExact--arbitraryX509 :: Gen SignedCertificate-arbitraryX509 = do-    let (pubKey, privKey) = getGlobalRSAPair-    arbitraryX509WithKey (PubKeyRSA pubKey, PrivKeyRSA privKey)
− Tests/Ciphers.hs
@@ -1,50 +0,0 @@--- Disable this warning so we can still test deprecated functionality.-{-# OPTIONS_GHC -fno-warn-warnings-deprecations #-}-module Ciphers-    ( propertyBulkFunctional-    ) where--import Control.Applicative ((<$>), (<*>))--import Test.Tasty.QuickCheck--import qualified Data.ByteString as B-import Network.TLS.Cipher-import Network.TLS.Extra.Cipher--arbitraryKey :: Bulk -> Gen B.ByteString-arbitraryKey bulk = B.pack `fmap` vector (bulkKeySize bulk)--arbitraryIV :: Bulk -> Gen B.ByteString-arbitraryIV bulk = B.pack `fmap` vector (bulkIVSize bulk + bulkExplicitIV bulk)--arbitraryText :: Bulk -> Gen B.ByteString-arbitraryText bulk = B.pack `fmap` vector (bulkBlockSize bulk)--data BulkTest = BulkTest Bulk B.ByteString B.ByteString B.ByteString B.ByteString-    deriving (Show,Eq)--instance Arbitrary BulkTest where-    arbitrary = do-        bulk <- cipherBulk `fmap` elements ciphersuite_all-        BulkTest bulk <$> arbitraryKey bulk <*> arbitraryIV bulk <*> arbitraryText bulk <*> arbitraryText bulk--propertyBulkFunctional :: BulkTest -> Bool-propertyBulkFunctional (BulkTest bulk key iv t additional) =-    let enc = bulkInit bulk BulkEncrypt key-        dec = bulkInit bulk BulkDecrypt key-     in case (enc, dec) of-        (BulkStateBlock encF, BulkStateBlock decF)   -> block encF decF-        (BulkStateAEAD encF, BulkStateAEAD decF)     -> aead encF decF-        (BulkStateStream (BulkStream encF), BulkStateStream (BulkStream decF)) -> stream encF decF-        _                                            -> True-  where-        block e d =-            let (etxt, e_iv) = e iv t-                (dtxt, d_iv) = d iv etxt-             in dtxt == t && d_iv == e_iv-        stream e d = (fst . d . fst . e) t == t-        aead e d =-            let (encrypted, at)  = e iv t additional-                (decrypted, at2) = d iv encrypted additional-             in decrypted == t && at == at2
− Tests/Connection.hs
@@ -1,269 +0,0 @@--- Disable this warning so we can still test deprecated functionality.-{-# OPTIONS_GHC -fno-warn-warnings-deprecations #-}-module Connection-    ( newPairContext-    , arbitraryCiphers-    , arbitraryVersions-    , arbitraryHashSignatures-    , arbitraryGroups-    , arbitraryPairParams-    , arbitraryPairParamsWithVersionsAndCiphers-    , arbitraryClientCredential-    , isCustomDHParams-    , leafPublicKey-    , oneSessionManager-    , setPairParamsSessionManager-    , setPairParamsSessionResuming-    , establishDataPipe-    , initiateDataPipe-    ) where--import Test.Tasty.QuickCheck-import Certificate-import PubKey-import PipeChan-import Network.TLS as TLS-import Network.TLS.Extra-import Data.X509-import Data.Default.Class-import Data.IORef-import Control.Applicative-import Control.Concurrent.Chan-import Control.Concurrent-import qualified Control.Exception as E-import Data.List (isInfixOf)--import qualified Data.ByteString as B--debug :: Bool-debug = False--knownCiphers :: [Cipher]-knownCiphers = filter nonECDSA (ciphersuite_all ++ ciphersuite_weak)-  where-    ciphersuite_weak = [-        cipher_DHE_DSS_RC4_SHA1-      , cipher_RC4_128_MD5-      , cipher_null_MD5-      , cipher_null_SHA1-      ]-    -- arbitraryCredentialsOfEachType cannot generate ECDSA-    nonECDSA c = not ("ECDSA" `isInfixOf` cipherName c)--arbitraryCiphers :: Gen [Cipher]-arbitraryCiphers = listOf1 $ elements knownCiphers--knownVersions :: [Version]-knownVersions = [SSL3,TLS10,TLS11,TLS12]--arbitraryVersions :: Gen [Version]-arbitraryVersions = sublistOf knownVersions--knownHashSignatures :: [HashAndSignatureAlgorithm]-knownHashSignatures = filter nonECDSA availableHashSignatures-  where-    availableHashSignatures = [(TLS.HashIntrinsic, SignatureRSApssSHA256)-                              ,(TLS.HashSHA512, SignatureRSA)-                              ,(TLS.HashSHA512, SignatureECDSA)-                              ,(TLS.HashSHA384, SignatureRSA)-                              ,(TLS.HashSHA384, SignatureECDSA)-                              ,(TLS.HashSHA256, SignatureRSA)-                              ,(TLS.HashSHA256, SignatureECDSA)-                              ,(TLS.HashSHA1,   SignatureRSA)-                              ,(TLS.HashSHA1,   SignatureDSS)-                              ]-    -- arbitraryCredentialsOfEachType cannot generate ECDSA-    nonECDSA (_,s) = s /= SignatureECDSA--arbitraryHashSignatures :: Gen [HashAndSignatureAlgorithm]-arbitraryHashSignatures = sublistOf knownHashSignatures--knownGroups, knownECGroups, knownFFGroups :: [Group]-knownECGroups = [P256,P384,P521,X25519,X448]-knownFFGroups = [FFDHE2048,FFDHE3072,FFDHE4096,FFDHE6144,FFDHE8192]-knownGroups   = knownECGroups ++ knownFFGroups--arbitraryGroups :: Gen [Group]-arbitraryGroups = listOf1 $ elements knownGroups--arbitraryCredentialsOfEachType :: Gen [(CertificateChain, PrivKey)]-arbitraryCredentialsOfEachType = do-    let (pubKey, privKey) = getGlobalRSAPair-    (dsaPub, dsaPriv) <- arbitraryDSAPair-    mapM (\(pub, priv) -> do-              cert <- arbitraryX509WithKey (pub, priv)-              return (CertificateChain [cert], priv)-         ) [ (PubKeyRSA pubKey, PrivKeyRSA privKey)-           , (PubKeyDSA dsaPub, PrivKeyDSA dsaPriv)-           ]--isCustomDHParams :: DHParams -> Bool-isCustomDHParams params = params == dhParams--leafPublicKey :: CertificateChain -> Maybe PubKey-leafPublicKey (CertificateChain [])       = Nothing-leafPublicKey (CertificateChain (leaf:_)) = Just (certPubKey $ signedObject $ getSigned leaf)--arbitraryCipherPair :: Version -> Gen ([Cipher], [Cipher])-arbitraryCipherPair connectVersion = do-    serverCiphers      <- arbitraryCiphers `suchThat`-                                (\cs -> or [maybe True (<= connectVersion) (cipherMinVer x) | x <- cs])-    clientCiphers      <- arbitraryCiphers `suchThat`-                                (\cs -> or [x `elem` serverCiphers &&-                                            maybe True (<= connectVersion) (cipherMinVer x) | x <- cs])-    return (clientCiphers, serverCiphers)--arbitraryPairParams :: Gen (ClientParams, ServerParams)-arbitraryPairParams = do-    connectVersion <- elements knownVersions-    (clientCiphers, serverCiphers) <- arbitraryCipherPair connectVersion-    -- The shared ciphers may set a floor on the compatible protocol versions-    let allowedVersions = [ v | v <- knownVersions,-                                or [ x `elem` serverCiphers &&-                                     maybe True (<= v) (cipherMinVer x) | x <- clientCiphers ]]-    serAllowedVersions <- (:[]) `fmap` elements allowedVersions-    arbitraryPairParamsWithVersionsAndCiphers (allowedVersions, serAllowedVersions) (clientCiphers, serverCiphers)--arbitraryECGroupPair :: Gen ([Group], [Group])-arbitraryECGroupPair = do-    let arbitraryECGroups = listOf1 $ elements knownECGroups-    serverGroups <- arbitraryECGroups-    clientGroups <- arbitraryECGroups `suchThat` any (`elem` serverGroups)-    return (clientGroups, serverGroups)--arbitraryHashSignaturePair :: Gen ([HashAndSignatureAlgorithm], [HashAndSignatureAlgorithm])-arbitraryHashSignaturePair = do-    serverHashSignatures <- shuffle knownHashSignatures-    clientHashSignatures <- shuffle knownHashSignatures-    return (clientHashSignatures, serverHashSignatures)--arbitraryPairParamsWithVersionsAndCiphers :: ([Version], [Version])-                                          -> ([Cipher], [Cipher])-                                          -> Gen (ClientParams, ServerParams)-arbitraryPairParamsWithVersionsAndCiphers (clientVersions, serverVersions) (clientCiphers, serverCiphers) = do-    secNeg             <- arbitrary-    dhparams           <- elements [dhParams,ffdhe2048,ffdhe3072]--    creds              <- arbitraryCredentialsOfEachType-    (clientGroups, serverGroups) <- arbitraryECGroupPair-    (clientHashSignatures, serverHashSignatures) <- arbitraryHashSignaturePair-    let serverState = def-            { serverSupported = def { supportedCiphers  = serverCiphers-                                    , supportedVersions = serverVersions-                                    , supportedSecureRenegotiation = secNeg-                                    , supportedGroups   = serverGroups-                                    , supportedHashSignatures = serverHashSignatures-                                    }-            , serverDHEParams = Just dhparams-            , serverShared = def { sharedCredentials = Credentials creds }-            }-    let clientState = (defaultParamsClient "" B.empty)-            { clientSupported = def { supportedCiphers  = clientCiphers-                                    , supportedVersions = clientVersions-                                    , supportedSecureRenegotiation = secNeg-                                    , supportedGroups   = clientGroups-                                    , supportedHashSignatures = clientHashSignatures-                                    }-            , clientShared = def { sharedValidationCache = ValidationCache-                                        { cacheAdd = \_ _ _ -> return ()-                                        , cacheQuery = \_ _ _ -> return ValidationCachePass-                                        }-                                }-            }-    return (clientState, serverState)--arbitraryClientCredential :: Gen Credential-arbitraryClientCredential = arbitraryCredentialsOfEachType >>= elements---- | simple session manager to store one session id and session data for a single thread.--- a Real concurrent session manager would use an MVar and have multiples items.-oneSessionManager :: IORef (Maybe (SessionID, SessionData)) -> SessionManager-oneSessionManager ref = SessionManager-    { sessionResume     = \myId     -> (>>= maybeResume myId) <$> readIORef ref-    , sessionEstablish  = \myId dat -> writeIORef ref $ Just (myId, dat)-    , sessionInvalidate = \_        -> return ()-    }-  where-    maybeResume myId (sid, sdata)-        | sid == myId = Just sdata-        | otherwise   = Nothing--setPairParamsSessionManager :: SessionManager -> (ClientParams, ServerParams) -> (ClientParams, ServerParams)-setPairParamsSessionManager manager (clientState, serverState) = (nc,ns)-  where nc = clientState { clientShared = updateSessionManager $ clientShared clientState }-        ns = serverState { serverShared = updateSessionManager $ serverShared serverState }-        updateSessionManager shared = shared { sharedSessionManager = manager }--setPairParamsSessionResuming :: (SessionID, SessionData) -> (ClientParams, ServerParams) -> (ClientParams, ServerParams)-setPairParamsSessionResuming sessionStuff (clientState, serverState) =-    ( clientState { clientWantSessionResume = Just sessionStuff }-    , serverState)--newPairContext :: PipeChan -> (ClientParams, ServerParams) -> IO (Context, Context)-newPairContext pipe (cParams, sParams) = do-    let noFlush = return ()-    let noClose = return ()--    let cBackend = Backend noFlush noClose (writePipeA pipe) (readPipeA pipe)-    let sBackend = Backend noFlush noClose (writePipeB pipe) (readPipeB pipe)-    cCtx' <- contextNew cBackend cParams-    sCtx' <- contextNew sBackend sParams--    contextHookSetLogging cCtx' (logging "client: ")-    contextHookSetLogging sCtx' (logging "server: ")--    return (cCtx', sCtx')-  where-        logging pre =-            if debug-                then def { loggingPacketSent = putStrLn . ((pre ++ ">> ") ++)-                                    , loggingPacketRecv = putStrLn . ((pre ++ "<< ") ++) }-                else def--establishDataPipe :: (ClientParams, ServerParams) -> (Context -> Chan result -> IO ()) -> (Chan start -> Context -> IO ()) -> IO (Chan start, Chan result)-establishDataPipe params tlsServer tlsClient = do-    -- initial setup-    pipe        <- newPipe-    _           <- (runPipe pipe)-    startQueue  <- newChan-    resultQueue <- newChan--    (cCtx, sCtx) <- newPairContext pipe params--    _ <- forkIO $ E.catch (tlsServer sCtx resultQueue)-                          (printAndRaise "server" (serverSupported $ snd params))-    _ <- forkIO $ E.catch (tlsClient startQueue cCtx)-                          (printAndRaise "client" (clientSupported $ fst params))--    return (startQueue, resultQueue)-  where-        printAndRaise :: String -> Supported -> E.SomeException -> IO ()-        printAndRaise s supported e = do-            putStrLn $ s ++ " exception: " ++ show e ++-                           ", supported: " ++ show supported-            E.throw e--initiateDataPipe :: (ClientParams, ServerParams) -> (Context -> IO a1) -> (Context -> IO a) -> IO (Either E.SomeException a, Either E.SomeException a1)-initiateDataPipe params tlsServer tlsClient = do-    -- initial setup-    pipe        <- newPipe-    _           <- (runPipe pipe)-    cQueue      <- newChan-    sQueue      <- newChan--    (cCtx, sCtx) <- newPairContext pipe params--    _ <- forkIO $ E.catch (tlsServer sCtx >>= writeSuccess sQueue)-                          (writeException sQueue)-    _ <- forkIO $ E.catch (tlsClient cCtx >>= writeSuccess cQueue)-                          (writeException cQueue)--    sRes <- readChan sQueue-    cRes <- readChan cQueue-    return (cRes, sRes)-  where-        writeException :: Chan (Either E.SomeException a) -> E.SomeException -> IO ()-        writeException queue e = writeChan queue (Left e)--        writeSuccess :: Chan (Either E.SomeException a) -> a -> IO ()-        writeSuccess queue res = writeChan queue (Right res)
− Tests/Marshalling.hs
@@ -1,103 +0,0 @@-{-# LANGUAGE CPP #-}-{-# OPTIONS_GHC -fno-warn-orphans #-}-module Marshalling where--import Control.Monad-import Control.Applicative-import Test.Tasty.QuickCheck-import Network.TLS.Internal-import Network.TLS--import qualified Data.ByteString as B-import Data.Word-import Data.X509-import Certificate--genByteString :: Int -> Gen B.ByteString-genByteString i = B.pack <$> vector i--instance Arbitrary Version where-    arbitrary = elements [ SSL2, SSL3, TLS10, TLS11, TLS12 ]--instance Arbitrary ProtocolType where-    arbitrary = elements-            [ ProtocolType_ChangeCipherSpec-            , ProtocolType_Alert-            , ProtocolType_Handshake-            , ProtocolType_AppData ]--instance Arbitrary Header where-    arbitrary = Header <$> arbitrary <*> arbitrary <*> arbitrary--instance Arbitrary ClientRandom where-    arbitrary = ClientRandom <$> (genByteString 32)--instance Arbitrary ServerRandom where-    arbitrary = ServerRandom <$> (genByteString 32)--instance Arbitrary Session where-    arbitrary = do-        i <- choose (1,2) :: Gen Int-        case i of-            2 -> liftM (Session . Just) (genByteString 32)-            _ -> return $ Session Nothing--instance Arbitrary DigitallySigned where-    arbitrary = DigitallySigned Nothing <$> genByteString 32--arbitraryCiphersIDs :: Gen [Word16]-arbitraryCiphersIDs = choose (0,200) >>= vector--arbitraryCompressionIDs :: Gen [Word8]-arbitraryCompressionIDs = choose (0,200) >>= vector--someWords8 :: Int -> Gen [Word8]-someWords8 i = replicateM i (fromIntegral <$> (choose (0,255) :: Gen Int))--instance Arbitrary CertificateType where-    arbitrary = elements-            [ CertificateType_RSA_Sign, CertificateType_DSS_Sign-            , CertificateType_RSA_Fixed_DH, CertificateType_DSS_Fixed_DH-            , CertificateType_RSA_Ephemeral_DH, CertificateType_DSS_Ephemeral_DH-            , CertificateType_fortezza_dms ]--instance Arbitrary Handshake where-    arbitrary = oneof-            [ ClientHello-                <$> arbitrary-                <*> arbitrary-                <*> arbitrary-                <*> arbitraryCiphersIDs-                <*> arbitraryCompressionIDs-                <*> (return [])-                <*> (return Nothing)-            , ServerHello-                <$> arbitrary-                <*> arbitrary-                <*> arbitrary-                <*> arbitrary-                <*> arbitrary-                <*> (return [])-            , liftM Certificates (CertificateChain <$> (resize 2 $ listOf $ arbitraryX509))-            , pure HelloRequest-            , pure ServerHelloDone-            , ClientKeyXchg . CKX_RSA <$> genByteString 48-            --, liftM  ServerKeyXchg-            , liftM3 CertRequest arbitrary (return Nothing) (return [])-            , CertVerify <$> arbitrary-            , Finished <$> (genByteString 12)-            ]--{- quickcheck property -}--prop_header_marshalling_id :: Header -> Bool-prop_header_marshalling_id x = (decodeHeader $ encodeHeader x) == Right x--prop_handshake_marshalling_id :: Handshake -> Bool-prop_handshake_marshalling_id x = (decodeHs $ encodeHandshake x) == Right x-  where decodeHs b = case decodeHandshakeRecord b of-                        GotPartial _ -> error "got partial"-                        GotError e   -> error ("got error: " ++ show e)-                        GotSuccessRemaining _ _ -> error "got remaining byte left"-                        GotSuccess (ty, content) -> decodeHandshake cp ty content-        cp = CurrentParams { cParamsVersion = TLS10, cParamsKeyXchgType = Just CipherKeyExchange_RSA }
− Tests/PipeChan.hs
@@ -1,70 +0,0 @@--- create a similar concept than a unix pipe.-module PipeChan-    ( PipeChan(..)-    , newPipe-    , runPipe-    , readPipeA-    , readPipeB-    , writePipeA-    , writePipeB-    ) where--import Control.Applicative-import Control.Concurrent.Chan-import Control.Concurrent-import Control.Monad (forever)-import Data.ByteString (ByteString)-import Data.IORef-import qualified Data.ByteString as B---- | represent a unidirectional pipe with a buffered read channel and a write channel-data UniPipeChan = UniPipeChan (Chan ByteString) (Chan ByteString)--newUniPipeChan :: IO UniPipeChan-newUniPipeChan = UniPipeChan <$> newChan <*> newChan--runUniPipe :: UniPipeChan -> IO ThreadId-runUniPipe (UniPipeChan r w) = forkIO $ forever $ readChan r >>= writeChan w--getReadUniPipe :: UniPipeChan -> Chan ByteString-getReadUniPipe (UniPipeChan r _)  = r--getWriteUniPipe :: UniPipeChan -> Chan ByteString-getWriteUniPipe (UniPipeChan _ w) = w---- | Represent a bidirectional pipe with 2 nodes A and B-data PipeChan = PipeChan (IORef ByteString) (IORef ByteString) UniPipeChan UniPipeChan--newPipe :: IO PipeChan-newPipe = PipeChan <$> newIORef B.empty <*> newIORef B.empty <*> newUniPipeChan <*> newUniPipeChan--runPipe :: PipeChan -> IO ThreadId-runPipe (PipeChan _ _ cToS sToC) = runUniPipe cToS >> runUniPipe sToC--readPipeA :: PipeChan -> Int -> IO ByteString-readPipeA (PipeChan _ b _ s) sz = readBuffered b (getWriteUniPipe s) sz--writePipeA :: PipeChan -> ByteString -> IO ()-writePipeA (PipeChan _ _ c _)   = writeChan $ getWriteUniPipe c--readPipeB :: PipeChan -> Int -> IO ByteString-readPipeB (PipeChan b _ c _) sz = readBuffered b (getWriteUniPipe c) sz--writePipeB :: PipeChan -> ByteString -> IO ()-writePipeB (PipeChan _ _ _ s)   = writeChan $ getReadUniPipe s---- helper to read buffered data.-readBuffered :: IORef ByteString -> Chan ByteString -> Int -> IO ByteString-readBuffered buf chan sz = do-    left <- readIORef buf-    if B.length left >= sz-        then do-            let (ret, nleft) = B.splitAt sz left-            writeIORef buf nleft-            return ret-        else do-            let newSize = (sz - B.length left)-            newData <- readChan chan-            writeIORef buf newData-            remain <- readBuffered buf chan newSize-            return (left `B.append` remain)
− Tests/PubKey.hs
@@ -1,73 +0,0 @@-module PubKey-    ( arbitraryRSAPair-    , arbitraryDSAPair-    , globalRSAPair-    , getGlobalRSAPair-    , dhParams-    , dsaParams-    , rsaParams-    ) where--import Test.Tasty.QuickCheck--import qualified Crypto.PubKey.DH as DH-import Crypto.Random-import qualified Crypto.PubKey.RSA as RSA-import qualified Crypto.PubKey.DSA as DSA--import Control.Concurrent.MVar-import System.IO.Unsafe--arbitraryRSAPair :: Gen (RSA.PublicKey, RSA.PrivateKey)-arbitraryRSAPair = (rngToRSA . drgNewTest) `fmap` arbitrary-  where-    rngToRSA :: ChaChaDRG -> (RSA.PublicKey, RSA.PrivateKey)-    rngToRSA rng = fst $ withDRG rng arbitraryRSAPairWithRNG--arbitraryRSAPairWithRNG :: MonadRandom m => m (RSA.PublicKey, RSA.PrivateKey)-arbitraryRSAPairWithRNG = RSA.generate 256 0x10001--{-# NOINLINE globalRSAPair #-}-globalRSAPair :: MVar (RSA.PublicKey, RSA.PrivateKey)-globalRSAPair = unsafePerformIO $ do-    drg <- drgNew-    newMVar (fst $ withDRG drg arbitraryRSAPairWithRNG)--{-# NOINLINE getGlobalRSAPair #-}-getGlobalRSAPair :: (RSA.PublicKey, RSA.PrivateKey)-getGlobalRSAPair = unsafePerformIO (readMVar globalRSAPair)--rsaParams :: (RSA.PublicKey, RSA.PrivateKey)-rsaParams = (pub, priv)- where priv = RSA.PrivateKey { RSA.private_pub  = pub-                             , RSA.private_d    = d-                             , RSA.private_p    = 0-                             , RSA.private_q    = 0-                             , RSA.private_dP   = 0-                             , RSA.private_dQ   = 0-                             , RSA.private_qinv = 0-                             }-       pub = RSA.PublicKey { RSA.public_size = (1024 `div` 8), RSA.public_n = n, RSA.public_e = e }-       n = 0x00c086b4c6db28ae578d73766d6fdd04b913808a85bf9ad7bcfc9a6ff04d13d2ff75f761ce7db9ee8996e29dc433d19a2d3f748e8d368ba099781d58276e1863a324ae3fb1a061874cd9f3510e54e49727c68de0616964335371cfb63f15ebff8ce8df09c74fb8625f8f58548b90f079a3405f522e738e664d0c645b015664f7c7-       e = 0x10001-       d = 0x3edc3cae28e4717818b1385ba7088d0038c3e176a606d2a5dbfc38cc46fe500824e62ec312fde04a803f61afac13a5b95c5c9c26b346879b54429083df488b4f29bb7b9722d366d6f5d2b512150a2e950eacfe0fd9dd56b87b0322f74ae3c8d8674ace62bc723f7c05e9295561efd70d7a924c6abac2e482880fc0149d5ad481--dhParams :: DH.Params-dhParams = DH.Params-    { DH.params_p = 0x00ccaa3884b50789ebea8d39bef8bbc66e20f2a78f537a76f26b4edde5de8b0ff15a8193abf0873cbdc701323a2bf6e860affa6e043fe8300d47e95baf9f6354cb-    , DH.params_g = 0x2-    , DH.params_bits = 512-    }--dsaParams :: DSA.Params-dsaParams = DSA.Params-    { DSA.params_p = 0x009f356bbc4750645555b02aa3918e85d5e35bdccd56154bfaa3e1801d5fe0faf65355215148ea866d5732fd27eb2f4d222c975767d2eb573513e460eceae327c8ac5da1f4ce765c49a39cae4c904b4e5cc64554d97148f20a2655027a0cf8f70b2550cc1f0c9861ce3a316520ab0588407ea3189d20c78bd52df97e56cbe0bbeb-    , DSA.params_q = 0x00f33a57b47de86ff836f9fe0bb060c54ab293133b-    , DSA.params_g = 0x3bb973c4f6eee92d1530f250487735595d778c2e5c8147d67a46ebcba4e6444350d49da8e7da667f9b1dbb22d2108870b9fcfabc353cdfac5218d829f22f69130317cc3b0d724881e34c34b8a2571d411da6458ef4c718df9e826f73e16a035b1dcbc1c62cac7a6604adb3e7930be8257944c6dfdddd655004b98253185775ff-    }--arbitraryDSAPair :: Gen (DSA.PublicKey, DSA.PrivateKey)-arbitraryDSAPair = do-    priv <- choose (1, DSA.params_q dsaParams)-    let pub = DSA.calculatePublic dsaParams priv-    return (DSA.PublicKey dsaParams pub, DSA.PrivateKey dsaParams priv)
− Tests/Tests.hs
@@ -1,339 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE OverloadedStrings #-}--import Test.Tasty-import Test.Tasty.QuickCheck-import Test.QuickCheck.Monadic--import PipeChan-import Connection-import Marshalling-import Ciphers--import Data.Maybe-import Data.List (intersect)--import qualified Data.ByteString as B-import qualified Data.ByteString.Char8 as C8-import qualified Data.ByteString.Lazy as L-import Network.TLS-import Network.TLS.Extra-import Control.Applicative-import Control.Concurrent-import Control.Monad--import Data.IORef--import System.Timeout--prop_pipe_work :: PropertyM IO ()-prop_pipe_work = do-    pipe <- run newPipe-    _ <- run (runPipe pipe)--    let bSize = 16-    n <- pick (choose (1, 32))--    let d1 = B.replicate (bSize * n) 40-    let d2 = B.replicate (bSize * n) 45--    d1' <- run (writePipeA pipe d1 >> readPipeB pipe (B.length d1))-    d1 `assertEq` d1'--    d2' <- run (writePipeB pipe d2 >> readPipeA pipe (B.length d2))-    d2 `assertEq` d2'--    return ()--recvDataNonNull :: Context -> IO C8.ByteString-recvDataNonNull ctx = recvData ctx >>= \l -> if B.null l then recvDataNonNull ctx else return l--runTLSPipe :: (ClientParams, ServerParams) -> (Context -> Chan C8.ByteString -> IO ()) -> (Chan C8.ByteString -> Context -> IO ()) -> PropertyM IO ()-runTLSPipe params tlsServer tlsClient = do-    (startQueue, resultQueue) <- run (establishDataPipe params tlsServer tlsClient)-    -- send some data-    d <- B.pack <$> pick (someWords8 256)-    run $ writeChan startQueue d-    -- receive it-    dres <- run $ timeout 10000000 $ readChan resultQueue-    -- check if it equal-    Just d `assertEq` dres-    return ()--runTLSPipeSimple :: (ClientParams, ServerParams) -> PropertyM IO ()-runTLSPipeSimple params = runTLSPipe params tlsServer tlsClient-  where tlsServer ctx queue = do-            handshake ctx-            d <- recvDataNonNull ctx-            writeChan queue d-            return ()-        tlsClient queue ctx = do-            handshake ctx-            d <- readChan queue-            sendData ctx (L.fromChunks [d])-            bye ctx-            return ()--runTLSInitFailure :: (ClientParams, ServerParams) -> PropertyM IO ()-runTLSInitFailure params = do-    (cRes, sRes) <- run (initiateDataPipe params tlsServer tlsClient)-    assertIsLeft cRes-    assertIsLeft sRes-  where tlsServer ctx = handshake ctx >> bye ctx >> return ("server success" :: String)-        tlsClient ctx = handshake ctx >> bye ctx >> return ("client success" :: String)--prop_handshake_initiate :: PropertyM IO ()-prop_handshake_initiate = do-    params  <- pick arbitraryPairParams-    runTLSPipeSimple params--prop_handshake_ciphersuites :: PropertyM IO ()-prop_handshake_ciphersuites = do-    let clientVersions = [TLS12]-        serverVersions = [TLS12]-    clientCiphers <- pick arbitraryCiphers-    serverCiphers <- pick arbitraryCiphers-    (clientParam,serverParam) <- pick $ arbitraryPairParamsWithVersionsAndCiphers-                                            (clientVersions, serverVersions)-                                            (clientCiphers, serverCiphers)-    let shouldFail = null (clientCiphers `intersect` serverCiphers)-    if shouldFail-        then runTLSInitFailure (clientParam,serverParam)-        else runTLSPipeSimple  (clientParam,serverParam)--prop_handshake_hashsignatures :: PropertyM IO ()-prop_handshake_hashsignatures = do-    let clientVersions = [TLS12]-        serverVersions = [TLS12]-        ciphers = [ cipher_ECDHE_RSA_AES256GCM_SHA384-                  , cipher_ECDHE_RSA_AES128CBC_SHA-                  , cipher_DHE_RSA_AES128_SHA1-                  , cipher_DHE_DSS_AES128_SHA1-                  ]-    (clientParam,serverParam) <- pick $ arbitraryPairParamsWithVersionsAndCiphers-                                            (clientVersions, serverVersions)-                                            (ciphers, ciphers)-    clientHashSigs <- pick arbitraryHashSignatures-    serverHashSigs <- pick arbitraryHashSignatures-    let clientParam' = clientParam { clientSupported = (clientSupported clientParam)-                                       { supportedHashSignatures = clientHashSigs }-                                   }-        serverParam' = serverParam { serverSupported = (serverSupported serverParam)-                                       { supportedHashSignatures = serverHashSigs }-                                   }-        shouldFail = null (clientHashSigs `intersect` serverHashSigs)-    if shouldFail-        then runTLSInitFailure (clientParam',serverParam')-        else runTLSPipeSimple  (clientParam',serverParam')---- Tests ability to use or ignore client "signature_algorithms" extension when--- choosing a server certificate.  Here peers allow DHE_RSA_AES128_SHA1 but--- the server RSA certificate has a SHA-1 signature that the client does not--- support.  Server may choose the DSA certificate only when cipher--- DHE_DSS_AES128_SHA1 is allowed.  Otherwise it must fallback to the RSA--- certificate.-prop_handshake_cert_fallback :: PropertyM IO ()-prop_handshake_cert_fallback = do-    let clientVersions = [TLS12]-        serverVersions = [TLS12]-        commonCiphers  = [ cipher_DHE_RSA_AES128_SHA1 ]-        otherCiphers   = [ cipher_ECDHE_RSA_AES256GCM_SHA384-                         , cipher_ECDHE_RSA_AES128CBC_SHA-                         , cipher_DHE_DSS_AES128_SHA1-                         ]-        hashSignatures = [ (HashSHA256, SignatureRSA), (HashSHA1, SignatureDSS) ]-    chainRef <- run $ newIORef Nothing-    clientCiphers <- pick $ sublistOf otherCiphers-    serverCiphers <- pick $ sublistOf otherCiphers-    (clientParam,serverParam) <- pick $ arbitraryPairParamsWithVersionsAndCiphers-                                            (clientVersions, serverVersions)-                                            (clientCiphers ++ commonCiphers, serverCiphers ++ commonCiphers)-    let clientParam' = clientParam { clientSupported = (clientSupported clientParam)-                                       { supportedHashSignatures = hashSignatures }-                                   , clientHooks = (clientHooks clientParam)-                                       { onServerCertificate = \_ _ _ chain ->-                                             writeIORef chainRef (Just chain) >> return [] }-                                   }-        dssDisallowed = cipher_DHE_DSS_AES128_SHA1 `notElem` clientCiphers-                            || cipher_DHE_DSS_AES128_SHA1 `notElem` serverCiphers-    runTLSPipeSimple (clientParam',serverParam)-    serverChain <- run $ readIORef chainRef-    dssDisallowed `assertEq` isLeafRSA serverChain-  where-    isLeafRSA chain = case chain >>= leafPublicKey of-                          Just (PubKeyRSA _) -> True-                          _                  -> False--prop_handshake_groups :: PropertyM IO ()-prop_handshake_groups = do-    let clientVersions = [TLS12]-        serverVersions = [TLS12]-        ciphers = [ cipher_ECDHE_RSA_AES256GCM_SHA384-                  , cipher_ECDHE_RSA_AES128CBC_SHA-                  , cipher_DHE_RSA_AES256GCM_SHA384-                  , cipher_DHE_RSA_AES128_SHA1-                  ]-    (clientParam,serverParam) <- pick $ arbitraryPairParamsWithVersionsAndCiphers-                                            (clientVersions, serverVersions)-                                            (ciphers, ciphers)-    clientGroups <- pick arbitraryGroups-    serverGroups <- pick arbitraryGroups-    denyCustom   <- pick arbitrary-    let groupUsage = if denyCustom then GroupUsageUnsupported "custom group denied" else GroupUsageValid-        clientParam' = clientParam { clientSupported = (clientSupported clientParam)-                                       { supportedGroups = clientGroups }-                                   , clientHooks = (clientHooks clientParam)-                                       { onCustomFFDHEGroup = \_ _ -> return groupUsage }-                                   }-        serverParam' = serverParam { serverSupported = (serverSupported serverParam)-                                       { supportedGroups = serverGroups }-                                   }-        isCustom = maybe True isCustomDHParams (serverDHEParams serverParam')-        shouldFail = null (clientGroups `intersect` serverGroups) && isCustom && denyCustom-    if shouldFail-        then runTLSInitFailure (clientParam',serverParam')-        else runTLSPipeSimple  (clientParam',serverParam')--prop_handshake_client_auth :: PropertyM IO ()-prop_handshake_client_auth = do-    (clientParam,serverParam) <- pick arbitraryPairParams-    cred <- pick arbitraryClientCredential-    let clientParam' = clientParam { clientHooks = (clientHooks clientParam)-                                       { onCertificateRequest = \_ -> return $ Just cred }-                                   }-        serverParam' = serverParam { serverWantClientCert = True-                                   , serverHooks = (serverHooks serverParam)-                                        { onClientCertificate = validateChain cred }-                                   }-    runTLSPipeSimple (clientParam',serverParam')-  where validateChain cred chain-            | chain == fst cred = return CertificateUsageAccept-            | otherwise         = return (CertificateUsageReject CertificateRejectUnknownCA)--prop_handshake_alpn :: PropertyM IO ()-prop_handshake_alpn = do-    (clientParam,serverParam) <- pick arbitraryPairParams-    let clientParam' = clientParam { clientHooks = (clientHooks clientParam)-                                       { onSuggestALPN = return $ Just ["h2", "http/1.1"] }-                                    }-        serverParam' = serverParam { serverHooks = (serverHooks serverParam)-                                        { onALPNClientSuggest = Just alpn }-                                   }-        params' = (clientParam',serverParam')-    runTLSPipe params' tlsServer tlsClient-  where tlsServer ctx queue = do-            handshake ctx-            proto <- getNegotiatedProtocol ctx-            Just "h2" `assertEq` proto-            d <- recvDataNonNull ctx-            writeChan queue d-            return ()-        tlsClient queue ctx = do-            handshake ctx-            proto <- getNegotiatedProtocol ctx-            Just "h2" `assertEq` proto-            d <- readChan queue-            sendData ctx (L.fromChunks [d])-            bye ctx-            return ()-        alpn xs-          | "h2"    `elem` xs = return "h2"-          | otherwise         = return "http/1.1"--prop_handshake_sni :: PropertyM IO ()-prop_handshake_sni = do-    (clientParam,serverParam) <- pick arbitraryPairParams-    let clientParam' = clientParam { clientServerIdentification = (serverName, "")-                                   , clientUseServerNameIndication = True-                                    }-        params' = (clientParam',serverParam)-    runTLSPipe params' tlsServer tlsClient-  where tlsServer ctx queue = do-            handshake ctx-            sni <- getClientSNI ctx-            Just serverName `assertEq` sni-            d <- recvDataNonNull ctx-            writeChan queue d-            return ()-        tlsClient queue ctx = do-            handshake ctx-            d <- readChan queue-            sendData ctx (L.fromChunks [d])-            bye ctx-            return ()-        serverName = "haskell.org"--prop_handshake_renegotiation :: PropertyM IO ()-prop_handshake_renegotiation = do-    (cparams, sparams) <- pick arbitraryPairParams-    let sparams' = sparams {-            serverSupported = (serverSupported sparams) {-                 supportedClientInitiatedRenegotiation = True-               }-          }-    runTLSPipe (cparams, sparams') tlsServer tlsClient-  where tlsServer ctx queue = do-            handshake ctx-            d <- recvDataNonNull ctx-            writeChan queue d-            return ()-        tlsClient queue ctx = do-            handshake ctx-            handshake ctx-            d <- readChan queue-            sendData ctx (L.fromChunks [d])-            bye ctx-            return ()--prop_handshake_session_resumption :: PropertyM IO ()-prop_handshake_session_resumption = do-    sessionRef <- run $ newIORef Nothing-    let sessionManager = oneSessionManager sessionRef--    plainParams <- pick arbitraryPairParams-    let params = setPairParamsSessionManager sessionManager plainParams--    runTLSPipeSimple params--    -- and resume-    sessionParams <- run $ readIORef sessionRef-    assert (isJust sessionParams)-    let params2 = setPairParamsSessionResuming (fromJust sessionParams) params--    runTLSPipeSimple params2--assertEq :: (Show a, Monad m, Eq a) => a -> a -> m ()-assertEq expected got = unless (expected == got) $ error ("got " ++ show got ++ " but was expecting " ++ show expected)--assertIsLeft :: (Show b, Monad m) => Either a b -> m ()-assertIsLeft (Left  _) = return()-assertIsLeft (Right b) = error ("got " ++ show b ++ " but was expecting a failure")--main :: IO ()-main = defaultMain $ testGroup "tls"-    [ tests_marshalling-    , tests_ciphers-    , tests_handshake-    ]-  where -- lowlevel tests to check the packet marshalling.-        tests_marshalling = testGroup "Marshalling"-            [ testProperty "Header" prop_header_marshalling_id-            , testProperty "Handshake" prop_handshake_marshalling_id-            ]-        tests_ciphers = testGroup "Ciphers"-            [ testProperty "Bulk" propertyBulkFunctional ]--        -- high level tests between a client and server with fake ciphers.-        tests_handshake = testGroup "Handshakes"-            [ testProperty "Setup" (monadicIO prop_pipe_work)-            , testProperty "Initiation" (monadicIO prop_handshake_initiate)-            , testProperty "Hash and signatures" (monadicIO prop_handshake_hashsignatures)-            , testProperty "Cipher suites" (monadicIO prop_handshake_ciphersuites)-            , testProperty "Groups" (monadicIO prop_handshake_groups)-            , testProperty "Certificate fallback" (monadicIO prop_handshake_cert_fallback)-            , testProperty "Client authentication" (monadicIO prop_handshake_client_auth)-            , testProperty "ALPN" (monadicIO prop_handshake_alpn)-            , testProperty "SNI" (monadicIO prop_handshake_sni)-            , testProperty "Renegotiation" (monadicIO prop_handshake_renegotiation)-            , testProperty "Resumption" (monadicIO prop_handshake_session_resumption)-            ]
+ test/API.hs view
@@ -0,0 +1,22 @@+{-# LANGUAGE OverloadedStrings #-}++module API where++import Control.Applicative+import Control.Monad+import Data.ByteString (ByteString)+import Data.Maybe+import Network.TLS+import Test.Hspec++checkCtxFinished :: Context -> IO ()+checkCtxFinished ctx = do+    mUnique <- getTLSUnique ctx+    mExporter <- getTLSExporter ctx+    when (isNothing (mUnique <|> mExporter)) $+        fail "unexpected channel binding"++recvDataAssert :: Context -> ByteString -> IO ()+recvDataAssert ctx expected = do+    got <- recvData ctx+    got `shouldBe` expected
+ test/Arbitrary.hs view
@@ -0,0 +1,452 @@+{-# LANGUAGE FlexibleInstances #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}++module Arbitrary where++import Control.Monad+import qualified Data.ByteString as B+import Data.List+import Data.Word+import Data.X509 (ExtKeyUsageFlag)+import Network.TLS+import Network.TLS.Extra.Cipher+import Network.TLS.Internal+import Test.QuickCheck++import Certificate+import PubKey++----------------------------------------------------------------++instance Arbitrary Version where+    arbitrary = elements [TLS12, TLS13]++instance Arbitrary ProtocolType where+    arbitrary =+        elements+            [ ProtocolType_ChangeCipherSpec+            , ProtocolType_Alert+            , ProtocolType_Handshake+            , ProtocolType_AppData+            ]++instance Arbitrary Header where+    arbitrary = Header <$> arbitrary <*> arbitrary <*> arbitrary++instance Arbitrary ClientRandom where+    arbitrary = ClientRandom <$> genByteString 32++instance Arbitrary ServerRandom where+    arbitrary = ServerRandom <$> genByteString 32++instance Arbitrary Session where+    arbitrary = do+        i <- choose (1, 2) :: Gen Int+        case i of+            2 -> Session . Just <$> genByteString 32+            _ -> return $ Session Nothing++instance {-# OVERLAPS #-} Arbitrary [HashAndSignatureAlgorithm] where+    arbitrary = shuffle supportedSignatureSchemes++instance Arbitrary DigitallySigned where+    arbitrary = DigitallySigned . unsafeHead <$> arbitrary <*> genByteString 32++instance Arbitrary ExtensionRaw where+    arbitrary =+        let arbitraryContent = choose (0, 40) >>= genByteString+         in ExtensionRaw . ExtensionID <$> arbitrary <*> arbitraryContent++instance Arbitrary CertificateType where+    arbitrary =+        elements+            [ CertificateType_RSA_Sign+            , CertificateType_DSA_Sign+            , CertificateType_ECDSA_Sign+            ]++instance Arbitrary CipherId where+    arbitrary = CipherId <$> arbitrary++instance Arbitrary Handshake where+    arbitrary =+        oneof+            [ arbitrary >>= \ver -> do+                ClientHello+                    <$> ( CH ver+                            <$> arbitrary+                            <*> arbitrary+                            <*> arbitraryCiphersIds+                            <*> arbitraryCompressionIDs+                            <*> arbitraryHelloExtensions ver+                        )+            , arbitrary >>= \ver ->+                ServerHello+                    <$> ( SH ver+                            <$> arbitrary+                            <*> arbitrary+                            <*> arbitrary+                            <*> arbitrary+                            <*> arbitraryHelloExtensions ver+                        )+            , Certificate . CertificateChain_ . CertificateChain+                <$> resize 2 (listOf arbitraryX509)+            , pure HelloRequest+            , pure ServerHelloDone+            , ClientKeyXchg . CKX_RSA <$> genByteString 48+            , CertRequest <$> arbitrary <*> arbitrary <*> listOf arbitraryDN+            , CertVerify <$> arbitrary+            , Finished . VerifyData <$> genByteString 12+            ]++instance Arbitrary Handshake13 where+    arbitrary =+        oneof+            [ arbitrary >>= \ver ->+                ServerHello13+                    <$> ( SH TLS12+                            <$> arbitrary+                            <*> arbitrary+                            <*> arbitrary+                            <*> pure 0+                            <*> arbitraryHelloExtensions ver+                        )+            , NewSessionTicket13+                <$> arbitrary+                <*> arbitrary+                <*> (TicketNonce <$> genByteString 32) -- nonce+                <*> (SessionIDorTicket_ <$> genByteString 32) -- session ID+                <*> arbitrary+            , pure EndOfEarlyData13+            , EncryptedExtensions13 <$> arbitrary+            , CertRequest13+                <$> arbitraryCertReqContext+                <*> arbitrary+            , resize 2 (listOf arbitraryX509) >>= \certs ->+                Certificate13+                    <$> arbitraryCertReqContext+                    <*> return (CertificateChain_ (CertificateChain certs))+                    <*> replicateM (length certs) arbitrary+            , CertVerify13+                <$> ( DigitallySigned . unsafeHead+                        <$> arbitrary+                        <*> genByteString 32+                    )+            , Finished13 . VerifyData <$> genByteString 12+            , KeyUpdate13 <$> elements [UpdateNotRequested, UpdateRequested]+            ]++----------------------------------------------------------------++arbitraryCiphersIds :: Gen [CipherId]+arbitraryCiphersIds = map CipherId <$> (choose (0, 200) >>= vector)++arbitraryCompressionIDs :: Gen [Word8]+arbitraryCompressionIDs = choose (0, 200) >>= vector++someWords8 :: Int -> Gen [Word8]+someWords8 = vector++arbitraryHelloExtensions :: Version -> Gen [ExtensionRaw]+arbitraryHelloExtensions _ver = arbitrary++arbitraryCertReqContext :: Gen B.ByteString+arbitraryCertReqContext = oneof [return B.empty, genByteString 32]++----------------------------------------------------------------++knownCiphers :: [Cipher]+knownCiphers = ciphersuite_all++instance Arbitrary Cipher where+    arbitrary = elements knownCiphers++knownVersions :: [Version]+knownVersions = [TLS13, TLS12]++arbitraryVersions :: Gen [Version]+arbitraryVersions = sublistOf knownVersions++-- for performance reason P521, FFDHE6144, FFDHE8192 are not tested+knownGroups, knownECGroups, knownFFGroups :: [Group]+knownECGroups = [P256, P384, X25519, X448]+knownFFGroups = [FFDHE2048, FFDHE3072, FFDHE4096]+knownGroups = knownECGroups ++ knownFFGroups++defaultECGroup :: Group+defaultECGroup = P256 -- same as defaultECCurve++otherKnownECGroups :: [Group]+otherKnownECGroups = filter (/= defaultECGroup) knownECGroups++instance Arbitrary Group where+    arbitrary = elements knownGroups++instance {-# OVERLAPS #-} Arbitrary [Group] where+    arbitrary = sublistOf knownGroups++newtype EC = EC [Group] deriving (Show)++instance Arbitrary EC where+    arbitrary = EC <$> shuffle knownECGroups++newtype FFDHE = FFDHE [Group] deriving (Show)++instance Arbitrary FFDHE where+    arbitrary = FFDHE <$> shuffle knownFFGroups++isCredentialDSA :: (CertificateChain, PrivKey) -> Bool+isCredentialDSA (_, PrivKeyDSA _) = True+isCredentialDSA _ = False++----------------------------------------------------------------++arbitraryCredentialsOfEachType :: Gen [(CertificateChain, PrivKey)]+arbitraryCredentialsOfEachType = arbitraryCredentialsOfEachType' >>= shuffle++arbitraryCredentialsOfEachType' :: Gen [(CertificateChain, PrivKey)]+arbitraryCredentialsOfEachType' = do+    let (pubKey, privKey) = getGlobalRSAPair+        curveName = defaultECCurve+    (ecdsaPub, ecdsaPriv) <- arbitraryECDSAPair curveName+    (ed25519Pub, ed25519Priv) <- arbitraryEd25519Pair+    (ed448Pub, ed448Priv) <- arbitraryEd448Pair+    mapM+        ( \(pub, priv) -> do+            cert <- arbitraryX509WithKey (pub, priv)+            return (CertificateChain [cert], priv)+        )+        [ (PubKeyRSA pubKey, PrivKeyRSA privKey)+        , (toPubKeyEC curveName ecdsaPub, toPrivKeyEC curveName ecdsaPriv)+        , (PubKeyEd25519 ed25519Pub, PrivKeyEd25519 ed25519Priv)+        , (PubKeyEd448 ed448Pub, PrivKeyEd448 ed448Priv)+        ]++arbitraryCredentialsOfEachCurve :: Gen [(CertificateChain, PrivKey)]+arbitraryCredentialsOfEachCurve = arbitraryCredentialsOfEachCurve' >>= shuffle++arbitraryCredentialsOfEachCurve' :: Gen [(CertificateChain, PrivKey)]+arbitraryCredentialsOfEachCurve' = do+    ecdsaPairs <-+        mapM+            ( \curveName -> do+                (ecdsaPub, ecdsaPriv) <- arbitraryECDSAPair curveName+                return (toPubKeyEC curveName ecdsaPub, toPrivKeyEC curveName ecdsaPriv)+            )+            knownECCurves+    (ed25519Pub, ed25519Priv) <- arbitraryEd25519Pair+    (ed448Pub, ed448Priv) <- arbitraryEd448Pair+    mapM+        ( \(pub, priv) -> do+            cert <- arbitraryX509WithKey (pub, priv)+            return (CertificateChain [cert], priv)+        )+        $ [ (PubKeyEd25519 ed25519Pub, PrivKeyEd25519 ed25519Priv)+          , (PubKeyEd448 ed448Pub, PrivKeyEd448 ed448Priv)+          ]+            ++ ecdsaPairs++----------------------------------------------------------------++leafPublicKey :: CertificateChain -> Maybe PubKey+leafPublicKey (CertificateChain []) = Nothing+leafPublicKey (CertificateChain (leaf : _)) = Just (certPubKey $ getCertificate leaf)++isLeafRSA :: Maybe CertificateChain -> Bool+isLeafRSA chain = case chain >>= leafPublicKey of+    Just (PubKeyRSA _) -> True+    _ -> False++arbitraryCipherPair :: Version -> Gen ([Cipher], [Cipher])+arbitraryCipherPair connectVersion = do+    serverCiphers <-+        arbitrary+            `suchThat` (\cs -> or [cipherAllowedForVersion connectVersion x | x <- cs])+    clientCiphers <-+        arbitrary+            `suchThat` ( \cs ->+                            or+                                [ x `elem` serverCiphers+                                    && cipherAllowedForVersion connectVersion x+                                | x <- cs+                                ]+                       )+    return (clientCiphers, serverCiphers)++----------------------------------------------------------------++instance {-# OVERLAPS #-} Arbitrary (ClientParams, ServerParams) where+    arbitrary = elements knownVersions >>= arbitraryPairParamsAt++----------------------------------------------------------------++data GGP = GGP [Group] [Group] deriving (Show)++instance Arbitrary GGP where+    arbitrary = arbitraryGroupPair++-- Pair of groups so that at least the default EC group P256 and one FF group+-- are in common.  This makes DHE and ECDHE ciphers always compatible with+-- extension "Supported Elliptic Curves" / "Supported Groups".+arbitraryGroupPair :: Gen GGP+arbitraryGroupPair = do+    (serverECGroups, clientECGroups) <-+        arbitraryGroupPairWith defaultECGroup otherKnownECGroups+    serverGroups <- shuffle serverECGroups+    clientGroups <- shuffle clientECGroups+    return $ GGP clientGroups serverGroups+  where+    arbitraryGroupPairWith e es = do+        s <- sublistOf es+        c <- sublistOf es+        return (e : s, e : c)++----------------------------------------------------------------++arbitraryPairParams12 :: Gen (ClientParams, ServerParams)+arbitraryPairParams12 = arbitraryPairParamsAt TLS12++arbitraryPairParams13 :: Gen (ClientParams, ServerParams)+arbitraryPairParams13 = arbitraryPairParamsAt TLS13++arbitraryPairParamsAt :: Version -> Gen (ClientParams, ServerParams)+arbitraryPairParamsAt connectVersion = do+    (clientCiphers, serverCiphers) <- arbitraryCipherPair connectVersion+    -- Select version lists containing connectVersion, as well as some other+    -- versions for which we have compatible ciphers.  Criteria about cipher+    -- ensure we can test version downgrade.+    let allowedVersions =+            [ v+            | v <- knownVersions+            , or+                [ x `elem` serverCiphers+                    && cipherAllowedForVersion v x+                | x <- clientCiphers+                ]+            ]+        allowedVersionsFiltered = filter (<= connectVersion) allowedVersions+    -- Server or client is allowed to have versions > connectVersion, but not+    -- both simultaneously.+    filterSrv <- arbitrary+    let (clientAllowedVersions, serverAllowedVersions)+            | filterSrv = (allowedVersions, allowedVersionsFiltered)+            | otherwise = (allowedVersionsFiltered, allowedVersions)+    -- Generate version lists containing less than 127 elements, otherwise the+    -- "supported_versions" extension cannot be correctly serialized+    clientVersions <- listWithOthers connectVersion 126 clientAllowedVersions+    serverVersions <- listWithOthers connectVersion 126 serverAllowedVersions+    arbitraryPairParamsWithVersionsAndCiphers+        (clientVersions, serverVersions)+        (clientCiphers, serverCiphers)+  where+    listWithOthers :: a -> Int -> [a] -> Gen [a]+    listWithOthers fixedElement maxOthers others+        | maxOthers < 1 = return [fixedElement]+        | otherwise = sized $ \n -> do+            num <- choose (0, min n maxOthers)+            pos <- choose (0, num)+            prefix <- vectorOf pos $ elements others+            suffix <- vectorOf (num - pos) $ elements others+            return $ prefix ++ (fixedElement : suffix)++----------------------------------------------------------------++getConnectVersion :: (ClientParams, ServerParams) -> Version+getConnectVersion (cparams, sparams) = maximum (cver `intersect` sver)+  where+    sver = supportedVersions (serverSupported sparams)+    cver = supportedVersions (clientSupported cparams)++isVersionEnabled :: Version -> (ClientParams, ServerParams) -> Bool+isVersionEnabled ver (cparams, sparams) =+    (ver `elem` supportedVersions (serverSupported sparams))+        && (ver `elem` supportedVersions (clientSupported cparams))++arbitraryPairParamsWithVersionsAndCiphers+    :: ([Version], [Version])+    -> ([Cipher], [Cipher])+    -> Gen (ClientParams, ServerParams)+arbitraryPairParamsWithVersionsAndCiphers (clientVersions, serverVersions) (clientCiphers, serverCiphers) = do+    secNeg <- arbitrary++    creds <- arbitraryCredentialsOfEachType+    GGP clientGroups serverGroups <- arbitraryGroupPair+    clientHashSignatures <- arbitrary+    serverHashSignatures <- arbitrary+    let serverState =+            defaultParamsServer+                { serverSupported =+                    defaultSupported+                        { supportedCiphers = serverCiphers+                        , supportedVersions = serverVersions+                        , supportedSecureRenegotiation = secNeg+                        , supportedHashSignatures = serverHashSignatures+                        , supportedGroups = serverGroups+                        , supportedGroupsTLS13 = [serverGroups]+                        }+                , serverShared = defaultShared{sharedCredentials = Credentials creds}+                }+    let clientState =+            (defaultParamsClient "" B.empty)+                { clientSupported =+                    defaultSupported+                        { supportedCiphers = clientCiphers+                        , supportedVersions = clientVersions+                        , supportedSecureRenegotiation = secNeg+                        , supportedGroups = clientGroups+                        , supportedHashSignatures = clientHashSignatures+                        }+                , clientShared =+                    defaultShared+                        { sharedValidationCache =+                            ValidationCache+                                { cacheAdd = \_ _ _ -> return ()+                                , cacheQuery = \_ _ _ -> return ValidationCachePass+                                }+                        }+                }+    return (clientState, serverState)++arbitraryClientCredential :: Version -> Gen Credential+arbitraryClientCredential _ = arbitraryCredentialsOfEachCurve' >>= elements++arbitraryRSACredentialWithUsage+    :: [ExtKeyUsageFlag] -> Gen (CertificateChain, PrivKey)+arbitraryRSACredentialWithUsage usageFlags = do+    let (pubKey, privKey) = getGlobalRSAPair+    cert <- arbitraryX509WithKeyAndUsage usageFlags (PubKeyRSA pubKey, ())+    return (CertificateChain [cert], PrivKeyRSA privKey)++instance {-# OVERLAPS #-} Arbitrary (EMSMode, EMSMode) where+    arbitrary = (,) <$> gen <*> gen+      where+        gen = elements [NoEMS, AllowEMS, RequireEMS]++setEMSMode+    :: (EMSMode, EMSMode)+    -> (ClientParams, ServerParams)+    -> (ClientParams, ServerParams)+setEMSMode (cems, sems) (clientParam, serverParam) = (clientParam', serverParam')+  where+    clientParam' =+        clientParam+            { clientSupported =+                (clientSupported clientParam)+                    { supportedExtendedMainSecret = cems+                    }+            }+    serverParam' =+        serverParam+            { serverSupported =+                (serverSupported serverParam)+                    { supportedExtendedMainSecret = sems+                    }+            }++genByteString :: Int -> Gen B.ByteString+genByteString i = B.pack <$> vector i++-- Just for preventing warnings of GHC 9.10+unsafeHead :: [a] -> a+unsafeHead [] = error "unsafeHead"+unsafeHead (x : _) = x
+ test/Certificate.hs view
@@ -0,0 +1,161 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE OverloadedStrings #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}++module Certificate (+    arbitraryX509,+    arbitraryX509WithKey,+    arbitraryX509WithKeyAndUsage,+    arbitraryDN,+    simpleCertificate,+    simpleX509,+    getSignatureALG,+    toPubKeyEC,+    toPrivKeyEC,+) where++import Crypto.Number.Serialize (i2ospOf_)+import qualified Crypto.PubKey.ECC.ECDSA as ECDSA+import qualified Crypto.PubKey.ECC.Types as ECC+import Data.ASN1.OID+import qualified Data.ByteString as B+import Data.Hourglass+import Data.X509+import Test.QuickCheck++import PubKey++arbitraryDN :: Gen DistinguishedName+arbitraryDN = return $ DistinguishedName []++instance Arbitrary Date where+    arbitrary = do+        y <- choose (1971, 2035)+        m <- elements [January .. December]+        d <- choose (1, 30)+        return $ normalizeDate $ Date y m d++normalizeDate :: Date -> Date+normalizeDate d = timeConvert (timeConvert d :: Elapsed)++instance Arbitrary TimeOfDay where+    arbitrary = do+        h <- choose (0, 23)+        mi <- choose (0, 59)+        se <- choose (0, 59)+        let nsec = 0+        return $ TimeOfDay (Hours h) (Minutes mi) (Seconds se) nsec++instance Arbitrary DateTime where+    arbitrary = DateTime <$> arbitrary <*> arbitrary++maxSerial :: Integer+maxSerial = 16777216++arbitraryCertificate :: [ExtKeyUsageFlag] -> PubKey -> Gen Certificate+arbitraryCertificate usageFlags pubKey = do+    serial <- choose (0, maxSerial)+    subjectdn <- arbitraryDN+    validity <- (,) <$> arbitrary <*> arbitrary+    let sigalg = getSignatureALG pubKey+    return $+        Certificate+            { certVersion = 3+            , certSerial = serial+            , certSignatureAlg = sigalg+            , certIssuerDN = issuerdn+            , certSubjectDN = subjectdn+            , certValidity = validity+            , certPubKey = pubKey+            , certExtensions =+                Extensions $+                    Just+                        [ extensionEncode True $ ExtKeyUsage usageFlags+                        ]+            }+  where+    issuerdn = DistinguishedName [(getObjectID DnCommonName, "Root CA")]++simpleCertificate :: PubKey -> Certificate+simpleCertificate pubKey =+    Certificate+        { certVersion = 3+        , certSerial = 0+        , certSignatureAlg = getSignatureALG pubKey+        , certIssuerDN = simpleDN+        , certSubjectDN = simpleDN+        , certValidity = (time1, time2)+        , certPubKey = pubKey+        , certExtensions =+            Extensions $+                Just+                    [ extensionEncode True $+                        ExtKeyUsage [KeyUsage_digitalSignature, KeyUsage_keyEncipherment]+                    ]+        }+  where+    time1 = DateTime (Date 1999 January 1) (TimeOfDay 0 0 0 0)+    time2 = DateTime (Date 2049 January 1) (TimeOfDay 0 0 0 0)+    simpleDN = DistinguishedName []++simpleX509 :: PubKey -> SignedCertificate+simpleX509 pubKey =+    let cert = simpleCertificate pubKey+        sig = replicate 40 1+        sigalg = getSignatureALG pubKey+        (signedExact, ()) = objectToSignedExact (\_ -> (B.pack sig, sigalg, ())) cert+     in signedExact++arbitraryX509WithKey :: (PubKey, t) -> Gen SignedCertificate+arbitraryX509WithKey = arbitraryX509WithKeyAndUsage knownKeyUsage++arbitraryX509WithKeyAndUsage+    :: [ExtKeyUsageFlag] -> (PubKey, t) -> Gen SignedCertificate+arbitraryX509WithKeyAndUsage usageFlags (pubKey, _) = do+    cert <- arbitraryCertificate usageFlags pubKey+    sig <- resize 40 $ listOf1 arbitrary+    let sigalg = getSignatureALG pubKey+    let (signedExact, ()) = objectToSignedExact (\_ -> (B.pack sig, sigalg, ())) cert+    return signedExact++arbitraryX509 :: Gen SignedCertificate+arbitraryX509 = do+    let (pubKey, privKey) = getGlobalRSAPair+    arbitraryX509WithKey (PubKeyRSA pubKey, PrivKeyRSA privKey)++instance {-# OVERLAPS #-} Arbitrary [ExtKeyUsageFlag] where+    arbitrary = sublistOf knownKeyUsage++knownKeyUsage :: [ExtKeyUsageFlag]+knownKeyUsage =+    [ KeyUsage_digitalSignature+    , KeyUsage_keyEncipherment+    , KeyUsage_keyAgreement+    ]++getSignatureALG :: PubKey -> SignatureALG+getSignatureALG (PubKeyRSA _) = SignatureALG HashSHA1 PubKeyALG_RSA+getSignatureALG (PubKeyDSA _) = SignatureALG HashSHA1 PubKeyALG_DSA+getSignatureALG (PubKeyEC _) = SignatureALG HashSHA256 PubKeyALG_EC+getSignatureALG (PubKeyEd25519 _) = SignatureALG_IntrinsicHash PubKeyALG_Ed25519+getSignatureALG (PubKeyEd448 _) = SignatureALG_IntrinsicHash PubKeyALG_Ed448+getSignatureALG pubKey =+    error $ "getSignatureALG: unsupported public key: " ++ show pubKey++toPubKeyEC :: ECC.CurveName -> ECDSA.PublicKey -> PubKey+toPubKeyEC curveName key =+    let (x, y) = fromPoint $ ECDSA.public_q key+        pub = SerializedPoint bs+        bs = B.cons 4 (i2ospOf_ bytes x `B.append` i2ospOf_ bytes y)+        bits = ECC.curveSizeBits (ECC.getCurveByName curveName)+        bytes = (bits + 7) `div` 8+     in PubKeyEC (PubKeyEC_Named curveName pub)++toPrivKeyEC :: ECC.CurveName -> ECDSA.PrivateKey -> PrivKey+toPrivKeyEC curveName key =+    let priv = ECDSA.private_d key+     in PrivKeyEC (PrivKeyEC_Named curveName priv)++fromPoint :: ECC.Point -> (Integer, Integer)+fromPoint (ECC.Point x y) = (x, y)+fromPoint _ = error "fromPoint"
+ test/CiphersSpec.hs view
@@ -0,0 +1,76 @@+module CiphersSpec where++import qualified Data.ByteArray as BA+import Data.ByteString (ByteString)+import qualified Data.ByteString as B+import Network.TLS.Cipher+import Network.TLS.Extra.Cipher+import Test.Hspec+import Test.Hspec.QuickCheck+import Test.QuickCheck++spec :: Spec+spec = do+    describe "ciphers" $ do+        prop "can ecnrypt/decrypt" $ \(BulkTest bulk key iv t additional) -> do+            let enc = bulkInit bulk BulkEncrypt key+                dec = bulkInit bulk BulkDecrypt key+            case (enc, dec) of+                (BulkStateBlock encF, BulkStateBlock decF) -> block encF decF iv t+                (BulkStateAEAD encF, BulkStateAEAD decF) -> aead encF decF iv t additional+                (BulkStateStream (BulkStream encF), BulkStateStream (BulkStream decF)) -> stream encF decF t+                _ -> return ()++block+    :: BulkBlock+    -> BulkBlock+    -> BulkIV+    -> ByteString+    -> IO ()+block e d iv t = do+    let (etxt, e_iv) = e iv t+        (dtxt, d_iv) = d iv etxt+    dtxt `shouldBe` t+    d_iv `shouldBe` e_iv++stream+    :: (ByteString -> (ByteString, BulkStream))+    -> (ByteString -> (ByteString, BulkStream))+    -> ByteString+    -> Expectation+stream e d t = (fst . d . fst . e) t `shouldBe` t++aead+    :: BulkAEAD+    -> BulkAEAD+    -> BulkNonce+    -> ByteString+    -> BulkAdditionalData+    -> Expectation+aead e d iv t additional = do+    let (encrypted, at) = e iv t additional+        (decrypted, at2) = d iv encrypted additional+    decrypted `shouldBe` t+    at `shouldBe` at2++arbitraryKey :: Bulk -> Gen BA.ScrubbedBytes+arbitraryKey bulk = BA.pack `fmap` vector (bulkKeySize bulk)++arbitraryIV :: Bulk -> Gen B.ByteString+arbitraryIV bulk = B.pack `fmap` vector (bulkIVSize bulk + bulkExplicitIV bulk)++arbitraryText :: Bulk -> Gen B.ByteString+arbitraryText bulk = B.pack `fmap` vector (bulkBlockSize bulk)++data BulkTest+    = BulkTest Bulk BA.ScrubbedBytes B.ByteString B.ByteString B.ByteString+    deriving (Show, Eq)++instance Arbitrary BulkTest where+    arbitrary = do+        bulk <- cipherBulk `fmap` elements ciphersuite_all+        BulkTest bulk+            <$> arbitraryKey bulk+            <*> arbitraryIV bulk+            <*> arbitraryText bulk+            <*> arbitraryText bulk
+ test/ECHSpec.hs view
@@ -0,0 +1,446 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}++module ECHSpec (spec) where++import Data.ByteString (ByteString)+import qualified Data.ByteString as B+import qualified Data.ByteString.Base64 as B64+import qualified Data.ByteString.Lazy as L+import Data.Maybe+import Network.TLS+import Network.TLS.ECH.Config+import Network.TLS.Extra.Cipher+import Network.TLS.Internal+import Test.Hspec+import Test.Hspec.QuickCheck+import Test.QuickCheck++import Arbitrary+import Run+import Session++spec :: Spec+spec = do+    describe "ECH" $ do+        prop "can handshake with TLS 1.3 Full" handshake13_full+        prop "can handshake with TLS 1.3 HRR" handshake13_hrr+        prop "can handshake with TLS 1.3 PSK" handshake13_psk+        prop "can handshake with TLS 1.3 PSK ticket" handshake13_psk_ticket+        prop "can handshake with TLS 1.3 PSK -> HRR" handshake13_psk_fallback+        prop "can handshake with TLS 1.3 0RTT" handshake13_0rtt+        prop "can handshake with TLS 1.3 0RTT -> PSK" handshake13_0rtt_fallback+        prop "can handshake with TLS 1.3 EC groups" handshake13_ec+        prop "can handshake with TLS 1.3 FFDHE groups" handshake13_ffdhe+    describe "ECH greasing" $ do+        prop "sends greasing ECH" handshake13_greasing+        prop "sends greasing ECH HRR" handshake13_greasing_hrr++--------------------------------------------------------------++newtype CSP13 = CSP13 (ClientParams, ServerParams) deriving (Show)++instance Arbitrary CSP13 where+    arbitrary = CSP13 <$> arbitraryPairParams13++--------------------------------------------------------------++handshake13_full :: CSP13 -> IO ()+handshake13_full (CSP13 (cli, srv)) = do+    let cliSupported =+            defaultSupported+                { supportedCiphers = [cipher13_AES_128_GCM_SHA256]+                , supportedGroups = [X25519]+                }+        svrSupported =+            defaultSupported+                { supportedCiphers = [cipher13_AES_128_GCM_SHA256]+                , supportedGroups = [X25519]+                , supportedGroupsTLS13 = [[X25519]]+                }+        params =+            setParams+                ( cli{clientSupported = cliSupported}+                , srv{serverSupported = svrSupported}+                )+    runTLSSimple13ECH params FullHandshake++handshake13_hrr :: CSP13 -> IO ()+handshake13_hrr (CSP13 (cli, srv)) = do+    let cliSupported =+            defaultSupported+                { supportedCiphers = [cipher13_AES_128_GCM_SHA256]+                , supportedGroups = [P256, X25519]+                }+        svrSupported =+            defaultSupported+                { supportedCiphers = [cipher13_AES_128_GCM_SHA256]+                , supportedGroups = [X25519]+                , supportedGroupsTLS13 = [[X25519]]+                }+        params =+            setParams+                ( cli{clientSupported = cliSupported}+                , srv{serverSupported = svrSupported}+                )+    runTLSSimple13ECH params HelloRetryRequest++handshake13_psk :: CSP13 -> IO ()+handshake13_psk (CSP13 (cli, srv)) = do+    let cliSupported =+            defaultSupported+                { supportedCiphers = [cipher13_AES_128_GCM_SHA256]+                , supportedGroups = [P256, X25519]+                }+        svrSupported =+            defaultSupported+                { supportedCiphers = [cipher13_AES_128_GCM_SHA256]+                , supportedGroups = [X25519]+                , supportedGroupsTLS13 = [[X25519]]+                }+        params0 =+            setParams+                ( cli{clientSupported = cliSupported}+                , srv{serverSupported = svrSupported}+                )++    sessionRefs <- twoSessionRefs+    let sessionManagers = twoSessionManagers sessionRefs++    let params = setPairParamsSessionManagers sessionManagers params0++    runTLSSimple13ECH params HelloRetryRequest++    -- and resume+    sessionParams <- readClientSessionRef sessionRefs+    expectJust "session param should be Just" sessionParams+    let params2 = setPairParamsSessionResuming (fromJust sessionParams) params++    runTLSSimple13ECH params2 PreSharedKey++handshake13_psk_ticket :: CSP13 -> IO ()+handshake13_psk_ticket (CSP13 (cli, srv)) = do+    let cliSupported =+            defaultSupported+                { supportedCiphers = [cipher13_AES_128_GCM_SHA256]+                , supportedGroups = [P256, X25519]+                }+        svrSupported =+            defaultSupported+                { supportedCiphers = [cipher13_AES_128_GCM_SHA256]+                , supportedGroups = [X25519]+                , supportedGroupsTLS13 = [[X25519]]+                }+        params0 =+            setParams+                ( cli{clientSupported = cliSupported}+                , srv{serverSupported = svrSupported}+                )++    sessionRefs <- twoSessionRefs+    let sessionManagers0 = twoSessionManagers sessionRefs+        sessionManagers = (fst sessionManagers0, oneSessionTicket)++    let params = setPairParamsSessionManagers sessionManagers params0++    runTLSSimple13ECH params HelloRetryRequest++    -- and resume+    sessionParams <- readClientSessionRef sessionRefs+    expectJust "session param should be Just" sessionParams+    let params2 = setPairParamsSessionResuming (fromJust sessionParams) params++    runTLSSimple13ECH params2 PreSharedKey++handshake13_psk_fallback :: CSP13 -> IO ()+handshake13_psk_fallback (CSP13 (cli, srv)) = do+    let cliSupported =+            defaultSupported+                { supportedCiphers =+                    [ cipher13_AES_128_GCM_SHA256+                    , cipher13_AES_128_CCM_SHA256+                    ]+                , supportedGroups = [P256, X25519]+                }+        svrSupported =+            defaultSupported+                { supportedCiphers = [cipher13_AES_128_GCM_SHA256]+                , supportedGroups = [X25519]+                , supportedGroupsTLS13 = [[X25519]]+                }+        params0 =+            setParams+                ( cli{clientSupported = cliSupported}+                , srv{serverSupported = svrSupported}+                )++    sessionRefs <- twoSessionRefs+    let sessionManagers = twoSessionManagers sessionRefs++    let params = setPairParamsSessionManagers sessionManagers params0++    runTLSSimple13ECH params HelloRetryRequest++    -- resumption fails because GCM cipher is not supported anymore, full+    -- handshake is not possible because X25519 has been removed, so we are+    -- back with P256 after hello retry+    sessionParams <- readClientSessionRef sessionRefs+    expectJust "session param should be Just" sessionParams+    let (cli2, srv2) = setPairParamsSessionResuming (fromJust sessionParams) params+        srv2' = srv2{serverSupported = svrSupported'}+        svrSupported' =+            defaultSupported+                { supportedCiphers = [cipher13_AES_128_CCM_SHA256]+                , supportedGroups = [P256]+                , supportedGroupsTLS13 = [[P256]]+                }++    runTLSSimple13ECH (cli2, srv2') HelloRetryRequest++handshake13_0rtt :: CSP13 -> IO ()+handshake13_0rtt (CSP13 (cli, srv)) = do+    let cliSupported =+            defaultSupported+                { supportedCiphers = [cipher13_AES_128_GCM_SHA256]+                , supportedGroups = [P256, X25519]+                }+        svrSupported =+            defaultSupported+                { supportedCiphers = [cipher13_AES_128_GCM_SHA256]+                , supportedGroups = [X25519]+                , supportedGroupsTLS13 = [[X25519]]+                }+        cliHooks =+            defaultClientHooks+                { onSuggestALPN = return $ Just ["h2"]+                }+        svrHooks =+            defaultServerHooks+                { onALPNClientSuggest = Just (return . unsafeHead)+                }+        params0 =+            setParams+                ( cli+                    { clientSupported = cliSupported+                    , clientHooks = cliHooks+                    }+                , srv+                    { serverSupported = svrSupported+                    , serverHooks = svrHooks+                    , serverEarlyDataSize = 2048+                    }+                )++    sessionRefs <- twoSessionRefs+    let sessionManagers = twoSessionManagers sessionRefs++    let params = setPairParamsSessionManagers sessionManagers params0++    runTLSSimple13ECH params HelloRetryRequest+    runTLS0rtt params sessionRefs+    runTLS0rtt params sessionRefs+  where+    runTLS0rtt params sessionRefs = do+        -- and resume+        sessionParams <- readClientSessionRef sessionRefs+        expectJust "session param should be Just" sessionParams+        clearClientSessionRef sessionRefs+        earlyData <- B.pack <$> generate (someWords8 256)+        let (pc, ps) = setPairParamsSessionResuming (fromJust sessionParams) params+            params2 = (pc{clientUseEarlyData = True}, ps)++        runTLS0RTTech params2 RTT0 earlyData++handshake13_0rtt_fallback :: CSP13 -> IO ()+handshake13_0rtt_fallback (CSP13 (cli, srv)) = do+    group0 <- generate $ elements [P256, X25519]+    let cliSupported =+            defaultSupported+                { supportedCiphers = [cipher13_AES_128_GCM_SHA256]+                , supportedGroups = [P256, X25519]+                }+        svrSupported =+            defaultSupported+                { supportedCiphers = [cipher13_AES_128_GCM_SHA256]+                , supportedGroups = [group0]+                , supportedGroupsTLS13 = [[group0]]+                }+        params =+            setParams+                ( cli{clientSupported = cliSupported}+                , srv+                    { serverSupported = svrSupported+                    , serverEarlyDataSize = 1024+                    }+                )++    sessionRefs <- twoSessionRefs+    let sessionManagers = twoSessionManagers sessionRefs++    let params0 = setPairParamsSessionManagers sessionManagers params++    let mode = if group0 == P256 then FullHandshake else HelloRetryRequest+    runTLSSimple13ECH params0 mode++    -- and resume+    mSessionParams <- readClientSessionRef sessionRefs+    case mSessionParams of+        Nothing -> expectationFailure "session params: Just is expected"+        Just sessionParams -> do+            earlyData <- B.pack <$> generate (someWords8 256)+            group1 <- generate $ elements [P256, X25519]+            let (pc, ps) = setPairParamsSessionResuming sessionParams params0+                svrSupported1 =+                    defaultSupported+                        { supportedCiphers = [cipher13_AES_128_GCM_SHA256]+                        , supportedGroups = [group1]+                        , supportedGroupsTLS13 = [[group1]]+                        }+                params1 =+                    ( pc{clientUseEarlyData = True}+                    , ps+                        { serverEarlyDataSize = 0+                        , serverSupported = svrSupported1+                        }+                    )+            -- C: [P256, X25519]+            -- S: [group0]+            -- C: [P256, X25519]+            -- S: [group1]+            if group0 == group1+                -- 0-RTT is not allowed, so fallback to PreSharedKey+                then runTLS0RTTech params1 PreSharedKey earlyData+                -- HRR but not allowed for 0-RTT+                else runTLSFailure params1 (tlsClient earlyData) tlsServer+  where+    tlsClient earlyData ctx = do+        handshake ctx+        sendData ctx $ L.fromStrict earlyData+        _ <- recvData ctx+        bye ctx+    tlsServer ctx = do+        handshake ctx+        _ <- recvData ctx+        bye ctx++handshake13_ec :: CSP13 -> IO ()+handshake13_ec (CSP13 (cli, srv)) = do+    EC cgrps <- generate arbitrary+    EC sgrps <- generate arbitrary+    let cliSupported = (clientSupported cli){supportedGroups = cgrps}+        svrSupported =+            (serverSupported srv)+                { supportedGroups = sgrps+                , supportedGroupsTLS13 = [sgrps]+                }+        params =+            setParams+                ( cli{clientSupported = cliSupported}+                , srv{serverSupported = svrSupported}+                )+    runTLSSimple13ECH params FullHandshake++handshake13_ffdhe :: CSP13 -> IO ()+handshake13_ffdhe (CSP13 (cli, srv)) = do+    FFDHE cgrps <- generate arbitrary+    FFDHE sgrps <- generate arbitrary+    let cliSupported = (clientSupported cli){supportedGroups = cgrps}+        svrSupported =+            (serverSupported srv)+                { supportedGroups = sgrps+                , supportedGroupsTLS13 = [sgrps]+                }++        params =+            setParams+                ( cli{clientSupported = cliSupported}+                , srv{serverSupported = svrSupported}+                )+    runTLSSimple13ECH params FullHandshake++handshake13_greasing :: CSP13 -> IO ()+handshake13_greasing (CSP13 (cli, srv)) = do+    let cliSupported =+            defaultSupported+                { supportedCiphers = [cipher13_AES_128_GCM_SHA256]+                , supportedGroups = [X25519]+                }+        svrSupported =+            defaultSupported+                { supportedCiphers = [cipher13_AES_128_GCM_SHA256]+                , supportedGroups = [X25519]+                , supportedGroupsTLS13 = [[X25519]]+                }+        params =+            ( cli+                { clientSupported = cliSupported+                , clientUseECH = True+                , clientShared = (clientShared cli){sharedECHConfigList = echConfList}+                }+            , srv{serverSupported = svrSupported}+            )+    (clientMessages, _) <- runTLSCaptureFail params+    let isGreasing (ExtensionRaw eid _) = eid == EID_EncryptedClientHello+        eeMessagesHaveExt =+            [ any isGreasing chExtensions+            | ClientHello CH{..} <- clientMessages+            ]+    eeMessagesHaveExt `shouldBe` [True]++handshake13_greasing_hrr :: CSP13 -> IO ()+handshake13_greasing_hrr (CSP13 (cli, srv)) = do+    let cliSupported =+            defaultSupported+                { supportedCiphers = [cipher13_AES_128_GCM_SHA256]+                , supportedGroups = [P256, X25519]+                }+        svrSupported =+            defaultSupported+                { supportedCiphers = [cipher13_AES_128_GCM_SHA256]+                , supportedGroups = [X25519]+                , supportedGroupsTLS13 = [[X25519]]+                }+        params =+            ( cli+                { clientSupported = cliSupported+                , clientUseECH = True+                , clientShared = (clientShared cli){sharedECHConfigList = echConfList}+                }+            , srv{serverSupported = svrSupported}+            )+    (clientMessages, _) <- runTLSCaptureFail params+    let isGreasing (ExtensionRaw eid _) = eid == EID_EncryptedClientHello+        eeMessagesHaveExt =+            [ any isGreasing chExtensions+            | ClientHello CH{..} <- clientMessages+            ]+    eeMessagesHaveExt `shouldBe` [True, True]++expectJust :: String -> Maybe a -> Expectation+expectJust tag mx = case mx of+    Nothing -> expectationFailure tag+    Just _ -> return ()++setParams :: (ClientParams, ServerParams) -> (ClientParams, ServerParams)+setParams (cli, srv) = (cli', srv')+  where+    cli' =+        cli+            { clientUseECH = True+            , clientShared = (clientShared cli){sharedECHConfigList = echConfList}+            }+    srv' =+        srv+            { serverECHKey = echKey+            , serverShared = (serverShared srv){sharedECHConfigList = echConfList}+            }++echKey :: [(ConfigId, ByteString)]+echKey = [(0, B64.decodeLenient "GAl/YqzDDnssODe5t+2xlQsbSv26kNlfJ0D+nZbK62I=")]++echConfList :: ECHConfigList+echConfList =+    fromJust $+        decodeECHConfigList $+            B64.decodeLenient+                "AEP+DQA/AAAgACDGNVZWrmqQfzAuYGJNa8+OEc6zaUfzd0ltyJQ2y1U2AwAEAAEAAQAQcHVibGljLWxvY2FsaG9zdAAA"
+ test/EncodeSpec.hs view
@@ -0,0 +1,39 @@+module EncodeSpec where++import Data.ByteString (ByteString)+import Network.TLS+import Network.TLS.Internal+import Test.Hspec+import Test.Hspec.QuickCheck++import Arbitrary ()++spec :: Spec+spec = do+    describe "encoder/decoder" $ do+        prop "can encode/decode Header" $ \x -> do+            decodeHeader (encodeHeader x) `shouldBe` Right x+        prop "can encode/decode Handshake" $ \x -> do+            decodeHs (encodeHandshake x) `shouldBe` Right x+        prop "can encode/decode Handshake13" $ \x -> do+            decodeHs13 (encodeHandshake13 x) `shouldBe` Right x++decodeHs :: ByteString -> Either TLSError Handshake+decodeHs b = verifyResult (decodeHandshake cp) $ decodeHandshakeRecord b+  where+    cp =+        CurrentParams+            { cParamsVersion = TLS12+            , cParamsKeyXchgType = Just CipherKeyExchange_RSA+            }++decodeHs13 :: ByteString -> Either TLSError Handshake13+decodeHs13 b = verifyResult decodeHandshake13 $ decodeHandshakeRecord13 b++verifyResult :: (f -> r -> a) -> GetResult (f, r) -> a+verifyResult fn result =+    case result of+        GotPartial _ -> error "got partial"+        GotError e -> error ("got error: " ++ show e)+        GotSuccessRemaining _ _ -> error "got remaining byte left"+        GotSuccess (ty, content) -> fn ty content
+ test/HandshakeSpec.hs view
@@ -0,0 +1,1088 @@+{-# LANGUAGE OverloadedStrings #-}++module HandshakeSpec where++import Control.Monad+import qualified Data.ByteString as B+import qualified Data.ByteString.Lazy as L+import Data.IORef+import Data.List+import Data.Maybe+import Data.X509 (ExtKeyUsageFlag (..))+import Network.TLS+import Network.TLS.Extra.Cipher+import Network.TLS.Extra.CipherCBC+import Network.TLS.Internal+import Test.Hspec+import Test.Hspec.QuickCheck+import Test.QuickCheck++import API+import Arbitrary+import PipeChan+import Run+import Session++spec :: Spec+spec = do+    describe "pipe" $ do+        it "can setup a channel" pipe_work+    describe "handshake" $ do+        prop "can run TLS 1.2" handshake_simple+        prop "can run TLS 1.3" handshake13_simple+        prop "can update key for TLS 1.3" handshake_update_key+        prop "can prevent downgrade attack" handshake13_downgrade+        prop "can negotiate hash and signature" handshake_hashsignatures+        prop "can negotiate cipher suite" handshake_ciphersuites+        prop "can negotiate group" handshake_groups+        prop "can negotiate elliptic curve" handshake_ec+        prop "can fallback for certificate with cipher" handshake_cert_fallback_cipher+        prop+            "can fallback for certificate with hash and signature"+            handshake_cert_fallback_hs+        prop "can handle server key usage" handshake_server_key_usage+        prop "can handle client key usage" handshake_client_key_usage+        prop "can authenticate client" handshake_client_auth+        prop "can receive client authentication failure" handshake_client_auth_fail+        prop "can handle extended main secret" handshake_ems+        prop "can resume with extended main secret" handshake_resumption_ems+        prop "can handle ALPN" handshake_alpn+        prop "can handle SNI" handshake_sni+        prop "can handshake with TLS 1.2 CBC" handshake_cbc+        prop "can re-negotiate with TLS 1.2" handshake12_renegotiation+        prop "can resume session with TLS 1.2" handshake12_session_resumption+        prop "can resume session ticket with TLS 1.2" handshake12_session_ticket+        prop "can handshake with TLS 1.3 Full" handshake13_full+        prop "can handshake with TLS 1.3 HRR" handshake13_hrr+        prop "can handshake with TLS 1.3 PSK" handshake13_psk+        prop "can handshake with TLS 1.3 PSK ticket" handshake13_psk_ticket+        prop "can handshake with TLS 1.3 PSK -> HRR" handshake13_psk_fallback+        prop "can handshake with TLS 1.3 0RTT" handshake13_0rtt+        prop "can handshake with TLS 1.3 0RTT -> PSK" handshake13_0rtt_fallback+        prop "can handshake with TLS 1.3 EE" handshake13_ee_groups+        prop "can handshake with TLS 1.3 EC groups" handshake13_ec+        prop "can handshake with TLS 1.3 FFDHE groups" handshake13_ffdhe+        prop "can handshake with TLS 1.3 Post-handshake auth" post_handshake_auth++--------------------------------------------------------------++pipe_work :: IO ()+pipe_work = do+    pipe <- newPipe+    _ <- runPipe pipe++    let bSize = 16+    n <- generate (choose (1, 32))++    let d1 = B.replicate (bSize * n) 40+    let d2 = B.replicate (bSize * n) 45++    d1' <- writePipeC pipe d1 >> readPipeS pipe (B.length d1)+    d1' `shouldBe` d1++    d2' <- writePipeS pipe d2 >> readPipeC pipe (B.length d2)+    d2' `shouldBe` d2++--------------------------------------------------------------++handshake_simple :: (ClientParams, ServerParams) -> IO ()+handshake_simple = runTLSSimple++--------------------------------------------------------------++newtype CSP13 = CSP13 (ClientParams, ServerParams) deriving (Show)++instance Arbitrary CSP13 where+    arbitrary = CSP13 <$> arbitraryPairParams13++handshake13_simple :: CSP13 -> IO ()+handshake13_simple (CSP13 params) = runTLSSimple13 params hs+  where+    cgrps = supportedGroups $ clientSupported $ fst params+    sgrps = supportedGroups $ serverSupported $ snd params+    hs = if unsafeHead cgrps `elem` sgrps then FullHandshake else HelloRetryRequest++--------------------------------------------------------------++handshake_cbc :: IO ()+handshake_cbc = do+    clientCiphers <- generate $ cipherGen >>= shuffle+    serverCiphers <- generate $ cipherGen >>= shuffle+    clientGroups <- generate $ groupGen >>= shuffle+    serverGroups <- generate $ groupGen >>= shuffle+    (clientParam, serverParam) <- generate $+        arbitraryPairParamsWithVersionsAndCiphers+            ([TLS12], [TLS12])+            (clientCiphers, serverCiphers)+    let clientParam' = clientParam {+            clientSupported = (clientSupported clientParam)+                { supportedGroups = clientGroups } }+        serverParam' = serverParam {+            serverSupported = (serverSupported serverParam)+                { supportedGroups = serverGroups } }+    let ciphers = clientCiphers `intersect` serverCiphers+        groups = clientGroups `intersect` serverGroups+     in if compat ciphers groups+        then runTLSSimple (clientParam', serverParam')+        else runTLSFailure (clientParam', serverParam') handshake handshake+  where+    groupGen :: Gen [Group]+    groupGen = sublistOf grps `suchThat` (not . null)+      where+        grps = [X25519, P256, P384, FFDHE2048, FFDHE3072, FFDHE4096]++    cipherGen :: Gen [Cipher]+    cipherGen = sublistOf ciphersuite_pfs_sha2_cbc `suchThat` (not . null)++    compat :: [Cipher] -> [Group] -> Bool+    compat [] _ = False+    compat _ [] = False+    compat ciphers groups =+        let mustdh = all (== CipherKeyExchange_DHE_RSA) $ map cipherKeyExchange ciphers+            mustec = all (/= CipherKeyExchange_DHE_RSA) $ map cipherKeyExchange ciphers+            havedh = any (`elem` [FFDHE2048, FFDHE3072, FFDHE4096]) groups+            haveec = any (`elem` [X25519, P256, P384]) groups+         in ((not mustdh || havedh) && (not mustec || haveec))++--------------------------------------------------------------++handshake13_downgrade :: (ClientParams, ServerParams) -> IO ()+handshake13_downgrade (cparam, sparam) = do+    versionForced <-+        generate $ elements (supportedVersions $ clientSupported cparam)+    let debug' = (serverDebug sparam){debugVersionForced = Just versionForced}+        sparam' = sparam{serverDebug = debug'}+        params = (cparam, sparam')+        downgraded =+            (isVersionEnabled TLS13 params && versionForced < TLS13)+                || (isVersionEnabled TLS12 params && versionForced < TLS12)+    if downgraded+        then runTLSFailure params handshake handshake+        else runTLSSimple params++handshake_update_key :: (ClientParams, ServerParams) -> IO ()+handshake_update_key = runTLSSimpleKeyUpdate++--------------------------------------------------------------++handshake_hashsignatures+    :: ([HashAndSignatureAlgorithm], [HashAndSignatureAlgorithm]) -> IO ()+handshake_hashsignatures (clientHashSigs, serverHashSigs) = do+    tls13 <- generate arbitrary+    let version = if tls13 then TLS13 else TLS12+        ciphers =+            [ cipher_ECDHE_RSA_WITH_AES_256_GCM_SHA384+            , cipher_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384+            , cipher13_AES_128_GCM_SHA256+            ]+    (clientParam, serverParam) <-+        generate $+            arbitraryPairParamsWithVersionsAndCiphers+                ([version], [version])+                (ciphers, ciphers)+    let clientParam' =+            clientParam+                { clientSupported =+                    (clientSupported clientParam)+                        { supportedHashSignatures = clientHashSigs+                        }+                }+        serverParam' =+            serverParam+                { serverSupported =+                    (serverSupported serverParam)+                        { supportedHashSignatures = serverHashSigs+                        }+                }+        commonHashSigs = clientHashSigs `intersect` serverHashSigs+        shouldFail+            | tls13 = all incompatibleWithDefaultCurve commonHashSigs+            | otherwise = null commonHashSigs+    if shouldFail+        then runTLSFailure (clientParam', serverParam') handshake handshake+        else runTLSSimple (clientParam', serverParam')+  where+    incompatibleWithDefaultCurve (h, SignatureECDSA) = h /= HashSHA256+    incompatibleWithDefaultCurve _ = False++handshake_ciphersuites :: ([Cipher], [Cipher]) -> IO ()+handshake_ciphersuites (clientCiphers, serverCiphers) = do+    tls13 <- generate arbitrary+    let version = if tls13 then TLS13 else TLS12+    (clientParam, serverParam) <-+        generate $+            arbitraryPairParamsWithVersionsAndCiphers+                ([version], [version])+                (clientCiphers, serverCiphers)+    let adequate = cipherAllowedForVersion version+        shouldSucceed = any adequate (clientCiphers `intersect` serverCiphers)+    if shouldSucceed+        then runTLSSimple (clientParam, serverParam)+        else runTLSFailure (clientParam, serverParam) handshake handshake++--------------------------------------------------------------++handshake_groups :: GGP -> IO ()+handshake_groups (GGP clientGroups serverGroups) = do+    tls13 <- generate arbitrary+    let versions = if tls13 then [TLS13] else [TLS12]+        ciphers = ciphersuite_strong+    (clientParam, serverParam) <-+        generate $+            arbitraryPairParamsWithVersionsAndCiphers+                (versions, versions)+                (ciphers, ciphers)+    denyCustom <- generate arbitrary+    let groupUsage =+            if denyCustom+                then GroupUsageUnsupported "custom group denied"+                else GroupUsageValid+        clientParam' =+            clientParam+                { clientSupported =+                    (clientSupported clientParam)+                        { supportedGroups = clientGroups+                        }+                , clientHooks =+                    (clientHooks clientParam)+                        { onCustomFFDHEGroup = \_ _ -> return groupUsage+                        }+                }+        serverParam' =+            serverParam+                { serverSupported =+                    (serverSupported serverParam)+                        { supportedGroups = serverGroups+                        , supportedGroupsTLS13 = [serverGroups]+                        }+                }+        commonGroups = clientGroups `intersect` serverGroups+        shouldFail = null commonGroups+        p minfo = isNothing (minfo >>= infoSupportedGroup) == null commonGroups+    if shouldFail+        then runTLSFailure (clientParam', serverParam') handshake handshake+        else runTLSPredicate (clientParam', serverParam') p++--------------------------------------------------------------++newtype SG = SG [Group] deriving (Show)++instance Arbitrary SG where+    arbitrary = SG <$> shuffle sigGroups+      where+        sigGroups = [P256, P521]++handshake_ec :: SG -> IO ()+handshake_ec (SG sigGroups) = do+    let versions = [TLS12]+        ciphers =+            [ cipher_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384+            ]+        hashSignatures =+            [ (HashSHA256, SignatureECDSA)+            ]+    (clientParam, serverParam) <-+        generate $+            arbitraryPairParamsWithVersionsAndCiphers+                (versions, versions)+                (ciphers, ciphers)+    clientGroups <- generate $ shuffle sigGroups+    clientHashSignatures <- generate $ sublistOf hashSignatures+    serverHashSignatures <- generate $ sublistOf hashSignatures+    credentials <- generate arbitraryCredentialsOfEachCurve+    let clientParam' =+            clientParam+                { clientSupported =+                    (clientSupported clientParam)+                        { supportedGroups = clientGroups+                        , supportedHashSignatures = clientHashSignatures+                        }+                }+        serverParam' =+            serverParam+                { serverSupported =+                    (serverSupported serverParam)+                        { supportedGroups = sigGroups+                        , supportedGroupsTLS13 = [sigGroups]+                        , supportedHashSignatures = serverHashSignatures+                        }+                , serverShared =+                    (serverShared serverParam)+                        { sharedCredentials = Credentials credentials+                        }+                }+        sigAlgs = map snd (clientHashSignatures `intersect` serverHashSignatures)+        ecdsaDenied = SignatureECDSA `notElem` sigAlgs+    if ecdsaDenied+        then runTLSFailure (clientParam', serverParam') handshake handshake+        else runTLSSimple (clientParam', serverParam')++-- Tests ability to use or ignore client "signature_algorithms" extension when+-- choosing a server certificate.  Here peers allow DHE_RSA_AES128_SHA1 but+-- the server RSA certificate has a SHA-1 signature that the client does not+-- support.  Server may choose the DSA certificate only when cipher+-- DHE_DSA_AES128_SHA1 is allowed.  Otherwise it must fallback to the RSA+-- certificate.++data OC = OC [Cipher] [Cipher] deriving (Show)++instance Arbitrary OC where+    arbitrary = OC <$> sublistOf otherCiphers <*> sublistOf otherCiphers+      where+        otherCiphers =+            [ cipher_ECDHE_RSA_WITH_AES_256_GCM_SHA384+            , cipher_ECDHE_RSA_WITH_AES_128_GCM_SHA256+            ]++handshake_cert_fallback_cipher :: OC -> IO ()+handshake_cert_fallback_cipher (OC clientCiphers serverCiphers) = do+    let clientVersions = [TLS12]+        serverVersions = [TLS12]+        commonCiphers = [cipher_ECDHE_RSA_WITH_AES_128_GCM_SHA256]+        hashSignatures = [(HashSHA256, SignatureRSA), (HashSHA1, SignatureDSA)]+    chainRef <- newIORef Nothing+    (clientParam, serverParam) <-+        generate $+            arbitraryPairParamsWithVersionsAndCiphers+                (clientVersions, serverVersions)+                (clientCiphers ++ commonCiphers, serverCiphers ++ commonCiphers)+    let clientParam' =+            clientParam+                { clientSupported =+                    (clientSupported clientParam)+                        { supportedHashSignatures = hashSignatures+                        }+                , clientHooks =+                    (clientHooks clientParam)+                        { onServerCertificate = \_ _ _ chain ->+                            writeIORef chainRef (Just chain) >> return []+                        }+                }+    runTLSSimple (clientParam', serverParam)+    serverChain <- readIORef chainRef+    isLeafRSA serverChain `shouldBe` True++-- Same as above but testing with supportedHashSignatures directly instead of+-- ciphers, and thus allowing TLS13.  Peers accept RSA with SHA-256 but the+-- server RSA certificate has a SHA-1 signature.  When Ed25519 is allowed by+-- both client and server, the Ed25519 certificate is selected.  Otherwise the+-- server fallbacks to RSA.+--+-- Note: SHA-1 is supposed to be disallowed in X.509 signatures with TLS13+-- unless client advertises explicit support.  Currently this is not enforced by+-- the library, which is useful to test this scenario.  SHA-1 could be replaced+-- by another algorithm.++data OHS = OHS [HashAndSignatureAlgorithm] [HashAndSignatureAlgorithm]+    deriving (Show)++instance Arbitrary OHS where+    arbitrary = OHS <$> sublistOf otherHS <*> sublistOf otherHS+      where+        otherHS = [(HashIntrinsic, SignatureEd25519)]++handshake_cert_fallback_hs :: OHS -> IO ()+handshake_cert_fallback_hs (OHS clientHS serverHS) = do+    tls13 <- generate arbitrary+    let versions = if tls13 then [TLS13] else [TLS12]+        ciphers =+            [ cipher_ECDHE_RSA_WITH_AES_128_GCM_SHA256+            , cipher_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256+            , cipher13_AES_128_GCM_SHA256+            ]+        commonHS =+            [ (HashSHA256, SignatureRSA)+            , (HashIntrinsic, SignatureRSApssRSAeSHA256)+            ]+    chainRef <- newIORef Nothing+    (clientParam, serverParam) <-+        generate $+            arbitraryPairParamsWithVersionsAndCiphers+                (versions, versions)+                (ciphers, ciphers)+    let clientParam' =+            clientParam+                { clientSupported =+                    (clientSupported clientParam)+                        { supportedHashSignatures = commonHS ++ clientHS+                        }+                , clientHooks =+                    (clientHooks clientParam)+                        { onServerCertificate = \_ _ _ chain ->+                            writeIORef chainRef (Just chain) >> return []+                        }+                }+        serverParam' =+            serverParam+                { serverSupported =+                    (serverSupported serverParam)+                        { supportedHashSignatures = commonHS ++ serverHS+                        }+                }+        eddsaDisallowed =+            (HashIntrinsic, SignatureEd25519) `notElem` clientHS+                || (HashIntrinsic, SignatureEd25519) `notElem` serverHS+    runTLSSimple (clientParam', serverParam')+    serverChain <- readIORef chainRef+    isLeafRSA serverChain `shouldBe` eddsaDisallowed++--------------------------------------------------------------++handshake_server_key_usage :: [ExtKeyUsageFlag] -> IO ()+handshake_server_key_usage usageFlags = do+    tls13 <- generate arbitrary+    let versions = if tls13 then [TLS13] else [TLS12]+        ciphers = ciphersuite_all+    (clientParam, serverParam) <-+        generate $+            arbitraryPairParamsWithVersionsAndCiphers+                (versions, versions)+                (ciphers, ciphers)+    cred <- generate $ arbitraryRSACredentialWithUsage usageFlags+    let serverParam' =+            serverParam+                { serverShared =+                    (serverShared serverParam)+                        { sharedCredentials = Credentials [cred]+                        }+                }+        shouldSucceed = KeyUsage_digitalSignature `elem` usageFlags+    if shouldSucceed+        then runTLSSimple (clientParam, serverParam')+        else runTLSFailure (clientParam, serverParam') handshake handshake++handshake_client_key_usage :: [ExtKeyUsageFlag] -> IO ()+handshake_client_key_usage usageFlags = do+    (clientParam, serverParam) <- generate arbitrary+    cred <- generate $ arbitraryRSACredentialWithUsage usageFlags+    let clientParam' =+            clientParam+                { clientHooks =+                    (clientHooks clientParam)+                        { onCertificateRequest = \_ -> return $ Just cred+                        }+                }+        serverParam' =+            serverParam+                { serverWantClientCert = True+                , serverHooks =+                    (serverHooks serverParam)+                        { onClientCertificate = \_ -> return CertificateUsageAccept+                        }+                }+        shouldSucceed = KeyUsage_digitalSignature `elem` usageFlags+    if shouldSucceed+        then runTLSSimple (clientParam', serverParam')+        else runTLSFailure (clientParam', serverParam') handshake handshake++--------------------------------------------------------------++handshake_client_auth :: (ClientParams, ServerParams) -> IO ()+handshake_client_auth (clientParam, serverParam) = do+    let clientVersions = supportedVersions $ clientSupported clientParam+        serverVersions = supportedVersions $ serverSupported serverParam+        version = maximum (clientVersions `intersect` serverVersions)+    cred <- generate (arbitraryClientCredential version)+    let clientParam' =+            clientParam+                { clientHooks =+                    (clientHooks clientParam)+                        { onCertificateRequest = \_ -> return $ Just cred+                        }+                }+        serverParam' =+            serverParam+                { serverWantClientCert = True+                , serverHooks =+                    (serverHooks serverParam)+                        { onClientCertificate = validateChain cred+                        }+                }+    runTLSSimple (clientParam', serverParam')+  where+    validateChain cred chain+        | chain == fst cred = return CertificateUsageAccept+        | otherwise = return (CertificateUsageReject CertificateRejectUnknownCA)++handshake_client_auth_fail :: (ClientParams, ServerParams) -> IO ()+handshake_client_auth_fail (clientParam, serverParam) = do+    let clientVersions = supportedVersions $ clientSupported clientParam+        serverVersions = supportedVersions $ serverSupported serverParam+        version = maximum (clientVersions `intersect` serverVersions)+    cred <- generate (arbitraryClientCredential version)+    let clientParam' =+            clientParam+                { clientHooks =+                    (clientHooks clientParam)+                        { onCertificateRequest = \_ -> return $ Just cred+                        }+                }+        serverParam' =+            serverParam+                { serverWantClientCert = True+                , serverHooks =+                    (serverHooks serverParam)+                        { onClientCertificate = validateChain cred+                        }+                }+    runTLSFailure (clientParam', serverParam') handshake handshake+  where+    validateChain _ _ = return (CertificateUsageReject CertificateRejectUnknownCA)++--------------------------------------------------------------++handshake_ems :: (EMSMode, EMSMode) -> IO ()+handshake_ems (cems, sems) = do+    params <- generate arbitrary+    let params' = setEMSMode (cems, sems) params+        version = getConnectVersion params'+        emsVersion = version >= TLS10 && version <= TLS12+        use = cems /= NoEMS && sems /= NoEMS+        require = cems == RequireEMS || sems == RequireEMS+        p info = infoExtendedMainSecret info == (emsVersion && use)+    if emsVersion && require && not use+        then runTLSFailure params' handshake handshake+        else runTLSPredicate params' (maybe False p)++newtype CompatEMS = CompatEMS (EMSMode, EMSMode) deriving (Show)++instance Arbitrary CompatEMS where+    arbitrary = CompatEMS <$> (arbitrary `suchThat` compatible)+      where+        compatible (NoEMS, RequireEMS) = False+        compatible (RequireEMS, NoEMS) = False+        compatible _ = True++handshake_resumption_ems :: (CompatEMS, CompatEMS) -> IO ()+handshake_resumption_ems (CompatEMS ems, CompatEMS ems2) = do+    sessionRefs <- twoSessionRefs+    let sessionManagers = twoSessionManagers sessionRefs++    plainParams <- generate arbitrary+    let params =+            setEMSMode ems $+                setPairParamsSessionManagers sessionManagers plainParams++    runTLSSimple params++    -- and resume+    sessionParams <- readClientSessionRef sessionRefs+    expectJust "session param should be Just" sessionParams+    let params2 =+            setEMSMode ems2 $+                setPairParamsSessionResuming (fromJust sessionParams) params++    let version = getConnectVersion params2+        emsVersion = version >= TLS10 && version <= TLS12++    if emsVersion && use ems && not (use ems2)+        then runTLSFailure params2 handshake handshake+        else do+            runTLSSimple params2+            mSessionParams2 <- readClientSessionRef sessionRefs+            let sameSession = sessionParams == mSessionParams2+                sameUse = use ems == use ems2+            when emsVersion (sameSession `shouldBe` sameUse)+  where+    use (NoEMS, _) = False+    use (_, NoEMS) = False+    use _ = True++--------------------------------------------------------------++handshake_alpn :: (ClientParams, ServerParams) -> IO ()+handshake_alpn (clientParam, serverParam) = do+    let clientParam' =+            clientParam+                { clientHooks =+                    (clientHooks clientParam)+                        { onSuggestALPN = return $ Just ["h2", "http/1.1"]+                        }+                }+        serverParam' =+            serverParam+                { serverHooks =+                    (serverHooks serverParam)+                        { onALPNClientSuggest = Just alpn+                        }+                }+        params' = (clientParam', serverParam')+    runTLSSuccess params' hsClient hsServer+  where+    hsClient ctx = do+        handshake ctx+        proto <- getNegotiatedProtocol ctx+        proto `shouldBe` Just "h2"+    hsServer ctx = do+        handshake ctx+        proto <- getNegotiatedProtocol ctx+        proto `shouldBe` Just "h2"+    alpn xs+        | "h2" `elem` xs = return "h2"+        | otherwise = return "http/1.1"++handshake_sni :: (ClientParams, ServerParams) -> IO ()+handshake_sni (clientParam, serverParam) = do+    ref <- newIORef Nothing+    let clientParam' =+            clientParam+                { clientServerIdentification = (serverName, "")+                }+        serverParam' =+            serverParam+                { serverHooks =+                    (serverHooks serverParam)+                        { onServerNameIndication = onSNI ref+                        }+                }+        params' = (clientParam', serverParam')+    runTLSSuccess params' hsClient hsServer+    receivedName <- readIORef ref+    receivedName `shouldBe` Just (Just serverName)+  where+    hsClient ctx = do+        handshake ctx+        msni <- getClientSNI ctx+        expectMaybe "C: SNI should be Just" serverName msni+    hsServer ctx = do+        handshake ctx+        msni <- getClientSNI ctx+        expectMaybe "S: SNI should be Just" serverName msni+    onSNI ref name = do+        mx <- readIORef ref+        mx `shouldBe` Nothing+        writeIORef ref (Just name)+        return (Credentials [])+    serverName = "haskell.org"++--------------------------------------------------------------++newtype CSP12 = CSP12 (ClientParams, ServerParams) deriving (Show)++instance Arbitrary CSP12 where+    arbitrary = CSP12 <$> arbitraryPairParams12++handshake12_renegotiation :: CSP12 -> IO ()+handshake12_renegotiation (CSP12 (cparams, sparams)) = do+    renegDisabled <- generate arbitrary+    let sparams' =+            sparams+                { serverSupported =+                    (serverSupported sparams)+                        { supportedClientInitiatedRenegotiation = not renegDisabled+                        }+                }+    if renegDisabled+        then runTLSFailure (cparams, sparams') hsClient hsServer+        else runTLSSimple (cparams, sparams')+  where+    hsClient ctx = handshake ctx >> handshake ctx+    -- recvData receives the alert from the second handshake+    hsServer ctx = handshake ctx >> void (recvData ctx)++handshake12_session_resumption :: CSP12 -> IO ()+handshake12_session_resumption (CSP12 plainParams) = do+    sessionRefs <- twoSessionRefs+    let sessionManagers = twoSessionManagers sessionRefs++    let params = setPairParamsSessionManagers sessionManagers plainParams++    runTLSSimple params++    -- and resume+    sessionParams <- readClientSessionRef sessionRefs+    expectJust "session param should be Just" sessionParams+    let params2 = setPairParamsSessionResuming (fromJust sessionParams) params++    runTLSPredicate params2 (maybe False infoTLS12Resumption)++handshake12_session_ticket :: CSP12 -> IO ()+handshake12_session_ticket (CSP12 plainParams) = do+    sessionRefs <- twoSessionRefs+    let sessionManagers0 = twoSessionManagers sessionRefs+        sessionManagers = (fst sessionManagers0, oneSessionTicket)++    let params = setPairParamsSessionManagers sessionManagers plainParams++    runTLSSimple params++    -- and resume+    sessionParams <- readClientSessionRef sessionRefs+    expectJust "session param should be Just" sessionParams+    let params2 = setPairParamsSessionResuming (fromJust sessionParams) params++    runTLSPredicate params2 (maybe False infoTLS12Resumption)++--------------------------------------------------------------++handshake13_full :: CSP13 -> IO ()+handshake13_full (CSP13 (cli, srv)) = do+    let cliSupported =+            defaultSupported+                { supportedCiphers = [cipher13_AES_128_GCM_SHA256]+                , supportedGroups = [X25519]+                }+        svrSupported =+            defaultSupported+                { supportedCiphers = [cipher13_AES_128_GCM_SHA256]+                , supportedGroups = [X25519]+                , supportedGroupsTLS13 = [[X25519]]+                }+        params =+            ( cli{clientSupported = cliSupported}+            , srv{serverSupported = svrSupported}+            )+    runTLSSimple13 params FullHandshake++handshake13_hrr :: CSP13 -> IO ()+handshake13_hrr (CSP13 (cli, srv)) = do+    let cliSupported =+            defaultSupported+                { supportedCiphers = [cipher13_AES_128_GCM_SHA256]+                , supportedGroups = [P256, X25519]+                }+        svrSupported =+            defaultSupported+                { supportedCiphers = [cipher13_AES_128_GCM_SHA256]+                , supportedGroups = [X25519]+                , supportedGroupsTLS13 = [[X25519]]+                }+        params =+            ( cli{clientSupported = cliSupported}+            , srv{serverSupported = svrSupported}+            )+    runTLSSimple13 params HelloRetryRequest++handshake13_psk :: CSP13 -> IO ()+handshake13_psk (CSP13 (cli, srv)) = do+    let cliSupported =+            defaultSupported+                { supportedCiphers = [cipher13_AES_128_GCM_SHA256]+                , supportedGroups = [P256, X25519]+                }+        svrSupported =+            defaultSupported+                { supportedCiphers = [cipher13_AES_128_GCM_SHA256]+                , supportedGroups = [X25519]+                , supportedGroupsTLS13 = [[X25519]]+                }+        params0 =+            ( cli{clientSupported = cliSupported}+            , srv{serverSupported = svrSupported}+            )++    sessionRefs <- twoSessionRefs+    let sessionManagers = twoSessionManagers sessionRefs++    let params = setPairParamsSessionManagers sessionManagers params0++    runTLSSimple13 params HelloRetryRequest++    -- and resume+    sessionParams <- readClientSessionRef sessionRefs+    expectJust "session param should be Just" sessionParams+    let params2 = setPairParamsSessionResuming (fromJust sessionParams) params++    runTLSSimple13 params2 PreSharedKey++handshake13_psk_ticket :: CSP13 -> IO ()+handshake13_psk_ticket (CSP13 (cli, srv)) = do+    let cliSupported =+            defaultSupported+                { supportedCiphers = [cipher13_AES_128_GCM_SHA256]+                , supportedGroups = [P256, X25519]+                }+        svrSupported =+            defaultSupported+                { supportedCiphers = [cipher13_AES_128_GCM_SHA256]+                , supportedGroups = [X25519]+                , supportedGroupsTLS13 = [[X25519]]+                }+        params0 =+            ( cli{clientSupported = cliSupported}+            , srv{serverSupported = svrSupported}+            )++    sessionRefs <- twoSessionRefs+    let sessionManagers0 = twoSessionManagers sessionRefs+        sessionManagers = (fst sessionManagers0, oneSessionTicket)++    let params = setPairParamsSessionManagers sessionManagers params0++    runTLSSimple13 params HelloRetryRequest++    -- and resume+    sessionParams <- readClientSessionRef sessionRefs+    expectJust "session param should be Just" sessionParams+    let params2 = setPairParamsSessionResuming (fromJust sessionParams) params++    runTLSSimple13 params2 PreSharedKey++handshake13_psk_fallback :: CSP13 -> IO ()+handshake13_psk_fallback (CSP13 (cli, srv)) = do+    let cliSupported =+            defaultSupported+                { supportedCiphers =+                    [ cipher13_AES_128_GCM_SHA256+                    , cipher13_AES_128_CCM_SHA256+                    ]+                , supportedGroups = [P256, X25519]+                }+        svrSupported =+            defaultSupported+                { supportedCiphers = [cipher13_AES_128_GCM_SHA256]+                , supportedGroups = [X25519]+                , supportedGroupsTLS13 = [[X25519]]+                }+        params0 =+            ( cli{clientSupported = cliSupported}+            , srv{serverSupported = svrSupported}+            )++    sessionRefs <- twoSessionRefs+    let sessionManagers = twoSessionManagers sessionRefs++    let params = setPairParamsSessionManagers sessionManagers params0++    runTLSSimple13 params HelloRetryRequest++    -- resumption fails because GCM cipher is not supported anymore, full+    -- handshake is not possible because X25519 has been removed, so we are+    -- back with P256 after hello retry+    sessionParams <- readClientSessionRef sessionRefs+    expectJust "session param should be Just" sessionParams+    let (cli2, srv2) = setPairParamsSessionResuming (fromJust sessionParams) params+        srv2' =+            srv2{serverSupported = svrSupported'}+        svrSupported' =+            defaultSupported+                { supportedCiphers = [cipher13_AES_128_CCM_SHA256]+                , supportedGroups = [P256]+                , supportedGroupsTLS13 = [[P256]]+                }++    runTLSSimple13 (cli2, srv2') HelloRetryRequest++handshake13_0rtt :: CSP13 -> IO ()+handshake13_0rtt (CSP13 (cli, srv)) = do+    let cliSupported =+            defaultSupported+                { supportedCiphers = [cipher13_AES_128_GCM_SHA256]+                , supportedGroups = [P256, X25519]+                }+        svrSupported =+            defaultSupported+                { supportedCiphers = [cipher13_AES_128_GCM_SHA256]+                , supportedGroups = [X25519]+                , supportedGroupsTLS13 = [[X25519]]+                }+        cliHooks =+            defaultClientHooks+                { onSuggestALPN = return $ Just ["h2"]+                }+        svrHooks =+            defaultServerHooks+                { onALPNClientSuggest = Just (return . unsafeHead)+                }+        params0 =+            ( cli+                { clientSupported = cliSupported+                , clientHooks = cliHooks+                }+            , srv+                { serverSupported = svrSupported+                , serverHooks = svrHooks+                , serverEarlyDataSize = 2048+                }+            )++    sessionRefs <- twoSessionRefs+    let sessionManagers = twoSessionManagers sessionRefs++    let params = setPairParamsSessionManagers sessionManagers params0++    runTLSSimple13 params HelloRetryRequest+    runTLS0rtt params sessionRefs+    runTLS0rtt params sessionRefs+  where+    runTLS0rtt params sessionRefs = do+        -- and resume+        sessionParams <- readClientSessionRef sessionRefs+        expectJust "session param should be Just" sessionParams+        clearClientSessionRef sessionRefs+        earlyData <- B.pack <$> generate (someWords8 256)+        let (pc, ps) = setPairParamsSessionResuming (fromJust sessionParams) params+            params2 = (pc{clientUseEarlyData = True}, ps)++        runTLS0RTT params2 RTT0 earlyData++handshake13_0rtt_fallback :: CSP13 -> IO ()+handshake13_0rtt_fallback (CSP13 (cli, srv)) = do+    group0 <- generate $ elements [P256, X25519]+    let cliSupported =+            defaultSupported+                { supportedCiphers = [cipher13_AES_128_GCM_SHA256]+                , supportedGroups = [P256, X25519]+                }+        svrSupported =+            defaultSupported+                { supportedCiphers = [cipher13_AES_128_GCM_SHA256]+                , supportedGroups = [group0]+                , supportedGroupsTLS13 = [[group0]]+                }+        params =+            ( cli{clientSupported = cliSupported}+            , srv+                { serverSupported = svrSupported+                , serverEarlyDataSize = 1024+                }+            )++    sessionRefs <- twoSessionRefs+    let sessionManagers = twoSessionManagers sessionRefs++    let params0 = setPairParamsSessionManagers sessionManagers params++    let mode = if group0 == P256 then FullHandshake else HelloRetryRequest+    runTLSSimple13 params0 mode++    -- and resume+    mSessionParams <- readClientSessionRef sessionRefs+    case mSessionParams of+        Nothing -> expectationFailure "session params: Just is expected"+        Just sessionParams -> do+            earlyData <- B.pack <$> generate (someWords8 256)+            group1 <- generate $ elements [P256, X25519]+            let (pc, ps) = setPairParamsSessionResuming sessionParams params0+                svrSupported1 =+                    defaultSupported+                        { supportedCiphers = [cipher13_AES_128_GCM_SHA256]+                        , supportedGroups = [group1]+                        , supportedGroupsTLS13 = [[group1]]+                        }+                params1 =+                    ( pc{clientUseEarlyData = True}+                    , ps+                        { serverEarlyDataSize = 0+                        , serverSupported = svrSupported1+                        }+                    )+            -- C: [P256, X25519]+            -- S: [group0]+            -- C: [P256, X25519]+            -- S: [group1]+            if group0 == group1+                -- 0-RTT is not allowed, so fallback to PreSharedKey+                then runTLS0RTT params1 PreSharedKey earlyData+                -- HRR but not allowed for 0-RTT+                else runTLSFailure params1 (tlsClient earlyData) tlsServer+  where+    tlsClient earlyData ctx = do+        handshake ctx+        sendData ctx $ L.fromStrict earlyData+        _ <- recvData ctx+        bye ctx+    tlsServer ctx = do+        handshake ctx+        _ <- recvData ctx+        bye ctx++handshake13_ee_groups :: CSP13 -> IO ()+handshake13_ee_groups (CSP13 (cli, srv)) = do+    let -- The client prefers P256+        cliSupported = (clientSupported cli){supportedGroups = [P256, X25519]}+        -- The server prefers X25519+        svrSupported =+            (serverSupported srv)+                { supportedGroups = [X25519, P256]+                , supportedGroupsTLS13 = [[X25519, P256]]+                }+        params =+            ( cli{clientSupported = cliSupported}+            , srv{serverSupported = svrSupported}+            )+    (_, serverMessages) <- runTLSCapture13 params+    -- The server should tell X25519 in supported_groups in EE to client+    let isSupportedGroups (ExtensionRaw eid _) = eid == EID_SupportedGroups+        eeMessagesHaveExt =+            [ any isSupportedGroups exts+            | EncryptedExtensions13 exts <- serverMessages+            ]+    eeMessagesHaveExt `shouldBe` [True]++handshake13_ec :: CSP13 -> IO ()+handshake13_ec (CSP13 (cli, srv)) = do+    EC cgrps <- generate arbitrary+    EC sgrps <- generate arbitrary+    let cliSupported = (clientSupported cli){supportedGroups = cgrps}+        svrSupported =+            (serverSupported srv)+                { supportedGroups = sgrps+                , supportedGroupsTLS13 = [sgrps]+                }+        params =+            ( cli{clientSupported = cliSupported}+            , srv{serverSupported = svrSupported}+            )+    runTLSSimple13 params FullHandshake++handshake13_ffdhe :: CSP13 -> IO ()+handshake13_ffdhe (CSP13 (cli, srv)) = do+    FFDHE cgrps <- generate arbitrary+    FFDHE sgrps <- generate arbitrary+    let cliSupported = (clientSupported cli){supportedGroups = cgrps}+        svrSupported =+            (serverSupported srv)+                { supportedGroups = sgrps+                , supportedGroupsTLS13 = [sgrps]+                }+        params =+            ( cli{clientSupported = cliSupported}+            , srv{serverSupported = svrSupported}+            )+    runTLSSimple13 params FullHandshake++post_handshake_auth :: CSP13 -> IO ()+post_handshake_auth (CSP13 (clientParam, serverParam)) = do+    cred <- generate (arbitraryClientCredential TLS13)+    let clientParam' =+            clientParam+                { clientHooks =+                    (clientHooks clientParam)+                        { onCertificateRequest = \_ -> return $ Just cred+                        }+                }+        serverParam' =+            serverParam+                { serverHooks =+                    (serverHooks serverParam)+                        { onClientCertificate = validateChain cred+                        }+                }+    if isCredentialDSA cred+        then runTLSFailure (clientParam', serverParam') hsClient hsServer+        else runTLSSuccess (clientParam', serverParam') hsClient hsServer+  where+    validateChain cred chain+        | chain == fst cred = return CertificateUsageAccept+        | otherwise = return (CertificateUsageReject CertificateRejectUnknownCA)+    hsClient ctx = do+        handshake ctx+        sendData ctx "request 1"+        recvDataAssert ctx "response 1"+        sendData ctx "request 2"+        recvDataAssert ctx "response 2"+    hsServer ctx = do+        handshake ctx+        recvDataAssert ctx "request 1"+        _ <- requestCertificate ctx -- single request+        sendData ctx "response 1"+        recvDataAssert ctx "request 2"+        _ <- requestCertificate ctx+        _ <- requestCertificate ctx -- two simultaneously+        sendData ctx "response 2"++expectJust :: String -> Maybe a -> Expectation+expectJust tag mx = case mx of+    Nothing -> expectationFailure tag+    Just _ -> return ()
+ test/PipeChan.hs view
@@ -0,0 +1,85 @@+{-# LANGUAGE RecordWildCards #-}++-- create a similar concept than a unix pipe.+module PipeChan (+    PipeChan (..),+    newPipe,+    runPipe,+    readPipeC,+    readPipeS,+    writePipeC,+    writePipeS,+) where++import Control.Concurrent+import Control.Monad (forever)+import Data.ByteString (ByteString)+import qualified Data.ByteString as B+import Data.IORef++----------------------------------------------------------------++-- | represent a unidirectional pipe with a buffered read channel and+-- a write channel+data UniPipeChan = UniPipeChan+    { getReadUniPipe :: Chan ByteString+    , getWriteUniPipe :: Chan ByteString+    }++newUniPipeChan :: IO UniPipeChan+newUniPipeChan = UniPipeChan <$> newChan <*> newChan++runUniPipe :: UniPipeChan -> IO ThreadId+runUniPipe UniPipeChan{..} =+    forkIO $+        forever $+            readChan getReadUniPipe >>= writeChan getWriteUniPipe++----------------------------------------------------------------++-- | Represent a bidirectional pipe with 2 nodes A and B+data PipeChan = PipeChan+    { fromC :: IORef ByteString+    , fromS :: IORef ByteString+    , c2s :: UniPipeChan+    , s2c :: UniPipeChan+    }++newPipe :: IO PipeChan+newPipe =+    PipeChan+        <$> newIORef B.empty+        <*> newIORef B.empty+        <*> newUniPipeChan+        <*> newUniPipeChan++runPipe :: PipeChan -> IO (ThreadId, ThreadId)+runPipe PipeChan{..} = (,) <$> runUniPipe c2s <*> runUniPipe s2c++readPipeC :: PipeChan -> Int -> IO ByteString+readPipeC PipeChan{..} sz = readBuffered fromS (getWriteUniPipe s2c) sz++writePipeC :: PipeChan -> ByteString -> IO ()+writePipeC PipeChan{..} = writeChan $ getWriteUniPipe c2s++readPipeS :: PipeChan -> Int -> IO ByteString+readPipeS PipeChan{..} sz = readBuffered fromC (getWriteUniPipe c2s) sz++writePipeS :: PipeChan -> ByteString -> IO ()+writePipeS PipeChan{..} = writeChan $ getReadUniPipe s2c++-- helper to read buffered data.+readBuffered :: IORef ByteString -> Chan ByteString -> Int -> IO ByteString+readBuffered ref chan sz = do+    left <- readIORef ref+    if B.length left >= sz+        then do+            let (ret, nleft) = B.splitAt sz left+            writeIORef ref nleft+            return ret+        else do+            let newSize = sz - B.length left+            newData <- readChan chan+            writeIORef ref newData+            remain <- readBuffered ref chan newSize+            return (left `B.append` remain)
+ test/PubKey.hs view
@@ -0,0 +1,123 @@+module PubKey (+    arbitraryRSAPair,+    arbitraryDSAPair,+    arbitraryECDSAPair,+    arbitraryEd25519Pair,+    arbitraryEd448Pair,+    globalRSAPair,+    getGlobalRSAPair,+    knownECCurves,+    defaultECCurve,+    dsaParams,+    rsaParams,+) where++import Control.Concurrent.MVar+import Crypto.Error+import qualified Crypto.PubKey.DSA as DSA+import qualified Crypto.PubKey.ECC.ECDSA as ECDSA+import qualified Crypto.PubKey.ECC.Prim as ECC+import qualified Crypto.PubKey.ECC.Types as ECC+import qualified Crypto.PubKey.Ed25519 as Ed25519+import qualified Crypto.PubKey.Ed448 as Ed448+import qualified Crypto.PubKey.RSA as RSA+import Crypto.Random+import qualified Data.ByteString as B+import System.IO.Unsafe+import Test.QuickCheck++arbitraryRSAPair :: Gen (RSA.PublicKey, RSA.PrivateKey)+arbitraryRSAPair = (rngToRSA . drgNewTest) `fmap` arbitrary+  where+    rngToRSA :: ChaChaDRG -> (RSA.PublicKey, RSA.PrivateKey)+    rngToRSA rng = fst $ withDRG rng arbitraryRSAPairWithRNG++arbitraryRSAPairWithRNG :: MonadRandom m => m (RSA.PublicKey, RSA.PrivateKey)+arbitraryRSAPairWithRNG = RSA.generate 256 0x10001++{-# NOINLINE globalRSAPair #-}+globalRSAPair :: MVar (RSA.PublicKey, RSA.PrivateKey)+globalRSAPair = unsafePerformIO $ do+    drg <- drgNew+    newMVar (fst $ withDRG drg arbitraryRSAPairWithRNG)++{-# NOINLINE getGlobalRSAPair #-}+getGlobalRSAPair :: (RSA.PublicKey, RSA.PrivateKey)+getGlobalRSAPair = unsafePerformIO (readMVar globalRSAPair)++rsaParams :: (RSA.PublicKey, RSA.PrivateKey)+rsaParams = (pub, priv)+  where+    priv =+        RSA.PrivateKey+            { RSA.private_pub = pub+            , RSA.private_d = d+            , RSA.private_p = 0+            , RSA.private_q = 0+            , RSA.private_dP = 0+            , RSA.private_dQ = 0+            , RSA.private_qinv = 0+            }+    pub =+        RSA.PublicKey+            { RSA.public_size = 1024 `div` 8+            , RSA.public_n = n+            , RSA.public_e = e+            }+    n =+        0x00c086b4c6db28ae578d73766d6fdd04b913808a85bf9ad7bcfc9a6ff04d13d2ff75f761ce7db9ee8996e29dc433d19a2d3f748e8d368ba099781d58276e1863a324ae3fb1a061874cd9f3510e54e49727c68de0616964335371cfb63f15ebff8ce8df09c74fb8625f8f58548b90f079a3405f522e738e664d0c645b015664f7c7+    e = 0x10001+    d =+        0x3edc3cae28e4717818b1385ba7088d0038c3e176a606d2a5dbfc38cc46fe500824e62ec312fde04a803f61afac13a5b95c5c9c26b346879b54429083df488b4f29bb7b9722d366d6f5d2b512150a2e950eacfe0fd9dd56b87b0322f74ae3c8d8674ace62bc723f7c05e9295561efd70d7a924c6abac2e482880fc0149d5ad481++dsaParams :: DSA.Params+dsaParams =+    DSA.Params+        { DSA.params_p =+            0x009f356bbc4750645555b02aa3918e85d5e35bdccd56154bfaa3e1801d5fe0faf65355215148ea866d5732fd27eb2f4d222c975767d2eb573513e460eceae327c8ac5da1f4ce765c49a39cae4c904b4e5cc64554d97148f20a2655027a0cf8f70b2550cc1f0c9861ce3a316520ab0588407ea3189d20c78bd52df97e56cbe0bbeb+        , DSA.params_q = 0x00f33a57b47de86ff836f9fe0bb060c54ab293133b+        , DSA.params_g =+            0x3bb973c4f6eee92d1530f250487735595d778c2e5c8147d67a46ebcba4e6444350d49da8e7da667f9b1dbb22d2108870b9fcfabc353cdfac5218d829f22f69130317cc3b0d724881e34c34b8a2571d411da6458ef4c718df9e826f73e16a035b1dcbc1c62cac7a6604adb3e7930be8257944c6dfdddd655004b98253185775ff+        }++arbitraryDSAPair :: Gen (DSA.PublicKey, DSA.PrivateKey)+arbitraryDSAPair = do+    priv <- choose (1, DSA.params_q dsaParams)+    let pub = DSA.calculatePublic dsaParams priv+    return (DSA.PublicKey dsaParams pub, DSA.PrivateKey dsaParams priv)++-- for performance reason P521 is not tested+knownECCurves :: [ECC.CurveName]+knownECCurves =+    [ ECC.SEC_p256r1+    , ECC.SEC_p384r1+    , ECC.SEC_p521r1+    ]++defaultECCurve :: ECC.CurveName+defaultECCurve = ECC.SEC_p256r1++arbitraryECDSAPair :: ECC.CurveName -> Gen (ECDSA.PublicKey, ECDSA.PrivateKey)+arbitraryECDSAPair curveName = do+    d <- choose (1, n - 1)+    let p = ECC.pointBaseMul curve d+    return (ECDSA.PublicKey curve p, ECDSA.PrivateKey curve d)+  where+    curve = ECC.getCurveByName curveName+    n = ECC.ecc_n . ECC.common_curve $ curve++arbitraryEd25519Pair :: Gen (Ed25519.PublicKey, Ed25519.SecretKey)+arbitraryEd25519Pair = do+    bytes <- vectorOf 32 arbitrary+    let priv = fromCryptoPassed $ Ed25519.secretKey (B.pack bytes)+    return (Ed25519.toPublic priv, priv)++arbitraryEd448Pair :: Gen (Ed448.PublicKey, Ed448.SecretKey)+arbitraryEd448Pair = do+    bytes <- vectorOf 57 arbitrary+    let priv = fromCryptoPassed $ Ed448.secretKey (B.pack bytes)+    return (Ed448.toPublic priv, priv)++fromCryptoPassed :: CryptoFailable a -> a+fromCryptoPassed (CryptoPassed x) = x+fromCryptoPassed _ = error "fromCryptoPassed"
+ test/Run.hs view
@@ -0,0 +1,394 @@+{-# LANGUAGE OverloadedStrings #-}+{-# OPTIONS_GHC -Wno-orphans #-}++module Run (+    runTLS,+    runTLSSimple,+    runTLSPredicate,+    runTLSSimple13,+    runTLSSimple13ECH,+    runTLS0RTT,+    runTLS0RTTech,+    runTLSSimpleKeyUpdate,+    runTLSCaptureFail,+    runTLSCapture13,+    runTLSSuccess,+    runTLSFailure,+    expectMaybe,+    newPairContext,+    withDataPipe,+    byeBye,+) where++import Control.Concurrent+import Control.Concurrent.Async+import qualified Control.Exception as E+import Control.Monad+import Data.ByteString (ByteString)+import qualified Data.ByteString as B+import qualified Data.ByteString.Lazy as L+import Data.IORef+import Network.TLS+import System.Timeout+import Test.Hspec+import Test.QuickCheck++import API+import Arbitrary+import PipeChan++type ClinetWithInput = Chan ByteString -> Context -> IO ()+type ServerWithOutput = Context -> Chan [ByteString] -> IO ()++----------------------------------------------------------------++runTLS+    :: (ClientParams, ServerParams)+    -> ClinetWithInput+    -> ServerWithOutput+    -> IO ()+runTLS = runTLSN 1++runTLSN+    :: Int+    -> (ClientParams, ServerParams)+    -> ClinetWithInput+    -> ServerWithOutput+    -> IO ()+runTLSN n params tlsClient tlsServer = do+    inputChan <- newChan+    outputChan <- newChan+    -- generate some data to send+    ds <- replicateM n $ B.pack <$> generate (someWords8 256)+    forM_ ds $ writeChan inputChan+    -- run client and server+    withPairContext params $ \(cCtx, sCtx) ->+        concurrently_ (server sCtx outputChan) (client inputChan cCtx)+    -- read result+    mDs <- timeout 1000000 $ readChan outputChan -- 60 sec+    expectMaybe "timeout" ds mDs+  where+    server sCtx outputChan =+        E.catch+            (tlsServer sCtx outputChan)+            (printAndRaise "S: " (serverSupported $ snd params))+    client inputChan cCtx =+        E.catch+            (tlsClient inputChan cCtx)+            (printAndRaise "C: " (clientSupported $ fst params))+    printAndRaise :: String -> Supported -> E.SomeException -> IO ()+    printAndRaise s supported e = do+        putStrLn $+            s+                ++ " exception: "+                ++ show e+                ++ ", supported: "+                ++ show supported+        E.throwIO e++----------------------------------------------------------------++runTLSSimple :: (ClientParams, ServerParams) -> IO ()+runTLSSimple params = runTLSPredicate params (const True)++runTLSPredicate+    :: (ClientParams, ServerParams) -> (Maybe Information -> Bool) -> IO ()+runTLSPredicate params p = runTLSSuccess params hsClient hsServer+  where+    hsClient ctx = do+        handshake ctx+        checkInfoPredicate ctx+    hsServer ctx = do+        handshake ctx+        checkInfoPredicate ctx+    checkInfoPredicate ctx = do+        minfo <- contextGetInformation ctx+        unless (p minfo) $+            fail ("unexpected information: " ++ show minfo)++----------------------------------------------------------------++runTLSSimple13+    :: (ClientParams, ServerParams)+    -> HandshakeMode13+    -> IO ()+runTLSSimple13 params mode =+    runTLSSuccess params hsClient hsServer+  where+    hsClient ctx = do+        handshake ctx+        mmode <- (>>= infoTLS13HandshakeMode) <$> contextGetInformation ctx+        expectMaybe "C: mode should be Just" mode mmode+    hsServer ctx = do+        handshake ctx+        mmode <- (>>= infoTLS13HandshakeMode) <$> contextGetInformation ctx+        expectMaybe "S: mode should be Just" mode mmode++runTLSSimple13ECH+    :: (ClientParams, ServerParams)+    -> HandshakeMode13+    -> IO ()+runTLSSimple13ECH params mode =+    runTLSSuccess params hsClient hsServer+  where+    hsClient ctx = do+        handshake ctx+        minfo <- contextGetInformation ctx+        let mmode = minfo >>= infoTLS13HandshakeMode+            maccepted = infoIsECHAccepted <$> minfo+        expectMaybe "C: mode should be Just" mode mmode+        expectMaybe "C: TLS accepted should be Just" True maccepted+    hsServer ctx = do+        handshake ctx+        mmode <- (>>= infoTLS13HandshakeMode) <$> contextGetInformation ctx+        expectMaybe "S: mode should be Just" mode mmode++runTLS0RTT+    :: (ClientParams, ServerParams)+    -> HandshakeMode13+    -> ByteString+    -> IO ()+runTLS0RTT params mode earlyData =+    withPairContext params $ \(cCtx, sCtx) ->+        concurrently_ (tlsServer sCtx) (tlsClient cCtx)+  where+    tlsClient ctx = do+        handshake ctx+        sendData ctx $ L.fromStrict earlyData+        _ <- recvData ctx+        bye ctx+        mmode <- (>>= infoTLS13HandshakeMode) <$> contextGetInformation ctx+        expectMaybe "C: mode should be Just" mode mmode+    tlsServer ctx = do+        handshake ctx+        let ls = chunkLengths $ B.length earlyData+        chunks <- replicateM (length ls) $ recvData ctx+        (map B.length chunks, B.concat chunks) `shouldBe` (ls, earlyData)+        sendData ctx $ L.fromStrict earlyData+        bye ctx+        mmode <- (>>= infoTLS13HandshakeMode) <$> contextGetInformation ctx+        expectMaybe "S: mode should be Just" mode mmode+    chunkLengths :: Int -> [Int]+    chunkLengths len+        | len > 16384 = 16384 : chunkLengths (len - 16384)+        | len > 0 = [len]+        | otherwise = []++runTLS0RTTech+    :: (ClientParams, ServerParams)+    -> HandshakeMode13+    -> ByteString+    -> IO ()+runTLS0RTTech params mode earlyData =+    withPairContext params $ \(cCtx, sCtx) ->+        concurrently_ (tlsServer sCtx) (tlsClient cCtx)+  where+    tlsClient ctx = do+        handshake ctx+        sendData ctx $ L.fromStrict earlyData+        _ <- recvData ctx+        bye ctx+        minfo <- contextGetInformation ctx+        let mmode = minfo >>= infoTLS13HandshakeMode+            maccepted = infoIsECHAccepted <$> minfo+        expectMaybe "C: mode should be Just" mode mmode+        expectMaybe "C: TLS accepted should be Just" True maccepted+    tlsServer ctx = do+        handshake ctx+        let ls = chunkLengths $ B.length earlyData+        chunks <- replicateM (length ls) $ recvData ctx+        (map B.length chunks, B.concat chunks) `shouldBe` (ls, earlyData)+        sendData ctx $ L.fromStrict earlyData+        bye ctx+        mmode <- (>>= infoTLS13HandshakeMode) <$> contextGetInformation ctx+        expectMaybe "S: mode should be Just" mode mmode+    chunkLengths :: Int -> [Int]+    chunkLengths len+        | len > 16384 = 16384 : chunkLengths (len - 16384)+        | len > 0 = [len]+        | otherwise = []++expectMaybe :: (Show a, Eq a) => String -> a -> Maybe a -> Expectation+expectMaybe tag e mx = case mx of+    Nothing -> expectationFailure tag+    Just x -> x `shouldBe` e++runTLSCaptureFail+    :: (ClientParams, ServerParams) -> IO ([Handshake], [Handshake])+runTLSCaptureFail params = do+    sRef <- newIORef []+    cRef <- newIORef []+    runTLSFailure params (hsClient cRef) (hsServer sRef)+    sReceived <- readIORef sRef+    cReceived <- readIORef cRef+    return (reverse sReceived, reverse cReceived)+  where+    hsClient ref ctx = do+        installHook ctx ref+        handshake ctx+        sendData ctx "Foo"+    hsServer ref ctx = do+        installHook ctx ref+        handshake ctx+        _ <- recvData ctx+        return ()+    installHook ctx ref =+        let recv hss = modifyIORef ref (hss :) >> return hss+         in contextHookSetHandshakeRecv ctx recv++runTLSCapture13+    :: (ClientParams, ServerParams) -> IO ([Handshake13], [Handshake13])+runTLSCapture13 params = do+    sRef <- newIORef []+    cRef <- newIORef []+    runTLSSuccess params (hsClient cRef) (hsServer sRef)+    sReceived <- readIORef sRef+    cReceived <- readIORef cRef+    return (reverse sReceived, reverse cReceived)+  where+    hsClient ref ctx = do+        installHook ctx ref+        handshake ctx+    hsServer ref ctx = do+        installHook ctx ref+        handshake ctx+    installHook ctx ref =+        let recv hss = modifyIORef ref (hss :) >> return hss+         in contextHookSetHandshake13Recv ctx recv++runTLSSimpleKeyUpdate :: (ClientParams, ServerParams) -> IO ()+runTLSSimpleKeyUpdate params = runTLSN 3 params tlsClient tlsServer+  where+    tlsClient queue ctx = do+        handshake ctx+        d0 <- readChan queue+        sendData ctx (L.fromChunks [d0])+        d1 <- readChan queue+        sendData ctx (L.fromChunks [d1])+        req <- generate $ elements [OneWay, TwoWay]+        _ <- updateKey ctx req+        d2 <- readChan queue+        sendData ctx (L.fromChunks [d2])+        checkCtxFinished ctx+        bye ctx+    tlsServer ctx queue = do+        handshake ctx+        d0 <- recvData ctx+        req <- generate $ elements [OneWay, TwoWay]+        _ <- updateKey ctx req+        d1 <- recvData ctx+        d2 <- recvData ctx+        writeChan queue [d0, d1, d2]+        checkCtxFinished ctx+        bye ctx++----------------------------------------------------------------++runTLSSuccess+    :: (ClientParams, ServerParams)+    -> (Context -> IO ())+    -> (Context -> IO ())+    -> IO ()+runTLSSuccess params hsClient hsServer = runTLS params tlsClient tlsServer+  where+    tlsClient queue ctx = do+        hsClient ctx+        d <- readChan queue+        sendData ctx (L.fromChunks [d])+        checkCtxFinished ctx+        bye ctx+    tlsServer ctx queue = do+        hsServer ctx+        d <- recvData ctx+        writeChan queue [d]+        checkCtxFinished ctx+        bye ctx++runTLSFailure+    :: (ClientParams, ServerParams)+    -> (Context -> IO c)+    -> (Context -> IO s)+    -> IO ()+runTLSFailure params hsClient hsServer =+    withPairContext params $ \(cCtx, sCtx) ->+        concurrently_ (tlsServer sCtx) (tlsClient cCtx)+  where+    tlsClient ctx = hsClient ctx `shouldThrow` anyTLSException+    tlsServer ctx = hsServer ctx `shouldThrow` anyTLSException++anyTLSException :: Selector TLSException+anyTLSException = const True++----------------------------------------------------------------++debug :: Bool+debug = False++withPairContext+    :: (ClientParams, ServerParams) -> ((Context, Context) -> IO ()) -> IO ()+withPairContext params body =+    E.bracket+        (newPairContext params)+        (\((t1, t2), _) -> killThread t1 >> killThread t2)+        (\(_, ctxs) -> body ctxs)++newPairContext+    :: (ClientParams, ServerParams)+    -> IO ((ThreadId, ThreadId), (Context, Context))+newPairContext (cParams, sParams) = do+    pipe <- newPipe+    tids <- runPipe pipe+    let noFlush = return ()+    let noClose = return ()++    let cBackend = Backend noFlush noClose (writePipeC pipe) (readPipeC pipe)+    let sBackend = Backend noFlush noClose (writePipeS pipe) (readPipeS pipe)+    cCtx' <- contextNew cBackend cParams+    sCtx' <- contextNew sBackend sParams++    contextHookSetLogging cCtx' (logging "client: ")+    contextHookSetLogging sCtx' (logging "server: ")++    return (tids, (cCtx', sCtx'))+  where+    logging pre =+        if debug+            then+                defaultLogging+                    { loggingPacketSent = putStrLn . ((pre ++ ">> ") ++)+                    , loggingPacketRecv = putStrLn . ((pre ++ "<< ") ++)+                    }+            else defaultLogging+++withDataPipe :: (ClientParams, ServerParams) -> (Context -> Chan result -> IO ()) -> (Chan start -> Context -> IO ()) -> ((start -> IO (), IO result) -> IO a) -> IO a+withDataPipe params tlsServer tlsClient cont = do+    -- initial setup+    startQueue  <- newChan+    resultQueue <- newChan++    (cCtx, sCtx) <- snd <$> newPairContext params++    withAsync (E.catch (tlsServer sCtx resultQueue)+                       (printAndRaise "server" (serverSupported $ snd params))) $ \sAsync -> withAsync (E.catch (tlsClient startQueue cCtx)+                                (printAndRaise "client" (clientSupported $ fst params))) $ \cAsync -> do+      let readResult = waitBoth cAsync sAsync >> readChan resultQueue+      cont (writeChan startQueue, readResult)++  where+        printAndRaise :: String -> Supported -> E.SomeException -> IO ()+        printAndRaise s supported e = do+            putStrLn $ s ++ " exception: " ++ show e +++                            ", supported: " ++ show supported+            E.throwIO e++-- Terminate the write direction and wait to receive the peer EOF.  This is+-- necessary in situations where we want to confirm the peer status, or to make+-- sure to receive late messages like session tickets.  In the test suite this+-- is used each time application code ends the connection without prior call to+-- 'recvData'.+byeBye :: Context -> IO ()+byeBye ctx = do+    bye ctx+    bs <- recvData ctx+    unless (B.null bs) $ fail "byeBye: unexpected application data"
+ test/Session.hs view
@@ -0,0 +1,92 @@+{-# OPTIONS_GHC -Wno-orphans #-}++module Session (+    readClientSessionRef,+    clearClientSessionRef,+    twoSessionRefs,+    twoSessionManagers,+    setPairParamsSessionManagers,+    setPairParamsSessionResuming,+    oneSessionTicket,+) where++import Codec.Serialise+import Control.Monad+import qualified Data.ByteString.Lazy as L+import Data.IORef+import Network.TLS+import Network.TLS.Internal++----------------------------------------------------------------++readClientSessionRef :: (IORef (Maybe c), IORef (Maybe s)) -> IO (Maybe c)+readClientSessionRef refs = readIORef (fst refs)++clearClientSessionRef :: (IORef (Maybe c), IORef (Maybe s)) -> IO ()+clearClientSessionRef refs = writeIORef (fst refs) Nothing++twoSessionRefs :: IO (IORef (Maybe client), IORef (Maybe server))+twoSessionRefs = (,) <$> newIORef Nothing <*> newIORef Nothing++-- | simple session manager to store one session id and session data for a single thread.+-- a Real concurrent session manager would use an MVar and have multiples items.+oneSessionManager :: IORef (Maybe (SessionID, SessionData)) -> SessionManager+oneSessionManager ref =+    noSessionManager+        { sessionResume = \myId -> readIORef ref >>= maybeResume False myId+        , sessionResumeOnlyOnce = \myId -> readIORef ref >>= maybeResume True myId+        , sessionEstablish = \myId dat -> writeIORef ref (Just (myId, dat)) >> return Nothing+        , sessionInvalidate = \_ -> return ()+        , sessionUseTicket = False+        }+  where+    maybeResume onlyOnce myId (Just (sid, sdata))+        | sid == myId = when onlyOnce (writeIORef ref Nothing) >> return (Just sdata)+    maybeResume _ _ _ = return Nothing++twoSessionManagers+    :: (IORef (Maybe (SessionID, SessionData)), IORef (Maybe (SessionID, SessionData)))+    -> (SessionManager, SessionManager)+twoSessionManagers (cRef, sRef) = (oneSessionManager cRef, oneSessionManager sRef)++setPairParamsSessionManagers+    :: (SessionManager, SessionManager)+    -> (ClientParams, ServerParams)+    -> (ClientParams, ServerParams)+setPairParamsSessionManagers (clientManager, serverManager) (clientParams, serverParams) = (nc, ns)+  where+    nc =+        clientParams+            { clientShared = updateSessionManager clientManager $ clientShared clientParams+            }+    ns =+        serverParams+            { serverShared = updateSessionManager serverManager $ serverShared serverParams+            }+    updateSessionManager manager shared = shared{sharedSessionManager = manager}++----------------------------------------------------------------++setPairParamsSessionResuming+    :: (SessionID, SessionData)+    -> (ClientParams, ServerParams)+    -> (ClientParams, ServerParams)+setPairParamsSessionResuming sessionStuff (clientParams, serverParams) =+    ( clientParams{clientWantSessionResume = Just sessionStuff}+    , serverParams+    )++oneSessionTicket :: SessionManager+oneSessionTicket =+    noSessionManager+        { sessionResume = resume+        , sessionResumeOnlyOnce = resume+        , sessionEstablish = \_ dat -> return $ Just $ L.toStrict $ serialise dat+        , sessionInvalidate = \_ -> return ()+        , sessionUseTicket = True+        }++resume :: Ticket -> IO (Maybe SessionData)+resume ticket+    | isTicket ticket = return $ Just $ deserialise $ L.fromStrict ticket+    | otherwise = return Nothing
+ test/Spec.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
+ test/ThreadSpec.hs view
@@ -0,0 +1,46 @@+{-# LANGUAGE OverloadedStrings #-}++module ThreadSpec where++import Control.Concurrent+import Control.Concurrent.Async+import Data.ByteString (ByteString)+import qualified Data.ByteString.Lazy as L+import Data.Foldable (traverse_)+import Network.TLS+import Test.Hspec+import Test.Hspec.QuickCheck++import API+import Arbitrary ()+import Run++spec :: Spec+spec = do+    describe "thread safety" $ do+        prop "can read/write concurrently" $ \params ->+            runTLS params tlsClient tlsServer++tlsClient :: Chan ByteString -> Context -> IO ()+tlsClient queue ctx = do+    handshake ctx+    runReaderWriters ctx "server-value" "client-value"+    d <- readChan queue+    sendData ctx (L.fromChunks [d])+    checkCtxFinished ctx+    bye ctx++tlsServer :: Context -> Chan [ByteString] -> IO ()+tlsServer ctx queue = do+    handshake ctx+    runReaderWriters ctx "client-value" "server-value"+    d <- recvData ctx+    writeChan queue [d]+    checkCtxFinished ctx+    bye ctx++runReaderWriters :: Context -> ByteString -> L.ByteString -> IO ()+runReaderWriters ctx r w =+    -- run concurrently 10 readers and 10 writers on the same context+    let workers = concat $ replicate 10 [recvDataAssert ctx r, sendData ctx w]+     in runConcurrently $ traverse_ Concurrently workers
tls.cabal view
@@ -1,170 +1,280 @@-Name:                tls-Version:             1.4.1-Description:-   Native Haskell TLS and SSL protocol implementation for server and client.-   .-   This provides a high-level implementation of a sensitive security protocol,-   eliminating a common set of security issues through the use of the advanced-   type system, high level constructions and common Haskell features.-   .-   Currently implement the SSL3.0, TLS1.0, TLS1.1 and TLS1.2 protocol,-   and support RSA and Ephemeral (Elliptic curve and regular) Diffie Hellman key exchanges,-   and many extensions.-   .-   Some debug tools linked with tls, are available through the-   <http://hackage.haskell.org/package/tls-debug/>.-License:             BSD3-License-file:        LICENSE-Copyright:           Vincent Hanquez <vincent@snarc.org>-Author:              Vincent Hanquez <vincent@snarc.org>-Maintainer:          Vincent Hanquez <vincent@snarc.org>-Synopsis:            TLS/SSL protocol native implementation (Server and Client)-Build-Type:          Simple-Category:            Network-stability:           experimental-Cabal-Version:       >=1.8-Homepage:            http://github.com/vincenthz/hs-tls-extra-source-files:  Tests/*.hs-                     CHANGELOG.md+cabal-version:      >=1.10+name:               tls+version:            2.4.3+license:            BSD3+license-file:       LICENSE+copyright:          Vincent Hanquez <vincent@snarc.org>+maintainer:         Kazu Yamamoto <kazu@iij.ad.jp>+author:             Vincent Hanquez <vincent@snarc.org>+homepage:           https://github.com/haskell-tls/hs-tls+synopsis:           TLS protocol native implementation+description:+    Native Haskell TLS 1.2/1.3 protocol implementation for servers and clients. -Flag compat-  Description:       Accept SSLv2 client hello for beginning SSLv3 / TLS handshake-  Default:           True+category:           Network+build-type:         Simple+extra-source-files:+    test/*.hs+    CHANGELOG.md -Flag network-  Description:       Use the base network library-  Default:           True+source-repository head+    type:     git+    location: https://github.com/haskell-tls/hs-tls+    subdir:   tls -Flag hans-  Description:       Use the Haskell Network Stack (HaNS)-  Default:           False+flag devel+    description: Development commands+    default:     False -Library-  Build-Depends:     base >= 4.7 && < 5-                   , mtl >= 2-                   , transformers-                   , cereal >= 0.4-                   , bytestring-                   , data-default-class-                   -- crypto related-                   , memory >= 0.14.6-                   , cryptonite >= 0.24-                   -- certificate related-                   , asn1-types >= 0.2.0-                   , asn1-encoding-                   , x509 >= 1.7.1-                   , x509-store >= 1.6-                   , x509-validation >= 1.6.5-                   , async >= 2.0-  if flag(network)-    Build-Depends:   network >= 2.4.0.0-    cpp-options:     -DINCLUDE_NETWORK-  if flag(hans)-    Build-Depends:   hans-    cpp-options:     -DINCLUDE_HANS-  Exposed-modules:   Network.TLS-                     Network.TLS.Cipher-                     Network.TLS.Compression-                     Network.TLS.Internal-                     Network.TLS.Extra-                     Network.TLS.Extra.Cipher-                     Network.TLS.Extra.FFDHE-  other-modules:     Network.TLS.Cap-                     Network.TLS.Struct-                     Network.TLS.Core-                     Network.TLS.Context-                     Network.TLS.Context.Internal-                     Network.TLS.Credentials-                     Network.TLS.Backend-                     Network.TLS.Crypto-                     Network.TLS.Crypto.DH-                     Network.TLS.Crypto.IES-                     Network.TLS.Crypto.Types-                     Network.TLS.ErrT-                     Network.TLS.Extension-                     Network.TLS.Handshake-                     Network.TLS.Handshake.Common-                     Network.TLS.Handshake.Certificate-                     Network.TLS.Handshake.Key-                     Network.TLS.Handshake.Client-                     Network.TLS.Handshake.Server-                     Network.TLS.Handshake.Process-                     Network.TLS.Handshake.Signature-                     Network.TLS.Handshake.State-                     Network.TLS.Hooks-                     Network.TLS.IO-                     Network.TLS.Imports-                     Network.TLS.MAC-                     Network.TLS.Measurement-                     Network.TLS.Packet-                     Network.TLS.Parameters-                     Network.TLS.Record-                     Network.TLS.Record.Types-                     Network.TLS.Record.Engage-                     Network.TLS.Record.Disengage-                     Network.TLS.Record.State-                     Network.TLS.RNG-                     Network.TLS.State-                     Network.TLS.Session-                     Network.TLS.Sending-                     Network.TLS.Receiving-                     Network.TLS.Util-                     Network.TLS.Util.ASN1-                     Network.TLS.Util.Serialization-                     Network.TLS.Types-                     Network.TLS.Wire-                     Network.TLS.X509-  ghc-options:       -Wall-  if flag(compat)-    cpp-options:     -DSSLV2_COMPATIBLE+library+    exposed-modules:+        Network.TLS+        Network.TLS.Cipher+        Network.TLS.Compression+        Network.TLS.Internal+        Network.TLS.Extra+        Network.TLS.Extra.Cipher+        Network.TLS.Extra.CipherCBC+        Network.TLS.Extra.FFDHE+        Network.TLS.QUIC -Test-Suite test-tls-  type:              exitcode-stdio-1.0-  hs-source-dirs:    Tests-  Main-is:           Tests.hs-  other-modules:     Certificate-                     Ciphers-                     Connection-                     Marshalling-                     PipeChan-                     PubKey-  Build-Depends:     base >= 3 && < 5-                   , mtl-                   , cereal >= 0.3-                   , data-default-class-                   , tasty-                   , tasty-quickcheck-                   , tls-                   , QuickCheck-                   , cryptonite-                   , bytestring-                   , asn1-types-                   , x509-                   , x509-validation-                   , hourglass-  ghc-options:       -Wall -fno-warn-unused-imports+    other-modules:+        Network.TLS.Struct+        Network.TLS.Struct13+        Network.TLS.Core+        Network.TLS.Context+        Network.TLS.Context.Internal+        Network.TLS.Credentials+        Network.TLS.Backend+        Network.TLS.Crypto+        Network.TLS.Crypto.DH+        Network.TLS.Crypto.IES+        Network.TLS.Crypto.Types+        Network.TLS.ErrT+        Network.TLS.Error+        Network.TLS.Extension+        Network.TLS.Handshake+        Network.TLS.Handshake.Certificate+        Network.TLS.Handshake.Client+        Network.TLS.Handshake.Client.ClientHello+        Network.TLS.Handshake.Client.Common+        Network.TLS.Handshake.Client.ServerHello+        Network.TLS.Handshake.Client.TLS12+        Network.TLS.Handshake.Client.TLS13+        Network.TLS.Handshake.Common+        Network.TLS.Handshake.Common13+        Network.TLS.Handshake.Control+        Network.TLS.Handshake.Key+        Network.TLS.Handshake.Random+        Network.TLS.Handshake.Server+        Network.TLS.Handshake.Server.ClientHello+        Network.TLS.Handshake.Server.ClientHello12+        Network.TLS.Handshake.Server.ClientHello13+        Network.TLS.Handshake.Server.Common+        Network.TLS.Handshake.Server.ServerHello12+        Network.TLS.Handshake.Server.ServerHello13+        Network.TLS.Handshake.Server.TLS12+        Network.TLS.Handshake.Server.TLS13+        Network.TLS.Handshake.Signature+        Network.TLS.Handshake.State+        Network.TLS.Handshake.State13+        Network.TLS.Handshake.TranscriptHash+        Network.TLS.HashAndSignature+        Network.TLS.Hooks+        Network.TLS.IO+        Network.TLS.IO.Decode+        Network.TLS.IO.Encode+        Network.TLS.Imports+        Network.TLS.KeySchedule+        Network.TLS.MAC+        Network.TLS.Measurement+        Network.TLS.Packet+        Network.TLS.Packet13+        Network.TLS.Parameters+        Network.TLS.PostHandshake+        Network.TLS.RNG+        Network.TLS.Record+        Network.TLS.Record.Decrypt+        Network.TLS.Record.Encrypt+        Network.TLS.Record.Layer+        Network.TLS.Record.Recv+        Network.TLS.Record.Send+        Network.TLS.Record.State+        Network.TLS.Record.Types+        Network.TLS.Session+        Network.TLS.State+        Network.TLS.Types+        Network.TLS.Types.Cipher+        Network.TLS.Types.Secret+        Network.TLS.Types.Session+        Network.TLS.Types.Version+        Network.TLS.Util+        Network.TLS.Util.ASN1+        Network.TLS.Util.Serialization+        Network.TLS.Wire+        Network.TLS.X509 -Benchmark bench-tls-  hs-source-dirs:    Benchmarks Tests-  Main-Is:           Benchmarks.hs-  type:              exitcode-stdio-1.0-  Build-depends:     base >= 4 && < 5-                   , tls-                   , x509-                   , x509-validation-                   , data-default-class-                   , cryptonite-                   , criterion >= 1.0-                   , mtl-                   , bytestring-                   , asn1-types-                   , hourglass-                   , QuickCheck >= 2-                   , tasty-quickcheck-                   , tls-  ghc-options:       -Wall -fno-warn-unused-imports+    default-language:   Haskell2010+    default-extensions: Strict StrictData+    ghc-options:        -Wall+    build-depends:+        base >=4.9 && <5,+        base16-bytestring,+        bytestring >=0.10 && <0.13,+        cereal >=0.5.3 && <0.6,+        crypton >=1.1.2 && <1.2,+        crypton-asn1-encoding >= 0.10.0 && < 0.11,+        crypton-asn1-types >= 0.4.1 && < 0.5,+        crypton-x509 >=1.9 && <1.10,+        crypton-x509-store >=1.9 && <1.10,+        crypton-x509-validation >=1.9 && <1.10,+        data-default,+        ech-config,+        hpke >=0.1.0 && <0.2,+        mlkem >= 0.2.0 && <0.3,+        mtl >=2.2 && <2.4,+        network >=3.1,+        ram >=0.22.0 && <0.23,+        random >=1.2 && <1.4,+        serialise >=0.2 && <0.3,+        transformers >=0.5 && <0.7,+        unix-time >=0.4.11 && <0.6,+        zlib >=0.7 && <0.8 -source-repository head-  type: git-  location: https://github.com/vincenthz/hs-tls-  subdir: core+executable tls-server+    main-is:            tls-server.hs+    hs-source-dirs:     util+    other-modules:+        Common+        Server+        Imports++    default-language:   Haskell2010+    default-extensions: Strict StrictData+    ghc-options:        -Wall -threaded -rtsopts+    build-depends:+        base >=4.9 && <5,+        base16-bytestring,+        bytestring,+        containers,+        crypton,+        crypton-x509-store,+        crypton-x509-system,+        ech-config,+        network,+        network-run,+        tls++    if flag(devel)++    else+        buildable: False++executable tls-client+    main-is:            tls-client.hs+    hs-source-dirs:     util+    other-modules:+        Client+        Common+        Imports++    default-language:   Haskell2010+    default-extensions: Strict StrictData+    ghc-options:        -Wall -threaded -rtsopts+    build-depends:+        base >=4.9 && <5,+        base16-bytestring,+        bytestring,+        crypton,+        crypton-x509-store,+        crypton-x509-system,+        ech-config,+        network,+        network-run >=0.5,+        tls++    if flag(devel)++    else+        buildable: False++test-suite spec+    type:               exitcode-stdio-1.0+    main-is:            Spec.hs+    build-tool-depends: hspec-discover:hspec-discover+    hs-source-dirs:     test+    other-modules:+        API+        Arbitrary+        Certificate+        CiphersSpec+        ECHSpec+        EncodeSpec+        HandshakeSpec+        PipeChan+        PubKey+        Run+        Session+        ThreadSpec++    default-language:   Haskell2010+    default-extensions: Strict StrictData+    ghc-options:        -Wall -threaded -rtsopts+    build-depends:+        base >=4.9 && <5,+        QuickCheck,+        async,+        base64-bytestring,+        bytestring,+        crypton,+        crypton-asn1-types,+        crypton-x509,+        crypton-x509-validation,+        ech-config,+        hspec,+        ram,+        serialise,+        time-hourglass,+        tls++benchmark tls-bench+    type:             exitcode-stdio-1.0+    main-is:          Benchmarks.hs+    hs-source-dirs:   Benchmarks test+    other-modules:+        API+        Arbitrary+        Certificate+        CiphersSpec+        ECHSpec+        EncodeSpec+        HandshakeSpec+        PipeChan+        PubKey+        Run+        Session+        ThreadSpec++    default-language: Haskell2010+    ghc-options:      -Wall+    build-depends:+        base >=4.9 && <5,+        QuickCheck,+        async,+        base64-bytestring,+        bytestring,+        containers,+        crypton,+        crypton-asn1-types,+        crypton-x509,+        crypton-x509-store,+        crypton-x509-validation,+        data-default,+        ech-config,+        hspec,+        network,+        network-run,+        serialise,+        tasty-bench,+        time-hourglass,+        tls
+ util/Client.hs view
@@ -0,0 +1,61 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}++module Client (+    Aux (..),+    Cli,+    clientHTTP11,+    clientDNS,+) where++import qualified Data.ByteString.Base16 as BS16+import qualified Data.ByteString.Char8 as C8+import qualified Data.ByteString.Lazy.Char8 as CL8+import Data.List.NonEmpty (NonEmpty)+import qualified Data.List.NonEmpty as NE+import Network.Socket+import Network.TLS++import Imports++data Aux = Aux+    { auxAuthority :: HostName+    , auxPort :: ServiceName+    , auxDebugPrint :: String -> IO ()+    , auxShow :: ByteString -> IO ()+    , auxReadResumptionData :: IO [(SessionID, SessionData)]+    }++type Cli = Aux -> NonEmpty ByteString -> Context -> IO ()++clientHTTP11 :: Cli+clientHTTP11 aux@Aux{..} paths ctx = do+    sendData ctx $+        CL8.fromStrict $+            "GET "+                <> NE.head paths+                <> " HTTP/1.1\r\n"+                <> "Host: "+                <> C8.pack auxAuthority+                <> "\r\n"+                <> "Connection: close\r\n"+                <> "\r\n"+    consume ctx aux++clientDNS :: Cli+clientDNS Aux{..} _paths ctx = do+    sendData+        ctx+        "\x00\x2c\xdc\xe3\x01\x00\x00\x01\x00\x00\x00\x00\x00\x01\x03\x77\x77\x77\x07\x65\x78\x61\x6d\x70\x6c\x65\x03\x63\x6f\x6d\x00\x00\x01\x00\x01\x00\x00\x29\x04\xd0\x00\x00\x00\x00\x00\x00"+    bs <- recvData ctx+    auxShow $ "Reply: " <> BS16.encode bs+    auxShow "\n"++consume :: Context -> Aux -> IO ()+consume ctx Aux{..} = loop+  where+    loop = do+        bs <- recvData ctx+        if bs == ""+            then auxShow "\n"+            else auxShow bs >> loop
+ util/Common.hs view
@@ -0,0 +1,133 @@+{-# LANGUAGE CPP #-}+-- Disable this warning so we can still test deprecated functionality.+{-# OPTIONS_GHC -fno-warn-warnings-deprecations #-}++module Common (+    printDHParams,+    printGroups,+    readNumber,+    readDHParams,+    readGroups,+    getCertificateStore,+    getLogger,+    namedGroups,+    getInfo,+    printHandshakeInfo,+    showBytesHex,+) where++import qualified Data.ByteString.Base16 as B16+import qualified Data.ByteString.Char8 as C8+import Data.Char (isDigit)+import Data.X509.CertificateStore+import Network.TLS hiding (HostName)+import Network.TLS.Extra.FFDHE+import System.Exit+import System.X509++import Imports++namedDHParams :: [(String, DHParams)]+namedDHParams =+    [ ("ffdhe2048", ffdhe2048)+    , ("ffdhe3072", ffdhe3072)+    , ("ffdhe4096", ffdhe4096)+    , ("ffdhe6144", ffdhe6144)+    , ("ffdhe8192", ffdhe8192)+    ]++{- FOURMOLU_DISABLE -}+namedGroups :: [(String, Group)]+namedGroups =+    [ ("ffdhe2048",      FFDHE2048)+    , ("ffdhe3072",      FFDHE3072)+    , ("ffdhe4096",      FFDHE4096)+    , ("ffdhe6144",      FFDHE6144)+    , ("ffdhe8192",      FFDHE8192)+    , ("p256",           P256)+    , ("p384",           P384)+    , ("p521",           P521)+    , ("x25519",         X25519)+    , ("x448",           X448)+    , ("mlkem512",       MLKEM512)+    , ("mlkem768",       MLKEM768)+    , ("mlkem1024",      MLKEM1024)+    , ("x25519mlkem768", X25519MLKEM768)+    , ("p256mlkem768",   P256MLKEM768)+    , ("p384mlkem1024",  P384MLKEM1024)+    ]+{- FOURMOLU_ENABLE -}++readNumber :: (Num a, Read a) => String -> Maybe a+readNumber s+    | all isDigit s = Just $ read s+    | otherwise = Nothing++readDHParams :: String -> IO (Maybe DHParams)+readDHParams s =+    case lookup s namedDHParams of+        Nothing -> (Just . read) `fmap` readFile s+        mparams -> return mparams++readGroups :: String -> [Group]+readGroups s = fromMaybe [] $ traverse (`lookup` namedGroups) (split ',' s)++printDHParams :: IO ()+printDHParams = do+    putStrLn "DH Parameters"+    putStrLn "====================================="+    forM_ namedDHParams $ \(name, _) -> putStrLn name+    putStrLn "(or /path/to/dhparams)"++printGroups :: IO ()+printGroups = do+    putStrLn "Groups"+    putStrLn "====================================="+    forM_ namedGroups $ \(name, _) -> putStrLn name++split :: Char -> String -> [String]+split _ "" = []+split c s = case break (c ==) s of+    ("", _ : rs) -> split c rs+    (s', "") -> [s']+    (s', _ : rs) -> s' : split c rs++getCertificateStore :: [FilePath] -> IO CertificateStore+getCertificateStore [] = getSystemCertificateStore+getCertificateStore paths = foldM readPathAppend mempty paths+  where+    readPathAppend acc path = do+        mstore <- readCertificateStore path+        case mstore of+            Nothing -> error ("invalid certificate store: " ++ path)+            Just st -> return $! mappend st acc++getLogger :: Maybe FilePath -> (String -> IO ())+getLogger Nothing = \_ -> return ()+getLogger (Just file) = \msg -> appendFile file (msg ++ "\n")++getInfo :: Context -> IO Information+getInfo ctx = do+    minfo <- contextGetInformation ctx+    case minfo of+        Nothing -> do+            putStrLn "Error: information cannot be obtained"+            exitFailure+        Just info -> return info++printHandshakeInfo :: Information -> IO ()+printHandshakeInfo i = do+    putStrLn $ "Version: " ++ show (infoVersion i)+    putStrLn $ "Cipher: " ++ show (infoCipher i)+    putStrLn $ "Compression: " ++ show (infoCompression i)+    putStrLn $ "Groups: " ++ maybe "(none)" show (infoSupportedGroup i)+    when (infoVersion i < TLS13) $ do+        putStrLn $ "Extended master secret: " ++ show (infoExtendedMainSecret i)+        putStrLn $ "Resumption: " ++ show (infoTLS12Resumption i)+    when (infoVersion i == TLS13) $ do+        putStrLn $ "Handshake mode: " ++ show (fromJust (infoTLS13HandshakeMode i))+        putStrLn $ "Early data accepted: " ++ show (infoIsEarlyDataAccepted i)+        putStrLn $ "Encrypted client hello accepted: " ++ show (infoIsECHAccepted i)++showBytesHex :: ByteString -> String+showBytesHex bs = C8.unpack $ B16.encode bs
+ util/Imports.hs view
@@ -0,0 +1,17 @@+module Imports (+    ByteString,+    module Control.Applicative,+    module Control.Monad,+    module Data.List,+    module Data.Maybe,+    module Data.Monoid,+    module Data.Word,+) where++import Control.Applicative+import Control.Monad+import Data.ByteString (ByteString)+import Data.List+import Data.Maybe+import Data.Monoid+import Data.Word
+ util/Server.hs view
@@ -0,0 +1,92 @@+{-# LANGUAGE OverloadedStrings #-}++module Server where++import qualified Data.ByteString as BS+import qualified Data.ByteString.Char8 as C8+import qualified Data.ByteString.Lazy.Char8 as CL8+import Data.IORef+import Network.TLS+import Prelude hiding (getLine)++import Imports++-- "<>" creates *chunks* of lazy ByteString, resulting+-- many TLS fragments.+-- To prevent this, strict ByteString is created first and+-- converted into lazy one.+html :: CL8.ByteString+html =+    CL8.fromStrict $+        "HTTP/1.1 200 OK\r\n"+            <> "Context-Type: text/html\r\n"+            <> "Content-Length: "+            <> C8.pack (show (BS.length body))+            <> "\r\n"+            <> "\r\n"+            <> body+  where+    body = "<html><<body>Hello world!</body></html>"++server :: Context -> Bool -> IO ()+server ctx showRequest = do+    bs <- recvData ctx+    case C8.uncons bs of+        Nothing -> return ()+        Just ('A', _) -> do+            sendData ctx $ CL8.fromStrict bs+            echo ctx+        Just _ -> handleHTML ctx showRequest bs++echo :: Context -> IO ()+echo ctx = loop+  where+    loop = do+        bs <- recvData ctx+        when (bs /= "") $ do+            sendData ctx $ CL8.fromStrict bs+            loop++handleHTML :: Context -> Bool -> ByteString -> IO ()+handleHTML ctx showRequest ini = do+    getLine <- newSource ctx ini+    process getLine+  where+    process getLine = do+        bs <- getLine+        when ("GET /keyupdate" `BS.isPrefixOf` bs) $ do+            r <- updateKey ctx TwoWay+            putStrLn $ "Updating key..." ++ if r then "OK" else "NG"+        when ("GET /secret" `BS.isPrefixOf` bs) $ do+            r <- requestCertificate ctx+            putStrLn $ "Post handshake authentication..." ++ if r then "OK" else "NG"+        when (bs /= "") $ do+            when showRequest $ do+                BS.putStr bs+                BS.putStr "\n"+            consume getLine+            sendData ctx html+    consume getLine = do+        bs <- getLine+        when (bs /= "") $ do+            when showRequest $ do+                BS.putStr bs+                BS.putStr "\n"+            consume getLine++newSource :: Context -> ByteString -> IO (IO ByteString)+newSource ctx ini = do+    ref <- newIORef ini+    return $ getline ref+  where+    getline :: IORef ByteString -> IO ByteString+    getline ref = do+        bs0 <- readIORef ref+        case BS.breakSubstring "\n" bs0 of+            (_, "") -> do+                bs1 <- recvData ctx+                writeIORef ref (bs0 <> bs1)+                getline ref+            (bs1, bs2) -> do+                writeIORef ref $ BS.drop 1 bs2+                return $ BS.dropWhileEnd (== 0x0d) bs1
+ util/tls-client.hs view
@@ -0,0 +1,465 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE MultiWayIf #-}+{-# LANGUAGE OverloadedLists #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}++module Main where++import Control.Concurrent+import qualified Control.Exception as E+import qualified Data.ByteString.Base16 as BS16+import qualified Data.ByteString.Char8 as C8+import Data.IORef+import Data.List.NonEmpty (NonEmpty)+import qualified Data.List.NonEmpty as NE+import Data.X509.CertificateStore+import Network.Run.TCP+import Network.Socket+import Network.TLS hiding (is0RTTPossible)+import Network.TLS.ECH.Config+import Network.TLS.Internal (makeCipherShowPretty)+import System.Console.GetOpt+import System.Environment+import System.Exit+import System.X509++import Client+import Common+import Imports++data Options = Options+    { optDebugLog :: Bool+    , optShow :: Bool+    , optKeyLogFile :: Maybe FilePath+    , optGroups :: [Group]+    , optValidate :: Bool+    , optVerNego :: Bool+    , optResumption :: Bool+    , opt0RTT :: Bool+    , optRetry :: Bool+    , optVersions :: [Version]+    , optALPN :: String+    , optCertFile :: Maybe FilePath+    , optKeyFile :: Maybe FilePath+    , optECHConfigFile :: Maybe FilePath+    , optTraceKey :: Bool+    , optIPv4Only :: Bool+    , optIPv6Only :: Bool+    , optTrustedAnchor :: Maybe FilePath+    }+    deriving (Show)++defaultOptions :: Options+defaultOptions =+    Options+        { optDebugLog = False+        , optShow = False+        , optKeyLogFile = Nothing+        , optGroups = supportedGroups defaultSupported+        , optValidate = False+        , optVerNego = False+        , optResumption = False+        , opt0RTT = False+        , optRetry = False+        , optVersions = supportedVersions defaultSupported+        , optALPN = "http/1.1"+        , optCertFile = Nothing+        , optKeyFile = Nothing+        , optECHConfigFile = Nothing+        , optTraceKey = False+        , optIPv4Only = False+        , optIPv6Only = False+        , optTrustedAnchor = Nothing+        }++usage :: String+usage = "Usage: tls-client [OPTION] addr port [path]"++options :: [OptDescr (Options -> Options)]+options =+    [ Option+        ['d']+        ["debug"]+        (NoArg (\o -> o{optDebugLog = True}))+        "print debug info"+    , Option+        ['v']+        ["show-content"]+        (NoArg (\o -> o{optShow = True}))+        "print downloaded content"+    , Option+        ['l']+        ["key-log-file"]+        (ReqArg (\file o -> o{optKeyLogFile = Just file}) "<file>")+        "a file to store negotiated secrets"+    , Option+        ['g']+        ["groups"]+        (ReqArg (\gs o -> o{optGroups = readGroups gs}) "<groups>")+        "specify groups"+    , Option+        ['e']+        ["validate"]+        (NoArg (\o -> o{optValidate = True}))+        "validate server's certificate"+    , Option+        ['R']+        ["resumption"]+        (NoArg (\o -> o{optResumption = True}))+        "try session resumption"+    , Option+        ['Z']+        ["0rtt"]+        (NoArg (\o -> o{opt0RTT = True}))+        "try sending early data"+    , Option+        ['S']+        ["hello-retry"]+        (NoArg (\o -> o{optRetry = True}))+        "try client hello retry"+    , Option+        ['2']+        ["tls12"]+        (NoArg (\o -> o{optVersions = [TLS12]}))+        "use TLS 1.2"+    , Option+        ['3']+        ["tls13"]+        (NoArg (\o -> o{optVersions = [TLS13]}))+        "use TLS 1.3"+    , Option+        ['a']+        ["alpn"]+        (ReqArg (\a o -> o{optALPN = a}) "<alpn>")+        "set ALPN"+    , Option+        ['c']+        ["cert"]+        (ReqArg (\fl o -> o{optCertFile = Just fl}) "<file>")+        "certificate file"+    , Option+        ['k']+        ["key"]+        (ReqArg (\fl o -> o{optKeyFile = Just fl}) "<file>")+        "key file"+    , Option+        []+        ["ech-config"]+        (ReqArg (\fl o -> o{optECHConfigFile = Just fl}) "<file>")+        "ECH config file"+    , Option+        []+        ["trace-key"]+        (NoArg (\o -> o{optTraceKey = True}))+        "Trace transcript hash"+    , Option+        ['4']+        []+        (NoArg (\o -> o{optIPv4Only = True, optIPv6Only = False}))+        "IPv4 only"+    , Option+        ['6']+        []+        (NoArg (\o -> o{optIPv6Only = True, optIPv4Only = False}))+        "IPv6 only"+    , Option+        ['t']+        ["trusted-anchor"]+        (ReqArg (\fl o -> o{optTrustedAnchor = Just fl}) "<file>")+        "trusted anchor file"+    ]++showUsageAndExit :: String -> IO a+showUsageAndExit msg = do+    putStrLn msg+    putStrLn $ usageInfo usage options+    putStrLn $ "  <groups> = " ++ intercalate "," (map fst namedGroups)+    exitFailure++clientOpts :: [String] -> IO (Options, [String])+clientOpts argv =+    case getOpt Permute options argv of+        (o, n, []) -> return (foldl (flip id) defaultOptions o, n)+        (_, _, errs) -> showUsageAndExit $ concat errs++main :: IO ()+main = do+    args <- getArgs+    (opts@Options{..}, ips) <- clientOpts args+    (host, port, paths) <- case ips of+        [] -> showUsageAndExit usage+        _ : [] -> showUsageAndExit usage+        h : p : [] -> return (h, p, ["/"])+        h : p : ps -> return (h, p, C8.pack <$> NE.fromList ps)+    when (null optGroups) $ do+        putStrLn "Error: unsupported groups"+        exitFailure+    let onCertReq = \_ -> case optCertFile of+            Just certFile -> case optKeyFile of+                Just keyFile -> do+                    Right (!cc, !priv) <- credentialLoadX509 certFile keyFile+                    return $ Just (cc, priv)+                _ -> return Nothing+            _ -> return Nothing+    ref <- newIORef []+    let debug+            | optDebugLog = putStrLn+            | otherwise = \_ -> return ()+        traceKey+            | optTraceKey = putStrLn+            | otherwise = \_ -> return ()+        showContent+            | optShow = C8.putStr+            | otherwise = \_ -> return ()+        aux =+            Aux+                { auxAuthority = host+                , auxPort = port+                , auxDebugPrint = debug+                , auxShow = showContent+                , auxReadResumptionData = readIORef ref+                }+    mstore <- case optTrustedAnchor of+        Nothing ->+            if optValidate then Just <$> getSystemCertificateStore else return Nothing+        Just file -> do+            mstore' <- readCertificateStore file+            when (isNothing mstore') $ showUsageAndExit "cannot set trusted anchor"+            return mstore'+    echConfList <- case optECHConfigFile of+        Nothing -> return []+        Just ecnff ->+            loadECHConfigList ecnff `E.catch` \(E.SomeException _) -> do+                putStrLn $ ecnff ++ " is broken"+                exitFailure+    let cparams =+            getClientParams+                opts+                host+                port+                (smIORef ref)+                mstore+                onCertReq+                echConfList+                debug+                traceKey+        client+            | optALPN == "dot" = clientDNS+            | otherwise = clientHTTP11+    makeCipherShowPretty+    runClient opts client cparams aux paths++runClient+    :: Options -> Cli -> ClientParams -> Aux -> NonEmpty ByteString -> IO ()+runClient opts@Options{..} client cparams aux@Aux{..} paths = do+    auxDebugPrint "------------------------"+    (info1, msd) <- runTLS opts cparams aux $ \ctx -> do+        i1 <- getInfo ctx+        when optDebugLog $ printHandshakeInfo i1+        client aux paths ctx+        msd' <- auxReadResumptionData+        return (i1, msd')+    if+        | optResumption ->+            if isResumptionPossible msd+                then do+                    let cparams2 = modifyClientParams cparams msd False+                    info2 <- runClient2 opts client cparams2 aux paths+                    if infoVersion info1 == TLS12+                        then do+                            if infoTLS12Resumption info2+                                then do+                                    putStrLn "Result: (R) TLS resumption ... OK"+                                    exitSuccess+                                else do+                                    putStrLn "Result: (R) TLS resumption ... NG"+                                    exitFailure+                        else do+                            if infoTLS13HandshakeMode info2 == Just PreSharedKey+                                then do+                                    putStrLn "Result: (R) TLS resumption ... OK"+                                    exitSuccess+                                else do+                                    putStrLn "Result: (R) TLS resumption ... NG"+                                    exitFailure+                else do+                    putStrLn "Result: (R) TLS resumption ... NG"+                    exitFailure+        | opt0RTT ->+            if is0RTTPossible info1 msd+                then do+                    let cparams2 = modifyClientParams cparams msd True+                    info2 <- runClient2 opts client cparams2 aux paths+                    if infoTLS13HandshakeMode info2 == Just RTT0+                        then do+                            putStrLn "Result: (Z) 0-RTT ... OK"+                            exitSuccess+                        else do+                            putStrLn "Result: (Z) 0-RTT ... NG"+                            exitFailure+                else do+                    putStrLn "Result: (Z) 0-RTT ... NG"+                    exitFailure+        | optRetry ->+            if infoTLS13HandshakeMode info1 == Just HelloRetryRequest+                then do+                    putStrLn "Result: (S) retry ... OK"+                    exitSuccess+                else do+                    putStrLn "Result: (S) retry ... NG"+                    exitFailure+        | otherwise -> do+            putStrLn "Result: (H) handshake ... OK"+            when (optALPN == "http/1.1") $+                putStrLn "Result: (1) HTTP/1.1 transaction ... OK"+            exitSuccess++runClient2+    :: Options+    -> Cli+    -> ClientParams+    -> Aux+    -> NonEmpty ByteString+    -> IO Information+runClient2 opts@Options{..} client cparams aux@Aux{..} paths = do+    threadDelay 100000+    auxDebugPrint "<<<< next connection >>>>"+    auxDebugPrint "------------------------"+    runTLS opts cparams aux $ \ctx -> do+        if opt0RTT+            then do+                void $ client aux paths ctx+                i <- getInfo ctx+                when optDebugLog $ printHandshakeInfo i+                return i+            else do+                i <- getInfo ctx+                when optDebugLog $ printHandshakeInfo i+                void $ client aux paths ctx+                return i++runTLS+    :: Options+    -> ClientParams+    -> Aux+    -> (Context -> IO a)+    -> IO a+runTLS Options{..} cparams Aux{..} action =+    runTCPClientWithSettings settings auxAuthority auxPort $ \sock -> do+        ctx <- contextNew sock cparams+        when optDebugLog $+            contextHookSetLogging+                ctx+                defaultLogging+                    { loggingPacketSent = putStrLn . (">> " ++)+                    , loggingPacketRecv = putStrLn . ("<< " ++)+                    --                    , loggingIOSent = \bs -> putStrLn $ "}} " ++ showBytesHex bs+                    --                    , loggingIORecv = \hd bs -> putStrLn $ "{{ " ++ show hd ++ " " ++ showBytesHex bs+                    }+        handshake ctx+        r <- action ctx+        bye ctx+        return r+  where+    select addrs+        | optIPv4Only = case NE.filter (\ai -> addrFamily ai == AF_INET) addrs of+            [] -> error "IPv4 address is not available"+            ai : _ -> ai+        | optIPv6Only = case NE.filter (\ai -> addrFamily ai == AF_INET6) addrs of+            [] -> error "IPv6 address is not available"+            ai : _ -> ai+        | otherwise = NE.head addrs+    settings =+        defaultSettings+            { settingsSelectAddrInfo = select+            }++modifyClientParams+    :: ClientParams -> [(SessionID, SessionData)] -> Bool -> ClientParams+modifyClientParams cparams ts early =+    cparams+        { clientWantSessionResumeList = ts+        , clientUseEarlyData = early+        }++getClientParams+    :: Options+    -> HostName+    -> ServiceName+    -> SessionManager+    -> Maybe CertificateStore+    -> OnCertificateRequest+    -> ECHConfigList+    -> (String -> IO ())+    -> (String -> IO ())+    -> ClientParams+getClientParams Options{..} serverName port sm mstore onCertReq echConfList printError traceKey =+    (defaultParamsClient serverName (C8.pack port))+        { clientSupported = supported+        , clientUseServerNameIndication = True+        , clientShared = shared+        , clientHooks = hooks+        , clientDebug = debug+        , clientUseECH = not (null echConfList)+        }+  where+    groups+        | optRetry = FFDHE8192 : optGroups+        | otherwise = optGroups+    shared =+        defaultShared+            { sharedSessionManager = sm+            , sharedCAStore = fromMaybe mempty mstore+            , sharedValidationCache = validateCache+            , sharedLimit =+                defaultLimit+                    { limitRecordSize = Just 8192+                    }+            , sharedECHConfigList = echConfList+            }+    supported =+        defaultSupported+            { supportedVersions = optVersions+            , supportedGroups = groups+            }+    hooks =+        defaultClientHooks+            { onSuggestALPN = return $ Just [C8.pack optALPN]+            , onCertificateRequest = onCertReq+            }+    validateCache+        | isJust mstore = sharedValidationCache defaultShared+        | otherwise =+            ValidationCache+                (\_ _ _ -> return ValidationCachePass)+                (\_ _ _ -> return ())+    debug =+        defaultDebugParams+            { debugKeyLogger = getLogger optKeyLogFile+            , debugError = printError+            , debugTraceKey = traceKey+            }++smIORef :: IORef [(SessionID, SessionData)] -> SessionManager+smIORef ref =+    noSessionManager+        { sessionEstablish = \sid sdata ->+            modifyIORef' ref (\xs -> (sid, sdata) : xs)+                >> printTicket sid sdata+                >> return Nothing+        }++printTicket :: SessionID -> SessionData -> IO ()+printTicket sid sdata = do+    C8.putStr $ "Ticket: " <> C8.take 16 (BS16.encode sid) <> "..., "+    putStrLn $ "0-RTT: " <> if sessionMaxEarlyDataSize sdata > 0 then "OK" else "NG"++isResumptionPossible :: [(SessionID, SessionData)] -> Bool+isResumptionPossible = not . null++is0RTTPossible :: Information -> [(SessionID, SessionData)] -> Bool+is0RTTPossible _ [] = False+is0RTTPossible info xs =+    infoVersion info == TLS13+        && any (\(_, sd) -> sessionMaxEarlyDataSize sd > 0) xs
+ util/tls-server.hs view
@@ -0,0 +1,270 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}++module Main where++import Data.IORef+import qualified Data.Map.Strict as M+import Data.X509.CertificateStore+import Network.Run.TCP+import Network.TLS+import Network.TLS.ECH.Config+import Network.TLS.Internal+import System.Console.GetOpt+import System.Environment (getArgs)+import System.Exit+import System.IO+import System.X509++import Common+import Imports+import Server++data Options = Options+    { optDebugLog :: Bool+    , optClientAuth :: Bool+    , optShow :: Bool+    , optKeyLogFile :: Maybe FilePath+    , optTrustedAnchor :: Maybe FilePath+    , optGroups :: [Group]+    , optCertFile :: FilePath+    , optKeyFile :: FilePath+    , optECHConfigFile :: Maybe FilePath+    , optECHKeyFile :: Maybe FilePath+    , optTraceKey :: Bool+    }+    deriving (Show)++defaultOptions :: Options+defaultOptions =+    Options+        { optDebugLog = False+        , optClientAuth = False+        , optShow = False+        , optKeyLogFile = Nothing+        , optTrustedAnchor = Nothing+        , -- excluding FFDHE8192 for retry+          optGroups = FFDHE8192 `delete` supportedGroups defaultSupported+        , optCertFile = "servercert.pem"+        , optKeyFile = "serverkey.pem"+        , optECHConfigFile = Nothing+        , optECHKeyFile = Nothing+        , optTraceKey = False+        }++options :: [OptDescr (Options -> Options)]+options =+    [ Option+        ['a']+        ["client-auth"]+        (NoArg (\o -> o{optClientAuth = True}))+        "require client authentication"+    , Option+        ['d']+        ["debug"]+        (NoArg (\o -> o{optDebugLog = True}))+        "print debug info"+    , Option+        ['v']+        ["show-content"]+        (NoArg (\o -> o{optShow = True}))+        "print downloaded content"+    , Option+        ['l']+        ["key-log-file"]+        (ReqArg (\file o -> o{optKeyLogFile = Just file}) "<file>")+        "a file to store negotiated secrets"+    , Option+        ['g']+        ["groups"]+        (ReqArg (\gs o -> o{optGroups = readGroups gs}) "<groups>")+        "groups for key exchange"+    , Option+        ['c']+        ["cert"]+        (ReqArg (\fl o -> o{optCertFile = fl}) "<file>")+        "certificate file"+    , Option+        ['k']+        ["key"]+        (ReqArg (\fl o -> o{optKeyFile = fl}) "<file>")+        "key file"+    , Option+        ['t']+        ["trusted-anchor"]+        (ReqArg (\fl o -> o{optTrustedAnchor = Just fl}) "<file>")+        "trusted anchor file"+    , Option+        []+        ["ech-config"]+        (ReqArg (\fl o -> o{optECHConfigFile = Just fl}) "<file>")+        "ECH config file"+    , Option+        []+        ["ech-key"]+        (ReqArg (\fl o -> o{optECHKeyFile = Just fl}) "<file>")+        "ECH key file"+    , Option+        []+        ["trace-key"]+        (NoArg (\o -> o{optTraceKey = True}))+        "Trace transcript hash"+    ]++usage :: String+usage = "Usage: tls-server [OPTION] addr port"++showUsageAndExit :: String -> IO a+showUsageAndExit msg = do+    putStrLn msg+    putStrLn $ usageInfo usage options+    exitFailure++serverOpts :: [String] -> IO (Options, [String])+serverOpts argv =+    case getOpt Permute options argv of+        (o, n, []) -> return (foldl (flip id) defaultOptions o, n)+        (_, _, errs) -> showUsageAndExit $ concat errs++main :: IO ()+main = do+    hSetBuffering stdout NoBuffering+    args <- getArgs+    (Options{..}, ips) <- serverOpts args+    (host, port) <- case ips of+        [h, p] -> return (h, p)+        _ -> showUsageAndExit "cannot recognize <addr> and <port>\n"+    when (null optGroups) $ do+        putStrLn "Error: unsupported groups"+        exitFailure+    smgr <- newSessionManager+    Right cred@(!_cc, !_priv) <- credentialLoadX509 optCertFile optKeyFile+    mstore <- do+        mstore' <- case optTrustedAnchor of+            Nothing -> Just <$> getSystemCertificateStore+            Just file -> readCertificateStore file+        when (isNothing mstore') $ showUsageAndExit "cannot set trusted anchor"+        return mstore'+    ech <- case optECHKeyFile of+        Nothing -> case optECHConfigFile of+            Nothing -> return ([], [])+            Just _ -> showUsageAndExit "must specify ECH key file, too"+        Just ekeyf -> case optECHConfigFile of+            Nothing -> showUsageAndExit "must specify ECH config file, too"+            Just ecnff -> do+                ekey <- loadECHSecretKeys [ekeyf]+                ecnf <- loadECHConfigList ecnff+                return (ekey, ecnf)+    let keyLog = getLogger optKeyLogFile+        printError+            | optDebugLog = putStrLn+            | otherwise = \_ -> return ()+        traceKey+            | optTraceKey = putStrLn+            | otherwise = \_ -> return ()+        creds = Credentials [cred]+    makeCipherShowPretty+    runTCPServer (Just host) port $ \sock -> do+        let sparams =+                getServerParams+                    creds+                    optGroups+                    smgr+                    keyLog+                    optClientAuth+                    mstore+                    ech+                    printError+                    traceKey+        ctx <- contextNew sock sparams+        when optDebugLog $+            contextHookSetLogging+                ctx+                defaultLogging+                    { loggingPacketSent = putStrLn . ("<< " ++)+                    , loggingPacketRecv = putStrLn . (">> " ++)+                    --                    , loggingIOSent = \bs -> putStrLn $ "{{ " ++ showBytesHex bs+                    --                    , loggingIORecv = \hd bs -> putStrLn $ "}} " ++ show hd ++ " " ++ showBytesHex bs+                    }+        when (optDebugLog || optShow) $ putStrLn "------------------------"+        handshake ctx+        when optDebugLog $+            getInfo ctx >>= printHandshakeInfo+        server ctx optShow+        bye ctx++getServerParams+    :: Credentials+    -> [Group]+    -> SessionManager+    -> (String -> IO ())+    -> Bool+    -> Maybe CertificateStore+    -> ([(Word8, ByteString)], ECHConfigList)+    -> (String -> IO ())+    -> (String -> IO ())+    -> ServerParams+getServerParams creds groups sm keyLog clientAuth mstore (ekey, ecnf) printError traceKey =+    defaultParamsServer+        { serverSupported = supported+        , serverShared = shared+        , serverHooks = hooks+        , serverDebug = debug+        , serverEarlyDataSize = 2048+        , serverWantClientCert = clientAuth+        , serverECHKey = ekey+        }+  where+    shared =+        defaultShared+            { sharedCredentials = creds+            , sharedSessionManager = sm+            , sharedCAStore = case mstore of+                Just store -> store+                Nothing -> sharedCAStore defaultShared+            , sharedECHConfigList = ecnf+            , sharedLimit =+                defaultLimit+                    { limitRecordSize = Just 16384+                    }+            }+    supported =+        defaultSupported+            { supportedGroups = groups+            }+    hooks =+        defaultServerHooks+            { onALPNClientSuggest = Just chooseALPN+            , onClientCertificate = case mstore of+                Nothing -> onClientCertificate defaultServerHooks+                Just _ ->+                    validateClientCertificate (sharedCAStore shared) (sharedValidationCache shared)+            }+    debug =+        defaultDebugParams+            { debugKeyLogger = keyLog+            , debugError = printError+            , debugTraceKey = traceKey+            }++chooseALPN :: [ByteString] -> IO ByteString+chooseALPN protos+    | "http/1.1" `elem` protos = return "http/1.1"+    | otherwise = return ""++newSessionManager :: IO SessionManager+newSessionManager = do+    ref <- newIORef M.empty+    return $+        noSessionManager+            { sessionResume = \key -> do+                M.lookup key <$> readIORef ref+            , sessionResumeOnlyOnce = \key -> do+                M.lookup key <$> readIORef ref+            , sessionEstablish = \key val -> do+                atomicModifyIORef' ref $ \m -> (M.insert key val m, Nothing)+            , sessionInvalidate = \key -> do+                atomicModifyIORef' ref $ \m -> (M.delete key m, ())+            , sessionUseTicket = False+            }