Hazmat Symmetric KeySymmetric key encryption involves the same key being used to encrypt and decrypt. Apart from ECB mode, we also normally apply a salt value known as an IV (Initialization Vector). The main modes are ECB (Electronic Code Book), CBC (Cipher Block Chaining), CTR (Counter), OFB (Output Feedback) and GCM (Galois/Counter Mode). In this case we will use the cryptography library in Python, and implement AES, Chacha20, Camellia, 3DES, IDEA, CAST5 and Blowfish. The Python program will use a random 256-bit encryption key, and a random 128-bit IV value. In most of the modes, we need to pad the plaintext to the size of the block (typically this is 128 bits or 16 bytes), but AES GCM mode we do not need to pad the cipher as it is a stream cipher. |
Theory
Symmetric key methods provide the core foundation of privacy in cybersecurity. With this Bob and Alice have the same encryption key, and this key is used to encrypt and also to decrypt. We normally also add a salt value — known as an IV (Initialization Vector). In this case, we will use the Hazmat primitives in the cryptography library. For most of the encryption modes we can use the following function:
def go_encrypt(msg,method,mode): cipher = Cipher(method, mode) encryptor = cipher.encryptor() ct = encryptor.update(msg) + encryptor.finalize() return (ct) def go_decrypt(ct,method,mode): cipher = Cipher(method, mode) decryptor = cipher.decryptor() return (decryptor.update(ct) + decryptor.finalize())
We can then pass the algorithm with the key, and the mode. In this case, we use the CBC mode with the IV:
cipher=go_encrypt(padded_data,algorithms.AES(key), modes.CBC(iv)) plain=go_decrypt(cipher,algorithms.AES(key), modes.CBC(iv))
Overall we must pad our input data with:
def pad(data,size=128): padder = padding.PKCS7(size).padder() padded_data = padder.update(data) padded_data += padder.finalize() return(padded_data)
and unpad after decryption with:
def unpad(data,size=128): padder = padding.PKCS7(size).unpadder() unpadded_data = padder.update(data) unpadded_data += padder.finalize() return(unpadded_data)
In both cases, the block size is 128 bits in size. There are some ciphers, such as ChaCha20 and AES GCM, where we do not need to pad and unpad, as they are stream ciphers. With GCM we add an authentication tag:
def go_encrypt_with_auth(msg,method,mode,add): cipher = Cipher(method, mode) encryptor = cipher.encryptor() encryptor.authenticate_additional_data(add) ct = encryptor.update(msg) + encryptor.finalize() return (ct,encryptor.tag) def go_decrypt_with_auth(ct,method,mode,add): cipher = Cipher(method, mode) decryptor = cipher.decryptor() decryptor.authenticate_additional_data(add) pl=decryptor.update(ct) + decryptor.finalize() return (pl)
The following is the completed code:
import os from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from cryptography.hazmat.primitives import padding import sys import binascii def go_encrypt(msg,method,mode): cipher = Cipher(method, mode) encryptor = cipher.encryptor() ct = encryptor.update(msg) + encryptor.finalize() return (ct) def go_decrypt(ct,method,mode): cipher = Cipher(method, mode) decryptor = cipher.decryptor() return (decryptor.update(ct) + decryptor.finalize()) def go_encrypt_with_auth(msg,method,mode,add): cipher = Cipher(method, mode) encryptor = cipher.encryptor() encryptor.authenticate_additional_data(add) ct = encryptor.update(msg) + encryptor.finalize() return (ct,encryptor.tag) def go_decrypt_with_auth(ct,method,mode,add): cipher = Cipher(method, mode) decryptor = cipher.decryptor() decryptor.authenticate_additional_data(add) pl=decryptor.update(ct) + decryptor.finalize() return (pl) def pad(data,size=128): padder = padding.PKCS7(size).padder() padded_data = padder.update(data) padded_data += padder.finalize() return(padded_data) def unpad(data,size=128): padder = padding.PKCS7(size).unpadder() unpadded_data = padder.update(data) unpadded_data += padder.finalize() return(unpadded_data) key = os.urandom(32) iv = os.urandom(16) msg=b"Hello" tag= b"Some data for the authentication tag" if (len(sys.argv)>1): msg=str(sys.argv[1]).encode() print ("Message:\t",msg.decode()) print ("Key:\t",binascii.b2a_hex(key)) print ("IV:\t",binascii.b2a_hex(iv)) padded_data=pad(msg) print ("\n\n=== AES ECB === ") cipher=go_encrypt(padded_data,algorithms.AES(key), modes.ECB()) plain=go_decrypt(cipher,algorithms.AES(key), modes.ECB()) data=unpad(plain) print ("Cipher: ",binascii.b2a_hex(cipher)) print (f"Decrypted: {data.decode()}") cipher=go_encrypt(padded_data,algorithms.AES(key), modes.CBC(iv)) print ("=== AES CBC === ") cipher=go_encrypt(padded_data,algorithms.AES(key), modes.CBC(iv)) plain=go_decrypt(cipher,algorithms.AES(key), modes.CBC(iv)) data=unpad(plain) print ("Cipher: ",binascii.b2a_hex(cipher)) print (f"Decrypted: {data.decode()}") cipher=go_encrypt(padded_data,algorithms.AES(key), modes.CBC(iv)) print ("=== AES OFB === ") cipher=go_encrypt(padded_data,algorithms.AES(key), modes.OFB(iv)) plain=go_decrypt(cipher,algorithms.AES(key), modes.OFB(iv)) data=unpad(plain) print ("Cipher: ",binascii.b2a_hex(cipher)) print (f"Decrypted: {data.decode()}") print ("=== AES CFB === ") cipher=go_encrypt(padded_data,algorithms.AES(key), modes.CFB(iv)) plain=go_decrypt(cipher,algorithms.AES(key), modes.CFB(iv)) data=unpad(plain) print ("Cipher: ",binascii.b2a_hex(cipher)) print (f"Decrypted: {data.decode()}") print ("=== AES CTR === ") cipher=go_encrypt(padded_data,algorithms.AES(key), modes.CTR(iv)) plain=go_decrypt(cipher,algorithms.AES(key), modes.CTR(iv)) data=unpad(plain) print ("Cipher: ",binascii.b2a_hex(cipher)) print (f"Decrypted: {data.decode()}") print ("=== AES GCM === ") # In GCM mode we convert to a stream cipher, so there is no need for padding cipher,auth_tag=go_encrypt_with_auth(msg,algorithms.AES(key), modes.GCM(iv),tag) plain=go_decrypt_with_auth(cipher,algorithms.AES(key), modes.GCM(iv,auth_tag),tag) print ("Cipher: ",binascii.b2a_hex(cipher)) print (f"Decrypted: {plain.decode()}") print ("=== AES XTS === ") # In XTS the iv value is known as a tweak - and relates to the sector number # The keys are double length, so that a 32 byte key (256 bits) is actually AES-128 cipher=go_encrypt(padded_data,algorithms.AES(key), modes.XTS(iv)) plain=go_decrypt(cipher,algorithms.AES(key), modes.XTS(iv)) data=unpad(plain) print ("Cipher: ",binascii.b2a_hex(cipher)) print (f"Decrypted: {data.decode()}") print ("\n=== Blowfish ECB === ") cipher=go_encrypt(padded_data,algorithms.Blowfish(key), modes.ECB()) plain=go_decrypt(cipher,algorithms.Blowfish(key), modes.ECB()) data=unpad(plain) print ("Cipher: ",binascii.b2a_hex(cipher)) print (f"Decrypted: {data.decode()}") print ("=== Blowfish CBC === ") cipher=go_encrypt(padded_data,algorithms.Blowfish(key), modes.CBC(iv[:8])) plain=go_decrypt(cipher,algorithms.Blowfish(key), modes.CBC(iv[:8])) data=unpad(plain) print ("Cipher: ",binascii.b2a_hex(cipher)) print (f"Decrypted: {data.decode()}") print ("\n=== ChaCha20 === ") cipher=go_encrypt(msg,algorithms.ChaCha20(key,iv), None) plain=go_decrypt(cipher,algorithms.ChaCha20(key,iv), None) print ("Cipher: ",binascii.b2a_hex(cipher)) print (f"Decrypted: {data.decode()}") print ("\n=== 3DES ECB === ") cipher=go_encrypt(padded_data,algorithms.TripleDES(key[:16]), modes.ECB()) plain=go_decrypt(cipher,algorithms.TripleDES(key[:16]), modes.ECB()) print ("Cipher: ",binascii.b2a_hex(cipher)) print (f"Decrypted: {data.decode()}") print ("=== 3DES CBC === ") cipher=go_encrypt(padded_data,algorithms.TripleDES(key[:16]), modes.CBC(iv[:8])) plain=go_decrypt(cipher,algorithms.TripleDES(key[:16]), modes.CBC(iv[:8])) print ("Cipher: ",binascii.b2a_hex(cipher)) print (f"Decrypted: {data.decode()}") print ("\n=== Camellia ECB === ") cipher=go_encrypt(padded_data,algorithms.Camellia(key), modes.ECB()) plain=go_decrypt(cipher,algorithms.Camellia(key), modes.ECB()) print ("Cipher: ",binascii.b2a_hex(cipher)) print (f"Decrypted: {data.decode()}") print ("=== Camellia CBC === ") cipher=go_encrypt(padded_data,algorithms.Camellia(key), modes.CBC(iv)) plain=go_decrypt(cipher,algorithms.Camellia(key), modes.CBC(iv)) print ("Cipher: ",binascii.b2a_hex(cipher)) print (f"Decrypted: {data.decode()}") print ("=== IDEA ECB === ") cipher=go_encrypt(padded_data,algorithms.IDEA(key[:16]), modes.ECB()) plain=go_decrypt(cipher,algorithms.IDEA(key[:16]), modes.ECB()) print ("Cipher: ",binascii.b2a_hex(cipher)) print (f"Decrypted: {data.decode()}") print ("=== IDEA CBC === ") cipher=go_encrypt(padded_data,algorithms.IDEA(key[:16]), modes.CBC(iv[:8])) plain=go_decrypt(cipher,algorithms.IDEA(key[:16]), modes.CBC(iv[:8])) print ("Cipher: ",binascii.b2a_hex(cipher)) print (f"Decrypted: {data.decode()}") print ("\n=== CAST5 ECB === ") cipher=go_encrypt(padded_data,algorithms.CAST5(key[:16]), modes.ECB()) plain=go_decrypt(cipher,algorithms.CAST5(key[:16]), modes.ECB()) print ("Cipher: ",binascii.b2a_hex(cipher)) print (f"Decrypted: {data.decode()}") print ("=== CAST5 CBC === ") cipher=go_encrypt(padded_data,algorithms.CAST5(key[:16]), modes.CBC(iv[:8])) plain=go_decrypt(cipher,algorithms.CAST5(key[:16]), modes.CBC(iv[:8])) print ("Cipher: ",binascii.b2a_hex(cipher)) print (f"Decrypted: {data.decode()}")
A sample run:
Message: The quick brown fox jumps over the lazy dog Key: b'09921d2fa67f19a782d37528c04450a58fa0a7f193e643f44a2078c3c2eb0174' IV: b'30921f221f75f84da538b59e8ff3ea00' === AES ECB === Cipher: b'fd8b5e86e027b5138ae49648ac3b1b34b9de42a9fae01e4f44443faa5f1d3e1023fde2eeb41ab3870ae8c40ca0500c18' Decrypted: The quick brown fox jumps over the lazy dog === AES CBC === Cipher: b'c65812a62e88c42b3fc6c2a7ceb0075bca2754d79aec9806573b40f040c3e020830742ff584269d435bcafe8deafc2e3' Decrypted: The quick brown fox jumps over the lazy dog === AES OFB === Cipher: b'6d651987310ba30b089e9a40144856b37124efba29206952ec154670e85a5016cc36a1fe594113915082b27de0786423' Decrypted: The quick brown fox jumps over the lazy dog === AES CFB === Cipher: b'6d651987310ba30b089e9a40144856b3db6b31248096f0ba7db72b00b4b1a0e74f35f50668670338a82be0845446c58e' Decrypted: The quick brown fox jumps over the lazy dog === AES CTR === Cipher: b'6d651987310ba30b089e9a40144856b3e33aa4334580fd0d204e570515d2d0e7824df94c1178f46e67eec41b668afc0e' Decrypted: The quick brown fox jumps over the lazy dog === AES GCM === Cipher: b'42ba846d2065c1de4e2e6ab8b47f8accd9a10fa8b7399a753ae6c83c2fb333764ebd69f8cb58cbb09a427f' Decrypted: The quick brown fox jumps over the lazy dog === AES XTS === Cipher: b'b56efe9deec40a29b92664b8a02f3786441fe8f256db10257a9fd36810752def5747d4c1a4def881050271ba028fec54' Decrypted: The quick brown fox jumps over the lazy dog === Blowfish ECB === Cipher: b'74edf570208d0b067734ca66c42cf8ee9fc2b083e2b3324537025c48e2855294db1ab47ac5e6795ac47961d987bd856e' Decrypted: The quick brown fox jumps over the lazy dog === Blowfish CBC === Cipher: b'cea08dd376271dc0c45276ec75dc424ea0c3925138ad017ed882d1056e5124d904e8603821a7c2883ef19f6e45d52316' Decrypted: The quick brown fox jumps over the lazy dog === ChaCha20 === Cipher: b'02927c4015c9c072868becef1bbf35da887473863522d983eabf93d504adb71afe1dd201d8a7007b3daaa0' Decrypted: The quick brown fox jumps over the lazy dog === 3DES ECB === Cipher: b'd43d7788c3e357a426daf3148e75bc2338170e83b7d27dd0216235e66927c08e4505dcd9f4f4423ca5403bbf00f6c8c7' Decrypted: The quick brown fox jumps over the lazy dog === 3DES CBC === Cipher: b'4ce363c8e1eb40ea47522fc3c383f3f124e4bb082d6396a5018542d43dc56749fab942cf29c766431015e71c31dd4cd0' Decrypted: The quick brown fox jumps over the lazy dog === Camellia ECB === Cipher: b'9c447d8be1895853abff289047c060c5b93f17c2cea00908348439568132ee1f3d630a2ebb75ed83817ba3926544b16b' Decrypted: The quick brown fox jumps over the lazy dog === Camellia CBC === Cipher: b'0794624d77ed24efc6d8cf3811ccd0359f8b7944dfc924738d5d3abe680a0a3dbf6cb0733741a1648325c606568f74c0' Decrypted: The quick brown fox jumps over the lazy dog === IDEA ECB === Cipher: b'881889ac97a1aee0f9040e3d19f542d2cd2e0aa5ea01bd345d2e4333563d9d457b6a8d886724950454b09fbb3556770e' Decrypted: The quick brown fox jumps over the lazy dog === IDEA CBC === Cipher: b'18fa0a6fc3ed4288bf79c0de1e200582e040b177b4871567d699db929fccc33933f04f003189ef8f5fb4442def9b913f' Decrypted: The quick brown fox jumps over the lazy dog === CAST5 ECB === Cipher: b'6289fa3bb74cf173373114e8dd02e65c48158e31bf89eee655dd451b734d40e93ad1badc1b89fc7ceca5aa9cb7a4c17b' Decrypted: The quick brown fox jumps over the lazy dog === CAST5 CBC === Cipher: b'5308a595c40b280540d38417d6d43e7d614b251e963db416485a923e110cacaf71190c9c453e54ada43af18f8423c17a' Decrypted: The quick brown fox jumps over the lazy dog