summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Tox.hs362
-rw-r--r--src/Network/BitTorrent/DHT/Token.hs18
2 files changed, 269 insertions, 111 deletions
diff --git a/Tox.hs b/Tox.hs
index 8ee065d6..a14e223b 100644
--- a/Tox.hs
+++ b/Tox.hs
@@ -7,71 +7,78 @@
7{-# LANGUAGE GeneralizedNewtypeDeriving #-} 7{-# LANGUAGE GeneralizedNewtypeDeriving #-}
8{-# LANGUAGE NamedFieldPuns #-} 8{-# LANGUAGE NamedFieldPuns #-}
9{-# LANGUAGE PatternSynonyms #-} 9{-# LANGUAGE PatternSynonyms #-}
10{-# LANGUAGE RankNTypes #-}
10{-# LANGUAGE ScopedTypeVariables #-} 11{-# LANGUAGE ScopedTypeVariables #-}
11{-# LANGUAGE TupleSections #-} 12{-# LANGUAGE TupleSections #-}
12module Tox where 13module Tox where
13 14
14import Control.Applicative 15import Control.Applicative
15import Control.Arrow 16import Control.Arrow
16import Control.Concurrent (MVar) 17import Control.Concurrent (MVar)
17import Control.Concurrent.STM 18import Control.Concurrent.STM
18import qualified Crypto.Cipher.Salsa as Salsa 19import Control.Monad
19import qualified Crypto.Cipher.XSalsa as XSalsa 20import qualified Crypto.Cipher.ChaChaPoly1305 as Symmetric
21import qualified Crypto.Cipher.Salsa as Salsa
22import qualified Crypto.Cipher.XSalsa as XSalsa
20import Crypto.ECC.Class 23import Crypto.ECC.Class
21import qualified Crypto.Error as Cryptonite 24import qualified Crypto.Error as Cryptonite
22import Crypto.Error.Types 25import Crypto.Error.Types
23import qualified Crypto.MAC.Poly1305 as Poly1305 26import qualified Crypto.MAC.Poly1305 as Poly1305
24import Crypto.PubKey.Curve25519 27import Crypto.PubKey.Curve25519
25import Crypto.PubKey.ECC.Types 28import Crypto.PubKey.ECC.Types
26import Crypto.Random 29import Crypto.Random
30import qualified Data.Aeson as JSON
31 ;import Data.Aeson (FromJSON, ToJSON, (.=))
32import Data.Bitraversable (bisequence)
33import Data.Bits
34import Data.Bits.ByteString ()
27import Data.Bool 35import Data.Bool
28import qualified Data.ByteArray as BA 36import qualified Data.ByteArray as BA
29 ;import Data.ByteArray (ByteArrayAccess,Bytes) 37 ;import Data.ByteArray (ByteArrayAccess, Bytes)
30import qualified Data.ByteString as B 38import qualified Data.ByteString as B
31 ;import Data.ByteString (ByteString) 39 ;import Data.ByteString (ByteString)
32import qualified Data.ByteString.Base16 as Base16 40import qualified Data.ByteString.Base16 as Base16
33import qualified Data.ByteString.Char8 as C8 41import qualified Data.ByteString.Char8 as C8
34import Data.ByteString.Lazy (toStrict) 42import Data.ByteString.Lazy (toStrict)
43import Data.Char
35import Data.Data 44import Data.Data
45import Data.Hashable
36import Data.IP 46import Data.IP
37import Data.Maybe 47import Data.Maybe
48import qualified Data.MinMaxPSQ as MinMaxPSQ
49 ;import Data.MinMaxPSQ (MinMaxPSQ')
38import Data.Monoid 50import Data.Monoid
39import qualified Data.Serialize as S 51import Data.Ord
52import qualified Data.Serialize as S
53import Data.Time.Clock.POSIX (POSIXTime, getPOSIXTime)
40import Data.Typeable 54import Data.Typeable
41import Data.Word 55import Data.Word
56import qualified Data.Wrapper.PSQ as PSQ
57 ;import Data.Wrapper.PSQ (PSQ)
58import qualified Data.Wrapper.PSQInt as Int
42import Foreign.Marshal.Alloc 59import Foreign.Marshal.Alloc
43import Foreign.Ptr 60import Foreign.Ptr
44import Foreign.Storable 61import Foreign.Storable
45import GHC.Generics (Generic) 62import GHC.Generics (Generic)
46import Network.Address (Address, fromSockAddr, sockAddrPort, testIdBit, 63import Global6
47 toSockAddr, setPort, un4map, WantIP(..), ipFamily, 64import Kademlia
48 either4or6) 65import Network.Address (Address, WantIP (..), either4or6,
66 fromSockAddr, ipFamily, setPort,
67 sockAddrPort, testIdBit,
68 toSockAddr, un4map)
69import Network.BitTorrent.DHT.Search (Search (..))
70import qualified Network.DHT.Routing as R
49import Network.QueryResponse 71import Network.QueryResponse
50import Network.Socket 72import Network.Socket
51import System.Endian 73import System.Endian
52import Data.Hashable
53import Data.Bits
54import Data.Bits.ByteString ()
55import qualified Text.ParserCombinators.ReadP as RP
56import Data.Char
57import TriadCommittee
58import qualified Network.DHT.Routing as R
59import qualified Data.Wrapper.PSQInt as Int
60import Data.Time.Clock.POSIX (POSIXTime)
61import Global6
62import Data.Ord
63import System.IO 74import System.IO
64import qualified Data.Aeson as JSON 75import qualified Text.ParserCombinators.ReadP as RP
65 ;import Data.Aeson (FromJSON, ToJSON, (.=))
66import Control.Monad
67import Text.Read
68import Kademlia
69import Network.BitTorrent.DHT.Search (Search (..))
70import Text.Printf 76import Text.Printf
71import qualified Crypto.Cipher.ChaChaPoly1305 as Symmetric 77import Text.Read
72import Data.Bitraversable (bisequence) 78import qualified ToxMessage as Tox
73import ToxMessage (quoted,bin2hex) 79 ;import ToxMessage (bin2hex, quoted)
74import qualified ToxMessage as Tox 80import TriadCommittee
81import Network.BitTorrent.DHT.Token as Token
75 82
76{- 83{-
77newtype NodeId = NodeId ByteString 84newtype NodeId = NodeId ByteString
@@ -224,7 +231,7 @@ nodeInfo nid saddr
224 | otherwise = Left "Address family not supported." 231 | otherwise = Left "Address family not supported."
225 232
226data TransactionId = TransactionId 233data TransactionId = TransactionId
227 { transactionKey :: Nonce8 -- ^ Used to lookup pending query. 234 { transactionKey :: Tox.Nonce8 -- ^ Used to lookup pending query.
228 , cryptoNonce :: Tox.Nonce24 -- ^ Used during the encryption layer. 235 , cryptoNonce :: Tox.Nonce24 -- ^ Used during the encryption layer.
229 } 236 }
230 237
@@ -247,8 +254,7 @@ pattern OnionRequest0 = Tox.PacketKind 128 -- 0x80 Onion Request 0
247pattern OnionRequest1 = Tox.PacketKind 129 -- 0x81 Onion Request 1 254pattern OnionRequest1 = Tox.PacketKind 129 -- 0x81 Onion Request 1
248pattern OnionRequest2 = Tox.PacketKind 130 -- 0x82 Onion Request 2 255pattern OnionRequest2 = Tox.PacketKind 130 -- 0x82 Onion Request 2
249pattern AnnounceType = Tox.PacketKind 131 -- 0x83 Announce Request 256pattern AnnounceType = Tox.PacketKind 131 -- 0x83 Announce Request
250 257pattern AnnounceResponseType = Tox.PacketKind 132 -- 0x84 Announce Response
251-- 0x84 Announce Response
252-- 0x85 Onion Data Request (data to route request packet) 258-- 0x85 Onion Data Request (data to route request packet)
253-- 0x86 Onion Data Response (data to route response packet) 259-- 0x86 Onion Data Response (data to route response packet)
254-- 0x8c Onion Response 3 260-- 0x8c Onion Response 3
@@ -276,19 +282,6 @@ instance Show Tox.PacketKind where
276 showsPrec d AnnounceType = mappend "AnnounceType" 282 showsPrec d AnnounceType = mappend "AnnounceType"
277 showsPrec d (Tox.PacketKind x) = mappend "PacketKind " . showsPrec (d+1) x 283 showsPrec d (Tox.PacketKind x) = mappend "PacketKind " . showsPrec (d+1) x
278 284
279newtype Nonce8 = Nonce8 Word64
280 deriving (Eq, Ord, S.Serialize)
281
282instance ByteArrayAccess Nonce8 where
283 length _ = 8
284 withByteArray (Nonce8 w64) kont =
285 allocaBytes 8 $ \p -> do
286 poke (castPtr p :: Ptr Word64) $ toBE64 w64
287 kont p
288
289instance Show Nonce8 where
290 showsPrec d nonce = quoted (mappend $ bin2hex nonce)
291
292{- 285{-
293newtype Tox.Nonce24 = Tox.Nonce24 ByteString 286newtype Tox.Nonce24 = Tox.Nonce24 ByteString
294 deriving (Eq, Ord, ByteArrayAccess) 287 deriving (Eq, Ord, ByteArrayAccess)
@@ -319,7 +312,7 @@ data Msg = Msg
319 { msgType :: Tox.PacketKind 312 { msgType :: Tox.PacketKind
320 , msgNonce :: Tox.Nonce24 313 , msgNonce :: Tox.Nonce24
321 , msgData :: ByteString 314 , msgData :: ByteString
322 , msgSendBack :: Nonce8 315 , msgSendBack :: Tox.Nonce8
323 } 316 }
324 deriving Show 317 deriving Show
325 318
@@ -400,7 +393,7 @@ putMessage (Message {..}) = do
400 393
401{- 394{-
402data Plain a = Plain 395data Plain a = Plain
403 { plainId :: Nonce8 -- transactionKey of TransactionId 396 { plainId :: Tox.Nonce8 -- transactionKey of TransactionId
404 , plainPayload :: a 397 , plainPayload :: a
405 } 398 }
406 deriving (Eq, Show, Generic, Functor, Foldable, Traversable) 399 deriving (Eq, Show, Generic, Functor, Foldable, Traversable)
@@ -426,11 +419,11 @@ key2id pk = case S.decode (BA.convert pk) of
426 Right nid -> nid 419 Right nid -> nid
427 420
428 421
429zeros32 :: Bytes 422zeros32 :: Nonce32
430zeros32 = BA.replicate 32 0 423zeros32 = Nonce32 $ BA.replicate 32 0
431 424
432zeros24 :: Bytes 425zeros24 :: ByteString
433zeros24 = BA.take 24 zeros32 426zeros24 = BA.take 24 zs where Nonce32 zs = zeros32
434 427
435hsalsa20 k n = a <> b 428hsalsa20 k n = a <> b
436 where 429 where
@@ -451,7 +444,7 @@ computeSharedSecret sk recipient nonce = (hash, crypt)
451 -- cipher state 444 -- cipher state
452 st0 = XSalsa.initialize 20 k nonce 445 st0 = XSalsa.initialize 20 k nonce
453 -- Poly1305 key 446 -- Poly1305 key
454 (rs, crypt) = XSalsa.combine st0 zeros32 447 (rs, crypt) = XSalsa.combine st0 zs where Nonce32 zs = zeros32
455 -- Since rs is 32 bytes, this pattern should never fail... 448 -- Since rs is 32 bytes, this pattern should never fail...
456 Cryptonite.CryptoPassed hash = Poly1305.initialize rs 449 Cryptonite.CryptoPassed hash = Poly1305.initialize rs
457 450
@@ -464,16 +457,25 @@ encryptMessage sk _ recipient plaintext
464 else Left . OnionPayload <$> plaintext 457 else Left . OnionPayload <$> plaintext
465-} 458-}
466 459
467encryptAssymetric :: SecretKey -> NodeId -> NodeId -> Msg -> (Tox.PacketKind, Tox.Assymetric) 460encryptAssymetric :: SecretKey -> NodeId -> NodeId -> Msg -> Tox.Assymetric
468encryptAssymetric sk pk recipient (Msg typ nonce plaintext sendback) 461encryptAssymetric sk pk recipient (Msg typ nonce plaintext sendback) = assym
469 = ( typ 462 where
470 , Tox.Assymetric 463 assym = Tox.Assymetric
471 { senderKey = pk 464 { senderKey = pk
472 , sent = Tox.UnclaimedAssymetric 465 , sent = Tox.UnclaimedAssymetric
473 { assymetricNonce = nonce 466 { assymetricNonce = nonce
474 , assymetricData = withSecret encipherAndHash sk recipient nonce (plaintext <> S.encode sendback) 467 , assymetricData = withSecret encipherAndHash sk recipient nonce (plaintext <> S.encode sendback)
475 } 468 }
476 } ) 469 }
470
471encryptUnclm :: SecretKey -> NodeId -> NodeId -> Msg -> Tox.UnclaimedAssymetric
472encryptUnclm sk pk recipient (Msg typ nonce plaintext _) = unclm
473 where
474 unclm = Tox.UnclaimedAssymetric
475 { assymetricNonce = nonce
476 , assymetricData = withSecret encipherAndHash sk recipient nonce plaintext
477 }
478
477 479
478{- 480{-
479decryptMessage :: SecretKey -> SecretsCache -> Message Ciphered -> Either String (Message ByteString) 481decryptMessage :: SecretKey -> SecretsCache -> Message Ciphered -> Either String (Message ByteString)
@@ -481,15 +483,29 @@ decryptMessage sk _ ciphertext
481 = mapM (withSecret decipherAndAuth sk (msgOrigin ciphertext) (msgNonce ciphertext)) ciphertext 483 = mapM (withSecret decipherAndAuth sk (msgOrigin ciphertext) (msgNonce ciphertext)) ciphertext
482-} 484-}
483 485
484decryptAssymetric :: SecretKey -> (Tox.PacketKind, Tox.Assymetric) -> Either String Msg 486decryptAssymetric :: SecretKey -> Tox.PacketKind -> Tox.Assymetric -> Either String Msg
485decryptAssymetric sk (typ,assym) 487decryptAssymetric sk typ assym
486 = f <$> withSecret decipherAndAuth sk 488 = f <$> withSecret decipherAndAuth sk
487 (Tox.senderKey assym) 489 (Tox.senderKey assym)
488 nonce 490 nonce
489 (Tox.assymetricData . Tox.sent $ assym) 491 (Tox.assymetricData . Tox.sent $ assym)
490 where 492 where
491 nonce = Tox.assymetricNonce . Tox.sent $ assym 493 nonce = Tox.assymetricNonce . Tox.sent $ assym
492 f bs = uncurry (Msg typ nonce) . second (either (const (Nonce8 0)) id . S.decode) $ B.splitAt (B.length bs - 8) bs 494 f bs = uncurry (Msg typ nonce)
495 . second (either (const (Tox.Nonce8 0)) id . S.decode)
496 $ B.splitAt (B.length bs - 8) bs
497
498{-
499decryptUnclm :: SecretKey -> Tox.PacketKind -> NodeId -> Tox.Nonce8 -> Tox.UnclaimedAssymetric -> Either String Msg
500decryptUnclm sk typ sender n8 unclm
501 = f <$> withSecret decipherAndAuth sk
502 sender
503 nonce
504 (Tox.assymetricData unclm)
505 where
506 nonce = Tox.assymetricNonce unclm
507 f bs = Msg typ nonce bs n8
508-}
493 509
494withSecret f sk recipient nonce x = f hash crypt x 510withSecret f sk recipient nonce x = f hash crypt x
495 where 511 where
@@ -544,7 +560,7 @@ unzipMessage msg = either (\x -> Left msg { msgPayload = x })
544 560
545-- TODO: 561-- TODO:
546-- Represents the encrypted portion of a Tox packet. 562-- Represents the encrypted portion of a Tox packet.
547-- data Payload a = Payload a !Nonce8 563-- data Payload a = Payload a !Tox.Nonce8
548-- 564--
549-- Generic packet type: Message (Payload ByteString) 565-- Generic packet type: Message (Payload ByteString)
550 566
@@ -565,22 +581,40 @@ encodePacket sk cache msg ni = ( S.runPut . putMessage $ encryptMessage sk cache
565 581
566msgLayer :: SecretKey 582msgLayer :: SecretKey
567 -> NodeId 583 -> NodeId
568 -> Transport String NodeInfo (Tox.PacketKind,Tox.Assymetric) 584 -> Transport String NodeInfo (Tox.PacketKind,InterediateRep)
569 -> Transport String NodeInfo Msg 585 -> Transport String NodeInfo Msg
570msgLayer sk pk = layerTransport parse serialize 586msgLayer sk pk = layerTransport parse serialize
571 where 587 where
572 parse x addr = fmap (,addr) $ decryptAssymetric sk x 588 parse (typ,Assym x) addr = fmap (,addr) $ decryptAssymetric sk typ x
573 serialize x addr = (encryptAssymetric sk pk (nodeId addr) x, addr) 589 parse (typ,Assym' x) addr = fmap (,addr) $ decryptAssymetric sk typ x
574 590 parse (typ,Unclm n x) addr = Right ( Msg typ (Tox.assymetricNonce x) (S.encode (Tox.assymetricData x)) n
575asymLayer :: Transport String SockAddr Tox.Packet -> Transport String NodeInfo (Tox.PacketKind,Tox.Assymetric) 591 , addr)
592 serialize x addr = case Tox.pktClass (msgType x) of
593 Tox.AssymetricClass {} -> ((msgType x, Assym $ encryptAssymetric sk pk (nodeId addr) x), addr)
594 Tox.AliasedClass {} -> ((msgType x, Assym' $ encryptAssymetric sk pk (nodeId addr) x), addr)
595 Tox.NoncedUnclaimedClass {} -> ((msgType x, Unclm (msgSendBack x) $ encryptUnclm sk pk (nodeId addr) x),addr)
596
597data InterediateRep = Assym Tox.Assymetric
598 | Assym' Tox.Assymetric
599 | Unclm Tox.Nonce8 Tox.UnclaimedAssymetric
600
601asymLayer :: Transport String SockAddr Tox.Packet -> Transport String NodeInfo (Tox.PacketKind,InterediateRep)
576asymLayer = layerTransport parse serialize 602asymLayer = layerTransport parse serialize
577 where 603 where
578 parse x addr = case Tox.pktClass (Tox.pktKind x) of 604 parse x addr = case Tox.pktClass (Tox.pktKind x) of
579 Tox.AssymetricClass top fromp -> fmap ((Tox.pktKind x,y),) $ nodeInfo (Tox.senderKey y) addr where y = fromp x 605 Tox.AssymetricClass top fromp -> go Tox.senderKey fromp Assym
580 606 Tox.AliasedClass top fromp -> go Tox.senderKey ((\(Tox.Aliased a) -> a) . fromp) Assym'
581 serialize (typ,assym) addr = (x,nodeAddr addr) 607 Tox.NoncedUnclaimedClass top fromp -> go (const zeroID) fromp (uncurry Unclm)
582 where x = case Tox.pktClass typ of 608 where go mkaddr fromp c = let y = fromp x
583 Tox.AssymetricClass top _ -> top assym 609 in fmap ((Tox.pktKind x,c y),)
610 $ nodeInfo (mkaddr y) addr
611
612 serialize (typ,Assym assym) addr = (x,nodeAddr addr)
613 where x = case Tox.pktClass typ of Tox.AssymetricClass top _ -> top assym
614 serialize (typ,Assym' assym) addr = (x,nodeAddr addr)
615 where x = case Tox.pktClass typ of Tox.AliasedClass top _ -> top (Tox.Aliased assym)
616 serialize (typ,Unclm nonce unclm) addr = (x,nodeAddr addr)
617 where x = case Tox.pktClass typ of Tox.NoncedUnclaimedClass top _ -> top nonce unclm
584 618
585toxLayer :: Transport String SockAddr ByteString -> Transport String SockAddr Tox.Packet 619toxLayer :: Transport String SockAddr ByteString -> Transport String SockAddr Tox.Packet
586toxLayer = layerTransport (\x addr -> (,addr) <$> S.decode x) 620toxLayer = layerTransport (\x addr -> (,addr) <$> S.decode x)
@@ -605,11 +639,13 @@ trimPackets :: SockAddr -> ByteString -> IO (Maybe (ByteString -> ByteString))
605trimPackets addr bs = do 639trimPackets addr bs = do
606 hPutStrLn stderr $ "GOT " ++ show (Tox.PacketKind (B.head bs)) 640 hPutStrLn stderr $ "GOT " ++ show (Tox.PacketKind (B.head bs))
607 return $ case Tox.PacketKind (B.head bs) of 641 return $ case Tox.PacketKind (B.head bs) of
608 PingType -> Just id 642 PingType -> Just id
609 PongType -> Just id 643 PongType -> Just id
610 SendNodesType -> Just id 644 SendNodesType -> Just id
611 GetNodesType -> Just id 645 GetNodesType -> Just id
612 _ -> Nothing 646 AnnounceType -> Just id
647 AnnounceResponseType -> Just id
648 _ -> Nothing
613 649
614newClient :: SockAddr -> IO (ToxClient, Routing) 650newClient :: SockAddr -> IO (ToxClient, Routing)
615newClient addr = do 651newClient addr = do
@@ -664,18 +700,23 @@ newClient addr = do
664 let mapT = transactionMethods (contramapT nonceKey mapMethods) gen 700 let mapT = transactionMethods (contramapT nonceKey mapMethods) gen
665 map_var <- atomically $ newTVar (drg, mempty) 701 map_var <- atomically $ newTVar (drg, mempty)
666 return $ Left (mapT,map_var) 702 return $ Left (mapT,map_var)
703 keydb <- atomically $ newTVar $ AnnouncedKeys PSQ.empty MinMaxPSQ.empty
704 toks <- do
705 nil <- nullSessionTokens
706 atomically $ newTVar nil { maxInterval = 20 } -- 20 second timeout on announce ping-ids.
667 let net = addHandler (handleMessage client) 707 let net = addHandler (handleMessage client)
668 $ addVerbosity 708 $ addVerbosity
669 $ msgLayer secret pubkey 709 $ msgLayer secret pubkey
670 $ onInbound (updateRouting client routing) 710 $ onInbound (updateRouting client routing)
671 $ asymLayer 711 $ asymLayer
712 -- $ addHandler (handleMessage aclient)
672 $ toxLayer 713 $ toxLayer
673 $ addVerbosity2 714 $ addVerbosity2
674 $ addHandler trimPackets udp 715 $ addHandler trimPackets udp
675 716
676 dispatch tbl var = DispatchMethods 717 dispatch tbl var handlers = DispatchMethods
677 { classifyInbound = classify 718 { classifyInbound = classify
678 , lookupHandler = handlers var 719 , lookupHandler = handlers -- var
679 , tableMethods = tbl 720 , tableMethods = tbl
680 } 721 }
681 722
@@ -685,8 +726,11 @@ newClient addr = do
685 726
686 727
687 -- handlers :: TVar -> Method -> Maybe Handler 728 -- handlers :: TVar -> Method -> Maybe Handler
688 handlers var PingType = handler PongType pingH 729 -- handlers :: forall h u. (TVar (h, u (MVar Msg)) -> Tox.PacketKind -> Maybe Handler)
689 handlers var GetNodesType = handler SendNodesType $ getNodesH routing 730 handlers :: Tox.PacketKind -> Maybe Handler
731 handlers PingType = handler PongType pingH
732 handlers GetNodesType = handler SendNodesType $ getNodesH routing
733 handlers AnnounceType = handler AnnounceResponseType $ announceH routing toks keydb
690 {- 734 {-
691 handlers var OnionRequest0 = noreply OnionRequest0 735 handlers var OnionRequest0 = noreply OnionRequest0
692 $ onionSend0H (symmetricCipher (return symkey) 736 $ onionSend0H (symmetricCipher (return symkey)
@@ -697,28 +741,33 @@ newClient addr = do
697 $ onionResponse1H (symmetricDecipher (return symkey)) 741 $ onionResponse1H (symmetricDecipher (return symkey))
698 udp 742 udp
699 -} 743 -}
700 handlers var _ = Nothing 744 handlers _ = Nothing
701 -- TODO DHTRequest public key (onion) 745 -- TODO DHTRequest public key (onion)
702 -- TODO DHTRequest NAT ping 746 -- TODO DHTRequest NAT ping
703 -- TODO BootstrapInfo 0xf0 747 -- TODO BootstrapInfo 0xf0
704 748
749 announceHandlers _ = Nothing
750
705 genNonce24 var (TransactionId nonce8 _) = atomically $ do 751 genNonce24 var (TransactionId nonce8 _) = atomically $ do
706 (g,pending) <- readTVar var 752 (g,pending) <- readTVar var
707 let (bs, g') = randomBytesGenerate 24 g 753 let (bs, g') = randomBytesGenerate 24 g
708 writeTVar var (g',pending) 754 writeTVar var (g',pending)
709 return $ TransactionId nonce8 (Tox.Nonce24 bs) 755 return $ TransactionId nonce8 (Tox.Nonce24 bs)
710 756
711 client = either mkclient mkclient tblvar 757 client = either mkclient mkclient tblvar handlers
712 758
713 mkclient :: DRG g => 759 mkclient :: DRG g =>
714 ( TransactionMethods (g,t (MVar Msg)) 760 ( TransactionMethods (g,t (MVar Msg))
715 TransactionId 761 TransactionId
716 Msg 762 Msg
717 , TVar (g, t (MVar Msg)) 763 , TVar (g, t (MVar Msg))
718 ) -> ToxClient 764 )
719 mkclient (tbl,var) = Client 765 -- -> (forall h u. (TVar (h, u (MVar Msg)) -> Tox.PacketKind -> Maybe Handler))
766 -> (Tox.PacketKind -> Maybe Handler)
767 -> ToxClient
768 mkclient (tbl,var) handlers = Client
720 { clientNet = net 769 { clientNet = net
721 , clientDispatcher = dispatch tbl var 770 , clientDispatcher = dispatch tbl var handlers
722 , clientErrorReporter = (printErrors stderr) { reportTimeout = reportTimeout ignoreErrors } 771 , clientErrorReporter = (printErrors stderr) { reportTimeout = reportTimeout ignoreErrors }
723 , clientPending = var 772 , clientPending = var
724 , clientAddress = \maddr -> atomically $ do 773 , clientAddress = \maddr -> atomically $ do
@@ -756,15 +805,15 @@ toxSpace = R.KademliaSpace
756 805
757 806
758{- 807{-
759last8 :: ByteString -> Nonce8 808last8 :: ByteString -> Tox.Nonce8
760last8 bs 809last8 bs
761 | let len = B.length bs 810 | let len = B.length bs
762 , (len >= 8) 811 , (len >= 8)
763 = Nonce8 $ let bs' = B.drop (len - 8) bs 812 = Tox.Nonce8 $ let bs' = B.drop (len - 8) bs
764 Right w = S.runGet S.getWord64be bs' 813 Right w = S.runGet S.getWord64be bs'
765 in w 814 in w
766 | otherwise 815 | otherwise
767 = Nonce8 0 816 = Tox.Nonce8 0
768 817
769dropEnd8 :: ByteString -> ByteString 818dropEnd8 :: ByteString -> ByteString
770dropEnd8 bs = B.take (B.length bs - 8) bs 819dropEnd8 bs = B.take (B.length bs - 8) bs
@@ -772,7 +821,7 @@ dropEnd8 bs = B.take (B.length bs - 8) bs
772 821
773data Payload a = Payload 822data Payload a = Payload
774 { payload :: a 823 { payload :: a
775 , sendback :: Nonce8 824 , sendback :: Tox.Nonce8
776 } 825 }
777 826
778instance S.Serialize a => S.Serialize (Payload a) where 827instance S.Serialize a => S.Serialize (Payload a) where
@@ -826,7 +875,7 @@ classify (Msg { msgType = typ
826 _ -> const $ IsUnknown ("Unknown message type: "++show typ) 875 _ -> const $ IsUnknown ("Unknown message type: "++show typ)
827 876
828{- 877{-
829encodePayload typ (TransactionId (Nonce8 tid) nonce) self dest b 878encodePayload typ (TransactionId (Tox.Nonce8 tid) nonce) self dest b
830 = Message { msgType = typ 879 = Message { msgType = typ
831 , msgOrigin = nodeId self 880 , msgOrigin = nodeId self
832 , msgNonce = nonce 881 , msgNonce = nonce
@@ -856,12 +905,13 @@ transitionCommittee committee (RoutingTransition ni Stranger) = do
856 hPutStrLn stderr $ "delVote "++show (nodeId ni) 905 hPutStrLn stderr $ "delVote "++show (nodeId ni)
857transitionCommittee committee _ = return $ return () 906transitionCommittee committee _ = return $ return ()
858 907
859updateRouting :: ToxClient -> Routing -> NodeInfo -> (Tox.PacketKind, Tox.Assymetric) -> IO () 908updateRouting :: ToxClient -> Routing -> NodeInfo -> (Tox.PacketKind, InterediateRep) -> IO ()
860updateRouting client routing naddr (typ,msg) = do 909updateRouting client routing naddr (typ,Assym msg) = do
861 hPutStrLn stderr $ "updateRouting "++show typ 910 hPutStrLn stderr $ "updateRouting "++show typ
862 case prefer4or6 naddr Nothing of 911 case prefer4or6 naddr Nothing of
863 Want_IP4 -> updateTable client naddr (routing4 routing) (committee4 routing) (sched4 routing) 912 Want_IP4 -> updateTable client naddr (routing4 routing) (committee4 routing) (sched4 routing)
864 Want_IP6 -> updateTable client naddr (routing6 routing) (committee6 routing) (sched6 routing) 913 Want_IP6 -> updateTable client naddr (routing6 routing) (committee6 routing) (sched6 routing)
914updateRouting _ _ _ _ = return ()
865 915
866updateTable client naddr tbl committee sched = do 916updateTable client naddr tbl committee sched = do
867 self <- atomically $ R.thisNode <$> readTVar tbl 917 self <- atomically $ R.thisNode <$> readTVar tbl
@@ -1034,12 +1084,47 @@ putCiphered (Ciphered (Poly1305.Auth mac) bs) = do
1034 S.putByteString (BA.convert mac) 1084 S.putByteString (BA.convert mac)
1035 S.putByteString bs 1085 S.putByteString bs
1036 1086
1037data Announce = Announce 1087newtype Nonce32 = Nonce32 ByteString
1038 { announcePingId :: NodeId -- Ping ID 1088 deriving (Eq, Ord, ByteArrayAccess, Data)
1039 , announceSeeking :: NodeId -- Public key we are searching for 1089
1040 , announceKey :: NodeId -- Public key that we want those sending back data packets to use 1090instance S.Serialize Nonce32 where
1091 get = Nonce32 <$> S.getBytes 32
1092 put (Nonce32 bs) = S.putByteString bs
1093
1094data AnnounceRequest = AnnounceRequest
1095 { announcePingId :: Nonce32 -- Ping ID
1096 , announceSeeking :: NodeId -- Public key we are searching for
1097 , announceKey :: NodeId -- Public key that we want those sending back data packets to use
1098 }
1099
1100instance S.Serialize AnnounceRequest where
1101 get = AnnounceRequest <$> S.get <*> S.get <*> S.get
1102 put (AnnounceRequest p s k) = S.put (p,s,k)
1103
1104data KeyRecord = NotStored Nonce32
1105 | SendBackKey Tox.PubKey
1106 | Acknowledged Nonce32
1107
1108instance S.Serialize KeyRecord where
1109 get = do
1110 is_stored <- S.get :: S.Get Word8
1111 case is_stored of
1112 1 -> SendBackKey <$> S.get
1113 2 -> Acknowledged <$> S.get
1114 _ -> NotStored <$> S.get
1115 put (NotStored n32) = S.put (0 :: Word8) >> S.put n32
1116 put (SendBackKey key) = S.put (1 :: Word8) >> S.put key
1117 put (Acknowledged n32) = S.put (2 :: Word8) >> S.put n32
1118
1119data AnnounceResponse = AnnounceResponse
1120 { is_stored :: KeyRecord
1121 , announceNodes :: SendNodes
1041 } 1122 }
1042 1123
1124instance S.Serialize AnnounceResponse where
1125 get = AnnounceResponse <$> S.get <*> S.get
1126 put (AnnounceResponse st ns) = S.put st >> S.put ns
1127
1043pingH :: NodeInfo -> Ping -> IO Pong 1128pingH :: NodeInfo -> Ping -> IO Pong
1044pingH _ Ping = return Pong 1129pingH _ Ping = return Pong
1045 1130
@@ -1075,6 +1160,50 @@ getNodesH routing addr (GetNodes nid) = do
1075 1160
1076 k = 4 1161 k = 4
1077 1162
1163-- Toxcore generates `ping_id`s by taking a 32 byte sha hash of the current time,
1164-- some secret bytes generated when the instance is created, the current time
1165-- divided by a 20 second timeout, the public key of the requester and the source
1166-- ip/port that the packet was received from. Since the ip/port that the packet
1167-- was received from is in the `ping_id`, the announce packets being sent with a
1168-- ping id must be sent using the same path as the packet that we received the
1169-- `ping_id` from or announcing will fail.
1170--
1171-- The reason for this 20 second timeout in toxcore is that it gives a reasonable
1172-- time (20 to 40 seconds) for a peer to announce himself while taking in count
1173-- all the possible delays with some extra seconds.
1174announceH :: Routing -> TVar SessionTokens -> TVar AnnouncedKeys -> NodeInfo -> AnnounceRequest -> IO AnnounceResponse
1175announceH routing toks keydb naddr req = do
1176 case () of
1177 _ | announcePingId req == zeros32
1178 -> go False
1179
1180 _ | Nonce32 bs <- announcePingId req
1181 , let tok = fromPaddedByteString 32 bs
1182 -> checkToken toks naddr tok >>= go
1183 where
1184 go withTok = do
1185 ns <- getNodesH routing naddr (GetNodes (announceSeeking req))
1186 tm <- getPOSIXTime
1187 let storing = (nodeId naddr == announceSeeking req)
1188 record <- atomically $ do
1189 when (withTok && storing) $ do
1190 let ni = Tox.Aliased (naddr { nodeId = announceKey req })
1191 -- Note: The following distance calculation assumes that
1192 -- our nodeid doesn't change and is the same for both
1193 -- routing4 and routing6.
1194 d = xor (nodeId (tentativeId routing))
1195 (announceSeeking req)
1196 modifyTVar' keydb (insertKey tm (announceSeeking req) ni d)
1197 ks <- readTVar keydb
1198 return $ snd <$> MinMaxPSQ.lookup' (announceSeeking req) (keyAssoc ks)
1199 newtok <- if storing
1200 then Nonce32 . toPaddedByteString 32 <$> grantToken toks naddr
1201 else return $ zeros32
1202 let k = case record of
1203 Nothing -> NotStored newtok
1204 Just (Tox.Aliased ni) | storing -> Acknowledged newtok
1205 Just (Tox.Aliased ni) -> SendBackKey (nodeId ni)
1206 return $ AnnounceResponse k ns
1078 1207
1079{- 1208{-
1080symmetricCipher :: DRG g => STM ByteString -> STM g -> (g -> STM ()) -> ByteString -> IO (Tox.Nonce24, SymmetricCiphered) 1209symmetricCipher :: DRG g => STM ByteString -> STM g -> (g -> STM ()) -> ByteString -> IO (Tox.Nonce24, SymmetricCiphered)
@@ -1157,9 +1286,9 @@ onionResponse1H symdecipher udp addr Message{ msgNonce
1157-} 1286-}
1158 1287
1159intKey :: TransactionId -> Int 1288intKey :: TransactionId -> Int
1160intKey (TransactionId (Nonce8 w) _) = fromIntegral w 1289intKey (TransactionId (Tox.Nonce8 w) _) = fromIntegral w
1161 1290
1162nonceKey :: TransactionId -> Nonce8 1291nonceKey :: TransactionId -> Tox.Nonce8
1163nonceKey (TransactionId n _) = n 1292nonceKey (TransactionId n _) = n
1164 1293
1165-- randomBytesGenerate :: ByteArray byteArray => Int -> gen -> (byteArray, gen) 1294-- randomBytesGenerate :: ByteArray byteArray => Int -> gen -> (byteArray, gen)
@@ -1168,7 +1297,7 @@ gen :: forall gen. DRG gen => gen -> (TransactionId, gen)
1168gen g = let (bs, g') = randomBytesGenerate 24 g 1297gen g = let (bs, g') = randomBytesGenerate 24 g
1169 (ws, g'') = randomBytesGenerate 8 g' 1298 (ws, g'') = randomBytesGenerate 8 g'
1170 Right w = S.runGet S.getWord64be ws 1299 Right w = S.runGet S.getWord64be ws
1171 in ( TransactionId (Nonce8 w) (Tox.Nonce24 bs), g'' ) 1300 in ( TransactionId (Tox.Nonce8 w) (Tox.Nonce24 bs), g'' )
1172 1301
1173 1302
1174 1303
@@ -1206,3 +1335,16 @@ toxSearch qry = Search
1206 1335
1207nodeSearch client = toxSearch (getNodes client) 1336nodeSearch client = toxSearch (getNodes client)
1208 1337
1338
1339type NodeDistance = Tox.PubKey
1340
1341data AnnouncedKeys = AnnouncedKeys
1342 { keyByAge :: PSQ NodeId (Down POSIXTime) -- timeout of 300 seconds
1343 , keyAssoc :: MinMaxPSQ' Tox.PubKey NodeDistance (Tox.Aliased NodeInfo)
1344 }
1345
1346insertKey :: POSIXTime -> Tox.PubKey -> Tox.Aliased NodeInfo -> NodeDistance -> AnnouncedKeys -> AnnouncedKeys
1347insertKey tm pub ni d keydb = AnnouncedKeys
1348 { keyByAge = PSQ.insert pub (Down tm) (keyByAge keydb)
1349 , keyAssoc = MinMaxPSQ.insert' pub ni d (keyAssoc keydb)
1350 }
diff --git a/src/Network/BitTorrent/DHT/Token.hs b/src/Network/BitTorrent/DHT/Token.hs
index 08079b75..756b5a98 100644
--- a/src/Network/BitTorrent/DHT/Token.hs
+++ b/src/Network/BitTorrent/DHT/Token.hs
@@ -21,6 +21,9 @@
21module Network.BitTorrent.DHT.Token 21module Network.BitTorrent.DHT.Token
22 ( -- * Token 22 ( -- * Token
23 Token 23 Token
24 , maxInterval
25 , toPaddedByteString
26 , fromPaddedByteString
24 27
25 -- * Session tokens 28 -- * Session tokens
26 , TokenMap 29 , TokenMap
@@ -74,7 +77,19 @@ instance Show Token where
74 77
75-- | Meaningless token, for testing purposes only. 78-- | Meaningless token, for testing purposes only.
76instance Default Token where 79instance Default Token where
77 def = Token "0xdeadbeef" 80 def = makeToken (0::Int) 0
81
82-- | Prepend token with 0x20 bytes to fill the available width.
83--
84-- If n > 8, then this will also guarantee a nonzero token, which is useful for
85-- Tox ping-id values for announce responses.
86toPaddedByteString :: Int -> Token -> BS.ByteString
87toPaddedByteString n (Token bs) = BS.append (BS.replicate (n - BS.length bs) 0x20) bs
88
89fromPaddedByteString :: Int -> BS.ByteString -> Token
90fromPaddedByteString n bs = Token $ BS.drop (n - len) bs
91 where
92 len = BS.length tok where Token tok = def
78 93
79-- | The secret value used as salt. 94-- | The secret value used as salt.
80type Secret = Int 95type Secret = Int
@@ -85,6 +100,7 @@ makeToken :: Hashable a => a -> Secret -> Token
85makeToken n s = Token $ toBS $ hashWithSalt s n 100makeToken n s = Token $ toBS $ hashWithSalt s n
86 where 101 where
87 toBS = toStrict . toLazyByteString . int64BE . fromIntegral 102 toBS = toStrict . toLazyByteString . int64BE . fromIntegral
103{-# INLINE makeToken #-}
88 104
89-- | Constant space 'Node' to 'Token' map based on the secret value. 105-- | Constant space 'Node' to 'Token' map based on the secret value.
90data TokenMap = TokenMap 106data TokenMap = TokenMap