summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorjoe <joe@jerkface.net>2017-09-15 04:17:23 -0400
committerjoe <joe@jerkface.net>2017-09-15 04:17:23 -0400
commit0a1417e6c6cc2e907a34987d026c168a8ab55b8a (patch)
tree2967db0e979b69c0fd217b44591deb533ac97853 /src
parent672efcbc63ff04b7321aae61ddb66811fdde4068 (diff)
Moved OnionTransport to its hierarchical location.
Diffstat (limited to 'src')
-rw-r--r--src/Network/Tox/Onion/Transport.hs569
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 #-}
18module 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
39import Network.Address (fromSockAddr,toSockAddr,setPort,either4or6,sockAddrPort)
40import Network.QueryResponse
41import Crypto.Tox hiding (encrypt,decrypt)
42import Network.Tox.Address
43import qualified Crypto.Tox as ToxCrypto
44import Network.Tox.DHT.Transport (NodeInfo(..),NodeId(..),SendNodes,nodeInfo,DHTPublicKey)
45
46import Debug.Trace
47import Control.Arrow
48import Control.Concurrent.STM
49import Control.Monad
50import qualified Data.ByteString as B
51 ;import Data.ByteString (ByteString)
52import Data.Coerce
53import Data.Function
54import Data.Functor.Contravariant
55import Data.Functor.Identity
56import Data.IP
57import Data.Maybe
58import Data.Monoid
59import Data.Serialize as S
60import Data.Type.Equality
61import Data.Typeable
62import Data.Word
63import GHC.Generics ()
64import GHC.TypeLits
65import Network.Socket
66import System.IO
67
68type HandleLo a = Maybe (Either String (ByteString, SockAddr)) -> IO a
69
70type UDPTransport = Transport String SockAddr ByteString
71
72
73getOnionAssym :: Get (Assym (Encrypted DataToRoute))
74getOnionAssym = getAliasedAssym
75
76putOnionAssym :: Serialize a => Word8 -> Put -> Assym a -> Put
77putOnionAssym typ p a = put typ >> p >> putAliasedAssym a
78
79data 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
85deriving instance ( Show (f (AnnounceRequest, Nonce8))
86 , Show (f AnnounceResponse)
87 , Show (f DataToRoute)
88 ) => Show (OnionMessage f)
89
90data OnionToOwner = OnionToOwner NodeInfo (ReturnPath N3)
91 | OnionToMe SockAddr -- SockAddr is immediate peer in route
92 deriving Show
93
94onionKey :: OnionToOwner -> Maybe PublicKey
95onionKey (OnionToOwner ni _) = Just $ id2key (nodeId ni)
96onionKey _ = Nothing
97
98instance 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
109instance 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
123onionToOwner :: Assym a -> ReturnPath N3 -> SockAddr -> Either String OnionToOwner
124onionToOwner 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
130onion :: Sized msg =>
131 ByteString
132 -> SockAddr
133 -> Get (Assym (Encrypted msg) -> t)
134 -> Either String (t, OnionToOwner)
135onion bs saddr getf = do (f,(assym,ret3)) <- runGet ((,) <$> getf <*> getOnionRequest) bs
136 oaddr <- onionToOwner assym ret3 saddr
137 return (f assym, oaddr)
138
139
140parseOnionAddr :: (ByteString, SockAddr) -> Either (OnionMessage Encrypted,OnionToOwner) (ByteString,SockAddr)
141parseOnionAddr (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
153getOnionReply :: Word8 -> Get (OnionMessage Encrypted)
154getOnionReply 0x84 = OnionAnnounceResponse <$> get <*> get <*> get
155getOnionReply 0x86 = OnionToRouteResponse <$> getOnionAssym
156
157putOnionMsg :: OnionMessage Encrypted -> Put
158putOnionMsg (OnionAnnounce a) = putOnionAssym 0x83 (return ()) a
159putOnionMsg (OnionToRoute pubkey a) = putOnionAssym 0x85 (putPublicKey pubkey) a
160putOnionMsg (OnionAnnounceResponse n8 n24 x) = put (0x84 :: Word8) >> put n8 >> put n24 >> put x
161putOnionMsg (OnionToRouteResponse a) = putOnionAssym 0x86 (return ()) a
162
163encodeOnionAddr :: (OnionMessage Encrypted,OnionToOwner) -> (ByteString, SockAddr)
164encodeOnionAddr (msg,OnionToOwner ni p) = ( runPut $ putResponse (OnionResponse p msg)
165 , nodeAddr ni )
166encodeOnionAddr (msg,OnionToMe a) = ( runPut (putOnionMsg msg), a)
167
168forwardOnions :: TransportCrypto -> UDPTransport -> UDPTransport -- HandleHi a -> IO a -> HandleLo a
169forwardOnions crypto udp = udp { awaitMessage = forwardAwait crypto udp }
170
171-- forMe :: HandleHi
172-- forThem :: handleLo
173forwardAwait :: TransportCrypto -> UDPTransport -> HandleLo a -> IO a
174forwardAwait 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
187forward :: forall c b b1. (Serialize b, Show b) =>
188 (Maybe (Either String b1) -> c) -> ByteString -> (b -> c) -> c
189forward kont bs f = either (kont . Just . Left) f $ decode $ B.tail bs
190
191class SumToThree a b
192
193instance SumToThree N0 N3
194instance SumToThree (S a) b => SumToThree a (S b)
195
196class ( 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
202instance LessThanThree N0
203instance LessThanThree N1
204instance LessThanThree N2
205
206type 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
213data OnionRequest n = OnionRequest
214 { onionNonce :: Nonce24
215 , onionForward :: Forwarding (ThreeMinus n) (OnionMessage Encrypted)
216 , pathFromOwner :: ReturnPath n
217 }
218
219deriving instance ( Show (Forwarding (ThreeMinus n) (OnionMessage Encrypted))
220 , KnownNat (PeanoNat n)
221 ) => Show (OnionRequest n)
222
223instance ( 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
243data OnionResponse n = OnionResponse
244 { pathToOwner :: ReturnPath n
245 , msgToOwner :: OnionMessage Encrypted
246 }
247
248deriving instance KnownNat (PeanoNat n) => Show (OnionResponse n)
249
250instance ( Serialize (ReturnPath n) ) => Serialize (OnionResponse n) where
251 get = OnionResponse <$> get <*> (get >>= getOnionReply)
252 put (OnionResponse p m) = put p >> putOnionMsg m
253
254
255data Addressed a = Addressed { sockAddr :: SockAddr, unaddressed :: a }
256 deriving (Eq,Show)
257
258instance 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
263getForwardAddr :: S.Get SockAddr
264getForwardAddr = 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
273putForwardAddr :: SockAddr -> S.Put
274putForwardAddr 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
283instance Serialize a => Serialize (Addressed a) where
284 get = Addressed <$> getForwardAddr <*> get
285 put (Addressed addr x) = putForwardAddr addr >> put x
286
287data N0
288data S n
289type N1 = S N0
290type N2 = S N1
291type N3 = S N2
292
293class KnownPeanoNat n where
294 peanoVal :: p n -> Int
295
296instance KnownPeanoNat N0 where
297 peanoVal _ = 0
298instance KnownPeanoNat n => KnownPeanoNat (S n) where
299 peanoVal _ = 1 + peanoVal (Proxy :: Proxy n)
300
301type family PeanoNat p where
302 PeanoNat N0 = 0
303 PeanoNat (S n) = 1 + PeanoNat n
304
305data 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)
310instance Sized (ReturnPath N0) where size = ConstSize 0
311instance 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{-
317instance KnownNat (PeanoNat n) => Sized (ReturnPath n) where
318 size = ConstSize $ 59 * fromIntegral (natVal (Proxy :: Proxy (PeanoNat n)))
319-}
320
321instance Serialize (ReturnPath N0) where get = pure NoReturnPath
322 put NoReturnPath = pure ()
323
324instance Serialize (ReturnPath N1) where
325 get = ReturnPath <$> get <*> get
326 put (ReturnPath n24 p) = put n24 >> put p
327
328instance (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)
335instance (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
340instance 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
350data Forwarding n msg where
351 NotForwarded :: msg -> Forwarding N0 msg
352 Forwarding :: PublicKey -> Encrypted (Addressed (Forwarding n msg)) -> Forwarding (S n) msg
353
354instance Show msg => Show (Forwarding N0 msg) where
355 show (NotForwarded x) = "NotForwarded "++show x
356
357instance ( 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
366instance 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
371instance 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
376instance Serialize msg => Serialize (Forwarding N0 msg) where
377 get = NotForwarded <$> get
378 put (NotForwarded msg) = put msg
379
380instance (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
384handleOnionRequest :: 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
389handleOnionRequest 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
404wrapSymmetric :: Serialize (ReturnPath n) =>
405 SymmetricKey -> Nonce24 -> SockAddr -> ReturnPath n -> ReturnPath (S n)
406wrapSymmetric sym n saddr rpath = ReturnPath n $ encryptSymmetric sym n (encodePlain $ Addressed saddr rpath)
407
408peelSymmetric :: Serialize (Addressed (ReturnPath n))
409 => SymmetricKey -> ReturnPath (S n) -> Either String (Addressed (ReturnPath n))
410peelSymmetric sym (ReturnPath nonce e) = decryptSymmetric sym nonce e >>= decodePlain
411
412
413peelOnion :: Serialize (Addressed (Forwarding n t))
414 => TransportCrypto
415 -> Nonce24
416 -> Forwarding (S n) t
417 -> Either String (Addressed (Forwarding n t))
418peelOnion crypto nonce (Forwarding k fwd) =
419 fmap runIdentity $ uncomposed $ decryptMessage crypto nonce (Right $ Assym k nonce fwd)
420
421handleOnionResponse :: (KnownPeanoNat n, Sized (ReturnPath n), Serialize (ReturnPath n)) => proxy (S n) -> TransportCrypto -> SockAddr -> UDPTransport -> IO a -> OnionResponse (S n) -> IO a
422handleOnionResponse 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
435data 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
442instance Sized AnnounceRequest where size = ConstSize (32*3)
443
444instance 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
448getOnionRequest :: Sized msg => Get (Assym (Encrypted msg), ReturnPath N3)
449getOnionRequest = 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
458putRequest :: (KnownPeanoNat n, Serialize (OnionRequest n)) => OnionRequest n -> Put
459putRequest req = do
460 putWord8 $ 0x80 + fromIntegral (peanoVal req)
461 put req
462
463putResponse :: (KnownPeanoNat n, Serialize (OnionResponse n)) => OnionResponse n -> Put
464putResponse 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
472data KeyRecord = NotStored Nonce32
473 | SendBackKey PublicKey
474 | Acknowledged Nonce32
475 deriving Show
476
477instance Sized KeyRecord where size = ConstSize 33
478
479instance 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
490data AnnounceResponse = AnnounceResponse
491 { is_stored :: KeyRecord
492 , announceNodes :: SendNodes
493 }
494 deriving Show
495
496instance Sized AnnounceResponse where
497 size = contramap is_stored size <> contramap announceNodes size
498
499instance S.Serialize AnnounceResponse where
500 get = AnnounceResponse <$> S.get <*> S.get
501 put (AnnounceResponse st ns) = S.put st >> S.put ns
502
503data DataToRoute = DataToRoute
504 { dataFromKey :: PublicKey -- Real public key of sender
505 , dataToRoute :: Encrypted OnionData -- (Word8,ByteString) -- DHTPK 0x9c
506 }
507
508instance Sized DataToRoute where
509 size = ConstSize 32 <> contramap dataToRoute size
510
511instance Serialize DataToRoute where
512 get = DataToRoute <$> getPublicKey <*> get
513 put (DataToRoute k dta) = putPublicKey k >> put dta
514
515data OnionData = OnionDHTPublicKey DHTPublicKey -- 0x9c
516
517instance 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
523encrypt :: TransportCrypto -> OnionMessage Identity -> OnionToOwner -> (OnionMessage Encrypted, OnionToOwner)
524encrypt 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
531encryptMessage :: Serialize a =>
532 TransportCrypto -> PublicKey -> Nonce24 -> Either (Identity a) (Assym (Identity a)) -> Encrypted a
533encryptMessage crypto destKey n (Right a) = ToxCrypto.encrypt secret plain
534 where
535 secret = computeSharedSecret (transportSecret crypto) destKey n
536 plain = encodePlain $ runIdentity $ assymData a
537encryptMessage crypto destKey n (Left x) = ToxCrypto.encrypt secret plain
538 where
539 secret = computeSharedSecret (transportSecret crypto) destKey n
540 plain = encodePlain $ runIdentity $ x
541
542decrypt :: TransportCrypto -> OnionMessage Encrypted -> OnionToOwner -> Either String (OnionMessage Identity, OnionToOwner)
543decrypt crypto msg addr = (, addr) <$> (sequenceMessage $ transcode (decryptMessage crypto) msg)
544
545decryptMessage :: Serialize x =>
546 TransportCrypto
547 -> Nonce24
548 -> Either (Encrypted x) (Assym (Encrypted x))
549 -> (Either String ∘ Identity) x
550decryptMessage 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)
555decryptMessage crypto n (Left e) = _todo -- OnionAnnounceResponse has no sender key
556
557
558sequenceMessage :: Applicative m => OnionMessage (m ∘ f) -> m (OnionMessage f)
559sequenceMessage (OnionAnnounce a) = fmap OnionAnnounce $ sequenceA $ fmap uncomposed a
560sequenceMessage (OnionToRoute pub a) = fmap (OnionToRoute pub) $ sequenceA $ fmap uncomposed a
561sequenceMessage (OnionToRouteResponse a) = fmap OnionToRouteResponse $ sequenceA $ fmap uncomposed a
562sequenceMessage (OnionAnnounceResponse n8 n24 dta) = OnionAnnounceResponse n8 n24 <$> uncomposed dta
563
564transcode :: forall f g. (forall a. Serialize a => Nonce24 -> Either (f a) (Assym (f a)) -> g a) -> OnionMessage f -> OnionMessage g
565transcode f (OnionAnnounce a) = OnionAnnounce $ a { assymData = f (assymNonce a) (Right a) }
566transcode f (OnionToRoute pub a) = OnionToRoute pub $ a { assymData = f (assymNonce a) (Right a) }
567transcode f (OnionToRouteResponse a) = OnionToRouteResponse $ a { assymData = f (assymNonce a) (Right a) }
568transcode f (OnionAnnounceResponse n8 n24 dta) = OnionAnnounceResponse n8 n24 $ f n24 $ Left dta
569