on master help sign in
Merge pull request #630 from kazu-yamamoto/tls-session-manager

TLS session manager

commit703fd8a6875204f311a2fa51e65f5e5041176b2d authorMichael Snoyman <michael@snoyman.com> dateTue 4 Jul 2017, 05:24 (9 years ago) parent65f18a1f68fe53
4 files changed+193
tls-session-manager/LICENSE+29 tls-session-manager/Network/TLS/SessionManager.hs+132 tls-session-manager/Setup.hs+2 tls-session-manager/tls-session-manager.cabal+30
tls-session-manager/LICENSEnew file+29
@@ -0,0 +1,29 @@
1+Copyright (c) 2017, IIJ Innovation Institute Inc.
2+All rights reserved.
3+
4+Redistribution and use in source and binary forms, with or without
5+modification, are permitted provided that the following conditions
6+are met:
7+
8+ * Redistributions of source code must retain the above copyright
9+ notice, this list of conditions and the following disclaimer.
10+ * Redistributions in binary form must reproduce the above copyright
11+ notice, this list of conditions and the following disclaimer in
12+ the documentation and/or other materials provided with the
13+ distribution.
14+ * Neither the name of the copyright holders nor the names of its
15+ contributors may be used to endorse or promote products derived
16+ from this software without specific prior written permission.
17+
18+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29+POSSIBILITY OF SUCH DAMAGE.
tls-session-manager/Network/TLS/SessionManager.hsnew file+132
@@ -0,0 +1,132 @@
1+{-# LANGUAGE BangPatterns #-}
2+
3+-- | In-memory TLS session manager.
4+--
5+-- * Limitation: you can set the maximum size of the session data database.
6+-- * Automatic pruning: old session data over their lifetime are pruned automatically.
7+-- * Energy saving: no dedicate pruning thread is running when the size of session data database is zero.
8+-- * (Replay resistance: each session data is used at most once to prevent replay attacks against 0RTT early data of TLS 1.3.)
9+
10+module Network.TLS.SessionManager (
11+ Config(..)
12+ , defaultConfig
13+ , newSessionManager
14+ ) where
15+
16+import Control.Exception (assert)
17+import Control.Reaper
18+import Data.IORef
19+import Data.Int (Int64)
20+import Data.List (foldl')
21+import Data.OrdPSQ (OrdPSQ)
22+import qualified Data.OrdPSQ as Q
23+import Network.TLS (SessionID, SessionData, SessionManager(..))
24+import qualified System.Clock as C
25+
26+----------------------------------------------------------------
27+
28+-- | Configuration for session managers.
29+data Config = Config {
30+ -- | Ticket lifetime in seconds.
31+ ticketLifetime :: !Int
32+ -- | Pruning delay in seconds. This is set to 'reaperDelay'.
33+ , pruningDelay :: !Int
34+ -- | The limit size of session data entries.
35+ , dbMaxSize :: !Int
36+ }
37+
38+-- | Lifetime: 1 day , delay: 10 minutes, limit: 10,000 entries.
39+defaultConfig :: Config
40+defaultConfig = Config {
41+ ticketLifetime = 86400
42+ , pruningDelay = 6000
43+ , dbMaxSize = 10000
44+ }
45+
46+----------------------------------------------------------------
47+
48+type Sec = Int64
49+type Value = (SessionData, IORef Availability)
50+type DB = OrdPSQ SessionID Sec Value
51+type Item = (SessionID, Sec, Value, Operation)
52+
53+data Operation = Add | Del
54+data Use = SingleUse | MultipleUse
55+data Availability = Fresh | Used
56+
57+----------------------------------------------------------------
58+
59+-- | Creating an in-memory session manager.
60+newSessionManager :: Config -> IO SessionManager
61+newSessionManager conf = do
62+ let lifetime = fromIntegral $ ticketLifetime conf
63+ maxsiz = dbMaxSize conf
64+ reaper <- mkReaper defaultReaperSettings {
65+ reaperEmpty = Q.empty
66+ , reaperCons = cons maxsiz
67+ , reaperAction = clean
68+ , reaperNull = Q.null
69+ , reaperDelay = pruningDelay conf * 1000000
70+ }
71+ return SessionManager {
72+ sessionResume = resume reaper MultipleUse
73+ , sessionEstablish = establish reaper lifetime
74+ , sessionInvalidate = invalidate reaper
75+ }
76+
77+cons :: Int -> Item -> DB -> DB
78+cons lim (k,t,v,Add) db
79+ | lim <= 0 = Q.empty
80+ | Q.size db == lim = case Q.minView db of
81+ Nothing -> assert False $ Q.insert k t v Q.empty
82+ Just (_,_,_,db') -> Q.insert k t v db'
83+ | otherwise = Q.insert k t v db
84+cons _ (k,_,_,Del) db = Q.delete k db
85+
86+clean :: DB -> IO (DB -> DB)
87+clean olddb = do
88+ currentTime <- C.sec <$> C.getTime C.Monotonic
89+ let !pruned = snd $ Q.atMostView currentTime olddb
90+ return $ merge pruned
91+ where
92+ ins db (k,p,v) = Q.insert k p v db
93+ -- There is not 'merge' API.
94+ -- We hope that newdb is smaller than pruned.
95+ merge pruned newdb = foldl' ins pruned entries
96+ where
97+ entries = Q.toList newdb
98+
99+----------------------------------------------------------------
100+
101+establish :: Reaper DB Item -> Sec
102+ -> SessionID -> SessionData -> IO ()
103+establish reaper lifetime k sd = do
104+ ref <- newIORef Fresh
105+ !p <- ((+ lifetime) . C.sec) <$> C.getTime C.Monotonic
106+ let !v = (sd,ref)
107+ reaperAdd reaper (k,p,v,Add)
108+
109+resume :: Reaper DB Item -> Use
110+ -> SessionID -> IO (Maybe SessionData)
111+resume reaper use k = do
112+ db <- reaperRead reaper
113+ case Q.lookup k db of
114+ Nothing -> return Nothing
115+ Just (p,v@(sd,ref)) -> do
116+ case use of
117+ SingleUse -> do
118+ available <- atomicModifyIORef' ref check
119+ reaperAdd reaper (k,p,v,Del)
120+ return $ if available then Just sd else Nothing
121+ MultipleUse -> return $ Just sd
122+ where
123+ check Fresh = (Used,True)
124+ check Used = (Used,False)
125+
126+invalidate :: Reaper DB Item
127+ -> SessionID -> IO ()
128+invalidate reaper k = do
129+ db <- reaperRead reaper
130+ case Q.lookup k db of
131+ Nothing -> return ()
132+ Just (p,v) -> reaperAdd reaper (k,p,v,Del)
tls-session-manager/Setup.hsnew file+2
@@ -0,0 +1,2 @@
1+import Distribution.Simple
2+main = defaultMain
tls-session-manager/tls-session-manager.cabalnew file+30
@@ -0,0 +1,30 @@
1+-- Initial tls-session-manager.cabal generated by cabal init. For further
2+-- documentation, see http://haskell.org/cabal/users-guide/
3+
4+name: tls-session-manager
5+version: 0.0.0.0
6+synopsis: In-memory TLS session manager
7+description: TLS session manager with limitation, automatic pruning, replay resistance and energy saving
8+license: BSD3
9+license-file: LICENSE
10+author: Kazu Yamamoto
11+maintainer: kazu@iij.ad.jp
12+-- copyright:
13+category: Web
14+build-type: Simple
15+extra-source-files: ChangeLog.md
16+cabal-version: >= 1.10
17+
18+library
19+ exposed-modules: Network.TLS.SessionManager
20+ -- other-modules:
21+ -- other-extensions:
22+ build-depends: base >= 4.9 && < 5
23+ , auto-update
24+ , clock
25+ , psqueues >= 0.2.3
26+ , time
27+ , tls
28+ -- hs-source-dirs:
29+ default-language: Haskell2010
30+ ghc-options: -Wall