mirror of
https://github.com/TheAlgorithms/Python.git
synced 2026-09-29 05:53:07 +08:00
* Removed incorrect type hints * [Feature] Implemented DES Algorithm in ECB mode * Revised variable names * updating DIRECTORY.md --------- Co-authored-by: Christian Clauss <cclauss@me.com> Co-authored-by: cclauss <cclauss@users.noreply.github.com>
558 lines
20 KiB
Python
558 lines
20 KiB
Python
"""
|
|
Python program for DES (Data Encryption Standard) using Electronic Codebook (ECB) mode.
|
|
|
|
DES is a symmetric-key block cipher that encrypts data in fixed-size blocks (64 bits).
|
|
In ECB mode, the plaintext is divided into 64-bit blocks, and each block is encrypted
|
|
independently using the same key. This makes ECB the simplest block cipher mode, but
|
|
also one of the least secure, as identical plaintext blocks will produce identical
|
|
ciphertext blocks.
|
|
|
|
This implementation of DES includes key scheduling, encryption, and decryption.
|
|
It uses standard DES operations such as initial and final permutations, expansion,
|
|
permutation, and S-box lookups. Padding is applied to ensure the plaintext length is
|
|
a multiple of 64 bits.
|
|
|
|
Warning: ECB mode is not secure for most use cases due to its vulnerability to block
|
|
repetition analysis. Consider using a more secure mode of operation, such as CBC
|
|
(Cipher Block Chaining), for sensitive data encryption.
|
|
|
|
References:
|
|
- Handbook of Applied Cryptography (Algorithm 7.82)
|
|
- Handbook of Applied Cryptography (Algorithm 7.83)
|
|
- Handbook of Applied Cryptography (Algorithm 9.29)
|
|
- https://en.wikipedia.org/wiki/Data_Encryption_Standard
|
|
"""
|
|
|
|
import random
|
|
|
|
# fmt: off
|
|
|
|
# Initial Permutation Table
|
|
IP = [58, 50, 42, 34, 26, 18, 10, 2,
|
|
60, 52, 44, 36, 28, 20, 12, 4,
|
|
62, 54, 46, 38, 30, 22, 14, 6,
|
|
64, 56, 48, 40, 32, 24, 16, 8,
|
|
57, 49, 41, 33, 25, 17, 9, 1,
|
|
59, 51, 43, 35, 27, 19, 11, 3,
|
|
61, 53, 45, 37, 29, 21, 13, 5,
|
|
63, 55, 47, 39, 31, 23, 15, 7]
|
|
|
|
# Final Permutation Table
|
|
IP_INV = [40, 8, 48, 16, 56, 24, 64, 32,
|
|
39, 7, 47, 15, 55, 23, 63, 31,
|
|
38, 6, 46, 14, 54, 22, 62, 30,
|
|
37, 5, 45, 13, 53, 21, 61, 29,
|
|
36, 4, 44, 12, 52, 20, 60, 28,
|
|
35, 3, 43, 11, 51, 19, 59, 27,
|
|
34, 2, 42, 10, 50, 18, 58, 26,
|
|
33, 1, 41, 9, 49, 17, 57, 25]
|
|
|
|
# Expansion Table
|
|
E = [32, 1, 2, 3, 4, 5,
|
|
4, 5, 6, 7, 8, 9,
|
|
8, 9, 10, 11, 12, 13,
|
|
12, 13, 14, 15, 16, 17,
|
|
16, 17, 18, 19, 20, 21,
|
|
20, 21, 22, 23, 24, 25,
|
|
24, 25, 26, 27, 28, 29,
|
|
28, 29, 30, 31, 32, 1]
|
|
|
|
# Permutation Table
|
|
P = [16, 7, 20, 21, 29, 12, 28, 17,
|
|
1, 15, 23, 26, 5, 18, 31, 10,
|
|
2, 8, 24, 14, 32, 27, 3, 9,
|
|
19, 13, 30, 6, 22, 11, 4, 25]
|
|
|
|
# S-boxes (Substitution Boxes)
|
|
S_BOXES = {
|
|
"S1": [[14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7],
|
|
[0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8],
|
|
[4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0],
|
|
[15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13]],
|
|
|
|
"S2": [[15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10],
|
|
[3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5],
|
|
[0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15],
|
|
[13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9]],
|
|
|
|
"S3": [[10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8],
|
|
[13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1],
|
|
[13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7],
|
|
[1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12]],
|
|
|
|
"S4": [[7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15],
|
|
[13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9],
|
|
[10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4],
|
|
[3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14]],
|
|
|
|
"S5": [[2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9],
|
|
[14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6],
|
|
[4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14],
|
|
[11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3]],
|
|
|
|
"S6": [[12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11],
|
|
[10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8],
|
|
[9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6],
|
|
[4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13]],
|
|
|
|
"S7": [[4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1],
|
|
[13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6],
|
|
[1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2],
|
|
[6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12]],
|
|
|
|
"S8": [[13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7],
|
|
[1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2],
|
|
[7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8],
|
|
[2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11]]
|
|
}
|
|
|
|
# Permuted Choice 1 Table
|
|
PC1 = [57, 49, 41, 33, 25, 17, 9,
|
|
1, 58, 50, 42, 34, 26, 18,
|
|
10, 2, 59, 51, 43, 35, 27,
|
|
19, 11, 3, 60, 52, 44, 36,
|
|
63, 55, 47, 39, 31, 23, 15,
|
|
7, 62, 54, 46, 38, 30, 22,
|
|
14, 6, 61, 53, 45, 37, 29,
|
|
21, 13, 5, 28, 20, 12, 4]
|
|
|
|
# Permuted Choice 2 Table
|
|
PC2 = [14, 17, 11, 24, 1, 5,
|
|
3, 28, 15, 6, 21, 10,
|
|
23, 19, 12, 4, 26, 8,
|
|
16, 7, 27, 20, 13, 2,
|
|
41, 52, 31, 37, 47, 55,
|
|
30, 40, 51, 45, 33, 48,
|
|
44, 49, 39, 56, 34, 53,
|
|
46, 42, 50, 36, 29, 32]
|
|
# fmt: on
|
|
|
|
|
|
class Operations:
|
|
@staticmethod
|
|
def string_to_bitset(string: str) -> list:
|
|
"""
|
|
Converts a string into a list of binary digits (bitset).
|
|
|
|
Args:
|
|
string (str): The input string to be converted.
|
|
|
|
Returns:
|
|
list: A list of binary digits representing the string.
|
|
|
|
Examples:
|
|
>>> Operations.string_to_bitset('A')
|
|
['0', '1', '0', '0', '0', '0', '0', '1']
|
|
>>> len(Operations.string_to_bitset('ab'))
|
|
16
|
|
>>> Operations.string_to_bitset(' ')
|
|
['0', '0', '1', '0', '0', '0', '0', '0']
|
|
"""
|
|
return list("".join(format(ord(char), "08b") for char in string))
|
|
|
|
@staticmethod
|
|
def pad_right_to_multiple_of_n(bitset: list, length: int) -> list:
|
|
"""
|
|
Pads the bitset with zeros on the right until
|
|
its length is a multiple of `length`.
|
|
|
|
Args:
|
|
bitset (list): A list of binary digits (as strings) to be padded.
|
|
|
|
Returns:
|
|
list: The padded bitset, with a length that is a multiple of n.
|
|
|
|
Examples:
|
|
>>> Operations.pad_right_to_multiple_of_n(['1', '0', '1'], 4)
|
|
['1', '0', '1', '0']
|
|
>>> len(Operations.pad_right_to_multiple_of_n(['1'] * 64, 64)) % 64
|
|
0
|
|
>>> len(Operations.pad_right_to_multiple_of_n(['0'] * 63, 64)) % 64
|
|
0
|
|
"""
|
|
if len(bitset) % length != 0:
|
|
bitset += list("0" * (length - len(bitset) % length))
|
|
return bitset
|
|
|
|
@staticmethod
|
|
def pad_left_to_multiple_of_n(bitset: list, length: int) -> list:
|
|
"""
|
|
Pads the bitset with zeros on the left until
|
|
its length is a multiple of length.
|
|
|
|
Args:
|
|
bitset (list): A list of binary digits (as strings) to be padded.
|
|
|
|
Returns:
|
|
list: The padded bitset, with a length that is a multiple of n.
|
|
|
|
Examples:
|
|
>>> Operations.pad_left_to_multiple_of_n(['1', '0', '1'], 4)
|
|
['0', '1', '0', '1']
|
|
>>> len(Operations.pad_left_to_multiple_of_n(['1'] * 64, 64)) % 64
|
|
0
|
|
>>> len(Operations.pad_left_to_multiple_of_n(['0'] * 63, 64)) % 64
|
|
0
|
|
"""
|
|
if len(bitset) % length != 0:
|
|
bitset = list("0" * (length - len(bitset) % length)) + bitset
|
|
return bitset
|
|
|
|
@staticmethod
|
|
def bitset_to_hex(bitset: list) -> str:
|
|
"""
|
|
Converts a list of binary digits
|
|
into its 16 digit hexadecimal representation.
|
|
|
|
Args:
|
|
bitset (list): A list of binary digits (as strings)\
|
|
representing a binary number.
|
|
|
|
Returns:
|
|
str: The 16 digit hexadecimal representation of the binary number.
|
|
|
|
Examples:
|
|
>>> Operations.bitset_to_hex(['1', '0', '1', '0', '1', '1', '1', '0'])
|
|
'00000000000000ae'
|
|
>>> Operations.bitset_to_hex(['1', '1', '1', '1'] * 16)
|
|
'ffffffffffffffff'
|
|
>>> Operations.bitset_to_hex(['0'] * 64)
|
|
'0000000000000000'
|
|
"""
|
|
return format(int("".join(bitset), 2), "016x")
|
|
|
|
@staticmethod
|
|
def hex_to_bitset(hex_string: str, left_pad: int) -> list:
|
|
"""
|
|
Converts a hexadecimal string to a bitset
|
|
and pads the bitset to a specified length.
|
|
|
|
Args:
|
|
hex_string (str): The hexadecimal string to convert.
|
|
left_pad (int): The length to pad the bitset on the left.
|
|
|
|
Returns:
|
|
list: The padded bitset.
|
|
|
|
Examples:
|
|
>>> Operations.hex_to_bitset('ae', 8)
|
|
['1', '0', '1', '0', '1', '1', '1', '0']
|
|
>>> Operations.hex_to_bitset('1f', 10)
|
|
['0', '0', '0', '0', '0', '1', '1', '1', '1', '1']
|
|
"""
|
|
return Operations.pad_left_to_multiple_of_n(
|
|
list(format(int(hex_string, 16), f"0{left_pad}b")), left_pad
|
|
)
|
|
|
|
@staticmethod
|
|
def bitset_to_string(bitset: list) -> str:
|
|
"""
|
|
Converts a bitset into a string by interpreting every 8 bits as a character.
|
|
|
|
Args:
|
|
bitset (list): The list of binary digits (bitset).
|
|
|
|
Returns:
|
|
str: The decoded string.
|
|
|
|
Examples:
|
|
>>> Operations.bitset_to_string(['0', '1', '0', '0', '0', '0', '0', '1'])
|
|
'A'
|
|
>>> Operations.bitset_to_string(['0', '1', '0', '0', '0', '1',\
|
|
'0', '0', '0', '1', '0', '1', '0', '1', '1', '0'])
|
|
'DV'
|
|
"""
|
|
return "".join(
|
|
chr(int("".join(bitset[i : i + 8]), 2)) for i in range(0, len(bitset), 8)
|
|
)
|
|
|
|
@staticmethod
|
|
def xor(bitset1: list, bitset2: list) -> list:
|
|
"""
|
|
Applies a bitwise XOR operation between two bitsets of the same length.
|
|
|
|
Args:
|
|
bitset1 (list): The first bitset.
|
|
bitset2 (list): The second bitset.
|
|
|
|
Returns:
|
|
list: The result of the XOR operation as a new bitset.
|
|
|
|
Examples:
|
|
>>> Operations.xor(['0', '1', '0', '1'], ['1', '0', '1', '1'])
|
|
['1', '1', '1', '0']
|
|
>>> Operations.xor(['1', '0', '1', '0'], ['0', '0', '0', '1'])
|
|
['1', '0', '1', '1']
|
|
>>> Operations.xor(['1', '0', '1', '0', '1'], ['0', '0', '0', '1'])
|
|
Traceback (most recent call last):
|
|
...
|
|
ValueError: Bitsets must be of the same length
|
|
>>> Operations.xor(['1', '0', '1', '0'], ['0', '0', '0', '1', '1'])
|
|
Traceback (most recent call last):
|
|
...
|
|
ValueError: Bitsets must be of the same length
|
|
"""
|
|
if len(bitset1) != len(bitset2):
|
|
raise ValueError("Bitsets must be of the same length")
|
|
return [str(int(bitset1[i]) ^ int(bitset2[i])) for i in range(len(bitset1))]
|
|
|
|
@staticmethod
|
|
def shift_left(bitset: list, position: int) -> list:
|
|
"""
|
|
Performs a rotated left shift on a bitset by `position` positions.
|
|
|
|
Args:
|
|
bitset (list): The bitset to be shifted.
|
|
n (int): The number of positions to shift.
|
|
|
|
Returns:
|
|
list: The left-shifted bitset.
|
|
|
|
Examples:
|
|
>>> Operations.shift_left(['1', '0', '0', '1'], 2)
|
|
['0', '1', '1', '0']
|
|
>>> Operations.shift_left(['0', '1', '1', '1'], 1)
|
|
['1', '1', '1', '0']
|
|
>>> Operations.shift_left(['0', '1', '1', '1'], 7)
|
|
['1', '0', '1', '1']
|
|
"""
|
|
position = position % len(bitset)
|
|
return bitset[position:] + bitset[:position]
|
|
|
|
@staticmethod
|
|
def permute(bitset: list, permutation: list) -> list:
|
|
"""
|
|
Permutes a bitset according to a given permutation table.
|
|
|
|
Args:
|
|
bitset (list): The bitset to be permuted.
|
|
permutation (list): The permutation table specifying the new order.
|
|
|
|
Returns:
|
|
list: The permuted bitset.
|
|
|
|
Examples:
|
|
>>> Operations.permute(['1', '0', '1', '0', '1', '1'], [6, 5, 4, 3, 2, 1])
|
|
['1', '1', '0', '1', '0', '1']
|
|
>>> Operations.permute(['0', '1', '1', '0', '1', '0'], [3, 1, 6, 5, 4, 2])
|
|
['1', '0', '0', '1', '0', '1']
|
|
>>> Operations.permute(['0', '1', '1', '0', '1', '0'], \
|
|
[3, 1, 6, 5, 4, 2, 7])
|
|
Traceback (most recent call last):
|
|
...
|
|
ValueError: Permutation values must be within the range of the bitset
|
|
"""
|
|
if not all(0 < i <= len(bitset) for i in permutation):
|
|
raise ValueError(
|
|
"Permutation values must be within the range of the bitset"
|
|
)
|
|
return [bitset[i - 1] for i in permutation]
|
|
|
|
|
|
class Des:
|
|
@staticmethod
|
|
def generate_key() -> str:
|
|
"""
|
|
Generates a random hexadecimal key of 16 characters (64 bits).
|
|
|
|
Returns:
|
|
str: A random hexadecimal key.
|
|
|
|
Examples:
|
|
>>> key = Des.generate_key()
|
|
>>> len(key) # Check if the key length is correct
|
|
16
|
|
>>> # Ensure key only contains valid hex characters
|
|
>>> all(c in "0123456789abcdef" for c in key)
|
|
True
|
|
"""
|
|
return "".join(random.choice("0123456789abcdef") for i in range(16))
|
|
|
|
@staticmethod
|
|
def key_schedule(key: str) -> list:
|
|
"""
|
|
Generates 16 subkeys (round keys) from a given
|
|
64-bit hexadecimal key using the DES key schedule.
|
|
|
|
Args:
|
|
key (str): A 16-character hexadecimal string representing a 64-bit key.
|
|
|
|
Returns:
|
|
list: A list of 16 subkeys, each of which is a permuted bitset.
|
|
|
|
Examples:
|
|
>>> subkeys = Des.key_schedule('133457799BBCDFF1')
|
|
>>> len(subkeys) # Check that 16 subkeys are generated
|
|
16
|
|
>>> # Ensure each subkey is 48 bits long
|
|
>>> all(len(subkey) == 48 for subkey in subkeys)
|
|
True
|
|
"""
|
|
key_bitset = Operations.hex_to_bitset(key, 64)
|
|
key_permuted = Operations.permute(key_bitset, PC1)
|
|
|
|
left_key = key_permuted[:28]
|
|
right_key = key_permuted[28:]
|
|
|
|
keys = []
|
|
|
|
shift_list = [1, 2, 9, 16]
|
|
|
|
for i in range(1, 17):
|
|
if i in shift_list:
|
|
left_key = Operations.shift_left(left_key, 1)
|
|
right_key = Operations.shift_left(right_key, 1)
|
|
else:
|
|
left_key = Operations.shift_left(left_key, 2)
|
|
right_key = Operations.shift_left(right_key, 2)
|
|
keys.append(Operations.permute(left_key + right_key, PC2))
|
|
|
|
return keys
|
|
|
|
@staticmethod
|
|
def des(keys: list, plain_bitset: list) -> list:
|
|
"""
|
|
Encrypts a plain bitset using the provided
|
|
subkeys with DES encryption algorithm.
|
|
|
|
Args:
|
|
keys (list): A list of 16 subkeys, each 48 bits long.
|
|
plain_bitset (list): A bitset representing the plain text,\
|
|
which should be divisible into 64-bit blocks.
|
|
|
|
Returns:
|
|
list: The encrypted bitset.
|
|
|
|
Examples:
|
|
>>> plain_bitset = Operations.string_to_bitset("Test string")
|
|
>>> plain_bitset = Operations.pad_right_to_multiple_of_n(plain_bitset, 64)
|
|
>>> keys = Des.key_schedule(Des.generate_key())
|
|
>>> cipher_bitset = Des.des(keys, plain_bitset)
|
|
>>> # The output length should be the same as the input
|
|
>>> len(cipher_bitset) == len(plain_bitset)
|
|
True
|
|
>>> # Decryption should be the inverse of encryption
|
|
>>> plain_bitset == Des.des(keys[::-1], cipher_bitset)
|
|
True
|
|
"""
|
|
|
|
no_of_blocks = len(plain_bitset) // 64
|
|
|
|
cipher_bitset = []
|
|
|
|
for i in range(no_of_blocks):
|
|
block = plain_bitset[i * 64 : (i + 1) * 64]
|
|
initial_permutation = Operations.permute(block, IP)
|
|
|
|
left_bits = initial_permutation[:32]
|
|
right_bits = initial_permutation[32:]
|
|
|
|
for i in range(16):
|
|
right_expanded = Operations.permute(right_bits, E)
|
|
right_xor_key = Operations.xor(right_expanded, keys[i])
|
|
right_s_box = []
|
|
for j in range(8):
|
|
s_box_input = right_xor_key[j * 6 : (j + 1) * 6]
|
|
row = 2 * int(s_box_input[0]) + int(s_box_input[5])
|
|
col = int("".join(s_box_input[1:5]), 2)
|
|
s_box_output = format(S_BOXES[f"S{j + 1}"][row][col], "04b")
|
|
right_s_box += list(s_box_output)
|
|
|
|
right_permuted = Operations.permute(right_s_box, P)
|
|
|
|
left_bits, right_bits = (
|
|
right_bits,
|
|
Operations.xor(left_bits, right_permuted),
|
|
)
|
|
|
|
left_bits, right_bits = right_bits, left_bits
|
|
cipher_bitset += Operations.permute(left_bits + right_bits, IP_INV)
|
|
return cipher_bitset
|
|
|
|
@staticmethod
|
|
def encrypt(key: str, input_string: str) -> str:
|
|
"""
|
|
Encrypts a given input string using the DES encryption algorithm.
|
|
|
|
Args:
|
|
key (str): A 16-character hexadecimal string representing a 64-bit key.
|
|
input_string (str): The plain text string to be encrypted.
|
|
|
|
Returns:
|
|
str: A hexadecimal string representing the encrypted data.
|
|
|
|
Examples:
|
|
>>> key = '133457799BBCDFF1'
|
|
>>> input_string = 'Test string'
|
|
>>> encrypted = Des.encrypt(key, input_string)
|
|
>>> encrypted # Checking the cipher to ensure consistency
|
|
'c84e3c8fb646872720b224896db4f60'
|
|
"""
|
|
plain_bitset = Operations.string_to_bitset(input_string)
|
|
padded_bitset = Operations.pad_right_to_multiple_of_n(plain_bitset, 64)
|
|
keys = Des.key_schedule(key)
|
|
cipher_bitset = Des.des(keys, padded_bitset)
|
|
return Operations.bitset_to_hex(cipher_bitset)
|
|
|
|
@staticmethod
|
|
def decrypt(key: str, cipher_text: str) -> str:
|
|
"""
|
|
Decrypts the given cipher text using DES decryption.
|
|
|
|
Args:
|
|
key (str): A 16-character hexadecimal string representing a 64-bit key.
|
|
cipher_text (str): A hexadecimal string representing the encrypted data.
|
|
|
|
Returns:
|
|
str: The decrypted plain text string.
|
|
|
|
Examples:
|
|
>>> key = '133457799BBCDFF1'
|
|
>>> encrypted_string = 'c84e3c8fb646872720b224896db4f60'
|
|
>>> decrypted = Des.decrypt(key, encrypted_string)
|
|
>>> decrypted # Checking the cipher to ensure consistency
|
|
'Test string'
|
|
"""
|
|
cipher_bitset = Operations.hex_to_bitset(cipher_text, 64)
|
|
keys = Des.key_schedule(key)[::-1]
|
|
plain_bitset = Des.des(keys, cipher_bitset)
|
|
return Operations.bitset_to_string(plain_bitset).replace("\x00", "")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import doctest
|
|
|
|
doctest.testmod()
|
|
|
|
while True:
|
|
print()
|
|
print("################ DES Algorithm ################")
|
|
print()
|
|
print("Select an option:")
|
|
print("1. To encrypt a string, enter 'e'")
|
|
print("2. To decrypt a string, enter 'd'")
|
|
print("3. To generate a key, enter 'k'")
|
|
print("4. To Quit, enter 'q'")
|
|
mode = input("Enter the option: ").strip().lower()
|
|
if mode not in ["e", "d", "k", "q"]:
|
|
print("Invalid option. Please try again.")
|
|
continue
|
|
if mode == "q":
|
|
break
|
|
elif mode == "k":
|
|
print(f"Generated key: {Des.generate_key()}")
|
|
elif mode == "e":
|
|
key = input("Enter the key: ").strip()
|
|
if len(key) != 16 and not all(char in "0123456789abcdef" for char in key):
|
|
print("Invalid key. Please try again.")
|
|
continue
|
|
input_string = input("Enter the string to encrypt: ").strip()
|
|
print(f"Encrypted string: {Des.encrypt(key, input_string)}")
|
|
elif mode == "d":
|
|
key = input("Enter the key: ").strip()
|
|
if len(key) != 16 and not all(char in "0123456789abcdef" for char in key):
|
|
print("Invalid key. Please try again.")
|
|
continue
|
|
cipher_text = input("Enter the cipher text to decrypt: ").strip()
|
|
print(f"Decrypted string: {Des.decrypt(key, cipher_text)}")
|