Skip to content

Session 1: Introduction & Cryptography

header

Class page | Class recording

Assignments

Choose one or more of the assignments below.

Optional Advanced questions

  • Research how RSA creates its private key.
  • Research why faster factoring would break RSA.
  • Research why the industry is moving away from RSA and other older public-key systems.

Class

The session started with a welcome from Neil Gershenfeld and an overview of the Fab Futures program and how it developed.

Hamed Okhravi then introduced cybersecurity through several historical examples, including the breaking of the Enigma cipher during World War II, the Stuxnet attack against Iran's nuclear program, and hardware surveillance used by the Soviet Union. One of the main themes was that cybersecurity is much broader than computers and networks, spanning cryptography, software, hardware, and human psychology.

Radia Perlman followed with an overview of modern cryptography, covering secret key cryptography, public key cryptography, hash functions, digital signatures, certificates, and public key infrastructure (PKI).

The session finished with Julian and Jean explaining the class structure, tools, setup process, and assignments for the coming weeks.

Setup

My first step was to get both the documentation workflow and the class tools working.

Documentation

I was already familiar with Git and MkDocs, so setting up the documentation workflow was straightforward. I cloned my repository to my laptop and modified the template for the cybersecurity course.

I decided to switch from MkDocs to Zensical. The workflow is very similar to MkDocs Material, and since it is developed by the same team the migration was simple. All I had to do was update the requirements file, add a zensical.toml configuration file, and make a few changes to the CI pipeline.

The complete Zensical setup can be found in the repository for this project.

Setup VPN Access

For the class, a private network has been set up for the experiments and virtual machines. To access it, I used WireGuard and imported a personal configuration file that was provided.

At this point I had to refresh my VPN knowledge a bit. I had used VPNs before through GUI and only on Windows, but never really looked at what was happening underneath. As I understand it, a VPN creates a secure connection to another network. In this case it allows my computer to reach the course virtual machine, which is not directly accessible from the Internet.

Step 1 - Configure and Connect

To set up the VPN I ran:

sudo wg-quick up ./FFVPN-hanndoddi.conf

wg setup

This command both configured the WireGuard interface and connected to the VPN.

Step 2 - Verify the Connection

I initially thought there would be a separate connection step, but wg-quick up had already connected the VPN.

To check the status of the connection:

sudo wg

check wg

To disconnect:

sudo wg-quick down ./FFVPN-hanndoddi.conf

wg down

I also learned that WireGuard configuration files are often stored in /etc/wireguard, although using the file directly from my working directory was sufficient for getting started.

With the VPN connected, I was ready to try connecting to the virtual machine over SSH.

Accessing the VM

Now that I had a connection, I wanted to try both the Desktop VPN Access and Terminal Access options.

Desktop VPN Access

On the Tools dashboard there was a link to open NoVNC in the browser. Clicking it prompted me for a password. After entering the password, the remote desktop opened without any issues and I was greeted by a friendly little blue mouse on the desktop.

vnc

I already had TigerVNC installed, so I also tested connecting with that. It worked as expected and provided access to the same desktop environment.

Terminal Access

Again, the Tools dashboard provided clear instructions for connecting to the VM over SSH. At first I thought I would need to install my public SSH key on the machine, but I quickly realized a password had already been provided.

In the terminal I typed:

ssh ubuntu@<ip-address>

After accepting the server fingerprint and entering the same password (don't tell anyone), I was connected to the VM and ready to start exploring.

terminal ssh

Assignments work

Now that I had done most of the setup I began working on the asssignemnts. We got a long list of things to try out. Idealy I would like to try as mutch as I can.


=> Public and Private Key Pairs

I have made key pairs before to access servers, GitHub, and GitLab, but I never really paid much attention to how they work. When generating them, the choice is usually between RSA or Ed25519. Here is a breakdown of the differences:

Feature Ed25519 (Recommended) RSA
Security Level High (Equivalent to RSA ~3072+) Medium-High (If 3072-bit or higher)
Key Size 256 bits (Compact) 3072 or 4096 bits (Large)
Performance Extremely Fast Slower (Resource intensive)
Compatibility Modern systems (OpenSSH 6.5+) Legacy systems / Older hardware

The ssh-keygen tool is used to generate the key pair with this command:

ssh-keygen -t ed25519 -C "my_email@example.com"

Note: If you are connecting to a legacy system that does not support Ed25519, you can fall back to RSA with a 4096-bit length: ssh-keygen -t rsa -b 4096 -C "my_email@example.com"

Running this command creates two files: a private key and a public key.

  • The Public Key (.pub): This file ends with .pub and is safe to share with platforms you want to access (like GitHub or a remote server).
  • The Private Key (No extension): This file has no file extension. It stays strictly on your local system. It must never be shared. If someone gets your private key, it is exactly like giving them a physical key to your home.*

Making a Key Pair

I decided to generate a dedicated key pair specifically for the class VM. Following best practices, these keys will be stored locally within the hidden ~/.ssh directory.

I ran the following command on my local host machine:

ssh-keygen -t ed25519 -f ~/.ssh/cyberclass_id -C "ubuntu-vm-cyberclass-hanndoddi"
  • -t ed25519: Specifies the modern, fast Ed25519 algorithm.
  • -f ~/.ssh/cyberclass_id: Saves the keys with a custom name (cyberclass_id) inside the standard SSH directory.
  • -C "ubuntu-vm-cyberclass-hanndoddi": Adds a descriptive plain-text comment to identify the key holder.

When generating the key, there is an option to enter a passphrase for extra security. If set, you must enter this passphrase every time you use the key, leaving it empty will skip it. Below is a screen-shot of the progress:

keygen

keys

The .ssh directory now contains both files: the private key (cyberclass_id) and the public key (cyberclass_id.pub).

Common Use Cases

This public/private key pair can be used for several critical cryptographic functions:

  • Secure Remote Access (SSH): Logging into remote servers or virtual machines without needing a password.
  • Code Repository Authentication: Authenticating securely with Git hosting platforms like GitHub, GitLab, and Bitbucket.
  • Digital Signatures: Signing Git commits or code releases to prove that you were the actual author and that the code hasn't been altered.

=> Encrypt and decrypt a file

My colleague and friend Andri, who is also taking the course, suggested that we do this exercise together. The idea was that I would create a public/private key pair and send him my public key so he could encrypt a file that only I could decrypt.

For this exercise I used GPG, which I had never used before. The concept felt similar to SSH, where a public and private key pair is used, but instead of authenticating to a server, the keys are used to encrypt and decrypt files.

Making a GPG key

With gpg --list-keys I can get a list of my keys. Since I had never used GPG before, there were none.

There were some default files in ~/.gnupg, but unlike ~/.ssh you generally do not work directly in that directory. Instead, I created a separate working directory for the exercise:

~/dev/sandbox/cyber

To generate a key pair I typed:

gpg --full-generate-key

This opened an interactive prompt where I selected the following options:

  • 9 for ECC (sign and encrypt)
  • 1 for Curve 25519
  • 0 for the key to never expire
  • Enter a real name and email address
  • Leave the comment field blank
  • Press O to confirm
  • Enter a passphrase when prompted

gpg keygen

After the key was generated, I ran gpg --list-keys again and could see my new key in the list.

The next step was to export the public key:

gpg --armor --export doddi@doddi.is > doddi-public.asc

This created a file called doddi-public.asc containing my public key. The file is safe to share publicly and can be used by others to encrypt files that only I can decrypt with my private key.

gpg list

I then added my public key to the documentation repository so Andri could access it and send me an encrypted file.

If anyone would like to send me an encrypted message in the future, my public key is available here:

doddi-public.asc

Decrypting a file

After sending Andri my public key, he encrypted an image and sent me the file image.png.gpg.

To decrypt it I used:

gpg --decrypt image.png.gpg > image.png

GPG prompted me for the passphrase protecting my private key. After entering it, the file was successfully decrypted and the original image was restored.

The decrypted image is shown below.

decrypt image

Encrypting a file

Andri sent me his public key so I could encrypt a file for him. I decided to encrypt a photo.

First I had to import his public key:

gpg --import andri-public.asc
After importing the key, gpg --list-keys showed two keys in my keyring: my own key and Andri's key.

I could then encrypt the image using:

gpg --encrypt --recipient "Andri" secret-image.jpeg

During the process GPG warned that there was no guarantee the key actually belonged to Andri. For this exercise I accepted the warning because I had received the key directly from him.

The command generated the following file:

secret-image.jpeg.gpg

Only someone with the matching private key should be able to decrypt the file.

To see the original image, check out Andri's documentation page.

Symmetric Encryption

Andri also sent me a file encrypted using symmetric encryption. The main difference is that anyone who knows the passphrase can decrypt the file.

The weak point of this method is how to securely share the passphrase. Do you send it over Discord, SMS, email, or even a postcard?

In this case the passphrase was simply sent over Discord. To decrypt the file I used:

gpg -o decrypted_neilG.png --decrypt neilG.png.gpg

From the filename you might be able to guess what was in the file.

To encrypt a file using symmetric encryption you can use:

gpg -c image.png

This will prompt for a passphrase and create an encrypted version of the file. Anyone with the same passphrase can later decrypt it.

Lost Keys or Passphrase

Looking into this, it seems to happen more often than you might think. There are plenty of forum posts from people who have lost access to encrypted files because they forgot a passphrase or misplaced a key.

In short, if a private key or passphrase is lost, there is usually no way to recover the encrypted data. Some people suggest trying to find old backups of the key material in ~/.gnupg, while others mention brute-force attacks against weak passphrases. However, neither approach is guaranteed to work.

The lesson is simple: remember your passphrases, make secure backups of important keys, and keep them somewhere safe.


=> Installing an SSH Public Key on a Server

Currently, every time I connect to the class VM via SSH, I am prompted for a password. To streamline the login process and increase security, I want to install my newly created public key on the machine to enable passwordless authentication. Once that is verified, I plan to disable password logins to make it more secure.

To begin this process, I first enabled the WireGuard VPN on my local machine to establish a secure tunnel to the class network. Once the VPN connection was active, I was able to communicate with the remote server.

Manual Installation Way

Since I was already logged into the VM terminal, I decided to install the public key manually.

First, on my local machine's .ssh directory, I viewed the contents of my public key using the cat command so I could copy the exact string:

cat ~/.ssh/cyberclass_id.pub

cat command output

Next, on the remote VM, I opened the authorized_keys file inside the ~/.ssh/ directory using a text editor. I pasted the complete public key string into the file—which was previously empty—and saved the changes.

VM text editor key

To verify that the manual key installation was successful, I tested the passwordless login from my local machine using the -i flag to point directly to my custom-named private key:

ssh -i ~/.ssh/cyberclass_id ubuntu@<vm ip address>

As shown in the screenshot below, the handshake succeeded instantly, granting me secure terminal access to the Ubuntu 24.04 server environment without prompting me for an account password:

cm ssh con

Quality of Life Improvements

Alternatively, the initial public key deployment could have been performed automatically from the local machine with a single-line command using ssh-copy-id:

ssh-copy-id -i ~/.ssh/cyberclass_id.pub ubuntu@<ip address>

Furthermore, to avoid having to type out the full identity path and IP address every time I connect, I can create a local config file inside the ~/.ssh/ directory on my personal computer and add the following block:

Host cyberclass
    HostName <ip address>
    User ubuntu
    IdentityFile ~/.ssh/cyberclass_id

With this local configuration file saved, I can securely connect to the class VM in the future using a single, simplified command:

ssh cyberclass

=> Hash Again

I know that hashes are commonly used to store and verify passwords. It is convenient because when generating the hashes, they are always the same amount of bits (fixed length). Hashing is also a one-way operation, meaning it cannot be reversed to reveal the original data.

The command sha256sum is used to generate hashes, and there is also sha512sum, which is more secure.

First, I made a file using echo "hello" > test.txt and then generated the hash using:

hash  $ sha256sum test.txt 
5891b5b522d5df086d0ff0b110fbd9d21bb4fc7163af34d08286a2e846f6be03  test.txt

I got a string output from that command. Next, I opened the file and changed the text to a single capital letter, "Hello". I ran the command again and got this:

hash  $ sha256sum test.txt
66a045b452102c59d840ec097d59d9467e13a3f34f6494e539ffd32c1bb35f18  test.txt

From that output, I could see that the string was completely different just by changing one letter. I also tried changing the text back to the lowercase letter "hello" again to see if it would generate the exact same string I got at first, which it did.

hash

=> Signing a File

To try out GPG signatures, I decided to create a file, sign it, duplicate it, and then tamper with the duplicate. Since Andri already has my GPG public key, I could share both files with him and see if he could determine which one was the original. I'm also curious whether having my public key is actually required for this, so that's something I'll look into as well.

First, I created a file in my sandbox directory:

echo "<secret-text>" > neo.txt

I then created a detached armored signature:

gpg --detach-sign --armor neo.txt

After entering my passphrase, GPG created a signature file named neo.txt.asc.

Next, I duplicated neo.txt and tampered with the copy by changing its contents. I pushed all of the files to my repository, which you can find here.

It was now up to Andri to figure out which file was the original. If you're curious about the outcome, take a look at his documentation and see whether he was able to identify the authentic file.

To verify a signature this command is used:

gpg --verify <file.asc> <file>

Below is the outcome to see what was the original file:

sign good bad

  • Looking into if a public key is actually required to verify the signature the answer is that it does. Without it, GPG can't confirm that the signature.

< in progress more to come >

=> Encrypt a filesystem

I decided to do this assignment on my virtual machine. Since I am on a VM, using a physical USB stick isn't an option. At first, I thought I could use GPG to encrypt a folder, but GPG is only for individual files. Compressing a directory into a tarball and then encrypting it was an option, but that wasn't exactly what I was after.

Looking at what was available on the VM, I found cryptsetup, which is the standard tool for Linux disk encryption. Since I am working inside a safe virtual machine environment, I figured it was the perfect place to experiment and see what could go wrong!

To start, I needed a safe testing area, so I searched for "linux how to make an experimental disk space" and found the fallocate tool. I combined fallocate with cryptsetup using this recipe:

# 1. Create a 100MB empty file to act as the virtual drive
fallocate -l 100M secret.img

# 2. Format the file as an encrypted LUKS device (prompts for a password let's make it 1234)
sudo cryptsetup luksFormat secret.img

# 3. Unlock the file and map it to a virtual device mapping named "secret_disk"
sudo cryptsetup open secret.img secret_disk

# 4. Create an ext4 filesystem inside the unlocked device mapping
sudo mkfs.ext4 /dev/mapper/secret_disk

# 5. Create a mount point and mount it to access the secure space
sudo mkdir -p /mnt/secure_test
sudo mount /dev/mapper/secret_disk /mnt/secure_test

# 6. Unmount and close the device so nobody can read or access it
sudo umount /mnt/secure_test
sudo cryptsetup close secret_disk

As I understand it, this creates a temporary disk space that I can mount to view and edit files. The moment I unmount it and close the cryptsetup it locks down and leaves behind nothing but scrambled data.

Following the recipe, the disk space was successfully created and encrypted using LUKS. The system prompted me to set a passphrase during the setup, and in step 3, required that same passphrase to unlock and mount it. Once authenticated, I accessed the secure space, adjusted the permissions, and created a test text file. Afterward, I unmounted the filesystem and closed the device mapping. The data vanished instantly, leaving the /mnt/secure_test directory completely empty as if nothing had ever been there. Below are some screenshots detailing the experiment:

open disk

close disk

I tried opening it again and the files were back.

=> Certificate of an HTTPS Website.

I have my site hanndoddi.com and I wanted to look into the Cert for it. I could check it in browser but I also wanted to see if it could be done in the terminal. I found out it can be done openssl

< still in progress >