A hands-on approach to symmetric-key encryption

In this article, we will learn how symmetric-key encryption works from a practical perspective.

In the “Introduction to encryption for embedded Linux developers” article, we learned the basic concepts, including an introduction to security, confidentiality and encryption, the main motivations and how encryption works, types of encryption (symmetric-key and asymmetric-key encryption), the most commonly used ciphers and the trade-offs between them.

In this article, we will use OpenSSL to put into practice some concepts about symmetric-key encryption.

To follow along with this article and run the commands on your machine, you just need a terminal with a recent version of OpenSSL.

Have fun! :-)

Symmetric-key encryption

In a symmetric-key encryption algorithm, the same key is used for both encryption and decryption. This key is usually called a private key, and should stay private, or the security is broken!

There are several symmetric-key encryption algorithms available (DES, AES, IDEA, Blowfish, RC4, etc), and currently the most commonly used symmetric algorithm is AES (Advanced Encryption Standard).

AES is a standard set by the U.S. National Institute of Standards and Technology (NIST) in 2001 for the encryption of electronic data and became effective as a U.S. federal government standard in 2002. It is also the first (and only) publicly accessible cipher approved by the U.S. National Security Agency (NSA) for top-secret information. I will not judge if this is good or bad. :-)

AES is a block cipher and works with blocks of 128 bits. That means it will break the data to be encrypted (cleartext) in blocks/chunks of 16 bytes, encrypt them (block by block), and join the encrypted blocks together to form the ciphertext.

AES keys might have three different lengths (128, 192 or 256). Usually, longer keys improve security, especially against brute-force attacks (though sometimes that might not be true).

On the other hand, the execution might be slower with larger keys. AES for example will execute more rounds with larger keys. Currently, a 128 bits key in AES is considered to be a safe compromise between efficiency and security.

So let’s start by creating a 128 bits key for AES encryption.

Generating a symmetric key

An encryption key is just a random string of bits created explicitly for scrambling and unscrambling data. The more random, the better. That is one of the reasons a good random number generator is very important in cryptography.

We could create a key manually by typing any combination of 128 bits (for the purpose of this article, we don’t care much about the randomness of the key). Anyway, it is easier to do this with a tool like OpenSSL:

$ openssl enc -pbkdf2 -aes-128-ecb -k my-secret-passphrase -P

This command is asking OpenSSL to create a 128-bit AES key (-aes-128-ecb) using a password-based key derivation function (-pbkdf2) and print the key in the terminal (-P).

The password-based key derivation function used is called PBKDF2. This algorithm will apply a hash function to the passphrase given (-k my-secret-passphrase) along with a salt value and repeats the process many times to produce a derived key.

PBKDF2 algorithm

The salt is just random data that is used as an additional input to the hash function. Without it, the same passphrase would always produce the same key!

Now save the derived key in a file:

$ echo 4A48195DFA143D17F44AB817C831FACF > key.priv

And we are ready for some encryption!

Encryption with AES-128

Let’s encrypt a snippet from The Hacker Manifesto, a small essay written in 1986 by a computer security hacker who went by the pseudonym of The Mentor:

$ cat cleartext.txt
This is our world now... the world of the electron and the switch, the
beauty of the baud.  We make use of a service already existing without paying
for what could be dirt-cheap if it wasn't run by profiteering gluttons, and
you call us criminals.  We explore... and you call us criminals.  We seek
after knowledge... and you call us criminals.  We exist without skin color,
without nationality, without religious bias... and you call us criminals.
You build atomic bombs, you wage wars, you murder, cheat, and lie to us
and try to make us believe it's for our own good, yet we're the criminals.

Yes, I am a criminal.  My crime is that of curiosity.  My crime is
that of judging people by what they say and think, not what they look like.
My crime is that of outsmarting you, something that you will never forgive me

I am a hacker, and this is my manifesto.  You may stop this individual,
but you can't stop us all... after all, we're all alike.

The command below will encrypt the file using AES-128 (enc -aes-128-ecb), taking the input file provided by the -in option, encrypting it with the key given in the -K option and creating an encrypted file called ciphertext.txt via the -out option. Easy, right?

$ openssl enc -aes-128-ecb -in cleartext.txt -K $(cat key.priv) -out ciphertext.txt

Now if you try to print the encrypted file (ciphertext.txt), you will just see garbage.

In the same manner, we can decrypt the file by passing the -d option:

$ openssl enc -d -aes-128-ecb -in ciphertext.txt -K $(cat key.priv)
This is our world now... the world of the electron and the switch, the
beauty of the baud.  We make use of a service already existing without paying
for what could be dirt-cheap if it wasn't run by profiteering gluttons, and

There is just one problem with what we are doing here. We are using a block cipher mode called ECB (Electronic codebook). The ECB mode leaks information and it is very insecure!

So let’s learn now about block cipher modes and why ECB is so insecure…

Block cipher modes and ECB

Before explaining the problem, let me show how insecure the ECB mode is.

For the example, let’s take our lovely TUX image:

Tux Linux mascot

Download the image and convert it to PPM to make it easier to manipulate the header and the body of the image:

$ wget https://upload.wikimedia.org/wikipedia/commons/a/af/Tux.png
$ convert Tux.png tux.ppm

Now break down the file into two parts, the header and the body:

$ head -n 3 tux.ppm > header.txt
$ tail -n +4 tux.ppm > body.bin

Encrypt the body with AES-128 in ECB mode and add back the header to create a new image with the body encrypted:

$ openssl enc -aes-128-ecb -in body.bin -K $(cat key.priv) -out body.ecb.enc
$ cat header.txt body.ecb.enc > tux-ecb-enc.ppm

Open the encrypted image (tux-ecb-enc.ppm) and see the result for yourself!

Tux Linux mascot encrypted AES ECB

Can you see that Tux is still there? But why?

As mentioned before, AES is a block cipher that works with blocks of 128 bits. In ECB mode, it will encrypt individually the blocks. Blocks with the same content will output the same value!

AES ECB mode

Although the image is encrypted and we don’t know its contents, we still can see data patterns. That means the ECB mode leaks information, it is very insecure and you should never use it!

If you look at this Wikipedia page, you can see that there are different modes of operation for block ciphers. One of the most commonly used modes is CBC (Cipher Block Chaining).

AES encryption in CBC mode

In CBC mode, one encrypted block (ciphertext) is used as input to encrypt the next block. For the first block, we need an Initialization Vector (IV), a random and unique number usually the same size as the block.

AES CBC mode

The Initialization Vector (IV) can be generated with the rand function of OpenSSL:

$ openssl rand -hex 16 > iv.txt
$ cat iv.txt

And now the image can be encrypted by passing the -aes-128-cbc mode and the Initialization Vector with the -iv option:

$ openssl enc -aes-128-cbc -in body.bin -K $(cat key.priv) -iv $(cat iv.txt) -out body.cbc.enc

Now add the header to the encrypted image:

$ cat header.txt body.cbc.enc > tux-cbc-enc.ppm

And open the encrypted image (tux-cbc-enc.ppm) to confirm that information is not leaking anymore (the image will look like garbage):

Tux Linux mascot encrypted AES CBC

In this article, we learned some concepts about symmetric encryption, using AES in ECB and CBC modes to encrypt and decrypt data. In the next article, we will deep dive into asymmetric-key encryption and digital signatures.

See you there!

About the author: Sergio Prado has been working with embedded systems for more than 25 years. If you want to know more about his work, please visit the About Me page or Embedded Labworks website.

Please email your comments or questions to hello at sergioprado.blog, or sign up the newsletter to receive updates.

See also