Sub Rosa

Lab setup

First, make sure you have completed the initial setup.

If you are part of a course

  1. Open Terminal. Run the update command to make sure you have the latest code.
    $ mwc update
  2. Move to this lab's directory.
    $ cd ~/Desktop/making_with_code/ny-csdf/unit2/lab_subrosa
    
  3. Enter this lab's shell environment.
    $ poetry shell
    

If you are working on your own

  1. Move to your MWC directory.
    $ cd ~/Desktop/making_with_code
    
  2. Get a copy of this lab's materials.
    git clone https://git.makingwithcode.org/mwc/lab_subrosa.git

In the previous lab, we explored how information can be encrypted to keep it secret. But what if you want to send encrypted messages to someone else? If you both know the secret number or word, then you can just encrypt your message, send it to them, and they can decrypt it. But if you're worried about your messages getting intercepted, how can you share the secret with them in the first place?

Sure, if you and your friend meet up for coffee once a week you could just agree on a secret in person. But this won't work when people are far away from each other, who don't have a way to securely share secrets, or who don't even know each other yet.

In this lab we will explore a brilliant solution to this problem: public key encryption. In public key encryption, you generate two paired keys at the same. One is public and is shared with the world. (For example, you might publish your public key on your website.) The private key is kept secret. Either key can be used to encrypt a message, and only the other key can be used to decrypt the message.

With public key encryption, if you want to send someone an encrypted message you can encrypt it using their public key. Then this message can only be decrpyted by someone with the private key. If you want to send someone a message and prove it's from you, you can encrypt it using your private key. Anyone can then use your public key to decrypt it, and they can be sure that the sender had the private key. The math behind this is beautiful, but we're not going to get into it in this lab.

Public key encryption is the main form of encryption used in computer systems today.

Public and private keys

The encryption module provides a real implementation of public key encryption using the PrivateKey and PublicKey classes.

PrivateKey has the following methods:

PublicKey has the following methods:

💻 Let's try them out in Python's interactive mode. First, practice creating private and public keys. As their names imply, the private key is kept secret and the public key is shared with everyone.

$ python -i encryption.py
>>> private = PrivateKey.generate()
>>> public = private.get_public_key()
>>> print(private)
...
>>> print(public)
...
>>> private.save('test_key.pem')
>>> PrivateKey.load('test_key.pem')

💻 Next, practice encrypting and decrypting messages. If you want to send someone a secret message, you can encrypt it using their public key. The result can only be decrypted using the matching private key. People publish their private keys on their websites, in email footers, or using key servers.

>>> message = "Would you like to eat lunch together?"
>>> secret = public.encrypt(message)
>>> secret
'KDNq8FVjnm9k3NOpiNs5JmzIaIwaTz6QCHz/xtCbPeFCmTNFDBBeURMFwomXNvrjkhIT...'
>>> private.decrypt(secret)
'Would you like to eat lunch together?'
>>> private2 = PrivateKey.generate()
>>> private2.decrypt(secret)
ValueError: Decryption failed

💻 Finally, practice signing a message with your private key. Signing a message is a way of proving you have the private key, while keeping the key itself a secret. To create a signature, choose any message (e.g. "It's me!") and encrypt it with the private key. Then if you send someone the message and its encrypted ciphertext, they can decrypt the message with the public key, and make sure that the decrypted message matches the original message.

>>> signature = private.sign("It's me!")
>>> signature
'CwxoTLCRfzG7l6FiTUe1hK0htZbXdEs+jOOG86xsw3Ss1emlhoivdAPWH48pT0E70YMU...'
>>> public.verify_signature("It's me!", signature)
>>> public2 = private2.get_public_key()
>>> public2.verify_signature("It's me!", signature)
cryptography.exceptions.InvalidSignature

Sub Rosa server

This lab's code contains a client and a server for an encrypted chat system called Sub Rosa. The server is a banjo app, which you can run locally, and which is also running live at https://subrosa.makingwithcode.org. Before we can use the server, we need to generate a pair of keys.

💻 Generate a pair of keys and save them into files.

$ python -i encryption.py
>>> private = PrivateKey.generate()
>>> public = private.get_public_key()
>>> private.save("subrosa_private_key.pem")
>>> public.save("subrosa_public_key.pem")

💻 Now create an account. Unfortunately, the username funkychicken5 is already taken, so you'll have to use something else. (The @ is a shortcut which means "read in the contents of this file.")

$ http post https://subrosa.makingwithcode.org/users/new name=funkychicken5 public_key=@subrosa_public_key.pem

💻 Check your messages--there's already one from the Sub Rosa administrator! The message is encrypted, so you'll need to use your private key to decrypt it.

$ http get https://subrosa.makingwithcode.org/messages name=funkychicken5

{
    "messages": [
        {
            "ciphertext": "MXGwPnazj0JK+tE6Qmi62FXQVTMAmlePPe1mtGE6Qb...",
            "recipient": "funkychicken5",
            "sender": "subrosa_admin"
        }
    ]
}

If you want to send a message to someone else, you'll need to look up their public key so you can use it to encrypt the message. Sub Rosa uses end-to-end encryption, which means you need to encrypt messages before you send them to the server, and the recipient will decrypt them on their own computer. The plaintext message is never sent over the Internet, and is never stored on the server.

💻 Look up a user's public key.

$ http get https://subrosa.makingwithcode.org/users name=funkychicken5

{
    "name": "funkychicken5",
    "public_key": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQ..."
}

Finally, you can send messages to other users, but this is a bit tricky to do from the command line as you need to generate a signature (to prove your own identity). We will implement message-sending using the client program in the next section.

Sub Rosa client

A partially-working SubRosa client program is provided.

💻 Look at the client program's help message. A username is required; you can optionally give a key file or a server URL.

$ python client/user_interface.py --help
usage: user_interface.py [-h] [-k KEY] [-s SERVER_URL] username

positional arguments:
  username              Existing or new username.

options:
  -h, --help            show this help message and exit
  -k KEY, --key KEY     Private key file. Will be created if it does not exist.
  -s SERVER_URL, --server-url SERVER_URL
                        Server URL. For example, use 127.0.0.1:5000 for a local server

💻 Use the client program to read your messages. Unfortunately, the client isn't quite finished--the encrypted messages we fetch from the server aren't being decrypted. Also, sending messages doesn't work.

$ python client/user_interface.py funkychicken5
Welcome to SubRosa.
What would you like to do?
1. See messages
2. Send a message
> 1
--------------------------------------------------------------------------------
0. From subrosa_admin:
MXGwPnazj0JK+tE6Qmi62FXQVTMAmlePPe1mtGE6QbcNXT41MPvjzxZbUCgAdrNnrk9YD...
================================================================================
What would you like to do?
1. See messages
2. Send a message
> 2
Username of recipient: funkychicken5
Message: Hello.
--------------------------------------------------------------------------------
Sorry, couldn't send a message to funkychicken5; this method isn't implemented yet.
================================================================================

Your task: