summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Crypto/Tox.hs16
-rw-r--r--src/Network/Tox.hs4
-rw-r--r--src/Network/Tox/Crypto/Handlers.hs102
-rw-r--r--src/Network/Tox/Crypto/Transport.hs41
4 files changed, 144 insertions, 19 deletions
diff --git a/src/Crypto/Tox.hs b/src/Crypto/Tox.hs
index a25f9f4f..9f86f6a4 100644
--- a/src/Crypto/Tox.hs
+++ b/src/Crypto/Tox.hs
@@ -7,7 +7,7 @@
7{-# LANGUAGE DeriveTraversable #-} 7{-# LANGUAGE DeriveTraversable #-}
8{-# LANGUAGE ExplicitNamespaces #-} 8{-# LANGUAGE ExplicitNamespaces #-}
9{-# LANGUAGE TypeOperators #-} 9{-# LANGUAGE TypeOperators #-}
10{-# LANGUAGE MagicHash, UnboxedTuples #-} 10{-# LANGUAGE MagicHash, UnboxedTuples, BangPatterns #-}
11module Crypto.Tox 11module Crypto.Tox
12 ( PublicKey 12 ( PublicKey
13 , publicKey 13 , publicKey
@@ -35,6 +35,7 @@ module Crypto.Tox
35 , Nonce8(..) 35 , Nonce8(..)
36 , Nonce24(..) 36 , Nonce24(..)
37 , incrementNonce24 37 , incrementNonce24
38 , addtoNonce24
38 , Nonce32(..) 39 , Nonce32(..)
39 , getRemainingEncrypted 40 , getRemainingEncrypted
40 , putEncrypted 41 , putEncrypted
@@ -258,17 +259,18 @@ hsalsa20 k n = BA.append a b
258newtype Nonce24 = Nonce24 ByteString 259newtype Nonce24 = Nonce24 ByteString
259 deriving (Eq, Ord, ByteArrayAccess,Data) 260 deriving (Eq, Ord, ByteArrayAccess,Data)
260 261
261incrementNonce24 :: Nonce24 -> IO Nonce24 262addtoNonce24 :: Nonce24 -> Word -> IO Nonce24
262incrementNonce24 (Nonce24 n24) = Nonce24 <$> BA.copy n24 init 263addtoNonce24 (Nonce24 n24) n = Nonce24 <$> BA.copy n24 init
263 where 264 where
264 init :: Ptr Word -> IO () 265 init :: Ptr Word -> IO ()
265 init ptr | fitsInInt (Proxy :: Proxy Word64) = do 266 init ptr | fitsInInt (Proxy :: Proxy Word64) = do
266 let frmBE64 = fromIntegral . fromBE64 . fromIntegral 267 let frmBE64 = fromIntegral . fromBE64 . fromIntegral
267 tBE64 = fromIntegral . toBE64 . fromIntegral 268 tBE64 = fromIntegral . toBE64 . fromIntegral
269 !(W# input) = n
268 W# w1 <- frmBE64 <$> peek ptr 270 W# w1 <- frmBE64 <$> peek ptr
269 W# w2 <- frmBE64 <$> peekElemOff ptr 1 271 W# w2 <- frmBE64 <$> peekElemOff ptr 1
270 W# w3 <- frmBE64 <$> peekElemOff ptr 2 272 W# w3 <- frmBE64 <$> peekElemOff ptr 2
271 let (# overflw, sum #) = plusWord2# w3 (int2Word# 1#) 273 let (# overflw, sum #) = plusWord2# w3 input
272 (# overflw', sum' #) = plusWord2# w2 overflw 274 (# overflw', sum' #) = plusWord2# w2 overflw
273 (# discard, sum'' #) = plusWord2# w1 overflw' 275 (# discard, sum'' #) = plusWord2# w1 overflw'
274 poke ptr $ tBE64 (W# sum'') 276 poke ptr $ tBE64 (W# sum'')
@@ -278,13 +280,14 @@ incrementNonce24 (Nonce24 n24) = Nonce24 <$> BA.copy n24 init
278 init ptr | fitsInInt (Proxy :: Proxy Word32) = do 280 init ptr | fitsInInt (Proxy :: Proxy Word32) = do
279 let frmBE32 = fromIntegral . fromBE32 . fromIntegral 281 let frmBE32 = fromIntegral . fromBE32 . fromIntegral
280 tBE32 = fromIntegral . toBE32 . fromIntegral 282 tBE32 = fromIntegral . toBE32 . fromIntegral
283 !(W# input) = n
281 W# w1 <- frmBE32 <$> peek ptr 284 W# w1 <- frmBE32 <$> peek ptr
282 W# w2 <- frmBE32 <$> peekElemOff ptr 1 285 W# w2 <- frmBE32 <$> peekElemOff ptr 1
283 W# w3 <- frmBE32 <$> peekElemOff ptr 2 286 W# w3 <- frmBE32 <$> peekElemOff ptr 2
284 W# w4 <- frmBE32 <$> peekElemOff ptr 3 287 W# w4 <- frmBE32 <$> peekElemOff ptr 3
285 W# w5 <- frmBE32 <$> peekElemOff ptr 4 288 W# w5 <- frmBE32 <$> peekElemOff ptr 4
286 W# w6 <- frmBE32 <$> peekElemOff ptr 5 289 W# w6 <- frmBE32 <$> peekElemOff ptr 5
287 let (# overflw_, sum_ #) = plusWord2# w6 (int2Word# 1#) 290 let (# overflw_, sum_ #) = plusWord2# w6 input
288 (# overflw__, sum__ #) = plusWord2# w5 overflw_ 291 (# overflw__, sum__ #) = plusWord2# w5 overflw_
289 (# overflw___, sum___ #) = plusWord2# w6 overflw__ 292 (# overflw___, sum___ #) = plusWord2# w6 overflw__
290 (# overflw, sum #) = plusWord2# w3 overflw___ 293 (# overflw, sum #) = plusWord2# w3 overflw___
@@ -298,6 +301,9 @@ incrementNonce24 (Nonce24 n24) = Nonce24 <$> BA.copy n24 init
298 pokeElemOff ptr 5 $ tBE32 (W# sum_) 301 pokeElemOff ptr 5 $ tBE32 (W# sum_)
299 init _ = error "incrementNonce24: I only support 64 and 32 bits" 302 init _ = error "incrementNonce24: I only support 64 and 32 bits"
300 303
304incrementNonce24 :: Nonce24 -> IO Nonce24
305incrementNonce24 nonce24 = addtoNonce24 nonce24 1
306
301quoted :: ShowS -> ShowS 307quoted :: ShowS -> ShowS
302quoted shows s = '"':shows ('"':s) 308quoted shows s = '"':shows ('"':s)
303 309
diff --git a/src/Network/Tox.hs b/src/Network/Tox.hs
index 87835769..2f778874 100644
--- a/src/Network/Tox.hs
+++ b/src/Network/Tox.hs
@@ -89,7 +89,7 @@ import Crypto.Tox
89import Data.Word64Map (fitsInInt) 89import Data.Word64Map (fitsInInt)
90import qualified Data.Word64Map (empty) 90import qualified Data.Word64Map (empty)
91import Network.Tox.Crypto.Transport (NetCrypto) 91import Network.Tox.Crypto.Transport (NetCrypto)
92import Network.Tox.Crypto.Handlers (newSessionsState, cryptoNetHandler) 92import Network.Tox.Crypto.Handlers (newSessionsState, cryptoNetHandler, cryptoDefaultHooks)
93import qualified Network.Tox.DHT.Handlers as DHT 93import qualified Network.Tox.DHT.Handlers as DHT
94import qualified Network.Tox.DHT.Transport as DHT 94import qualified Network.Tox.DHT.Transport as DHT
95import Network.Tox.NodeId 95import Network.Tox.NodeId
@@ -309,7 +309,7 @@ newTox keydb addr = do
309 (const id) 309 (const id)
310 310
311 roster <- newRoster 311 roster <- newRoster
312 sessionsState <- newSessionsState crypto 312 sessionsState <- newSessionsState crypto cryptoDefaultHooks
313 return Tox 313 return Tox
314 { toxDHT = dhtclient 314 { toxDHT = dhtclient
315 , toxOnion = onionclient 315 , toxOnion = onionclient
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)