This assignment requires knowledge from Network Security and basic knowledge in C.
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. Please do not confuse the materials, you don't need to know materials in Week 11 and 12 before getting started.
This assignment requires gcc (or clang) and the OpenSSL development headers.
# 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-develRun this in the root project directory:
chmod +x ./setup.sh
./setup.sh
This will create 3 directories: /recv_files, /recv_files_enc, and /send_files_enc in the project root. They are all empty directories that can't be added in .git.
make
This compiles all source files. The Makefile automatically detects macOS vs Linux and finds the OpenSSL headers.
In two separate shell sessions, run (assuming you're in root project directory):
./ServerWithoutSecurity
and:
./ClientWithoutSecurity
You can also host the Server file in another computer:
./ServerWithoutSecurity [PORT] 0.0.0.0The client computer can connect to it using the command:
./ClientWithoutSecurity [PORT] [SERVER-IP-ADDRESS]This project ships with two layers of tests:
- Unit tests in
tests/unit/. Small C programs that exercise pure helper functions directly, using the Unity framework. Each test binary is one.cfile plustests/unity/unity.cplussource/common.c(and optionally a pairedsource/<name>.cif you have extracted one). - Integration tests in
tests/integration/. Bash scripts that build./ClientWithoutSecurityand./ServerWithoutSecurity, drive a real client / server pair over a loopback socket, and verify that received files match the originals byte-for-byte.
Run all tests:
make testRun only unit tests:
make unitRun only integration tests (depends on NoSec, which is built automatically):
make integrationThe integration server binds to port 14321 by default. Override it if that port is in use:
PA2_TEST_PORT=15555 make integrationTwo patterns are supported:
- If you extract a pure helper into
source/<name>.handsource/<name>.c, write its tests intests/unit/test_<name>.c. The Makefile links them together along withsource/common.cand Unity. - To add more tests for the bundled helpers in
common.c, createtests/unit/test_<group>.cwith a name that does not match anysource/<group>.c. The Makefile falls back to linking the test withsource/common.cand Unity only. Existing examples:test_int_bytes.c,test_socket.c.
Naming tip: avoid test_common.c. The Makefile uses $(sort) to dedupe in that case, but splitting tests by functional area (test_session_crypto.c, test_rsa_sign_verify.c, etc.) produces a cleaner suite.
Integration scripts live in tests/integration/test_*.sh. Each script:
- Sources
tests/integration/_lib.sh(providesstart_server,stop_server,reset_recv_files,dump_logs,cleanup). - Sets
trap cleanup EXIT. - Calls
reset_recv_filesandstart_server. - Drives the client, then asserts on the contents of
recv_files/.
The _lib.sh helper waits for the server to bind by attempting to bind the same port itself; when the bind fails with EADDRINUSE, the server is up. This avoids the buffering and TCP-probe pitfalls described in the comments inside _lib.sh.
This project includes an optional wrapper for drafting unit tests with an AI agent. After writing a helper, you can run:
make ai-unit-tests MODULE=common # tests for the bundled helpers
make ai-unit-tests MODULE=<your_helper> # tests for a helper you extractedThis invokes scripts/gen_unit_tests.sh, which builds a prompt from prompts/generate-unit-tests.md, AGENTS.md, and the named module's header + source. The script either pipes the prompt to your configured agent (via the PA2_AGENT_CMD environment variable) or prints it to stdout for you to paste into a chat interface. See AGENTS.md for setup details and the rules agents follow.
You remain responsible for every test in your submission. You must be able to explain each test during checkoff.
This is just for quality of life. We use VSCode as example.
If common.h shows red squiggly lines on #include <openssl/rsa.h> and similar lines in VS Code, the editor's C extension cannot find the OpenSSL headers. The compiler can (the Makefile handles that), but the IntelliSense engine reads a separate config.
Create .vscode/c_cpp_properties.json in the project root with the include paths for your platform.
Apple Silicon Macs (M1, M2, M3, M4), Homebrew installs to /opt/homebrew:
{
"configurations": [
{
"name": "Mac",
"includePath": [
"${workspaceFolder}/**",
"/opt/homebrew/opt/openssl/include"
],
"defines": [],
"compilerPath": "/usr/bin/clang",
"cStandard": "c11",
"intelliSenseMode": "macos-clang-arm64"
}
],
"version": 4
}Intel Macs, Homebrew installs to /usr/local:
{
"configurations": [
{
"name": "Mac",
"includePath": [
"${workspaceFolder}/**",
"/usr/local/opt/openssl/include"
],
"defines": [],
"compilerPath": "/usr/bin/clang",
"cStandard": "c11",
"intelliSenseMode": "macos-clang-x64"
}
],
"version": 4
}Linux (Ubuntu / Debian / Fedora), OpenSSL headers ship under /usr/include/openssl, which is on the default system include path. Usually no config is needed. If squiggles persist:
{
"configurations": [
{
"name": "Linux",
"includePath": ["${workspaceFolder}/**", "/usr/include"],
"defines": [],
"compilerPath": "/usr/bin/gcc",
"cStandard": "c11",
"intelliSenseMode": "linux-gcc-x64"
}
],
"version": 4
}If you don't know which Mac you have, run brew --prefix openssl in the terminal. That prints the actual include parent (append /include for the JSON above). On Apple Silicon you will see /opt/homebrew/opt/openssl; on Intel you will see /usr/local/opt/openssl. Both are symlinks into Cellar/openssl@3/<version>/, so pointing at the opt/openssl symlink is more stable than hardcoding the versioned Cellar path.
Reload the VS Code window after editing (Cmd/Ctrl+Shift+P, then "Developer: Reload Window") for IntelliSense to pick up the change.
This only affects the editor. The Makefile already detects Homebrew's prefix via brew --prefix openssl and passes the include path to the compiler, so make works regardless of whether you set this up.