So What Modes Can You Have For Symmetric Key?

GCM, CBC, CFB8, CFB, OFB, GCM, CTR, and XTS

So What Modes Can You Have For Symmetric Key?

GCM, CBC, CFB8, CFB, OFB, GCM, CTR, and XTS

Symmetric key encryption is the workhorse of cybersecurity. Its main task is to preserve the privacy of data, but sometimes it can also be used to authenticate the message (using a Message Authentication Code — MAC). Generally, it is much faster than asymmetric key (aka public key encryption) and can be run on fairly simple devices. Basically, it uses the same key to encrypt and decrypt:

But, we can add an initialization vector (IV)/salt value to the encryption process, and also either have a stream cipher or a block one. Along with this, we can add some additional data to give AEAD (Authenticated Encryption with Additional Data):

Basically, we have basic classifications for the modes of operation:

  • Mode With Initialization Vector (IV): ChaCha20, CBC, CFB8, CFB, and OFB.
  • Mode With Initialization Vector (IV) and Tag: GCM.
  • Mode With Nonce: CTR.
  • Mode With Tweak: XTS [here].
  • Mode: ECB.

Overall, GCM and ChaCha20 are stream ciphers, and where the others are block ciphers. The block ciphers thus require the padding of the data, so that all of the blocks are filled. In AES, the size of each block is 16 bytes (128 bits). For the block ciphers, we typically use PKCS7 padding, where a fixed value character is used to identify the unfilled places in the last block (see the CBC example below).

CTR (Counter) mode uses a nonce, while ChaCha20, GCM, CBC (Cipher Block Chaining), CFB8, CFB (Cipher Feedback Block), and OFB (Output feedback) use an IV. For AES and ChaCha20, the IV and nonce values are 16 bytes long (128 bits). The IV must be stored/transmitted with the cipher, otherwise, it will be extremely difficult to decrypt.

With XTS (XEX-based tweaked-codebook mode with ciphertext stealing), we use a “tweak”, where we take an encryption key, and encrypt the sector number, and use this to X-OR the data within the sector.

Coding

Python code for this is [here]:

import os
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives import padding
import sys
message="Hello"
associated_data=b"Hello"
tag=b"0123456789ABCDEF"
keysize=32
iv = os.urandom(16)
mode=0
if (len(sys.argv)>1):
message=str(sys.argv[1])
if (len(sys.argv)>2):
mode=int(sys.argv[2])
if (len(sys.argv)>3):
keysize=int(sys.argv[3])
if (mode==1): keysize=32
key = os.urandom(keysize)
padder = padding.PKCS7(128).padder()
unpadder = padding.PKCS7(128).unpadder()
cipher=Cipher(algorithms.AES(key), modes.GCM(iv))
if (mode==0):
cipher=Cipher(algorithms.AES(key), modes.GCM(iv))
if (mode==1):
cipher=Cipher(algorithms.ChaCha20(key,iv), None)
if (mode==2):
cipher = Cipher(algorithms.AES(key), modes.CBC(iv))
if (mode==3):
cipher = Cipher(algorithms.AES(key), modes.OFB(iv))
if (mode==4):
cipher = Cipher(algorithms.AES(key), modes.CFB(iv))
if (mode==5):
cipher = Cipher(algorithms.AES(key), modes.CFB8(iv))
if (mode==6):
cipher = Cipher(algorithms.AES(key), modes.CTR(iv))
if (mode==7):
cipher = Cipher(algorithms.AES(key), modes.XTS(iv))
if (mode==8):
cipher = Cipher(algorithms.AES(key), modes.ECB())

encryptor = cipher.encryptor()
if (mode==0): encryptor.authenticate_additional_data(associated_data)
if (mode>1):
str=padder.update(message.encode())+padder.finalize()
else:
str=message.encode()

ciphertext = encryptor.update(str ) + encryptor.finalize()
# Now decrypt
decryptor = cipher.decryptor()
if (mode==0):
decryptor=Cipher(algorithms.AES(key),modes.GCM(iv, encryptor.tag)).decryptor()
decryptor.authenticate_additional_data(associated_data)
if (mode>1):
rtn=unpadder.update(decryptor.update(ciphertext) + decryptor.finalize())+unpadder.finalize()
else:
rtn=decryptor.update(ciphertext) + decryptor.finalize()

print("Type:\t\t\t",cipher.algorithm.name)
if (mode!=1): print("Mode:\t\t\t",cipher.mode.name)
print("Message:\t\t",message)
print("Message with padding:\t",str)
print("\nKey:\t\t\t",key.hex())
print("IV:\t\t\t",iv.hex())
print("\nCipher:\t\t\t",ciphertext.hex())
if (mode==0): print("Tag:\t\t\t",encryptor.tag.hex())
print("Decrypt:\t\t",rtn.decode())

For AES ECB, we do not have an IV, and thus the cipher will always be the same for the same input and key [here]:

Type:			 AES
Mode: ECB
Message: Hello
Message with padding: b'Hello\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b'

Key: ac58926f69faabc7da3c553c01121461

Cipher: 78b30bfd5e5ac7ccf7ccaba9dab5d4f7
Decrypt: Hello

For ChaCha20 and GCM mode, we have a stream cipher, and so there is no padding. Thus the cipher text length will be the same of the plaintext length. In the following the message has five bytes (‘H’,’e’,’l’,’l’ and ‘o’) and so the cipher also has five bytes (‘3458f7263b’) [here]:

Type:			 AES
Mode: GCM
Message: Hello 123
Message with padding: b'Hello 123'

Key: 480fb3141241e8b4d0e6f33c20ffd46b346c58d2cd79246b6ffb2b0712d0b09d
IV: 092149e7229300d60971b208909e2420
Cipher: 8847250ea822b151e5
Tag: 75d1801d0e87f16f5282a4fff467cf25

Decrypt: Hello 123

For AES CBC, we have a block cipher, so there is padding in the message before it is encrypted. This uses PKCS7 padding, which fills the last block with the value of the bytes that are missing. In the example below, the message has nine characters, so we will with a 0x07 value (to give 16 bytes per block) [here]:

Type:			 AES
Mode: CBC
Message: Hello 123
Message with padding: b'Hello 123\x07\x07\x07\x07\x07\x07\x07'

Key: d41bdb1e2d73d5e71a38f297b939ea89e67ae00ad597baceabf4708bcaee371f
IV: a3395b3d8437e2870aa7222fe063f547

Cipher: 61f9df140b30ae5f005a62aaa245fe88
Decrypt: Hello 123

Conclusions

And you thought symmetric key encryption was so simple. Here’s some more information on the Cryptography Python library:

https://asecuritysite.com/hazmat

and for Symmetric key:

https://asecuritysite.com/symmetric