## Using Coroutines In Protocol Simulations

From: andrew cooke <andrew@...>

Date: Mon, 29 Apr 2013 21:14:30 -0400

I don't want to sound like a shill, but I am having a great time with the
Matasano crypto challenges http://www.matasano.com/articles/crypto-challenges/
and I wanted to share a neat trick.

So I just got DH key exchange working.  Which is awesome - I had no idea it
was so easy.  But what made me extra-happy was the way that I coded the two
agents that were communicating.  I wrote each as a co-routine.

So my code looked a little like this:

def alice(ptext):
my_key = random()
bobs_key = yield my_key
joint_key = f(my_key, bobs_key)
yield encrypt(ptext, joint_key)

def bob():
alices_key = yield
my_key = random()
ctext = yield my_key
joint_key = f(my_key, alices_key)
ptext = decrypt(ctext, joint_key)

communicate(alice('hiya sexy'), bob())

If you're confused by that, all you need to know, really, is that each "yield"
is a message send.  So alice sends her key to bob, bob receives it (the value
returned from the first yield in bob), and replies with his own key, which

Isn't that a cool way of writing communicating processes?

Here's the definition of communicate.  It will chain any number of agents
(because just maybe there's a mallory to add later...), passing messages from
left to right, then right to left, until done.

def communicate(*agents):
try:
data = lmap(next, agents)[0]  # prime, and receive initial data
while True:
print('data', data)
agents = list(reversed(agents))
except StopIteration: return

and lmap is list(map) (something I always have defined in Python 3).

Andrew

PS Again, the above reveals neither question nor solution from the challenge,
so I believe it is OK.

PPS One reason I think this approach is so cool (apart from it being compact
and easy to read) is that the agents are completely decoupled from the
communcations process.  So adding another agent to do a man-in-the-middle
attack doesn't require any modification to bob or alice.

### Example: Tracing

From: andrew cooke <andrew@...>

Date: Mon, 29 Apr 2013 22:45:16 -0400

Here's an example of how the above supports quick extension.  Instead of the
"print" in communicate I have defined:

def trace(label):
msg = None
while True:
msg = yield msg
print(label, msg)

So now I can run an example like:

communicate(alice(b'i hope no one is listening'),
trace('a-m'),
mallory(),
trace('m-b'),
bob())

and see the data labelled by who the two people conversing are.

Andrew