diff options
author | joe <joe@jerkface.net> | 2017-07-08 22:10:40 -0400 |
---|---|---|
committer | joe <joe@jerkface.net> | 2017-07-08 22:10:40 -0400 |
commit | 8fe93b8e1d1d968bdf0b8a35335b060d92a9d7d7 (patch) | |
tree | 6e9a4b35f11de5ad0e4f422e0a6d268b5270befd /src/Network/DatagramServer | |
parent | f75d515bc0100e5ca372d592aa2f5f4ff2fc858c (diff) |
WIP: Tox encryption.
Diffstat (limited to 'src/Network/DatagramServer')
-rw-r--r-- | src/Network/DatagramServer/Mainline.hs | 23 | ||||
-rw-r--r-- | src/Network/DatagramServer/Tox.hs | 118 | ||||
-rw-r--r-- | src/Network/DatagramServer/Types.hs | 33 |
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 | |||
79 | import Network.Socket (SockAddr (..),PortNumber,HostAddress) | 79 | import Network.Socket (SockAddr (..),PortNumber,HostAddress) |
80 | import Text.PrettyPrint as PP hiding ((<>)) | 80 | import Text.PrettyPrint as PP hiding ((<>)) |
81 | import Text.PrettyPrint.HughesPJClass hiding (($$), (<>)) | 81 | import Text.PrettyPrint.HughesPJClass hiding (($$), (<>)) |
82 | import 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 | ||
321 | instance Hashable (PacketDestination KMessageOf) where | ||
322 | hashWithSalt s (MainlineNode sockaddr) = hashWithSalt s (show sockaddr) | ||
323 | |||
324 | -- Serialize, Pretty) PacketDestination KMessageOf = MainlineNode SockAddr | ||
325 | instance Serialize (PacketDestination KMessageOf) where | ||
326 | put (MainlineNode addr) = putSockAddr addr | ||
327 | get = MainlineNode <$> getSockAddr | ||
328 | |||
329 | instance Pretty (PacketDestination KMessageOf) where | ||
330 | pPrint (MainlineNode addr) = PP.text $ show addr | ||
331 | |||
332 | instance Address (PacketDestination KMessageOf) where | ||
333 | toSockAddr (MainlineNode addr) = addr | ||
334 | fromSockAddr addr = Just $ MainlineNode addr | ||
314 | 335 | ||
315 | instance WireFormat BValue KMessageOf where | 336 | instance 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 #-} | ||
14 | module Network.DatagramServer.Tox where | 16 | module Network.DatagramServer.Tox where |
15 | 17 | ||
16 | import Data.Bits | 18 | import Data.Bits |
17 | import Data.ByteString (ByteString) | 19 | import Data.ByteString (ByteString) |
20 | import Data.ByteArray as BA (ByteArrayAccess,length,withByteArray) | ||
18 | import qualified Data.Serialize as S | 21 | import qualified Data.Serialize as S |
19 | -- import qualified Data.ByteString.Lazy as L | 22 | -- import qualified Data.ByteString.Lazy as L |
20 | import qualified Data.ByteString.Char8 as Char8 | 23 | import qualified Data.ByteString.Char8 as Char8 |
@@ -23,12 +26,25 @@ import Data.Word | |||
23 | import Data.LargeWord | 26 | import Data.LargeWord |
24 | import Data.IP | 27 | import Data.IP |
25 | import Data.Serialize | 28 | import Data.Serialize |
26 | -- import Network.Address (NodeInfo(..)) -- Serialize IP | 29 | import Network.Address |
27 | import GHC.Generics (Generic) | 30 | import GHC.Generics (Generic) |
28 | import Network.Socket | 31 | import Network.Socket |
29 | import Network.DatagramServer.Types | 32 | import Network.DatagramServer.Types |
30 | import qualified Network.DatagramServer.Types as Envelope (NodeId) | 33 | import qualified Network.DatagramServer.Types as Envelope (NodeId) |
31 | import Crypto.PubKey.ECC.Types | 34 | import Crypto.PubKey.ECC.Types |
35 | import Crypto.PubKey.Curve25519 | ||
36 | import Crypto.ECC.Class | ||
37 | import qualified Crypto.Cipher.XSalsa as Salsa20 | ||
38 | import Data.LargeWord | ||
39 | import Foreign.Ptr | ||
40 | import Foreign.Storable | ||
41 | import Foreign.Marshal.Alloc | ||
42 | import Data.Typeable | ||
43 | import StaticAssert | ||
44 | import Crypto.Error.Types | ||
45 | import Data.Hashable | ||
46 | import Text.PrettyPrint as PP hiding ((<>)) | ||
47 | import Text.PrettyPrint.HughesPJClass hiding (($$), (<>)) | ||
32 | 48 | ||
33 | 49 | ||
34 | type Key32 = Word256 -- 32 byte key | 50 | type 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 | ||
206 | data ToxCipherContext = ToxCipherContext -- TODO | 222 | data ToxCipherContext = ToxCipherContext |
223 | { dhtSecretKey :: SecretKey | ||
224 | } | ||
207 | 225 | ||
208 | newtype Ciphered = Ciphered { cipheredBytes :: ByteString } | 226 | newtype 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 | ||
248 | id2key :: NodeId Message -> PublicKey | ||
249 | id2key recipient = case publicKey recipient of | ||
250 | CryptoPassed key -> key | ||
251 | CryptoFailed e -> error ("id2key: "++show e) | ||
252 | |||
253 | lookupSecret :: ToxCipherContext -> NodeId Message -> TransactionID Message -> Salsa20.State | ||
254 | lookupSecret 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 | |||
230 | decipher :: ToxCipherContext -> Message Ciphered -> Either String (Message ByteString) | 258 | decipher :: ToxCipherContext -> Message Ciphered -> Either String (Message ByteString) |
231 | decipher = error "TODO TOX: decipher" | 259 | decipher ctx ciphered = Right (fst . Salsa20.combine st . cipheredBytes <$> ciphered) |
260 | where | ||
261 | st = lookupSecret ctx (msgClient ciphered) (msgNonce ciphered) | ||
232 | 262 | ||
233 | encipher :: ToxCipherContext -> Message ByteString -> Message Ciphered | 263 | encipher :: ToxCipherContext -> NodeId Message -> Message ByteString -> Message Ciphered |
234 | encipher = error "TODO TOX: encipher" | 264 | encipher 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 | -- | ||
237 | curve25519 :: Curve | 274 | curve25519 :: Curve |
238 | curve25519 = CurveFP (CurvePrime prime curvecommon) | 275 | curve25519 = 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 | ||
254 | instance Envelope Message where | 294 | instance 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 | |||
337 | staticAssert isLittleEndian -- assumed by 'withWord64Ptr' | ||
338 | |||
339 | with3Word64Ptr :: Nonce24 -> (Ptr Word64 -> IO a) -> IO a | ||
340 | with3Word64Ptr (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 | |||
347 | with4Word64Ptr :: Key32 -> (Ptr Word64 -> IO a) -> IO a | ||
348 | with4Word64Ptr (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 | |||
357 | instance ByteArrayAccess (TransactionID Message) where | ||
358 | length _ = 24 | ||
359 | withByteArray (TID nonce) kont = with3Word64Ptr nonce (kont . castPtr) | ||
360 | |||
361 | instance ByteArrayAccess (NodeId Message) where | ||
362 | length _ = 32 | ||
363 | withByteArray (NodeId nonce) kont = with4Word64Ptr nonce (kont . castPtr) | ||
364 | |||
365 | |||
366 | instance Hashable (NodeId Message) where | ||
367 | hashWithSalt s (NodeId (LargeKey a (LargeKey b (LargeKey c d)))) = | ||
368 | hashWithSalt s (a,b,c,d) | ||
369 | |||
370 | instance Hashable (PacketDestination Message) where | ||
371 | hashWithSalt s (ToxAddr nid addr) = hashWithSalt s nid | ||
372 | |||
373 | instance Serialize (PacketDestination Message) where | ||
374 | put (ToxAddr (NodeId nid) addr) = put nid >> putSockAddr addr | ||
375 | get = ToxAddr <$> (NodeId <$> get) <*> getSockAddr | ||
376 | |||
377 | instance Pretty (PacketDestination Message) where | ||
378 | pPrint = PP.text . show | ||
379 | |||
380 | instance Address (PacketDestination Message) where | ||
381 | toSockAddr (ToxAddr _ addr) = addr | ||
382 | fromSockAddr _ = Nothing | ||
383 | |||
284 | instance WireFormat ByteString Message where | 384 | instance 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 | ||
294 | instance Read (NodeId Message) where readsPrec d s = map (\(w,xs) -> (NodeId w, xs)) $ decodeHex s | 394 | instance 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 | 333 | class (Envelope envelope, Address (PacketDestination envelope)) => WireFormat raw envelope where | |
324 | class 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 | ||
342 | encodeHexDoc :: Serialize x => x -> Doc | 351 | encodeHexDoc :: 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 | |||
372 | putSockAddr (SockAddrInet port addr) | ||
373 | = put (0x34 :: Word8) >> put port >> put addr | ||
374 | putSockAddr (SockAddrInet6 port flow addr scope) | ||
375 | = put (0x36 :: Word8) >> put port >> put addr >> put scope >> put flow | ||
376 | putSockAddr (SockAddrUnix path) | ||
377 | = put (0x75 :: Word8) >> put path | ||
378 | putSockAddr (SockAddrCan num) | ||
379 | = put (0x63 :: Word8) >> put num | ||
380 | |||
381 | getSockAddr = 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" | ||