Lab 3: Performance and side channels
Instructions on how to submit Lab 3: Please download all the required files from the lab3-timing github repo and lab3-ssh github repo.
-
Code: Place your code answers in the template
timing/attacker.py
for the Problem 2 andssh/attack.py
for problem 3. -
Text: Answer the questions for Problem 1 in the Lab 3 Questions gradescope assignment.
-
Testing This lab contains nondeterministic parts. In order to decrease randomness, we have implemented hidden functions that will be used to grade your attacks. You can test your code against these implementations on the Lab 3: 2 Testing and Lab 3: 3A Testing gradescope assignments. These assignments are worth 0 points but are designed to help you develop your attacks.
Upload timing/attacker.py
as attacker.py
and ssh/attack.py
as attack.py
to the Lab 3 Code gradescope assignment.
Running the Lab on Windows
make check
and make venv
do not natively work on Windows.
If you are using a windows machine, please see the Windows Instructions.
Gradescope autograder: Your code will be graded with the Gradescope autograder with a total timeout of 40 minutes.
There is a STRICT 6.0GB memory limit on Gradescope. Reasonable solutions to this lab should not come close to approaching this memory limit.
Problem 1: Performance
In this problem, you will get some hands-on experience with the performance characteristics of different cryptographic algorithms.
For this lab, you will need access to a machine
with OpenSSL installed. The Athena machines will
work well for this if you don’t have a convenient
local environment to use.
To test whether you are on a machine with OpenSSL,
run the shell command openssl version
. You
should see some output like this:
$ openssl version
OpenSSL 3.0.7 1 Nov 2022 (Library: OpenSSL 3.0.7 1 Nov 2022)
Now, run openssl help
. It should display some
output like this:
$ openssl help
help:
Standard commands
asn1parse ca ciphers cmp
cms crl crl2pkcs7 dgst
dhparam dsa dsaparam ec
ecparam enc engine errstr
fipsinstall gendsa genpkey genrsa
help info kdf list
mac nseq ocsp passwd
pkcs12 pkcs7 pkcs8 pkey
pkeyparam pkeyutl prime rand
rehash req rsa rsautl
s_client s_server s_time sess_id
smime speed spkac srp
storeutl ts verify version
x509
Message Digest commands (see the `dgst' command for more details)
blake2b512 blake2s256 md4 md5
mdc2 rmd160 sha1 sha224
sha256 sha3-224 sha3-256 sha3-384
sha3-512 sha384 sha512 sha512-224
sha512-256 shake128 shake256 sm3
Cipher commands (see the `enc' command for more details)
aes-128-cbc aes-128-ecb aes-192-cbc aes-192-ecb
aes-256-cbc aes-256-ecb aria-128-cbc aria-128-cfb
aria-128-cfb1 aria-128-cfb8 aria-128-ctr aria-128-ecb
aria-128-ofb aria-192-cbc aria-192-cfb aria-192-cfb1
aria-192-cfb8 aria-192-ctr aria-192-ecb aria-192-ofb
aria-256-cbc aria-256-cfb aria-256-cfb1 aria-256-cfb8
aria-256-ctr aria-256-ecb aria-256-ofb base64
bf bf-cbc bf-cfb bf-ecb
bf-ofb camellia-128-cbc camellia-128-ecb camellia-192-cbc
camellia-192-ecb camellia-256-cbc camellia-256-ecb cast
cast-cbc cast5-cbc cast5-cfb cast5-ecb
cast5-ofb des des-cbc des-cfb
des-ecb des-ede des-ede-cbc des-ede-cfb
des-ede-ofb des-ede3 des-ede3-cbc des-ede3-cfb
des-ede3-ofb des-ofb des3 desx
idea idea-cbc idea-cfb idea-ecb
idea-ofb rc2 rc2-40-cbc rc2-64-cbc
rc2-cbc rc2-cfb rc2-ecb rc2-ofb
rc4 rc4-40 seed seed-cbc
seed-cfb seed-ecb seed-ofb sm4-cbc
sm4-cfb sm4-ctr sm4-ecb sm4-ofb
zlib
You can benchmark each of the message-digest
and cipher algorithms listed here by running
openssl speed <ALG_NAME>
. For example openssl
speed rc4
will give you performance numbers for
the RC4 block cipher.
A few additional algorithms not listed above are:
-
rsa1024
,rsa2048
,rsa4096
– RSA keypair generation and signing -
ecdh
– Elliptic-curve Diffie-Hellman key exchange -
dsa
– The digital signature algorithm, working over the integers modulo a prime p -
ecsda
– Elliptic-curve digital signature algorithm
In addition, the following command runs AES encryption using hardware acceleration (if your machine supports it), with a 256-bit key in Galois counter mode:
openssl speed -evp aes-256-gcm
The command openssl genrsa 4096
will generate
a 4096-bit keypair.
The command openssl speed -help
will give you
more options that you can pass to the speed
command.
Please answer the following questions on the Lab 3 Questions gradescope assignment.
Questions
-
You are designing a file-storage application that requires computing a MAC over large files. You have the option between using HMAC-SHA256 and AES-128-GMAC. Both of these MACs give 128-bit security. Which has better performance for encrypting >1MB files? (You should do a little bit of research on the design of both of these primitives to understand why.)
-
Your boss tells you that to protect against quantum computers, your company will have to switch from using AES-128 encryption to AES-256 encryption. Roughly how much longer will it take to encrypt a 100MB file after increasing the key size? Why is this the case?
-
MIT has asked you to redesign the software on the MIT certificate authority (CA). They are deciding between using RSA, DSA, and ECDSA signatures.
-
What is the minimum key length you must use for each of these three signature algorithms to achieve 128-bit security under the best-known attacks today?
Answer the following sub-questions assuming that you use key sizes for each algorithm that achieve 128-bit security.
-
Which of these three algorithms is fastest/slowest for signing?
-
Which of these three algorithms is fastest/slowest for signature verification?
-
Which of these three algorithms is fastest/slowest for keypair generation? (You should be able to infer the answer to this question from the output of the
openssl
commands given above.)
-
Problem 2: Timing side-channel attack
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 timing
.
Timing side channels are nondeterministic. To help decrease the randomness, we have implemented hidden functions that will be used to test your attack. You can test your solution on the Lab 3: 2 Testing gradescope assignment.
Scenario
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.)
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 ad-hoc authentication protocol.
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 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 two hexadecimal characters represent one byte of data.
The BadServer
allows any user to submit a VerifyRequest
with some password.
The server responds with a VerifyResponse
, which contains a single boolean value.
This value is True
if the password in the request matches the server’s secret.
Otherwise, the value is False
.
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
Your job is to implement steal_password
in timing/attacker.py
to steal the secret password from the server.
In particular,
- The autograder will test whether you can extract passwords of different lengths. The length in bytes is the
l
parameter tosteal_password
. - Every test will wait 20 minutes for the attacker to extract the secret password. Your attack must complete by this time (or the autograder will reject it).
- Your attack must not crash or fail (or the autograder will reject it).
- Your attack is correct if you can convince the autograder to accept your solution.
Finally, you must not access private variables of
the BadServer
instance.
Problem 3: SSH Security
In this problem, you will mount two
different attacks: one that theoretically could work against a real SSH implementation
and one that actually works on a real SSH implementation.
The SSH client and server are built with the very
slick paramiko
library.
The starter code for this assignment is in
ssh
.
You will have to implement two functions in
ssh/attack.py
.
You should not change any of the other files – we
will grade your solution against fresh copies of these files.
Problem 3A is nondeterministic against real SSH implementations. To simplify the attack, we built a hidden, custom compression algorithm allowing the attack to be more deterministic. To test your attack, please use the Lab 3: 3A Testing gradescope assignment.
Getting started
Run make venv
to set up your development
environment.
Running the server
Run venv/bin/python server.py
to start the ssh server.
If everything works, you should see the following message:
Listening for connection ...
To run the grader, open a new shell (separate from the shell that is running the server) and run:
venv/bin/python grader.py
It might be helpful for you to enable paramiko’s debug-level log messages. The following lines of code will enable logging:
import logging
logging.basicConfig()
logging.getLogger("paramiko").setLevel(logging.DEBUG)
Part (a): Compress then encrypt
The SSH protocol supports compression: the client first compresses the data it wants to send to the server, and then the client encrypts it. In this problem, you will see how an attacker can abuse compress-then-encrypt to decrypt encrypted traffic. This attack theoretically works against real SSH implementations.
The code in grade_decrypt
of
ssh/grader.py
chooses the names of three capital cities at
random and puts them into a JSON object like this:
{
"city0": "Victoria, Seychelles",
"city1": "Seoul, South Korea",
"city2": "Paris, France",
}
The grader stores this object in a string called
secret
, and then sends it via SSH to the SSH server.
As the attacker in this problem, you are able to
somehow convince the SSH client to send a string
evil
of your choosing alongside the string secret
.
So the string that the SSH client sends to the
server is actually evil+secret
.
(This scenario can occur in a number of contexts.
On the web, for example, a malicious website can trick a client
into sending HTTPS requests that combine
attacker-controlled fields with client secrets.)
Your task:
You must implement the function attack_decrypt
in
ssh/attack.py
.
Your code may call client_fn
many times as:
(bytes_out, bytes_in) = client_fn("evil string")
This instructs the SSH client to send the string
"evil string" + secret
to the SSH server over
the encrypted channel. The function returns the
number of bytes sent to the server (bytes_out
)
and bytes received from the server (bytes_in
)
during the client-server interaction.
Your job is to recover the string secret
exactly.
NOTE: Your attack does not need to succeed with probability 1, you just need to convince the grader to accept your solution. To test your attack, please use the Lab 3: 3A Testing.
Part (b): Tampering with packets
A standard SSH implementation applies
a MAC to each SSH-protocol packet.
In this part, you will attack a broken
implementation of SSH that does not use MACs at all.
(More precisely, our implementation uses a MAC that always
outputs a 160-bit string of zeros.
The code for this no-op MAC is in ssh/opts.py
.)
The code in grade_tamper
of
ssh/grader.py
starts an SSH client, connects to the SSH server,
and sends the shell command ls ./files/*
to the server.
Your task: You will play the role of an
in-network attacker that tampers with the SSH packets
as they flow from the client to the server.
Your task is to cause the server to receive the
shell command rm -r /
, instead of the command
that the client intended to send..
In particular, you must implement the function
AttackTamper.handle_data
in ssh/attack.py
.
The grader code will invoke your handle_data
function each time the SSH client sends data
to the SSH server. The function takes as input the
raw bytes of the data that the SSH client sent and it outputs
whatever bytes the attacker wants to relay to the SSH server.
If you want to save state across invocations of
handle_data
, you can store it as members of AttackTamper
.
(That is, handle_data
can set self.blah = 7
to
store the value of blah
.)
If the SSH server receives
the special string rm -r /
,
it will reply to the client with a special message
that the grader will use to determine that your
attack has worked.
You may find it helpful to read a bit about the SSH protocol in RFC 4253 and RFC 4254, though you should not need to go very deep into either to complete the problem.
Part (c): Extra credit
Repeat the attack of Part (b), except that now you
must mount the attack against an SSH client that
uses packet compression. Specifically, the details from the attack
in Part (b) are the same except we will now set compress=True
when
connecting to the server. Thus, the messages throughout the communication will remain the
same, but they will now be compressed before they are sent. The attack succeeds
if the SSH server
decompresses your message to the special string rm -r /
.