summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjoe <joe@jerkface.net>2017-08-02 01:10:08 -0400
committerjoe <joe@jerkface.net>2017-08-02 01:10:08 -0400
commit4198ce253ea9ef9184b325e4bb8d18fcc483b381 (patch)
treeee2db363a165e69d7ea9a07ab3d762e86b83f124
parent8a46b3a8808a15017207bdcea067aa7857a95a11 (diff)
More Tox stuff.
-rw-r--r--Mainline.hs2
-rw-r--r--Tox.hs399
-rw-r--r--src/Network/QueryResponse.hs19
3 files changed, 355 insertions, 65 deletions
diff --git a/Mainline.hs b/Mainline.hs
index 8ff13390..291a196f 100644
--- a/Mainline.hs
+++ b/Mainline.hs
@@ -589,7 +589,7 @@ newClient addr = do
589 client = Client 589 client = Client
590 { clientNet = net 590 { clientNet = net
591 , clientDispatcher = dispatch 591 , clientDispatcher = dispatch
592 , clientErrorReporter = printErrors stderr 592 , clientErrorReporter = ignoreErrors -- printErrors stderr
593 , clientPending = map_var 593 , clientPending = map_var
594 , clientAddress = \maddr -> atomically $ do 594 , clientAddress = \maddr -> atomically $ do
595 let var = case flip prefer4or6 Nothing <$> maddr of 595 let var = case flip prefer4or6 Nothing <$> maddr of
diff --git a/Tox.hs b/Tox.hs
index 6969e652..253c83e7 100644
--- a/Tox.hs
+++ b/Tox.hs
@@ -5,6 +5,7 @@
5{-# LANGUAGE DeriveTraversable #-} 5{-# LANGUAGE DeriveTraversable #-}
6{-# LANGUAGE FlexibleInstances #-} 6{-# LANGUAGE FlexibleInstances #-}
7{-# LANGUAGE GeneralizedNewtypeDeriving #-} 7{-# LANGUAGE GeneralizedNewtypeDeriving #-}
8{-# LANGUAGE NamedFieldPuns #-}
8{-# LANGUAGE PatternSynonyms #-} 9{-# LANGUAGE PatternSynonyms #-}
9{-# LANGUAGE ScopedTypeVariables #-} 10{-# LANGUAGE ScopedTypeVariables #-}
10{-# LANGUAGE TupleSections #-} 11{-# LANGUAGE TupleSections #-}
@@ -67,6 +68,8 @@ import Text.Read
67import Kademlia 68import Kademlia
68import Network.BitTorrent.DHT.Search (Search (..)) 69import Network.BitTorrent.DHT.Search (Search (..))
69import Text.Printf 70import Text.Printf
71import qualified Crypto.Cipher.ChaChaPoly1305 as Symmetric
72import Data.Bitraversable (bisequence)
70 73
71newtype NodeId = NodeId ByteString 74newtype NodeId = NodeId ByteString
72 deriving (Eq,Ord,ByteArrayAccess, Bits, Hashable) 75 deriving (Eq,Ord,ByteArrayAccess, Bits, Hashable)
@@ -127,17 +130,17 @@ instance FromJSON NodeInfo where
127 guard (B.length bs == 32) 130 guard (B.length bs == 32)
128 return $ NodeInfo (NodeId bs) ip (fromIntegral (portnum :: Word16)) 131 return $ NodeInfo (NodeId bs) ip (fromIntegral (portnum :: Word16))
129 132
133getIP :: Word8 -> S.Get IP
134getIP 0x02 = IPv4 <$> S.get
135getIP 0x0a = IPv6 <$> S.get
136getIP 0x82 = IPv4 <$> S.get -- TODO: TCP
137getIP 0x8a = IPv6 <$> S.get -- TODO: TCP
138getIP x = fail ("unsupported address family ("++show x++")")
130 139
131instance S.Serialize NodeInfo where 140instance S.Serialize NodeInfo where
132 get = do 141 get = do
133 addrfam <- S.get :: S.Get Word8 142 addrfam <- S.get :: S.Get Word8
134 ip <- case addrfam of 143 ip <- getIP addrfam
135 0x02 -> IPv4 <$> S.get
136 0x0a -> IPv6 <$> S.get
137 0x82 -> IPv4 <$> S.get -- TODO: TCP
138 0x8a -> IPv6 <$> S.get -- TODO: TCP
139 -- "Failed reading: unsupported address family (118)\nEmpty call stack\n" 0x76
140 x -> fail ("unsupported address family ("++show x++")")
141 port <- S.get :: S.Get PortNumber 144 port <- S.get :: S.Get PortNumber
142 nid <- S.get 145 nid <- S.get
143 return $ NodeInfo nid ip port 146 return $ NodeInfo nid ip port
@@ -243,11 +246,11 @@ pattern OnionRequest2 = MessageType 130 -- 0x82 Onion Request 2
243pattern AnnounceType = MessageType 131 -- 0x83 Announce Request 246pattern AnnounceType = MessageType 131 -- 0x83 Announce Request
244 247
245-- 0x84 Announce Response 248-- 0x84 Announce Response
246-- 0x85 Onion Data Request 249-- 0x85 Onion Data Request (data to route request packet)
247-- 0x86 Onion Data Response 250-- 0x86 Onion Data Response (data to route response packet)
248-- 0x8c Onion Response 3 251-- 0x8c Onion Response 3
249-- 0x8d Onion Response 2 252-- 0x8d Onion Response 2
250-- 0x8e Onion Response 1 253pattern OnionResponse1 = MessageType 142 -- 0x8e Onion Response 1
251-- 0xf0 Bootstrap Info 254-- 0xf0 Bootstrap Info
252 255
253-- TODO Fix these fails... 256-- TODO Fix these fails...
@@ -264,6 +267,10 @@ instance Show Method where
264 showsPrec d PongType = mappend "PongType" 267 showsPrec d PongType = mappend "PongType"
265 showsPrec d GetNodesType = mappend "GetNodesType" 268 showsPrec d GetNodesType = mappend "GetNodesType"
266 showsPrec d SendNodesType = mappend "SendNodesType" 269 showsPrec d SendNodesType = mappend "SendNodesType"
270 showsPrec d DHTRequestType = mappend "DHTRequestType"
271 showsPrec d OnionRequest0 = mappend "OnionRequest0"
272 showsPrec d OnionResponse1 = mappend "OnionResponse1"
273 showsPrec d AnnounceType = mappend "AnnounceType"
267 showsPrec d (MessageType x) = mappend "MessageType " . showsPrec (d+1) x 274 showsPrec d (MessageType x) = mappend "MessageType " . showsPrec (d+1) x
268 275
269newtype Nonce8 = Nonce8 Word64 276newtype Nonce8 = Nonce8 Word64
@@ -295,38 +302,71 @@ quoted shows s = '"':shows ('"':s)
295bin2hex :: ByteArrayAccess bs => bs -> String 302bin2hex :: ByteArrayAccess bs => bs -> String
296bin2hex = C8.unpack . Base16.encode . BA.convert 303bin2hex = C8.unpack . Base16.encode . BA.convert
297 304
305newtype SymmetricCiphered = SymmetricCiphered ByteString
306 deriving (Eq,Show)
298 307
299data Message a = Message 308data Message a = Message
300 { msgType :: Method 309 { msgType :: Method
301 , msgOrigin :: NodeId 310 , msgOrigin :: NodeId
302 , msgNonce :: Nonce24 -- cryptoNonce of TransactionId 311 , msgNonce :: Nonce24 -- cryptoNonce of TransactionId
303 , msgPayload :: a 312 , msgReturnPath :: Maybe SymmetricCiphered
313 , msgPayload :: a
304 } 314 }
305 deriving (Eq, Show, Generic, Functor, Foldable, Traversable) 315 deriving (Eq, Show, Generic, Functor, Foldable, Traversable)
306 316
317typeHasEncryptedPayload OnionResponse1 = False
318typeHasEncryptedPayload _ = True
319
320msgDHTKey Message{ msgOrigin, msgType = PingType } = Just msgOrigin
321msgDHTKey Message{ msgOrigin, msgType = PongType } = Just msgOrigin
322msgDHTKey Message{ msgOrigin, msgType = GetNodesType } = Just msgOrigin
323msgDHTKey Message{ msgOrigin, msgType = SendNodesType } = Just msgOrigin
324msgDHTKey Message{ msgOrigin, msgType = OnionRequest0 } = Just msgOrigin
325msgDHTKey _ = Nothing
326
307data Ciphered = Ciphered { cipheredMAC :: Poly1305.Auth 327data Ciphered = Ciphered { cipheredMAC :: Poly1305.Auth
308 , cipheredBytes :: ByteString } 328 , cipheredBytes :: ByteString }
309 deriving Eq 329 deriving Eq
310 330
311getMessage :: S.Get (Message Ciphered) 331newtype OnionPayload = OnionPayload { unpackOnionPayload :: ByteString }
332
333instance S.Serialize OnionPayload where
334 get = OnionPayload <$> (S.remaining >>= S.getBytes)
335 put (OnionPayload bs) = S.putByteString bs
336
337getMessage :: S.Get (Message (Either OnionPayload Ciphered))
312getMessage = do 338getMessage = do
313 typ <- S.get 339 typ <- S.get
314 (nid,tid) <- case typ of -- Seriously... what the fuck? 340 (nid,nonce) <- case typ of -- Seriously... what the fuck?
315 DHTRequestType -> flip (,) <$> S.get <*> S.get 341 DHTRequestType -> do
342 S.skip 32 -- TODO: get destination key
343 -- If it is ours, decrypt and handle.
344 -- If not ours, search routing table and forward if it's in there.
345 flip (,) <$> S.get <*> S.get
316 OnionRequest0 -> flip (,) <$> S.get <*> S.get 346 OnionRequest0 -> flip (,) <$> S.get <*> S.get
317 OnionRequest1 -> flip (,) <$> S.get <*> S.get 347 OnionRequest1 -> flip (,) <$> S.get <*> S.get
318 -- OnionRequest2 -> flip (,) <$> S.get <*> S.get 348 -- OnionRequest2 -> flip (,) <$> S.get <*> S.get
319 AnnounceType -> flip (,) <$> S.get <*> S.get 349 AnnounceType -> flip (,) <$> S.get <*> S.get
350 OnionResponse1 -> (NodeId $ BA.convert zeros32,) <$> S.get -- XXX: no msgOrigin!
320 _ -> (,) <$> S.get <*> S.get 351 _ -> (,) <$> S.get <*> S.get
321 mac <- Poly1305.Auth . BA.convert <$> S.getBytes 16 352 (payload,rpath) <- case typ of
322 cnt <- S.remaining 353 OnionResponse1 -> do
323 bs <- S.getBytes cnt 354 rpath <- Just . SymmetricCiphered <$> S.getBytes (16 + 19)
324 return Message { msgType = typ 355 payload <- Left . OnionPayload <$> (S.remaining >>= S.getBytes)
325 , msgOrigin = nid 356 return (payload,rpath)
326 , msgNonce = tid 357 _ -> do
327 , msgPayload = Ciphered mac bs } 358 payload <- Right <$> getCiphered
328 359 return (payload,Nothing)
329putMessage :: Message Ciphered -> S.Put 360 return Message { msgType = typ
361 , msgOrigin = nid
362 , msgNonce = nonce
363 , msgReturnPath = rpath
364 , msgPayload = payload }
365
366putOnionPayload :: OnionPayload -> S.Put
367putOnionPayload (OnionPayload bs) = S.putByteString bs
368
369putMessage :: Message (Either OnionPayload Ciphered) -> S.Put
330putMessage (Message {..}) = do 370putMessage (Message {..}) = do
331 S.put msgType 371 S.put msgType
332 case msgType of -- Seriously... what the fuck? 372 case msgType of -- Seriously... what the fuck?
@@ -336,9 +376,11 @@ putMessage (Message {..}) = do
336 -- OnionRequest2 -> S.put msgNonce >> S.put msgOrigin 376 -- OnionRequest2 -> S.put msgNonce >> S.put msgOrigin
337 AnnounceType -> S.put msgNonce >> S.put msgOrigin 377 AnnounceType -> S.put msgNonce >> S.put msgOrigin
338 _ -> S.put msgOrigin >> S.put msgNonce 378 _ -> S.put msgOrigin >> S.put msgNonce
339 let Ciphered (Poly1305.Auth mac) bs = msgPayload 379 let putPayload = either putOnionPayload putCiphered msgPayload
340 S.putByteString (BA.convert mac) 380 putReturnPath = forM_ msgReturnPath $ \(SymmetricCiphered bs) -> S.putByteString bs
341 S.putByteString bs 381 case msgType of
382 OnionResponse1 -> putReturnPath >> putPayload
383 _ -> putPayload >> putReturnPath
342 384
343{- 385{-
344data Plain a = Plain 386data Plain a = Plain
@@ -352,7 +394,7 @@ instance Serialize a => Serialize (Plain a) where
352 put (Plain tid a) = put a >> put tid 394 put (Plain tid a) = put a >> put tid
353-} 395-}
354 396
355-- TODO: Cache symmetric keys. 397-- TODO: Cache shared symmetric keys.
356data SecretsCache = SecretsCache 398data SecretsCache = SecretsCache
357newEmptyCache = return SecretsCache 399newEmptyCache = return SecretsCache
358 400
@@ -398,9 +440,12 @@ computeSharedSecret sk recipient nonce = (hash, crypt)
398 Cryptonite.CryptoPassed hash = Poly1305.initialize rs 440 Cryptonite.CryptoPassed hash = Poly1305.initialize rs
399 441
400 442
401encryptMessage :: SecretKey -> SecretsCache -> NodeId -> Message ByteString -> Message Ciphered 443encryptMessage :: SecretKey -> SecretsCache -> NodeId -> Message ByteString -> Message (Either OnionPayload Ciphered)
402encryptMessage sk _ recipient plaintext 444encryptMessage sk _ recipient plaintext
403 = withSecret encipherAndHash sk recipient (msgNonce plaintext) <$> plaintext 445 = if typeHasEncryptedPayload (msgType plaintext)
446 then Right . withSecret encipherAndHash sk recipient (msgNonce plaintext) <$> plaintext
447 else Left . OnionPayload <$> plaintext
448
404 449
405decryptMessage :: SecretKey -> SecretsCache -> Message Ciphered -> Either String (Message ByteString) 450decryptMessage :: SecretKey -> SecretsCache -> Message Ciphered -> Either String (Message ByteString)
406decryptMessage sk _ ciphertext 451decryptMessage sk _ ciphertext
@@ -452,6 +497,11 @@ showParseError bs addr err = unlines $
452 concat [ either show show (either4or6 addr), " --> ", err ] 497 concat [ either show show (either4or6 addr), " --> ", err ]
453 : xxd 0 bs 498 : xxd 0 bs
454 499
500unzipMessage :: Message (Either a b) -> Either (Message a) (Message b)
501unzipMessage msg = either (\x -> Left msg { msgPayload = x })
502 (\y -> Right msg { msgPayload = y })
503 (msgPayload msg)
504
455-- TODO: 505-- TODO:
456-- Represents the encrypted portion of a Tox packet. 506-- Represents the encrypted portion of a Tox packet.
457-- data Payload a = Payload a !Nonce8 507-- data Payload a = Payload a !Nonce8
@@ -460,11 +510,13 @@ showParseError bs addr err = unlines $
460 510
461parsePacket :: SecretKey -> SecretsCache -> ByteString -> SockAddr -> Either String (Message ByteString, NodeInfo) 511parsePacket :: SecretKey -> SecretsCache -> ByteString -> SockAddr -> Either String (Message ByteString, NodeInfo)
462parsePacket sk cache bs addr = left (showParseError bs addr) $ do 512parsePacket sk cache bs addr = left (showParseError bs addr) $ do
463 ciphered <- S.runGet getMessage bs 513 msg <- S.runGet getMessage bs
464 ni <- nodeInfo (msgOrigin ciphered) addr 514 ni <- nodeInfo (msgOrigin msg) addr
465 left (showPayloadError ciphered ni " --> ") $ do 515 let decrypt ciphered = left (showPayloadError ciphered ni " --> ") $ do
466 msg <- decryptMessage sk cache ciphered 516 msg <- decryptMessage sk cache ciphered
467 return (msg, ni) 517 return (msg, ni)
518 passthrough onion = return (unpackOnionPayload <$> onion, ni)
519 either passthrough decrypt $ unzipMessage msg
468 520
469encodePacket :: SecretKey -> SecretsCache -> Message ByteString -> NodeInfo -> (ByteString, SockAddr) 521encodePacket :: SecretKey -> SecretsCache -> Message ByteString -> NodeInfo -> (ByteString, SockAddr)
470encodePacket sk cache msg ni = ( S.runPut . putMessage $ encryptMessage sk cache (nodeId ni) msg 522encodePacket sk cache msg ni = ( S.runPut . putMessage $ encryptMessage sk cache (nodeId ni) msg
@@ -489,7 +541,9 @@ newClient addr = do
489 secret <- generateSecretKey 541 secret <- generateSecretKey
490 let pubkey = key2id $ toPublic secret 542 let pubkey = key2id $ toPublic secret
491 cache <- newEmptyCache 543 cache <- newEmptyCache
492 drg <- getSystemDRG 544 (symkey, drg) <- do
545 drg0 <- getSystemDRG
546 return $ randomBytesGenerate 32 drg0 :: IO (ByteString, SystemDRG)
493 let tentative_ip4 = fromMaybe (IPv4 $ toEnum 0) (IPv4 <$> fromSockAddr addr) 547 let tentative_ip4 = fromMaybe (IPv4 $ toEnum 0) (IPv4 <$> fromSockAddr addr)
494 tentative_ip6 = fromMaybe (IPv6 $ toEnum 0) (IPv6 <$> fromSockAddr addr) 548 tentative_ip6 = fromMaybe (IPv6 $ toEnum 0) (IPv6 <$> fromSockAddr addr)
495 tentative_info = NodeInfo 549 tentative_info = NodeInfo
@@ -545,20 +599,27 @@ newClient addr = do
545 -- mutual reference just to be safe. 599 -- mutual reference just to be safe.
546 outgoingClient = client { clientNet = net { awaitMessage = return Nothing } } 600 outgoingClient = client { clientNet = net { awaitMessage = return Nothing } }
547 601
548 dispatch tbl = DispatchMethods 602 dispatch tbl var = DispatchMethods
549 { classifyInbound = classify 603 { classifyInbound = classify
550 , lookupHandler = handlers 604 , lookupHandler = handlers var
551 , tableMethods = tbl 605 , tableMethods = tbl
552 } 606 }
553 607
554 handlers :: Method -> Maybe Handler 608 -- handlers :: TVar -> Method -> Maybe Handler
555 handlers PingType = handler PongType pingH 609 handlers var PingType = handler PongType pingH
556 handlers GetNodesType = handler SendNodesType $ getNodesH routing 610 handlers var GetNodesType = handler SendNodesType $ getNodesH routing
557 handlers _ = Nothing 611 handlers var OnionRequest0 = noreply OnionRequest0
612 $ onionSend0H (symmetricCipher (return symkey)
613 (fst <$> readTVar var)
614 (modifyTVar' var . first . const))
615 udp
616 handlers var OnionResponse1 = noreply OnionResponse1
617 $ onionResponse1H (symmetricDecipher (return symkey))
618 udp
619 handlers var _ = Nothing
558 -- TODO DHTRequest public key (onion) 620 -- TODO DHTRequest public key (onion)
559 -- TODO DHTRequest NAT ping 621 -- TODO DHTRequest NAT ping
560 -- TODO BootstrapInfo 0xf0 622 -- TODO BootstrapInfo 0xf0
561 --
562 623
563 genNonce24 var (TransactionId nonce8 _) = atomically $ do 624 genNonce24 var (TransactionId nonce8 _) = atomically $ do
564 (g,pending) <- readTVar var 625 (g,pending) <- readTVar var
@@ -576,8 +637,8 @@ newClient addr = do
576 ) -> ToxClient 637 ) -> ToxClient
577 mkclient (tbl,var) = Client 638 mkclient (tbl,var) = Client
578 { clientNet = net 639 { clientNet = net
579 , clientDispatcher = dispatch tbl 640 , clientDispatcher = dispatch tbl var
580 , clientErrorReporter = printErrors stderr 641 , clientErrorReporter = (printErrors stderr) { reportTimeout = reportTimeout ignoreErrors }
581 , clientPending = var 642 , clientPending = var
582 , clientAddress = \maddr -> atomically $ do 643 , clientAddress = \maddr -> atomically $ do
583 let var = case flip prefer4or6 Nothing <$> maddr of 644 let var = case flip prefer4or6 Nothing <$> maddr of
@@ -659,10 +720,11 @@ classify (Message { msgType = typ
659 _ -> const $ IsUnknown ("Unknown message type: "++show typ) 720 _ -> const $ IsUnknown ("Unknown message type: "++show typ)
660 721
661encodePayload typ (TransactionId (Nonce8 tid) nonce) self dest b 722encodePayload typ (TransactionId (Nonce8 tid) nonce) self dest b
662 = Message { msgType = typ 723 = Message { msgType = typ
663 , msgOrigin = nodeId self 724 , msgOrigin = nodeId self
664 , msgNonce = nonce 725 , msgNonce = nonce
665 , msgPayload = S.encode b <> S.runPut (S.putWord64be tid) 726 , msgReturnPath = Nothing
727 , msgPayload = S.encode b <> S.runPut (S.putWord64be tid)
666 } 728 }
667 729
668decodePayload :: S.Serialize a => Message ByteString -> Either String a 730decodePayload :: S.Serialize a => Message ByteString -> Either String a
@@ -672,6 +734,14 @@ type Handler = MethodHandler String TransactionId NodeInfo (Message ByteString)
672 734
673handler typ f = Just $ MethodHandler decodePayload (encodePayload typ) f 735handler typ f = Just $ MethodHandler decodePayload (encodePayload typ) f
674 736
737noreply :: S.Serialize b =>
738 Method
739 -> (addr -> Message b -> IO ())
740 -> Maybe (MethodHandler String tid addr (Message ByteString))
741noreply typ f = Just $ NoReply (mapM deserialize) f
742 where
743 deserialize = S.decode . bool id dropEnd8 (typeHasEncryptedPayload typ)
744
675transitionCommittee :: TriadCommittee NodeId SockAddr -> RoutingTransition NodeInfo -> STM (IO ()) 745transitionCommittee :: TriadCommittee NodeId SockAddr -> RoutingTransition NodeInfo -> STM (IO ())
676transitionCommittee committee (RoutingTransition ni Stranger) = do 746transitionCommittee committee (RoutingTransition ni Stranger) = do
677 delVote committee (nodeId ni) 747 delVote committee (nodeId ni)
@@ -680,12 +750,14 @@ transitionCommittee committee (RoutingTransition ni Stranger) = do
680transitionCommittee committee _ = return $ return () 750transitionCommittee committee _ = return $ return ()
681 751
682updateRouting :: ToxClient -> Routing -> NodeInfo -> Message ByteString -> IO () 752updateRouting :: ToxClient -> Routing -> NodeInfo -> Message ByteString -> IO ()
683updateRouting client routing naddr msg = do 753updateRouting client routing addr msg = do
684 case prefer4or6 naddr Nothing of 754 forM_ (msgDHTKey msg) $ \nid -> do
685 Want_IP4 -> go (routing4 routing) (committee4 routing) (sched4 routing) 755 let naddr = addr { nodeId = nid }
686 Want_IP6 -> go (routing6 routing) (committee6 routing) (sched6 routing) 756 case prefer4or6 naddr Nothing of
687 where 757 Want_IP4 -> updateTable client naddr (routing4 routing) (committee4 routing) (sched4 routing)
688 go tbl committee sched = do 758 Want_IP6 -> updateTable client naddr (routing6 routing) (committee6 routing) (sched6 routing)
759
760updateTable client naddr tbl committee sched = do
689 self <- atomically $ R.thisNode <$> readTVar tbl 761 self <- atomically $ R.thisNode <$> readTVar tbl
690 when (nodeIP self /= nodeIP naddr) $ do 762 when (nodeIP self /= nodeIP naddr) $ do
691 -- TODO: IP address vote? 763 -- TODO: IP address vote?
@@ -727,6 +799,141 @@ instance S.Serialize SendNodes where
727 mapM_ S.put ns' 799 mapM_ S.put ns'
728 800
729 801
802-- self -> A
803-- OnionRequest0: Message (OnionWrap (OnionWrap (Forward msg)))
804-- OnionRequest0: Message (OnionWrap (OnionWrap Ciphered))
805-- OnionRequest0: Message (OnionWrap Ciphered)
806-- OnionRequest0: Message Ciphered
807
808-- A -> B
809-- OnionRequest0: Message Ciphered
810-- OnionRequest0: Message (OnionWrap Ciphered)
811-- OnionRequest1: Message Ciphered ++ SockAddr
812-- OnionRequest1: Message Ciphered ++ SymmetricCiphered
813--
814-- B -> C
815-- OnionRequest1: Message Ciphered ++ SymmetricCiphered
816-- OnionRequest1: Message (OnionWrap Ciphered) ++ SymmetricCiphered
817-- OnionRequest2: Message Ciphered ++ (SockAddr ++ SymmetricCiphered)
818-- OnionRequest2: Message Ciphered ++ SymmetricCiphered
819--
820-- C -> D
821-- OnionRequest2: Message Ciphered ++ SymmetricCiphered
822-- OnionRequest2: Message (Forward msg) ++ SymmetricCiphered
823-- ?????????????: msg ++ ( SockAddr ++ SymmetricCiphered)
824-- ?????????????: msg ++ SymmetricCiphered
825
826-- D -> C
827-- ?????????????: msg ++ SymmetricCiphered
828-- OnionResponse3: Message SymmetricCiphered ++ response
829--
830-- C -> B
831-- OnionResponse3: Message SymmetricCiphered ++ response
832-- OnionResponse3: Message (SockAddr ++ SymmetricCiphered) ++ response
833-- OnionResponse2: Message SymmetricCiphered ++ response
834--
835-- B -> A
836-- OnionResponse2: Message SymmetricCiphered ++ response
837-- OnionResponse2: Message (SockAddr ++ SymmetricCiphered) ++ response
838-- OnionResponse1: Message SymmetricCiphered ++ response
839--
840-- A -> self
841-- OnionResponse1: Message SymmetricCiphered ++ response
842-- OnionResponse1: Message SockAddr ++ response
843-- ??????????????: response
844--
845-- Onion payloads:
846-- AnounceRequest (0x83)
847-- = SeekingKey nid
848-- | AnnouncingKey pingid nid sendback_key
849--
850-- AnnounceResponse (0x84)
851-- = KeyNotFound pingid [ni] -- is_stored=0
852-- | KeyFound sendback_key [ni] -- is_stored=1
853-- | Announced pingid [ni] -- is_stored=2 What's the pingid for in this caes?
854-- -- Should it be a fresh one?
855--
856-- -- After you find an announce node for your friend, you share your dht nodeid thus:
857-- DataToRouteRequest (0x85)
858-- -- cleartext: Public key of destination node (used to lookup the sendback_key,ip,port of onion-return path)
859-- -- cleartext: nonce
860-- -- cleartext: alias (just generated key)
861-- -- encrypted (nonce,alias,sendback_key):
862-- real public key
863-- id byte
864-- -- encrypted
865-- DHTPublicKey (0x9c)
866-- { no_replay :: Word64
867-- , dhtKey :: NodeId
868-- , nearbyNodes :: [NodeInfo]
869-- }
870-- payload (optional)
871--
872-- -- The announce node forwards your message thus:
873-- -- This is the same as 0x85, but the destination key was removed.
874-- DataToRouteResponse (0x86)
875-- -- cleartext: nonce
876-- -- cleartext: alias
877-- -- encrypted payload.
878
879data OnionWrap a = OnionWrap
880 { forwardAddress :: SockAddr
881 , forwardAlias :: NodeId
882 , onionPayload :: a
883 }
884
885instance S.Serialize (OnionWrap Ciphered) where
886 get = getOnion
887 put = putOnion
888
889getOnion :: S.Get (OnionWrap Ciphered)
890getOnion = do
891 addr <- getForwardAddr
892 alias <- S.get
893 ciphered <- getCiphered
894 return $ OnionWrap addr alias ciphered
895
896getForwardAddr :: S.Get SockAddr
897getForwardAddr = do
898 addrfam <- S.get :: S.Get Word8
899 ip <- getIP addrfam
900 case ip of IPv4 _ -> S.skip 12 -- compliant peers would zero-fill this.
901 IPv6 _ -> return ()
902 port <- S.get :: S.Get PortNumber
903 return $ setPort port $ toSockAddr ip
904
905
906putForwardAddr :: SockAddr -> S.Put
907putForwardAddr saddr = fromMaybe (return $ error "unsupported SockAddr family") $ do
908 port <- sockAddrPort saddr
909 ip <- fromSockAddr $ either id id $ either4or6 saddr
910 return $ do
911 case ip of
912 IPv4 ip4 -> S.put (0x02 :: Word8) >> S.put ip4 >> S.putByteString (B.replicate 12 0)
913 IPv6 ip6 -> S.put (0x0a :: Word8) >> S.put ip6
914 S.put port
915
916putOnion :: OnionWrap Ciphered -> S.Put
917putOnion = error "todo: putOnion"
918
919getCiphered :: S.Get Ciphered
920getCiphered = do
921 mac <- Poly1305.Auth . BA.convert <$> S.getBytes 16
922 cnt <- S.remaining
923 bs <- S.getBytes cnt
924 return $ Ciphered mac bs
925
926putCiphered :: Ciphered -> S.Put
927putCiphered (Ciphered (Poly1305.Auth mac) bs) = do
928 S.putByteString (BA.convert mac)
929 S.putByteString bs
930
931data Announce = Announce
932 { announcePingId :: NodeId -- Ping ID
933 , announceSeeking :: NodeId -- Public key we are searching for
934 , announceKey :: NodeId -- Public key that we want those sending back data packets to use
935 }
936
730pingH :: NodeInfo -> Ping -> IO Pong 937pingH :: NodeInfo -> Ping -> IO Pong
731pingH _ Ping = return Pong 938pingH _ Ping = return Pong
732 939
@@ -762,6 +969,80 @@ getNodesH routing addr (GetNodes nid) = do
762 969
763 k = 4 970 k = 4
764 971
972
973symmetricCipher :: DRG g => STM ByteString -> STM g -> (g -> STM ()) -> ByteString -> IO SymmetricCiphered
974symmetricCipher currentSymmetricKey readG writeG bs = (>>= \e -> hPutStrLn stderr (show e) >> Cryptonite.throwCryptoErrorIO e) $ atomically $ do
975 g <- readG
976 let (sym_nonce_bytes, g') = randomBytesGenerate 12 g
977 writeG g'
978 symmkey <- currentSymmetricKey
979 return $ do
980 sym_nonce <- Symmetric.nonce12 sym_nonce_bytes
981 symm <- Symmetric.initialize symmkey sym_nonce
982 let (rpath_bs, symm') = Symmetric.encrypt bs symm
983 auth = Symmetric.finalize symm' -- 16 bytes
984 -- For a single SockAddr, bs will be 19 bytes which gives
985 -- 12 + 16 + 19 = 47 bytes.
986 -- We need 12 more make 59 bytes, so we'll include the nonce twice.
987 return $ SymmetricCiphered (sym_nonce_bytes <> sym_nonce_bytes <> BA.convert auth <> rpath_bs)
988
989symmetricDecipher currentSymmetricKey (Nonce24 nonce24) (SymmetricCiphered bs) = atomically $ do
990 symmkey <- currentSymmetricKey
991 return $ do
992 let sym_nonce_bytes = B.drop 12 nonce24
993 (mac, bs'') = B.splitAt 16 bs
994 symm <- left show . Cryptonite.eitherCryptoError $ do
995 sym_nonce <- Symmetric.nonce12 sym_nonce_bytes
996 Symmetric.initialize symmkey sym_nonce
997 let (ds, symm') = Symmetric.decrypt bs'' symm
998 auth = Symmetric.finalize symm'
999 if BA.convert auth /= mac
1000 then Left "symmetricDecipher: Auth fail."
1001 else return $ ds
1002
1003
1004
1005-- OnionRequest0
1006onionSend0H :: (ByteString -> IO SymmetricCiphered)
1007 -> Transport err SockAddr ByteString
1008 -> NodeInfo
1009 -> Message (OnionWrap Ciphered)
1010 -> IO ()
1011onionSend0H symcipher udp addr Message{ msgNonce
1012 , msgPayload = OnionWrap forward alias ciphered } = do
1013 hPutStrLn stderr $ "onionSend0H( " ++ show addr ++ " --> " ++ either show show (either4or6 forward) ++ ")"
1014 rpath <- symcipher (S.runPut $ putForwardAddr forward)
1015 sendMessage udp forward $ S.runPut $ putMessage
1016 Message { msgType = OnionRequest1
1017 , msgOrigin = alias
1018 , msgNonce = msgNonce
1019 , msgReturnPath = Just rpath
1020 , msgPayload = Right ciphered
1021 }
1022 hPutStrLn stderr $ "onionSend0H SENT ( " ++ show addr ++ " --> " ++ either show show (either4or6 forward) ++ ")"
1023
1024-- OnionResponse1
1025--
1026-- No public-key decryption here.
1027onionResponse1H ::
1028 (Nonce24 -> SymmetricCiphered -> IO (Either String ByteString))
1029 -> Transport err SockAddr ByteString
1030 -> NodeInfo
1031 -> Message OnionPayload
1032 -> IO ()
1033onionResponse1H symdecipher udp addr Message{ msgNonce
1034 , msgReturnPath
1035 , msgPayload
1036 }
1037 = do
1038 hPutStrLn stderr $ "onionResponse1H " ++ show addr ++ maybe " Nothing" (const" Just") msgReturnPath
1039 forM_ msgReturnPath $ \rpath -> do
1040 eaddr <- (>>= S.runGet getForwardAddr) <$> symdecipher msgNonce rpath
1041 let go forward = do
1042 hPutStrLn stderr $ "onionResponse1H( " ++ show addr ++ " --> " ++ either show show (either4or6 forward) ++ ")"
1043 sendMessage udp forward (unpackOnionPayload msgPayload)
1044 either (hPutStrLn stderr . mappend "onionResponse1H decipher ERROR ") (\x -> go x >> hPutStrLn stderr "onionResponse1H SENT") eaddr
1045
765intKey :: TransactionId -> Int 1046intKey :: TransactionId -> Int
766intKey (TransactionId (Nonce8 w) _) = fromIntegral w 1047intKey (TransactionId (Nonce8 w) _) = fromIntegral w
767 1048
@@ -769,8 +1050,8 @@ nonceKey :: TransactionId -> Nonce8
769nonceKey (TransactionId n _) = n 1050nonceKey (TransactionId n _) = n
770 1051
771-- randomBytesGenerate :: ByteArray byteArray => Int -> gen -> (byteArray, gen) 1052-- randomBytesGenerate :: ByteArray byteArray => Int -> gen -> (byteArray, gen)
772-- gen :: forall gen. DRG gen => gen -> ((Nonce8, Nonce24), gen) 1053gen :: forall gen. DRG gen => gen -> (TransactionId, gen)
773gen :: SystemDRG -> (TransactionId, SystemDRG) 1054-- gen :: SystemDRG -> (TransactionId, SystemDRG)
774gen g = let (bs, g') = randomBytesGenerate 24 g 1055gen g = let (bs, g') = randomBytesGenerate 24 g
775 (ws, g'') = randomBytesGenerate 8 g' 1056 (ws, g'') = randomBytesGenerate 8 g'
776 Right w = S.runGet S.getWord64be ws 1057 Right w = S.runGet S.getWord64be ws
diff --git a/src/Network/QueryResponse.hs b/src/Network/QueryResponse.hs
index 29a221e8..c8a6fa80 100644
--- a/src/Network/QueryResponse.hs
+++ b/src/Network/QueryResponse.hs
@@ -124,18 +124,27 @@ data MethodHandler err tid addr x = forall a b. MethodHandler
124 -- address of the query is provided to the handler. 124 -- address of the query is provided to the handler.
125 , methodAction :: addr -> a -> IO b 125 , methodAction :: addr -> a -> IO b
126 } 126 }
127 | forall a. NoReply
128 { -- | Parse the query into a more specific type for this method.
129 methodParse :: x -> Either err a
130 -- | Fully typed action to perform upon the query. The remote origin
131 -- address of the query is provided to the handler.
132 , noreplyAction :: addr -> a -> IO ()
133 }
127 134
128-- | Attempt to invoke a 'MethodHandler' upon a given inbound query. If the 135-- | Attempt to invoke a 'MethodHandler' upon a given inbound query. If the
129-- parse is successful, the returned IO action will construct our reply. 136-- parse is successful, the returned IO action will construct our reply if
130-- Otherwise, a parse err is returned. 137-- there is one. Otherwise, a parse err is returned.
131dispatchQuery :: MethodHandler err tid addr x -- ^ Handler to invoke. 138dispatchQuery :: MethodHandler err tid addr x -- ^ Handler to invoke.
132 -> tid -- ^ The transaction id for this query\/response session. 139 -> tid -- ^ The transaction id for this query\/response session.
133 -> addr -- ^ Our own address, to which the query was sent. 140 -> addr -- ^ Our own address, to which the query was sent.
134 -> x -- ^ The query packet. 141 -> x -- ^ The query packet.
135 -> addr -- ^ The origin address of the query. 142 -> addr -- ^ The origin address of the query.
136 -> Either err (IO x) 143 -> Either err (IO (Maybe x))
137dispatchQuery (MethodHandler unwrapQ wrapR f) tid self x addr = 144dispatchQuery (MethodHandler unwrapQ wrapR f) tid self x addr =
138 fmap (\a -> wrapR tid self addr <$> f addr a) $ unwrapQ x 145 fmap (\a -> Just . wrapR tid self addr <$> f addr a) $ unwrapQ x
146dispatchQuery (NoReply unwrapQ f) tid self x addr =
147 fmap (\a -> f addr a >> return Nothing) $ unwrapQ x
139 148
140-- | These four parameters are required to implement an ougoing query. A 149-- | These four parameters are required to implement an ougoing query. A
141-- peer-to-peer algorithm will define a 'MethodSerializer' for every 'MethodHandler' that 150-- peer-to-peer algorithm will define a 'MethodSerializer' for every 'MethodHandler' that
@@ -365,7 +374,7 @@ handleMessage (Client net d err pending whoami responseID) again = do
365 self <- whoami (Just addr) 374 self <- whoami (Just addr)
366 tid' <- responseID tid 375 tid' <- responseID tid
367 either (reportParseError err) 376 either (reportParseError err)
368 (>>= sendMessage net addr) 377 (>>= mapM_ (sendMessage net addr))
369 (dispatchQuery m tid' self plain addr) 378 (dispatchQuery m tid' self plain addr)
370 IsResponse tid -> do 379 IsResponse tid -> do
371 action <- atomically $ do 380 action <- atomically $ do