cgacryptpy 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.
Files changed (35) hide show
  1. cgacryptpy-0.1.0/.gitignore +7 -0
  2. cgacryptpy-0.1.0/LICENSE +21 -0
  3. cgacryptpy-0.1.0/PKG-INFO +79 -0
  4. cgacryptpy-0.1.0/README.md +67 -0
  5. cgacryptpy-0.1.0/pyproject.toml +22 -0
  6. cgacryptpy-0.1.0/src/pycryptlib/__init__.py +0 -0
  7. cgacryptpy-0.1.0/src/pycryptlib/classical.py +137 -0
  8. cgacryptpy-0.1.0/src/pycryptlib/des.py +92 -0
  9. cgacryptpy-0.1.0/src/pycryptlib/encoding.py +80 -0
  10. cgacryptpy-0.1.0/src/pycryptlib/lfsr.py +89 -0
  11. cgacryptpy-0.1.0/src/pycryptlib/numtheory.py +67 -0
  12. cgacryptpy-0.1.0/src/pycryptlib/rsa.py +72 -0
  13. cgacryptpy-0.1.0/src/pycryptlib/stream_otp.py +50 -0
  14. cgacryptpy-0.1.0/src/pycryptlib/tables.py +89 -0
  15. cgacryptpy-0.1.0/src/tests/BACHER_exercise3.py +146 -0
  16. cgacryptpy-0.1.0/src/tests/Bacher - Crypto Lab 1.py +228 -0
  17. cgacryptpy-0.1.0/src/tests/Bacher - Crypto Lab 2.py +151 -0
  18. cgacryptpy-0.1.0/src/tests/Bacher_PA4.py +168 -0
  19. cgacryptpy-0.1.0/src/tests/Bacher_PA5.py +173 -0
  20. cgacryptpy-0.1.0/src/tests/Bacher_PA6.py +116 -0
  21. cgacryptpy-0.1.0/src/tests/Bacher_PA7.py +220 -0
  22. cgacryptpy-0.1.0/src/tests/desktop.ini +7 -0
  23. cgacryptpy-0.1.0/wheelcheck/pycryptlib/__init__.py +0 -0
  24. cgacryptpy-0.1.0/wheelcheck/pycryptlib/classical.py +137 -0
  25. cgacryptpy-0.1.0/wheelcheck/pycryptlib/des.py +92 -0
  26. cgacryptpy-0.1.0/wheelcheck/pycryptlib/encoding.py +80 -0
  27. cgacryptpy-0.1.0/wheelcheck/pycryptlib/lfsr.py +89 -0
  28. cgacryptpy-0.1.0/wheelcheck/pycryptlib/numtheory.py +67 -0
  29. cgacryptpy-0.1.0/wheelcheck/pycryptlib/rsa.py +72 -0
  30. cgacryptpy-0.1.0/wheelcheck/pycryptlib/stream_otp.py +50 -0
  31. cgacryptpy-0.1.0/wheelcheck/pycryptlib/tables.py +89 -0
  32. cgacryptpy-0.1.0/wheelcheck/pycryptlib-0.1.0.dist-info/METADATA +79 -0
  33. cgacryptpy-0.1.0/wheelcheck/pycryptlib-0.1.0.dist-info/RECORD +13 -0
  34. cgacryptpy-0.1.0/wheelcheck/pycryptlib-0.1.0.dist-info/WHEEL +4 -0
  35. cgacryptpy-0.1.0/wheelcheck/pycryptlib-0.1.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,7 @@
1
+ __pycache__/
2
+ *.pyc
3
+ dist/
4
+ build/
5
+ *.egg-info/
6
+ .venv/
7
+ .env
@@ -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.
@@ -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,67 @@
1
+ # pycryptlib
2
+
3
+ Multiple cryptologic functions ranging from a simple caesar cipher up to DES!
4
+
5
+ [![PyPI](https://img.shields.io/pypi/v/pycryptlib)](https://pypi.org/project/pycryptlib/)
6
+ [![Python](https://img.shields.io/pypi/pyversions/pycryptlib)](https://pypi.org/project/pycryptlib/)
7
+ [![License](https://img.shields.io/pypi/l/pycryptlib)](LICENSE)
8
+
9
+ > **Security notice:** This library has not been independently audited.
10
+ > It is intended for learning and experimentation. For production systems,
11
+ > use a well-established library such as `cryptography`.
12
+
13
+ ## Features
14
+
15
+ - Affine Cipher
16
+ - Viginere Cipher
17
+ - LFSR
18
+ - KidRSA
19
+ - RSA
20
+ - DES
21
+
22
+ ## Installation
23
+
24
+ ```bash
25
+ pip install pycryptlib
26
+ ```
27
+
28
+ Requires Python 3.9 or newer.
29
+
30
+ ## Quick start
31
+
32
+ ```python
33
+ from pycryptlib.lfsr import lfsr_encrypt
34
+
35
+ seed_3 = [1,0,0,0,1]
36
+
37
+ ciphertext, keystream = lfsr_encrypt("ACADEMY", seed_3)
38
+ print("7. Ciphertext bits:", ciphertext)
39
+ print("7. Keystream:", keystream)
40
+
41
+
42
+ ## Supported algorithms
43
+
44
+ | Algorithm | Module | Notes |
45
+ |-----------|--------|---------------|
46
+ | DES | `pycryptlib.des` | 64-bit keys |
47
+ | LSFR | `pycryptlib.lsfr` | |
48
+ | Classical | `pycryptlib.classical` | Caesar, stream, etc |
49
+ | KidRSA | `pycryptlib.rsa` |
50
+ | RSA | `pycryptlib.rsa` |
51
+
52
+ ## Feedback / Security Concerns
53
+ If you have any concerns regarding usage or issues (ie. function not working or not helpful explanation.),
54
+ feel free to email me or message me at sethrider02@gmail.com
55
+
56
+ ## Development
57
+
58
+ ```bash
59
+ git clone https://github.com/SethRider02/pycryptlib
60
+ cd pycryptlib
61
+ pip install -e ".[dev]"
62
+ pytest
63
+ ```
64
+
65
+ ## License
66
+
67
+ MIT. See [LICENSE](LICENSE).
@@ -0,0 +1,22 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [tool.hatch.build.targets.wheel]
6
+ packages = ["src/pycryptlib"]
7
+
8
+ [project]
9
+ name = "cgacryptpy"
10
+ version = "0.1.0"
11
+ description = "This library contains a multitude of cryptography functions that can be used in python."
12
+ readme = "README.md"
13
+ requires-python = ">=3.9"
14
+ license = { text = "MIT" }
15
+ authors = [{ name = "Seth Bacher", email = "sethrider02@gmail.com" }]
16
+ dependencies = []
17
+ classifiers = [
18
+ "Programming Language :: Python :: 3",
19
+ ]
20
+
21
+ [project.urls]
22
+ Homepage = "https://github.com/SethRider02/pycryptlib"
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
@@ -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)
@@ -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")
@@ -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
+