The symmetric.py file contains a python implementation of a block cipher using elements from the AES and DES systems and a stream cipher using the secrets module to create a simple one-time-pad stream cipher for short messages, and utilising the block cipher's key generation algorithm to create an infinitely long stream for longer messages.
There has been very little input sanitisaion or error handling done, however both files should work in Windows and Linux as long as valid file paths are provided.
File extensions are not required for any input or output files, however they are recommended on outputs to enable Windows to open them.
Key Schedule Generation:
The algorithm starts with a 128-bit (16-byte) key, generated using the Python secrets module, which produces cryptographically secure random numbers. This key serves as the seed for creating 12 round keys, each of 64 bits (8 bytes). The generation process involves splitting the initial 16-byte seed into two 8-byte keys, and then iteratively permuting substituting and XORing these keys to create twelve round keys. Permutations are done according to our predefined table, while substitutions rely on the AES s-box.
Block Encryption:
The block encryption process, which employs a Feistel-like structure similar to DES, encrypts a data block by first splitting it in half, creating a left and right section. The algorithm then performs 12 rounds of a series of transformations using the 12 keys from the key schedule. During each round, the following steps are performed: • Swap the left and right sections. • Rearrange the bytes on the left according to a predefined permutation table: [7, 6, 1, 8, 4, 3, 5, 2] (permutation). • Substitute all the bytes on the left with their corresponding values in the AES s-box (substitution). • XOR the left side with the round key (key mixing). • Transform the right side by XORing it with the left side (diffusion). These steps are repeated for a total of 12 rounds, after which the two sections are concatenated to form the final encrypted data block.
Mode of Operation:
The block cipher uses Counter (CTR) mode as its mode of operation. In CTR mode, a 16-byte counter is constructed by concatenating an 8-byte initialization vector (IV) with an 8-byte block counter. The combined IV and block counter are encrypted using the block encryption algorithm described above. The encryption process produces a keystream that is as long as the input file. The keystream is then XORed with the input file to produce the encrypted output.
To create the keystream, the Python “secrets” module is used. The “secrets” module is designed to provide cryptographically secure random numbers and bytes. The key extension function from the block cipher is then used to extend the key for longer messages. Encryption/decryption is a simple XOR with the keystream.
Key Generation: The key generation operation functions as follows:
- Two 512-bit prime numbers are generated by choosing random numbers and testing them with the Miller-Rabin test using a k value of 5 (Chia, 2013). These are p and q. This k value was chosen because it was recommended in the lecture notes. For a real cryptographically secure implementation I would use more because this reduces the chance of an error and the function still runs fast even with much higher values for k.
- A 1024-bit n is calculated by multiplying p and q (there is a check to make sure the result is actually 1024-bit).
- Phi is calculated from p and q
- 65537 is used as the default value for e. This is checked to ensure it is coprime with phi, and new values are checked until it is.
- Find the modular multiplicative inverse (a) of e mod (phi). This is done using the extended Euclidean algorithm (Wikibooks, 2021). This value is d.
- The two parts to the public key, n and e, are stored in hexadecimal in a text document, delimited by a \n linebreak character
- The two parts of the private key, n and d, are stored in the same way.
Hash Function:
- Input data is padded and split into 8-byte segments
- Starting seed is always 0x65B148EE48CE7431 (8 bytes)
- Hash function takes two 8-byte inputs and returns a single 8-byte output. o Initial inputs are seed and the first block o Subsequent inputs are the latest output and the next block
- The working hash is always the first input, beginning with the seed. This is manipulated and mixed with the second input as follows:
- Hash function operation includes 9 rounds of: o Byte-permutation: perm = [7, 6, 1, 8, 4, 3, 5, 2] o S-box substitution of all bytes (using AES S-box) (Daemen & Rijmen, 1999) o XOR with 2nd input
- After all rounds are completed for each block, the final 8-byte hash is returned as the output.
Encryption/Decryption/Signing/Verifying: All of these functions are extremely simple in RSA, see program comments for details.