diff options
author | joe <joe@jerkface.net> | 2017-09-15 04:17:23 -0400 |
---|---|---|
committer | joe <joe@jerkface.net> | 2017-09-15 04:17:23 -0400 |
commit | 0a1417e6c6cc2e907a34987d026c168a8ab55b8a (patch) | |
tree | 2967db0e979b69c0fd217b44591deb533ac97853 /src/Network/Tox/Onion | |
parent | 672efcbc63ff04b7321aae61ddb66811fdde4068 (diff) |
Moved OnionTransport to its hierarchical location.
Diffstat (limited to 'src/Network/Tox/Onion')
-rw-r--r-- | src/Network/Tox/Onion/Transport.hs | 569 |
1 files changed, 569 insertions, 0 deletions
diff --git a/src/Network/Tox/Onion/Transport.hs b/src/Network/Tox/Onion/Transport.hs new file mode 100644 index 00000000..f10dcb43 --- /dev/null +++ b/src/Network/Tox/Onion/Transport.hs | |||
@@ -0,0 +1,569 @@ | |||
1 | {-# LANGUAGE DataKinds #-} | ||
2 | {-# LANGUAGE FlexibleContexts #-} | ||
3 | {-# LANGUAGE FlexibleInstances #-} | ||
4 | {-# LANGUAGE GADTs #-} | ||
5 | {-# LANGUAGE GeneralizedNewtypeDeriving #-} | ||
6 | {-# LANGUAGE KindSignatures #-} | ||
7 | {-# LANGUAGE LambdaCase #-} | ||
8 | {-# LANGUAGE MultiParamTypeClasses #-} | ||
9 | {-# LANGUAGE PartialTypeSignatures #-} | ||
10 | {-# LANGUAGE RankNTypes #-} | ||
11 | {-# LANGUAGE ScopedTypeVariables #-} | ||
12 | {-# LANGUAGE StandaloneDeriving #-} | ||
13 | {-# LANGUAGE TupleSections #-} | ||
14 | {-# LANGUAGE TypeFamilies #-} | ||
15 | {-# LANGUAGE TypeFamilyDependencies #-} | ||
16 | {-# LANGUAGE TypeOperators #-} | ||
17 | {-# LANGUAGE UndecidableInstances #-} | ||
18 | module Network.Tox.Onion.Transport | ||
19 | ( parseOnionAddr | ||
20 | , encodeOnionAddr | ||
21 | , forwardOnions | ||
22 | , OnionToOwner(..) | ||
23 | , OnionMessage(..) | ||
24 | , DataToRoute(..) | ||
25 | , AnnounceResponse(..) | ||
26 | , AnnounceRequest(..) | ||
27 | , Forwarding(..) | ||
28 | , ReturnPath(..) | ||
29 | , OnionRequest(..) | ||
30 | , OnionResponse(..) | ||
31 | , Addressed(..) | ||
32 | , UDPTransport | ||
33 | , KeyRecord(..) | ||
34 | , encrypt | ||
35 | , decrypt | ||
36 | , peelSymmetric | ||
37 | ) where | ||
38 | |||
39 | import Network.Address (fromSockAddr,toSockAddr,setPort,either4or6,sockAddrPort) | ||
40 | import Network.QueryResponse | ||
41 | import Crypto.Tox hiding (encrypt,decrypt) | ||
42 | import Network.Tox.Address | ||
43 | import qualified Crypto.Tox as ToxCrypto | ||
44 | import Network.Tox.DHT.Transport (NodeInfo(..),NodeId(..),SendNodes,nodeInfo,DHTPublicKey) | ||
45 | |||
46 | import Debug.Trace | ||
47 | import Control.Arrow | ||
48 | import Control.Concurrent.STM | ||
49 | import Control.Monad | ||
50 | import qualified Data.ByteString as B | ||
51 | ;import Data.ByteString (ByteString) | ||
52 | import Data.Coerce | ||
53 | import Data.Function | ||
54 | import Data.Functor.Contravariant | ||
55 | import Data.Functor.Identity | ||
56 | import Data.IP | ||
57 | import Data.Maybe | ||
58 | import Data.Monoid | ||
59 | import Data.Serialize as S | ||
60 | import Data.Type.Equality | ||
61 | import Data.Typeable | ||
62 | import Data.Word | ||
63 | import GHC.Generics () | ||
64 | import GHC.TypeLits | ||
65 | import Network.Socket | ||
66 | import System.IO | ||
67 | |||
68 | type HandleLo a = Maybe (Either String (ByteString, SockAddr)) -> IO a | ||
69 | |||
70 | type UDPTransport = Transport String SockAddr ByteString | ||
71 | |||
72 | |||
73 | getOnionAssym :: Get (Assym (Encrypted DataToRoute)) | ||
74 | getOnionAssym = getAliasedAssym | ||
75 | |||
76 | putOnionAssym :: Serialize a => Word8 -> Put -> Assym a -> Put | ||
77 | putOnionAssym typ p a = put typ >> p >> putAliasedAssym a | ||
78 | |||
79 | data OnionMessage (f :: * -> *) | ||
80 | = OnionAnnounce (Assym (f (AnnounceRequest,Nonce8))) | ||
81 | | OnionAnnounceResponse Nonce8 Nonce24 (f AnnounceResponse) | ||
82 | | OnionToRoute PublicKey (Assym (f DataToRoute)) -- destination key, aliased Assym | ||
83 | | OnionToRouteResponse (Assym (f DataToRoute)) | ||
84 | |||
85 | deriving instance ( Show (f (AnnounceRequest, Nonce8)) | ||
86 | , Show (f AnnounceResponse) | ||
87 | , Show (f DataToRoute) | ||
88 | ) => Show (OnionMessage f) | ||
89 | |||
90 | data OnionToOwner = OnionToOwner NodeInfo (ReturnPath N3) | ||
91 | | OnionToMe SockAddr -- SockAddr is immediate peer in route | ||
92 | deriving Show | ||
93 | |||
94 | onionKey :: OnionToOwner -> Maybe PublicKey | ||
95 | onionKey (OnionToOwner ni _) = Just $ id2key (nodeId ni) | ||
96 | onionKey _ = Nothing | ||
97 | |||
98 | instance Sized (OnionMessage Encrypted) where | ||
99 | size = VarSize $ \case | ||
100 | OnionAnnounce a -> case size of ConstSize n -> n + 1 | ||
101 | VarSize f -> f a + 1 | ||
102 | OnionAnnounceResponse n8 n24 x -> case size of ConstSize n -> n + 33 | ||
103 | VarSize f -> f x + 33 | ||
104 | OnionToRoute pubkey a -> case size of ConstSize n -> n + 33 | ||
105 | VarSize f -> f a + 33 | ||
106 | OnionToRouteResponse a -> case size of ConstSize n -> n + 1 | ||
107 | VarSize f -> f a + 1 | ||
108 | |||
109 | instance Serialize (OnionMessage Encrypted) where | ||
110 | get = do | ||
111 | typ <- get | ||
112 | case typ :: Word8 of | ||
113 | 0x83 -> OnionAnnounce <$> getAliasedAssym | ||
114 | 0x85 -> OnionToRoute <$> getPublicKey <*> getAliasedAssym | ||
115 | 0x84 -> getOnionReply typ | ||
116 | 0x86 -> getOnionReply typ | ||
117 | t -> fail $ "Unknown onion payload: " ++ show t | ||
118 | put (OnionAnnounce a) = putWord8 0x83 >> putAliasedAssym a | ||
119 | put (OnionToRoute k a) = putWord8 0x85 >> putPublicKey k >> putAliasedAssym a | ||
120 | put (OnionAnnounceResponse n8 n24 x) = putWord8 0x84 >> put n8 >> put n24 >> put x | ||
121 | put (OnionToRouteResponse a) = putWord8 0x86 >> putAliasedAssym a | ||
122 | |||
123 | onionToOwner :: Assym a -> ReturnPath N3 -> SockAddr -> Either String OnionToOwner | ||
124 | onionToOwner assym ret3 saddr = do | ||
125 | ni <- nodeInfo (key2id $ senderKey assym) saddr | ||
126 | return $ OnionToOwner ni ret3 | ||
127 | -- data CookieAddress = WithoutCookie NodeInfo | CookieAddress Cookie SockAddr | ||
128 | |||
129 | |||
130 | onion :: Sized msg => | ||
131 | ByteString | ||
132 | -> SockAddr | ||
133 | -> Get (Assym (Encrypted msg) -> t) | ||
134 | -> Either String (t, OnionToOwner) | ||
135 | onion bs saddr getf = do (f,(assym,ret3)) <- runGet ((,) <$> getf <*> getOnionRequest) bs | ||
136 | oaddr <- onionToOwner assym ret3 saddr | ||
137 | return (f assym, oaddr) | ||
138 | |||
139 | |||
140 | parseOnionAddr :: (ByteString, SockAddr) -> Either (OnionMessage Encrypted,OnionToOwner) (ByteString,SockAddr) | ||
141 | parseOnionAddr (msg,saddr) | ||
142 | | Just (typ,bs) <- B.uncons msg | ||
143 | , let right = Right (msg,saddr) | ||
144 | query = either (const right) Left | ||
145 | response = either (const right) (Left . (, OnionToMe saddr)) | ||
146 | = case typ of | ||
147 | 0x83 -> query $ onion bs saddr (pure OnionAnnounce) -- Announce Request | ||
148 | 0x85 -> query $ onion bs saddr (OnionToRoute <$> getPublicKey) -- Onion Data Request | ||
149 | 0x84 -> response $ runGet (getOnionReply 0x84) bs -- Announce Response | ||
150 | 0x86 -> response $ runGet (getOnionReply 0x86) bs -- Onion Data Response | ||
151 | _ -> right | ||
152 | |||
153 | getOnionReply :: Word8 -> Get (OnionMessage Encrypted) | ||
154 | getOnionReply 0x84 = OnionAnnounceResponse <$> get <*> get <*> get | ||
155 | getOnionReply 0x86 = OnionToRouteResponse <$> getOnionAssym | ||
156 | |||
157 | putOnionMsg :: OnionMessage Encrypted -> Put | ||
158 | putOnionMsg (OnionAnnounce a) = putOnionAssym 0x83 (return ()) a | ||
159 | putOnionMsg (OnionToRoute pubkey a) = putOnionAssym 0x85 (putPublicKey pubkey) a | ||
160 | putOnionMsg (OnionAnnounceResponse n8 n24 x) = put (0x84 :: Word8) >> put n8 >> put n24 >> put x | ||
161 | putOnionMsg (OnionToRouteResponse a) = putOnionAssym 0x86 (return ()) a | ||
162 | |||
163 | encodeOnionAddr :: (OnionMessage Encrypted,OnionToOwner) -> (ByteString, SockAddr) | ||
164 | encodeOnionAddr (msg,OnionToOwner ni p) = ( runPut $ putResponse (OnionResponse p msg) | ||
165 | , nodeAddr ni ) | ||
166 | encodeOnionAddr (msg,OnionToMe a) = ( runPut (putOnionMsg msg), a) | ||
167 | |||
168 | forwardOnions :: TransportCrypto -> UDPTransport -> UDPTransport -- HandleHi a -> IO a -> HandleLo a | ||
169 | forwardOnions crypto udp = udp { awaitMessage = forwardAwait crypto udp } | ||
170 | |||
171 | -- forMe :: HandleHi | ||
172 | -- forThem :: handleLo | ||
173 | forwardAwait :: TransportCrypto -> UDPTransport -> HandleLo a -> IO a | ||
174 | forwardAwait crypto udp kont = do | ||
175 | fix $ \another -> do | ||
176 | awaitMessage udp $ \case | ||
177 | m@(Just (Right (bs,saddr))) -> case B.head bs of | ||
178 | 0x80 -> forward kont bs $ handleOnionRequest (Proxy :: Proxy N0) crypto saddr udp another | ||
179 | 0x81 -> forward kont bs $ handleOnionRequest (Proxy :: Proxy N1) crypto saddr udp another | ||
180 | 0x82 -> forward kont bs $ handleOnionRequest (Proxy :: Proxy N2) crypto saddr udp another | ||
181 | 0x8c -> forward kont bs $ handleOnionResponse (Proxy :: Proxy N3) crypto saddr udp another | ||
182 | 0x8d -> forward kont bs $ handleOnionResponse (Proxy :: Proxy N2) crypto saddr udp another | ||
183 | 0x8e -> forward kont bs $ handleOnionResponse (Proxy :: Proxy N1) crypto saddr udp another | ||
184 | _ -> kont m | ||
185 | m -> kont m | ||
186 | |||
187 | forward :: forall c b b1. (Serialize b, Show b) => | ||
188 | (Maybe (Either String b1) -> c) -> ByteString -> (b -> c) -> c | ||
189 | forward kont bs f = either (kont . Just . Left) f $ decode $ B.tail bs | ||
190 | |||
191 | class SumToThree a b | ||
192 | |||
193 | instance SumToThree N0 N3 | ||
194 | instance SumToThree (S a) b => SumToThree a (S b) | ||
195 | |||
196 | class ( Serialize (ReturnPath n) | ||
197 | , Serialize (ReturnPath (S n)) | ||
198 | , Serialize (Forwarding (ThreeMinus (S n)) (OnionMessage Encrypted)) | ||
199 | , ThreeMinus n ~ S (ThreeMinus (S n)) | ||
200 | ) => LessThanThree n | ||
201 | |||
202 | instance LessThanThree N0 | ||
203 | instance LessThanThree N1 | ||
204 | instance LessThanThree N2 | ||
205 | |||
206 | type family ThreeMinus n = r | r -> n where | ||
207 | ThreeMinus N3 = N0 | ||
208 | ThreeMinus N2 = N1 | ||
209 | ThreeMinus N1 = N2 | ||
210 | ThreeMinus N0 = N3 | ||
211 | |||
212 | -- n = 0, 1, 2 | ||
213 | data OnionRequest n = OnionRequest | ||
214 | { onionNonce :: Nonce24 | ||
215 | , onionForward :: Forwarding (ThreeMinus n) (OnionMessage Encrypted) | ||
216 | , pathFromOwner :: ReturnPath n | ||
217 | } | ||
218 | |||
219 | deriving instance ( Show (Forwarding (ThreeMinus n) (OnionMessage Encrypted)) | ||
220 | , KnownNat (PeanoNat n) | ||
221 | ) => Show (OnionRequest n) | ||
222 | |||
223 | instance ( Serialize (Forwarding (ThreeMinus n) (OnionMessage Encrypted)) | ||
224 | , Sized (ReturnPath n) | ||
225 | , Serialize (ReturnPath n) | ||
226 | ) => Serialize (OnionRequest n) where | ||
227 | get = do | ||
228 | -- TODO share code with 'getOnionRequest' | ||
229 | n24 <- get | ||
230 | cnt <- remaining | ||
231 | let fwdsize = case size :: Size (ReturnPath n) of ConstSize n -> cnt - n | ||
232 | fwd <- isolate fwdsize get | ||
233 | rpath <- get | ||
234 | return $ OnionRequest n24 fwd rpath | ||
235 | put (OnionRequest n f p) = put n >> put f >> put p | ||
236 | |||
237 | -- getRequest :: _ | ||
238 | -- getRequest = OnionRequest <$> get <*> get <*> get | ||
239 | |||
240 | -- n = 1, 2, 3 | ||
241 | -- Attributed (Encrypted ( | ||
242 | |||
243 | data OnionResponse n = OnionResponse | ||
244 | { pathToOwner :: ReturnPath n | ||
245 | , msgToOwner :: OnionMessage Encrypted | ||
246 | } | ||
247 | |||
248 | deriving instance KnownNat (PeanoNat n) => Show (OnionResponse n) | ||
249 | |||
250 | instance ( Serialize (ReturnPath n) ) => Serialize (OnionResponse n) where | ||
251 | get = OnionResponse <$> get <*> (get >>= getOnionReply) | ||
252 | put (OnionResponse p m) = put p >> putOnionMsg m | ||
253 | |||
254 | |||
255 | data Addressed a = Addressed { sockAddr :: SockAddr, unaddressed :: a } | ||
256 | deriving (Eq,Show) | ||
257 | |||
258 | instance Sized a => Sized (Addressed a) where | ||
259 | size = case size :: Size a of | ||
260 | ConstSize n -> ConstSize $ 1{-family-} + 16{-ip-} + 2{-port-} + n | ||
261 | VarSize f -> VarSize $ \x -> 1{-family-} + 16{-ip-} + 2{-port-} + f (unaddressed x) | ||
262 | |||
263 | getForwardAddr :: S.Get SockAddr | ||
264 | getForwardAddr = do | ||
265 | addrfam <- S.get :: S.Get Word8 | ||
266 | ip <- getIP addrfam | ||
267 | case ip of IPv4 _ -> S.skip 12 -- compliant peers would zero-fill this. | ||
268 | IPv6 _ -> return () | ||
269 | port <- S.get :: S.Get PortNumber | ||
270 | return $ setPort port $ toSockAddr ip | ||
271 | |||
272 | |||
273 | putForwardAddr :: SockAddr -> S.Put | ||
274 | putForwardAddr saddr = fromMaybe (return $ error "unsupported SockAddr family") $ do | ||
275 | port <- sockAddrPort saddr | ||
276 | ip <- fromSockAddr $ either id id $ either4or6 saddr | ||
277 | return $ do | ||
278 | case ip of | ||
279 | IPv4 ip4 -> S.put (0x02 :: Word8) >> S.put ip4 >> S.putByteString (B.replicate 12 0) | ||
280 | IPv6 ip6 -> S.put (0x0a :: Word8) >> S.put ip6 | ||
281 | S.put port | ||
282 | |||
283 | instance Serialize a => Serialize (Addressed a) where | ||
284 | get = Addressed <$> getForwardAddr <*> get | ||
285 | put (Addressed addr x) = putForwardAddr addr >> put x | ||
286 | |||
287 | data N0 | ||
288 | data S n | ||
289 | type N1 = S N0 | ||
290 | type N2 = S N1 | ||
291 | type N3 = S N2 | ||
292 | |||
293 | class KnownPeanoNat n where | ||
294 | peanoVal :: p n -> Int | ||
295 | |||
296 | instance KnownPeanoNat N0 where | ||
297 | peanoVal _ = 0 | ||
298 | instance KnownPeanoNat n => KnownPeanoNat (S n) where | ||
299 | peanoVal _ = 1 + peanoVal (Proxy :: Proxy n) | ||
300 | |||
301 | type family PeanoNat p where | ||
302 | PeanoNat N0 = 0 | ||
303 | PeanoNat (S n) = 1 + PeanoNat n | ||
304 | |||
305 | data ReturnPath n where | ||
306 | NoReturnPath :: ReturnPath N0 | ||
307 | ReturnPath :: Nonce24 -> Encrypted (Addressed (ReturnPath n)) -> ReturnPath (S n) | ||
308 | |||
309 | -- Size: 59 = 1(family) + 16(ip) + 2(port) +16(mac) + 24(nonce) | ||
310 | instance Sized (ReturnPath N0) where size = ConstSize 0 | ||
311 | instance Sized (ReturnPath n) => Sized (ReturnPath (S n)) where | ||
312 | size = ConstSize 59 <> contramap (\x -> let _ = x :: ReturnPath (S n) | ||
313 | in error "non-constant ReturnPath size") | ||
314 | (size :: Size (ReturnPath n)) | ||
315 | |||
316 | {- | ||
317 | instance KnownNat (PeanoNat n) => Sized (ReturnPath n) where | ||
318 | size = ConstSize $ 59 * fromIntegral (natVal (Proxy :: Proxy (PeanoNat n))) | ||
319 | -} | ||
320 | |||
321 | instance Serialize (ReturnPath N0) where get = pure NoReturnPath | ||
322 | put NoReturnPath = pure () | ||
323 | |||
324 | instance Serialize (ReturnPath N1) where | ||
325 | get = ReturnPath <$> get <*> get | ||
326 | put (ReturnPath n24 p) = put n24 >> put p | ||
327 | |||
328 | instance (Sized (ReturnPath n), Serialize (ReturnPath n)) => Serialize (ReturnPath (S (S n))) where | ||
329 | get = ReturnPath <$> get <*> get | ||
330 | put (ReturnPath n24 p) = put n24 >> put p | ||
331 | |||
332 | |||
333 | {- | ||
334 | -- This doesn't work because it tried to infer it for (0 - 1) | ||
335 | instance (Serialize (Encrypted (Addressed (ReturnPath (n - 1))))) => Serialize (ReturnPath n) where | ||
336 | get = ReturnPath <$> get <*> get | ||
337 | put (ReturnPath n24 p) = put n24 >> put p | ||
338 | -} | ||
339 | |||
340 | instance KnownNat (PeanoNat n) => Show (ReturnPath n) where | ||
341 | show rpath = "ReturnPath" ++ show (natVal (Proxy :: Proxy (PeanoNat n))) | ||
342 | |||
343 | |||
344 | -- instance KnownNat n => Serialize (ReturnPath n) where | ||
345 | -- -- Size: 59 = 1(family) + 16(ip) + 2(port) +16(mac) + 24(nonce) | ||
346 | -- get = ReturnPath <$> getBytes ( 59 * (fromIntegral $ natVal $ Proxy @n) ) | ||
347 | -- put (ReturnPath bs) = putByteString bs | ||
348 | |||
349 | |||
350 | data Forwarding n msg where | ||
351 | NotForwarded :: msg -> Forwarding N0 msg | ||
352 | Forwarding :: PublicKey -> Encrypted (Addressed (Forwarding n msg)) -> Forwarding (S n) msg | ||
353 | |||
354 | instance Show msg => Show (Forwarding N0 msg) where | ||
355 | show (NotForwarded x) = "NotForwarded "++show x | ||
356 | |||
357 | instance ( KnownNat (PeanoNat (S n)) | ||
358 | , Show (Encrypted (Addressed (Forwarding n msg))) | ||
359 | ) => Show (Forwarding (S n) msg) where | ||
360 | show (Forwarding k a) = unwords [ "Forwarding" | ||
361 | , "("++show (natVal (Proxy :: Proxy (PeanoNat (S n))))++")" | ||
362 | , show (key2id k) | ||
363 | , show a | ||
364 | ] | ||
365 | |||
366 | instance Sized msg => Sized (Forwarding N0 msg) | ||
367 | where size = case size :: Size msg of | ||
368 | ConstSize n -> ConstSize n | ||
369 | VarSize f -> VarSize $ \(NotForwarded x) -> f x | ||
370 | |||
371 | instance Sized (Forwarding n msg) => Sized (Forwarding (S n) msg) | ||
372 | where size = ConstSize 32 | ||
373 | <> contramap (\(Forwarding _ e) -> e) | ||
374 | (size :: Size (Encrypted (Addressed (Forwarding n msg)))) | ||
375 | |||
376 | instance Serialize msg => Serialize (Forwarding N0 msg) where | ||
377 | get = NotForwarded <$> get | ||
378 | put (NotForwarded msg) = put msg | ||
379 | |||
380 | instance (Serialize (Encrypted (Addressed (Forwarding n msg)))) => Serialize (Forwarding (S n) msg) where | ||
381 | get = Forwarding <$> getPublicKey <*> get | ||
382 | put (Forwarding k x) = putPublicKey k >> put x | ||
383 | |||
384 | handleOnionRequest :: forall a proxy n. | ||
385 | ( LessThanThree n | ||
386 | , KnownPeanoNat n | ||
387 | , Sized (ReturnPath n) | ||
388 | ) => proxy n -> TransportCrypto -> SockAddr -> UDPTransport -> IO a -> OnionRequest n -> IO a | ||
389 | handleOnionRequest proxy crypto saddr udp kont (OnionRequest nonce msg rpath) = do | ||
390 | let n = peanoVal rpath | ||
391 | hPutStrLn stderr $ "handleOnionRequest " ++ show n | ||
392 | (sym, snonce) <- atomically ( (,) <$> transportSymmetric crypto | ||
393 | <*> transportNewNonce crypto ) | ||
394 | case peelOnion crypto nonce msg of | ||
395 | Left e -> do | ||
396 | -- todo report encryption error | ||
397 | hPutStrLn stderr $ unwords [ "peelOnion:", show n, either show show (either4or6 saddr), e] | ||
398 | kont | ||
399 | Right (Addressed dst msg') -> do | ||
400 | hPutStrLn stderr $ unwords [ "peelOnion:", show n, either show show (either4or6 saddr), "SUCCESS"] | ||
401 | sendMessage udp dst (runPut $ putRequest $ OnionRequest nonce msg' $ wrapSymmetric sym snonce saddr rpath) | ||
402 | kont | ||
403 | |||
404 | wrapSymmetric :: Serialize (ReturnPath n) => | ||
405 | SymmetricKey -> Nonce24 -> SockAddr -> ReturnPath n -> ReturnPath (S n) | ||
406 | wrapSymmetric sym n saddr rpath = ReturnPath n $ encryptSymmetric sym n (encodePlain $ Addressed saddr rpath) | ||
407 | |||
408 | peelSymmetric :: Serialize (Addressed (ReturnPath n)) | ||
409 | => SymmetricKey -> ReturnPath (S n) -> Either String (Addressed (ReturnPath n)) | ||
410 | peelSymmetric sym (ReturnPath nonce e) = decryptSymmetric sym nonce e >>= decodePlain | ||
411 | |||
412 | |||
413 | peelOnion :: Serialize (Addressed (Forwarding n t)) | ||
414 | => TransportCrypto | ||
415 | -> Nonce24 | ||
416 | -> Forwarding (S n) t | ||
417 | -> Either String (Addressed (Forwarding n t)) | ||
418 | peelOnion crypto nonce (Forwarding k fwd) = | ||
419 | fmap runIdentity $ uncomposed $ decryptMessage crypto nonce (Right $ Assym k nonce fwd) | ||
420 | |||
421 | handleOnionResponse :: (KnownPeanoNat n, Sized (ReturnPath n), Serialize (ReturnPath n)) => proxy (S n) -> TransportCrypto -> SockAddr -> UDPTransport -> IO a -> OnionResponse (S n) -> IO a | ||
422 | handleOnionResponse proxy crypto saddr udp kont (OnionResponse path msg) = do | ||
423 | sym <- atomically $ transportSymmetric crypto | ||
424 | case peelSymmetric sym path of | ||
425 | Left e -> do | ||
426 | -- todo report encryption error | ||
427 | let n = peanoVal path | ||
428 | hPutStrLn stderr $ unwords [ "peelSymmetric:", show n, either show show (either4or6 saddr), e] | ||
429 | kont | ||
430 | Right (Addressed dst path') -> do | ||
431 | sendMessage udp dst (runPut $ putResponse $ OnionResponse path' msg) | ||
432 | kont | ||
433 | |||
434 | |||
435 | data AnnounceRequest = AnnounceRequest | ||
436 | { announcePingId :: Nonce32 -- Ping ID | ||
437 | , announceSeeking :: NodeId -- Public key we are searching for | ||
438 | , announceKey :: NodeId -- Public key that we want those sending back data packets to use | ||
439 | } | ||
440 | deriving Show | ||
441 | |||
442 | instance Sized AnnounceRequest where size = ConstSize (32*3) | ||
443 | |||
444 | instance S.Serialize AnnounceRequest where | ||
445 | get = AnnounceRequest <$> S.get <*> S.get <*> S.get | ||
446 | put (AnnounceRequest p s k) = S.put (p,s,k) | ||
447 | |||
448 | getOnionRequest :: Sized msg => Get (Assym (Encrypted msg), ReturnPath N3) | ||
449 | getOnionRequest = do | ||
450 | -- Assumes return path is constant size so that we can isolate | ||
451 | -- the variable-sized prefix. | ||
452 | cnt <- remaining | ||
453 | a <- isolate (case size :: Size (ReturnPath N3) of ConstSize n -> cnt - n) | ||
454 | getAliasedAssym | ||
455 | path <- get | ||
456 | return (a,path) | ||
457 | |||
458 | putRequest :: (KnownPeanoNat n, Serialize (OnionRequest n)) => OnionRequest n -> Put | ||
459 | putRequest req = do | ||
460 | putWord8 $ 0x80 + fromIntegral (peanoVal req) | ||
461 | put req | ||
462 | |||
463 | putResponse :: (KnownPeanoNat n, Serialize (OnionResponse n)) => OnionResponse n -> Put | ||
464 | putResponse resp = do | ||
465 | let tag = 0x8f - fromIntegral (peanoVal resp) | ||
466 | -- OnionResponse N0 is an alias for the OnionMessage Encrypted type which includes a tag | ||
467 | -- in it's Serialize instance. | ||
468 | when (tag /= 0x8f) (putWord8 tag) | ||
469 | put resp | ||
470 | |||
471 | |||
472 | data KeyRecord = NotStored Nonce32 | ||
473 | | SendBackKey PublicKey | ||
474 | | Acknowledged Nonce32 | ||
475 | deriving Show | ||
476 | |||
477 | instance Sized KeyRecord where size = ConstSize 33 | ||
478 | |||
479 | instance S.Serialize KeyRecord where | ||
480 | get = do | ||
481 | is_stored <- S.get :: S.Get Word8 | ||
482 | case is_stored of | ||
483 | 1 -> SendBackKey <$> getPublicKey | ||
484 | 2 -> Acknowledged <$> S.get | ||
485 | _ -> NotStored <$> S.get | ||
486 | put (NotStored n32) = S.put (0 :: Word8) >> S.put n32 | ||
487 | put (SendBackKey key) = S.put (1 :: Word8) >> putPublicKey key | ||
488 | put (Acknowledged n32) = S.put (2 :: Word8) >> S.put n32 | ||
489 | |||
490 | data AnnounceResponse = AnnounceResponse | ||
491 | { is_stored :: KeyRecord | ||
492 | , announceNodes :: SendNodes | ||
493 | } | ||
494 | deriving Show | ||
495 | |||
496 | instance Sized AnnounceResponse where | ||
497 | size = contramap is_stored size <> contramap announceNodes size | ||
498 | |||
499 | instance S.Serialize AnnounceResponse where | ||
500 | get = AnnounceResponse <$> S.get <*> S.get | ||
501 | put (AnnounceResponse st ns) = S.put st >> S.put ns | ||
502 | |||
503 | data DataToRoute = DataToRoute | ||
504 | { dataFromKey :: PublicKey -- Real public key of sender | ||
505 | , dataToRoute :: Encrypted OnionData -- (Word8,ByteString) -- DHTPK 0x9c | ||
506 | } | ||
507 | |||
508 | instance Sized DataToRoute where | ||
509 | size = ConstSize 32 <> contramap dataToRoute size | ||
510 | |||
511 | instance Serialize DataToRoute where | ||
512 | get = DataToRoute <$> getPublicKey <*> get | ||
513 | put (DataToRoute k dta) = putPublicKey k >> put dta | ||
514 | |||
515 | data OnionData = OnionDHTPublicKey DHTPublicKey -- 0x9c | ||
516 | |||
517 | instance Sized OnionData where | ||
518 | size = VarSize $ \(OnionDHTPublicKey dhtpk) -> case size of | ||
519 | ConstSize n -> n -- Override because OnionData probably | ||
520 | -- should be treated as variable sized. | ||
521 | VarSize f -> f dhtpk | ||
522 | |||
523 | encrypt :: TransportCrypto -> OnionMessage Identity -> OnionToOwner -> (OnionMessage Encrypted, OnionToOwner) | ||
524 | encrypt crypto msg rpath = ( transcode (encryptMessage crypto okey) msg | ||
525 | , rpath) | ||
526 | where | ||
527 | -- The OnionToMe case shouldn't happen, but we'll use our own public | ||
528 | -- key in this situation. | ||
529 | okey = fromMaybe (transportPublic crypto) $ onionKey rpath | ||
530 | |||
531 | encryptMessage :: Serialize a => | ||
532 | TransportCrypto -> PublicKey -> Nonce24 -> Either (Identity a) (Assym (Identity a)) -> Encrypted a | ||
533 | encryptMessage crypto destKey n (Right a) = ToxCrypto.encrypt secret plain | ||
534 | where | ||
535 | secret = computeSharedSecret (transportSecret crypto) destKey n | ||
536 | plain = encodePlain $ runIdentity $ assymData a | ||
537 | encryptMessage crypto destKey n (Left x) = ToxCrypto.encrypt secret plain | ||
538 | where | ||
539 | secret = computeSharedSecret (transportSecret crypto) destKey n | ||
540 | plain = encodePlain $ runIdentity $ x | ||
541 | |||
542 | decrypt :: TransportCrypto -> OnionMessage Encrypted -> OnionToOwner -> Either String (OnionMessage Identity, OnionToOwner) | ||
543 | decrypt crypto msg addr = (, addr) <$> (sequenceMessage $ transcode (decryptMessage crypto) msg) | ||
544 | |||
545 | decryptMessage :: Serialize x => | ||
546 | TransportCrypto | ||
547 | -> Nonce24 | ||
548 | -> Either (Encrypted x) (Assym (Encrypted x)) | ||
549 | -> (Either String ∘ Identity) x | ||
550 | decryptMessage crypto n (Right assymE) = plain $ ToxCrypto.decrypt secret e | ||
551 | where | ||
552 | secret = computeSharedSecret (transportSecret crypto) (senderKey assymE) n | ||
553 | e = assymData assymE | ||
554 | plain = Composed . fmap Identity . (>>= decodePlain) | ||
555 | decryptMessage crypto n (Left e) = _todo -- OnionAnnounceResponse has no sender key | ||
556 | |||
557 | |||
558 | sequenceMessage :: Applicative m => OnionMessage (m ∘ f) -> m (OnionMessage f) | ||
559 | sequenceMessage (OnionAnnounce a) = fmap OnionAnnounce $ sequenceA $ fmap uncomposed a | ||
560 | sequenceMessage (OnionToRoute pub a) = fmap (OnionToRoute pub) $ sequenceA $ fmap uncomposed a | ||
561 | sequenceMessage (OnionToRouteResponse a) = fmap OnionToRouteResponse $ sequenceA $ fmap uncomposed a | ||
562 | sequenceMessage (OnionAnnounceResponse n8 n24 dta) = OnionAnnounceResponse n8 n24 <$> uncomposed dta | ||
563 | |||
564 | transcode :: forall f g. (forall a. Serialize a => Nonce24 -> Either (f a) (Assym (f a)) -> g a) -> OnionMessage f -> OnionMessage g | ||
565 | transcode f (OnionAnnounce a) = OnionAnnounce $ a { assymData = f (assymNonce a) (Right a) } | ||
566 | transcode f (OnionToRoute pub a) = OnionToRoute pub $ a { assymData = f (assymNonce a) (Right a) } | ||
567 | transcode f (OnionToRouteResponse a) = OnionToRouteResponse $ a { assymData = f (assymNonce a) (Right a) } | ||
568 | transcode f (OnionAnnounceResponse n8 n24 dta) = OnionAnnounceResponse n8 n24 $ f n24 $ Left dta | ||
569 | |||