summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjoe <joe@jerkface.net>2017-08-30 06:17:23 -0400
committerjoe <joe@jerkface.net>2017-08-30 06:17:23 -0400
commit2d0d30e70bea230ede343bd1cc2700b11becb494 (patch)
tree1650c5339a90c29a33624a5caf0f8841d6741023
parent9d16ca2529a184309cbd50bd3b6bc228b31c5e91 (diff)
More progress on ToxTransport and related modules.
-rw-r--r--ToxAddress.hs7
-rw-r--r--ToxCrypto.hs9
-rw-r--r--ToxPacket.hs46
-rw-r--r--ToxTransport.hs234
-rwxr-xr-xc4
-rwxr-xr-xci5
6 files changed, 283 insertions, 22 deletions
diff --git a/ToxAddress.hs b/ToxAddress.hs
index 08c9031b..a0d5345d 100644
--- a/ToxAddress.hs
+++ b/ToxAddress.hs
@@ -39,6 +39,7 @@ import qualified Text.ParserCombinators.ReadP as RP
39import Text.Read 39import Text.Read
40import GHC.TypeLits 40import GHC.TypeLits
41import Crypto.PubKey.Curve25519 41import Crypto.PubKey.Curve25519
42import Crypto.Error.Types (CryptoFailable(..))
42 43
43data Address 44data Address
44 = DHTNode NodeInfo -- A direct DHT exchange. 45 = DHTNode NodeInfo -- A direct DHT exchange.
@@ -74,14 +75,12 @@ instance Ord NodeId where
74zeroID :: NodeId 75zeroID :: NodeId
75zeroID = NodeId $ throwCryptoError $ publicKey $ B.replicate 32 0 76zeroID = NodeId $ throwCryptoError $ publicKey $ B.replicate 32 0
76 77
77{-
78instance Read NodeId where 78instance Read NodeId where
79 readsPrec _ str 79 readsPrec _ str
80 | (bs, xs) <- Base16.decode $ C8.pack str 80 | (bs, xs) <- Base16.decode $ C8.pack str
81 , B.length bs == 32 81 , CryptoPassed pub <- publicKey bs -- B.length bs == 32
82 = [ (NodeId bs, drop 64 str) ] 82 = [ (NodeId pub, drop 64 str) ]
83 | otherwise = [] 83 | otherwise = []
84-}
85 84
86instance Show NodeId where 85instance Show NodeId where
87 show (NodeId bs) = C8.unpack $ Base16.encode $ BA.convert bs 86 show (NodeId bs) = C8.unpack $ Base16.encode $ BA.convert bs
diff --git a/ToxCrypto.hs b/ToxCrypto.hs
index 98e02e91..cae7e251 100644
--- a/ToxCrypto.hs
+++ b/ToxCrypto.hs
@@ -17,6 +17,8 @@ module ToxCrypto
17 , getRemainingEncrypted 17 , getRemainingEncrypted
18 , putEncrypted 18 , putEncrypted
19 , Auth 19 , Auth
20 , Sized(..)
21 , Size(..)
20 ) where 22 ) where
21 23
22import qualified Crypto.Cipher.Salsa as Salsa 24import qualified Crypto.Cipher.Salsa as Salsa
@@ -81,6 +83,13 @@ instance Sized a => Serialize (Encrypted a) where
81 ConstSize n -> Encrypted <$> getBytes (16 + n) -- 16 extra for Poly1305 mac 83 ConstSize n -> Encrypted <$> getBytes (16 + n) -- 16 extra for Poly1305 mac
82 put = putEncrypted 84 put = putEncrypted
83 85
86instance (Sized a, Sized b) => Sized (a,b) where
87 size = case (size :: Size a, size :: Size b) of
88 (ConstSize a , ConstSize b) -> ConstSize $ a + b
89 (VarSize f , ConstSize b) -> VarSize $ \(a, _) -> f a + b
90 (ConstSize a , VarSize g) -> VarSize $ \(_, b) -> a + g b
91 (VarSize f , VarSize g) -> VarSize $ \(a, b) -> f a + g b
92
84getRemainingEncrypted :: Get (Encrypted a) 93getRemainingEncrypted :: Get (Encrypted a)
85getRemainingEncrypted = Encrypted <$> (remaining >>= getBytes) 94getRemainingEncrypted = Encrypted <$> (remaining >>= getBytes)
86 95
diff --git a/ToxPacket.hs b/ToxPacket.hs
index d10a7597..bc20f480 100644
--- a/ToxPacket.hs
+++ b/ToxPacket.hs
@@ -71,6 +71,52 @@ data Assym a = Assym
71 , assymData :: a 71 , assymData :: a
72 } 72 }
73 73
74newtype GetNodes = GetNodes NodeId
75 deriving (Eq,Ord,Show,Read,S.Serialize)
76
74newtype SendNodes = SendNodes [NodeInfo] 77newtype SendNodes = SendNodes [NodeInfo]
75 deriving (Eq,Ord,Show,Read) 78 deriving (Eq,Ord,Show,Read)
76 79
80instance S.Serialize SendNodes where
81 get = do
82 cnt <- S.get :: S.Get Word8
83 ns <- sequence $ replicate (fromIntegral cnt) S.get
84 return $ SendNodes ns
85
86 put (SendNodes ns) = do
87 let ns' = take 4 ns
88 S.put (fromIntegral (length ns') :: Word8)
89 mapM_ S.put ns'
90
91data Ping = Ping deriving Show
92data Pong = Pong deriving Show
93
94instance S.Serialize Ping where
95 get = do w8 <- S.get
96 if (w8 :: Word8) /= 0
97 then fail "Malformed ping."
98 else return Ping
99 put Ping = S.put (0 :: Word8)
100
101instance S.Serialize Pong where
102 get = do w8 <- S.get
103 if (w8 :: Word8) /= 1
104 then fail "Malformed pong."
105 else return Pong
106 put Pong = S.put (1 :: Word8)
107
108newtype CookieRequest = CookieRequest PublicKey
109newtype CookieResponse = CookieResponse Cookie
110
111data Cookie = Cookie Nonce24 (Encrypted CookieData)
112
113instance Sized Cookie where size = ConstSize 112 -- 24 byte nonce + 88 byte cookie data
114
115data CookieData = CookieData -- 16 (mac)
116 { cookieTime :: Word64 -- 8
117 , longTermKey :: PublicKey -- 32
118 , dhtKey :: PublicKey -- + 32
119 } -- = 88 bytes when encrypted.
120
121instance Sized CookieRequest where
122 size = ConstSize 64 -- 32 byte key + 32 byte padding
diff --git a/ToxTransport.hs b/ToxTransport.hs
index 1b2bcbe4..a927e55a 100644
--- a/ToxTransport.hs
+++ b/ToxTransport.hs
@@ -2,11 +2,14 @@
2{-# LANGUAGE DataKinds,KindSignatures #-} 2{-# LANGUAGE DataKinds,KindSignatures #-}
3{-# LANGUAGE GADTs #-} 3{-# LANGUAGE GADTs #-}
4{-# LANGUAGE TypeOperators #-} 4{-# LANGUAGE TypeOperators #-}
5{-# LANGUAGE LambdaCase #-}
6{-# LANGUAGE GeneralizedNewtypeDeriving #-}
7{-# LANGUAGE TupleSections #-}
5module ToxTransport where 8module ToxTransport where
6 9
7import Network.QueryResponse 10import Network.QueryResponse
8import ToxCrypto 11import ToxCrypto
9import ToxAddress as Tox hiding (ReturnPath) 12import ToxAddress as Tox hiding (ReturnPath,OnionToOwner)
10import ToxPacket 13import ToxPacket
11 14
12import Control.Concurrent.STM 15import Control.Concurrent.STM
@@ -14,9 +17,11 @@ import qualified Data.ByteString as B
14 ;import Data.ByteString (ByteString) 17 ;import Data.ByteString (ByteString)
15import Data.Word 18import Data.Word
16import Network.Socket 19import Network.Socket
17import Data.Serialize as S (decode, Serialize, get, put, Get, Put) 20import Data.Serialize as S (decode, Serialize, get, put, Get, Put, runGet)
18import GHC.TypeLits 21import GHC.TypeLits
19import Data.Typeable 22import Data.Typeable
23import Control.Applicative
24import Control.Arrow
20 25
21newtype SymmetricKey = SymmetricKey ByteString 26newtype SymmetricKey = SymmetricKey ByteString
22 27
@@ -44,6 +49,9 @@ data Message = Todo | DHTReq DHTRequest | AnnounceReq AnnounceRequest
44 49
45-- awaitMessage :: forall a. (Maybe (Either err (x, addr)) -> IO a) -> IO a 50-- awaitMessage :: forall a. (Maybe (Either err (x, addr)) -> IO a) -> IO a
46 51
52type UDPTransport = Transport String SockAddr ByteString
53
54{-
47toxTransport :: TransportCrypto -> Transport String SockAddr ByteString -> Transport String Tox.Address Message 55toxTransport :: TransportCrypto -> Transport String SockAddr ByteString -> Transport String Tox.Address Message
48toxTransport crypto (Transport await send close) = Transport await' send' close 56toxTransport crypto (Transport await send close) = Transport await' send' close
49 where 57 where
@@ -51,18 +59,171 @@ toxTransport crypto (Transport await send close) = Transport await' send' close
51 await' forMe = fix $ await . handleOnion crypto forMe 59 await' forMe = fix $ await . handleOnion crypto forMe
52 60
53 send' = _todo 61 send' = _todo
62-}
63toxTransport ::
64 TransportCrypto
65 -> UDPTransport
66 -> IO ( Transport String NodeInfo (DirectMessage Encrypted8)
67 , Transport String OnionToOwner (OnionMessage Encrypted)
68 , Transport String SockAddr ByteString )
69toxTransport crypto udp = do
70 (dht,udp1) <- partitionTransport parseDHTAddr encodeDHTAddr id $ handleOnion crypto udp
71 (onion,udp2) <- partitionTransport parseOnionAddr encodeOnionAddr id udp1
72 return (dht,onion,udp2)
73
54 74
55type HandleHi a = Maybe (Either String (Message, Tox.Address)) -> IO a 75type HandleHi a = Maybe (Either String (Message, Tox.Address)) -> IO a
56type HandleLo a = Maybe (Either String (ByteString, SockAddr)) -> IO a 76type HandleLo a = Maybe (Either String (ByteString, SockAddr)) -> IO a
57 77
78data DirectMessage (f :: * -> *)
79 = DirectPing (Assym (f Ping))
80 | DirectPong (Assym (f Pong))
81 | DirectGetNodes (Assym (f GetNodes))
82 | DirectSendNodes (Assym (f SendNodes))
83 | DirectCookieRequest (Assym (f CookieRequest))
84 | DirectCookie Nonce24 (f Cookie)
85 | DirectDHTRequest PublicKey (Assym (f DHTRequest))
86
87instance Sized GetNodes where
88 size = ConstSize 32 -- TODO This right?
89
90instance Sized SendNodes where
91 size = VarSize $ \(SendNodes ns) -> _nodeFormatSize * length ns
92
93instance Sized Ping where size = ConstSize 1
94instance Sized Pong where size = ConstSize 1
95
96newtype Encrypted8 a = E8 (Encrypted (a,Nonce8))
97 deriving Serialize
98
99-- instance (Sized a, Sized b) => Sized (a,b) where size = _todo
100
101getDirect :: Sized a => Get (Assym (Encrypted8 a))
102getDirect = _todo
103
104getOnionAssym :: Get (Assym (Encrypted DataToRoute))
105getOnionAssym = _todo
106
107getCookie :: Get (Nonce24, Encrypted8 Cookie)
108getCookie = get
109
110getDHTReqest :: Get (PublicKey, Assym (Encrypted8 DHTRequest))
111getDHTReqest = _todo
112
113fanGet :: ByteString -> Get x -> (x -> a) -> (x -> b) -> Either String (a,b)
114fanGet bs getIt f nid = fmap (f &&& nid) $ runGet getIt bs
115
116-- Throws an error if called with a non-internet socket.
117direct :: Sized a => ByteString
118 -> SockAddr
119 -> (Assym (Encrypted8 a)
120 -> DirectMessage Encrypted8)
121 -> Either String (DirectMessage Encrypted8, NodeInfo)
122direct bs saddr f = fanGet bs getDirect f (asymNodeInfo saddr)
123
124-- Throws an error if called with a non-internet socket.
125asymNodeInfo saddr asym = either (error . mappend "asymNodeInfo: ") id $ nodeInfo (NodeId $ senderKey asym) saddr
126
127-- Throws an error if called with a non-internet socket.
128noReplyAddr saddr = either (error . mappend "noReplyAddr: ") id $ nodeInfo zeroID saddr
129
130parseDHTAddr :: (ByteString, SockAddr) -> Either (DirectMessage Encrypted8,NodeInfo) (ByteString,SockAddr)
131parseDHTAddr (msg,saddr)
132 | Just (typ,bs) <- B.uncons msg
133 , let right = Right (msg,saddr)
134 left = either (const right) Left
135 = case typ of
136 0x00 -> left $ direct bs saddr DirectPing
137 0x01 -> left $ direct bs saddr DirectPong
138 0x02 -> left $ direct bs saddr DirectGetNodes
139 0x04 -> left $ direct bs saddr DirectSendNodes
140 0x18 -> left $ direct bs saddr DirectCookieRequest
141 0x19 -> left $ fanGet bs getCookie (uncurry DirectCookie) (const $ noReplyAddr saddr)
142 0x20 -> left $ fanGet bs getDHTReqest (uncurry DirectDHTRequest) (asymNodeInfo saddr . snd)
143 _ -> right
144
145encodeDHTAddr :: (DirectMessage Encrypted8,NodeInfo) -> (ByteString, SockAddr)
146encodeDHTAddr = _todo
147
148
149data OnionMessage (f :: * -> *)
150 = OnionAnnounce (Assym (f (AnnounceRequest,Nonce8)))
151 | OnionAnnounceResponse Nonce8 Nonce24 (f AnnounceResponse)
152 | OnionToRoute PublicKey (Assym (f DataToRoute)) -- destination key, aliased Assym
153 | OnionToRouteResponse (Assym (f DataToRoute))
154
155data OnionToOwner = OnionToOwner NodeInfo (ReturnPath 3)
156 | OnionToMe SockAddr -- SockAddr is immediate peer in route
157
158onionToOwner assym ret3 saddr = do
159 ni <- nodeInfo (NodeId $ senderKey assym) saddr
160 return $ OnionToOwner ni ret3
161
162onion bs saddr getf = do (f,(assym,ret3)) <- runGet ((,) <$> getf <*> getOnionRequest) bs
163 oaddr <- onionToOwner assym ret3 saddr
164 return (f assym, oaddr)
165
166parseOnionAddr :: (ByteString, SockAddr) -> Either (OnionMessage Encrypted,OnionToOwner) (ByteString,SockAddr)
167parseOnionAddr (msg,saddr)
168 | Just (typ,bs) <- B.uncons msg
169 , let right = Right (msg,saddr)
170 query = either (const right) Left
171 response = either (const right) (Left . (, OnionToMe saddr))
172 = case typ of
173 0x83 -> query $ onion bs saddr (pure OnionAnnounce) -- Announce Request
174 0x85 -> query $ onion bs saddr (OnionToRoute <$> getPublicKey) -- Onion Data Request
175 0x84 -> response $ runGet (OnionAnnounceResponse <$> get <*> get <*> get) bs -- Announce Response
176 0x86 -> response $ runGet (OnionToRouteResponse <$> getOnionAssym) bs -- Onion Data Response
177 _ -> right
178
179encodeOnionAddr :: (OnionMessage Encrypted,OnionToOwner) -> (ByteString, SockAddr)
180encodeOnionAddr = _todo
181
182
183data CookieAddress = WithoutCookie NodeInfo
184 | CookieAddress Cookie SockAddr
185
186-- Handshake packet:
187-- [uint8_t 26] (0x1a)
188-- [Cookie]
189-- [nonce (24 bytes)]
190-- [Encrypted message containing:
191-- [24 bytes base nonce]
192-- [session public key of the peer (32 bytes)]
193-- [sha512 hash of the entire Cookie sitting outside the encrypted part]
194-- [Other Cookie (used by the other to respond to the handshake packet)]
195-- ]
196
197-- cookie response packet (161 bytes):
198--
199-- [uint8_t 25]
200-- [Random nonce (24 bytes)]
201-- [Encrypted message containing:
202-- [Cookie]
203-- [uint64_t echo id (that was sent in the request)]
204-- ]
205--
206-- Encrypted message is encrypted with the exact same symmetric key as the
207-- cookie request packet it responds to but with a different nonce.
208-- (Encrypted message is encrypted with reqesters's DHT private key,
209-- responders's DHT public key and the nonce.)
210--
211-- Since we don't receive the public key, we will need to lookup the key by
212-- the SockAddr... I don't understand why the CookieResponse message is
213-- special this way. TODO: implement a multimap (SockAddr -> SharedSecret)
214-- and wrap cookie queries with store/delete. TODO: Should the entire
215-- SharedScret cache be keyed on only SockAddr ? Perhaps the secret cache
216-- should be (NodeId -> Secret) and the cookie-request map should be
217-- (SockAddr -> NodeId)
218
58-- Byte value Packet Kind Return address 219-- Byte value Packet Kind Return address
59-- :----------- :-------------------- 220-- :----------- :--------------------
60-- `0x00` Ping Request DHTNode 221-- `0x00` Ping Request DHTNode
61-- `0x01` Ping Response - 222-- `0x01` Ping Response -
62-- `0x02` Nodes Request DHTNode 223-- `0x02` Nodes Request DHTNode
63-- `0x04` Nodes Response - 224-- `0x04` Nodes Response -
64-- `0x18` Cookie Request DHTNode, but without sending pubkey 225-- `0x18` Cookie Request DHTNode, but without sending pubkey in response
65-- `0x19` Cookie Response - 226-- `0x19` Cookie Response - (no pubkey)
66-- 227--
67-- `0x21` LAN Discovery DHTNode (No reply, port 33445, trigger Nodes Request/Response) 228-- `0x21` LAN Discovery DHTNode (No reply, port 33445, trigger Nodes Request/Response)
68-- 229--
@@ -86,19 +247,22 @@ type HandleLo a = Maybe (Either String (ByteString, SockAddr)) -> IO a
86-- `0x8d` Onion Response 2 -return 247-- `0x8d` Onion Response 2 -return
87-- `0x8e` Onion Response 1 -return 248-- `0x8e` Onion Response 1 -return
88 249
89handleOnion :: forall a. TransportCrypto -> HandleHi a -> IO a -> HandleLo a 250handleOnion :: TransportCrypto -> UDPTransport -> UDPTransport -- HandleHi a -> IO a -> HandleLo a
90handleOnion crypto forMe forThem (Just (Right (bs,saddr))) = case B.head bs of 251handleOnion crypto udp = udp { awaitMessage = await' }
91 0x20 -> forward forMe bs $ handleDHTRequest crypto saddr forMe forThem
92 0x80 -> forward forMe bs $ handleOnionRequest (Proxy :: Proxy 0) crypto saddr forThem
93 0x81 -> forward forMe bs $ handleOnionRequest (Proxy :: Proxy 1) crypto saddr forThem
94 0x82 -> forward forMe bs $ handleOnionRequest (Proxy :: Proxy 2) crypto saddr forThem
95 0x8c -> forward forMe bs $ handleOnionResponse (Proxy :: Proxy 3) crypto saddr forThem
96 0x8d -> forward forMe bs $ handleOnionResponse (Proxy :: Proxy 2) crypto saddr forThem
97 0x8e -> forward forMe bs $ handleOnionResponse (Proxy :: Proxy 1) crypto saddr forThem
98 typ -> go typ (B.tail bs)
99 where 252 where
100 go :: Word8 -> ByteString -> IO a 253 -- forMe :: HandleHi
101 go typ bs = forMe $ Just (parseMessage typ bs) 254 -- forThem :: handleLo
255 await' :: HandleLo a -> IO a
256 await' forThem = awaitMessage udp $ \case
257 m@(Just (Right (bs,saddr))) -> case B.head bs of
258 0x80 -> forward forThem bs $ handleOnionRequest (Proxy :: Proxy 0) crypto saddr (forThem m)
259 0x81 -> forward forThem bs $ handleOnionRequest (Proxy :: Proxy 1) crypto saddr (forThem m)
260 0x82 -> forward forThem bs $ handleOnionRequest (Proxy :: Proxy 2) crypto saddr (forThem m)
261 0x8c -> forward forThem bs $ handleOnionResponse (Proxy :: Proxy 3) crypto saddr (forThem m)
262 0x8d -> forward forThem bs $ handleOnionResponse (Proxy :: Proxy 2) crypto saddr (forThem m)
263 0x8e -> forward forThem bs $ handleOnionResponse (Proxy :: Proxy 1) crypto saddr (forThem m)
264 _ -> forThem m
265 m -> forThem m
102 266
103forward :: forall c b b1. 267forward :: forall c b b1.
104 Serialize b => 268 Serialize b =>
@@ -197,3 +361,41 @@ instance S.Serialize AnnounceRequest where
197 361
198getOnionRequest :: Get (Assym (Encrypted msg), ReturnPath 3) 362getOnionRequest :: Get (Assym (Encrypted msg), ReturnPath 3)
199getOnionRequest = _todo 363getOnionRequest = _todo
364
365data KeyRecord = NotStored Nonce32
366 | SendBackKey PublicKey
367 | Acknowledged Nonce32
368
369getPublicKey :: Get PublicKey
370getPublicKey = _todo
371
372putPublicKey :: PublicKey -> Put
373putPublicKey = _todo
374
375instance S.Serialize KeyRecord where
376 get = do
377 is_stored <- S.get :: S.Get Word8
378 case is_stored of
379 1 -> SendBackKey <$> getPublicKey
380 2 -> Acknowledged <$> S.get
381 _ -> NotStored <$> S.get
382 put (NotStored n32) = S.put (0 :: Word8) >> S.put n32
383 put (SendBackKey key) = S.put (1 :: Word8) >> putPublicKey key
384 put (Acknowledged n32) = S.put (2 :: Word8) >> S.put n32
385
386data AnnounceResponse = AnnounceResponse
387 { is_stored :: KeyRecord
388 , announceNodes :: SendNodes
389 }
390
391instance Sized AnnounceResponse where
392 size = VarSize $ \AnnounceResponse {} -> _todo
393
394instance S.Serialize AnnounceResponse where
395 get = AnnounceResponse <$> S.get <*> S.get
396 put (AnnounceResponse st ns) = S.put st >> S.put ns
397
398data DataToRoute = DataToRoute
399 { dataFromKey :: PublicKey
400 , dataToRoute :: Encrypted (Word8,ByteString)
401 }
diff --git a/c b/c
index f11856e2..a9d9755a 100755
--- a/c
+++ b/c
@@ -1,5 +1,7 @@
1#!/bin/sh 1#!/bin/sh
2compile=ghc
2defs="-DBENCODE_AESON -DTHREAD_DEBUG" 3defs="-DBENCODE_AESON -DTHREAD_DEBUG"
3hide="-hide-package crypto-random -hide-package crypto-api -hide-package crypto-numbers -hide-package cryptohash -hide-package prettyclass" 4hide="-hide-package crypto-random -hide-package crypto-api -hide-package crypto-numbers -hide-package cryptohash -hide-package prettyclass"
4cbits="cbits/*.c" 5cbits="cbits/*.c"
5ghc -fdefer-typed-holes -Wno-typed-holes -freverse-errors $hide -isrc -XOverloadedStrings -XRecordWildCards $defs $cbits "$@" 6# -Wno-typed-holes
7$compile -fdefer-typed-holes -freverse-errors $hide -isrc -XOverloadedStrings -XRecordWildCards $defs $cbits "$@"
diff --git a/ci b/ci
index 0a699757..0b74496b 100755
--- a/ci
+++ b/ci
@@ -1,4 +1,7 @@
1#!/bin/sh 1#!/bin/sh
2compile=ghci
2defs="-DBENCODE_AESON -DTHREAD_DEBUG" 3defs="-DBENCODE_AESON -DTHREAD_DEBUG"
3hide="-hide-package crypto-random -hide-package crypto-api -hide-package crypto-numbers -hide-package cryptohash -hide-package prettyclass" 4hide="-hide-package crypto-random -hide-package crypto-api -hide-package crypto-numbers -hide-package cryptohash -hide-package prettyclass"
4ghci -freverse-errors $hide -isrc -XOverloadedStrings -XRecordWildCards $defs "$@" 5# cbits="cbits/*.c"
6$compile -fdefer-typed-holes -Wno-typed-holes -freverse-errors $hide -isrc -XOverloadedStrings -XRecordWildCards $defs $cbits "$@"
7