Compare commits
3
Commits
No files matched your search
@@ -0,0 +1 @@
|
||||
__pycache__/
|
||||
@@ -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
@@ -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()
|
||||
@@ -69,9 +69,12 @@ def theREALsmx(x, e, n):
|
||||
|
||||
|
||||
def realest(x=None, y=None, n=None):
|
||||
x = x or random.randint(-10e9, 10e9)
|
||||
y = y or random.randint(-10e9, 10e9)
|
||||
n = n or random.randint(2, 10e9)
|
||||
# no way
|
||||
m10e9 = int(-10*10**9)
|
||||
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)
|
||||
if y < 0:
|
||||
try:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from proto_rsa import LdaRsahh
|
||||
from proto_rsa import LdaRSA
|
||||
|
||||
|
||||
def e1():
|
||||
@@ -18,16 +18,16 @@ def e2():
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
def e4():
|
||||
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)
|
||||
|
||||
|
||||
|
||||
@@ -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
@@ -7,25 +7,35 @@ def inv(a: int, n: int):
|
||||
return pow(a, -1, n)
|
||||
|
||||
|
||||
class LdaRsahh:
|
||||
class LdaRSA:
|
||||
def __init__(
|
||||
self,
|
||||
p: int,
|
||||
q: int,
|
||||
d: int | None = None,
|
||||
*,
|
||||
e: int | None = None,
|
||||
d: 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
|
||||
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:
|
||||
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
|
||||
if e is None:
|
||||
e = inv(d, ln) # type: ignore
|
||||
elif not 1 < e < ln:
|
||||
e %= ln # bad
|
||||
# elif not 1 < e < ln:
|
||||
# e %= ln # bad
|
||||
if not 1 < e < ln:
|
||||
raise ValueError(f"{e = }, {ln = }, 1 < e < ln not satisfied")
|
||||
if math.gcd(e, ln) != 1:
|
||||
@@ -34,34 +44,28 @@ class LdaRsahh:
|
||||
)
|
||||
if d is None:
|
||||
d = inv(e, ln)
|
||||
self._d, self._e = d, e
|
||||
self.__d, self.__e = d, e
|
||||
|
||||
@cached_property
|
||||
def n(self):
|
||||
return self._p * self._q
|
||||
return self.__p * self.__q
|
||||
|
||||
@cached_property
|
||||
def ln(self):
|
||||
return self._lnops((self._p - 1), (self._q - 1))
|
||||
|
||||
# @property
|
||||
# def p(self):
|
||||
# return self._p
|
||||
|
||||
# @property
|
||||
# def q(self):
|
||||
# return self._q
|
||||
return self._lnops((self.__p - 1), (self.__q - 1))
|
||||
|
||||
@property
|
||||
def d(self):
|
||||
return self._d
|
||||
return self.__d
|
||||
|
||||
@property
|
||||
def e(self):
|
||||
return self._e
|
||||
return self.__e
|
||||
|
||||
def enc(self, m: int):
|
||||
"encode message m"
|
||||
return pow(m, self.e, self.n)
|
||||
|
||||
def dec(self, c: int):
|
||||
"decode encoded msg c"
|
||||
return pow(c, self.d, self.n)
|
||||
Reference in new issue
Block a user