1from collections import namedtuple
2from Queue import Queue
3from threading import Event, Thread
4
5import pytest
6
7from pyaxo import Axolotl
8
9
10def test_init_conversation(axolotl_a, axolotl_b,
11                           a_identity_keys, b_identity_keys,
12                           a_handshake_keys, b_handshake_keys,
13                           a_ratchet_keys, b_ratchet_keys,
14                           exchange):
15    conv_a = axolotl_a.init_conversation(
16        axolotl_b.name,
17        priv_identity_key=a_identity_keys.priv,
18        identity_key=a_identity_keys.pub,
19        priv_handshake_key=a_handshake_keys.priv,
20        other_identity_key=b_identity_keys.pub,
21        other_handshake_key=b_handshake_keys.pub,
22        priv_ratchet_key=a_ratchet_keys.priv,
23        ratchet_key=a_ratchet_keys.pub,
24        other_ratchet_key=b_ratchet_keys.pub)
25
26    conv_b = axolotl_b.init_conversation(
27        axolotl_a.name,
28        priv_identity_key=b_identity_keys.priv,
29        identity_key=b_identity_keys.pub,
30        priv_handshake_key=b_handshake_keys.priv,
31        other_identity_key=a_identity_keys.pub,
32        other_handshake_key=a_handshake_keys.pub,
33        priv_ratchet_key=b_ratchet_keys.priv,
34        ratchet_key=b_ratchet_keys.pub,
35        other_ratchet_key=a_ratchet_keys.pub)
36
37    exchange(conv_a, conv_b)
38
39
40def test_init_nonthreaded_conversations(
41        axolotl_a, axolotl_b, axolotl_c,
42        a_identity_keys, b_identity_keys, c_identity_keys,
43        a_handshake_keys, b_handshake_keys, c_handshake_keys,
44        a_ratchet_keys, b_ratchet_keys, c_ratchet_keys,
45        exchange):
46    conversations = initialize_conversations(
47        axolotl_a, a_identity_keys, a_handshake_keys, a_ratchet_keys,
48        axolotl_b, b_identity_keys, b_handshake_keys, b_ratchet_keys,
49        axolotl_c, c_identity_keys, c_handshake_keys, c_ratchet_keys)
50
51    exchange(conversations.ab, conversations.ba)
52    exchange(conversations.ac, conversations.ca)
53    exchange(conversations.bc, conversations.cb)
54
55
56def test_init_threaded_conversations(
57        threaded_axolotl_a, threaded_axolotl_b, threaded_axolotl_c,
58        a_identity_keys, b_identity_keys, c_identity_keys,
59        a_handshake_keys, b_handshake_keys, c_handshake_keys,
60        a_ratchet_keys, b_ratchet_keys, c_ratchet_keys,
61        exchange):
62    conversations = initialize_conversations(
63        threaded_axolotl_a, a_identity_keys, a_handshake_keys, a_ratchet_keys,
64        threaded_axolotl_b, b_identity_keys, b_handshake_keys, b_ratchet_keys,
65        threaded_axolotl_c, c_identity_keys, c_handshake_keys, c_ratchet_keys)
66
67    run_threaded_exchanges(exchange, conversations)
68
69
70def test_create_conversation(axolotl_a, axolotl_b,
71                             a_identity_keys, b_identity_keys,
72                             a_handshake_keys, b_handshake_keys,
73                             a_ratchet_keys, b_ratchet_keys,
74                             exchange):
75    mkey = 'masterkey'
76
77    conv_a = axolotl_a.create_conversation(
78        other_name=axolotl_b.name,
79        mkey=mkey,
80        mode=True,
81        priv_identity_key=a_identity_keys.priv,
82        identity_key=a_identity_keys.pub,
83        other_identity_key=b_identity_keys.pub,
84        other_ratchet_key=b_ratchet_keys.pub)
85
86    conv_b = axolotl_b.create_conversation(
87        other_name=axolotl_a.name,
88        mkey=mkey,
89        mode=False,
90        priv_identity_key=b_identity_keys.priv,
91        identity_key=b_identity_keys.pub,
92        other_identity_key=a_identity_keys.pub,
93        priv_ratchet_key=b_ratchet_keys.priv,
94        ratchet_key=b_ratchet_keys.pub)
95
96    exchange(conv_a, conv_b)
97
98
99def test_create_nonthreaded_conversations(
100        axolotl_a, axolotl_b, axolotl_c,
101        a_identity_keys, b_identity_keys, c_identity_keys,
102        a_handshake_keys, b_handshake_keys, c_handshake_keys,
103        a_ratchet_keys, b_ratchet_keys, c_ratchet_keys,
104        exchange):
105    mkey_ab = 'masterkey_ab'
106    mkey_ac = 'masterkey_ac'
107    mkey_bc = 'masterkey_bc'
108
109    conversations = create_conversations(
110        axolotl_a, a_identity_keys, a_handshake_keys, a_ratchet_keys,
111        axolotl_b, b_identity_keys, b_handshake_keys, b_ratchet_keys,
112        axolotl_c, c_identity_keys, c_handshake_keys, c_ratchet_keys,
113        mkey_ab, mkey_ac, mkey_bc)
114
115    exchange(conversations.ab, conversations.ba)
116    exchange(conversations.ac, conversations.ca)
117    exchange(conversations.bc, conversations.cb)
118
119
120def test_create_threaded_conversations(
121        threaded_axolotl_a, threaded_axolotl_b, threaded_axolotl_c,
122        a_identity_keys, b_identity_keys, c_identity_keys,
123        a_handshake_keys, b_handshake_keys, c_handshake_keys,
124        a_ratchet_keys, b_ratchet_keys, c_ratchet_keys,
125        exchange):
126    mkey_ab = 'masterkey_ab'
127    mkey_ac = 'masterkey_ac'
128    mkey_bc = 'masterkey_bc'
129
130    conversations = create_conversations(
131        threaded_axolotl_a, a_identity_keys, a_handshake_keys, a_ratchet_keys,
132        threaded_axolotl_b, b_identity_keys, b_handshake_keys, b_ratchet_keys,
133        threaded_axolotl_c, c_identity_keys, c_handshake_keys, c_ratchet_keys,
134        mkey_ab, mkey_ac, mkey_bc)
135
136    run_threaded_exchanges(exchange, conversations)
137
138
139@pytest.fixture()
140def threaded_axolotl_a():
141    return Axolotl('Angie', dbpassphrase=None, nonthreaded_sql=False)
142
143
144@pytest.fixture()
145def threaded_axolotl_b():
146    return Axolotl('Barb', dbpassphrase=None, nonthreaded_sql=False)
147
148
149@pytest.fixture()
150def threaded_axolotl_c():
151    return Axolotl('Charlie', dbpassphrase=None, nonthreaded_sql=False)
152
153
154class ThreadedExchange(Thread):
155    def __init__(self, exchange, axolotl_x, axolotl_y, event, queue):
156        super(ThreadedExchange, self).__init__()
157        self.daemon = True
158        self.exchange = exchange
159        self.axolotl_x = axolotl_x
160        self.axolotl_y = axolotl_y
161        self.event = event
162        self.queue = queue
163
164    def run(self):
165        self.event.wait()
166        try:
167            self.exchange(self.axolotl_x, self.axolotl_y)
168        except AssertionError:
169            self.queue.put(False)
170        else:
171            self.queue.put(True)
172
173
174Conversations = namedtuple('Conversations', 'ab ac ba bc ca cb')
175
176
177def initialize_conversations(
178        axolotl_a, a_identity_keys, a_handshake_keys, a_ratchet_keys,
179        axolotl_b, b_identity_keys, b_handshake_keys, b_ratchet_keys,
180        axolotl_c, c_identity_keys, c_handshake_keys, c_ratchet_keys):
181    ab = axolotl_a.init_conversation(
182        other_name=axolotl_b.name,
183        priv_identity_key=a_identity_keys.priv,
184        identity_key=a_identity_keys.pub,
185        priv_handshake_key=a_handshake_keys.priv,
186        other_identity_key=b_identity_keys.pub,
187        other_handshake_key=b_handshake_keys.pub,
188        priv_ratchet_key=a_ratchet_keys.priv,
189        ratchet_key=a_ratchet_keys.pub,
190        other_ratchet_key=b_ratchet_keys.pub)
191
192    ac = axolotl_a.init_conversation(
193        other_name=axolotl_c.name,
194        priv_identity_key=a_identity_keys.priv,
195        identity_key=a_identity_keys.pub,
196        priv_handshake_key=a_handshake_keys.priv,
197        other_identity_key=c_identity_keys.pub,
198        other_handshake_key=c_handshake_keys.pub,
199        priv_ratchet_key=a_ratchet_keys.priv,
200        ratchet_key=a_ratchet_keys.pub,
201        other_ratchet_key=c_ratchet_keys.pub)
202
203    ba = axolotl_b.init_conversation(
204        other_name=axolotl_a.name,
205        priv_identity_key=b_identity_keys.priv,
206        identity_key=b_identity_keys.pub,
207        priv_handshake_key=b_handshake_keys.priv,
208        other_identity_key=a_identity_keys.pub,
209        other_handshake_key=a_handshake_keys.pub,
210        priv_ratchet_key=b_ratchet_keys.priv,
211        ratchet_key=b_ratchet_keys.pub,
212        other_ratchet_key=a_ratchet_keys.pub)
213
214    bc = axolotl_b.init_conversation(
215        other_name=axolotl_c.name,
216        priv_identity_key=b_identity_keys.priv,
217        identity_key=b_identity_keys.pub,
218        priv_handshake_key=b_handshake_keys.priv,
219        other_identity_key=c_identity_keys.pub,
220        other_handshake_key=c_handshake_keys.pub,
221        priv_ratchet_key=b_ratchet_keys.priv,
222        ratchet_key=b_ratchet_keys.pub,
223        other_ratchet_key=c_ratchet_keys.pub)
224
225    ca = axolotl_c.init_conversation(
226        other_name=axolotl_a.name,
227        priv_identity_key=c_identity_keys.priv,
228        identity_key=c_identity_keys.pub,
229        priv_handshake_key=c_handshake_keys.priv,
230        other_identity_key=a_identity_keys.pub,
231        other_handshake_key=a_handshake_keys.pub,
232        priv_ratchet_key=c_ratchet_keys.priv,
233        ratchet_key=b_ratchet_keys.pub,
234        other_ratchet_key=a_ratchet_keys.pub)
235
236    cb = axolotl_c.init_conversation(
237        other_name=axolotl_b.name,
238        priv_identity_key=c_identity_keys.priv,
239        identity_key=c_identity_keys.pub,
240        priv_handshake_key=c_handshake_keys.priv,
241        other_identity_key=b_identity_keys.pub,
242        other_handshake_key=b_handshake_keys.pub,
243        priv_ratchet_key=c_ratchet_keys.priv,
244        ratchet_key=c_ratchet_keys.pub,
245        other_ratchet_key=b_ratchet_keys.pub)
246
247    return Conversations(ab, ac, ba, bc, ca, cb)
248
249
250def create_conversations(
251        axolotl_a, a_identity_keys, a_handshake_keys, a_ratchet_keys,
252        axolotl_b, b_identity_keys, b_handshake_keys, b_ratchet_keys,
253        axolotl_c, c_identity_keys, c_handshake_keys, c_ratchet_keys,
254        mkey_ab, mkey_ac, mkey_bc):
255    ab = axolotl_a.create_conversation(
256        other_name=axolotl_b.name,
257        mkey=mkey_ab,
258        mode=True,
259        priv_identity_key=a_identity_keys.priv,
260        identity_key=a_identity_keys.pub,
261        other_identity_key=b_identity_keys.pub,
262        other_ratchet_key=b_ratchet_keys.pub)
263
264    ac = axolotl_a.create_conversation(
265        other_name=axolotl_c.name,
266        mkey=mkey_ac,
267        mode=False,
268        priv_identity_key=a_identity_keys.priv,
269        identity_key=a_identity_keys.pub,
270        other_identity_key=c_identity_keys.pub,
271        priv_ratchet_key=a_ratchet_keys.priv,
272        ratchet_key=a_ratchet_keys.pub)
273
274    ba = axolotl_b.create_conversation(
275        other_name=axolotl_a.name,
276        mkey=mkey_ab,
277        mode=False,
278        priv_identity_key=b_identity_keys.priv,
279        identity_key=b_identity_keys.pub,
280        other_identity_key=a_identity_keys.pub,
281        priv_ratchet_key=b_ratchet_keys.priv,
282        ratchet_key=b_ratchet_keys.pub)
283
284    bc = axolotl_b.create_conversation(
285        other_name=axolotl_c.name,
286        mkey=mkey_bc,
287        mode=True,
288        priv_identity_key=b_identity_keys.priv,
289        identity_key=b_identity_keys.pub,
290        other_identity_key=c_identity_keys.pub,
291        other_ratchet_key=c_ratchet_keys.pub)
292
293    ca = axolotl_c.create_conversation(
294        other_name=axolotl_a.name,
295        mkey=mkey_ac,
296        mode=True,
297        priv_identity_key=c_identity_keys.priv,
298        identity_key=c_identity_keys.pub,
299        other_identity_key=a_identity_keys.pub,
300        other_ratchet_key=a_ratchet_keys.pub)
301
302    cb = axolotl_c.create_conversation(
303        other_name=axolotl_b.name,
304        mkey=mkey_bc,
305        mode=False,
306        priv_identity_key=c_identity_keys.priv,
307        identity_key=c_identity_keys.pub,
308        other_identity_key=b_identity_keys.pub,
309        priv_ratchet_key=c_ratchet_keys.priv,
310        ratchet_key=c_ratchet_keys.pub)
311
312    return Conversations(ab, ac, ba, bc, ca, cb)
313
314
315def run_threaded_exchanges(exchange, conversations):
316    event = Event()
317
318    queue_ab = Queue()
319    exchange_ab = ThreadedExchange(exchange,
320                                   conversations.ab, conversations.ba,
321                                   event, queue_ab)
322
323    queue_ac = Queue()
324    exchange_ac = ThreadedExchange(exchange,
325                                   conversations.ac, conversations.ca,
326                                   event, queue_ac)
327
328    queue_bc = Queue()
329    exchange_bc = ThreadedExchange(exchange,
330                                   conversations.bc, conversations.cb,
331                                   event, queue_bc)
332
333    exchange_ab.start()
334    exchange_ac.start()
335    exchange_bc.start()
336
337    event.set()
338
339    assert queue_ab.get() and queue_ac.get() and queue_bc.get()
340