summaryrefslogtreecommitdiff
path: root/src/Network/DatagramServer
diff options
context:
space:
mode:
authorjoe <joe@jerkface.net>2017-07-08 22:10:40 -0400
committerjoe <joe@jerkface.net>2017-07-08 22:10:40 -0400
commit8fe93b8e1d1d968bdf0b8a35335b060d92a9d7d7 (patch)
tree6e9a4b35f11de5ad0e4f422e0a6d268b5270befd /src/Network/DatagramServer
parentf75d515bc0100e5ca372d592aa2f5f4ff2fc858c (diff)
WIP: Tox encryption.
Diffstat (limited to 'src/Network/DatagramServer')
-rw-r--r--src/Network/DatagramServer/Mainline.hs23
-rw-r--r--src/Network/DatagramServer/Tox.hs118
-rw-r--r--src/Network/DatagramServer/Types.hs33
3 files changed, 161 insertions, 13 deletions
diff --git a/src/Network/DatagramServer/Mainline.hs b/src/Network/DatagramServer/Mainline.hs
index 89a275c1..1f07b13f 100644
--- a/src/Network/DatagramServer/Mainline.hs
+++ b/src/Network/DatagramServer/Mainline.hs
@@ -79,6 +79,7 @@ import Data.Typeable
79import Network.Socket (SockAddr (..),PortNumber,HostAddress) 79import Network.Socket (SockAddr (..),PortNumber,HostAddress)
80import Text.PrettyPrint as PP hiding ((<>)) 80import Text.PrettyPrint as PP hiding ((<>))
81import Text.PrettyPrint.HughesPJClass hiding (($$), (<>)) 81import Text.PrettyPrint.HughesPJClass hiding (($$), (<>))
82import Data.Hashable
82 83
83 84
84-- | This transaction ID is generated by the querying node and is 85-- | This transaction ID is generated by the querying node and is
@@ -290,6 +291,9 @@ instance Envelope KMessageOf where
290 } 291 }
291 deriving (Show, Eq, Ord, Typeable) 292 deriving (Show, Eq, Ord, Typeable)
292 293
294 newtype PacketDestination KMessageOf = MainlineNode SockAddr
295 deriving (Show, Eq, Ord, Typeable)
296
293 envelopePayload (Q q) = queryArgs q 297 envelopePayload (Q q) = queryArgs q
294 envelopePayload (R r) = respVals r 298 envelopePayload (R r) = respVals r
295 envelopePayload (E _) = error "TODO: messagePayload for KError" 299 envelopePayload (E _) = error "TODO: messagePayload for KError"
@@ -302,6 +306,9 @@ instance Envelope KMessageOf where
302 envelopeClass (R r) = Response (respIP r) 306 envelopeClass (R r) = Response (respIP r)
303 envelopeClass (E e) = Error e 307 envelopeClass (E e) = Error e
304 308
309 -- replyAddress :: envelope a -> SockAddr -> PacketDestination envelope
310 makeAddress _ addr = MainlineNode addr
311
305 buildReply self addr qry response = 312 buildReply self addr qry response =
306 (R (KResponse response (envelopeTransaction qry) (Just $ ReflectedIP addr))) 313 (R (KResponse response (envelopeTransaction qry) (Just $ ReflectedIP addr)))
307 314
@@ -311,6 +318,20 @@ instance Envelope KMessageOf where
311 318
312 fromRoutableNode = not . queryIsReadOnly 319 fromRoutableNode = not . queryIsReadOnly
313 320
321instance Hashable (PacketDestination KMessageOf) where
322 hashWithSalt s (MainlineNode sockaddr) = hashWithSalt s (show sockaddr)
323
324-- Serialize, Pretty) PacketDestination KMessageOf = MainlineNode SockAddr
325instance Serialize (PacketDestination KMessageOf) where
326 put (MainlineNode addr) = putSockAddr addr
327 get = MainlineNode <$> getSockAddr
328
329instance Pretty (PacketDestination KMessageOf) where
330 pPrint (MainlineNode addr) = PP.text $ show addr
331
332instance Address (PacketDestination KMessageOf) where
333 toSockAddr (MainlineNode addr) = addr
334 fromSockAddr addr = Just $ MainlineNode addr
314 335
315instance WireFormat BValue KMessageOf where 336instance WireFormat BValue KMessageOf where
316 type SerializableTo BValue = BEncode 337 type SerializableTo BValue = BEncode
@@ -323,7 +344,7 @@ instance WireFormat BValue KMessageOf where
323 decodeHeaders _ = BE.fromBEncode 344 decodeHeaders _ = BE.fromBEncode
324 decodePayload kmsg = mapM BE.fromBEncode kmsg 345 decodePayload kmsg = mapM BE.fromBEncode kmsg
325 346
326 encodeHeaders _ kmsg = L.toStrict $ BE.encode kmsg 347 encodeHeaders _ kmsg _ = L.toStrict $ BE.encode kmsg
327 encodePayload msg = fmap BE.toBEncode msg 348 encodePayload msg = fmap BE.toBEncode msg
328 349
329-- | KRPC 'compact list' compatible encoding: contact information for 350-- | KRPC 'compact list' compatible encoding: contact information for
diff --git a/src/Network/DatagramServer/Tox.hs b/src/Network/DatagramServer/Tox.hs
index f666b951..8d2f9289 100644
--- a/src/Network/DatagramServer/Tox.hs
+++ b/src/Network/DatagramServer/Tox.hs
@@ -11,10 +11,13 @@
11{-# LANGUAGE TupleSections #-} 11{-# LANGUAGE TupleSections #-}
12{-# LANGUAGE TypeFamilies #-} 12{-# LANGUAGE TypeFamilies #-}
13{-# LANGUAGE UnboxedTuples #-} 13{-# LANGUAGE UnboxedTuples #-}
14{-# LANGUAGE TemplateHaskell #-}
15{-# LANGUAGE RankNTypes #-}
14module Network.DatagramServer.Tox where 16module Network.DatagramServer.Tox where
15 17
16import Data.Bits 18import Data.Bits
17import Data.ByteString (ByteString) 19import Data.ByteString (ByteString)
20import Data.ByteArray as BA (ByteArrayAccess,length,withByteArray)
18import qualified Data.Serialize as S 21import qualified Data.Serialize as S
19-- import qualified Data.ByteString.Lazy as L 22-- import qualified Data.ByteString.Lazy as L
20import qualified Data.ByteString.Char8 as Char8 23import qualified Data.ByteString.Char8 as Char8
@@ -23,12 +26,25 @@ import Data.Word
23import Data.LargeWord 26import Data.LargeWord
24import Data.IP 27import Data.IP
25import Data.Serialize 28import Data.Serialize
26-- import Network.Address (NodeInfo(..)) -- Serialize IP 29import Network.Address
27import GHC.Generics (Generic) 30import GHC.Generics (Generic)
28import Network.Socket 31import Network.Socket
29import Network.DatagramServer.Types 32import Network.DatagramServer.Types
30import qualified Network.DatagramServer.Types as Envelope (NodeId) 33import qualified Network.DatagramServer.Types as Envelope (NodeId)
31import Crypto.PubKey.ECC.Types 34import Crypto.PubKey.ECC.Types
35import Crypto.PubKey.Curve25519
36import Crypto.ECC.Class
37import qualified Crypto.Cipher.XSalsa as Salsa20
38import Data.LargeWord
39import Foreign.Ptr
40import Foreign.Storable
41import Foreign.Marshal.Alloc
42import Data.Typeable
43import StaticAssert
44import Crypto.Error.Types
45import Data.Hashable
46import Text.PrettyPrint as PP hiding ((<>))
47import Text.PrettyPrint.HughesPJClass hiding (($$), (<>))
32 48
33 49
34type Key32 = Word256 -- 32 byte key 50type Key32 = Word256 -- 32 byte key
@@ -203,7 +219,9 @@ instance Serialize NodeFormat where
203-- [Sendback data, length=8 bytes] 219-- [Sendback data, length=8 bytes]
204-- ] 220-- ]
205 221
206data ToxCipherContext = ToxCipherContext -- TODO 222data ToxCipherContext = ToxCipherContext
223 { dhtSecretKey :: SecretKey
224 }
207 225
208newtype Ciphered = Ciphered { cipheredBytes :: ByteString } 226newtype Ciphered = Ciphered { cipheredBytes :: ByteString }
209 227
@@ -227,29 +245,51 @@ putMessage (Message {..}) = do
227 let Ciphered bs = msgPayload 245 let Ciphered bs = msgPayload
228 putByteString bs 246 putByteString bs
229 247
248id2key :: NodeId Message -> PublicKey
249id2key recipient = case publicKey recipient of
250 CryptoPassed key -> key
251 CryptoFailed e -> error ("id2key: "++show e)
252
253lookupSecret :: ToxCipherContext -> NodeId Message -> TransactionID Message -> Salsa20.State
254lookupSecret ctx recipient nonce = Salsa20.initialize 20 key nonce
255 where
256 key = ecdh (Proxy :: Proxy Curve_X25519) (dhtSecretKey ctx) (id2key recipient) -- ByteArrayAccess b => b
257
230decipher :: ToxCipherContext -> Message Ciphered -> Either String (Message ByteString) 258decipher :: ToxCipherContext -> Message Ciphered -> Either String (Message ByteString)
231decipher = error "TODO TOX: decipher" 259decipher ctx ciphered = Right (fst . Salsa20.combine st . cipheredBytes <$> ciphered)
260 where
261 st = lookupSecret ctx (msgClient ciphered) (msgNonce ciphered)
232 262
233encipher :: ToxCipherContext -> Message ByteString -> Message Ciphered 263encipher :: ToxCipherContext -> NodeId Message -> Message ByteString -> Message Ciphered
234encipher = error "TODO TOX: encipher" 264encipher ctx recipient plain = Ciphered . fst . Salsa20.combine st <$> plain
265 where
266 st = lookupSecret ctx recipient (msgNonce plain)
235 267
236-- see rfc7748 268-- see rfc7748
269--
270-- Crypto.ECC
271-- Crypto.PubKey.Curve25519
272-- Crypto.Cipher.XSalsa
273--
237curve25519 :: Curve 274curve25519 :: Curve
238curve25519 = CurveFP (CurvePrime prime curvecommon) 275curve25519 = CurveFP (CurvePrime prime curvecommon)
239 where 276 where
240 prime = 2^255 - 19 -- (≅ 1 modulo 4) 277 prime = 2^255 - 19 -- (≅ 1 modulo 4)
241 278
279 sqrt_of_39420360 = 14781619447589544791020593568409986887264606134616475288964881837755586237401
280
242 -- 1 * v^2 = u^3 + 486662*u^2 + u 281 -- 1 * v^2 = u^3 + 486662*u^2 + u
243 282
244 curvecommon = CurveCommon 283 curvecommon = CurveCommon
245 { ecc_a = 486662 284 { ecc_a = 486662
246 , ecc_b = 1 285 , ecc_b = 1
247 , ecc_g = Point 9 14781619447589544791020593568409986887264606134616475288964881837755586237401 -- base point 286 , ecc_g = Point 9 sqrt_of_39420360 -- base point
248 , ecc_n = 2^252 + 0x14def9dea2f79cd65812631a5cf5d3ed -- order 287 , ecc_n = 2^252 + 0x14def9dea2f79cd65812631a5cf5d3ed -- order
249 , ecc_h = 8 -- cofactor 288 , ecc_h = 8 -- cofactor
250 } 289 }
251 290
252 291-- crypto_box uses xsalsa20 symmetric encryption and poly1305 authentication.
292-- https://en.wikipedia.org/wiki/Poly1305
253 293
254instance Envelope Message where 294instance Envelope Message where
255 newtype TransactionID Message = TID Nonce24 295 newtype TransactionID Message = TID Nonce24
@@ -263,6 +303,11 @@ instance Envelope Message where
263 newtype QueryExtra Message = QueryNonce { qryNonce :: Nonce8 } 303 newtype QueryExtra Message = QueryNonce { qryNonce :: Nonce8 }
264 newtype ResponseExtra Message = ResponseNonce { rspNonce :: Nonce8 } 304 newtype ResponseExtra Message = ResponseNonce { rspNonce :: Nonce8 }
265 305
306 data PacketDestination Message = ToxAddr { toxID :: NodeId Message
307 , toxSockAddr :: SockAddr
308 }
309 deriving (Eq,Ord,Show)
310
266 envelopePayload = msgPayload 311 envelopePayload = msgPayload
267 312
268 envelopeTransaction = msgNonce 313 envelopeTransaction = msgNonce
@@ -272,15 +317,70 @@ instance Envelope Message where
272 envelopeClass Message { msgType = GetNodes } = Query GetNodes 317 envelopeClass Message { msgType = GetNodes } = Query GetNodes
273 envelopeClass Message { msgType = SendNodes } = Response Nothing 318 envelopeClass Message { msgType = SendNodes } = Response Nothing
274 319
320 makeAddress qry = ToxAddr (either id msgClient qry)
321
275 buildReply self addr qry payload = (fmap (const payload) qry) { msgClient = self } 322 buildReply self addr qry payload = (fmap (const payload) qry) { msgClient = self }
276 323
277 -- buildQuery :: NodeId envelope -> SockAddr -> QueryMethod envelope -> TransactionID envelope -> a -> IO (envelope a) 324 -- buildQuery :: NodeId envelope -> SockAddr -> QueryMethod envelope -> TransactionID envelope -> a -> IO (envelope a)
278 -- buildQuery nid addr meth tid q = todo 325 buildQuery nid addr meth tid q = return $ Message
326 { msgType = meth
327 , msgClient = nid
328 , msgNonce = tid
329 , msgPayload = q
330 }
279 331
280 uniqueTransactionId cnt = do 332 uniqueTransactionId cnt = do
281 return $ either (error "failed to create TransactionId") TID 333 return $ either (error "failed to create TransactionId") TID
282 $ S.decode $ Char8.pack (take 24 $ show cnt ++ repeat ' ') 334 $ S.decode $ Char8.pack (take 24 $ show cnt ++ repeat ' ')
283 335
336
337staticAssert isLittleEndian -- assumed by 'withWord64Ptr'
338
339with3Word64Ptr :: Nonce24 -> (Ptr Word64 -> IO a) -> IO a
340with3Word64Ptr (LargeKey wlo (LargeKey wmid whi)) kont =
341 allocaBytes (sizeOf wlo * 3) $ \p -> do
342 pokeElemOff p 0 wlo
343 pokeElemOff p 1 wmid
344 pokeElemOff p 2 whi
345 kont p
346
347with4Word64Ptr :: Key32 -> (Ptr Word64 -> IO a) -> IO a
348with4Word64Ptr (LargeKey wlo (LargeKey wmid (LargeKey whi whighest))) kont =
349 allocaBytes (sizeOf wlo * 4) $ \p -> do
350 pokeElemOff p 0 wlo
351 pokeElemOff p 1 wmid
352 pokeElemOff p 2 whi
353 pokeElemOff p 3 whighest
354 kont p
355
356
357instance ByteArrayAccess (TransactionID Message) where
358 length _ = 24
359 withByteArray (TID nonce) kont = with3Word64Ptr nonce (kont . castPtr)
360
361instance ByteArrayAccess (NodeId Message) where
362 length _ = 32
363 withByteArray (NodeId nonce) kont = with4Word64Ptr nonce (kont . castPtr)
364
365
366instance Hashable (NodeId Message) where
367 hashWithSalt s (NodeId (LargeKey a (LargeKey b (LargeKey c d)))) =
368 hashWithSalt s (a,b,c,d)
369
370instance Hashable (PacketDestination Message) where
371 hashWithSalt s (ToxAddr nid addr) = hashWithSalt s nid
372
373instance Serialize (PacketDestination Message) where
374 put (ToxAddr (NodeId nid) addr) = put nid >> putSockAddr addr
375 get = ToxAddr <$> (NodeId <$> get) <*> getSockAddr
376
377instance Pretty (PacketDestination Message) where
378 pPrint = PP.text . show
379
380instance Address (PacketDestination Message) where
381 toSockAddr (ToxAddr _ addr) = addr
382 fromSockAddr _ = Nothing
383
284instance WireFormat ByteString Message where 384instance WireFormat ByteString Message where
285 type SerializableTo ByteString = Serialize 385 type SerializableTo ByteString = Serialize
286 type CipherContext ByteString Message = ToxCipherContext 386 type CipherContext ByteString Message = ToxCipherContext
@@ -289,6 +389,6 @@ instance WireFormat ByteString Message where
289 encodePayload = fmap encode 389 encodePayload = fmap encode
290 390
291 decodeHeaders ctx bs = runGet getMessage bs >>= decipher ctx 391 decodeHeaders ctx bs = runGet getMessage bs >>= decipher ctx
292 encodeHeaders ctx msg = runPut $ putMessage $ encipher ctx msg 392 encodeHeaders ctx msg recipient = runPut $ putMessage $ encipher ctx (toxID recipient) msg
293 393
294instance Read (NodeId Message) where readsPrec d s = map (\(w,xs) -> (NodeId w, xs)) $ decodeHex s 394instance Read (NodeId Message) where readsPrec d s = map (\(w,xs) -> (NodeId w, xs)) $ decodeHex s
diff --git a/src/Network/DatagramServer/Types.hs b/src/Network/DatagramServer/Types.hs
index 13f79afb..14968764 100644
--- a/src/Network/DatagramServer/Types.hs
+++ b/src/Network/DatagramServer/Types.hs
@@ -96,11 +96,21 @@ class Envelope envelope where
96 data NodeId envelope 96 data NodeId envelope
97 data QueryExtra envelope 97 data QueryExtra envelope
98 data ResponseExtra envelope 98 data ResponseExtra envelope
99 data PacketDestination envelope
99 100
100 envelopePayload :: envelope a -> a 101 envelopePayload :: envelope a -> a
101 envelopeTransaction :: envelope a -> TransactionID envelope 102 envelopeTransaction :: envelope a -> TransactionID envelope
102 envelopeClass :: envelope a -> MessageClass envelope 103 envelopeClass :: envelope a -> MessageClass envelope
103 104
105 -- | > replyAddress qry addr
106 --
107 -- [ qry ] received query message
108 --
109 -- [ addr ] SockAddr of query origin
110 --
111 -- Returns: Destination address for reply.
112 makeAddress :: Either (NodeId envelope) (envelope a) -> SockAddr -> PacketDestination envelope
113
104 -- | > buildReply self addr qry response 114 -- | > buildReply self addr qry response
105 -- 115 --
106 -- [ self ] this node's id. 116 -- [ self ] this node's id.
@@ -320,8 +330,7 @@ genBucketSample' gen self (q,m,b)
320 h = xor b (complement m .&. BS.last hd) 330 h = xor b (complement m .&. BS.last hd)
321 t = m .&. BS.head tl 331 t = m .&. BS.head tl
322 332
323 333class (Envelope envelope, Address (PacketDestination envelope)) => WireFormat raw envelope where
324class Envelope envelope => WireFormat raw envelope where
325 type SerializableTo raw :: * -> Constraint 334 type SerializableTo raw :: * -> Constraint
326 type CipherContext raw envelope 335 type CipherContext raw envelope
327 336
@@ -336,7 +345,7 @@ class Envelope envelope => WireFormat raw envelope where
336 decodeHeaders :: CipherContext raw envelope -> raw -> Either String (envelope raw) 345 decodeHeaders :: CipherContext raw envelope -> raw -> Either String (envelope raw)
337 decodePayload :: SerializableTo raw a => envelope raw -> Either String (envelope a) 346 decodePayload :: SerializableTo raw a => envelope raw -> Either String (envelope a)
338 347
339 encodeHeaders :: CipherContext raw envelope -> envelope raw -> ByteString 348 encodeHeaders :: CipherContext raw envelope -> envelope raw -> PacketDestination envelope -> ByteString
340 encodePayload :: SerializableTo raw a => envelope a -> envelope raw 349 encodePayload :: SerializableTo raw a => envelope a -> envelope raw
341 350
342encodeHexDoc :: Serialize x => x -> Doc 351encodeHexDoc :: Serialize x => x -> Doc
@@ -359,3 +368,21 @@ instance (Pretty ip, Pretty (NodeId dht)) => Pretty [NodeInfo dht ip u] where
359 pPrint = PP.vcat . PP.punctuate "," . map pPrint 368 pPrint = PP.vcat . PP.punctuate "," . map pPrint
360 369
361 370
371
372putSockAddr (SockAddrInet port addr)
373 = put (0x34 :: Word8) >> put port >> put addr
374putSockAddr (SockAddrInet6 port flow addr scope)
375 = put (0x36 :: Word8) >> put port >> put addr >> put scope >> put flow
376putSockAddr (SockAddrUnix path)
377 = put (0x75 :: Word8) >> put path
378putSockAddr (SockAddrCan num)
379 = put (0x63 :: Word8) >> put num
380
381getSockAddr = do
382 c <- get
383 case c :: Word8 of
384 0x34 -> SockAddrInet <$> get <*> get
385 0x36 -> (\p a s f -> SockAddrInet6 p f a s) <$> get <*> get <*> get <*> get
386 0x75 -> SockAddrUnix <$> get
387 0x63 -> SockAddrCan <$> get
388 _ -> fail "getSockAddr"