project-des 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.
- project_des-0.1.0/PKG-INFO +111 -0
- project_des-0.1.0/README.md +96 -0
- project_des-0.1.0/project_DES/__init__.py +2 -0
- project_des-0.1.0/project_DES/des_core/__init__.py +1 -0
- project_des-0.1.0/project_DES/des_core/bit_utils.py +35 -0
- project_des-0.1.0/project_DES/des_core/crypto.py +59 -0
- project_des-0.1.0/project_DES/des_core/feistel.py +23 -0
- project_des-0.1.0/project_DES/des_core/key.py +63 -0
- project_des-0.1.0/project_DES/des_core/mangler.py +97 -0
- project_des-0.1.0/project_DES/des_core/permute.py +14 -0
- project_des-0.1.0/project_DES/project_DES.py +158 -0
- project_des-0.1.0/project_des.egg-info/PKG-INFO +111 -0
- project_des-0.1.0/project_des.egg-info/SOURCES.txt +15 -0
- project_des-0.1.0/project_des.egg-info/dependency_links.txt +1 -0
- project_des-0.1.0/project_des.egg-info/top_level.txt +1 -0
- project_des-0.1.0/pyproject.toml +31 -0
- project_des-0.1.0/setup.cfg +4 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: project-des
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Educational Python implementation of the Data Encryption Standard (DES)
|
|
5
|
+
Author-email: shrimp2845 <goldencheng15@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/shrimp2845-tw/project_DES
|
|
8
|
+
Project-URL: Repository, https://github.com/shrimp2845-tw/project_DES
|
|
9
|
+
Keywords: des,cryptography,education,feistel
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Requires-Python: >=3.10
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
16
|
+
**_Warning: This implementation is intended for self learning purposes only and should not be used for real-world security._**
|
|
17
|
+
|
|
18
|
+
## About
|
|
19
|
+
|
|
20
|
+
- **Author:** shrimp2845
|
|
21
|
+
- **Version:** 0.1.0
|
|
22
|
+
- **License:** MIT
|
|
23
|
+
|
|
24
|
+
This is an educational Python implementation of the Data Encryption Standard (DES). This project breaks down the complex Feistel network into human-readable modules following the official NIST specifications, and supports ECB and CBC modes for real file encryption.
|
|
25
|
+
|
|
26
|
+
## Installation & Usage
|
|
27
|
+
|
|
28
|
+
### install
|
|
29
|
+
Requires Python 3.10+
|
|
30
|
+
```bash
|
|
31
|
+
pip install project-des
|
|
32
|
+
```
|
|
33
|
+
### usage
|
|
34
|
+
```bash
|
|
35
|
+
python -c "from project_DES import DES; cipher = DES(b'key'); print(cipher.encrypt(b'data').hex())"
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Docs
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
class initialize
|
|
42
|
+
----------------------
|
|
43
|
+
project_DES.DES(key, mode='ECB')
|
|
44
|
+
|
|
45
|
+
key
|
|
46
|
+
-> 8 bytes or key file name
|
|
47
|
+
containing an encryption key,
|
|
48
|
+
if key is str, it extract
|
|
49
|
+
first 8 bytes in the file
|
|
50
|
+
|
|
51
|
+
mode
|
|
52
|
+
-> optional, block cipher mode of
|
|
53
|
+
operation that used,ECB and CBC
|
|
54
|
+
available in current version
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
methods
|
|
58
|
+
----------------------
|
|
59
|
+
|
|
60
|
+
encrypt(data: bytes) -> bytes,
|
|
61
|
+
decrypt(data: bytes) -> bytes
|
|
62
|
+
-> return encrypt/decrypt bytes data
|
|
63
|
+
|
|
64
|
+
encrypt_file(name: str, new_name: str),
|
|
65
|
+
decrypt_file(name: str, new_name: str)
|
|
66
|
+
-> encrypt/decrypt target file 'name'
|
|
67
|
+
and save it as 'new_name'
|
|
68
|
+
|
|
69
|
+
get_key(), get_mode()
|
|
70
|
+
-> get current using key/mode of
|
|
71
|
+
operation
|
|
72
|
+
|
|
73
|
+
change_key(key: str or bytes),
|
|
74
|
+
change_mode(mode: str)
|
|
75
|
+
-> change current using key/mode of
|
|
76
|
+
operation
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Examples
|
|
81
|
+
Project provides two examples: [string_cipher.py](https://github.com/shrimp2845-tw/project_DES/blob/main/examples/string_cipher.py) & [file_cipher.py](https://github.com/shrimp2845-tw/project_DES/blob/main/examples/file_cipher.py)
|
|
82
|
+
|
|
83
|
+
you can find samples of [file_cipher.py](https://github.com/shrimp2845-tw/project_DES/blob/main/examples/file_cipher.py) at:
|
|
84
|
+
[ecb_sample](https://github.com/shrimp2845-tw/project_DES/tree/main/examples/ecb_sample) & [cbc_sample](https://github.com/shrimp2845-tw/project_DES/tree/main/examples/cbc_sample)
|
|
85
|
+
|
|
86
|
+
### explanation for samples
|
|
87
|
+
**mumei.jpg**: plaintext file
|
|
88
|
+
|
|
89
|
+
**mumei.bin**: encrypt mumei.jpg with key ‘berries’ and mode ECB
|
|
90
|
+
|
|
91
|
+
**mumei_correct_decrypt**.jpg: decrypt mumei.bin with key ‘berries’ and mode ECB
|
|
92
|
+
|
|
93
|
+
**mumei_wrong_decrypt.jpg**: decrypt mumei.bin with key ‘friend’ and mode ECB
|
|
94
|
+
|
|
95
|
+
**ina.jpg**: plaintext file
|
|
96
|
+
|
|
97
|
+
**ina.bin**: encrypt ina.jpg with key ‘takodachi’ and mode CBC
|
|
98
|
+
|
|
99
|
+
**ina_correct_decrypt.jpg**: decrypt ina.bin with key ‘takodachi’ and mode CBC
|
|
100
|
+
|
|
101
|
+
**ina_wrong_decrypt.jpg**: decrypt ina.bin with key ‘violet’ and mode CBC
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
## Performance
|
|
105
|
+
This project is designed for learning and understanding how DES works rather than for practical encryption use. The implementation prioritizes readability and modular structure over performance.
|
|
106
|
+
|
|
107
|
+
As a result, encryption process is **very slow**. It takes nearly 2 minutes to encrypt 1MB file.
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
**_Warning: This implementation is intended for self learning purposes only and should not be used for real-world security._**
|
|
2
|
+
|
|
3
|
+
## About
|
|
4
|
+
|
|
5
|
+
- **Author:** shrimp2845
|
|
6
|
+
- **Version:** 0.1.0
|
|
7
|
+
- **License:** MIT
|
|
8
|
+
|
|
9
|
+
This is an educational Python implementation of the Data Encryption Standard (DES). This project breaks down the complex Feistel network into human-readable modules following the official NIST specifications, and supports ECB and CBC modes for real file encryption.
|
|
10
|
+
|
|
11
|
+
## Installation & Usage
|
|
12
|
+
|
|
13
|
+
### install
|
|
14
|
+
Requires Python 3.10+
|
|
15
|
+
```bash
|
|
16
|
+
pip install project-des
|
|
17
|
+
```
|
|
18
|
+
### usage
|
|
19
|
+
```bash
|
|
20
|
+
python -c "from project_DES import DES; cipher = DES(b'key'); print(cipher.encrypt(b'data').hex())"
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Docs
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
class initialize
|
|
27
|
+
----------------------
|
|
28
|
+
project_DES.DES(key, mode='ECB')
|
|
29
|
+
|
|
30
|
+
key
|
|
31
|
+
-> 8 bytes or key file name
|
|
32
|
+
containing an encryption key,
|
|
33
|
+
if key is str, it extract
|
|
34
|
+
first 8 bytes in the file
|
|
35
|
+
|
|
36
|
+
mode
|
|
37
|
+
-> optional, block cipher mode of
|
|
38
|
+
operation that used,ECB and CBC
|
|
39
|
+
available in current version
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
methods
|
|
43
|
+
----------------------
|
|
44
|
+
|
|
45
|
+
encrypt(data: bytes) -> bytes,
|
|
46
|
+
decrypt(data: bytes) -> bytes
|
|
47
|
+
-> return encrypt/decrypt bytes data
|
|
48
|
+
|
|
49
|
+
encrypt_file(name: str, new_name: str),
|
|
50
|
+
decrypt_file(name: str, new_name: str)
|
|
51
|
+
-> encrypt/decrypt target file 'name'
|
|
52
|
+
and save it as 'new_name'
|
|
53
|
+
|
|
54
|
+
get_key(), get_mode()
|
|
55
|
+
-> get current using key/mode of
|
|
56
|
+
operation
|
|
57
|
+
|
|
58
|
+
change_key(key: str or bytes),
|
|
59
|
+
change_mode(mode: str)
|
|
60
|
+
-> change current using key/mode of
|
|
61
|
+
operation
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Examples
|
|
66
|
+
Project provides two examples: [string_cipher.py](https://github.com/shrimp2845-tw/project_DES/blob/main/examples/string_cipher.py) & [file_cipher.py](https://github.com/shrimp2845-tw/project_DES/blob/main/examples/file_cipher.py)
|
|
67
|
+
|
|
68
|
+
you can find samples of [file_cipher.py](https://github.com/shrimp2845-tw/project_DES/blob/main/examples/file_cipher.py) at:
|
|
69
|
+
[ecb_sample](https://github.com/shrimp2845-tw/project_DES/tree/main/examples/ecb_sample) & [cbc_sample](https://github.com/shrimp2845-tw/project_DES/tree/main/examples/cbc_sample)
|
|
70
|
+
|
|
71
|
+
### explanation for samples
|
|
72
|
+
**mumei.jpg**: plaintext file
|
|
73
|
+
|
|
74
|
+
**mumei.bin**: encrypt mumei.jpg with key ‘berries’ and mode ECB
|
|
75
|
+
|
|
76
|
+
**mumei_correct_decrypt**.jpg: decrypt mumei.bin with key ‘berries’ and mode ECB
|
|
77
|
+
|
|
78
|
+
**mumei_wrong_decrypt.jpg**: decrypt mumei.bin with key ‘friend’ and mode ECB
|
|
79
|
+
|
|
80
|
+
**ina.jpg**: plaintext file
|
|
81
|
+
|
|
82
|
+
**ina.bin**: encrypt ina.jpg with key ‘takodachi’ and mode CBC
|
|
83
|
+
|
|
84
|
+
**ina_correct_decrypt.jpg**: decrypt ina.bin with key ‘takodachi’ and mode CBC
|
|
85
|
+
|
|
86
|
+
**ina_wrong_decrypt.jpg**: decrypt ina.bin with key ‘violet’ and mode CBC
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
## Performance
|
|
90
|
+
This project is designed for learning and understanding how DES works rather than for practical encryption use. The implementation prioritizes readability and modular structure over performance.
|
|
91
|
+
|
|
92
|
+
As a result, encryption process is **very slow**. It takes nearly 2 minutes to encrypt 1MB file.
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .crypto import BasicDES
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
|
|
2
|
+
meticulous_mode = True
|
|
3
|
+
|
|
4
|
+
def xor(b1: list[int], b2: list[int]) -> list[int]:
|
|
5
|
+
if meticulous_mode and (len(b1) != len(b2)):
|
|
6
|
+
raise ValueError('xor:invalid input')
|
|
7
|
+
op = [int(not(i == j)) for i, j in zip(b1, b2)]
|
|
8
|
+
return op
|
|
9
|
+
|
|
10
|
+
def bytes_to_bits(byd: bytes) -> list[int]:
|
|
11
|
+
btd = [int(bit) for byte in byd for bit in format(byte, '08b')]
|
|
12
|
+
return btd
|
|
13
|
+
|
|
14
|
+
def bits_to_bytes(btd: list[int]) -> bytes:
|
|
15
|
+
if meticulous_mode and len(btd)%8 != 0:
|
|
16
|
+
raise ValueError('bits_to_bytes:invalid input')
|
|
17
|
+
byd = bytes(int("".join(map(str, btd[i:i+8])), 2) for i in range(0, len(btd), 8))
|
|
18
|
+
return byd
|
|
19
|
+
|
|
20
|
+
def int_to_bits(bid: int, length: int) -> list[int]:
|
|
21
|
+
btd = [int(bit) for bit in format(bid, f'0{length}b')]
|
|
22
|
+
if meticulous_mode and len(format(bid, 'b')) > length:
|
|
23
|
+
raise ValueError('int_to_bits:invalid input')
|
|
24
|
+
return btd
|
|
25
|
+
|
|
26
|
+
def bits_to_int(btd: list[int]) -> int:
|
|
27
|
+
bid = int(''.join(map(str,btd)), 2)
|
|
28
|
+
return bid
|
|
29
|
+
|
|
30
|
+
def main():
|
|
31
|
+
pass
|
|
32
|
+
|
|
33
|
+
if __name__ == "__main__":
|
|
34
|
+
main()
|
|
35
|
+
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
from . import bit_utils as b
|
|
2
|
+
from . import permute as p
|
|
3
|
+
from . import key as k
|
|
4
|
+
from . import mangler as m
|
|
5
|
+
from . import feistel as f
|
|
6
|
+
|
|
7
|
+
meticulous_mode = True
|
|
8
|
+
|
|
9
|
+
def ip(btd: list[int]) -> list[int]:
|
|
10
|
+
if meticulous_mode and len(btd) != 64:
|
|
11
|
+
raise ValueError('ip:invalid input')
|
|
12
|
+
t = [58, 50, 42, 34, 26, 18, 10, 2,
|
|
13
|
+
60, 52, 44, 36, 28, 20, 12, 4,
|
|
14
|
+
62, 54, 46, 38, 30, 22, 14, 6,
|
|
15
|
+
64, 56, 48, 40, 32, 24, 16, 8,
|
|
16
|
+
57, 49, 41, 33, 25, 17, 9, 1,
|
|
17
|
+
59, 51, 43, 35, 27, 19, 11, 3,
|
|
18
|
+
61, 53, 45, 37, 29, 21, 13, 5,
|
|
19
|
+
63, 55, 47, 39, 31, 23, 15, 7]
|
|
20
|
+
nbtd = p.permutation(btd, t)
|
|
21
|
+
return nbtd
|
|
22
|
+
|
|
23
|
+
def fp(btd: list[int]) -> list[int]:
|
|
24
|
+
if meticulous_mode and len(btd) != 64:
|
|
25
|
+
raise ValueError('fp:invalid input')
|
|
26
|
+
t = [40, 8, 48, 16, 56, 24, 64, 32,
|
|
27
|
+
39, 7, 47, 15, 55, 23, 63, 31,
|
|
28
|
+
38, 6, 46, 14, 54, 22, 62, 30,
|
|
29
|
+
37, 5, 45, 13, 53, 21, 61, 29,
|
|
30
|
+
36, 4, 44, 12, 52, 20, 60, 28,
|
|
31
|
+
35, 3, 43, 11, 51, 19, 59, 27,
|
|
32
|
+
34, 2, 42, 10, 50, 18, 58, 26,
|
|
33
|
+
33, 1, 41, 9, 49, 17, 57, 25]
|
|
34
|
+
nbtd = p.permutation(btd, t)
|
|
35
|
+
return nbtd
|
|
36
|
+
|
|
37
|
+
class BasicDES:
|
|
38
|
+
def __init__(self, main_key: bytes):
|
|
39
|
+
self.rks = k.generate_round_key(main_key)
|
|
40
|
+
|
|
41
|
+
def encrypt(self, block: bytes) -> bytes:
|
|
42
|
+
if len(block) != 8:
|
|
43
|
+
raise ValueError('encrypt:invalid block')
|
|
44
|
+
plaintext = b.bytes_to_bits(block)
|
|
45
|
+
cipher_text = fp(f.feistel(ip(plaintext), self.rks, m.f))
|
|
46
|
+
return b.bits_to_bytes(cipher_text)
|
|
47
|
+
|
|
48
|
+
def decrypt(self, block: bytes) -> bytes:
|
|
49
|
+
if len(block) != 8:
|
|
50
|
+
raise ValueError('decrypt:invalid block')
|
|
51
|
+
cipher_text = b.bytes_to_bits(block)
|
|
52
|
+
plaintext = fp(f.feistel(ip(cipher_text), self.rks[::-1], m.f))
|
|
53
|
+
return b.bits_to_bytes(plaintext)
|
|
54
|
+
|
|
55
|
+
def main():
|
|
56
|
+
pass
|
|
57
|
+
|
|
58
|
+
if __name__ == "__main__":
|
|
59
|
+
main()
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from . import bit_utils as b
|
|
2
|
+
from typing import Callable
|
|
3
|
+
|
|
4
|
+
meticulous_mode = True
|
|
5
|
+
|
|
6
|
+
def feistel(btd:list[list[int]], rks: list[list[int]], f: Callable[[list[int], list[int]], list[int]]) -> list[int]:
|
|
7
|
+
if meticulous_mode and len(btd) % 2 != 0:
|
|
8
|
+
raise ValueError("feistel:invalid input")
|
|
9
|
+
def one_round(l: list[int], r: list[int], rk: list[int]) -> tuple[list[int], list[int]]:
|
|
10
|
+
nl = r
|
|
11
|
+
nr = b.xor(l, f(r, rk))
|
|
12
|
+
return nl, nr
|
|
13
|
+
l, r = btd[:len(btd)//2], btd[len(btd)//2:]
|
|
14
|
+
for rk in rks:
|
|
15
|
+
l, r = one_round(l, r, rk)
|
|
16
|
+
nbtd = r+l
|
|
17
|
+
return nbtd
|
|
18
|
+
|
|
19
|
+
def main():
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
if __name__ == "__main__":
|
|
23
|
+
main()
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
from . import bit_utils as b
|
|
2
|
+
from . import permute as p
|
|
3
|
+
|
|
4
|
+
meticulous_mode = True
|
|
5
|
+
|
|
6
|
+
def pc1(btd: list[int]) -> list[int]:
|
|
7
|
+
if meticulous_mode and len(btd) != 64:
|
|
8
|
+
raise ValueError('pc1:invalid input')
|
|
9
|
+
t = [57, 49, 41, 33, 25, 17, 9,
|
|
10
|
+
1, 58, 50, 42, 34, 26, 18,
|
|
11
|
+
10, 2, 59, 51, 43, 35, 27,
|
|
12
|
+
19, 11, 3, 60, 52, 44, 36,
|
|
13
|
+
63, 55, 47, 39, 31, 23, 15,
|
|
14
|
+
7, 62, 54, 46, 38, 30, 22,
|
|
15
|
+
14, 6, 61, 53, 45, 37, 29,
|
|
16
|
+
21, 13, 5, 28, 20, 12, 4]
|
|
17
|
+
nbtd = p.permutation(btd, t)
|
|
18
|
+
return nbtd
|
|
19
|
+
|
|
20
|
+
def pc2(btd: list[int]) -> list[int]:
|
|
21
|
+
if meticulous_mode and len(btd) != 56:
|
|
22
|
+
raise ValueError('pc2:invalid input')
|
|
23
|
+
t = [14, 17, 11, 24, 1, 5,
|
|
24
|
+
3, 28, 15, 6, 21, 10,
|
|
25
|
+
23, 19, 12, 4, 26, 8,
|
|
26
|
+
16, 7, 27, 20, 13, 2,
|
|
27
|
+
41, 52, 31, 37, 47, 55,
|
|
28
|
+
30, 40, 51, 45, 33, 48,
|
|
29
|
+
44, 49, 39, 56, 34, 53,
|
|
30
|
+
46, 42, 50, 36, 29, 32]
|
|
31
|
+
nbtd = p.permutation(btd, t)
|
|
32
|
+
return nbtd
|
|
33
|
+
|
|
34
|
+
def lcs(btd: list[int], t: int) -> list[int]:
|
|
35
|
+
op = btd[t:]+btd[:t]
|
|
36
|
+
return op
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def generate_round_key(mk: bytes) -> list[list[int]]:
|
|
40
|
+
if meticulous_mode and len(mk) != 8:
|
|
41
|
+
raise ValueError('generate_round_key:invalid main key')
|
|
42
|
+
def generate_ki(rd: int, lsk: list[int]) -> tuple[list[int], list[int]]:#rd -> 1 ~ 16
|
|
43
|
+
if meticulous_mode and len(lsk) != 56:
|
|
44
|
+
raise ValueError('generate_ki:invalid sub key')
|
|
45
|
+
l, r = lsk[:28], lsk[28:]
|
|
46
|
+
lcs_t = [None, 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1]
|
|
47
|
+
l, r = lcs(l, lcs_t[rd]), lcs(r, lcs_t[rd])
|
|
48
|
+
nsk = l+r
|
|
49
|
+
ki = pc2(nsk)
|
|
50
|
+
return ki, nsk
|
|
51
|
+
bmk = b.bytes_to_bits(mk)
|
|
52
|
+
sk = pc1(bmk)
|
|
53
|
+
rkl = []
|
|
54
|
+
for i in range(1, 17):
|
|
55
|
+
ki, sk = generate_ki(i, sk)
|
|
56
|
+
rkl.append(ki)
|
|
57
|
+
return rkl
|
|
58
|
+
|
|
59
|
+
def main():
|
|
60
|
+
pass
|
|
61
|
+
|
|
62
|
+
if __name__ == "__main__":
|
|
63
|
+
main()
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
from . import bit_utils as b
|
|
2
|
+
from . import permute as p
|
|
3
|
+
|
|
4
|
+
meticulous_mode = True
|
|
5
|
+
|
|
6
|
+
def p_box(btd: list[int]) -> list[int]:
|
|
7
|
+
if meticulous_mode and len(btd) != 32:
|
|
8
|
+
raise ValueError('p_box:invalid input')
|
|
9
|
+
t = [16, 7, 20, 21,
|
|
10
|
+
29, 12, 28, 17,
|
|
11
|
+
1, 15, 23, 26,
|
|
12
|
+
5, 18, 31, 10,
|
|
13
|
+
2, 8, 24, 14,
|
|
14
|
+
32, 27, 3, 9,
|
|
15
|
+
19, 13, 30, 6,
|
|
16
|
+
22, 11, 4, 25]
|
|
17
|
+
nbtd = p.permutation(btd, t)
|
|
18
|
+
return nbtd
|
|
19
|
+
|
|
20
|
+
def s_box(btd: list[int]) -> list[int]:
|
|
21
|
+
if meticulous_mode and len(btd) != 48:
|
|
22
|
+
raise ValueError('s_box:invalid input')
|
|
23
|
+
t ={1: [[14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7],
|
|
24
|
+
[0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8],
|
|
25
|
+
[4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0],
|
|
26
|
+
[15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13]],
|
|
27
|
+
2: [[15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10],
|
|
28
|
+
[3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5],
|
|
29
|
+
[0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15],
|
|
30
|
+
[13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9]],
|
|
31
|
+
3: [[10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8],
|
|
32
|
+
[13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1],
|
|
33
|
+
[13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7],
|
|
34
|
+
[1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12]],
|
|
35
|
+
4: [[7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15],
|
|
36
|
+
[13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9],
|
|
37
|
+
[10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4],
|
|
38
|
+
[3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14]],
|
|
39
|
+
5: [[2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9],
|
|
40
|
+
[14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6],
|
|
41
|
+
[4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14],
|
|
42
|
+
[11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3]],
|
|
43
|
+
6: [[12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11],
|
|
44
|
+
[10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8],
|
|
45
|
+
[9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6],
|
|
46
|
+
[4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13]],
|
|
47
|
+
7: [[4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1],
|
|
48
|
+
[13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6],
|
|
49
|
+
[1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2],
|
|
50
|
+
[6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12]],
|
|
51
|
+
8: [[13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7],
|
|
52
|
+
[1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2],
|
|
53
|
+
[7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8],
|
|
54
|
+
[2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11]],
|
|
55
|
+
}
|
|
56
|
+
def si(bd: list[int], box: int) -> list[int]:
|
|
57
|
+
nonlocal t
|
|
58
|
+
if meticulous_mode and (len(bd) != 6 or box > 8):
|
|
59
|
+
raise ValueError(f'si:invalid input')
|
|
60
|
+
row = b.bits_to_int([bd[0], bd[-1]])
|
|
61
|
+
column = b.bits_to_int(bd[1:-1])
|
|
62
|
+
nbd = b.int_to_bits(t[box][row][column], 4)
|
|
63
|
+
return nbd
|
|
64
|
+
sbtd = [btd[i:i+6] for i in range(0, 48, 6)]
|
|
65
|
+
nbtd = []
|
|
66
|
+
for i in range(8):
|
|
67
|
+
nbtd += si(sbtd[i], i+1)
|
|
68
|
+
return nbtd
|
|
69
|
+
|
|
70
|
+
def ep(btd: list[int]) -> list[int]:
|
|
71
|
+
if meticulous_mode and len(btd) != 32:
|
|
72
|
+
raise ValueError('ep:invalid input')
|
|
73
|
+
t = [32, 1, 2, 3, 4, 5,
|
|
74
|
+
4, 5, 6, 7, 8, 9,
|
|
75
|
+
8, 9, 10, 11, 12, 13,
|
|
76
|
+
12, 13, 14, 15, 16, 17,
|
|
77
|
+
16, 17, 18, 19, 20, 21,
|
|
78
|
+
20, 21, 22, 23, 24, 25,
|
|
79
|
+
24, 25, 26, 27, 28, 29,
|
|
80
|
+
28, 29, 30, 31, 32, 1]
|
|
81
|
+
nbtd = p.permutation(btd, t)
|
|
82
|
+
return nbtd
|
|
83
|
+
|
|
84
|
+
def f(btd: list[int], rk: list[int]) -> list[int]:
|
|
85
|
+
if meticulous_mode and len(btd) != 32 or len(rk) != 48:
|
|
86
|
+
raise ValueError('f:invalid input')
|
|
87
|
+
ep_btd = ep(btd)
|
|
88
|
+
rk_btd = b.xor(ep_btd, rk)
|
|
89
|
+
s_btd = s_box(rk_btd)
|
|
90
|
+
nbtd = p_box(s_btd)
|
|
91
|
+
return nbtd
|
|
92
|
+
|
|
93
|
+
def main():
|
|
94
|
+
pass
|
|
95
|
+
|
|
96
|
+
if __name__ == "__main__":
|
|
97
|
+
main()
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
|
|
2
|
+
meticulous_mode = True
|
|
3
|
+
|
|
4
|
+
def permutation(btd: list[int], table: list[int]) -> list[int]:
|
|
5
|
+
if meticulous_mode and len(btd) < max(table):
|
|
6
|
+
raise ValueError('permutation:index overflow')
|
|
7
|
+
n_btd = [btd[i-1] for i in table]
|
|
8
|
+
return n_btd
|
|
9
|
+
|
|
10
|
+
def main():
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
if __name__ == "__main__":
|
|
14
|
+
main()
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
from .des_core import BasicDES
|
|
2
|
+
from typing import Union, Iterable
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
def read_bytes(name: str, l = 0) -> bytes:
|
|
6
|
+
"""read bytes from target file"""
|
|
7
|
+
bfile = open(name, 'rb')
|
|
8
|
+
data = bfile.read()
|
|
9
|
+
bfile.close()
|
|
10
|
+
if l == 0:
|
|
11
|
+
return data
|
|
12
|
+
return data[:l]
|
|
13
|
+
|
|
14
|
+
def write_bytes(name: str, data: bytes):
|
|
15
|
+
"""write bytes into a new created file"""
|
|
16
|
+
bfile = open(name, 'wb')
|
|
17
|
+
bfile.write(data)
|
|
18
|
+
bfile.close()
|
|
19
|
+
|
|
20
|
+
class DES:
|
|
21
|
+
def __init__(self, key: Union[bytes, str], mode: str = 'ECB'):
|
|
22
|
+
"""setting initial mode and cipher machine"""
|
|
23
|
+
self.modes = {'ECB', 'CBC'}
|
|
24
|
+
if isinstance(key, str):
|
|
25
|
+
key = read_bytes(key, 8)
|
|
26
|
+
if len(key) != 8:
|
|
27
|
+
raise ValueError('DES: invalid key')
|
|
28
|
+
self.cipher = BasicDES(key)
|
|
29
|
+
self.__key = key
|
|
30
|
+
if mode not in self.modes:
|
|
31
|
+
raise ValueError('DES: unknown mode of operation')
|
|
32
|
+
self.__mode = mode
|
|
33
|
+
|
|
34
|
+
@staticmethod
|
|
35
|
+
def __add_padding(file: bytes) -> bytes:
|
|
36
|
+
"""add padding to a bytes string"""
|
|
37
|
+
if not isinstance(file, bytes):
|
|
38
|
+
raise TypeError
|
|
39
|
+
if len(file)%8 == 0:
|
|
40
|
+
return file + b'\x08'*8
|
|
41
|
+
return file + bytes([8-(len(file)%8)]*(8-(len(file)%8)))
|
|
42
|
+
|
|
43
|
+
@staticmethod
|
|
44
|
+
def __remove_padding(file: bytes) -> bytes:
|
|
45
|
+
"""remove padding from a bytes string"""
|
|
46
|
+
if not isinstance(file, bytes):
|
|
47
|
+
raise TypeError
|
|
48
|
+
pl = file[-1]
|
|
49
|
+
if pl > 8 or pl < 1 or file[-pl:] != bytes([pl])*pl:
|
|
50
|
+
return file
|
|
51
|
+
return file[:-pl]
|
|
52
|
+
|
|
53
|
+
@staticmethod
|
|
54
|
+
def __split_data(file: bytes) -> list[bytes]:
|
|
55
|
+
"""split a bytes string to a list contain bytes strings with length of 8"""
|
|
56
|
+
if (len(file)%8) != 0:
|
|
57
|
+
raise ValueError ('split_data: data must be splited perfectly')
|
|
58
|
+
return [file[i: i+8] for i in range(0, len(file), 8)]
|
|
59
|
+
|
|
60
|
+
@staticmethod
|
|
61
|
+
def __merge_data(blocks: Iterable[bytes]) -> bytes:
|
|
62
|
+
"""merge a list contain bytes strings into bytes string"""
|
|
63
|
+
return b''.join(blocks)
|
|
64
|
+
|
|
65
|
+
@staticmethod
|
|
66
|
+
def __random_iv() -> bytes:
|
|
67
|
+
"""generate a random initialization vector for cbc encryption"""
|
|
68
|
+
return os.urandom(8)
|
|
69
|
+
|
|
70
|
+
@staticmethod
|
|
71
|
+
def __xor(b1: bytes, b2: bytes):
|
|
72
|
+
"""perform xor operation between two bytes string of equal length"""
|
|
73
|
+
return bytes(i^j for i, j in zip(b1, b2))
|
|
74
|
+
|
|
75
|
+
def __ecb(self, blocks: list[bytes], decrypt: bool = False) -> bytes:
|
|
76
|
+
"""perform encrypt & decryption of ECB method"""
|
|
77
|
+
if not decrypt:
|
|
78
|
+
return self.__merge_data(self.cipher.encrypt(i) for i in blocks)
|
|
79
|
+
else:
|
|
80
|
+
return self.__remove_padding(self.__merge_data(self.cipher.decrypt(i) for i in blocks))
|
|
81
|
+
|
|
82
|
+
def __cbc(self, blocks: list[bytes], decrypt: bool = False) -> bytes:
|
|
83
|
+
"""perform encryption & decryption of CBC method"""
|
|
84
|
+
if not decrypt:
|
|
85
|
+
v = self.__random_iv()
|
|
86
|
+
cipher_data = [v]
|
|
87
|
+
for i in blocks:
|
|
88
|
+
v = self.cipher.encrypt(self.__xor(i, v))
|
|
89
|
+
cipher_data.append(v)
|
|
90
|
+
return self.__merge_data(cipher_data)
|
|
91
|
+
else:
|
|
92
|
+
if len(blocks) < 2:
|
|
93
|
+
raise ValueError("cbc: ciphertext too short")
|
|
94
|
+
va, blocks = blocks[:-1], blocks[1:]
|
|
95
|
+
return self.__remove_padding(self.__merge_data(self.__xor(self.cipher.decrypt(i), j) for i, j in zip(blocks, va)))
|
|
96
|
+
|
|
97
|
+
def get_modes(self) -> list[str]:
|
|
98
|
+
"""get avaliable cipher mode of operation"""
|
|
99
|
+
return self.modes
|
|
100
|
+
|
|
101
|
+
def get_mode(self) -> str:
|
|
102
|
+
"""get current cipher mode of operation"""
|
|
103
|
+
return self.__mode
|
|
104
|
+
|
|
105
|
+
def change_mode(self, mode: str):
|
|
106
|
+
"""change current cipher mode of operation"""
|
|
107
|
+
if mode not in self.modes:
|
|
108
|
+
raise ValueError('change_mode: unknown mode of operation')
|
|
109
|
+
self.__mode = mode
|
|
110
|
+
|
|
111
|
+
def get_key(self) -> bytes:
|
|
112
|
+
"""get current using key"""
|
|
113
|
+
return self.__key
|
|
114
|
+
|
|
115
|
+
def change_key(self, key: Union[bytes, str]):
|
|
116
|
+
"""change current using key"""
|
|
117
|
+
if isinstance(key, str):
|
|
118
|
+
key = read_bytes(key, 8)
|
|
119
|
+
if len(key) != 8:
|
|
120
|
+
raise ValueError('change_key: invalid key')
|
|
121
|
+
self.cipher = BasicDES(key)
|
|
122
|
+
self.__key = key
|
|
123
|
+
|
|
124
|
+
def encrypt(self, plaintext: bytes) -> bytes:
|
|
125
|
+
"""encrypt bytes data"""
|
|
126
|
+
blocks = self.__split_data(self.__add_padding(plaintext))
|
|
127
|
+
mode = self.__mode
|
|
128
|
+
if mode == 'ECB':
|
|
129
|
+
return self.__ecb(blocks)
|
|
130
|
+
elif mode == 'CBC':
|
|
131
|
+
return self.__cbc(blocks)
|
|
132
|
+
|
|
133
|
+
def decrypt(self, ciphertext: bytes) -> bytes:
|
|
134
|
+
"""decrypt bytes data"""
|
|
135
|
+
blocks = self.__split_data(ciphertext)
|
|
136
|
+
mode = self.__mode
|
|
137
|
+
if mode == 'ECB':
|
|
138
|
+
return self.__ecb(blocks, decrypt = True)
|
|
139
|
+
elif mode == 'CBC':
|
|
140
|
+
return self.__cbc(blocks, decrypt = True)
|
|
141
|
+
|
|
142
|
+
def encrypt_file(self, name: str, new_name: str):
|
|
143
|
+
"""encrypt target file and save it as 'new_name' """
|
|
144
|
+
plaintext = read_bytes(name)
|
|
145
|
+
ciphertext = self.encrypt(plaintext)
|
|
146
|
+
write_bytes(new_name, ciphertext)
|
|
147
|
+
|
|
148
|
+
def decrypt_file(self, name: str, new_name: str):
|
|
149
|
+
"""decrypt target file and save it as 'new_name' """
|
|
150
|
+
ciphertext = read_bytes(name)
|
|
151
|
+
plaintext = self.decrypt(ciphertext)
|
|
152
|
+
write_bytes(new_name, plaintext)
|
|
153
|
+
|
|
154
|
+
def main():
|
|
155
|
+
pass
|
|
156
|
+
|
|
157
|
+
if __name__ == "__main__":
|
|
158
|
+
main()
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: project-des
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Educational Python implementation of the Data Encryption Standard (DES)
|
|
5
|
+
Author-email: shrimp2845 <goldencheng15@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/shrimp2845-tw/project_DES
|
|
8
|
+
Project-URL: Repository, https://github.com/shrimp2845-tw/project_DES
|
|
9
|
+
Keywords: des,cryptography,education,feistel
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Requires-Python: >=3.10
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
16
|
+
**_Warning: This implementation is intended for self learning purposes only and should not be used for real-world security._**
|
|
17
|
+
|
|
18
|
+
## About
|
|
19
|
+
|
|
20
|
+
- **Author:** shrimp2845
|
|
21
|
+
- **Version:** 0.1.0
|
|
22
|
+
- **License:** MIT
|
|
23
|
+
|
|
24
|
+
This is an educational Python implementation of the Data Encryption Standard (DES). This project breaks down the complex Feistel network into human-readable modules following the official NIST specifications, and supports ECB and CBC modes for real file encryption.
|
|
25
|
+
|
|
26
|
+
## Installation & Usage
|
|
27
|
+
|
|
28
|
+
### install
|
|
29
|
+
Requires Python 3.10+
|
|
30
|
+
```bash
|
|
31
|
+
pip install project-des
|
|
32
|
+
```
|
|
33
|
+
### usage
|
|
34
|
+
```bash
|
|
35
|
+
python -c "from project_DES import DES; cipher = DES(b'key'); print(cipher.encrypt(b'data').hex())"
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Docs
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
class initialize
|
|
42
|
+
----------------------
|
|
43
|
+
project_DES.DES(key, mode='ECB')
|
|
44
|
+
|
|
45
|
+
key
|
|
46
|
+
-> 8 bytes or key file name
|
|
47
|
+
containing an encryption key,
|
|
48
|
+
if key is str, it extract
|
|
49
|
+
first 8 bytes in the file
|
|
50
|
+
|
|
51
|
+
mode
|
|
52
|
+
-> optional, block cipher mode of
|
|
53
|
+
operation that used,ECB and CBC
|
|
54
|
+
available in current version
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
methods
|
|
58
|
+
----------------------
|
|
59
|
+
|
|
60
|
+
encrypt(data: bytes) -> bytes,
|
|
61
|
+
decrypt(data: bytes) -> bytes
|
|
62
|
+
-> return encrypt/decrypt bytes data
|
|
63
|
+
|
|
64
|
+
encrypt_file(name: str, new_name: str),
|
|
65
|
+
decrypt_file(name: str, new_name: str)
|
|
66
|
+
-> encrypt/decrypt target file 'name'
|
|
67
|
+
and save it as 'new_name'
|
|
68
|
+
|
|
69
|
+
get_key(), get_mode()
|
|
70
|
+
-> get current using key/mode of
|
|
71
|
+
operation
|
|
72
|
+
|
|
73
|
+
change_key(key: str or bytes),
|
|
74
|
+
change_mode(mode: str)
|
|
75
|
+
-> change current using key/mode of
|
|
76
|
+
operation
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Examples
|
|
81
|
+
Project provides two examples: [string_cipher.py](https://github.com/shrimp2845-tw/project_DES/blob/main/examples/string_cipher.py) & [file_cipher.py](https://github.com/shrimp2845-tw/project_DES/blob/main/examples/file_cipher.py)
|
|
82
|
+
|
|
83
|
+
you can find samples of [file_cipher.py](https://github.com/shrimp2845-tw/project_DES/blob/main/examples/file_cipher.py) at:
|
|
84
|
+
[ecb_sample](https://github.com/shrimp2845-tw/project_DES/tree/main/examples/ecb_sample) & [cbc_sample](https://github.com/shrimp2845-tw/project_DES/tree/main/examples/cbc_sample)
|
|
85
|
+
|
|
86
|
+
### explanation for samples
|
|
87
|
+
**mumei.jpg**: plaintext file
|
|
88
|
+
|
|
89
|
+
**mumei.bin**: encrypt mumei.jpg with key ‘berries’ and mode ECB
|
|
90
|
+
|
|
91
|
+
**mumei_correct_decrypt**.jpg: decrypt mumei.bin with key ‘berries’ and mode ECB
|
|
92
|
+
|
|
93
|
+
**mumei_wrong_decrypt.jpg**: decrypt mumei.bin with key ‘friend’ and mode ECB
|
|
94
|
+
|
|
95
|
+
**ina.jpg**: plaintext file
|
|
96
|
+
|
|
97
|
+
**ina.bin**: encrypt ina.jpg with key ‘takodachi’ and mode CBC
|
|
98
|
+
|
|
99
|
+
**ina_correct_decrypt.jpg**: decrypt ina.bin with key ‘takodachi’ and mode CBC
|
|
100
|
+
|
|
101
|
+
**ina_wrong_decrypt.jpg**: decrypt ina.bin with key ‘violet’ and mode CBC
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
## Performance
|
|
105
|
+
This project is designed for learning and understanding how DES works rather than for practical encryption use. The implementation prioritizes readability and modular structure over performance.
|
|
106
|
+
|
|
107
|
+
As a result, encryption process is **very slow**. It takes nearly 2 minutes to encrypt 1MB file.
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
project_DES/__init__.py
|
|
4
|
+
project_DES/project_DES.py
|
|
5
|
+
project_DES/des_core/__init__.py
|
|
6
|
+
project_DES/des_core/bit_utils.py
|
|
7
|
+
project_DES/des_core/crypto.py
|
|
8
|
+
project_DES/des_core/feistel.py
|
|
9
|
+
project_DES/des_core/key.py
|
|
10
|
+
project_DES/des_core/mangler.py
|
|
11
|
+
project_DES/des_core/permute.py
|
|
12
|
+
project_des.egg-info/PKG-INFO
|
|
13
|
+
project_des.egg-info/SOURCES.txt
|
|
14
|
+
project_des.egg-info/dependency_links.txt
|
|
15
|
+
project_des.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
project_DES
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "project-des"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Educational Python implementation of the Data Encryption Standard (DES)"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = {text = "MIT"}
|
|
12
|
+
|
|
13
|
+
authors = [
|
|
14
|
+
{name = "shrimp2845", email = "goldencheng15@gmail.com"}
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
keywords = ["des", "cryptography", "education", "feistel"]
|
|
18
|
+
|
|
19
|
+
classifiers = [
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.10",
|
|
22
|
+
"License :: OSI Approved :: MIT License",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
[project.urls]
|
|
26
|
+
Homepage = "https://github.com/shrimp2845-tw/project_DES"
|
|
27
|
+
Repository = "https://github.com/shrimp2845-tw/project_DES"
|
|
28
|
+
|
|
29
|
+
[tool.setuptools.packages.find]
|
|
30
|
+
where = ["."]
|
|
31
|
+
include = ["project_DES*"]
|