summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ToxMessage.hs289
1 files changed, 289 insertions, 0 deletions
diff --git a/ToxMessage.hs b/ToxMessage.hs
new file mode 100644
index 00000000..6853a4a1
--- /dev/null
+++ b/ToxMessage.hs
@@ -0,0 +1,289 @@
1{-# LANGUAGE GeneralizedNewtypeDeriving #-}
2{-# LANGUAGE DeriveFunctor, DeriveTraversable,DeriveDataTypeable #-}
3{-# LANGUAGE GADTs #-}
4module ToxMessage where
5
6import Data.ByteString (ByteString)
7import qualified Crypto.MAC.Poly1305 as Poly1305 (Auth(..))
8import qualified Crypto.PubKey.Curve25519 as Curve25519
9import Data.ByteArray as BA (ByteArrayAccess, Bytes)
10import qualified Data.ByteArray as BA
11import qualified Data.ByteString as B
12import qualified Data.ByteString.Char8 as C8
13import qualified Data.ByteString.Base16 as Base16
14import Data.Bits
15import Data.Hashable
16import Data.Bits.ByteString ()
17import Data.Word
18import Data.Data
19import Data.Ord
20import Data.Serialize
21
22newtype Auth = Auth Poly1305.Auth
23 deriving (Eq, ByteArrayAccess)
24
25instance Ord Auth where
26 compare (Auth a) (Auth b) = comparing (BA.convert :: Poly1305.Auth -> Bytes) a b
27
28instance Data Auth where
29 gfoldl k z x = z x
30
31 -- Well, this is a little wonky... XXX
32 gunfold k z c = k (z (Auth . Poly1305.Auth . (BA.convert :: ByteString -> Bytes)))
33
34 toConstr _ = con_Auth
35
36 dataTypeOf _ = mkDataType "ToxMessage" [con_Auth]
37
38con_Auth = mkConstr (dataTypeOf (Auth (error "con_Auth"))) "Auth" [] Prefix
39
40instance Serialize Auth where
41 get = Auth . Poly1305.Auth . BA.convert <$> getBytes 16
42 put (Auth (Poly1305.Auth bs)) = putByteString $ BA.convert bs
43--
44-- | An 'Aliased' 'PubKey' is one that is not the DHT key and so should not go
45-- into the kademlia routing table buckets.
46--
47-- Note: This includes the long-term tox-id key that humans use to friend each
48-- other and is often refered to as your "real public key" by the Tox
49-- documents. For the purposes of the DHT, it is an alias.
50newtype Aliased a = Aliased a
51 deriving (Eq,Ord,Show,Data,Functor,Foldable,Traversable,Serialize)
52
53newtype Nonce24 = Nonce24 ByteString
54 deriving (Eq, Ord, ByteArrayAccess,Data)
55
56quoted :: ShowS -> ShowS
57quoted shows s = '"':shows ('"':s)
58
59bin2hex :: ByteArrayAccess bs => bs -> String
60bin2hex = C8.unpack . Base16.encode . BA.convert
61
62instance Show Nonce24 where
63 showsPrec d nonce = quoted (mappend $ bin2hex nonce)
64
65instance Serialize Nonce24 where
66 get = Nonce24 <$> getBytes 24
67 put (Nonce24 bs) = putByteString bs
68
69newtype Nonce8 = Nonce8 Word64
70 deriving (Eq, Ord,Data)
71
72-- TODO: This should probably be represented by Curve25519.PublicKey, but
73-- ByteString has more instances...
74newtype PubKey = PubKey ByteString
75 deriving (Eq,Ord,Data, ByteArrayAccess, Bits, Hashable)
76
77instance Serialize PubKey where
78 get = PubKey <$> getBytes 32
79 put (PubKey bs) = putByteString bs
80
81instance Show PubKey where
82 show (PubKey bs) = C8.unpack $ Base16.encode bs
83
84instance FiniteBits PubKey where
85 finiteBitSize _ = 256
86
87instance Read PubKey where
88 readsPrec _ str
89 | (bs, xs) <- Base16.decode $ C8.pack str
90 , B.length bs == 32
91 = [ (PubKey bs, drop 64 str) ]
92 | otherwise = []
93
94
95
96-- | A chunk of data encrypted with public-key cryptography.
97data ImplicitAssymetric = ImplicitAssymetric
98 { assymetricAuth :: Auth
99 , assymetricBytes :: ByteString
100 }
101 deriving (Eq, Ord,Data)
102
103getRemaining = remaining >>= getBytes
104
105instance Serialize ImplicitAssymetric where
106 get = ImplicitAssymetric <$> get <*> getRemaining
107 put (ImplicitAssymetric auth bs) = put auth >> putByteString bs
108
109-- | Like ImplicitAssymetric, but includes the nonce used to encrypt.
110data UnclaimedAssymetric = UnclaimedAssymetric
111 { assymetricNonce :: Nonce24
112 , assymetricData :: !ImplicitAssymetric
113 }
114 deriving (Eq, Ord, Data)
115
116instance Serialize UnclaimedAssymetric where
117 get = UnclaimedAssymetric <$> get <*> get
118 put (UnclaimedAssymetric nonce dta) = put nonce >> put dta
119
120-- | Like UnclaimedAssymetric, but includes the public key of the sender.
121data Assymetric = Assymetric
122 { senderKey :: PubKey
123 , sent :: !UnclaimedAssymetric
124 }
125 deriving (Eq, Ord,Data)
126
127-- get requires isolate.
128instance Serialize Assymetric where
129 get = Assymetric <$> get <*> get
130 put (Assymetric key dta) = put key >> put dta
131
132newtype Cookie = Cookie UnclaimedAssymetric
133 deriving (Eq, Ord,Data)
134
135data Symmetric = Symmetric
136 { symmetricNonce :: Nonce24
137 , symmetricAuth :: Auth
138 , symmetricBytes :: ByteString
139 }
140 deriving (Eq, Ord,Data)
141
142data Packet where
143 Ping :: Assymetric -> Packet -- 0x00 -- Assymetric query
144 Pong :: Assymetric -> Packet -- 0x01 -- Assymetric response
145
146 GetNodes :: Assymetric -> Packet -- 0x02 -- Assymetric query
147 SendNodes :: Assymetric -> Packet -- 0x04 -- Assymetric response
148
149 CookieRequest :: Assymetric -> Packet -- 0x18
150 CookieResponse :: UnclaimedAssymetric -> Packet -- 0x19
151
152 OnionRequest0 :: Assymetric -> Packet -- 0x80
153
154
155 CryptoHandshake :: Cookie -> UnclaimedAssymetric -> Packet -- 0x1a
156
157 CryptoData :: Word16 -> ImplicitAssymetric -> Packet -- 0x1b
158
159 DHTRequest :: PubKey -> Assymetric -> Packet -- 0x20 -- Sometimes Assymetric query
160
161 DataToRoute :: PubKey -> Aliased Assymetric -> Packet
162 DataToRouteResponse :: Aliased Assymetric -> Packet
163
164 LanDiscovery :: PubKey -> Packet -- 0x21
165
166 OnionRequest1 :: Aliased Assymetric -> Symmetric -> Packet -- 0x81
167 OnionRequest2 :: Aliased Assymetric -> Symmetric -> Packet -- 0x82
168
169 OnionRequest3 :: ByteString -> Symmetric -> Packet -- 0x82
170
171 Announce :: Aliased Assymetric -> Packet --0x83
172 AnnounceResponse :: Nonce8 -> UnclaimedAssymetric -> Packet -- 0x84
173
174 OnionResponse3 :: Symmetric -> ByteString -> Packet -- 0x8c
175 OnionResponse2 :: Symmetric -> ByteString -> Packet -- 0x8d
176 OnionResponse1 :: Symmetric -> ByteString -> Packet -- 0x8e
177
178
179 GetBootstrapInfo :: ByteString -> Packet -- 0xf0 + 77 bytes -- ByteString query
180 BootstrapInfo :: Word32 -> ByteString -> Packet -- 0xf0 + version + (256 byte motd) -- ByteSTring response
181
182 deriving (Eq, Ord,Data)
183
184newtype PacketKind = PacketKind Word8
185 deriving (Eq, Ord, Serialize)
186
187pktKind :: Packet -> PacketKind
188
189-- These are (Assymetric -> Assymetric) queries.
190pktKind Ping {} = PacketKind 0x00
191pktKind Pong {} = PacketKind 0x01
192pktKind GetNodes {} = PacketKind 0x02
193pktKind SendNodes {} = PacketKind 0x04
194
195
196-- This is a (Assymetric -> UnclaimedAssymetric) query
197pktKind CookieRequest {} = PacketKind 0x18
198pktKind CookieResponse {} = PacketKind 0x19
199
200-- Query (Assymetric -> (Nonce8,UnclaimedAssymetric))
201pktKind Announce {} = PacketKind 0x83
202pktKind AnnounceResponse {} = PacketKind 0x84
203
204-- Query (Assymetric -> ByteString)
205pktKind OnionRequest0 {} = PacketKind 0x80
206
207
208-- This is a (ByteString -> ByteString) query
209pktKind GetBootstrapInfo {} = PacketKind 0xf0
210pktKind BootstrapInfo {} = PacketKind 0xf0
211
212
213-- Trigering event. No direct response. (PubKey -> ())
214pktKind LanDiscovery {} = PacketKind 0x21
215
216-- Two cases:
217-- Half-established: (Cookie,UnclaimedAssymetric) -> (Cookie,UnclaimedAssymetric)
218-- Session established: (Cookie,UnclaimedAssymetric) -> (Word16,ImplicitAssymetric)
219pktKind CryptoHandshake {} = PacketKind 0x1a
220
221-- Session data, no direct response.
222-- (reponse to CryptoHandshake, or other data)
223pktKind CryptoData {} = PacketKind 0x1b
224
225-- Two cases:
226-- ( (PubKey, Assymetric) -> response )
227-- ( (PubKey, Assymetric) -> () )
228pktKind DHTRequest {} = PacketKind 0x20
229
230
231-- Query ( (PubKey,Aliased Assymetric) -> Aliased Assymetric)
232pktKind DataToRoute {} = PacketKind 0x85
233pktKind DataToRouteResponse {} = PacketKind 0x86
234
235-- 3 Queries ( (Aliased Assymetric, Symmetric )
236-- -> ( Symmetric, ByteString ) )
237pktKind OnionRequest1 {} = PacketKind 0x81
238pktKind OnionResponse1 {} = PacketKind 0x8e
239
240pktKind OnionRequest2 {} = PacketKind 0x82
241pktKind OnionResponse2 {} = PacketKind 0x8d
242
243pktKind OnionRequest3 {} = PacketKind 0x82
244pktKind OnionResponse3 {} = PacketKind 0x8c
245
246data PacketClass =
247 AssymetricClass (Assymetric -> Packet) (Packet -> Assymetric)
248 | Unclassified
249
250pktClass :: PacketKind -> PacketClass
251pktClass (PacketKind 0) = AssymetricClass Ping (\(Ping a) -> a)
252pktClass (PacketKind 1) = AssymetricClass Pong (\(Pong a) -> a)
253pktClass (PacketKind 2) = AssymetricClass GetNodes (\(GetNodes a) -> a)
254pktClass (PacketKind 4) = AssymetricClass SendNodes (\(SendNodes a) -> a)
255
256pktClass (PacketKind 0x18) = AssymetricClass CookieRequest (\(CookieRequest a) -> a)
257pktClass (PacketKind 0x80) = AssymetricClass OnionRequest0 (\(OnionRequest0 a) -> a)
258pktClass (PacketKind 0x86) = AssymetricClass (DataToRouteResponse . Aliased) (\(DataToRouteResponse (Aliased a)) -> a)
259
260 -- (indexConstr (dataTypeOf (error "dataTypeOf Packet" :: Packet)) 0) -- Ping
261
262instance Serialize Packet where
263 get = getPacket
264 put = putPacket
265
266getPacket = do
267 typ <- get
268 case pktClass typ of
269 AssymetricClass toPacket fromPacket -> toPacket <$> get
270
271putPacket p = do
272 put $ pktKind p
273 case pktClass (pktKind p) of
274 AssymetricClass toPacket fromPacket -> put $ fromPacket p
275
276{-
277data Packet' where
278 :: Assymetric -> Packet
279 :: UnclaimedAssymetric -> Packet
280 :: Word16 -> ImplicitAssymetric -> Packet
281 :: PubKey -> Assymetric -> Packet
282 :: PubKey -> Packet
283 :: Aliased Assymetric -> Symmetric -> Packet
284 :: ByteString -> Symmetric -> Packet
285 :: Aliased Assymetric -> Packet
286 :: Symmetric -> ByteString -> Packet
287 :: ByteString -> Packet
288 :: Word32 -> ByteString -> Packet
289-}