Skip to main content Link Search Menu Expand Document (external link)

50.005 Computer System Engineering
Information Systems Technology and Design
Singapore University of Technology and Design
Natalie Agus (Summer 2026)

Programming Assignment 2

Main Task

In this assignment, you will implement a secure file upload application from a client to a secure file server. You will design and code the secure File Transfer Protocol (FTP) into the application.

Secure means that it fulfiles these three requirements simultaneously:

  1. First, before you do your upload as the client, you should authenticate the identity of the file server so you won’t leak your data to random entities including criminals.
  2. Secondly, you want to ensure that you’re talking to a live server.
  3. Thirdly, while carrying out the upload you should be able to protect the confidentiality of the data against eavesdropping by any curious adversaries.

You may complete this assignment in groups of 2-3 pax. Indicate your partner’s name in the google sheet provided in our course handout.

There are three parts of this assignment:

  1. Authentication Protocol (AP)
  2. Confidentiality Protocol 1 (CP1)
  3. Confidentiality Protocol 2 (CP2)

These three parts form a strict Secure File Transfer protocol. You will be using socket programming (from the first half of the term) and cryptography knowledge (from the second half of the term) to complete this assignment.

Secure FTP != HTTPS

Note that you will be implementing Secure FTP as your own whole new application layer protocol. In NO WAY we are relying on HTTP/s.

There seem to be some ridiculous misunderstanding from your seniors in the past years that this assignment requires knowledge on HTTP/s and/or DNS. It’s totally two different protocol even though they are both application layer protocol. HTTPS is not equal to our SFTP, they are unrelated, as unrelated as 🍊 fruit is to 🌹 flower.

System Requirements

The starter code provided to you is written in C and uses the OpenSSL library (libssl, libcrypto) for all cryptographic operations. You need:

  • A C compiler (gcc or clang)
  • OpenSSL development headers
  • A POSIX-compliant OS (Linux or macOS)

Install the dependencies:

# Ubuntu / Debian
sudo apt-get install build-essential libssl-dev

# macOS (Homebrew) — the Makefile auto-detects the path
brew install openssl

# Fedora / RHEL
sudo dnf install gcc openssl-devel

Starter Code

You should have joined the GitHub Classroom and obtained the starter code for this assignment there. The link can be found in the Course Calendar portion of your Course Handout.

PA2 Files

Anything under source/ is where you will work for this assignment. All files in the same level as source/ are for admin purposes. Do not modify these.

.[PROJECT_DIR]
├── files
│   ├── cbc.bmp
│   ├── file.txt
│   ├── image.ppm
│   ├── jsim.jar
│   ├── player.psd
│   ├── squeak.wav
│   ├── vscodejsim.mp4
│   └── week9.html
├── Makefile
├── README.md
├── recv_files
├── recv_files_enc
├── send_files_enc
├── setup.sh
└── source
    ├── auth
    │   ├── cacsertificate.crt
    │   └── generate_keys.sh
    ├── ClientWithoutSecurity.c
    └── ServerWithoutSecurity.c

Run ./setup.sh

[PROJ_ROOT_DIR]/recv_files, [PROJ_ROOT_DIR]/recv_files_enc, and [PROJ_ROOT_DIR]/send_files_enc are all empty directories that are not added in .git. To create them, simply run ./setup.sh in the project root.

You should see exactly the above file structure afterwards. ./setup.sh is a bash script, in order to execute it you must chmod it to be executable first.

Custom Crypto Library common.c

All complex OpenSSL calls are wrapped for you in common.h / common.c. You call these wrapper functions and you do NOT need to write raw OpenSSL EVP_* code yourself.

Here is the mapping between the Python functions you may be familiar with and the C wrapper functions provided:

Functionality C function (in common.h)
Generate random bytes RAND_bytes(buf, len)
Send 8-byte int send_int(sockfd, val)
Send raw bytes send_all(sockfd, buf, len)
Read exact bytes read_bytes(sockfd, len), must free()
Convert 8B buf to int bytes_to_int(buf)
Parse cert from bytes load_cert_bytes(data, len), must X509_free()
Verify cert vs CA verify_server_cert(cert, ca_path), returns 1 or 0
Verify RSA-PSS signature verify_message_pss(cert, sig, sig_len, msg, msg_len), 1/0
Sign with RSA-PSS sign_message_pss(key, msg, len, &sig_len), must free()
Load private key load_private_key(path), must EVP_PKEY_free()
Extract pub key from cert X509_get_pubkey(cert), must EVP_PKEY_free()
RSA encrypt one block rsa_encrypt_block(key, pt, pt_len, &ct_len, use_oaep), must free()
RSA decrypt one block rsa_decrypt_block(key, ct, ct_len, &pt_len, use_oaep), must free()
Generate session key generate_session_key(key_buf)
Symmetric encrypt session_encrypt(key, pt, pt_len, &ct_len), must free()
Symmetric decrypt session_decrypt(key, ct, ct_len, &pt_len), must free()

Memory management in C

Unlike Python, C does not have garbage collection. Every function that returns a malloc()‘d buffer (marked “must free()” above) requires you to call free() on the returned pointer when you are done with it. Similarly, X509* objects must be freed with X509_free() and EVP_PKEY* with EVP_PKEY_free(). Failure to do so causes memory leaks. Read common.h carefully — each function’s documentation specifies who must free what.

The documentation for common.c library can be found here. Refer to it when completing this assignment.

Test the Starter Code

Build

Run the following command in the project root to compile:

make

The Makefile automatically detects macOS vs Linux and locates OpenSSL headers. If the build fails, ensure libssl-dev (Linux) or brew install openssl (macOS) is installed.

Using the same machine

The starter code provided to you implements a simple, non-secure file transfer protocol. We will explain in detail what the protocol is. For now, let’s just ensure that everything runs normally.

Run each of these commands in two separate shell sessions:

./ServerWithoutSecurity
./ClientWithoutSecurity

You can type in the filename you want to send, e.g files/image.ppm from the Client’s window, and the server will receive it and store it under [PROJECT_DIR]/recv_files directory.

You can repeat the above steps multiple times for each file you want to send to the server. If the client would like to close connection to the server, key in -1.

Using different machines

You can also host the Server file in another computer:

./ServerWithoutSecurity [PORT] 0.0.0.0

You can use any high port you want, e.g: 12345.

The client computer can connect to it using the command:

./ClientWithoutSecurity [PORT] [SERVER-IP-ADDRESS]

Ensure you use the same port. The server’s IP address (private) should be discoverable using the following if you use WiFi (en0), or en1 if you use ethernet:

# Linux
ip addr

# macOS
ipconfig getifaddr en0

To get this to work, you most probably need to be on the same subnet. Just ensure that both machines are connected to the same WiFi and subnet. If you use SUTD WiFi, there’s a chance that you are not in the same subnet. In that case, simply use your phone hotspot and both machines shall connect to it.

Example: Direct LAN / Local AP setup

You may try to create a local network using one Linux machine acting as a temporary Wi-Fi AP, or by directly connecting machines over Ethernet. This example assumes the Linux host uses NetworkManager via nmcli. If you use another network manager or a different OS, adapt the commands accordingly.

Different examples below intentionally use different private subnets to reduce accidental address conflicts if you directly reuse the commands for multiple setups. In each individual example, all participating machines must still be placed on the same subnet.

Example 1: Linux <–> Linux over RJ45 (nmcli)

Find your interface:

nmcli device status

Look for a device name like eth0, enp3s0 etc.

Assign static IPs on the same private subnet.

Host:

nmcli connection modify [connection name] \
ipv4.method manual \
ipv4.addresses 192.168.10.1/24
nmcli connection up [connection name]

Client:

nmcli connection modify [connection name] \
ipv4.method manual \
ipv4.addresses 192.168.10.2/24
nmcli connection up [connection name]

Example 2: Linux <–> Linux over RJ45 (no nmcli)

Find your interface:

ip link
ip addr

Look for a device name like eth0, enp3s0 etc.

Assign static IPs in the same private subnet.

Host:

ip addr add 192.168.20.1/24 dev eth0
ip link set eth0 up

Client:

ip addr add 192.168.20.2/24 dev eth0
ip link set eth0 up

Example 3: Linux host <–> Windows client over RJ45 (nmcli)

Find your interface:

nmcli device status

Look for a device name like eth0, enp3s0 etc.

Assign static IPs in the same private subnet.

Linux Host:

nmcli connection modify [connection name] \
ipv4.method manual \
ipv4.addresses 192.168.30.1/24
nmcli connection up [connection name]

Windows Client (Ethernet adapter -> IPv4):

  • IP: 192.168.30.2/24
  • Subnet: 255.255.255.0
  • Gateway: [leave empty]

Restore networking configuration

Linux (NetworkManager):

nmcli connection modify [connection name] ipv4.method auto ipv4.addresses ""
nmcli connection up [connection name]

Linux (manual):

ip addr flush dev eth0

Windows:

  • Set IPv4 back to “Obtain IP automatically”

Example 4: Linux Host (AP with nmcli)

Find your interface:

nmcli device status

Start AP:

nmcli device wifi hotspot \
ifname [change this to the device name e.g. wlan0 / wlp2s1] \
ssid [change this to network name e.g. pa2-lan] \
password [change this e.g. 12345678]

Modify the IP:

nmcli connection modify Hotspot ipv4.method manual ipv4.addresses 192.168.40.1/24
nmcli connection up Hotspot

Example 5: Linux Client (connect with nmcli)

Find your interface:

nmcli device status

Find/Verify the AP exists:

nmcli dev wifi

Connect to the AP:

nmcli device wifi connect [network name, e.g. pa2-lan] password [password, e.g. 12345678]

Connect with a static IP:

nmcli connection modify [connection name, usually the SSID] \
ipv4.method manual \
ipv4.addresses 192.168.40.2/24 \
ipv4.gateway 192.168.40.1
nmcli connection up [connection name, usually the SSID]

Example 6: Linux Client (connect with wpa_supplicant)

Create a minimal wpa_supplicant config file e.g. wpapa2.conf:

network={
    ssid="pa2-lan"
    psk="12345678"
}

Connect to the network defined in the config:

wpa_supplicant -i [wireless interface, e.g. wlan0] -c wpapa2.conf -B

Assign an IP on the same subnet as host:

ip addr add 192.168.40.3/24 dev [wireless interface, e.g. wlan0]
ip link set [wireless interface, e.g. wlan0] up

Restore Normal Connection

Host:

nmcli connection down Hotspot

Connect to usual Wi-Fi:

nmcli device wifi connect [some SSID] password [your password]

Remove the temporary hotspot profile if needed:

nmcli connection delete Hotspot

Restore DHCP if configured to static IP:

nmcli connection modify [connection name] ipv4.method auto ipv4.addresses "" ipv4.gateway ""
nmcli connection up [connection name]

Important Requirements

Do NOT Modify common.h / common.c

You are NOT allowed to modify common.h or common.c. These files contain all the cryptographic wrappers and socket helpers you need. You should only #include "common.h" in your new source files and call the functions declared there.

No other cryptographic libraries allowed

You are also not allowed to use any cryptographic library other than OpenSSL (which is what common.c already uses). You may use any standard C library headers (stdio.h, stdlib.h, string.h, etc.) as they are already included via common.h.