hello
- •
Attribution
- •
Solved by rn. AI-assisted solve; the original assistance link is preserved below.
- •
- •
What it asked
- •
Decrypt an RSA-like ciphertext whose plaintext is a polynomial in . The generator chose as the inverse of modulo , where and is small.
- •
- •
Math — factor N
- •
From , obtain . Since is close to and is small, appears among the useful continued-fraction convergents of for this instance.
- •
For every candidate , compute . Then and .
- •
The roots of are and . Require a nonnegative square discriminant, exact parity, exact tenth roots, , and primality. These checks reject continued-fraction false positives.
- •
- •
Math — decrypt in the quotient ring
- •
is not the Euler totient of this polynomial quotient ring, so the small generator value is not automatically the decryption exponent.
- •
Factor over and . For each square-free irreducible factor of degree , the component is or , whose nonzero elements have order or .
- •
Take as the least common multiple of those component orders, then . Positive exponentiation also preserves zero components, so recovers the polynomial even when a component is zero.
- •
Serialize all ten coefficients at one fixed byte width, preserve leading zero bytes, and validate complete PKCS#7 padding plus the
COMPFEST18{prefix. Finally require in the quotient ring.
- •
- •
Solution
- •
from sage.all import * def exact_tenth_root(v): root, exact = Integer(v).nth_root(10, truncate_mode=True) if not exact: raise ValueError('not a tenth power') return root def recover_factors(N, e): for conv in continued_fraction(QQ(e) / N**10).convergents(): k, d = conv.numerator(), conv.denominator() if k == 0 or d <= 1 or (e*d + 1) % k: continue phi_guess = (e*d + 1) // k S = N**10 - phi_guess + 1 disc = S*S - 4*N**10 if disc < 0 or not disc.is_square(): continue root = isqrt(disc) if (S + root) % 2: continue try: p = exact_tenth_root((S + root) // 2) q = exact_tenth_root((S - root) // 2) except ValueError: continue if p*q == N and p.is_prime() and q.is_prime(): return p, q raise ValueError('no valid convergent') def component_exponent(prime): R = PolynomialRing(GF(prime), 'x'); x = R.gen() factors = (x**10 - 2).factor() assert all(mult == 1 for _, mult in factors) return lcm(prime**f.degree() - 1 for f, _ in factors) p, q = recover_factors(N, e) lam = lcm(component_exponent(p), component_exponent(q)) d_true = inverse_mod(e, lam) m = c**d_true assert m**e == c coefficients = list(m) + [0] * (10 - len(list(m))) for width in range(1, 50): try: padded = b''.join(int(v).to_bytes(width, 'big') for v in coefficients) except OverflowError: continue pad = padded[-1] if (1 <= pad <= 10 and padded.endswith(bytes([pad]) * pad) and padded.startswith(b'COMPFEST18{')): print(padded[:-pad].decode()) break else: raise ValueError('no valid fixed-width, padded plaintext')
- •
- •
Verification
- •
Reproduced from the printed , , and ciphertext: is 2043 bits; the successful continued-fraction candidate was number 2903; factors with degrees over both prime fields; coefficient width is 6 bytes. The plaintext matches the PDF and re-encrypts to the supplied ciphertext exactly.
- •
- •
Concepts
- •