Skip to content

Commit

Permalink
work on lab3
Browse files Browse the repository at this point in the history
  • Loading branch information
henrycg committed Aug 24, 2023
1 parent aed7936 commit d5f158a
Show file tree
Hide file tree
Showing 7 changed files with 78 additions and 64 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
__pycache__
.*.swp
venv

44 changes: 23 additions & 21 deletions docs/lab3.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,36 +170,42 @@ command.

## Problem 2: Timing side-channel attack

In this problem you will mount your own attack to extract a secret token from a "VeRY Secur3 SerVer".
In this problem you will mount your own attack to
extract a secret password from a server using an
insecure authentication scheme.

The code for this assignment is in the zip file
[`lab3-code.zip`](lab3/code.zip).
The code for this assignment is in [`https://github.com/mit-pdos/6.1600-labs/tree/main/time/problem2`](time/problem2).

# Scenario

The scenario of this lab is simple.
You are now the attacker.
Bob runs a payments service that, after Bob
authenticates by sending a password to the server,
runs the `send_money` routine to process
a payment. (In this toy example, `send_money` is
a no-op.)

Bob is trying to save his favorite cryptocoin $ecret key on a server so he can easily verify if he is remembering it correctly.
To do this, Bob sends a request containing his key on a secure connection to the server.
The server replies with a bit that indicates whether the secret in the request matches the one on the server.
In a secure implementation, Bob's server would use
a robust off-the-shelf authenticated transport
protocol (SSH, TLS 1.3 with pre-shared keys,
etc.). But since Bob has not taken 6.1600 yet, he
cooked up his own scheme.

Even though the server does not authenticate the party making the request, Bob believes that he is safe as the API is very restricted: an attacker trying to guess the secret by querying the API learns no information other than whether it was correct.

Prove him wrong!
Bob's server accepts requests from the network,
where each request contains a password. Bob's
server checks the request's password against
the true password and calls the `send_money`
function only if the passwords match.

# More specifically

On initialization, a `SecureServer` instance generates a secret token using fresh randomness and saves it as a hexadecimal string i.e., one with characters from `0` to `9` or `a` to `f`. Note that you need **two** hexadecimal characters to represent one byte of data.
On initialization, a `BadServer` instance generates a secret password using fresh randomness and saves it as a hexadecimal string i.e., one with characters from `0` to `9` or `a` to `f`. Note that you need **two** hexadecimal characters to represent one byte of data.

The `SecureServer` allows any user to submit a `VerifyTokenRequest` with some token.
The `BadServer` allows any user to submit a `VerifyTokenRequest` with some password.
The server responds with a `VerifyTokenResponse`, which contains a single boolean value.
This value is `True` if the token in the request matches the server's secret.
This value is `True` if the password in the request matches the server's secret.
Otherwise, the value is `False`.

In addition to this correctness property, Bob claims that the server has the following security property: there is a negligible probability that an attacker can recover the secret token from the server in polynomial time (with respect to the length of the token).

Unfortunately, implementation errors make it possible for you, the attacker, to violate this property.
Implementation errors make it possible for you, the attacker, to violate this property.
In particular, software side channels (specifically, timing side channels) foil Bob's attempt to achieve this property.

# Your job
Expand All @@ -219,9 +225,5 @@ Finaly, note that you are expected to respect the python conventions and you sho

For the rest, have fun and good luck!

# Submit lab 5

Upload your
[`problem2/attacker.py`](https://github.com/mit-pdos/6.1600-labs/tree/main/time/problem2/attacker.py) to Gradescope.


8 changes: 4 additions & 4 deletions time/problem2/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
A common API between the client and the server.
"""

class VerifyTokenRequest():
def __init__(self, token: str):
self.token = token
class VerifyRequest():
def __init__(self, password: str):
self.password = password

class VerifyTokenResponse():
class VerifyResponse():
def __init__(self, ret: bool):
self.ret = ret
20 changes: 10 additions & 10 deletions time/problem2/attacker.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
import secure_server
import bad_server
import api
import secrets
from typing import Optional

class Client:
def __init__(self, remote: secure_server.VerySecureServer):
def __init__(self, remote: bad_server.BadServer):
self._remote = remote

def steal_secret_token(self, l: int) -> Optional[str]:
secret_token = secrets.token_hex(l)
req = api.VerifyTokenRequest(secret_token)
if self._remote.verify_token(req).ret:
return secret_token
def steal_password (self, l: int) -> Optional[str]:
password = secrets.token_hex(l)
req = api.VerifyRequest(password)
if self._remote.verify_password(req).ret:
return password
else:
return None

if __name__ == "__main__":
token = '37a4e5bf847630173da7e6d19991bb8d'
nbytes = len(token) // 2
server = secure_server.VerySecureServer(token)
passwd = '37a4e5bf847630173da7e6d19991bb8d'
nbytes = len(passwd) // 2
server = bad_server.BadServer(passwd)
alice = Client(server)
print(alice.steal_secret_token(nbytes))
34 changes: 34 additions & 0 deletions time/problem2/bad_server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import secrets
import api
from typing import Optional

class BadServer:
def __init__(self, password: Optional[str] = None):
if password is None:
self._password = secrets.token_hex(256)
else:
self._password = password

def send_money(self):
# This is where the protected code would run.
pass

def verify_password(self, request: api.VerifyRequest) -> api.VerifyResponse:
try:
s = request.password
for i in range(len(self._password)):
if len(s) <= i:
return api.VerifyResponse(False)
elif s[i] != self._password[i]:
return api.VerifyResponse(False)

# At this point, the user is authenticated.
self.send_money()

return api.VerifyResponse(True)
except:
return api.VerifyResponse(False)

if __name__ == "__main__":
import doctest
doctest.testmod()
6 changes: 3 additions & 3 deletions time/problem2/grade-lab.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import secure_server
import bad_server
import attacker
import secrets
import argparse
Expand All @@ -13,9 +13,9 @@

def extract_bytes(n_bytes):
secret = secrets.token_hex(n_bytes)
server = secure_server.VerySecureServer(secret)
server = bad_server.BadServer(secret)
attack = attacker.Client(server)
res = attack.steal_secret_token(n_bytes)
res = attack.steal_password(n_bytes)
return res == secret

tests = [
Expand Down
26 changes: 0 additions & 26 deletions time/problem2/secure_server.py

This file was deleted.

0 comments on commit d5f158a

Please sign in to comment.