Compare commits

...
3 Commits
7 changed files with 177 additions and 26 deletions

No files matched your search

+1
View File
@@ -0,0 +1 @@
__pycache__/
+65
View File
@@ -0,0 +1,65 @@
"""
# Mode of Operation
so uhhh those diagrams are Shit Ass, so with the help of claude i turned it into python.
"""
from itertools import batched
def AES(key, x):
"stub"
return x
def chunk(message: bytes, size: int):
return batched(message, size) # yields tuples of `size` bytes
def xor_bytes(a, b):
return bytes(x ^ y for x, y in zip(a, b))
# --- ECB ---
def ecb(key, message):
"things go in things go out"
for block in chunk(message, 16):
yield AES(key, block) # message goes straight in
# --- CBC ---
def cbc(key, message, iv):
"y_i = AES(x_i ^ y_{i-1})"
prev = iv
for block in chunk(message, 16):
x_i = xor_bytes(block, prev) # XOR with previous ciphertext
prev = AES(key, x_i)
yield prev
# --- OFB ---
def ofb(key, message, iv):
"keystream s evolves on itself, we yield y_i = x_i ^ s_i"
keystream = iv
for block in chunk(message, 16):
keystream = AES(key, keystream) # encrypt previous *keystream*
yield xor_bytes(block, keystream)
# keystream feeds back, independent of ciphertext
# --- CFB ---
def cfb(key, message, iv):
"difference to OFB is that the y_i is coming back instead of s_i"
prev = iv
for block in chunk(message, 16):
keystream = AES(key, prev) # encrypt previous ciphertext
prev = xor_bytes(block, keystream)
yield prev # ciphertext feeds back, not keystream
# --- CTR ---
def ctr(key, message, nonce):
"ts is so easy to understand in python"
for i, block in enumerate(chunk(message, 16)):
x_i = nonce + i.to_bytes(8) # counter, message never touches AES
yield xor_bytes(block, AES(key, x_i)) # WHATTTTT NO FUCKING WAY
+50
View File
@@ -0,0 +1,50 @@
import math
class Elgamal:
def __init__(self, p: int, alpha: int, d: int) -> None:
"contract: p is prime, and"
if not 0 <= alpha < p or math.gcd(alpha, p) != 1:
# set Z*_n is all i [0, n) st gcd(i, n) = 1, if n is prime thats jus the whole thing
raise ValueError()
if not 2 <= d <= p - 2:
raise ValueError()
self.p, self.alpha, self.d = p, alpha, d
self.beta = pow(alpha, d, p)
@property
def pubkey(self):
return (self.p, self.alpha, self.beta)
@property
def privkey(self):
return self.d
def sig_gen(self, x: int, ke: int) -> tuple[int, int]:
if not 2 <= ke <= self.p - 2 or math.gcd(ke, self.p - 1) != 1:
raise ValueError()
r = pow(self.alpha, ke, self.p)
kei = pow(ke, -1, self.p - 1) # mod p - 1
s = ((x - self.d * r) * kei) % (self.p - 1)
return r, s
@staticmethod
def sig_verify(x: int, rs: tuple[int, int], pab: tuple[int, int, int]) -> bool:
r, s = rs
p, alpha, beta = pab
t = (pow(beta, r, p) * pow(r, s, p)) % p
return (t - pow(alpha, x, p)) % p == 0
def main():
bob = Elgamal(29, 2, 12)
assert bob.beta % 29 == 7
r, s = bob.sig_gen(26, 5)
assert r % 29 == 3, f"{r = }"
assert s % 28 == 26, f"{s = }"
assert Elgamal.sig_verify(26, (r, s), bob.pubkey)
if __name__ == "__main__":
main()
+6 -3
View File
@@ -69,9 +69,12 @@ def theREALsmx(x, e, n):
def realest(x=None, y=None, n=None): def realest(x=None, y=None, n=None):
x = x or random.randint(-10e9, 10e9) # no way
y = y or random.randint(-10e9, 10e9) m10e9 = int(-10*10**9)
n = n or random.randint(2, 10e9) p10e9 = int(10*10**9)
x = x or random.randint(m10e9, p10e9)
y = y or random.randint(m10e9, p10e9)
n = n or random.randint(2, p10e9)
# print(x, y, n) # print(x, y, n)
if y < 0: if y < 0:
try: try:
+4 -4
View File
@@ -1,4 +1,4 @@
from proto_rsa import LdaRsahh from proto_rsa import LdaRSA
def e1(): def e1():
@@ -18,16 +18,16 @@ def e2():
def e3(): def e3():
a = LdaRsahh(p=251, q=191, e=65537).enc(26729) a = LdaRSA(p=251, q=191, e=65537).enc(26729)
b = LdaRsahh(p=233, q=151, d=9473).dec(29600) b = LdaRSA(p=233, q=151, d=9473).dec(29600)
return a, b return a, b
def e4(): def e4():
C = 412589464 C = 412589464
M = LdaRsahh(p=81401, q=27109, e=65537).dec(C) M = LdaRSA(p=81401, q=27109, e=65537).dec(C)
return dec_ascii(M) return dec_ascii(M)
+28
View File
@@ -0,0 +1,28 @@
class Person:
def __init__(self, p: int, g: int, k: int):
self.p, self.g, self.k = p, g, k
self.s = None
def public_key(self, recieved: int | None = None) -> int:
return pow(recieved or self.g, self.k, self.p)
def recieve_public_key(self, K: int) -> int:
self.s = self.public_key(K)
return self.s
def main():
p = 23
g = 5
alice = Person(p, g, 4)
bob = Person(p, g, 3)
assert (
alice.recieve_public_key(bob.public_key())
== bob.recieve_public_key(alice.public_key())
== 18
)
if __name__ == "__main__":
main()
print("done")
+23 -19
View File
@@ -7,25 +7,35 @@ def inv(a: int, n: int):
return pow(a, -1, n) return pow(a, -1, n)
class LdaRsahh: class LdaRSA:
def __init__( def __init__(
self, self,
p: int, p: int,
q: int, q: int,
d: int | None = None, *,
e: int | None = None, e: int | None = None,
d: int | None = None,
ln_ops: Callable[[int, int], int] | None = None, ln_ops: Callable[[int, int], int] | None = None,
): ):
"""
p and q is Prime.
ln = lcm(p-1,q-1)
e needs to be in range (1, ln) and be coprime with ln, and is the public key.
d s.t. d*e === 1 mod ln is the private key
"""
# operator.mul or math.lcm # operator.mul or math.lcm
self._lnops: Callable[[int, int], int] = ln_ops or math.lcm self._lnops: Callable[[int, int], int] = ln_ops or math.lcm
self._p, self._q = p, q self.__p, self.__q = p, q
if d is None and e is None: if d is None and e is None:
raise ValueError("at least d or e needs to be fed in") raise ValueError("at least e or d needs to be fed in")
ln = self.ln ln = self.ln
if e is None: if e is None:
e = inv(d, ln) # type: ignore e = inv(d, ln) # type: ignore
elif not 1 < e < ln: # elif not 1 < e < ln:
e %= ln # bad # e %= ln # bad
if not 1 < e < ln: if not 1 < e < ln:
raise ValueError(f"{e = }, {ln = }, 1 < e < ln not satisfied") raise ValueError(f"{e = }, {ln = }, 1 < e < ln not satisfied")
if math.gcd(e, ln) != 1: if math.gcd(e, ln) != 1:
@@ -34,34 +44,28 @@ class LdaRsahh:
) )
if d is None: if d is None:
d = inv(e, ln) d = inv(e, ln)
self._d, self._e = d, e self.__d, self.__e = d, e
@cached_property @cached_property
def n(self): def n(self):
return self._p * self._q return self.__p * self.__q
@cached_property @cached_property
def ln(self): def ln(self):
return self._lnops((self._p - 1), (self._q - 1)) return self._lnops((self.__p - 1), (self.__q - 1))
# @property
# def p(self):
# return self._p
# @property
# def q(self):
# return self._q
@property @property
def d(self): def d(self):
return self._d return self.__d
@property @property
def e(self): def e(self):
return self._e return self.__e
def enc(self, m: int): def enc(self, m: int):
"encode message m"
return pow(m, self.e, self.n) return pow(m, self.e, self.n)
def dec(self, c: int): def dec(self, c: int):
"decode encoded msg c"
return pow(c, self.d, self.n) return pow(c, self.d, self.n)