ddes-dua-crypto 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,90 @@
1
+ Metadata-Version: 2.4
2
+ Name: ddes-dua-crypto
3
+ Version: 0.1.0
4
+ Summary: Dynamic Data Encryption Standard (D-DES) with a single dynamic S-box.
5
+ Author-email: Dua Amir <duaamir392004@gmail.com>
6
+ Project-URL: Homepage, https://github.com/duaamir39/d-des.git
7
+ Classifier: Programming Language :: Python :: 3.13
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Operating System :: OS Independent
10
+ Classifier: Topic :: Security :: Cryptography
11
+ Requires-Python: >=3.13
12
+ Description-Content-Type: text/markdown
13
+
14
+ # D-DES (Dynamic Data Encryption Standard)
15
+
16
+ > **⚠️ Security Disclaimer:** This is an experimental cryptographic tool for research and educational purposes. It is not intended for securing sensitive production data.
17
+
18
+ D-DES is a custom implementation of the classic DES (Data Encryption Standard) block cipher, modified to feature a **Dynamic S-Box**.
19
+
20
+ ## Requirements
21
+ - **Python 3.13.3+**: This library leverages modern Python type hinting and bit-manipulation features. Ensure you are running Python 3.13.3 or higher.
22
+
23
+ ## Features
24
+ - Standard 64-bit block size and 56-bit effective key.
25
+ - 16 Feistel rounds with standard permutation tables (IP, IP-1, E, P, PC-1, PC-2).
26
+ - **Dynamic S-Box**: Replaces the 8 static S-boxes with a single, dynamically generated $6 \times 4$ S-box completely seeded by the encryption key.
27
+ - **CBC Mode Support**: Prevents repeating data blocks by utilizing an Initialization Vector (IV).
28
+ - **PKCS#7 Padding**: Safely encrypts and decrypts files of any arbitrary size.
29
+ - **CLI Utility**: Built-in command line interface to seamlessly encrypt/decrypt any file on your computer.
30
+ - **Operational Logger**: Automatically and securely logs operation metadata to a `logs.json` file without leaking secrets or plaintext.
31
+
32
+ ## Installation
33
+
34
+ You can install the package directly from PyPI. In your terminal, run:
35
+ ```bash
36
+ pip install ddes-dua-crypto
37
+ ```
38
+
39
+ ## CLI Usage
40
+
41
+ The package installs a globally available `ddes-cli` tool.
42
+
43
+ **To encrypt a file (CBC mode):**
44
+ ```bash
45
+ ddes-cli encrypt -k "8bytekey" -m CBC --iv "8byte_iv" -i plaintext.txt -o encrypted.bin
46
+ ```
47
+
48
+ **To decrypt a file:**
49
+ ```bash
50
+ ddes-cli decrypt -k "8bytekey" -m CBC --iv "8byte_iv" -i encrypted.bin -o decrypted.txt
51
+ ```
52
+
53
+ ## Python API Usage
54
+
55
+ You can also use the cipher directly inside your own Python projects.
56
+
57
+ ```python
58
+ from ddes import DDESCipher
59
+
60
+ # 8-byte key and IV
61
+ key = b'8bytekey'
62
+ iv = b'8byte_iv'
63
+
64
+ # Initialize in CBC mode
65
+ cipher = DDESCipher(key, mode='CBC', iv=iv)
66
+
67
+ # Encrypt
68
+ plaintext = b'Hello World! This is D-DES.'
69
+ ciphertext = cipher.encrypt(plaintext)
70
+ print(f"Ciphertext (Hex): {ciphertext.hex()}")
71
+
72
+ # Decrypt
73
+ decrypted = cipher.decrypt(ciphertext)
74
+ print(f"Decrypted: {decrypted.decode('utf-8')}")
75
+ ```
76
+
77
+ ## Operational Logger
78
+ All CLI encryption and decryption commands automatically append an audit trail to `logs.json`. The logger securely stores the timestamp, operation type, mode, and success status. **It never stores keys or file contents.**
79
+
80
+ **Example Audit Log (`logs.json`):**
81
+ ```json
82
+ [
83
+ {
84
+ "timestamp": "2026-05-08T19:02:15.123456",
85
+ "operation": "Encrypt",
86
+ "mode": "CBC",
87
+ "status": "Success"
88
+ }
89
+ ]
90
+ ```
@@ -0,0 +1,77 @@
1
+ # D-DES (Dynamic Data Encryption Standard)
2
+
3
+ > **⚠️ Security Disclaimer:** This is an experimental cryptographic tool for research and educational purposes. It is not intended for securing sensitive production data.
4
+
5
+ D-DES is a custom implementation of the classic DES (Data Encryption Standard) block cipher, modified to feature a **Dynamic S-Box**.
6
+
7
+ ## Requirements
8
+ - **Python 3.13.3+**: This library leverages modern Python type hinting and bit-manipulation features. Ensure you are running Python 3.13.3 or higher.
9
+
10
+ ## Features
11
+ - Standard 64-bit block size and 56-bit effective key.
12
+ - 16 Feistel rounds with standard permutation tables (IP, IP-1, E, P, PC-1, PC-2).
13
+ - **Dynamic S-Box**: Replaces the 8 static S-boxes with a single, dynamically generated $6 \times 4$ S-box completely seeded by the encryption key.
14
+ - **CBC Mode Support**: Prevents repeating data blocks by utilizing an Initialization Vector (IV).
15
+ - **PKCS#7 Padding**: Safely encrypts and decrypts files of any arbitrary size.
16
+ - **CLI Utility**: Built-in command line interface to seamlessly encrypt/decrypt any file on your computer.
17
+ - **Operational Logger**: Automatically and securely logs operation metadata to a `logs.json` file without leaking secrets or plaintext.
18
+
19
+ ## Installation
20
+
21
+ You can install the package directly from PyPI. In your terminal, run:
22
+ ```bash
23
+ pip install ddes-dua-crypto
24
+ ```
25
+
26
+ ## CLI Usage
27
+
28
+ The package installs a globally available `ddes-cli` tool.
29
+
30
+ **To encrypt a file (CBC mode):**
31
+ ```bash
32
+ ddes-cli encrypt -k "8bytekey" -m CBC --iv "8byte_iv" -i plaintext.txt -o encrypted.bin
33
+ ```
34
+
35
+ **To decrypt a file:**
36
+ ```bash
37
+ ddes-cli decrypt -k "8bytekey" -m CBC --iv "8byte_iv" -i encrypted.bin -o decrypted.txt
38
+ ```
39
+
40
+ ## Python API Usage
41
+
42
+ You can also use the cipher directly inside your own Python projects.
43
+
44
+ ```python
45
+ from ddes import DDESCipher
46
+
47
+ # 8-byte key and IV
48
+ key = b'8bytekey'
49
+ iv = b'8byte_iv'
50
+
51
+ # Initialize in CBC mode
52
+ cipher = DDESCipher(key, mode='CBC', iv=iv)
53
+
54
+ # Encrypt
55
+ plaintext = b'Hello World! This is D-DES.'
56
+ ciphertext = cipher.encrypt(plaintext)
57
+ print(f"Ciphertext (Hex): {ciphertext.hex()}")
58
+
59
+ # Decrypt
60
+ decrypted = cipher.decrypt(ciphertext)
61
+ print(f"Decrypted: {decrypted.decode('utf-8')}")
62
+ ```
63
+
64
+ ## Operational Logger
65
+ All CLI encryption and decryption commands automatically append an audit trail to `logs.json`. The logger securely stores the timestamp, operation type, mode, and success status. **It never stores keys or file contents.**
66
+
67
+ **Example Audit Log (`logs.json`):**
68
+ ```json
69
+ [
70
+ {
71
+ "timestamp": "2026-05-08T19:02:15.123456",
72
+ "operation": "Encrypt",
73
+ "mode": "CBC",
74
+ "status": "Success"
75
+ }
76
+ ]
77
+ ```
@@ -0,0 +1,25 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "ddes-dua-crypto"
7
+ version = "0.1.0"
8
+ authors = [
9
+ { name="Dua Amir", email="duaamir392004@gmail.com" },
10
+ ]
11
+ description = "Dynamic Data Encryption Standard (D-DES) with a single dynamic S-box."
12
+ readme = "README.md"
13
+ requires-python = ">=3.13"
14
+ classifiers = [
15
+ "Programming Language :: Python :: 3.13",
16
+ "License :: OSI Approved :: MIT License",
17
+ "Operating System :: OS Independent",
18
+ "Topic :: Security :: Cryptography",
19
+ ]
20
+
21
+ [project.urls]
22
+ "Homepage" = "https://github.com/duaamir39/d-des.git"
23
+
24
+ [project.scripts]
25
+ ddes-cli = "ddes.cli:main"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,6 @@
1
+ """
2
+ D-DES: Dynamic Data Encryption Standard library.
3
+ """
4
+ from .cipher import DDESCipher
5
+
6
+ __all__ = ["DDESCipher"]
@@ -0,0 +1,183 @@
1
+ import hashlib
2
+ import random
3
+ from typing import List
4
+
5
+ from . import tables
6
+ from . import utils
7
+
8
+ class DDESCipher:
9
+ def __init__(self, key: bytes, mode: str = 'ECB', iv: bytes = None):
10
+ """
11
+ Initializes the D-DES cipher with a given 8-byte key.
12
+ """
13
+ if len(key) != 8:
14
+ raise ValueError("Key must be exactly 8 bytes (64 bits)")
15
+ if mode not in ('ECB', 'CBC'):
16
+ raise ValueError("Mode must be 'ECB' or 'CBC'")
17
+ if mode == 'CBC':
18
+ if iv is None or len(iv) != 8:
19
+ raise ValueError("CBC mode requires exactly an 8-byte IV")
20
+ self.iv = iv
21
+ else:
22
+ self.iv = None
23
+
24
+ self.mode = mode
25
+ self.key_int = int.from_bytes(key, byteorder='big')
26
+ self.subkeys = self._generate_subkeys()
27
+ self.sbox = self._generate_dynamic_sbox(key)
28
+
29
+ def _generate_dynamic_sbox(self, key: bytes) -> List[int]:
30
+ """
31
+ Generate one single 6x4 S-box (64 entries total, values 0-15) based on the key.
32
+ """
33
+ # Derive a seed from the key using SHA-256
34
+ seed = int(hashlib.sha256(key).hexdigest(), 16)
35
+
36
+ # We need a 64-entry S-box with values 0-15.
37
+ # A typical S-box has 4 rows of 16 columns.
38
+ # For our dynamic S-box, we'll create an array of 64 elements,
39
+ # consisting of exactly four 0s, four 1s, ..., four 15s.
40
+ sbox = [i % 16 for i in range(64)]
41
+
42
+ # Shuffle the S-box deterministically using the derived seed
43
+ r = random.Random(seed)
44
+ r.shuffle(sbox)
45
+
46
+ return sbox
47
+
48
+ def _generate_subkeys(self) -> List[int]:
49
+ """
50
+ Generate the 16 48-bit subkeys using the standard DES key schedule.
51
+ """
52
+ subkeys = []
53
+
54
+ # Apply PC-1 to the 64-bit key
55
+ pc1_out = utils.permute(self.key_int, tables.PC_1, 64, 56)
56
+
57
+ # Split into two 28-bit halves
58
+ c, d = utils.split_half(pc1_out, 56)
59
+
60
+ for shift in tables.SHIFTS:
61
+ # Circular left shift each half
62
+ c = utils.left_shift(c, shift, 28)
63
+ d = utils.left_shift(d, shift, 28)
64
+
65
+ # Merge halves
66
+ cd = utils.merge_half(c, d, 28)
67
+
68
+ # Apply PC-2
69
+ subkey = utils.permute(cd, tables.PC_2, 56, 48)
70
+ subkeys.append(subkey)
71
+
72
+ return subkeys
73
+
74
+ def _feistel(self, right_half: int, subkey: int) -> int:
75
+ """
76
+ The core Feistel function f(R, K).
77
+ """
78
+ # 1. Expansion P-box
79
+ expanded = utils.permute(right_half, tables.E, 32, 48)
80
+
81
+ # 2. Key mixing
82
+ xored = expanded ^ subkey
83
+
84
+ # 3. Dynamic S-box substitution
85
+ # Split 48 bits into 8 6-bit blocks, pass each through the same single S-box
86
+ sbox_out = 0
87
+ for i in range(8):
88
+ # Extract 6 bits from left to right
89
+ shift_amount = 48 - 6 * (i + 1)
90
+ six_bit_block = (xored >> shift_amount) & 0x3F
91
+
92
+ # Use the 6 bits as an index into our 64-entry dynamic S-box
93
+ four_bit_out = self.sbox[six_bit_block]
94
+
95
+ # Append to the 32-bit output
96
+ sbox_out |= (four_bit_out << (32 - 4 * (i + 1)))
97
+
98
+ # 4. Permutation
99
+ return utils.permute(sbox_out, tables.P, 32, 32)
100
+
101
+ def _process_block(self, block: bytes, decrypt: bool = False) -> bytes:
102
+ """
103
+ Encrypts or decrypts a single 64-bit block.
104
+ """
105
+ if len(block) != 8:
106
+ raise ValueError("Block must be exactly 8 bytes (64 bits)")
107
+
108
+ block_int = int.from_bytes(block, byteorder='big')
109
+
110
+ # Initial Permutation (IP)
111
+ ip_out = utils.permute(block_int, tables.IP, 64, 64)
112
+
113
+ # Split into 32-bit halves
114
+ left, right = utils.split_half(ip_out, 64)
115
+
116
+ # 16 Feistel rounds
117
+ # Decryption uses subkeys in reverse order (K16 to K1)
118
+ subkeys = self.subkeys[::-1] if decrypt else self.subkeys
119
+
120
+ for subkey in subkeys:
121
+ next_left = right
122
+ next_right = left ^ self._feistel(right, subkey)
123
+ left, right = next_left, next_right
124
+
125
+ # Swap halves after the 16th round (before final permutation)
126
+ pre_output = utils.merge_half(right, left, 32)
127
+
128
+ # Final Permutation (IP-1)
129
+ final_out = utils.permute(pre_output, tables.IP_INV, 64, 64)
130
+
131
+ return final_out.to_bytes(8, byteorder='big')
132
+
133
+ def encrypt(self, data: bytes) -> bytes:
134
+ """
135
+ Encrypt data using D-DES (ECB or CBC mode with PKCS#7 padding).
136
+ """
137
+ # PKCS7 padding
138
+ pad_len = 8 - (len(data) % 8)
139
+ padded_data = data + bytes([pad_len] * pad_len)
140
+
141
+ ciphertext = bytearray()
142
+ prev_block = self.iv
143
+
144
+ for i in range(0, len(padded_data), 8):
145
+ block = padded_data[i:i+8]
146
+
147
+ if self.mode == 'CBC':
148
+ # XOR plaintext block with previous ciphertext block (or IV)
149
+ block = bytes(a ^ b for a, b in zip(block, prev_block))
150
+
151
+ enc_block = self._process_block(block, decrypt=False)
152
+ ciphertext.extend(enc_block)
153
+ prev_block = enc_block
154
+
155
+ return bytes(ciphertext)
156
+
157
+ def decrypt(self, data: bytes) -> bytes:
158
+ """
159
+ Decrypt data using D-DES (ECB or CBC mode with PKCS#7 unpadding).
160
+ """
161
+ if len(data) % 8 != 0:
162
+ raise ValueError("Ciphertext length must be a multiple of 8 bytes")
163
+
164
+ plaintext = bytearray()
165
+ prev_block = self.iv
166
+
167
+ for i in range(0, len(data), 8):
168
+ block = data[i:i+8]
169
+ dec_block = self._process_block(block, decrypt=True)
170
+
171
+ if self.mode == 'CBC':
172
+ # XOR decrypted block with previous ciphertext block (or IV)
173
+ dec_block = bytes(a ^ b for a, b in zip(dec_block, prev_block))
174
+ prev_block = block
175
+
176
+ plaintext.extend(dec_block)
177
+
178
+ # PKCS7 unpadding
179
+ pad_len = plaintext[-1]
180
+ if pad_len < 1 or pad_len > 8:
181
+ raise ValueError("Invalid padding detected during decryption.")
182
+
183
+ return bytes(plaintext[:-pad_len])
@@ -0,0 +1,61 @@
1
+ import argparse
2
+ import sys
3
+ from .cipher import DDESCipher
4
+ from .utils import Logger
5
+
6
+ def main():
7
+ parser = argparse.ArgumentParser(description="D-DES (Dynamic Data Encryption Standard) CLI")
8
+
9
+ parser.add_argument('action', choices=['encrypt', 'decrypt'], help="Action to perform")
10
+ parser.add_argument('-k', '--key', required=True, help="8-byte encryption key")
11
+ parser.add_argument('-i', '--input', required=True, help="Input file path")
12
+ parser.add_argument('-o', '--output', required=True, help="Output file path")
13
+ parser.add_argument('-m', '--mode', choices=['ECB', 'CBC'], default='CBC', help="Block cipher mode (default: CBC)")
14
+ parser.add_argument('--iv', help="8-byte initialization vector (required for CBC mode)")
15
+
16
+ args = parser.parse_args()
17
+
18
+ key_bytes = args.key.encode('utf-8')
19
+ if len(key_bytes) != 8:
20
+ print("Error: Key must be exactly 8 characters long.")
21
+ sys.exit(1)
22
+
23
+ iv_bytes = None
24
+ if args.mode == 'CBC':
25
+ if not args.iv:
26
+ print("Error: CBC mode requires an IV (--iv).")
27
+ sys.exit(1)
28
+ iv_bytes = args.iv.encode('utf-8')
29
+ if len(iv_bytes) != 8:
30
+ print("Error: IV must be exactly 8 characters long.")
31
+ sys.exit(1)
32
+
33
+ logger = Logger("logs.json")
34
+
35
+ try:
36
+ cipher = DDESCipher(key=key_bytes, mode=args.mode, iv=iv_bytes)
37
+
38
+ with open(args.input, 'rb') as f:
39
+ data = f.read()
40
+
41
+ if args.action == 'encrypt':
42
+ result = cipher.encrypt(data)
43
+ print(f"Successfully encrypted {len(data)} bytes to {len(result)} bytes.")
44
+ print(f"\nCiphertext (Hex): {result.hex()}\n")
45
+ logger.log("Encrypt", args.mode, "Success")
46
+ else:
47
+ result = cipher.decrypt(data)
48
+ print(f"Successfully decrypted {len(data)} bytes to {len(result)} bytes.")
49
+ print(f"\nDecrypted Data (Hex): {result.hex()}\n")
50
+ logger.log("Decrypt", args.mode, "Success")
51
+
52
+ with open(args.output, 'wb') as f:
53
+ f.write(result)
54
+
55
+ except Exception as e:
56
+ print(f"Error: {e}")
57
+ logger.log(args.action.capitalize(), args.mode, f"Failure - {str(e)}")
58
+ sys.exit(1)
59
+
60
+ if __name__ == '__main__':
61
+ main()
@@ -0,0 +1,74 @@
1
+ # Initial Permutation (IP)
2
+ IP = [
3
+ 58, 50, 42, 34, 26, 18, 10, 2,
4
+ 60, 52, 44, 36, 28, 20, 12, 4,
5
+ 62, 54, 46, 38, 30, 22, 14, 6,
6
+ 64, 56, 48, 40, 32, 24, 16, 8,
7
+ 57, 49, 41, 33, 25, 17, 9, 1,
8
+ 59, 51, 43, 35, 27, 19, 11, 3,
9
+ 61, 53, 45, 37, 29, 21, 13, 5,
10
+ 63, 55, 47, 39, 31, 23, 15, 7
11
+ ]
12
+
13
+ # Final Permutation (IP-1)
14
+ IP_INV = [
15
+ 40, 8, 48, 16, 56, 24, 64, 32,
16
+ 39, 7, 47, 15, 55, 23, 63, 31,
17
+ 38, 6, 46, 14, 54, 22, 62, 30,
18
+ 37, 5, 45, 13, 53, 21, 61, 29,
19
+ 36, 4, 44, 12, 52, 20, 60, 28,
20
+ 35, 3, 43, 11, 51, 19, 59, 27,
21
+ 34, 2, 42, 10, 50, 18, 58, 26,
22
+ 33, 1, 41, 9, 49, 17, 57, 25
23
+ ]
24
+
25
+ # Expansion Permutation (E)
26
+ E = [
27
+ 32, 1, 2, 3, 4, 5,
28
+ 4, 5, 6, 7, 8, 9,
29
+ 8, 9, 10, 11, 12, 13,
30
+ 12, 13, 14, 15, 16, 17,
31
+ 16, 17, 18, 19, 20, 21,
32
+ 20, 21, 22, 23, 24, 25,
33
+ 24, 25, 26, 27, 28, 29,
34
+ 28, 29, 30, 31, 32, 1
35
+ ]
36
+
37
+ # Permutation Function (P)
38
+ P = [
39
+ 16, 7, 20, 21,
40
+ 29, 12, 28, 17,
41
+ 1, 15, 23, 26,
42
+ 5, 18, 31, 10,
43
+ 2, 8, 24, 14,
44
+ 32, 27, 3, 9,
45
+ 19, 13, 30, 6,
46
+ 22, 11, 4, 25
47
+ ]
48
+
49
+ # Permuted Choice 1 (PC-1)
50
+ PC_1 = [
51
+ 57, 49, 41, 33, 25, 17, 9,
52
+ 1, 58, 50, 42, 34, 26, 18,
53
+ 10, 2, 59, 51, 43, 35, 27,
54
+ 19, 11, 3, 60, 52, 44, 36,
55
+ 63, 55, 47, 39, 31, 23, 15,
56
+ 7, 62, 54, 46, 38, 30, 22,
57
+ 14, 6, 61, 53, 45, 37, 29,
58
+ 21, 13, 5, 28, 20, 12, 4
59
+ ]
60
+
61
+ # Permuted Choice 2 (PC-2)
62
+ PC_2 = [
63
+ 14, 17, 11, 24, 1, 5,
64
+ 3, 28, 15, 6, 21, 10,
65
+ 23, 19, 12, 4, 26, 8,
66
+ 16, 7, 27, 20, 13, 2,
67
+ 41, 52, 31, 37, 47, 55,
68
+ 30, 40, 51, 45, 33, 48,
69
+ 44, 49, 39, 56, 34, 53,
70
+ 46, 42, 50, 36, 29, 32
71
+ ]
72
+
73
+ # Shifts per round
74
+ SHIFTS = [1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1]
@@ -0,0 +1,58 @@
1
+ import json
2
+ import os
3
+ import datetime
4
+
5
+ def permute(block: int, table: list[int], input_size: int, output_size: int) -> int:
6
+ """Permute an integer block using the given table."""
7
+ output = 0
8
+ for i, bit_pos in enumerate(table):
9
+ # DES tables are 1-indexed, so we subtract 1.
10
+ # The bit position from the left in a block of size input_size
11
+ shift = input_size - bit_pos
12
+ bit = (block >> shift) & 1
13
+ output |= (bit << (output_size - 1 - i))
14
+ return output
15
+
16
+ def split_half(block: int, size: int) -> tuple[int, int]:
17
+ """Split an integer block into two halves."""
18
+ half_size = size // 2
19
+ mask = (1 << half_size) - 1
20
+ left = block >> half_size
21
+ right = block & mask
22
+ return left, right
23
+
24
+ def merge_half(left: int, right: int, half_size: int) -> int:
25
+ """Merge two integer halves into a single block."""
26
+ return (left << half_size) | right
27
+
28
+ def left_shift(block: int, shift: int, size: int) -> int:
29
+ """Circular left shift an integer block by the given shift amount."""
30
+ mask = (1 << size) - 1
31
+ return ((block << shift) | (block >> (size - shift))) & mask
32
+
33
+ class Logger:
34
+ def __init__(self, log_file="logs.json"):
35
+ self.log_file = log_file
36
+
37
+ def log(self, operation: str, mode: str, status: str):
38
+ log_entry = {
39
+ "timestamp": datetime.datetime.now().isoformat(),
40
+ "operation": operation,
41
+ "mode": mode,
42
+ "status": status
43
+ }
44
+
45
+ logs = []
46
+ if os.path.exists(self.log_file):
47
+ try:
48
+ with open(self.log_file, "r", encoding="utf-8") as f:
49
+ content = f.read().strip()
50
+ if content:
51
+ logs = json.loads(content)
52
+ except json.JSONDecodeError:
53
+ pass
54
+
55
+ logs.append(log_entry)
56
+
57
+ with open(self.log_file, "w", encoding="utf-8") as f:
58
+ json.dump(logs, f, indent=4)
@@ -0,0 +1,90 @@
1
+ Metadata-Version: 2.4
2
+ Name: ddes-dua-crypto
3
+ Version: 0.1.0
4
+ Summary: Dynamic Data Encryption Standard (D-DES) with a single dynamic S-box.
5
+ Author-email: Dua Amir <duaamir392004@gmail.com>
6
+ Project-URL: Homepage, https://github.com/duaamir39/d-des.git
7
+ Classifier: Programming Language :: Python :: 3.13
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Operating System :: OS Independent
10
+ Classifier: Topic :: Security :: Cryptography
11
+ Requires-Python: >=3.13
12
+ Description-Content-Type: text/markdown
13
+
14
+ # D-DES (Dynamic Data Encryption Standard)
15
+
16
+ > **⚠️ Security Disclaimer:** This is an experimental cryptographic tool for research and educational purposes. It is not intended for securing sensitive production data.
17
+
18
+ D-DES is a custom implementation of the classic DES (Data Encryption Standard) block cipher, modified to feature a **Dynamic S-Box**.
19
+
20
+ ## Requirements
21
+ - **Python 3.13.3+**: This library leverages modern Python type hinting and bit-manipulation features. Ensure you are running Python 3.13.3 or higher.
22
+
23
+ ## Features
24
+ - Standard 64-bit block size and 56-bit effective key.
25
+ - 16 Feistel rounds with standard permutation tables (IP, IP-1, E, P, PC-1, PC-2).
26
+ - **Dynamic S-Box**: Replaces the 8 static S-boxes with a single, dynamically generated $6 \times 4$ S-box completely seeded by the encryption key.
27
+ - **CBC Mode Support**: Prevents repeating data blocks by utilizing an Initialization Vector (IV).
28
+ - **PKCS#7 Padding**: Safely encrypts and decrypts files of any arbitrary size.
29
+ - **CLI Utility**: Built-in command line interface to seamlessly encrypt/decrypt any file on your computer.
30
+ - **Operational Logger**: Automatically and securely logs operation metadata to a `logs.json` file without leaking secrets or plaintext.
31
+
32
+ ## Installation
33
+
34
+ You can install the package directly from PyPI. In your terminal, run:
35
+ ```bash
36
+ pip install ddes-dua-crypto
37
+ ```
38
+
39
+ ## CLI Usage
40
+
41
+ The package installs a globally available `ddes-cli` tool.
42
+
43
+ **To encrypt a file (CBC mode):**
44
+ ```bash
45
+ ddes-cli encrypt -k "8bytekey" -m CBC --iv "8byte_iv" -i plaintext.txt -o encrypted.bin
46
+ ```
47
+
48
+ **To decrypt a file:**
49
+ ```bash
50
+ ddes-cli decrypt -k "8bytekey" -m CBC --iv "8byte_iv" -i encrypted.bin -o decrypted.txt
51
+ ```
52
+
53
+ ## Python API Usage
54
+
55
+ You can also use the cipher directly inside your own Python projects.
56
+
57
+ ```python
58
+ from ddes import DDESCipher
59
+
60
+ # 8-byte key and IV
61
+ key = b'8bytekey'
62
+ iv = b'8byte_iv'
63
+
64
+ # Initialize in CBC mode
65
+ cipher = DDESCipher(key, mode='CBC', iv=iv)
66
+
67
+ # Encrypt
68
+ plaintext = b'Hello World! This is D-DES.'
69
+ ciphertext = cipher.encrypt(plaintext)
70
+ print(f"Ciphertext (Hex): {ciphertext.hex()}")
71
+
72
+ # Decrypt
73
+ decrypted = cipher.decrypt(ciphertext)
74
+ print(f"Decrypted: {decrypted.decode('utf-8')}")
75
+ ```
76
+
77
+ ## Operational Logger
78
+ All CLI encryption and decryption commands automatically append an audit trail to `logs.json`. The logger securely stores the timestamp, operation type, mode, and success status. **It never stores keys or file contents.**
79
+
80
+ **Example Audit Log (`logs.json`):**
81
+ ```json
82
+ [
83
+ {
84
+ "timestamp": "2026-05-08T19:02:15.123456",
85
+ "operation": "Encrypt",
86
+ "mode": "CBC",
87
+ "status": "Success"
88
+ }
89
+ ]
90
+ ```
@@ -0,0 +1,13 @@
1
+ README.md
2
+ pyproject.toml
3
+ src/ddes/__init__.py
4
+ src/ddes/cipher.py
5
+ src/ddes/cli.py
6
+ src/ddes/tables.py
7
+ src/ddes/utils.py
8
+ src/ddes_dua_crypto.egg-info/PKG-INFO
9
+ src/ddes_dua_crypto.egg-info/SOURCES.txt
10
+ src/ddes_dua_crypto.egg-info/dependency_links.txt
11
+ src/ddes_dua_crypto.egg-info/entry_points.txt
12
+ src/ddes_dua_crypto.egg-info/top_level.txt
13
+ tests/test_cipher.py
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ ddes-cli = ddes.cli:main
@@ -0,0 +1,42 @@
1
+ import unittest
2
+ from ddes.cipher import DDESCipher
3
+
4
+ class TestDDES(unittest.TestCase):
5
+ def test_encryption_decryption_ecb(self):
6
+ key = b'secret!!'
7
+ cipher = DDESCipher(key, mode='ECB')
8
+
9
+ plaintext = b'Cryptography is fascinating!'
10
+ ciphertext = cipher.encrypt(plaintext)
11
+
12
+ self.assertNotEqual(plaintext, ciphertext)
13
+
14
+ decrypted = cipher.decrypt(ciphertext)
15
+ self.assertEqual(plaintext, decrypted)
16
+
17
+ def test_encryption_decryption_cbc(self):
18
+ key = b'secret!!'
19
+ iv = b'init_vec'
20
+ cipher = DDESCipher(key, mode='CBC', iv=iv)
21
+
22
+ plaintext = b'Cryptography is fascinating! CBC mode is secure.'
23
+ ciphertext = cipher.encrypt(plaintext)
24
+
25
+ self.assertNotEqual(plaintext, ciphertext)
26
+
27
+ decrypted = cipher.decrypt(ciphertext)
28
+ self.assertEqual(plaintext, decrypted)
29
+
30
+ def test_different_keys_different_sboxes(self):
31
+ key1 = b'key_one!'
32
+ key2 = b'key_two!'
33
+
34
+ cipher1 = DDESCipher(key1)
35
+ cipher2 = DDESCipher(key2)
36
+
37
+ # Since the keys are different, their seeds are different,
38
+ # and therefore the shuffled S-boxes should be highly likely to differ.
39
+ self.assertNotEqual(cipher1.sbox, cipher2.sbox)
40
+
41
+ if __name__ == '__main__':
42
+ unittest.main()