Irish Flan
6 minutes to read
Yum, time for dessert.
Challenge contributed by CryptoHack
Challenge files:
We are given a Python script that uses quaternions to hide an AES key used to encrypt the flag. The quaternion implementation is based on Python classes. We can assume that the implementation is correct (although there is a bug in the power of a quaternion, but it is not intended).
Source code analysis
The relevant part of the script is:
class Dessert:
def __init__(self):
self.n = getPrime(1024) * getPrime(1024)
self.R = Z(self.n)
r = secrets.randbelow(self.n**8)
self.χ = Q(*[self.R(secrets.randbelow(self.n)) for _ in "abcd"])
self.α = Q(*[self.R(secrets.randbelow(self.n)) for _ in "abcd"])
self.β = self.χ**-1 * self.α**-1 * self.χ
self.γ = self.χ**r
@property
def pub(self):
return (self.n,
self.α,
self.β,
self.γ)
def encrypt(self, m):
k = Q(*[self.R(secrets.randbelow(self.n)) for _ in "abcd"])
K = hashlib.sha256(str(k).encode()).digest()
c = AES.new(K, AES.MODE_CBC, iv=b"\0" * 16).encrypt(pad(m, 16))
s = secrets.randbelow(self.n**8)
δ = self.γ**s
ε = δ**-1 * self.α * δ
κ = δ**-1 * self.β * δ
μ = κ * k * κ
return (c, μ, ε)
if __name__ == "__main__":
flan = Dessert()
print("Public key:", ",".join(map(str, flan.pub)))
with open("flag.txt") as f:
print("Encryption:", ",".join(
map(str, flan.encrypt(f.read().strip().encode()))))
The Dessert
class defines a public modulus
In order to encrypt, the script takes a random quaternion
Solution
From the above values, we are given
- Our objective is to find
, which only appears in . So we need to find . - And to find
, we need to find , because there is a relation . - And we can probably find
from and , although we don’t know the exponent .
We may think that
One approach is to use a symbolic quaternion:
And try to solve for
An alternative approach is using the matrix form of the quaterions (let’s say lowercase greek letters denote quaternions and uppercase greek letters denote matrix forms), which is an equivalent procedure:
And
Now, this equation gives us
If we try to solve this with SageMath, we will get the trivial solution
For some reason, SageMath complains when trying to find the rank of
If we use C.right_kernel_matrix()
in SageMath, we will see that the basis of
for some integers
At this point, if we set whatever value in
However,
At this point, we can use the other relation:
And then,
So,
Something noticeable is that
Using the fact that
This time,
But even more, we don’t care about the value of
And the actual
But
Finally, we only need
Flag
After implementing all the required operations, we end up finding the flag:
$ python3 solve.py
ECSC{3qu1v4l3nt_k3y_rec0very_988b09742b}
The full script can be found in here: solve.py
.