diff options
Diffstat (limited to 'ToxMessage.hs')
-rw-r--r-- | ToxMessage.hs | 289 |
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 #-} | ||
4 | module ToxMessage where | ||
5 | |||
6 | import Data.ByteString (ByteString) | ||
7 | import qualified Crypto.MAC.Poly1305 as Poly1305 (Auth(..)) | ||
8 | import qualified Crypto.PubKey.Curve25519 as Curve25519 | ||
9 | import Data.ByteArray as BA (ByteArrayAccess, Bytes) | ||
10 | import qualified Data.ByteArray as BA | ||
11 | import qualified Data.ByteString as B | ||
12 | import qualified Data.ByteString.Char8 as C8 | ||
13 | import qualified Data.ByteString.Base16 as Base16 | ||
14 | import Data.Bits | ||
15 | import Data.Hashable | ||
16 | import Data.Bits.ByteString () | ||
17 | import Data.Word | ||
18 | import Data.Data | ||
19 | import Data.Ord | ||
20 | import Data.Serialize | ||
21 | |||
22 | newtype Auth = Auth Poly1305.Auth | ||
23 | deriving (Eq, ByteArrayAccess) | ||
24 | |||
25 | instance Ord Auth where | ||
26 | compare (Auth a) (Auth b) = comparing (BA.convert :: Poly1305.Auth -> Bytes) a b | ||
27 | |||
28 | instance 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 | |||
38 | con_Auth = mkConstr (dataTypeOf (Auth (error "con_Auth"))) "Auth" [] Prefix | ||
39 | |||
40 | instance 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. | ||
50 | newtype Aliased a = Aliased a | ||
51 | deriving (Eq,Ord,Show,Data,Functor,Foldable,Traversable,Serialize) | ||
52 | |||
53 | newtype Nonce24 = Nonce24 ByteString | ||
54 | deriving (Eq, Ord, ByteArrayAccess,Data) | ||
55 | |||
56 | quoted :: ShowS -> ShowS | ||
57 | quoted shows s = '"':shows ('"':s) | ||
58 | |||
59 | bin2hex :: ByteArrayAccess bs => bs -> String | ||
60 | bin2hex = C8.unpack . Base16.encode . BA.convert | ||
61 | |||
62 | instance Show Nonce24 where | ||
63 | showsPrec d nonce = quoted (mappend $ bin2hex nonce) | ||
64 | |||
65 | instance Serialize Nonce24 where | ||
66 | get = Nonce24 <$> getBytes 24 | ||
67 | put (Nonce24 bs) = putByteString bs | ||
68 | |||
69 | newtype 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... | ||
74 | newtype PubKey = PubKey ByteString | ||
75 | deriving (Eq,Ord,Data, ByteArrayAccess, Bits, Hashable) | ||
76 | |||
77 | instance Serialize PubKey where | ||
78 | get = PubKey <$> getBytes 32 | ||
79 | put (PubKey bs) = putByteString bs | ||
80 | |||
81 | instance Show PubKey where | ||
82 | show (PubKey bs) = C8.unpack $ Base16.encode bs | ||
83 | |||
84 | instance FiniteBits PubKey where | ||
85 | finiteBitSize _ = 256 | ||
86 | |||
87 | instance 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. | ||
97 | data ImplicitAssymetric = ImplicitAssymetric | ||
98 | { assymetricAuth :: Auth | ||
99 | , assymetricBytes :: ByteString | ||
100 | } | ||
101 | deriving (Eq, Ord,Data) | ||
102 | |||
103 | getRemaining = remaining >>= getBytes | ||
104 | |||
105 | instance 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. | ||
110 | data UnclaimedAssymetric = UnclaimedAssymetric | ||
111 | { assymetricNonce :: Nonce24 | ||
112 | , assymetricData :: !ImplicitAssymetric | ||
113 | } | ||
114 | deriving (Eq, Ord, Data) | ||
115 | |||
116 | instance 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. | ||
121 | data Assymetric = Assymetric | ||
122 | { senderKey :: PubKey | ||
123 | , sent :: !UnclaimedAssymetric | ||
124 | } | ||
125 | deriving (Eq, Ord,Data) | ||
126 | |||
127 | -- get requires isolate. | ||
128 | instance Serialize Assymetric where | ||
129 | get = Assymetric <$> get <*> get | ||
130 | put (Assymetric key dta) = put key >> put dta | ||
131 | |||
132 | newtype Cookie = Cookie UnclaimedAssymetric | ||
133 | deriving (Eq, Ord,Data) | ||
134 | |||
135 | data Symmetric = Symmetric | ||
136 | { symmetricNonce :: Nonce24 | ||
137 | , symmetricAuth :: Auth | ||
138 | , symmetricBytes :: ByteString | ||
139 | } | ||
140 | deriving (Eq, Ord,Data) | ||
141 | |||
142 | data 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 | |||
184 | newtype PacketKind = PacketKind Word8 | ||
185 | deriving (Eq, Ord, Serialize) | ||
186 | |||
187 | pktKind :: Packet -> PacketKind | ||
188 | |||
189 | -- These are (Assymetric -> Assymetric) queries. | ||
190 | pktKind Ping {} = PacketKind 0x00 | ||
191 | pktKind Pong {} = PacketKind 0x01 | ||
192 | pktKind GetNodes {} = PacketKind 0x02 | ||
193 | pktKind SendNodes {} = PacketKind 0x04 | ||
194 | |||
195 | |||
196 | -- This is a (Assymetric -> UnclaimedAssymetric) query | ||
197 | pktKind CookieRequest {} = PacketKind 0x18 | ||
198 | pktKind CookieResponse {} = PacketKind 0x19 | ||
199 | |||
200 | -- Query (Assymetric -> (Nonce8,UnclaimedAssymetric)) | ||
201 | pktKind Announce {} = PacketKind 0x83 | ||
202 | pktKind AnnounceResponse {} = PacketKind 0x84 | ||
203 | |||
204 | -- Query (Assymetric -> ByteString) | ||
205 | pktKind OnionRequest0 {} = PacketKind 0x80 | ||
206 | |||
207 | |||
208 | -- This is a (ByteString -> ByteString) query | ||
209 | pktKind GetBootstrapInfo {} = PacketKind 0xf0 | ||
210 | pktKind BootstrapInfo {} = PacketKind 0xf0 | ||
211 | |||
212 | |||
213 | -- Trigering event. No direct response. (PubKey -> ()) | ||
214 | pktKind LanDiscovery {} = PacketKind 0x21 | ||
215 | |||
216 | -- Two cases: | ||
217 | -- Half-established: (Cookie,UnclaimedAssymetric) -> (Cookie,UnclaimedAssymetric) | ||
218 | -- Session established: (Cookie,UnclaimedAssymetric) -> (Word16,ImplicitAssymetric) | ||
219 | pktKind CryptoHandshake {} = PacketKind 0x1a | ||
220 | |||
221 | -- Session data, no direct response. | ||
222 | -- (reponse to CryptoHandshake, or other data) | ||
223 | pktKind CryptoData {} = PacketKind 0x1b | ||
224 | |||
225 | -- Two cases: | ||
226 | -- ( (PubKey, Assymetric) -> response ) | ||
227 | -- ( (PubKey, Assymetric) -> () ) | ||
228 | pktKind DHTRequest {} = PacketKind 0x20 | ||
229 | |||
230 | |||
231 | -- Query ( (PubKey,Aliased Assymetric) -> Aliased Assymetric) | ||
232 | pktKind DataToRoute {} = PacketKind 0x85 | ||
233 | pktKind DataToRouteResponse {} = PacketKind 0x86 | ||
234 | |||
235 | -- 3 Queries ( (Aliased Assymetric, Symmetric ) | ||
236 | -- -> ( Symmetric, ByteString ) ) | ||
237 | pktKind OnionRequest1 {} = PacketKind 0x81 | ||
238 | pktKind OnionResponse1 {} = PacketKind 0x8e | ||
239 | |||
240 | pktKind OnionRequest2 {} = PacketKind 0x82 | ||
241 | pktKind OnionResponse2 {} = PacketKind 0x8d | ||
242 | |||
243 | pktKind OnionRequest3 {} = PacketKind 0x82 | ||
244 | pktKind OnionResponse3 {} = PacketKind 0x8c | ||
245 | |||
246 | data PacketClass = | ||
247 | AssymetricClass (Assymetric -> Packet) (Packet -> Assymetric) | ||
248 | | Unclassified | ||
249 | |||
250 | pktClass :: PacketKind -> PacketClass | ||
251 | pktClass (PacketKind 0) = AssymetricClass Ping (\(Ping a) -> a) | ||
252 | pktClass (PacketKind 1) = AssymetricClass Pong (\(Pong a) -> a) | ||
253 | pktClass (PacketKind 2) = AssymetricClass GetNodes (\(GetNodes a) -> a) | ||
254 | pktClass (PacketKind 4) = AssymetricClass SendNodes (\(SendNodes a) -> a) | ||
255 | |||
256 | pktClass (PacketKind 0x18) = AssymetricClass CookieRequest (\(CookieRequest a) -> a) | ||
257 | pktClass (PacketKind 0x80) = AssymetricClass OnionRequest0 (\(OnionRequest0 a) -> a) | ||
258 | pktClass (PacketKind 0x86) = AssymetricClass (DataToRouteResponse . Aliased) (\(DataToRouteResponse (Aliased a)) -> a) | ||
259 | |||
260 | -- (indexConstr (dataTypeOf (error "dataTypeOf Packet" :: Packet)) 0) -- Ping | ||
261 | |||
262 | instance Serialize Packet where | ||
263 | get = getPacket | ||
264 | put = putPacket | ||
265 | |||
266 | getPacket = do | ||
267 | typ <- get | ||
268 | case pktClass typ of | ||
269 | AssymetricClass toPacket fromPacket -> toPacket <$> get | ||
270 | |||
271 | putPacket p = do | ||
272 | put $ pktKind p | ||
273 | case pktClass (pktKind p) of | ||
274 | AssymetricClass toPacket fromPacket -> put $ fromPacket p | ||
275 | |||
276 | {- | ||
277 | data 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 | -} | ||