diff options
author | joe <joe@jerkface.net> | 2017-10-31 14:21:28 -0400 |
---|---|---|
committer | joe <joe@jerkface.net> | 2017-10-31 14:21:28 -0400 |
commit | 903612a4758c2286d9c4cc0a004cc5863abead9a (patch) | |
tree | db70241e32ea04e03a2a11d7b24d7c02f750233f /src/Network/Tox | |
parent | 03df596d08530bd2a49d792c6cf79c16f9a865ec (diff) | |
parent | 4727b4e84e7539ba0a71ae4a3baa069aa19a19a3 (diff) |
Merge branch 'dht-rewrite' of jerkface.net:repo/bittorrent into dht-rewrite
Diffstat (limited to 'src/Network/Tox')
-rw-r--r-- | src/Network/Tox/Crypto/Handlers.hs | 103 | ||||
-rw-r--r-- | src/Network/Tox/Crypto/Transport.hs | 46 |
2 files changed, 137 insertions, 12 deletions
diff --git a/src/Network/Tox/Crypto/Handlers.hs b/src/Network/Tox/Crypto/Handlers.hs index 29f55e54..c5476371 100644 --- a/src/Network/Tox/Crypto/Handlers.hs +++ b/src/Network/Tox/Crypto/Handlers.hs | |||
@@ -12,27 +12,48 @@ import Crypto.Hash | |||
12 | import Control.Applicative | 12 | import Control.Applicative |
13 | import Control.Monad | 13 | import Control.Monad |
14 | import Data.Time.Clock.POSIX | 14 | import Data.Time.Clock.POSIX |
15 | import qualified Data.ByteString as B | ||
16 | import Control.Lens | ||
17 | import Data.Function | ||
18 | import Data.Serialize as S | ||
19 | import Data.Word | ||
20 | import GHC.Conc (unsafeIOToSTM) | ||
21 | |||
22 | -- util, todo: move to another module | ||
23 | maybeToEither (Just x) = Right x | ||
24 | maybeToEither Nothing = Left "maybeToEither" | ||
15 | 25 | ||
16 | data NetCryptoSessionStatus = Unaccepted | Accepted | Confirmed | 26 | data NetCryptoSessionStatus = Unaccepted | Accepted | Confirmed |
17 | deriving (Eq,Ord,Show,Enum) | 27 | deriving (Eq,Ord,Show,Enum) |
18 | 28 | ||
19 | 29 | ||
30 | type IOHook addr x = addr -> x -> IO (Maybe (x -> x)) | ||
31 | type NetCryptoHook = IOHook SockAddr CryptoData | ||
32 | |||
33 | |||
20 | data NetCryptoSession = NCrypto { ncState :: TVar NetCryptoSessionStatus | 34 | data NetCryptoSession = NCrypto { ncState :: TVar NetCryptoSessionStatus |
21 | , ncTheirPacketNonce:: TVar Nonce24 -- base nonce + packet number | 35 | , ncTheirBaseNonce :: TVar Nonce24 -- base nonce + packet number |
22 | , ncMyPacketNonce :: TVar Nonce24 -- base nonce + packet number | 36 | , ncMyPacketNonce :: TVar Nonce24 -- base nonce + packet number |
23 | , ncHandShake :: TVar (Maybe (Handshake Encrypted)) | 37 | , ncHandShake :: TVar (Maybe (Handshake Encrypted)) |
24 | , ncCookie :: TVar (Maybe Cookie) | 38 | , ncCookie :: TVar (Maybe Cookie) |
25 | , ncTheirSessionPublic :: Maybe PublicKey | 39 | , ncTheirSessionPublic :: Maybe PublicKey |
26 | , ncSessionSecret :: SecretKey | 40 | , ncSessionSecret :: SecretKey |
27 | , ncSockAddr :: SockAddr | 41 | , ncSockAddr :: SockAddr |
42 | , ncHooks :: TVar (Map.Map MessageType [NetCryptoHook]) | ||
28 | } | 43 | } |
29 | 44 | ||
30 | data NetCryptoSessions = NCSessions { netCryptoSessions :: TVar (Map.Map SockAddr NetCryptoSession) | 45 | data NetCryptoSessions = NCSessions { netCryptoSessions :: TVar (Map.Map SockAddr NetCryptoSession) |
31 | , transportCrypto :: TransportCrypto | 46 | , transportCrypto :: TransportCrypto |
47 | , defaultHooks :: Map.Map MessageType [NetCryptoHook] | ||
32 | } | 48 | } |
33 | 49 | ||
34 | newSessionsState :: TransportCrypto -> IO NetCryptoSessions | 50 | newSessionsState :: TransportCrypto -> Map.Map MessageType [NetCryptoHook] -> IO NetCryptoSessions |
35 | newSessionsState crypto = error "todo" | 51 | newSessionsState crypto hooks = do |
52 | x <- atomically $ newTVar Map.empty | ||
53 | return NCSessions { netCryptoSessions = x | ||
54 | , transportCrypto = crypto | ||
55 | , defaultHooks = hooks | ||
56 | } | ||
36 | 57 | ||
37 | data HandshakeParams | 58 | data HandshakeParams |
38 | = HParam | 59 | = HParam |
@@ -48,6 +69,7 @@ newHandShakeData = error "todo" | |||
48 | 69 | ||
49 | cryptoNetHandler :: NetCryptoSessions -> SockAddr -> NetCrypto -> IO (Maybe (NetCrypto -> NetCrypto)) | 70 | cryptoNetHandler :: NetCryptoSessions -> SockAddr -> NetCrypto -> IO (Maybe (NetCrypto -> NetCrypto)) |
50 | cryptoNetHandler sessions addr (NetHandshake (Handshake (Cookie n24 ecookie) nonce24 encrypted)) = do | 71 | cryptoNetHandler sessions addr (NetHandshake (Handshake (Cookie n24 ecookie) nonce24 encrypted)) = do |
72 | -- Handle Handshake Message | ||
51 | let crypto = transportCrypto sessions | 73 | let crypto = transportCrypto sessions |
52 | allsessions = netCryptoSessions sessions | 74 | allsessions = netCryptoSessions sessions |
53 | anyRight xs f = foldr1 (<|>) $ map f xs | 75 | anyRight xs f = foldr1 (<|>) $ map f xs |
@@ -81,17 +103,18 @@ cryptoNetHandler sessions addr (NetHandshake (Handshake (Cookie n24 ecookie) non | |||
81 | Left _ -> return () | 103 | Left _ -> return () |
82 | Right hp@(HParam | 104 | Right hp@(HParam |
83 | { hpTheirBaseNonce = Just theirBaseNonce | 105 | { hpTheirBaseNonce = Just theirBaseNonce |
84 | , hpOtherCookie = Just otherCookie | 106 | , hpOtherCookie = Just otherCookie |
85 | , hpTheirSessionKeyPublic = theirSessionKey | 107 | , hpTheirSessionKeyPublic = theirSessionKey |
86 | , hpMySecretKey = key | 108 | , hpMySecretKey = key |
87 | , hpCookieRemotePubkey = remotePublicKey | 109 | , hpCookieRemotePubkey = remotePublicKey |
88 | , hpCookieRemoteDhtkey = remoteDhtPublicKey | 110 | , hpCookieRemoteDhtkey = remoteDhtPublicKey |
89 | }) -> do | 111 | }) -> do |
90 | sessionsmap <- atomically $ readTVar allsessions | 112 | sessionsmap <- atomically $ readTVar allsessions |
113 | -- Do a lookup, in case we decide to handle the update case differently | ||
91 | case Map.lookup addr sessionsmap of | 114 | case Map.lookup addr sessionsmap of |
92 | Nothing -> do -- create new session | 115 | _ -> do -- create new session |
93 | ncState0 <- atomically $ newTVar Accepted | 116 | ncState0 <- atomically $ newTVar Accepted |
94 | ncTheirPacketNonce0 <- atomically $ newTVar theirBaseNonce | 117 | ncTheirBaseNonce0 <- atomically $ newTVar theirBaseNonce |
95 | n24 <- atomically $ transportNewNonce crypto | 118 | n24 <- atomically $ transportNewNonce crypto |
96 | let myhandshakeData = newHandShakeData crypto hp | 119 | let myhandshakeData = newHandShakeData crypto hp |
97 | plain = encodePlain myhandshakeData | 120 | plain = encodePlain myhandshakeData |
@@ -105,22 +128,78 @@ cryptoNetHandler sessions addr (NetHandshake (Handshake (Cookie n24 ecookie) non | |||
105 | ncHandShake0 <- atomically $ newTVar (Just myhandshake) | 128 | ncHandShake0 <- atomically $ newTVar (Just myhandshake) |
106 | cookie0 <- atomically $ newTVar (Just otherCookie) | 129 | cookie0 <- atomically $ newTVar (Just otherCookie) |
107 | newsession <- generateSecretKey | 130 | newsession <- generateSecretKey |
131 | ncHooks0 <- atomically $ newTVar (defaultHooks sessions) | ||
108 | let netCryptoSession = | 132 | let netCryptoSession = |
109 | NCrypto { ncState = ncState0 | 133 | NCrypto { ncState = ncState0 |
110 | , ncTheirPacketNonce= ncTheirPacketNonce0 | 134 | , ncTheirBaseNonce= ncTheirBaseNonce0 |
111 | , ncMyPacketNonce = ncMyPacketNonce0 | 135 | , ncMyPacketNonce = ncMyPacketNonce0 |
112 | , ncHandShake = ncHandShake0 | 136 | , ncHandShake = ncHandShake0 |
113 | , ncCookie = cookie0 | 137 | , ncCookie = cookie0 |
114 | , ncTheirSessionPublic = Just theirSessionKey | 138 | , ncTheirSessionPublic = Just theirSessionKey |
115 | , ncSessionSecret = newsession | 139 | , ncSessionSecret = newsession |
116 | , ncSockAddr = addr | 140 | , ncSockAddr = addr |
141 | , ncHooks = ncHooks0 | ||
117 | } | 142 | } |
118 | atomically $ modifyTVar allsessions (Map.insert addr netCryptoSession) | 143 | atomically $ modifyTVar allsessions (Map.insert addr netCryptoSession) |
119 | Just netCryptoSession -> return () -- TODO: UPdate existing session | ||
120 | return Nothing | 144 | return Nothing |
121 | cryptoNetHandler sessions addr (NetCrypto (CryptoPacket nonce16 encrypted)) = do | 145 | cryptoNetHandler sessions addr (NetCrypto (CryptoPacket nonce16 encrypted)) = do |
122 | let crypto = transportCrypto sessions | 146 | let crypto = transportCrypto sessions |
147 | allsessions = netCryptoSessions sessions | ||
148 | sessionsmap <- atomically $ readTVar allsessions | ||
123 | -- Handle Encrypted Message | 149 | -- Handle Encrypted Message |
124 | -- TODO | 150 | case Map.lookup addr sessionsmap of |
125 | return Nothing | 151 | Nothing -> return Nothing -- drop packet, we have no session |
126 | -- cryptoNetHandler _ _ _ = return $ Just id | 152 | Just (NCrypto {ncState, ncHooks,ncSessionSecret,ncTheirSessionPublic,ncTheirBaseNonce}) -> do |
153 | theirBaseNonce <- atomically $ readTVar ncTheirBaseNonce | ||
154 | -- Try to decrypt message | ||
155 | let diff :: Word16 | ||
156 | diff = nonce16 - fromIntegral (last2Bytes theirBaseNonce) -- truncating to Word16 | ||
157 | tempNonce <- addtoNonce24 theirBaseNonce (fromIntegral diff) -- expanding to Word | ||
158 | let lr = do -- Either Monad -- | ||
159 | pubkey <- maybeToEither ncTheirSessionPublic | ||
160 | decodePlain =<< decrypt (computeSharedSecret ncSessionSecret pubkey tempNonce) encrypted | ||
161 | case lr of | ||
162 | Left _ -> return Nothing -- decryption failed, ignore packet | ||
163 | Right cd@(CryptoData {bufferStart, bufferEnd, bufferData=cm}) -> do -- decryption succeeded, | ||
164 | -- TODO: Why do I need bufferStart & bufferEnd? | ||
165 | -- | ||
166 | -- buffer_start = highest packet number handled + 1 | ||
167 | -- , recvbuffers buffer_start | ||
168 | -- | ||
169 | -- bufferEnd = sendbuffer buffer_end if lossy, otherwise packet number | ||
170 | -- update ncTheirBaseNonce if necessary | ||
171 | when (diff > 2 * dATA_NUM_THRESHOLD)$ | ||
172 | atomically $ do | ||
173 | y <- readTVar ncTheirBaseNonce | ||
174 | -- all because Storable forces IO... | ||
175 | x <- unsafeIOToSTM $ addtoNonce24 y (fromIntegral dATA_NUM_THRESHOLD) | ||
176 | writeTVar ncTheirBaseNonce y | ||
177 | -- then set session confirmed, | ||
178 | atomically $ writeTVar ncState Confirmed | ||
179 | hookmap <- atomically $ readTVar ncHooks | ||
180 | -- run hook | ||
181 | flip fix cd $ \lookupAgain cd -> do | ||
182 | let msgTyp = cd ^. messageType | ||
183 | case Map.lookup msgTyp hookmap of | ||
184 | Nothing -> return Nothing -- discarding, because no hooks | ||
185 | Just hooks -> flip fix (hooks,cd,msgTyp) $ \loop (hooks,cd,typ) -> do | ||
186 | let _ = cd :: CryptoData | ||
187 | case (hooks,cd) of | ||
188 | ([],_) -> return Nothing | ||
189 | (hook:more,cd) -> do | ||
190 | r <- hook addr cd :: IO (Maybe (CryptoData -> CryptoData)) | ||
191 | case r of | ||
192 | Just f -> let newcd = f cd | ||
193 | newtyp = newcd ^. messageType | ||
194 | in if newtyp == typ then loop (more,newcd,newtyp) | ||
195 | else lookupAgain newcd | ||
196 | Nothing -> return Nothing -- message consumed | ||
197 | where | ||
198 | last2Bytes :: Nonce24 -> Word | ||
199 | last2Bytes (Nonce24 bs) = case S.decode (B.drop 22 bs) of | ||
200 | Right n -> n | ||
201 | _ -> error "unreachable-last2Bytes" | ||
202 | dATA_NUM_THRESHOLD = 21845 -- = 65535 / 3 | ||
203 | |||
204 | |||
205 | cryptoDefaultHooks = Map.empty | ||
diff --git a/src/Network/Tox/Crypto/Transport.hs b/src/Network/Tox/Crypto/Transport.hs index 634a7a43..8739c853 100644 --- a/src/Network/Tox/Crypto/Transport.hs +++ b/src/Network/Tox/Crypto/Transport.hs | |||
@@ -1,4 +1,5 @@ | |||
1 | {-# LANGUAGE KindSignatures #-} | 1 | {-# LANGUAGE KindSignatures #-} |
2 | {-# LANGUAGE NamedFieldPuns #-} | ||
2 | {-# LANGUAGE ViewPatterns #-} | 3 | {-# LANGUAGE ViewPatterns #-} |
3 | {-# LANGUAGE FlexibleInstances #-} | 4 | {-# LANGUAGE FlexibleInstances #-} |
4 | {-# LANGUAGE LambdaCase #-} | 5 | {-# LANGUAGE LambdaCase #-} |
@@ -43,6 +44,8 @@ module Network.Tox.Crypto.Transport | |||
43 | , sizedN | 44 | , sizedN |
44 | , sizedAtLeastN | 45 | , sizedAtLeastN |
45 | , isIndirectGrpChat | 46 | , isIndirectGrpChat |
47 | , LossyOrLossless(..) | ||
48 | , lossyness | ||
46 | ) where | 49 | ) where |
47 | 50 | ||
48 | import Crypto.Tox | 51 | import Crypto.Tox |
@@ -148,6 +151,10 @@ data CryptoData = CryptoData | |||
148 | , bufferData :: CryptoMessage | 151 | , bufferData :: CryptoMessage |
149 | } | 152 | } |
150 | 153 | ||
154 | instance Serialize CryptoData where | ||
155 | get = CryptoData <$> get <*> get <*> get | ||
156 | put (CryptoData start end dta) = put start >> put end >> put dta | ||
157 | |||
151 | -- The 'UserStatus' equivalent in Presence is: | 158 | -- The 'UserStatus' equivalent in Presence is: |
152 | -- | 159 | -- |
153 | -- data JabberShow = Offline | 160 | -- data JabberShow = Offline |
@@ -176,6 +183,25 @@ instance Sized CryptoMessage where | |||
176 | TwoByte {} -> 2 | 183 | TwoByte {} -> 2 |
177 | UpToN { msgBytes = bs } -> 1 + B.length bs | 184 | UpToN { msgBytes = bs } -> 1 + B.length bs |
178 | 185 | ||
186 | instance Serialize CryptoMessage where | ||
187 | get = do | ||
188 | i <- get :: Get MessageID | ||
189 | n <- remaining | ||
190 | case msgSizeParam i of | ||
191 | Just (True,1) -> return $ OneByte i | ||
192 | Just (True,2) -> TwoByte i <$> get | ||
193 | _ -> UpToN i <$> getByteString n | ||
194 | |||
195 | put (OneByte i) = putWord8 (fromIntegral . fromEnum $ i) | ||
196 | put (TwoByte i b) = do putWord8 (fromIntegral . fromEnum $ i) | ||
197 | putWord8 b | ||
198 | put (UpToN i x) = do putWord8 (fromIntegral . fromEnum $ i) | ||
199 | putByteString x | ||
200 | |||
201 | instance Serialize MessageID where | ||
202 | get = toEnum . fromIntegral <$> getWord8 | ||
203 | put x = putWord8 (fromIntegral . fromEnum $ x) | ||
204 | |||
179 | erCompat :: String -> a | 205 | erCompat :: String -> a |
180 | erCompat lens = error $ "Use of '" ++ lens ++ "' lens on incompatible CryptoMessage type" | 206 | erCompat lens = error $ "Use of '" ++ lens ++ "' lens on incompatible CryptoMessage type" |
181 | 207 | ||
@@ -381,6 +407,12 @@ data MessageType = Msg MessageID | |||
381 | | GrpMsg MessageName | 407 | | GrpMsg MessageName |
382 | deriving (Eq,Show) | 408 | deriving (Eq,Show) |
383 | 409 | ||
410 | instance Ord MessageType where | ||
411 | compare (Msg x) (Msg y) = compare x y | ||
412 | compare (GrpMsg x) (GrpMsg y) = compare x y | ||
413 | compare (Msg _) (GrpMsg _) = LT | ||
414 | compare (GrpMsg _) (Msg _) = GT | ||
415 | |||
384 | class HasMessageType x where | 416 | class HasMessageType x where |
385 | getMessageType :: x -> MessageType | 417 | getMessageType :: x -> MessageType |
386 | setMessageType :: x -> MessageType -> x | 418 | setMessageType :: x -> MessageType -> x |
@@ -403,6 +435,10 @@ instance HasMessageType CryptoMessage where | |||
403 | setMessageType (TwoByte mid0 x) (Msg mid) = UpToN mid (B.singleton x) | 435 | setMessageType (TwoByte mid0 x) (Msg mid) = UpToN mid (B.singleton x) |
404 | setMessageType (UpToN mid0 x) (Msg mid) = UpToN mid x | 436 | setMessageType (UpToN mid0 x) (Msg mid) = UpToN mid x |
405 | 437 | ||
438 | instance HasMessageType CryptoData where | ||
439 | getMessageType (CryptoData { bufferData }) = getMessageType bufferData | ||
440 | setMessageType cd@(CryptoData { bufferData=bd }) typ = cd { bufferData=setMessageType bd typ } | ||
441 | |||
406 | -- | This lens should always succeed on CryptoMessage | 442 | -- | This lens should always succeed on CryptoMessage |
407 | messageType :: (Functor f, HasMessageType x) => (MessageType -> f MessageType) -> (x -> f x) | 443 | messageType :: (Functor f, HasMessageType x) => (MessageType -> f MessageType) -> (x -> f x) |
408 | messageType = lens getMessageType setMessageType | 444 | messageType = lens getMessageType setMessageType |
@@ -564,6 +600,16 @@ isIndirectGrpChat MESSAGE_GROUPCHAT = True | |||
564 | isIndirectGrpChat LOSSY_GROUPCHAT = True | 600 | isIndirectGrpChat LOSSY_GROUPCHAT = True |
565 | isIndirectGrpChat _ = False | 601 | isIndirectGrpChat _ = False |
566 | 602 | ||
603 | data LossyOrLossless = UnknownLossyness | Lossless | Lossy | ||
604 | deriving (Eq,Ord,Enum,Show,Bounded) | ||
605 | |||
606 | lossyness :: MessageID -> LossyOrLossless | ||
607 | lossyness (fromEnum -> x) | x < 3 = Lossy | ||
608 | lossyness (fromEnum -> x) | x >= 16, x < 192 = Lossless | ||
609 | lossyness (fromEnum -> x) | x >= 192, x < 255 = Lossy | ||
610 | lossyness (fromEnum -> 255) = Lossless | ||
611 | lossyness _ = UnknownLossyness | ||
612 | |||
567 | -- TODO: Flesh this out. | 613 | -- TODO: Flesh this out. |
568 | data MessageID -- First byte indicates data | 614 | data MessageID -- First byte indicates data |
569 | = Padding -- ^ 0 padding (skipped until we hit a non zero (data id) byte) | 615 | = Padding -- ^ 0 padding (skipped until we hit a non zero (data id) byte) |