diff options
author | joe <joe@jerkface.net> | 2017-08-02 01:10:08 -0400 |
---|---|---|
committer | joe <joe@jerkface.net> | 2017-08-02 01:10:08 -0400 |
commit | 4198ce253ea9ef9184b325e4bb8d18fcc483b381 (patch) | |
tree | ee2db363a165e69d7ea9a07ab3d762e86b83f124 | |
parent | 8a46b3a8808a15017207bdcea067aa7857a95a11 (diff) |
More Tox stuff.
-rw-r--r-- | Mainline.hs | 2 | ||||
-rw-r--r-- | Tox.hs | 399 | ||||
-rw-r--r-- | src/Network/QueryResponse.hs | 19 |
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 |
@@ -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 | |||
67 | import Kademlia | 68 | import Kademlia |
68 | import Network.BitTorrent.DHT.Search (Search (..)) | 69 | import Network.BitTorrent.DHT.Search (Search (..)) |
69 | import Text.Printf | 70 | import Text.Printf |
71 | import qualified Crypto.Cipher.ChaChaPoly1305 as Symmetric | ||
72 | import Data.Bitraversable (bisequence) | ||
70 | 73 | ||
71 | newtype NodeId = NodeId ByteString | 74 | newtype 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 | ||
133 | getIP :: Word8 -> S.Get IP | ||
134 | getIP 0x02 = IPv4 <$> S.get | ||
135 | getIP 0x0a = IPv6 <$> S.get | ||
136 | getIP 0x82 = IPv4 <$> S.get -- TODO: TCP | ||
137 | getIP 0x8a = IPv6 <$> S.get -- TODO: TCP | ||
138 | getIP x = fail ("unsupported address family ("++show x++")") | ||
130 | 139 | ||
131 | instance S.Serialize NodeInfo where | 140 | instance 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 | |||
243 | pattern AnnounceType = MessageType 131 -- 0x83 Announce Request | 246 | pattern 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 | 253 | pattern 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 | ||
269 | newtype Nonce8 = Nonce8 Word64 | 276 | newtype Nonce8 = Nonce8 Word64 |
@@ -295,38 +302,71 @@ quoted shows s = '"':shows ('"':s) | |||
295 | bin2hex :: ByteArrayAccess bs => bs -> String | 302 | bin2hex :: ByteArrayAccess bs => bs -> String |
296 | bin2hex = C8.unpack . Base16.encode . BA.convert | 303 | bin2hex = C8.unpack . Base16.encode . BA.convert |
297 | 304 | ||
305 | newtype SymmetricCiphered = SymmetricCiphered ByteString | ||
306 | deriving (Eq,Show) | ||
298 | 307 | ||
299 | data Message a = Message | 308 | data 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 | ||
317 | typeHasEncryptedPayload OnionResponse1 = False | ||
318 | typeHasEncryptedPayload _ = True | ||
319 | |||
320 | msgDHTKey Message{ msgOrigin, msgType = PingType } = Just msgOrigin | ||
321 | msgDHTKey Message{ msgOrigin, msgType = PongType } = Just msgOrigin | ||
322 | msgDHTKey Message{ msgOrigin, msgType = GetNodesType } = Just msgOrigin | ||
323 | msgDHTKey Message{ msgOrigin, msgType = SendNodesType } = Just msgOrigin | ||
324 | msgDHTKey Message{ msgOrigin, msgType = OnionRequest0 } = Just msgOrigin | ||
325 | msgDHTKey _ = Nothing | ||
326 | |||
307 | data Ciphered = Ciphered { cipheredMAC :: Poly1305.Auth | 327 | data Ciphered = Ciphered { cipheredMAC :: Poly1305.Auth |
308 | , cipheredBytes :: ByteString } | 328 | , cipheredBytes :: ByteString } |
309 | deriving Eq | 329 | deriving Eq |
310 | 330 | ||
311 | getMessage :: S.Get (Message Ciphered) | 331 | newtype OnionPayload = OnionPayload { unpackOnionPayload :: ByteString } |
332 | |||
333 | instance S.Serialize OnionPayload where | ||
334 | get = OnionPayload <$> (S.remaining >>= S.getBytes) | ||
335 | put (OnionPayload bs) = S.putByteString bs | ||
336 | |||
337 | getMessage :: S.Get (Message (Either OnionPayload Ciphered)) | ||
312 | getMessage = do | 338 | getMessage = 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) | |
329 | putMessage :: Message Ciphered -> S.Put | 360 | return Message { msgType = typ |
361 | , msgOrigin = nid | ||
362 | , msgNonce = nonce | ||
363 | , msgReturnPath = rpath | ||
364 | , msgPayload = payload } | ||
365 | |||
366 | putOnionPayload :: OnionPayload -> S.Put | ||
367 | putOnionPayload (OnionPayload bs) = S.putByteString bs | ||
368 | |||
369 | putMessage :: Message (Either OnionPayload Ciphered) -> S.Put | ||
330 | putMessage (Message {..}) = do | 370 | putMessage (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 | {- |
344 | data Plain a = Plain | 386 | data 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. |
356 | data SecretsCache = SecretsCache | 398 | data SecretsCache = SecretsCache |
357 | newEmptyCache = return SecretsCache | 399 | newEmptyCache = 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 | ||
401 | encryptMessage :: SecretKey -> SecretsCache -> NodeId -> Message ByteString -> Message Ciphered | 443 | encryptMessage :: SecretKey -> SecretsCache -> NodeId -> Message ByteString -> Message (Either OnionPayload Ciphered) |
402 | encryptMessage sk _ recipient plaintext | 444 | encryptMessage 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 | ||
405 | decryptMessage :: SecretKey -> SecretsCache -> Message Ciphered -> Either String (Message ByteString) | 450 | decryptMessage :: SecretKey -> SecretsCache -> Message Ciphered -> Either String (Message ByteString) |
406 | decryptMessage sk _ ciphertext | 451 | decryptMessage 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 | ||
500 | unzipMessage :: Message (Either a b) -> Either (Message a) (Message b) | ||
501 | unzipMessage 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 | ||
461 | parsePacket :: SecretKey -> SecretsCache -> ByteString -> SockAddr -> Either String (Message ByteString, NodeInfo) | 511 | parsePacket :: SecretKey -> SecretsCache -> ByteString -> SockAddr -> Either String (Message ByteString, NodeInfo) |
462 | parsePacket sk cache bs addr = left (showParseError bs addr) $ do | 512 | parsePacket 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 | ||
469 | encodePacket :: SecretKey -> SecretsCache -> Message ByteString -> NodeInfo -> (ByteString, SockAddr) | 521 | encodePacket :: SecretKey -> SecretsCache -> Message ByteString -> NodeInfo -> (ByteString, SockAddr) |
470 | encodePacket sk cache msg ni = ( S.runPut . putMessage $ encryptMessage sk cache (nodeId ni) msg | 522 | encodePacket 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 | ||
661 | encodePayload typ (TransactionId (Nonce8 tid) nonce) self dest b | 722 | encodePayload 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 | ||
668 | decodePayload :: S.Serialize a => Message ByteString -> Either String a | 730 | decodePayload :: S.Serialize a => Message ByteString -> Either String a |
@@ -672,6 +734,14 @@ type Handler = MethodHandler String TransactionId NodeInfo (Message ByteString) | |||
672 | 734 | ||
673 | handler typ f = Just $ MethodHandler decodePayload (encodePayload typ) f | 735 | handler typ f = Just $ MethodHandler decodePayload (encodePayload typ) f |
674 | 736 | ||
737 | noreply :: S.Serialize b => | ||
738 | Method | ||
739 | -> (addr -> Message b -> IO ()) | ||
740 | -> Maybe (MethodHandler String tid addr (Message ByteString)) | ||
741 | noreply typ f = Just $ NoReply (mapM deserialize) f | ||
742 | where | ||
743 | deserialize = S.decode . bool id dropEnd8 (typeHasEncryptedPayload typ) | ||
744 | |||
675 | transitionCommittee :: TriadCommittee NodeId SockAddr -> RoutingTransition NodeInfo -> STM (IO ()) | 745 | transitionCommittee :: TriadCommittee NodeId SockAddr -> RoutingTransition NodeInfo -> STM (IO ()) |
676 | transitionCommittee committee (RoutingTransition ni Stranger) = do | 746 | transitionCommittee 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 | |||
680 | transitionCommittee committee _ = return $ return () | 750 | transitionCommittee committee _ = return $ return () |
681 | 751 | ||
682 | updateRouting :: ToxClient -> Routing -> NodeInfo -> Message ByteString -> IO () | 752 | updateRouting :: ToxClient -> Routing -> NodeInfo -> Message ByteString -> IO () |
683 | updateRouting client routing naddr msg = do | 753 | updateRouting 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 | |||
760 | updateTable 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 | |||
879 | data OnionWrap a = OnionWrap | ||
880 | { forwardAddress :: SockAddr | ||
881 | , forwardAlias :: NodeId | ||
882 | , onionPayload :: a | ||
883 | } | ||
884 | |||
885 | instance S.Serialize (OnionWrap Ciphered) where | ||
886 | get = getOnion | ||
887 | put = putOnion | ||
888 | |||
889 | getOnion :: S.Get (OnionWrap Ciphered) | ||
890 | getOnion = do | ||
891 | addr <- getForwardAddr | ||
892 | alias <- S.get | ||
893 | ciphered <- getCiphered | ||
894 | return $ OnionWrap addr alias ciphered | ||
895 | |||
896 | getForwardAddr :: S.Get SockAddr | ||
897 | getForwardAddr = 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 | |||
906 | putForwardAddr :: SockAddr -> S.Put | ||
907 | putForwardAddr 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 | |||
916 | putOnion :: OnionWrap Ciphered -> S.Put | ||
917 | putOnion = error "todo: putOnion" | ||
918 | |||
919 | getCiphered :: S.Get Ciphered | ||
920 | getCiphered = 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 | |||
926 | putCiphered :: Ciphered -> S.Put | ||
927 | putCiphered (Ciphered (Poly1305.Auth mac) bs) = do | ||
928 | S.putByteString (BA.convert mac) | ||
929 | S.putByteString bs | ||
930 | |||
931 | data 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 | |||
730 | pingH :: NodeInfo -> Ping -> IO Pong | 937 | pingH :: NodeInfo -> Ping -> IO Pong |
731 | pingH _ Ping = return Pong | 938 | pingH _ 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 | |||
973 | symmetricCipher :: DRG g => STM ByteString -> STM g -> (g -> STM ()) -> ByteString -> IO SymmetricCiphered | ||
974 | symmetricCipher 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 | |||
989 | symmetricDecipher 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 | ||
1006 | onionSend0H :: (ByteString -> IO SymmetricCiphered) | ||
1007 | -> Transport err SockAddr ByteString | ||
1008 | -> NodeInfo | ||
1009 | -> Message (OnionWrap Ciphered) | ||
1010 | -> IO () | ||
1011 | onionSend0H 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. | ||
1027 | onionResponse1H :: | ||
1028 | (Nonce24 -> SymmetricCiphered -> IO (Either String ByteString)) | ||
1029 | -> Transport err SockAddr ByteString | ||
1030 | -> NodeInfo | ||
1031 | -> Message OnionPayload | ||
1032 | -> IO () | ||
1033 | onionResponse1H 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 | |||
765 | intKey :: TransactionId -> Int | 1046 | intKey :: TransactionId -> Int |
766 | intKey (TransactionId (Nonce8 w) _) = fromIntegral w | 1047 | intKey (TransactionId (Nonce8 w) _) = fromIntegral w |
767 | 1048 | ||
@@ -769,8 +1050,8 @@ nonceKey :: TransactionId -> Nonce8 | |||
769 | nonceKey (TransactionId n _) = n | 1050 | nonceKey (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) | 1053 | gen :: forall gen. DRG gen => gen -> (TransactionId, gen) |
773 | gen :: SystemDRG -> (TransactionId, SystemDRG) | 1054 | -- gen :: SystemDRG -> (TransactionId, SystemDRG) |
774 | gen g = let (bs, g') = randomBytesGenerate 24 g | 1055 | gen 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. |
131 | dispatchQuery :: MethodHandler err tid addr x -- ^ Handler to invoke. | 138 | dispatchQuery :: 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)) |
137 | dispatchQuery (MethodHandler unwrapQ wrapR f) tid self x addr = | 144 | dispatchQuery (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 |
146 | dispatchQuery (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 |