cgacryptpy 0.1.0__py3-none-any.whl

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,79 @@
1
+ Metadata-Version: 2.5
2
+ Name: cgacryptpy
3
+ Version: 0.1.0
4
+ Summary: This library contains a multitude of cryptography functions that can be used in python.
5
+ Project-URL: Homepage, https://github.com/SethRider02/pycryptlib
6
+ Author-email: Seth Bacher <sethrider02@gmail.com>
7
+ License: MIT
8
+ License-File: LICENSE
9
+ Classifier: Programming Language :: Python :: 3
10
+ Requires-Python: >=3.9
11
+ Description-Content-Type: text/markdown
12
+
13
+ # pycryptlib
14
+
15
+ Multiple cryptologic functions ranging from a simple caesar cipher up to DES!
16
+
17
+ [![PyPI](https://img.shields.io/pypi/v/pycryptlib)](https://pypi.org/project/pycryptlib/)
18
+ [![Python](https://img.shields.io/pypi/pyversions/pycryptlib)](https://pypi.org/project/pycryptlib/)
19
+ [![License](https://img.shields.io/pypi/l/pycryptlib)](LICENSE)
20
+
21
+ > **Security notice:** This library has not been independently audited.
22
+ > It is intended for learning and experimentation. For production systems,
23
+ > use a well-established library such as `cryptography`.
24
+
25
+ ## Features
26
+
27
+ - Affine Cipher
28
+ - Viginere Cipher
29
+ - LFSR
30
+ - KidRSA
31
+ - RSA
32
+ - DES
33
+
34
+ ## Installation
35
+
36
+ ```bash
37
+ pip install pycryptlib
38
+ ```
39
+
40
+ Requires Python 3.9 or newer.
41
+
42
+ ## Quick start
43
+
44
+ ```python
45
+ from pycryptlib.lfsr import lfsr_encrypt
46
+
47
+ seed_3 = [1,0,0,0,1]
48
+
49
+ ciphertext, keystream = lfsr_encrypt("ACADEMY", seed_3)
50
+ print("7. Ciphertext bits:", ciphertext)
51
+ print("7. Keystream:", keystream)
52
+
53
+
54
+ ## Supported algorithms
55
+
56
+ | Algorithm | Module | Notes |
57
+ |-----------|--------|---------------|
58
+ | DES | `pycryptlib.des` | 64-bit keys |
59
+ | LSFR | `pycryptlib.lsfr` | |
60
+ | Classical | `pycryptlib.classical` | Caesar, stream, etc |
61
+ | KidRSA | `pycryptlib.rsa` |
62
+ | RSA | `pycryptlib.rsa` |
63
+
64
+ ## Feedback / Security Concerns
65
+ If you have any concerns regarding usage or issues (ie. function not working or not helpful explanation.),
66
+ feel free to email me or message me at sethrider02@gmail.com
67
+
68
+ ## Development
69
+
70
+ ```bash
71
+ git clone https://github.com/SethRider02/pycryptlib
72
+ cd pycryptlib
73
+ pip install -e ".[dev]"
74
+ pytest
75
+ ```
76
+
77
+ ## License
78
+
79
+ MIT. See [LICENSE](LICENSE).
@@ -0,0 +1,13 @@
1
+ pycryptlib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ pycryptlib/classical.py,sha256=ygwLmWq0dt87mUJew8S-GrovOlQ5MdhIxUuHXinETPc,3747
3
+ pycryptlib/des.py,sha256=OTcAg8Kq39YTAoeDLGcpzEM-WWOrbNu6r9NNhUj4Flo,3114
4
+ pycryptlib/encoding.py,sha256=euDo_dAXUGOZbC1eBY74II7gTL7mgdW5p0QnD-l-Cts,2567
5
+ pycryptlib/lfsr.py,sha256=O-zprfWBR6o3xsuIrZOTagZqx4Ov5KPlSeqKhi1o2JI,2734
6
+ pycryptlib/numtheory.py,sha256=9OPIMAfBhJPLfrVhSEz13MAhsQ5Qcr6EAU3mXo6FHWo,1837
7
+ pycryptlib/rsa.py,sha256=uPAwEHKZVjWQt__SzHL5l5lVMprpV7QsgiucohWXB6w,1946
8
+ pycryptlib/stream_otp.py,sha256=l9EDTq30lkSrs-YYIz-bkjxSISLVyL6YGqMT0gNMP-Q,1606
9
+ pycryptlib/tables.py,sha256=bSA93zrL50-jSkfIGtA164MNZFgXWAqYXt_eH1n7Mhc,3265
10
+ cgacryptpy-0.1.0.dist-info/METADATA,sha256=IG1uKB1-fnMZsbr_BK1ngjO0r-ymDeQzOouCMr10AFE,2082
11
+ cgacryptpy-0.1.0.dist-info/WHEEL,sha256=2iZrJkDIkeDh0k9SikKMqFuRP-ua6mb4Ir1AsER4BEw,87
12
+ cgacryptpy-0.1.0.dist-info/licenses/LICENSE,sha256=lJ6Pt7Rq6prNDSdmuUO2RcyGuogj8gLN_T-WqOAySpg,1089
13
+ cgacryptpy-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.33.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 SethRider02
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
pycryptlib/__init__.py ADDED
File without changes
@@ -0,0 +1,137 @@
1
+ from .encoding import digitize, undigitize
2
+ from .numtheory import mod_inverse
3
+ import math
4
+
5
+ #==================================#
6
+ # SHIFT CIPHER FUNCTIONS #
7
+ #==================================#
8
+
9
+ def shift_encrypt(plaintext, key):
10
+ """Encrypts plaintext using a shift key from 0 to 25. Returns the ciphertext."""
11
+ cnumbs = []
12
+ for n in digitize(plaintext):
13
+ cnumbs.append((n+key)%26)
14
+ return undigitize(cnumbs)
15
+
16
+
17
+ def shift_decrypt(ciphertext, key):
18
+ """Decrypts ciphertext when the shift key is known. Returns the plaintext."""
19
+ pnumbs = []
20
+ for n in digitize(ciphertext):
21
+ pnumbs.append((n-key)%26)
22
+ return undigitize(pnumbs)
23
+
24
+
25
+ def brute_force_shift(ciphertext):
26
+ """Returns all 26 possible keys and corresponding plaintext outputs."""
27
+ bruteval = ""
28
+ for maykey in range(0,26):
29
+ pnumbs = shift_decrypt(ciphertext,maykey)
30
+ bruteval = bruteval+f"{pnumbs} Key = {maykey}\n"
31
+ return bruteval
32
+
33
+ #==================================#
34
+ # AFFINE CIPHER FUNCTIONS #
35
+ #==================================#
36
+
37
+ def affine_encrypt(plaintext, a, b):
38
+ """Encrypts plaintext with affine key (a, b). Returns ciphertext upon success."""
39
+ if math.gcd(a,26) != 1:
40
+ return None
41
+
42
+ dctext = []
43
+ for numb in digitize(plaintext):
44
+ dctext.append((a*numb + b)%26)
45
+
46
+ return undigitize(dctext)
47
+
48
+
49
+ def affine_decrypt(ciphertext, a, b):
50
+ """Decrypts ciphertext with a known affine key (a, b). Returns plaintext upon success."""
51
+ if math.gcd(a,b) != 1:
52
+ return None
53
+
54
+ dptext = []
55
+ a_inv = mod_inverse(a,26)
56
+
57
+ if a_inv == None:
58
+ return None
59
+
60
+ for numb in digitize(ciphertext):
61
+ dptext.append(a_inv * (numb-b) % 26)
62
+
63
+ return undigitize(dptext)
64
+
65
+
66
+ def brute_force_affine(ciphertext):
67
+ """
68
+ Returns every valid affine key and its candidate plaintext.
69
+ Valid a values are exactly those for which gcd(a, 26) == 1.
70
+ """
71
+ bruteval = ''
72
+ for a in range(26):
73
+ for b in range(26):
74
+ plaintext = affine_decrypt(ciphertext,a,b)
75
+ if plaintext != None:
76
+ bruteval = bruteval + f'{plaintext} Key = ({a},{b})\n'
77
+ return bruteval
78
+
79
+
80
+ #==================================#
81
+ # VIGENERE CIPHER FUNCTIONS #
82
+ #==================================#
83
+
84
+ def vigenere_encrypt(plaintext, key):
85
+ """Encrypts plaintext using a Vigenere key. Returns the ciphertext."""
86
+ dkey = digitize(key)
87
+ dctext = []
88
+ i = 0
89
+
90
+ for number in digitize(plaintext):
91
+ dctext.append((number+dkey[i%len(key)])%26)
92
+ i = i+1
93
+
94
+ return undigitize(dctext)
95
+
96
+
97
+ def vigenere_decrypt(ciphertext, key):
98
+ """Decrypt ciphertext when the Vigenere key is known. Return plaintext."""
99
+ dkey = digitize(key)
100
+ dptext = []
101
+ i = 0
102
+
103
+ for number in digitize(ciphertext):
104
+ dptext.append((number-dkey[i%len(key)])%26)
105
+ i = i+1
106
+
107
+ return undigitize(dptext)
108
+
109
+
110
+ def brute_force_vigenere(ciphertext, known_word):
111
+ """
112
+ Tries every three-letter key and print candidates containing known_word.
113
+ Return both the key and candidate plaintext when a match is found.
114
+ WARNING: Only works with 3 letter Vigenere keys!
115
+ """
116
+ pptext = ''
117
+ pkey = [0,0,0]
118
+ key = ''
119
+
120
+ while pkey[0] != 26:
121
+
122
+ if known_word in pptext:
123
+ break
124
+
125
+ key = undigitize(pkey)
126
+ pptext = vigenere_decrypt(ciphertext,key)
127
+ if pkey[2] == 25:
128
+ pkey[1]+=1
129
+ pkey[2] = 0
130
+ else:
131
+ pkey[2]+=1
132
+
133
+ if pkey[1] == 25:
134
+ pkey[0]+=1
135
+ pkey[1] = 0
136
+
137
+ return key, pptext
pycryptlib/des.py ADDED
@@ -0,0 +1,92 @@
1
+ # Contains functions required for DES (Data Encryption Standard)
2
+ import tables as T
3
+
4
+
5
+
6
+ def xor_bits(left, right):
7
+ """Provided helper: bitwise XOR of two equal-length binary strings."""
8
+ return ''.join('1' if a != b else '0' for a, b in zip(left, right))
9
+
10
+
11
+ def permute(bits, table):
12
+ """Return bits reordered using the given permutation table. Use [T.] before your table
13
+ to correctly grab from the tables file."""
14
+ permutation = ""
15
+ for index in table:
16
+ permutation = f"{permutation}{bits[index-1:index]}"
17
+ return permutation
18
+
19
+
20
+ def sbox_substitution(bits48):
21
+ """Takes a 48 bit string and passes them through all S Boxes. Returns 32 bit string."""
22
+ sbox_output = ""
23
+ i = 0
24
+ for table in T.SBOXES:
25
+ block = bits48[i*6:(i+1)*6]
26
+
27
+ sbox_row = f"{block[0:1]}{block[5:]}"
28
+ sbox_row = int(sbox_row,2)
29
+
30
+ sbox_col = f"{block[1:5]}"
31
+ sbox_col = int(sbox_col,2)
32
+
33
+ sbox_index = table[sbox_row][sbox_col]
34
+ bin_value = bin(sbox_index)[2:]
35
+
36
+ while len(bin_value) < 4:
37
+ bin_value = f"0{bin_value}"
38
+ sbox_output = f"{sbox_output}{bin_value}"
39
+
40
+ i = i+1
41
+
42
+ return sbox_output
43
+
44
+
45
+ def feistel(right32, round_key48):
46
+ """Takes the right 32 bits and specified round key through a feistal round. Returns the right
47
+ 32 bits after primitive permutation (P) function."""
48
+ E_right48 = permute(right32,T.E)
49
+ xor48 = xor_bits(E_right48,round_key48)
50
+ S_right32 = sbox_substitution(xor48)
51
+ P_right32 = permute(S_right32,T.P)
52
+ return P_right32
53
+
54
+
55
+ def rotate_left(bits, steps):
56
+ """Shifts bits left by X amount of steps. Shift loops (ie. 1st bit becomes 32nd bit).
57
+ Returns result of shifting."""
58
+ return bits[steps:] + bits[:steps]
59
+
60
+
61
+ def des_round_keys(key64):
62
+ """Generate round keys given the original 64-bit key. Returns list of keys."""
63
+ key56 = permute(key64, T.PC1)
64
+ c, d = key56[:28], key56[28:]
65
+ keys = []
66
+ for shift in T.SHIFTS:
67
+ c = rotate_left(c, shift)
68
+ d = rotate_left(d, shift)
69
+ keys.append(permute(c + d, T.PC2))
70
+ return keys
71
+
72
+
73
+ def des_encrypt_block(plain64, key64):
74
+ """Takes a 64-bit block and encrypts with a 64-bit key. Returns cipher of 64-bits."""
75
+ initial = permute(plain64, T.IP)
76
+ left, right = initial[:32], initial[32:]
77
+ for round_key in des_round_keys(key64):
78
+ left, right = right, xor_bits(left, feistel(right, round_key))
79
+ # DES swaps final halves before the inverse initial permutation.
80
+ return permute(right + left, T.IP_INV)
81
+
82
+
83
+ def des_decrypt_block(cipher64,key64):
84
+ """Decrypts a 64-bit cipher block with a given key. Returns 64-bit plaintext."""
85
+ cipher64 = permute(cipher64,T.IP)
86
+ left, right = cipher64[:32], cipher64[32:]
87
+ reverse_keys = des_round_keys(key64)
88
+ for i in range(0,16):
89
+ # print('Using Key: ',(-1-i)%16)
90
+ round_key = reverse_keys[-1-i]
91
+ left, right = right, xor_bits(left, feistel(right, round_key))
92
+ return permute(right+left,T.IP_INV)
pycryptlib/encoding.py ADDED
@@ -0,0 +1,80 @@
1
+ # Contains various encoding schemes used in cryptography
2
+
3
+
4
+ def digitize(my_string):
5
+ """Convert uppercase A-Z text into integers: A=0, ..., Z=25."""
6
+ num_list = []
7
+ for char in my_string:
8
+ if not "A" <= char <= "Z":
9
+ raise ValueError("Only uppercase letters A-Z are allowed.")
10
+ num_list.append(ord(char) - ord("A"))
11
+ return num_list
12
+
13
+
14
+ def undigitize(num_list):
15
+ """Convert a list of integers from 0 to 25 into uppercase text."""
16
+ my_string = ""
17
+ for value in num_list:
18
+ if not 0 <= value <= 25:
19
+ raise ValueError("Each recovered letter value must be 0 through 25.")
20
+ my_string += chr(int(value) + ord("A"))
21
+ return my_string
22
+
23
+
24
+ def text_to_bits(text):
25
+ """Encodes a string as UTF-8 and returns its bits as a list of 0s and 1s."""
26
+ return [
27
+ (byte >> shift) & 1
28
+ for byte in text.encode("utf-8")
29
+ for shift in range(7, -1, -1)
30
+ ]
31
+
32
+
33
+ def bits_to_text(bits):
34
+ """Converts a bit list whose length is a multiple of 8 back into text."""
35
+ if len(bits) % 8 != 0:
36
+ raise ValueError("The number of bits must be a multiple of 8.")
37
+
38
+ byte_values = []
39
+ for start in range(0, len(bits), 8):
40
+ value = 0
41
+ for bit in bits[start:start + 8]:
42
+ value = (value << 1) | bit
43
+ byte_values.append(value)
44
+
45
+ return bytes(byte_values).decode("utf-8")
46
+
47
+
48
+ def ascii_to_bits(ascii_string):
49
+ """Converts given ascii text into a string of bits. Returns full string of bits with no delimeter."""
50
+ ascii_bytes = ' '.join(format(ord(c),'08b') for c in ascii_string)
51
+ ascii_bytes = ascii_bytes.replace(' ','')
52
+ return ascii_bytes
53
+
54
+
55
+ def bits_to_ascii(bits):
56
+ """Converts a string of bits into an ASCII string. Returns ASCII string if successful, or None if unsuccessful."""
57
+ if len(bits) % 8 != 0:
58
+ return None
59
+
60
+ ascii_string = ''
61
+ byte_size = int(len(bits)/8)
62
+ ascii_list = []
63
+ for i in range(0,byte_size):
64
+ ascii_bin = bits[i*8:(i+1)*8]
65
+ ascii_list.append(int(ascii_bin,2))
66
+
67
+ for code in ascii_list:
68
+ ascii_string = f'{ascii_string}{format(code,'c')}'
69
+
70
+ return ascii_string
71
+
72
+
73
+ def hex_to_bits(hex_string, width=64):
74
+ """Given hexadecimal text, return exactly width binary characters."""
75
+ return format(int(hex_string, 16), f"0{width}b")
76
+
77
+
78
+ def bits_to_hex(bits):
79
+ """Given binary text, return uppercase hexadecimal text (with leading zeros)."""
80
+ return format(int(bits, 2), f"0{len(bits) // 4}X")
pycryptlib/lfsr.py ADDED
@@ -0,0 +1,89 @@
1
+ # Contains various functions dealing with Linear Feedback Shift Registers (LFSR's)
2
+
3
+ def text_to_bits(text):
4
+ """Convert uppercase A-Z text into one flat list of 5-bit values."""
5
+ bits = []
6
+ for char in text:
7
+ value = ord(char) - ord("A")
8
+ if value < 0 or value > 25:
9
+ raise ValueError("Only uppercase letters A-Z are allowed.")
10
+ bits.extend([
11
+ (value >> 4) & 1,
12
+ (value >> 3) & 1,
13
+ (value >> 2) & 1,
14
+ (value >> 1) & 1,
15
+ value & 1
16
+ ])
17
+ return bits
18
+
19
+
20
+ def bits_to_text(bits):
21
+ """Convert a flat list of 5-bit letter values back into uppercase text."""
22
+ if len(bits) % 5 != 0:
23
+ raise ValueError("The number of bits must be a multiple of 5.")
24
+
25
+ text = ""
26
+ for i in range(0, len(bits), 5):
27
+ group = bits[i:i + 5]
28
+ value = (
29
+ group[0] * 16 + group[1] * 8 + group[2] * 4
30
+ + group[3] * 2 + group[4]
31
+ )
32
+ if value > 25:
33
+ raise ValueError("5-bit value does not represent A-Z.")
34
+ text += chr(value + ord("A"))
35
+ return text
36
+
37
+
38
+ def clock_lfsr(seed, clocks):
39
+ """Shifts a 5-bit seed 'clocks' amount of clock cycles. Returns shifted bits as a list."""
40
+ cipher = []
41
+ for i in range(len(seed)):
42
+ cipher.append(seed[i])
43
+
44
+ for i in range(clocks):
45
+ tempbit = (cipher[2] + cipher[0]) % 2
46
+ cipher.append(tempbit)
47
+ cipher.pop(0)
48
+
49
+
50
+ return cipher
51
+
52
+
53
+ def lfsr(seed, length):
54
+ """Generates a key of a specified length given a seed. Returns the keystream as a list of bits."""
55
+ keystream = []
56
+ for i in range(len(seed)):
57
+ keystream.append(seed[i])
58
+
59
+ for i in range(5,length):
60
+ tempbit = (keystream[i-5] + keystream[i-3]) % 2
61
+ keystream.append(tempbit)
62
+
63
+ return keystream
64
+
65
+
66
+ def lfsr_encrypt(plaintext, seed):
67
+ """Encrypts a string plaintext with a seed list of five bits. Returns tuple with ciphertext as a list
68
+ of bits and keystream as a list of bits."""
69
+ plainbits = text_to_bits(plaintext)
70
+ cipherbits = []
71
+ keystream = lfsr(seed,(len(plainbits)))
72
+
73
+ for i in range(0,len(plainbits)):
74
+ tempbit = (plainbits[i] + keystream[i]) % 2
75
+ cipherbits.append(tempbit)
76
+
77
+ return (cipherbits,keystream)
78
+
79
+
80
+ def lfsr_decrypt(ciphertext_bits, keystream):
81
+ """Decrypts a list of ciphertext bits using a keystream list of bits. Returns a string of text."""
82
+ plainbits = []
83
+ for i in range(0,len(ciphertext_bits)):
84
+ tempbit = (ciphertext_bits[i] + keystream[i]) % 2
85
+ plainbits.append(tempbit)
86
+
87
+ plaintext = bits_to_text(plainbits)
88
+
89
+ return plaintext
@@ -0,0 +1,67 @@
1
+ # Contains various functions pertaining to number theory.
2
+ import math
3
+
4
+ def mod_inverse(a, n):
5
+ """Finds mod inverse of a mod(n). Returns inverse if found or none if not found."""
6
+ gcd = math.gcd(a,n)
7
+ if gcd == 1:
8
+ for x in range(1,n):
9
+ inverse = (a*x)%n
10
+ if inverse == 1:
11
+ break
12
+ else:
13
+ continue
14
+ else:
15
+ return None
16
+ return x
17
+
18
+
19
+ def bin_exp(exponent):
20
+ """Calculates the binary expansion of a given exponent. Returns a list of the power of 2's for the exponent."""
21
+ twopower = []
22
+ while(exponent != 0):
23
+ i = 0
24
+ while(exponent >= (2**i)):
25
+ i=i+1
26
+ i=i-1
27
+ twopower.append(2**i)
28
+ exponent = exponent - (2**i)
29
+
30
+ return twopower
31
+
32
+
33
+ def fast_mod_exp(base, exponent, modulus):
34
+ """Calculates a value given a base, positive exponent, and a modulus. Returns the value in the respective modulus."""
35
+ if modulus <= 0:
36
+ raise ValueError("modulus must be a positive integer.")
37
+ if exponent < 0:
38
+ raise ValueError("exponent must be nonnegative.")
39
+
40
+ powers = bin_exp(exponent)
41
+ result = 1
42
+ current_power = 1
43
+ current_value = base % modulus
44
+
45
+ while current_power <= exponent:
46
+ if current_power in powers:
47
+ result = (result * current_value) % modulus
48
+ current_value = (current_value * current_value) % modulus
49
+ current_power = current_power * 2
50
+
51
+ return result % modulus
52
+
53
+
54
+ def euler_totient(n):
55
+ """Returns factors from 1 to n that are relatively prime to n."""
56
+ factors = []
57
+ for a in range(1,n):
58
+ gcd = math.gcd(a,n)
59
+ if gcd == 1:
60
+ factors.append(a)
61
+ else:
62
+ continue
63
+
64
+ phi = len(factors)
65
+ return phi
66
+
67
+
pycryptlib/rsa.py ADDED
@@ -0,0 +1,72 @@
1
+ # Contains functions for both KidRSA and RSA
2
+ from .encoding import digitize, undigitize
3
+ from .numtheory import mod_inverse
4
+
5
+ def kidrsa_keygen(a, b, a0, b0):
6
+ """Takes given KidRSA components to compute M, e, d, and n. Returns tuple of values in the same order."""
7
+ if a * b <= 1:
8
+ raise ValueError("a*b must be greater than 1.")
9
+
10
+ M = a*b - 1
11
+ e = a0*M + a
12
+ d = b0*M + b
13
+ n = (e*d-1)/M
14
+
15
+ return (M, e, d, n)
16
+
17
+
18
+ def kidrsa_encrypt(message, e, n):
19
+ """Encrypts a string message given e and n. Returns ciphertext as a list of encoded values."""
20
+ dmess = digitize(message)
21
+ cmess = []
22
+ for num in dmess:
23
+ newnum = (e*num)%n
24
+ cmess.append(newnum)
25
+
26
+ return cmess
27
+
28
+
29
+ def kidrsa_decrypt(ciphertext, d, n):
30
+ """Decrypts a list of encoded integers using d and n. Returns plaintext as a string."""
31
+ pmess = []
32
+ for num in ciphertext:
33
+ newnum = (num*d)%n
34
+ pmess.append(newnum)
35
+
36
+ return undigitize(pmess)
37
+
38
+
39
+ def rsa_keygen(p, q, e):
40
+ """Generates RSA key values given p, q, and e. Returns a tuple with n, e, and d."""
41
+ n = p*q
42
+ phi_n = (p-1)*(q-1)
43
+ d = mod_inverse(e,phi_n)
44
+ return (n,e,d)
45
+
46
+
47
+ def rsa_encrypt(plaintext,p,q,e):
48
+ """Encrypts plaintext list of integers using RSA. Returns ciphertext as list of integers."""
49
+ keys = rsa_keygen(p,q,e)
50
+ n = keys[0]
51
+ e = keys[1]
52
+ ciphertext = []
53
+
54
+ for i in range(0,len(plaintext)):
55
+ m = plaintext[i]
56
+ c = pow(m,e,n)
57
+ ciphertext.append(c)
58
+
59
+ return ciphertext
60
+
61
+
62
+ def rsa_decrypt(ciphertext,p,q,e):
63
+ """Decrypts ciphertext list of integers using RSA. Returns plaintext as list of integers."""
64
+ keys = rsa_keygen(p,q,e)
65
+ n = keys[0]
66
+ d = keys[2]
67
+ recovered_plaintext = []
68
+
69
+ for i in range(0,len(ciphertext)):
70
+ c = ciphertext[i]
71
+ m = pow(c,d,n)
72
+ recovered_plaintext.append(m)
@@ -0,0 +1,50 @@
1
+ from .encoding import text_to_bits, bits_to_text
2
+ import secrets
3
+
4
+
5
+ def xor_bits(data_bits, keystream_bits):
6
+ """Take two streams of bits and xors them. Returns the resulting stream of bits."""
7
+ if len(data_bits) != len(keystream_bits):
8
+ raise ValueError("Data and keystream must have the same length.")
9
+ if not all(bit in (0, 1) for bit in data_bits + keystream_bits):
10
+ raise ValueError("Data and keystream must contain only 0s and 1s.")
11
+
12
+ i = 0
13
+ cipherbits = []
14
+ while i < len(data_bits):
15
+ if data_bits[i] != keystream_bits[i]:
16
+ cipherbits.append(1)
17
+ else:
18
+ cipherbits.append(0)
19
+ i = i+1
20
+
21
+ return cipherbits
22
+
23
+
24
+ def generate_one_time_pad(length):
25
+ """Generates a one time pad of a specified length using Python's secret module."""
26
+ if length < 0:
27
+ raise ValueError("Length cannot be negative.")
28
+
29
+ i = 0
30
+ rand_pad = []
31
+ while i < length:
32
+ rand_pad.append(secrets.randbits(1))
33
+ i = i+1
34
+
35
+ return rand_pad
36
+
37
+
38
+ def otp_encrypt(plaintext):
39
+ """Encrypts the plaintext using an OTP"""
40
+ bit_text = text_to_bits(plaintext)
41
+ ot_pad = generate_one_time_pad(len(bit_text))
42
+ cbit_text = xor_bits(bit_text, ot_pad)
43
+ return (cbit_text,ot_pad)
44
+
45
+
46
+ def otp_decrypt(ciphertext_bits, otp):
47
+ """XORs the ciphertext bit list with the one-time pad (keystream) used to encrypt the ciphertext, and converts the resulting bit list back to an ASCII plaintext string.
48
+ """
49
+ pbit_text = xor_bits(ciphertext_bits,otp)
50
+ return bits_to_text(pbit_text)
pycryptlib/tables.py ADDED
@@ -0,0 +1,89 @@
1
+ # This file contains all the tables necessary for DES!
2
+
3
+ IP = [
4
+ 58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4,
5
+ 62, 54, 46, 38, 30, 22, 14, 6, 64, 56, 48, 40, 32, 24, 16, 8,
6
+ 57, 49, 41, 33, 25, 17, 9, 1, 59, 51, 43, 35, 27, 19, 11, 3,
7
+ 61, 53, 45, 37, 29, 21, 13, 5, 63, 55, 47, 39, 31, 23, 15, 7
8
+ ]
9
+ IP_INV = [
10
+ 40, 8, 48, 16, 56, 24, 64, 32, 39, 7, 47, 15, 55, 23, 63, 31,
11
+ 38, 6, 46, 14, 54, 22, 62, 30, 37, 5, 45, 13, 53, 21, 61, 29,
12
+ 36, 4, 44, 12, 52, 20, 60, 28, 35, 3, 43, 11, 51, 19, 59, 27,
13
+ 34, 2, 42, 10, 50, 18, 58, 26, 33, 1, 41, 9, 49, 17, 57, 25
14
+ ]
15
+ E = [
16
+ 32, 1, 2, 3, 4, 5, 4, 5, 6, 7, 8, 9,
17
+ 8, 9, 10, 11, 12, 13, 12, 13, 14, 15, 16, 17,
18
+ 16, 17, 18, 19, 20, 21, 20, 21, 22, 23, 24, 25,
19
+ 24, 25, 26, 27, 28, 29, 28, 29, 30, 31, 32, 1
20
+ ]
21
+ P = [
22
+ 16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31, 10,
23
+ 2, 8, 24, 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4, 25
24
+ ]
25
+ PC1 = [
26
+ 57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18,
27
+ 10, 2, 59, 51, 43, 35, 27, 19, 11, 3, 60, 52, 44, 36,
28
+ 63, 55, 47, 39, 31, 23, 15, 7, 62, 54, 46, 38, 30, 22,
29
+ 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 28, 20, 12, 4
30
+ ]
31
+ PC2 = [
32
+ 14, 17, 11, 24, 1, 5, 3, 28, 15, 6, 21, 10,
33
+ 23, 19, 12, 4, 26, 8, 16, 7, 27, 20, 13, 2,
34
+ 41, 52, 31, 37, 47, 55, 30, 40, 51, 45, 33, 48,
35
+ 44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32
36
+ ]
37
+ SHIFTS = [1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1]
38
+
39
+ # Each S-box contains four rows of 16 integers (0-15)
40
+ SBOXES = [
41
+ [
42
+ [14,4,13,1,2,15,11,8,3,10,6,12,5,9,0,7],
43
+ [0,15,7,4,14,2,13,1,10,6,12,11,9,5,3,8],
44
+ [4,1,14,8,13,6,2,11,15,12,9,7,3,10,5,0],
45
+ [15,12,8,2,4,9,1,7,5,11,3,14,10,0,6,13]
46
+ ],
47
+ [
48
+ [15,1,8,14,6,11,3,4,9,7,2,13,12,0,5,10],
49
+ [3,13,4,7,15,2,8,14,12,0,1,10,6,9,11,5],
50
+ [0,14,7,11,10,4,13,1,5,8,12,6,9,3,2,15],
51
+ [13,8,10,1,3,15,4,2,11,6,7,12,0,5,14,9]
52
+ ],
53
+ [
54
+ [10,0,9,14,6,3,15,5,1,13,12,7,11,4,2,8],
55
+ [13,7,0,9,3,4,6,10,2,8,5,14,12,11,15,1],
56
+ [13,6,4,9,8,15,3,0,11,1,2,12,5,10,14,7],
57
+ [1,10,13,0,6,9,8,7,4,15,14,3,11,5,2,12]
58
+ ],
59
+ [
60
+ [7,13,14,3,0,6,9,10,1,2,8,5,11,12,4,15],
61
+ [13,8,11,5,6,15,0,3,4,7,2,12,1,10,14,9],
62
+ [10,6,9,0,12,11,7,13,15,1,3,14,5,2,8,4],
63
+ [3,15,0,6,10,1,13,8,9,4,5,11,12,7,2,14]
64
+ ],
65
+ [
66
+ [2,12,4,1,7,10,11,6,8,5,3,15,13,0,14,9],
67
+ [14,11,2,12,4,7,13,1,5,0,15,10,3,9,8,6],
68
+ [4,2,1,11,10,13,7,8,15,9,12,5,6,3,0,14],
69
+ [11,8,12,7,1,14,2,13,6,15,0,9,10,4,5,3]
70
+ ],
71
+ [
72
+ [12,1,10,15,9,2,6,8,0,13,3,4,14,7,5,11],
73
+ [10,15,4,2,7,12,9,5,6,1,13,14,0,11,3,8],
74
+ [9,14,15,5,2,8,12,3,7,0,4,10,1,13,11,6],
75
+ [4,3,2,12,9,5,15,10,11,14,1,7,6,0,8,13]
76
+ ],
77
+ [
78
+ [4,11,2,14,15,0,8,13,3,12,9,7,5,10,6,1],
79
+ [13,0,11,7,4,9,1,10,14,3,5,12,2,15,8,6],
80
+ [1,4,11,13,12,3,7,14,10,15,6,8,0,5,9,2],
81
+ [6,11,13,8,1,4,10,7,9,5,0,15,14,2,3,12]
82
+ ],
83
+ [
84
+ [13,2,8,4,6,15,11,1,10,9,3,14,5,0,12,7],
85
+ [1,15,13,8,10,3,7,4,12,5,6,11,0,14,9,2],
86
+ [7,11,4,1,9,12,14,2,0,6,10,13,15,3,5,8],
87
+ [2,1,14,7,4,10,8,13,15,12,9,0,3,5,6,11]
88
+ ]
89
+ ]