73 lines
2.9 KiB
Python
73 lines
2.9 KiB
Python
"""The active attacker: Mallory sits on the wire and can change what passes. You write the two methods marked YOUR TASK.
|
|
|
|
An eavesdropper only reads the wire. Mallory can also replace a message
|
|
before it arrives. wire.py hands her every Message through intercept(), and
|
|
whatever she returns is what the receiver gets.
|
|
|
|
Her goal: Alice and Bob each finish the exchange believing they share a
|
|
secret with the other, every message they send is readable by her, and
|
|
neither of them sees anything wrong.
|
|
|
|
Message fields (see wire.py): kind ("public" or "text"), sender, receiver,
|
|
value. wire.encrypt(secret, text) and wire.decrypt(secret, blob) are the
|
|
cipher Alice and Bob use.
|
|
|
|
Read first: why an unauthenticated key exchange lets someone in the middle,
|
|
https://en.wikipedia.org/wiki/Man-in-the-middle_attack
|
|
"""
|
|
|
|
from dataclasses import replace
|
|
|
|
import dh
|
|
import wire
|
|
|
|
|
|
class Mallory:
|
|
def __init__(self, g, p):
|
|
self.g, self.p = g, p
|
|
self.private = dh.generate_private(p)
|
|
self.public = dh.public_from(g, p, self.private)
|
|
self.secrets = {} # party name -> the shared secret Mallory holds with that party
|
|
self.read = [] # (sender, receiver, plaintext) for every text message she relays
|
|
self.rewrite = None # optional: a function text -> text applied to every message she relays
|
|
|
|
def intercept(self, message):
|
|
"""Called by the wire for every message. Returns the message to deliver."""
|
|
if message.kind == "public":
|
|
return self.swap_public(message)
|
|
return self.relay_text(message)
|
|
|
|
def swap_public(self, message):
|
|
"""A public value is travelling from message.sender to message.receiver.
|
|
|
|
Finish an exchange of your own with the sender, remember the secret
|
|
you now share with them in self.secrets, and return the message the
|
|
receiver should get instead. `replace(message, value=...)` makes a
|
|
copy of a Message with one field changed.
|
|
|
|
YOUR TASK.
|
|
"""
|
|
raise NotImplementedError("swap_public")
|
|
|
|
def relay_text(self, message):
|
|
"""An encrypted message is travelling from message.sender to message.receiver.
|
|
|
|
Read it, record (sender, receiver, plaintext) in self.read, apply
|
|
self.rewrite if it is set, and return a message the receiver can
|
|
decrypt with the secret they think they share with the sender.
|
|
|
|
YOUR TASK.
|
|
"""
|
|
raise NotImplementedError("relay_text")
|
|
|
|
|
|
def attack(g, p, texts, rewrite=None):
|
|
"""Run the exchange with Mallory in the middle, then send `texts`: a list of (sender name, text)."""
|
|
mallory = Mallory(g, p)
|
|
mallory.rewrite = rewrite
|
|
alice, bob, line = wire.exchange(g, p, middle=mallory)
|
|
people = {"Alice": (alice, bob), "Bob": (bob, alice)}
|
|
for sender, text in texts:
|
|
me, them = people[sender]
|
|
me.send_text(them, text)
|
|
return alice, bob, mallory, line
|