Symmetric key modes: GCM, ChaCha20, CBC, CFB8, CFB, OFB, GCM, CTR, and XTSBasically, we have give basic classications of modes: Mode With Initialization Vector: (ChaCha20, CBC, CFB8, CFB, and OFB); Mode With Initialization Vector and Tag (GCM); Mode With Nonce (CTR); Mode With Tweak (XTS); and Mode (ECB). CTR (Counter) mode uses a nonce, while ChaCha, GCM, CBC, CFB8, CFB, and OFB use an IV. The IV and Nonce values are 16 bytes long (128 bits) for AES. With XTS, we use a "tweak", and where we take an encryption key, and encrypt the sector number, and use this to X-OR the data within the sector. Overall, GCM and ChaCha20 are stream ciphers, where the others are block ciphers. The block ciphers thus require the padding of the data, so that all of the blocks are filled. |
Outline
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.
Code
Hazmat code is:
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 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'):
Type: ChaCha20 Message: Hello Message with padding: b'Hello' Key: 3e33645c1f6fe7e0b4550be8627627fafd6f66819e24ff531794a690dcc402bf IV: 73afaaced66d37ab6d1a1c8cf7a8e4e0 Cipher: 3458f7263b Decrypt: Hello
For AES ECB, we do not have an IV, and thus the cipher will always be the same for the same input and key:
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 AES GCM, we see that we have a tag associated with the cipher and where the associated data has been added into the cipher
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, and so there is padding in the message before it is encrypted. This uses PKCS7 padding, and which fills the last block with the value of the bytes that are missing. In the example below, the message has nine characters, and so we will with a 0x07 value (to give 16 bytes per block):
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