Created
July 4, 2024 22:14
-
-
Save rgov/06e1549a6b87958867cbc313ebe26f18 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import math | |
import numpy as np | |
M = 26 | |
dim = 3 | |
# Generate a random encryption matrix. It might turn out to be unusable. | |
Ke = np.random.randint(0, M-1, size=(dim,dim)) | |
# Check that the encryption matrix is invertible over the finite field. If so, | |
# find the inverse of the determinant. | |
Ke_det = int(round(np.linalg.det(Ke))) | |
assert math.gcd(M, Ke_det) == 1 # det has a multiplicative inverse mod M | |
Ke_det_inv = pow(Ke_det, -1, M) | |
# Find the adjugate of the encryption matrix | |
Ke_adj = (Ke_det * np.linalg.inv(Ke)).round().astype(int) | |
# Find the decryption matrix | |
Kd = (Ke_det_inv * Ke_adj) % M | |
# Evaluate | |
encrypt = lambda m: (Ke @ m) % M | |
decrypt = lambda m: (Kd @ m) % M | |
test = np.random.randint(0, M-1, size=dim) | |
assert np.array_equal(decrypt(encrypt(test)), test) | |
# Try to solve for the encryption key, creating system of linear equations from | |
# a handful of known (plaintext, ciphertext) associations: | |
# | |
# Ke @ P = C mod M | |
# Ke = Ke @ P @ inv(P) = C @ inv(P) mod M | |
P = np.random.randint(0, M-1, size=(dim,dim)) # arranged in columns | |
C = np.apply_along_axis(encrypt, axis=0, arr=P) | |
# Same steps as finding Ke above | |
P_det = int(round(np.linalg.det(P))) | |
P_det_inv = pow(P_det, -1, M) | |
P_inv = (P_det_inv * P_det * np.linalg.inv(P)).round().astype(int) % M | |
# Solve for Ke again | |
Ke_prime = (C @ P_inv) % M | |
assert np.array_equal(Ke, Ke_prime) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment