diff options
author | Joe Crayne <joe@jerkface.net> | 2018-09-08 04:31:03 -0400 |
---|---|---|
committer | Joe Crayne <joe@jerkface.net> | 2018-09-08 04:41:04 -0400 |
commit | e5add92a477060d9bba10de7b980c89c24012691 (patch) | |
tree | 8a7508a0ad55339ce6dac9a31f593f2dd776ee41 | |
parent | 55c5a979b3b25e1b7a13b1361c5f9cf1222f1653 (diff) |
HandshakeCache remembers sent handshake data.
-rw-r--r-- | HandshakeCache.hs | 130 |
1 files changed, 130 insertions, 0 deletions
diff --git a/HandshakeCache.hs b/HandshakeCache.hs new file mode 100644 index 00000000..6f9d466f --- /dev/null +++ b/HandshakeCache.hs | |||
@@ -0,0 +1,130 @@ | |||
1 | module HandshakeCache where | ||
2 | |||
3 | import Control.Concurrent.STM | ||
4 | import Control.Monad | ||
5 | import Data.Functor.Identity | ||
6 | import qualified Data.Map.Strict as Map | ||
7 | ;import Data.Map.Strict (Map) | ||
8 | import Data.Time.Clock.POSIX | ||
9 | import Network.Socket | ||
10 | import Data.Bool | ||
11 | |||
12 | import Crypto.Hash | ||
13 | |||
14 | import Crypto.Tox | ||
15 | import qualified Data.MinMaxPSQ as MM | ||
16 | ;import Data.MinMaxPSQ (MinMaxPSQ') | ||
17 | import DPut | ||
18 | import Network.Tox.Crypto.Transport (Handshake, HandshakeData (..)) | ||
19 | import Network.Tox.DHT.Handlers (createCookieSTM) | ||
20 | import Network.Tox.DHT.Transport (Cookie (..), CookieData (..), NodeInfo, | ||
21 | key2id, nodeInfo) | ||
22 | import Network.Tox.Handshake | ||
23 | |||
24 | data HandshakeCache = HandshakeCache | ||
25 | { -- Note that currently we are storing sent handshakes keyed by the | ||
26 | -- remotely issued cookie. This probably means that it's possible for | ||
27 | -- one your contacts that you are trying to open a session with to | ||
28 | -- prevent you from opening a session with another contact if they know | ||
29 | -- the cookie that person issued you. | ||
30 | hscTable :: TVar (MinMaxPSQ' (Cookie Encrypted) POSIXTime (SecretKey,HandshakeData)) | ||
31 | , hscSend :: SockAddr -> Handshake Encrypted -> IO () | ||
32 | , hscCrypto :: TransportCrypto | ||
33 | , hscPendingCookies :: TVar (Map (PublicKey,PublicKey) ()) | ||
34 | } | ||
35 | |||
36 | |||
37 | newHandshakeCache :: TransportCrypto -> (SockAddr -> Handshake Encrypted -> IO ()) -> IO HandshakeCache | ||
38 | newHandshakeCache crypto send = atomically $ do | ||
39 | tbl <- newTVar MM.empty | ||
40 | pcs <- newTVar Map.empty | ||
41 | return HandshakeCache | ||
42 | { hscTable = tbl | ||
43 | , hscSend = send | ||
44 | , hscCrypto = crypto | ||
45 | , hscPendingCookies = pcs | ||
46 | } | ||
47 | |||
48 | getSentHandshake :: HandshakeCache | ||
49 | -> SecretKey | ||
50 | -> SockAddr | ||
51 | -> Cookie Identity -- locally issued | ||
52 | -> Cookie Encrypted -- remotely issued | ||
53 | -> IO (Maybe (SecretKey, HandshakeData)) | ||
54 | getSentHandshake hscache me their_addr (Cookie _ (Identity cd)) ecookie = do | ||
55 | now <- getPOSIXTime | ||
56 | io <- atomically $ do | ||
57 | m <- checkExpiry now . MM.lookup' ecookie <$> readTVar (hscTable hscache) | ||
58 | case m of | ||
59 | Just s -> return $ return $ Just s | ||
60 | Nothing -> do | ||
61 | let them = longTermKey cd | ||
62 | case nodeInfo (key2id $ dhtKey cd) their_addr of | ||
63 | Left _ -> return $ return Nothing -- Non-internet address. | ||
64 | Right their_node -> do | ||
65 | (s,hs) <- cacheHandshakeSTM hscache me them their_node ecookie now | ||
66 | return $ do | ||
67 | hscSend hscache their_addr hs | ||
68 | return $ Just s | ||
69 | io | ||
70 | |||
71 | |||
72 | checkExpiry :: POSIXTime -> Maybe (POSIXTime,r) -> Maybe r | ||
73 | checkExpiry now m = do | ||
74 | (tm,s) <- m | ||
75 | guard $ tm + 5 {- seconds -} > now | ||
76 | return s | ||
77 | |||
78 | hashCookie :: HashAlgorithm a => Cookie Encrypted -> Digest a | ||
79 | hashCookie (Cookie n24 encrypted) | ||
80 | = hashFinalize $ hashUpdate (hashUpdate hashInit n24) encrypted | ||
81 | |||
82 | cacheHandshakeSTM :: HandshakeCache | ||
83 | -> SecretKey -- ^ my ToxID key | ||
84 | -> PublicKey -- ^ them | ||
85 | -> NodeInfo -- ^ their DHT node | ||
86 | -> Cookie Encrypted -- ^ issued to me by them | ||
87 | -> POSIXTime -- ^ current time | ||
88 | -> STM ((SecretKey,HandshakeData), Handshake Encrypted) | ||
89 | cacheHandshakeSTM hscache me them their_node ecookie timestamp = do | ||
90 | newsession <- transportNewKey (hscCrypto hscache) | ||
91 | freshCookie <- createCookieSTM timestamp (hscCrypto hscache) their_node them | ||
92 | n24 <- transportNewNonce (hscCrypto hscache) | ||
93 | let hsdata = HandshakeData | ||
94 | { baseNonce = n24 | ||
95 | , sessionKey = toPublic newsession | ||
96 | , cookieHash = hashCookie ecookie | ||
97 | , otherCookie = freshCookie | ||
98 | } | ||
99 | hs <- encodeHandshake timestamp (hscCrypto hscache) me them ecookie hsdata | ||
100 | modifyTVar' (hscTable hscache) $ MM.insertTake' 20 ecookie (newsession,hsdata) timestamp | ||
101 | return ((newsession,hsdata),hs) | ||
102 | |||
103 | cacheHandshake :: HandshakeCache | ||
104 | -> SecretKey | ||
105 | -> PublicKey | ||
106 | -> NodeInfo | ||
107 | -> Cookie Encrypted | ||
108 | -> IO (Handshake Encrypted) | ||
109 | cacheHandshake hscache me them their_node ecookie = do | ||
110 | timestamp <- getPOSIXTime | ||
111 | dput XNetCrypto $ "cacheHandshake " ++ show (key2id them,ecookie) | ||
112 | atomically $ snd <$> cacheHandshakeSTM hscache me them their_node ecookie timestamp | ||
113 | |||
114 | haveCachedCookie :: HandshakeCache | ||
115 | -> PublicKey | ||
116 | -> PublicKey | ||
117 | -> STM Bool | ||
118 | haveCachedCookie hscache me them = do | ||
119 | m <- Map.lookup (me,them) <$> readTVar (hscPendingCookies hscache) | ||
120 | return $ maybe True (const False) m | ||
121 | |||
122 | |||
123 | setPendingCookie :: HandshakeCache | ||
124 | -> PublicKey | ||
125 | -> PublicKey | ||
126 | -> Bool | ||
127 | -> STM () | ||
128 | setPendingCookie hscache me them pending = do | ||
129 | modifyTVar' (hscPendingCookies hscache) $ Map.alter (const $ bool Nothing (Just ()) pending) | ||
130 | (me,them) | ||