summaryrefslogtreecommitdiff
path: root/dht/HandshakeCache.hs
blob: d9ffacab950daf9be12024b4a309e36e82838c43 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
module HandshakeCache where

import Control.Concurrent.STM
import Control.Monad
import Data.Functor.Identity
import qualified Data.Map.Strict as Map
         ;import Data.Map.Strict (Map)
import Data.Time.Clock.POSIX
import Network.Socket
import Data.Bool

import Crypto.Hash

import Crypto.Tox
import qualified Data.MinMaxPSQ     as MM
         ;import Data.MinMaxPSQ     (MinMaxPSQ')
import qualified Data.Tox.DHT.Multi as Multi
import DPut
import DebugTag
import Network.Tox.Crypto.Transport (Handshake, HandshakeData (..))
import Network.Tox.DHT.Handlers     (createCookieSTM)
import Network.Tox.DHT.Transport    (Cookie (..), CookieData (..), NodeInfo,
                                     key2id, nodeInfo)
import Network.Tox.Handshake

data HandshakeCache = HandshakeCache
    { -- Note that currently we are storing sent handshakes keyed by the
      -- locally issued cookie nonce.
      hscTable  :: TVar (MinMaxPSQ' Nonce24 POSIXTime (SecretKey,HandshakeData))
    , hscSend   :: Multi.SessionAddress -> Handshake Encrypted -> IO ()
    , hscCrypto :: TransportCrypto
    , hscPendingCookies :: TVar (Map (PublicKey,PublicKey) ())
    }


newHandshakeCache :: TransportCrypto -> (Multi.SessionAddress -> Handshake Encrypted -> IO ()) -> IO HandshakeCache
newHandshakeCache crypto send = atomically $ do
    tbl <- newTVar MM.empty
    pcs <- newTVar Map.empty
    return HandshakeCache
        { hscTable  = tbl
        , hscSend   = send
        , hscCrypto = crypto
        , hscPendingCookies = pcs
        }

getSentHandshake :: HandshakeCache
                    -> SecretKey
                    -> Multi.SessionAddress
                    -> Cookie Identity -- locally issued
                    -> Cookie Encrypted -- remotely issued
                    -> IO (Maybe (SecretKey, HandshakeData))
getSentHandshake hscache me their_addr (Cookie n24 (Identity cd)) ecookie = do
    now <- getPOSIXTime
    io <- atomically $ do
        m <- checkExpiry now . MM.lookup' n24 <$> readTVar (hscTable hscache)
        case m of
            Just s  -> return $ return $ Just s
            Nothing -> do
                let them = longTermKey cd
                case Multi.nodeInfo (key2id $ dhtKey cd) their_addr of
                    Left _ -> return $ return Nothing -- Non-internet address.
                    Right their_node -> do
                        (s,hs) <- cacheHandshakeSTM hscache me them their_node ecookie now
                        return $ do
                            dput XNetCrypto $ "getSentHandshake sending new handshake."
                            hscSend hscache their_addr hs
                            return $ Just s
    r <- io
    dput XNetCrypto $ "getSentHandshake me="++show (key2id $ toPublic me)++" their_addr="++show their_addr++" --> " ++ show r
    return r


checkExpiry :: POSIXTime -> Maybe (POSIXTime,r) -> Maybe r
checkExpiry now m = do
    (tm,s) <- m
    guard $ tm + 5 {- seconds -} > now
    return s

hashCookie :: HashAlgorithm a => Cookie Encrypted -> Digest a
hashCookie (Cookie n24 encrypted)
    = hashFinalize $ hashUpdate (hashUpdate hashInit n24) encrypted

cacheHandshakeSTM :: HandshakeCache
                    -> SecretKey        -- ^ my ToxID key
                    -> PublicKey        -- ^ them
                    -> Multi.NodeInfo   -- ^ their DHT node
                    -> Cookie Encrypted -- ^ issued to me by them
                    -> POSIXTime        -- ^ current time
                    -> STM ((SecretKey,HandshakeData), Handshake Encrypted)
cacheHandshakeSTM hscache me them their_node ecookie timestamp = do
    newsession <- transportNewKey (hscCrypto hscache)
    freshCookie <- createCookieSTM timestamp (hscCrypto hscache) their_node them
    n24 <- transportNewNonce (hscCrypto hscache)
    let hsdata = HandshakeData
            { baseNonce   = n24
            , sessionKey  = toPublic newsession
            , cookieHash  = hashCookie ecookie
            , otherCookie = freshCookie
            }
    hs <- encodeHandshake timestamp (hscCrypto hscache) me them ecookie hsdata
    let Cookie cnonce _ = freshCookie
    modifyTVar' (hscTable hscache) $ MM.insertTake' 20 cnonce (newsession,hsdata) timestamp
    return ((newsession,hsdata),hs)

cacheHandshake :: HandshakeCache
                    -> SecretKey
                    -> PublicKey
                    -> Multi.NodeInfo
                    -> Cookie Encrypted
                    -> IO (Handshake Encrypted)
cacheHandshake hscache me them their_node ecookie = do
    timestamp <- getPOSIXTime
    dput XNetCrypto $ "cacheHandshake " ++ show (key2id them,ecookie)
    atomically $ snd <$> cacheHandshakeSTM hscache me them their_node ecookie timestamp

getPendingCookieFlag :: HandshakeCache
                    -> PublicKey
                    -> PublicKey
                    -> STM Bool
getPendingCookieFlag hscache me them = do
    m <- Map.lookup (me,them) <$> readTVar (hscPendingCookies hscache)
    return $ maybe False (const True) m


setPendingCookie :: HandshakeCache
                    -> PublicKey
                    -> PublicKey
                    -> Bool
                    -> STM ()
setPendingCookie hscache me them pending = do
    modifyTVar' (hscPendingCookies hscache) $ Map.alter (const $ bool Nothing (Just ()) pending)
                                                        (me,them)