summaryrefslogtreecommitdiff
path: root/src/Network/Tox
diff options
context:
space:
mode:
Diffstat (limited to 'src/Network/Tox')
-rw-r--r--src/Network/Tox/Crypto/Handlers.hs102
-rw-r--r--src/Network/Tox/Crypto/Transport.hs41
2 files changed, 131 insertions, 12 deletions
diff --git a/src/Network/Tox/Crypto/Handlers.hs b/src/Network/Tox/Crypto/Handlers.hs
index 29f55e54..12818b2e 100644
--- a/src/Network/Tox/Crypto/Handlers.hs
+++ b/src/Network/Tox/Crypto/Handlers.hs
@@ -12,27 +12,48 @@ import Crypto.Hash
12import Control.Applicative 12import Control.Applicative
13import Control.Monad 13import Control.Monad
14import Data.Time.Clock.POSIX 14import Data.Time.Clock.POSIX
15import qualified Data.ByteString as B
16import Control.Lens
17import Data.Function
18import Data.Serialize as S
19import Data.Word
20import GHC.Conc (unsafeIOToSTM)
21
22-- util, todo: move to another module
23maybeToEither (Just x) = Right x
24maybeToEither Nothing = Left "maybeToEither"
15 25
16data NetCryptoSessionStatus = Unaccepted | Accepted | Confirmed 26data NetCryptoSessionStatus = Unaccepted | Accepted | Confirmed
17 deriving (Eq,Ord,Show,Enum) 27 deriving (Eq,Ord,Show,Enum)
18 28
19 29
30type IOHook addr x = addr -> x -> IO (Maybe (x -> x))
31type NetCryptoHook = IOHook SockAddr CryptoMessage
32
33
20data NetCryptoSession = NCrypto { ncState :: TVar NetCryptoSessionStatus 34data 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
30data NetCryptoSessions = NCSessions { netCryptoSessions :: TVar (Map.Map SockAddr NetCryptoSession) 45data NetCryptoSessions = NCSessions { netCryptoSessions :: TVar (Map.Map SockAddr NetCryptoSession)
31 , transportCrypto :: TransportCrypto 46 , transportCrypto :: TransportCrypto
47 , defaultHooks :: Map.Map MessageType [NetCryptoHook]
32 } 48 }
33 49
34newSessionsState :: TransportCrypto -> IO NetCryptoSessions 50newSessionsState :: TransportCrypto -> Map.Map MessageType [NetCryptoHook] -> IO NetCryptoSessions
35newSessionsState crypto = error "todo" 51newSessionsState crypto hooks = do
52 x <- atomically $ newTVar Map.empty
53 return NCSessions { netCryptoSessions = x
54 , transportCrypto = crypto
55 , defaultHooks = hooks
56 }
36 57
37data HandshakeParams 58data HandshakeParams
38 = HParam 59 = HParam
@@ -48,6 +69,7 @@ newHandShakeData = error "todo"
48 69
49cryptoNetHandler :: NetCryptoSessions -> SockAddr -> NetCrypto -> IO (Maybe (NetCrypto -> NetCrypto)) 70cryptoNetHandler :: NetCryptoSessions -> SockAddr -> NetCrypto -> IO (Maybe (NetCrypto -> NetCrypto))
50cryptoNetHandler sessions addr (NetHandshake (Handshake (Cookie n24 ecookie) nonce24 encrypted)) = do 71cryptoNetHandler 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,77 @@ 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
121cryptoNetHandler sessions addr (NetCrypto (CryptoPacket nonce16 encrypted)) = do 145cryptoNetHandler 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 (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 -- if lossy, just run hook
181 if lossyness (msgID cm) == Lossy
182 then
183 case Map.lookup (cm ^. messageType) hookmap of
184 Nothing -> return Nothing -- discarding, because no hooks
185 Just hooks -> flip fix (hooks,cm) $ \loop (hooks,msg) -> do
186 let _ = cm :: CryptoMessage
187 case (hooks,cm) of
188 ([],_) -> return Nothing
189 (hook:more,cm) -> do
190 r <- hook addr cm :: IO (Maybe (CryptoMessage -> CryptoMessage))
191 case r of
192 Just f -> loop (more,f cm)
193 Nothing -> return Nothing -- message consumed
194 else -- Lossless message, so try to restore sequence
195 error "todo try to restore sequence of lossless messages"
196 where
197 last2Bytes :: Nonce24 -> Word
198 last2Bytes (Nonce24 bs) = case S.decode (B.drop 22 bs) of
199 Right n -> n
200 _ -> error "unreachable-last2Bytes"
201 dATA_NUM_THRESHOLD = 21845 -- = 65535 / 3
202
203
204cryptoDefaultHooks = Map.empty
diff --git a/src/Network/Tox/Crypto/Transport.hs b/src/Network/Tox/Crypto/Transport.hs
index 634a7a43..7bc6e67f 100644
--- a/src/Network/Tox/Crypto/Transport.hs
+++ b/src/Network/Tox/Crypto/Transport.hs
@@ -43,6 +43,8 @@ module Network.Tox.Crypto.Transport
43 , sizedN 43 , sizedN
44 , sizedAtLeastN 44 , sizedAtLeastN
45 , isIndirectGrpChat 45 , isIndirectGrpChat
46 , LossyOrLossless(..)
47 , lossyness
46 ) where 48 ) where
47 49
48import Crypto.Tox 50import Crypto.Tox
@@ -148,6 +150,10 @@ data CryptoData = CryptoData
148 , bufferData :: CryptoMessage 150 , bufferData :: CryptoMessage
149 } 151 }
150 152
153instance Serialize CryptoData where
154 get = CryptoData <$> get <*> get <*> get
155 put (CryptoData start end dta) = put start >> put end >> put dta
156
151-- The 'UserStatus' equivalent in Presence is: 157-- The 'UserStatus' equivalent in Presence is:
152-- 158--
153-- data JabberShow = Offline 159-- data JabberShow = Offline
@@ -176,6 +182,25 @@ instance Sized CryptoMessage where
176 TwoByte {} -> 2 182 TwoByte {} -> 2
177 UpToN { msgBytes = bs } -> 1 + B.length bs 183 UpToN { msgBytes = bs } -> 1 + B.length bs
178 184
185instance Serialize CryptoMessage where
186 get = do
187 i <- get :: Get MessageID
188 n <- remaining
189 case msgSizeParam i of
190 Just (True,1) -> return $ OneByte i
191 Just (True,2) -> TwoByte i <$> get
192 _ -> UpToN i <$> getByteString n
193
194 put (OneByte i) = putWord8 (fromIntegral . fromEnum $ i)
195 put (TwoByte i b) = do putWord8 (fromIntegral . fromEnum $ i)
196 putWord8 b
197 put (UpToN i x) = do putWord8 (fromIntegral . fromEnum $ i)
198 putByteString x
199
200instance Serialize MessageID where
201 get = toEnum . fromIntegral <$> getWord8
202 put x = putWord8 (fromIntegral . fromEnum $ x)
203
179erCompat :: String -> a 204erCompat :: String -> a
180erCompat lens = error $ "Use of '" ++ lens ++ "' lens on incompatible CryptoMessage type" 205erCompat lens = error $ "Use of '" ++ lens ++ "' lens on incompatible CryptoMessage type"
181 206
@@ -381,6 +406,12 @@ data MessageType = Msg MessageID
381 | GrpMsg MessageName 406 | GrpMsg MessageName
382 deriving (Eq,Show) 407 deriving (Eq,Show)
383 408
409instance Ord MessageType where
410 compare (Msg x) (Msg y) = compare x y
411 compare (GrpMsg x) (GrpMsg y) = compare x y
412 compare (Msg _) (GrpMsg _) = LT
413 compare (GrpMsg _) (Msg _) = GT
414
384class HasMessageType x where 415class HasMessageType x where
385 getMessageType :: x -> MessageType 416 getMessageType :: x -> MessageType
386 setMessageType :: x -> MessageType -> x 417 setMessageType :: x -> MessageType -> x
@@ -564,6 +595,16 @@ isIndirectGrpChat MESSAGE_GROUPCHAT = True
564isIndirectGrpChat LOSSY_GROUPCHAT = True 595isIndirectGrpChat LOSSY_GROUPCHAT = True
565isIndirectGrpChat _ = False 596isIndirectGrpChat _ = False
566 597
598data LossyOrLossless = UnknownLossyness | Lossless | Lossy
599 deriving (Eq,Ord,Enum,Show,Bounded)
600
601lossyness :: MessageID -> LossyOrLossless
602lossyness (fromEnum -> x) | x < 3 = Lossy
603lossyness (fromEnum -> x) | x >= 16, x < 192 = Lossless
604lossyness (fromEnum -> x) | x >= 192, x < 255 = Lossy
605lossyness (fromEnum -> 255) = Lossless
606lossyness _ = UnknownLossyness
607
567-- TODO: Flesh this out. 608-- TODO: Flesh this out.
568data MessageID -- First byte indicates data 609data MessageID -- First byte indicates data
569 = Padding -- ^ 0 padding (skipped until we hit a non zero (data id) byte) 610 = Padding -- ^ 0 padding (skipped until we hit a non zero (data id) byte)