I'm pretty bad at Rust, so I converted the code to Python to make it easier to understand
In main, we see that the compose function takes an integer(42) and returns the private and public key. The public key is a list of multiplication of 2 unknown numbers in a list, modulo an unknown number
The flag is then formatted into bits, and split into chunks of 42 bits. The record function then encrypts the chunks by multiplying the values of public key with its corresponding bit chunk value and takes the sum of all the final values.
This is an implementation of the knapsack cryptosystem, in which bruteforcing all the bits has 2^n complexity, so while it may seem secure, we can use a different method to break this cryptosystem by using the LLL algorithm.
# Shamelessly copied from ChatGPT
import random
import math
from typing import List, Tuple
def rndint(a: int, b: int) -> int:
"""Generates a random integer within the specified range (inclusive)."""
return random.randint(a, b)
def gcd(a: int, b: int) -> int:
"""Calculates the greatest common divisor of two integers."""
while b:
a, b = b, a % b
return a
def compose(n: int) -> Tuple[Tuple[List[int], int, int], List[int]]:
"""Generates the public and private keys for the knapsack cryptosystem."""
ps = [rndint(2**(i + n), 2**(i + n + 1) - 1) for i in range(n)]
m = rndint(2**(2 * n + 1) + 1, 2**(2 * n + 2) - 1)
tune = rndint(2, m - 2)
t = tune // gcd(tune, m)
mictape = [(t * a) % m for a in ps]
if gcd(t, m) != 1:
return compose(n) # Retry if gcd is not 1
return (ps, t, m), mictape
def record(msg: List[bool], mic: List[int]) -> int:
"""Encrypts a message using the public key."""
return sum(mic_val for msg_bit, mic_val in zip(msg, mic) if msg_bit)
def main():
"""Demonstrates the knapsack cryptosystem."""
n = 42
(ps, _, _), mic = compose(n)
print("Public Key (Mic):", mic)
msg_str = "GPNCTF{fake_flag}"
msg_bytes = msg_str.encode()
msg_bin = [
int(bit) for byte in msg_bytes for bit in format(byte, "08b")
]
for chunk in range(0, len(msg_bin), n):
msg_chunk = msg_bin[chunk : chunk + n]
if len(msg_chunk) < n:
msg_chunk.extend([0] * (n - len(msg_chunk)))
c = record(msg_chunk, mic)
print("Ciphertext Chunk:", c)
if __name__ == "__main__":
main()
from sage.all import *
M = identity_matrix(QQ,42)
M = M.augment(vector(mic))
M = M.stack(vector([-1/2 for i in range(42)]+[-c]))
ans=""
for k in t2:
M = identity_matrix(QQ, len(arr))
M = M.augment(vector(arr))
M = M.stack(vector([-1/2 for _ in range(len(arr))] + [-int(k)]))
M1 = M.LLL()
for row in M.LLL():
for row in (row, -row):
if row[-1] == 0:
subset = [t1[i] for i, x in enumerate(row[:-1]) if x+1/2==1]
if int(sum(subset)) == int(k):
r=(vector(row)[:-1] +vector([1/2 for i in range(len(arr))]))
ans+=''.join(list(map(str,r)))