Jekyll2022-09-07T20:35:58+08:00https://klwu.co/feed.xmlCocousin = UsinThe personal website for Wu "Cousin" Ka Lok. The place for blogs and write-ups to CTF challenges that he finds interesting.Cousin Wu Ka Lokhello@klwu.coFundamental Concepts Underlying Elliptic Curves (Level 2): Divisors and Pairings2022-05-12T02:00:00+08:002022-05-12T02:00:00+08:00https://klwu.co/knowledge/ec-basics-3-divisors<p>Elliptic curve has a lot of nice structures attached to it. One of them is the idea of divisors. Using divisors, we can construct an example of a pairing, which is used for Pairing-Based cryptography. This is especially useful in the blockchain world to construct cryptographic primitives like threshold signatures.
<!--exerpt-->
Here we only go over the basics of the theory of divisors and pairings, as we will be delaying the discussion of algebraic geometry, the field underlying the whole study of elliptic curves, to next time.</p>
<p>Prerequisites: <a href="/maths-in-crypto/ec-basics-1/">Fundamental Concepts Underlying Elliptic Curves (Level 1): Projective Space</a>)</p>
<script src="https://sagecell.sagemath.org/static/embedded_sagecell.js"></script>
<script>sagecell.makeSagecell({"inputLocation": ".sage", hide: ["fullScreen", "permalink", "sessionFiles"]});</script>
<h1 id="introduction">Introduction</h1>
<h1 id="zeros-and-poles">Zeros and Poles</h1>
<p>In complex analysis, we study functions that are differentiable (in the complex sense) at almost all points. The study of the zeros (\(f(z)=0\)) and poles (\((1/f)(z)=0\)) is very important, because in complex analysis, the famous Cauchy’s integral theorem implies if \(f\) is holomorphic (does not have any poles), then for \(C\) enclosing a simply connected domain,\[\oint_Cf(z)d\,z=0,\]
on the other hand, the residue theorem implies that the integral of \(f\) (that has poles) relates to the poles and its order.</p>
<h2 id="order-of-zeros">Order of zeros</h2>
\[\DeclareMathOperator{\div}{div} \DeclareMathOperator{\Div}{Div} \DeclareMathOperator{\Pic}{Pic} \DeclareMathOperator{\Cl}{Cl} \DeclareMathOperator{\ord}{ord}\]
<p>For polynomials, we know that \(f(x)=x\) and \(g(x)=x^2\) both have a zero at \(x=0\), but for \(g(x)\), the root is a “repeated root”. We say that for \(f(x)\), the order of zero (at \(x=0\)) is \(1\) while that of \(g(x)\) is \(2\).</p>
<p>On the other hand, \(h(x)=1/x\) and \(u(x)=1/x^2\) both have a pole at \(x=0\), but the order of pole of \(h\) is \(1\) while that of \(u\) is \(2\)<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>.</p>
<p>In general, for a function \(f(z)\), the order of zero of a point \(z_0\) is \(m\), if \(f\) is analytic at \(z_0\), \(f^{(n)}(z_0)=0\) for \(n=0,1,\cdots,m-1\), and \(f^{(m)}(z_0)\neq 0\). The order of pole of a point \(z_0\) is \(m\), if for the function \(1/f\), the order of zero of \(z_0\) is \(m\).</p>
<p>For rational functions (where it can be written as a fraction, both numerator and denominator are polynomials), the order of zero of the whole function is the order of zeros of the numerator minus the order of zeros of the denominator. We say the order of zero of the zero polynomial is infinity.</p>
<p>We can denote the order of zero of a holomorphic (complex-differentiable) function \(f\) at the point \(p\) by \(ord_p(f)\). Then to extend the definition to meromorphic functions, if \(f=g/h\) where \(g\) and \(h\) are holomorphic , then \(\ord_p(f)=\ord_p(g)-\ord_p(h).\)</p>
<p>The order satisfies the following properties:</p>
<ol>
<li>\(\ord_p(f)\in \mathbb{Z}\cup\{\infty\},\) and \(\ord_p(f)=\infty\iff f=0\)</li>
<li>\(\ord_p(fg)=\ord_p(f)+\ord_p(g)\) for any \(f\) and \(g\)</li>
<li>\(\ord_p(f+g)\leq \min\{\ord_p(f),\ord_p(g)\}\), equality holds if \(f\neq g\).</li>
</ol>
<hr />
<h1 id="divisor-of-meromorphic-functions">Divisor of Meromorphic Functions</h1>
<p>The zeros and poles of a meromorphic functions can be captured in a simple object.</p>
<h2 id="divisor-group">Divisor Group</h2>
<p>Let \(\Div(\mathbb{C}^*)\) be the free abelian group generated by the points of \(\mathbb{C}^*\)<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup>. Elements of \(Div(\mathbb{C}^*)\) have the form \(\displaystyle\sum_{P\in\mathbb{C}^*} n_p (P)\), where the sum is finite.</p>
<p>These elements are called divisors. The group itself is called the divisor group of \(\mathbb{C}^*\).</p>
<p>If \(D\in \Div(\mathbb{C}^*)\), we can define the <strong>degree</strong> of a divisor as \(\displaystyle\deg D = \sum_{P\in\mathbb{C}^*} n_p\).</p>
<h3 id="divisor-of-meromorphic-functions-1">Divisor of Meromorphic Functions</h3>
<p>For a meromophic function \(f(z)\), we can define the <strong>divisor</strong> of \(f\) as \[\div f=\sum_{P\in\mathbb{C}^*} \left(\ord_Pf\right)(P).\]
Since \(f\) is meromorphic, the points \(P\) at which \(\ord_P f\neq 0\) is finite, so \(\div f\) is indeed a divisor. In fact, if \(f\in K(\mathbb{C}^*)^*\) (non-zero meromorphic function), then \(\deg\div f=0\).</p>
<h2 id="principal-divisors">Principal Divisors</h2>
<p>Let \(K(\mathbb{C}^*)\) be the field of meromorphic functions on \(\mathbb{C}^*\). Then the divisor function \(\div\cdot: K(\mathbb{C}^*)^*\to \Div(\mathbb{C}^*)\) is a homomorphism of groups. Divisors of the form \(\div f\) is known as <strong>principal divisors</strong>.</p>
<p>We say two divisors are linearly equivalent, denoted \(D\sim D'\), if \(D-D' = \div f\) for some meromorphic \(f\). This give rise to the quotient group of \(\Div(\mathbb{C}^*)\) by the principal divisors, as the <em>Picard group</em> (or the divisor class group) of \(\mathbb{C}^*\), denoted as \(\Pic(\mathbb{C}^*)\) or \(\Cl(\mathbb{C}^*)\).</p>
<h2 id="example">Example</h2>
<p>Let’s work through an example of computing the divisor of the function \(f(z) = z^2 + 1\). Obviously we have a pole of order \(1\) at \(z=i\) and \(-i\). However, since we are considering the domain of \(\mathbb{C}^*\), we also need to consider the point at infinity. In this case we say we have a pole at infinity if \(f(\frac 1z)\) has a zero at \(z=0\), and a pole at infinity if \(f(\frac 1z)\) has a pole at \(z=0\).</p>
<p>Here we have \(f(\frac 1z) = \frac{1}{z^2}+1\), which has a pole of order \(2\) at \(z=0\). So in the divisor of \(f\) is \[\div f = (i)+(-i)-2(\infty)\]</p>
<p>Of course, since \(f\) is meromorphic, we already know that \(\deg \div f = 0\), so after computing the zeros and poles of points in \(\mathbb{C}\), we can already conclude that \(\ord_\infty f = -2\).</p>
<hr />
<h1 id="divisors-of-elliptic-curves">Divisors of Elliptic Curves</h1>
<p>Now we turn our attention back to elliptic curves. The theory of divisors are almost the same as the case of \(\mathbb{C}^*\) (with the details omitted for now), except in the case of the Riemann shpere, we are considering meromorphic functions. In the case of elliptic curves, the function we consider <em>rational functions</em>, which we shall discuss next time, but for now, just think of them as “locally” a ratio of polynomials.</p>
<h2 id="elliptic-curve-and-its-picard-group">Elliptic Curve and its Picard Group</h2>
<p>Using the definition as above, and let \(E\) be an elliptic curve,</p>
<blockquote class="notice--primary">
<p><strong>Theorem</strong> : For each degree-0 divisor \(D\in \Div(E)\), there exists a point \(P\in E\) such that \(D\sim(P)-(\mathcal{O})\).</p>
<p>This gives an isomorphism of groups between \(E\), and the degree-0 part of the Picard group \(\Pic^0(E)\) (where the group operation is taken from \(\Div(E)\)).</p>
</blockquote>
<blockquote class="notice--primary">
<p><strong>Corollary</strong><a name="cor"></a> : Let \(E\) be an elliptic curve and \(\displaystyle D=\sum_{P\in E}n_P(P)\in\Div(E)\) be a divisor. Then \(D\) is a principal divisor if and only if\[\sum_{P\in E}n_P=0\qquad\text{and}\qquad\sum_{P\in E} [n_P]P = \mathcal{O} \]</p>
<p>Where the second summation is using the group law of elliptic curves.</p>
</blockquote>
<hr />
<h1 id="pairing-based-cryptography">Pairing-based Cryptography</h1>
<p>To see how divisors relate to cryptography, we first turn our attention back to cryptoghraphy.</p>
<p>Let \(G_1,G_2,G_T\) be three cyclic groups all of order \(q\), where \(q\) is a prime number. By convention we write the first two group additively, and the third multiplicatively. Then a pairing is a map \(e:G_1\times G_2\to G_T\) such that</p>
<ol>
<li>(Bilinearity 1) For every \(U,V\in G_1\) and \(W\in G_2\), \(e(U+V,W)=e(U,W)e(V,W)\).</li>
<li>(Bilinearity 2) For every \(U\in G_1\) and \(V, W\in G_2\), \(e(U,V+W)=e(U,V)e(U,W)\).</li>
<li>(Non-degeneracy) For every \(P\in G_1\) that is not the identity, there exists \(Q\in G_2\) such that \(e(P,Q)\neq 1\). Similarly for \(Q\in G_2\).</li>
</ol>
<blockquote class="notice--warning">
<p><strong>Example</strong> (Determinant): For every free \(R\)-module \(M\), the determinant map \(\det: M\times M\) is a non-degenrate bilinear map.</p>
<p>Let \(R=\mathbb{Z}/m\mathbb{Z}\) where \(m\) is an integer larger than 1, and consider the free \(R-\)Module \(R\times R\), the determinant is a pairing. Choosing a basis \(\{u, v\}\), we have \[\det(au+bv, cu+dv)=ad-bc \]
where we note that the value is independent of the choice of basis.</p>
</blockquote>
<p>We also note that the definition of pairing implies for every \(P\in G_1, Q\in G_2\) and \(a,b\in \mathbb{Z}\), we have \(e(aP,bQ)={e(P,Q)}^{ab}\).</p>
<blockquote class="notice--warning">
<p><strong>Example</strong> : Let \(G_1=G_2=F_5\) (under addition), and \(G_T=\langle 5\rangle =\{1,3,4,5,9\}\in F_{11}^\times\) (under multiplication). Then the map \(e:G_1\times G_2\to G_T\), \(e(x,y)=3^{xy}\pmod{11}\) is a pairing.</p>
<p>Check to see that \(e(u+v, w)=3^{(u+v)w}= 3^{uw}3^{vw}=e(u,w)e(v,w)\). The case for \(G_2\) is the same. To see non-degeneracy, in this case we just need to enumerate all possibilities.</p>
</blockquote>
<div class="sage">
<script type="text/x-sage">
Z5 = Integers(5)
x, y = PolynomialRing(Z5, 2, 'xy').gens()
f(x,y) = 3^(x*y)
for i in range(5):
for j in range(5):
print("e({},{})={}".format(i, j, int(f(i,j))%11))
non_degenerate = all(any(int(f(i,j))%11 != 1 for j in range(5)) for i in range(1,5))
print("Non-degeneracy: {}".format(non_degenerate))
</script>
</div>
<blockquote class="notice--warning">
<p><strong>Exercise</strong>: Let \(G_1,G_2,G_T\) as above, \(u\in G_1, v\in G_2\) and \(a\in \mathbb{Z}_{>0}\). Show that \(e(au, v) = e(u,av)\).</p>
</blockquote>
<h2 id="bls-digital-signature">BLS digital signature</h2>
<p>One application of pairings in cryptography is to create digital signatures. Let \(G, G_T\) be cyclic groups of prime order \(q\), and \(e: G\times G\to G_T\) be a pairing. Additionally we also assume a hash function \(H\) that outputs an element in \(G\). Pick a generator \(g\) for \(G\).</p>
<p>To generate the key, we pick a random integer \(x\) with \(0<x<q\), the private key is \(x\), and the public key is \(g^x\).</p>
<p>To sign a message \(m\), we first compute the hash of it \(H(m)\), then the signature is \(H(m)^x\).</p>
<p>To verify a message \(s\) and the public key \(pk\), we simply verify that \(e(s,g) = e(H(m),pk)\).</p>
<p>The correctness of the signature is due to the fact that \(e(s,g) = e(H(m)^x, g) = e(H(m), g^x) = e(H(m), pk))\).</p>
<p>One nice property of BLS is that the signature is very small: it consists of only a single group element (of roughly the same size as \(q\))!</p>
<h3 id="computational-diffie-hellman">Computational Diffie-Hellman</h3>
<p>The security of BLS relies on the fact that (roughly) the discrete logarithm problem of the underlying group \(G\) is hard. More specifically, we assume that the <strong>computational Diffie-Hellman</strong> (CDH) problem is intractable.</p>
<blockquote class="notice--primary">
<p><strong>Definition</strong> (CDH): Let \(\langle g\rangle\) be a finite cyclic group of prime order \(p\). Let \(a,b\in \mathbb{Z}_p^*\), and given \((g, g^a, g^b)\), the <strong>computational Diffie-Hellman</strong> problem asks the value of \(g^{ab}\).</p>
</blockquote>
<hr />
<h1 id="weil-pairing">Weil Pairing</h1>
<p>Now we try to construct a pairing with elliptic curves. Let \(E\) be an elliptic curve over \(K\), \(m\) an integer larger than 2, which is relatively prime to \(char(K)\).</p>
<blockquote class="notice--primary">
<p><strong>Proposition-Definition</strong> (\(m-\)torsion point): The group of \(m-\)torsion points \(E[m]=\{P\in E: [m]P=\mathcal{O}\}\) are the points \(P\in E\) such that \([m]P=0\). It is the kernel of the function \([m]: E\to E\) defined by \(P\mapsto [m]P\).</p>
<p>We have that \(E[m] \cong \mathbb{Z}/m\mathbb{Z} \times \mathbb{Z}/m\mathbb{Z}\).</p>
</blockquote>
<p>Let \(P, Q\in E[m]\), and choose a rational function \(F\) such that the divisor is \[\div F = \sum_{0\leq k<m} (P+[k]Q) - ([k]Q) \].
This is possible because \(\displaystyle \sum_{0\leq k<m} P+[k]Q - [k]Q = [m]P= \mathcal{O}\) (since \(P\) is a \(m-\)torsion point), and by applying <a href="#cor">the corollary</a> above the divisor is principal.</p>
<p>Let \(G(X) = F(X+Q)\), then the divisor of \(G\) is the same, so \(F(X+Q)/F(X)\) is constant, and is a \(m-\)th root of unity. Then define the Weil pairing as \(e_m(P,Q) = \frac{F(X+Q)}{F(X)}\). This is a mapping from \(E[m]\times E[m]\to \mu_m\), where \(\mu_m\) contains the \(m-\)th roots of unity \(\mu_m=\{x\in K: x^m=1\}\).</p>
<h2 id="example-1">Example</h2>
<p>Here we use an example of the curve <em>BLS 12-381</em>, which is defined by \(y^2=x^3+4\pmod{q}\) where \(q\) is as defined below. The code snippet below shows a weil pairing with \(m=11\).</p>
<div class="sage">
<script type="text/x-sage">
q = 0X1A0111EA397FE69A4B1BA7B6434BACD764774B84F38512BF6730D2A0F6B0F6241EABFFFEB153FFFFB9FEFFFFFFFFAAAB
Zq = Integers(q)
E = EllipticCurve(Zq, [0, 4])
print(E)
P = E([6963810901242914501296791317468687704362012588555541933123759187588889612276948934757591021590876452626889675801 , 450183371774468501117745075101600224463590854136802934424170599121456489686626344016959585454111419105009962888370])
Q = E([565580074136799723979560811233171866688998818285942115042196744160793914024700832248898459781922310329909059071278 , 2474821493729866814383104269040477595147217218335444381148461749618861292699112515310718674711696883089064925070941])
print("e(P,Q) = {}".format(P.weil_pairing(Q, 11)))
# The output of e is a 11-th root of unity.
print("e(P,Q)^11 = {}".format(P.weil_pairing(Q, 11)^11))
# Bilinearity
print("e(P,3Q) == e(P,Q)^3: {}".format(P.weil_pairing(3*Q, 11) == P.weil_pairing(Q,11)^3))
print("e(5P,Q) == e(P,Q)^5: {}".format((5*P).weil_pairing(Q, 11) == P.weil_pairing(Q,11)^5))
</script>
</div>
<h1 id="references">References</h1>
<p>Silverman, J. H. (2009). <em>The arithmetic of elliptic curves</em> (Vol. 106, pp. xx+-513). New York: Springer.</p>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>We also say the order of zeros of \(h\) is \(-1\). <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p>The same as \(\mathbb{P}^1(\mathbb{C})\), the complex plane plus the point at infinity. \(\mathbb{C}^*\) is also known as the Riemann sphere. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>Cousin Wu Ka Lokhello@klwu.coElliptic curve has a lot of nice structures attached to it. One of them is the idea of divisors. Using divisors, we can construct an example of a pairing, which is used for Pairing-Based cryptography. This is especially useful in the blockchain world to construct cryptographic primitives like threshold signatures.Fundamental Concepts Underlying Elliptic Curves (Level 1): Projective Coordinates2021-12-16T00:00:00+08:002021-12-16T00:00:00+08:00https://klwu.co/maths-in-crypto/ec-basics-2<p>Last time we mentioned the basic concepts of Elliptic curves. However, there were some lingering questions about the weird representation of points in sage being \((x:y:1)\). In this post, we will answer this question by introducing the notion of projective space.
<!--exerpt--></p>
<script src="https://sagecell.sagemath.org/static/embedded_sagecell.js"></script>
<script>
sagecell.makeSagecell({
"inputLocation": ".sage",
"hide": ["fullScreen", "permalink", "sessionFiles"],
"editor": "codemirror-readonly"});
</script>
<p>Prerequisites: <a href="/maths-in-crypto/ec-basics-1/">Fundamental Concepts Underlying Elliptic Curves (Level 0): High-level Overview</a>)</p>
<h1 id="introduction">Introduction</h1>
<p>The point at infinity raises many questions. For example, why is it represented as \((0:1:0)\) and other points as \((x:y:1)\)?</p>
<hr />
<h1 id="equivalence-relations">Equivalence Relations</h1>
<p>We say a relation \(R\) over the set \(X\) is a subset of \(X\times X\). If \((a,b)\in R\), we write \(aRb\), or \(a\sim b\). Also say \(\sim\) is the relation.</p>
<p>Using the notion of relation, we can define an equivalence relation on X as a relation \(\sim\) such that</p>
<ol>
<li>Reflexivity: \(\forall a\in X, a\sim a\)</li>
<li>Symmetry: \(\forall a,b\in X, a\sim b\implies b\sim a\)</li>
<li>Transitivity: \(\forall a,b,c\in X, a\sim b,b\sim c\implies a\sim c\)</li>
</ol>
<h2 id="example-1-trivial-and-diagonal-relations">Example 1: Trivial and Diagonal Relations</h2>
<p>The trivial relation \(S\times S\) (so \(a\sim b\) for every \(a,b\in S\)) and the diagonal relation \(\Delta=\{(a,a):a\in S\}\) (so we only have \(a\sim a\) for all \(a\in S\) and nothing else) are equivalence relations on \(S\).</p>
<p>On the other hand, the empty relation \(\emptyset\) is a relation, but not an equivalence relation. To see why, note that reflexivity requires \((a,a)\in R\) for all \(a\in X\), but empty relation is, obviously, empty.</p>
<p>This also disproves the misconception some students have when learning the subject: if we have symmetry and transivity, symmetry gives \(a\sim b\) and \(b\sim a\), then transitivity gives \(a\sim a\), so reflexivity is not needed, right? The empty relation is a counter-example to this.</p>
<h2 id="example-2-fractions">Example 2: Fractions</h2>
<p>Define a relation \(\sim\) on \(\mathbb{Z}-\{0\}\) as saying \((a,b)\sim (c,d)\) if \(ad=bc\). This in fact defines the equivalence of (non-zero) fractions by viewing \((a,b)\) as \(a/b\).</p>
<h3 id="proof">Proof</h3>
<ol>
<li>Reflexivity: for \((x,y)\in(\mathbb{Z}-\{0\})^2\), \(xy=xy\) so \((x,y)\sim (x,y)\).</li>
<li>Symmetry: for \((x_1,y_1)\) and \((x_2,y_2)\in(\mathbb{Z}-\{0\})^2\), \((x_1,y_1)\sim (x_2,y_2)\) \(\implies x_1y_2=x_2y_1\) \(\implies (x_2,y_2)\sim (x_1,y_1)\).</li>
<li>Transivity: for \((x_1,y_1),(x_2,y_2),(x_3,y_3)\in(\mathbb{Z}-\{0\})^2\), if \((x_1,y_1)\sim (x_2,y_2)\) and \((x_2,y_2)\sim (x_3,y_3)\) \(\implies x_1y_2=x_2y_1\) and \(x_2y_3=x_3y_2\). Then\(\begin{align*}
x_1y_2=&x_2y_1\\
x_1y_2y_3=&x_2y_1y_3&\text{ by multiplying both sides by }y_3\\
x_1y_2y_3=&x_3y_2y_1&\text{ by }x_2y_3=x_3y_2\\
x_1y_3=&x_3y_1&\text{Since }y_2\neq 0.
\end{align*}\)</li>
</ol>
<p>So \((x_1,y_1)\sim(x_3,y_3)\).</p>
<blockquote class="notice--warning">
<p><strong>Exercise</strong></p>
<p>For any \(a,b\in\mathbb{Z}\), say \(a\equiv b\) (mod n) if \(a-b\) is divisible by n, or \(\exists k\in\mathbb{Z}\) such that \(a-b=nk\). Verify that this is an equivalence relation.</p>
</blockquote>
<h2 id="partitions">Partitions</h2>
<p>Let \(\equiv\) be an <strong>equivalence relation</strong> defined on the set X. Take \(a\in X\), denote the <strong>equivalence class</strong> of a: \([a]=\{k\in X: a\equiv k\}\) to be the set of elements <strong>equivalent</strong> to \(a\). Then \(\forall a,b\in X\), either\[
[a]=[b]\text{ or } [a]\cap[b]=\emptyset.
\]
This essentially show that the equivalence relation <strong>partitions</strong> a set X.</p>
<h3 id="proof-1">Proof</h3>
<p>Take \(a,b\in X\) and let \(\equiv\) to be the equivalence relation.<br />
If there are any elements, say \(c\), such that \(c\) is both in \([a]\) and \([b]\), then we have that\[<br />
a\equiv c\text{ and } b\equiv c.\]
By symmetry of equivalence relations, we get \(c \equiv b\). Then by <strong>transitivity</strong>, we get \(a\equiv b\). Now consider any elements \(k\in [b]\). We get \(b\equiv k\), so by the same argument, we get \(a\equiv k\), so \([b]\subseteq[a]\).<br />
Similarly, every element in \([a]\) is equivalent to b, so \([a]\subseteq[b]\).<br />
In this case we have \[[a]=[b].\]<br />
If there are no common elements in \([a]\) and \([b]\), then by definition \([a]\cap[b]=\emptyset\).</p>
<h2 id="quotient-set">Quotient Set</h2>
<p>Given a set \(X\) and an equivalence relation \(\sim\) on \(X\), we can define the <strong>quotient set</strong> \(X/\sim\) to be the set of equivalence classes \(\{[a]: a\in X\}\).</p>
<h3 id="example">Example</h3>
<p>As an example, take the mod equivalence relation on \(\mathbb{Z}\), i.e. for any \(a,b\in\mathbb{Z}\), say \(a\equiv b\) (mod n) if \(a-b\) is divisible by n. This partitions the integers into \(n\) <strong>equivalence classes</strong>. The equivalence classes are the \(n\) different remainders of integers when dividing by \(n\). This gives us the quotient set \(\mathbb{Z}/n\mathbb{Z}=\{[x]:x\in\mathbb{Z}\}\) which we can take the representative \([x]\) for \(x=0,1,\cdots,n-1\), so we can view \(\mathbb{Z}/n\mathbb{Z}=\{0,1,\cdots,n-1\}\).</p>
<h2 id="well-defined-functions-on-quotients">Well-Defined Functions on Quotients</h2>
<p>Given a function \(f\) on the set \(X\) and a equavalence relation \(\sim\) on \(X\), we can construct a “function” \(\tilde{f}\) on the quotient set \(X/\sim\) by setting \(\tilde{f}([x])=f(x)\). This is problematic, however, because if we pick two elements \(a,b\) from the same equivalence class \([x]\), then since \([a]=[b]\), the definition gives \(\tilde{f}([a])=\tilde{f}([b])\), which means that we will need \(f(a)=f(b)\).</p>
<p>The property that every representative of the same equivalence class should map to the same value is not trivial. This property is called well-definedness. A function is well-defined on the quotient when the value of the function is independent of the choice of representative.</p>
<p>It turns out that in general, functions defined this way are not well-defined. However when we deal with sets with additional structures such as groups and rings, this construction of \(\tilde{f}\) on certain equivalence relations will give a well-defined function. For example, the multiplication of integers mod \(n\), constructed using the ordinary integer multiplication, is well-defined.</p>
<h3 id="non-example">Non-Example</h3>
<p>Let us look at an example of non well-defined function<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>.</p>
<p>We define a function on non-zero fractions: write the fraction as \(\frac{a}{b}\), and set \(f(\frac{a}{b})=b\). This function is not well defined, because for example \(f(\frac{1}{2})=2\), but \(f(\frac{2}{4})=4\), and \(\frac{1}{2}=\frac{2}{4}\). When we fix this by changing the definition to: \(g(\frac{a}{b})=b/\gcd(a,b)\), this will be well-defined, because dividing by the gcd gives us the reduced form of fractions, which is unique.</p>
<hr />
<h1 id="projective-space">Projective Space</h1>
<p>Now we are ready to talk about the projective space. Instead of considering the points in the space \(K^n\) (where \(K\) is a field), we can consider the <strong>lines</strong> in \(K^n\), with the condition that it must pass through the center.</p>
<p>For example, let’s consider the real euclidean plane \(\mathbb{R}^2\). The lines that pass through the center will all have the form \(y=kx\), where \(k\in\mathbb{R}\) is the slope, or it may also be \(x = 0\) for the vertical line. We can also just use a point \(P=(x,y)\) to represent a line by constructing the unique line passing through \(P\) and the origin.</p>
<p>In this case, if we have two points \(P\) and \(Q\), with \(P,Q\) and the origin being colinear, then \(P\) and \(Q\) will define the same line, so we can consider them to be equivalent. If \(P=(x,y)\) and \(Q=(x',y')\), then \(P\) and \(Q\) define the same line if and only if there exists a non-zero integer \(\lambda\) such that \(y=\lambda x\) and \(y'=\lambda x'\). Of course \((0,0)\) can not define a line in this way.</p>
<p>Using this construction, we just made the real projective line \(\mathbb{P}^1(\mathbb{R})\). We can generalize this construction by using the point construction.</p>
<h2 id="definition-of-projective-space">Definition of Projective Space</h2>
<p>Given a field \(K\), define the Projective \(n\)-space to be the set \(\mathbb{P}^n(K)=\{(x_0,\cdots,x_n)\in K^{n+1}-\{0\}: x_i\in K\}/\sim\), where we declare \((x_0,\cdots,x_n)\sim(y_0,\cdots,y_n)\) if there exists \(\lambda\in K-\{0\}\) such that \((x_0,\cdots,x_n)=(\lambda y_0,\cdots,\lambda y_n)\).</p>
<p>We can denote the set of equivalence class as \([x_0:\cdots:x_n]\) or \([x_0,\cdots,x_n]\), and they are called the homogeneous coordinates.</p>
<h2 id="real-projective-line">Real Projective Line</h2>
<p>Take \([a,b]\in \mathbb{P}^1(\mathbb{R})\).</p>
<p>If \(b\neq 0\), we have \([a,b]=[a/b,1]\), and we can actually rewrite as \([x,1]\), where \(x\in\mathbb{R}\).</p>
<p>If \(b=0\), then \(a\neq 0\) so we can rewrite \([a,0]=[1,0]\).</p>
<p>In summary, we have \(\mathbb{P}^1(\mathbb{R})=\{[x,1]: x\in\mathbb{R}\}\sqcup\{[1,0]\}\).</p>
<h3 id="different-interpretations">Different Interpretations</h3>
<p>We can describe \(\mathbb{P}^1(\mathbb{R})\) as the set of all lines that pass through the origin. Note that \([a,b]=[\lambda a, \lambda b]\) exactly means that \((a,b)\) and \((\lambda a, \lambda b)\) lies on the same line (that passes through the center). Then \([a,1]\) gives the line \(x=ay\) and \([1,0]\) gives the horizontal line \(y=0\).</p>
<p>Another interpretation is that since \(\{[x,1]: x\in\mathbb{R}\}\) is really just \(\mathbb{R}\) (since the second coordinate does nothing), we can regard \(\mathbb{P}^1(\mathbb{R})\) as \(\mathbb{R}\), but with an extra point \([0,1]\) we shall call the <strong>point at infinity</strong>. Some may denote this as \(\mathbb{R}\cup\{\mathcal{O}\}\), where we use \(\mathcal{O}\) to denote the point at infinity.</p>
<p>Taking the line interpretation, for each line that passes through the center we can pick a point on the upper part of the unit circle (since such a line will pass through the unit circle at 2 points, so we pick the upper part). The points \((-1,0)\) and \((1,0)\) represent the same line, so we can join them together (identify them as the same point), and we see that in fact \(\mathbb{P}^1(\mathbb{R})\) is really the same as the unit circle. Since circle is compact, \(\mathbb{P}^1(\mathbb{R})\) (with the quotient topology of \(\mathbb{R}^2\)) is also compact.</p>
<h2 id="mathbbp2k">\(\mathbb{P}^2(K)\)</h2>
<p>How about \(\mathbb{P}^2(K)\)? It consists of the points \([x,y,z]\). If \(z \neq 0\), then we can just divide the whole thing by \(z\) to get \([x/z,y/z,1]\), and we can map these points \([a,b,1] \mapsto (a,b)\in K^2\). If \(z=0\), then since we cannot divide by zero, we consider the points \([x,y,0]\) the <strong>points at infinity</strong>.</p>
<p>In fact, if \(y\neq 0\) we can divide by \(y\) to get \([a,1,0]\), and further \(x=0\) we get \([0,1,0]\). So when \(z=0\), the remaining coordinates resemble \(\mathbb{P}^1(K)\)! So In summary we have the following disjoint union: \[ \mathbb{P}^2(K)=K^2\sqcup\mathbb{P}^1\]</p>
<p>We can also make a general statement of \(\mathbb{P}^n(K) = K^n\sqcup\mathbb{P}^{n-1}(K)\) with the same argument.</p>
<h2 id="affine-space">Affine Space</h2>
<p>We also have one space, the usual \(K^n\), which we will call it the affine \(n-\)space \(\mathbb{A}^n(K)=\{(x_1,\cdots,x_n):x_i\in K\}\).</p>
<h1 id="homogeneous-polynomials">Homogeneous Polynomials</h1>
<h2 id="polynomials-over-affine-space">Polynomials over Affine Space</h2>
<p>For an affine space \(\mathbb{A}^n(K)\), we can define a polynomial \(f(x_1,\cdots,x_n)\) as the set the formal sum of the form \(\displaystyle\sum_{d=1}^N\sum_{i_1+\cdots+i_n = d} a_{i_1i_2\cdots i_n} x_1^{i_1}\cdots x_n^{i_n}\), where \(a_{i_1\cdots i_n}\in K\). The <strong>degree</strong> of a polynomial is the highest sum of the exponents of the variables with non-zero coefficients. For example, \(f(x,y)=xy^3 + x^2 + xy + y + 1\) has degree 4.</p>
<p>There is a polynomial evaluation function \(\phi_f:\mathbb{A}^n(K)\to K\) by mapping \((a_1,\cdots,a_n)\mapsto f(a_1,\cdots,a_n)\). Then we can talk about the the set of points \(V(f)=\{P\in\mathbb{A}^n(K): f(P)=0\}\), aka the roots. This can give us an example of an <em>affine</em> <strong>variety</strong>, but we shall discuss this next time.</p>
<h2 id="homogeneous-polynomials-1">Homogeneous Polynomials</h2>
<p>Can this be mimicked to the projective space? Given \(f(x_0,\cdots,x_n)\), we may, carelessly, consider the root of \(f(P)=0,\), where \(P\in\mathbb{P}^n(K)\). This is problematic, however, because \(\lambda P \sim P\) for any non-zero \(\lambda\in K\), but \(f(P)=0\) in general does not mean \(f(\lambda P)=0\).</p>
<p>We say the function \(\phi_f: \mathbb{P}^n(K)\to K\) by \(\phi_f(P)=f(P)\) is not <strong>well-defined</strong>, because the value will depend on the choice of the representative \(P\) in the equivalence class.</p>
<p>This is why we can only consider polynomials in which the above holds.</p>
<blockquote class="notice--success">
<p><strong>Definition</strong>:</p>
<p>We say a polynomial \(f(x_1,\cdots,x_n)\) is <strong>homogeneous</strong> of degree <strong>d</strong> if all the terms \(a_{i_1i_2\cdots i_n} x_1^{i_1}\cdots x_n^{i_n}\) has the property that \(a_{i_1i_2\cdots i_n}\in K\) and \(i_1+\cdots+i_n=d\).</p>
<p>Equivalently, for all \(\lambda\in K\), \(f(\lambda x_1,\cdots,\lambda x_n)=\lambda^n f(x_1,\cdots,x_n)\).</p>
</blockquote>
<blockquote class="notice--warning">
<p><strong>Exercise</strong></p>
<p>Proof that the two conditions are indeed equivalent.</p>
</blockquote>
<p>For a homogeneous polynomial \(f\), and \(P\in\mathbb{P}^n(K)\), we really have \(f(P)=0\) implying \(f(\lambda P)=0\) for all \(\lambda\in K\). So we can make sense of the roots of homogeneous polynomials in projective space. We can also have the notation \(V(f)=\{P\in\mathbb{P}^n(K): f(P)=0\}\) where \(f\) is a homogeneous polynomial. This is a <em>projective</em> <strong>variety</strong>, and we will also delay the discussion of this.</p>
<h2 id="homogenization">Homogenization</h2>
<p>For any polynomial \(f(x_1,\cdots,x_n)\), we can convert it to a homogeneous polynomial \(F(x_0, x_1,\cdots,x_n)\) of degree \(d\) (\(d\) equal to the largest degree of terms in \(f\)) by doing \(F(x_0,\cdots,x_n)=x_0^d f(\frac{x_1}{x_0}\cdots,\frac{x_n}{x_0})\), or you may just think of the operation as multiplying each term by an appropiate power of \(x_0\), so that the end result has degree \(d\). This is called the <strong>homogenization</strong> of the polynomial.</p>
<p>We can convert \(F(x_0, x_1,\cdots,x_n)\) to \(f(x_1,\cdots,x_n)\) easily by \[ f(x_1,\cdots,x_n)=F(\mathbf{1},x_1,\cdots,x_n).\]</p>
<h3 id="example-1">Example</h3>
<p>For \(f(x,y) = y^2 - x^3 + 1\), the homogenization of \(f(x,y)\) will be \(F(x,y,z) = y^2z - x^3 + z^3\) (\(z\) is moved to the third coordinate for simplicity). Then \(F(x,y,1) = y^2-x^3+z^3 = f(x,y)\).</p>
<blockquote class="notice--warning">
<p><strong>Exercise</strong></p>
<p>Show that any homogeneous polynomial \(F(x_1,\cdots,x_n)\) of degree \(d\) satisfies the following:\[ d\cdot F=\sum\limits_{i=1}^n x_i \frac{\partial F}{\partial x_i}\]</p>
</blockquote>
<hr />
<h1 id="elliptic-curves-as-homogeneous-polynomials">Elliptic Curves as Homogeneous Polynomials</h1>
<p>For elliptic curves in (short) Weierstrass form \(f(x,y) = y^2 -( x^3 + Ax + B)\), the homogenization is \(F(x,y,z)=y^2z -( x^3 + Axz^2 + Bz^3)\). Now for any \((x,y)\in \mathbb{A}^2(K)\), we have an inclusion \(i: \mathbb{A}^2(K)\hookrightarrow \mathbb{P}^2(K)\) by \((x,y)\mapsto [x,y,1]\). So for those points, \(F(i(x,y)) = f(x,y)\).</p>
<p>If \(z=0\), then \(F(x,y,0) = -x^3\). Since \(F(0,y,0) = 0\) for any \(y\neq 0\) ( \(y=0\) is not allowed), there is only 1 point of infinity (out of the others in the form \([x,y,0]\)) that is also in the elliptic curve, namely \([0,1,0]\).</p>
<p>This suggests that we should define elliptic curves using projective space as: given the polynomial \(f(x,y) = y^2 - (x^3 + Ax+B)\) in short Weierstrass form, the elliptic curve is the set of points that satisfy \(F(x,y,z)=0\), i.e. \[ E(K) = \{ P\in \mathbb{P}^2(K): F(P)=0 \} \]</p>
<p>This will consist of \(\{[x,y,1]: f(x,y)=F(x,y,1)=0\}\subset \mathbb{P}^2(K)\), and the point at infinity \([0,1,0]\). \([x,y,1]\) can be mapped back to \((x,y)\in \mathbb{A}^2(K)\), and \([0,1,0]\) is a special point we call the point at infinity \(\mathcal{O}\).</p>
<hr />
<h1 id="example-2">Example</h1>
<p>In sage, we can use the projective coordinates to represent a point in the elliptic curve.</p>
<div class="sage">
<script type="text/x-sage">
E = EllipticCurve([-1,1])
P = E([3,5,1])
Q = E([15,25,5])
print(f"[3,5,1]=[15,25,5]:",P==Q)
</script>
</div>
<hr />
<h1 id="non-singular-elliptic-curves">Non-Singular Elliptic Curves</h1>
<p>With the projective space description above, we can finally define a “differentiable” elliptic curve.</p>
<p>We say a curve \(C\) (defined by a single equation \(f(x_1,\cdots,x_n)=0\)) is singular at a point \(P\in C\) if \(\frac{\partial f}{\partial x_i}(P)=0\) for all \(i\in\{1,\cdots,n\}\). Otherwise the curve is non-singular at \(P\).</p>
<p>If the curve is non-singular at all points, it is called a <strong>non-singular</strong> curve. If the curve is singular at some points, it is called a <strong>singular</strong> curve.</p>
<blockquote class="notice--warning">
<p><strong>Remark</strong>:</p>
<p>Note that the partial derivatives of polynomials can always be defined as \(\frac{dx^n}{dx}= nx^{n-1}\). However, if the characteristic of the field is \(n\), then \(\frac{dx^n}{dx}=0\). In any case, for polynomial \(f\), we always have that \(\frac{\partial f}{\partial x_i}\) is a polynomial, so everything is good.</p>
</blockquote>
<p>Now we can prove the following result: An elliptic curve defined by the short Weierstrass equation \(y^2=x^3+Ax+B\) non-singular at all points if and only iff the discriminant is non-zero.</p>
<h2 id="proof-2">Proof</h2>
<p>First we will show that the point at infinity is never singular: The homogeneous equation of \(C\) is \(F(x,y,z)=y^2z-x^3-Axz^2-Bz^3=0\). We have that \(\frac{\partial F}{\partial z}=y^2-2zAx-3z^2B\), so \(\frac{\partial F}{\partial z}(0,1,0)=1\) which is not zero.</p>
<p>Now say \(P\in E\) is not the point at infinity. Then the curve is singular iff \(\frac{\partial f}{\partial x}(P)=\frac{\partial f}{\partial y}(P)=0\) for some point \(P\).</p>
<p>Let \((x_0,y_0)\) be a singular point. then taking partial derivatives leave us with \(2y_0 = 3x_0^2+A=0\), which gives \(y_0=0\) and \(A=-3x_0^2\). Substituting it back to the original curve we have \(0 = y_0^2 = x_0^3 + (-3x_0^2)x_0 + B\), so \(B=2x_0^3\). The discriminant gives \[ \Delta = 4(-3x_0^2)^3+27(2x_0^3)^2=27\cdot 4(x_0^6-x_0^6)=0. \]</p>
<p>If \(\Delta=0\), then the equation \(x^3+Ax+B\) has a double root \(x_0\). This happens when the derivative at \(x_0\) is zero. Now the derivative of \(x^3+Ax+B\) at \(x_0\) is \(3x_0^2+A\), which is the same as \(-\frac{\partial f}{\partial x}(x_0,0)\), so \(\frac{\partial f}{\partial x}(x_0,0)=0\).</p>
<p>Then let’s consider \((x_0,0)\), we have \(\frac{\partial f}{\partial y}(x_0,0)=0\), and \((x_0,0)\) is a point in the curve since \(f(x_0,0) = 0^2 - (x_0^3 + Ax_0 + B) = 0\) (since \(x_0\) is a root to the cubic polynomial). This shows that \(\Delta=0\) implies \((x_0,0)\) is a singular point, so \(E\) is singular.</p>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>A non well-defined function is actually NOT a function, since you have one input mapping to many outputs, so non well-defined function is a misnomer. To understand the intricacies, readers can refer to the definition of function using relations: we say relation \(R\) of \(X\times Y\) is a function if \(\forall x\in X, y_1,y_2\in Y, (x,y_1)\in R\) and \((x,y_2)\in R\), implies \(y_1=y_2\), so each \(x\) can only map to one value of \(y\). Further, \(\forall x\in X\), there exists some \(y\) such that \((x,y)\in R\) (so every \(x\) is mapped). Then we can use \(y=f(x)\) to denote \((x,y)\in R\). <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>Cousin Wu Ka Lokhello@klwu.coLast time we mentioned the basic concepts of Elliptic curves. However, there were some lingering questions about the weird representation of points in sage being \((x:y:1)\). In this post, we will answer this question by introducing the notion of projective space.Fundamental Concepts Underlying Elliptic Curves (Level 0): High-level Overview2021-12-12T00:00:00+08:002021-12-12T00:00:00+08:00https://klwu.co/maths-in-crypto/ec-basics-1<p>Elliptic Curve is one of the biggest topic in mathematics, for example number theory and geometry, and is used to, for example, proof the famous Fermat’s Last Theorem. It also spawns elliptic curve cryptography, which is widely-used. Understanding elliptic curve can be as simple as taking the rules for granted, but the underlying mathematics is quite complex. We shall have a look at the basic concepts of elliptic curve.
<!--exerpt--></p>
<script src="https://sagecell.sagemath.org/static/embedded_sagecell.js"></script>
<script>
sagecell.makeSagecell({
"inputLocation": ".sage",
"hide": ["fullScreen", "permalink", "sessionFiles"],
"editor": "codemirror-readonly"});
sagecell.makeSagecell({
"inputLocation": ".sage-1",
"hide": ["fullScreen", "permalink", "sessionFiles"],
"editor": "codemirror-readonly",
"linked": true});
sagecell.makeSagecell({
"inputLocation": ".sage-2",
"hide": ["fullScreen", "permalink", "sessionFiles"],
"editor": "codemirror-readonly",
"linked": true});
</script>
<p>Prerequisites: The definition of a group</p>
<p>2021-12-16: Added some more example code</p>
<h1 id="introduction">Introduction</h1>
<p>To start out, we can first state what is an elliptic curve on a easy-to-understand form: An elliptic curve over a field \(K\), denoted \(E(K)\), is the set of solutions to the equation \[y^2 + a_1xy+a_3y = x^3 + a_2x^2 + a_4x + a_6\]
Where \((x,y)\) are both in \(K\), along with a distinguished point at infinity, usually denoted \(\infty\) or \(\mathcal{O}\).</p>
<p>If the characteristic of \(K\) is not 2 or 3 (for example \(\mathbb{R},\mathbb{C}\), \(\mathbb{Z}_p\) with \(p\) prime and larger than 3 (note we shall use \(\mathbb{Z}_p\) to denote the field \(\mathbb{Z}/p\mathbb{Z}\))), then we can simplify the equation using some coordinate transformations to get a much simpler equation of the form\[ y^2=x^3+Ax+B \]
This is called the <strong>Weierstrass form</strong>. For simplicity we shall consider the Weierstrass form in the following posts.</p>
<p>If the coefficients of the elliptic curve is in a field \(L\), then we say that the curve is defined over \(L\). For example, the curve \(y^2=x^3-x+1\) is defined over \(\mathbb{Q}\). We can consider the solution of the elliptic curve where the coordinates are in \(\mathbb{Q}\), but also it’s extensions like \(\bar{\mathbb{Q}}\) (the field of algebraic integers), \(\mathbb{R}\) and \(\mathbb{C}\) (which is the algebraic closure of \(\mathbb{R}\)).<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">1</a></sup></p>
<p>We can graph elliptic curves defined over \(\mathbb{R}\) in \(\mathbb{R}^2\) and it can look like this:</p>
<p class="text-center"><img src="/assets/img/ecc/-1,1.png" alt="$$y^2=x^3-x+1$$" />\(y^2=x^3-x+1,\Delta=-368\)</p>
<p class="text-center"><img src="/assets/img/ecc/1,0.png" alt="$$y^2=x^3+x$$" />\(y^2=x^3+x=x(x^2+1),\Delta=-64\)</p>
<p class="text-center"><img src="/assets/img/ecc/-1,0.png" alt="$$y^2=x^3-x$$" />\(y^2=x^3-x=(x-1)x(x+1),\Delta=64\)</p>
<p class="text-center"><img src="/assets/img/ecc/0,0.png" alt="$$y^2=x^3-x+1$$" />\(y^2=x^3,\Delta=0\)</p>
<p class="text-center"><img src="/assets/img/ecc/y2=x3+x2.png" alt="$$y^2=x^3-x+1$$" />\(y^2=x^3+x^2=x^2(x+1),\Delta=0\) (Not in Weierstrass Form)</p>
<p>We also define the quantity \(\Delta\), called the discriminant, as \(\Delta=-16(4A^3+27B^2)\). If \(\Delta\) is non-zero, then we call the curve non-singular, and we will just study non-singular elliptic curves in this post.</p>
<p>We note that this discriminant exactly corresponds with the disriminant of the cubic equation \(x^3+Ax+B=0\), and discriminant being 0 means that the cubic equation only has repeated roots, making the curve “non-differentiable” at some point, so we do not worry about these curves. On the other hand, if the discrimint is positive, then the equation has 3 real roots; and if it is negative, then the equation has 1 real root.</p>
<p>We have the definition of elliptic curve, which is great. But how about the “group law” we are used to hearing for elliptic curve? Indeed if we just look at the curve, it is not apparent that a group law can be defined over an elliptic curve.</p>
<h1 id="group-law">Group Law</h1>
<p>Take an “differentiable” elliptic curve. Then we can define an “addition” operation of any two points on the curve as follows:</p>
<h2 id="1-infinity">1. Infinity</h2>
<p>If one of the points is the point at infinity, WLOG assume \(P=\mathcal{O}\), then \(P+Q=O+\mathcal{O}=Q\). Same for \(Q=\mathcal{O}\).</p>
<h2 id="2-pneq-q-both-not-infinity">2. \(P\neq Q\) both not infinity</h2>
<p>If we take two points \(P\) and \(Q\) on the curve (for \(P\neq Q\)) and both not infinity, we can draw a line with that two points. Then (just take for granted that) the intersection of that line and the elliptic curve will contain another point \(R\) on the curve (besides \(P\) and \(Q\) already). If it really does not exist (for vertical lines), we can still say that the third point of intersection is \(\mathcal{O}\).</p>
<p>For example if you are considering the point \((x,y)\) and \((x,-y)\), then the straight line will not intersect any other points on the curve, so we say \(\mathcal{O}\) is the third point of intersection.
<img src="/assets/img/ecc/2pt.png" alt="Vertical Line" /></p>
<p>As an example, we consider the curve \[y^2=x^3-x+1\]
Let \(P=(0,1)\) and \(Q=(3,5)\). Check to see that \(P\) and \(Q\) are on the curve. Then we can draw a line passing through \(P\) and \(Q\) to find a third point \(R\).
<img src="/assets/img/ecc/3pt.png" alt="Third point in the intersection" /></p>
<p>Then, if the coordinate of the third point is \((x,y)\), then \(P+Q\) is defined as the point \((x,\mathbf{-y})\). The geometric meaning is to draw a vertical line on the third point, and the other intersection is what we call \(P+Q\).
<img src="/assets/img/ecc/addition.png" alt="Addition" />
In this case, we can do the algebra (or just use the explicit formula) to find that the third point of intersection has coordinate \((-\frac{11}{9},-\frac{17}{27})\), so \(P+Q = (-\frac{11}{9},\frac{17}{27})\).</p>
<div class="sage">
<script type="text/x-sage">
x, y = var("x y")
# Equation of Elliptic Curve
elliptic_curve = y^2==x^3-x+1
# Equation of line
line = y==(5-1)/(3-0)*x+1
# Solve for the intersection
sols = solve([elliptic_curve, line], x, y, solution_dict=True)
# Remove (0,1) and (3,5) from intersection
sol = [s for s in sols if s[x] not in [0,3]][0]
# Reflect along x-axis
sol[y] = -sol[y]
print("(0,1) + (3,5) in the elliptic curve y^2=x^3-x+1 is:")
print(sol)
</script>
</div>
<h2 id="3-pqneqmathcalo">3. \(P=Q\neq\mathcal{O}\)</h2>
<p>What if \(P=Q\)? Then instead of drawing a line that passing through two points, we just draw the tangent of the curve at \(P\). Then the line will intersect the curve at another point again (actually it still has 3 intersections, just one repeated root). Then the procedure is the same as above.</p>
<h2 id="inverse-element">Inverse Element</h2>
<p>If we have \(P=(x,y)\), then note that since \((x,y) + (x,-y) = \mathcal{O}\), then the inverse of \(P\) is \((x,-y)\). We can just denote the inverse as \(-P\). Note that this means that if \(P\), \(Q\) and \(R\) are colinear, then \(P+Q+R=\mathcal{O}\).</p>
<h2 id="group-properties">Group Properties</h2>
<p>The operation we defined this way has several properties: For any points \(P,Q,R\in E\) (\(E\) is the elliptic curve),</p>
<ol>
<li>\(P+Q\in E\) (Closure)</li>
<li>\(P+\mathcal{O}=\mathcal{O}+P=P\) (identity)</li>
<li>\(P+Q=Q+P\) (commutivity)</li>
<li>\((P+Q)+R=P+(Q+R)\) (associativity)</li>
</ol>
<p>The first and second property is trivially true from our construction. The third one is true by the geometric intuition. The fourth property, however, is actually very difficult to prove. While you can use the explicit formula for addition to validate it, but it is very cumbersome. We will just take associativity for granted.</p>
<p>Then the operation we defined make \(E\) into a <strong>group</strong>!</p>
<h2 id="explicit-formula-for-point-addition">Explicit Formula for Point Addition</h2>
<p>The formula, or actually the algorithm for point addition is as follows: For \(P=(x_1,y_1)\) and \(Q=(x_2,y_2)\) both in the elliptic curve \(E\), if we want to calculate \(P+Q\),</p>
<ol>
<li>If \(P=\mathcal{O}\), then \(P+Q=Q\).</li>
<li>If \(Q=\mathcal{O}\), then \(P+Q=P\).</li>
<li>If \(x_1=x_2\) and \(y_1=-y_2\), then \(P+Q=\mathcal{O}\).</li>
<li>Define \(\lambda\) by \(\displaystyle\lambda=\begin{cases}
\frac{y_2-y_1}{x_2-x_1}\text{ If }P\neq Q\\
\frac{3x_1^2+A}{2y_1}\text{ If }P=Q
\end{cases}\)</li>
</ol>
<p>Now let \(x=\lambda^2-x_1-x_2, y=\lambda(x_1-x)-y_1\), we have \(P+Q=(x,y)\).</p>
<p>The only non-obvious part is (4). If \(P\neq Q\), then \(\lambda\) is the slope of the line that passes through \(P\) and \(Q\). If \(P=Q\), then \(\lambda\) is the slope of the tangent of the curve at \(P\), since by implicit differentiation (actually just the formal derivitive)</p>
<div class="text-center">\[\begin{align*}
y^2=&x^3+Ax+B\\
2y\frac{dy}{dx}=& 3x^2 + A\\
\frac{dy}{dx}=& \frac{3x^2+A}{2y}\\
\left.\frac{dy}{dx}\right\rvert_{P}=& \frac{3x_1^2+A}{2y_1}
\end{align*}\]
</div>
<p>Now the equation of the line will be \(y = \lambda x + \nu\), where \(\nu = y_1 - \lambda x_1\) is the y-intersect of the line. Now substitute the line equation to the elliptic curve equation to find intersection:
\(\begin{align*}
(\lambda x + \nu)^2 =& x^3+Ax+B\\
\lambda^2x^2 + 2\lambda\nu x + \nu^2 =& x^3+Ax+B\\
x^3 - \lambda^2 x^2 + (A-2\lambda\nu)x + (B-\nu^2) =& 0
\end{align*}\)</p>
<p>The is a cubic equation, but we do not need to actually solve it, since we already know two of the intersections of the line with the curve, namely \(P\) and \(Q\) by construction! So the above cubic equation have factor \((x-x_1)(x-x_2)\). So we get\[ x^3 - \lambda^2 x^2 + (A-2\lambda\nu)x + (B-\nu^2)=(x-x_1)(x-x_2)(x-x_3) \]
Where we use \(x_3\) to denote the unknown intersection. Now compare the \(x^2\) term on both sides: LHS gives \(\lambda^2\), and right hand side gives the negative of sum of \(x_i\)’s, so \(\lambda^2 = - x_1 - x_2 - x_3\). So \(x_3 = \lambda^2 - x_1 - x_2\).</p>
<p>To recover the \(y-\)coordinate, we just substitute the equation back to the line \(y=\lambda x+\nu\), and get \(y_3=\lambda x_3 + \nu\), then expand to get \(y_3 = \lambda x_3 + y_1 - \lambda x_1 = \lambda (x_3-x_1) + y_1\).</p>
<p>Then \(P+Q\) will be this point reflected along the x-axis, so we just get the negative of \(y_3\), and we get our desired \(P+Q=(x_3, \lambda(x_1-x_3)-y_1)\).</p>
<h2 id="remarks">Remarks</h2>
<p>We note that in the algorithm for point addition, we actually do not use the value of \(B\). This is curious, as if the point we are “adding” in the curve are actually not in the curve by using a different \(B\), the formula is still the same.</p>
<h1 id="example">Example</h1>
<p>We can define an elliptic curve over the rationals (in computers we use rational numbers instead of real numbers as we can’t represent real numbers accurately anyway). Let’s define the curve \(y^2 = x^3-x+1\) as \(E\), and take the points \((0,1)\) and \((3,5)\) on the curve. We can also see the discriminant of \(E\) is \(-368\).</p>
<div class="sage-1">
<script type="text/x-sage">
E = EllipticCurve([-1,1])
P = E([0,1])
Q = E([3,5])
print(E)
print("Discriminant of E:", E.discriminant())
print("P =", P)
print("Q =", Q)
</script>
</div>
<p>Note that \(P\) is represented as \((0:1:1)\) instead of just \((0,1)\), but we can ignore the third coordinate for now.</p>
<p>Let’s add \(P\) and \(Q\) to verify our above calculation.</p>
<div class="sage-1">
<script type="text/x-sage">
print("P + Q =", (P+Q).xy())
</script>
</div>
<h1 id="elliptic-curve-over-mathbbf_p">Elliptic Curve over \(\mathbb{F}_p\)</h1>
<p>So far we are concerned with elliptic curves over the reals. However, in cryptography, we often need to work with elliptic curves over a finite field \(\mathbb{F}_p\) for \(p\) prime. Recall that \(\mathbb{F}_p\) is just the set \(\{0,1,\cdots,p-1\}\) with addition and multiplication done mod \(p\).</p>
<p>We can, of course, define an elliptic curve over \(\mathbb{F}_p\) using the same definition as for the reals, and for the point addition, we use the same formulae, except the division becomes inverse. All the theories of addition and group structure applies.</p>
<p>In the case of \(\mathbb{F}_p\), the elliptic curve only consists of finitely many points, as \(\mathbb{F}_p^2\) only has \(p^2\) points anyway. Moreover, since each \(x\) only corresponds to 2 \(y\) (because any polynomial over degree \(d\) over a field has at most \(d\) solutions), the number of points are at most \(2p+1\).</p>
<p>We have a even better bound of the maximum number of points on the curve over \(\mathbb{F}_p\) (even with \(\mathbb{F}_{p^n}\) for any \(n\) positive integer), and this is called the <strong>Hasse’s Theorem</strong>: If \(\#E(\mathbb{F}_q)\) is the number of points on an elliptic curve over \(\mathbb{F}_q\) for \(q\) prime or power of primes, then \[ (q-1)-2\sqrt{q}\leq \#E(\mathbb{F}_q)\leq (q-1)+2\sqrt{q}\]</p>
<p>On the other hand, we may also want to know the structure of \(E(\mathbb{F}_p)\). Actually, it is either cyclic, or a product of two cyclic groups, but we will not discuss this here.</p>
<h1 id="scalar-multiplication">Scalar Multiplication</h1>
<p>It also makes sense to define the scalar multiple of a point \(P\) to just be adding the point \(P\) to itself a number of times. We use the notation \([n]P = \underbrace{P+\cdots+P}_{n\text{ times}}\) for positive \(n\), and \([n]P = \underbrace{-P-\cdots-P}_{\lvert n\rvert\text{ times}}\) for negative \(n\). We can also say that \([0]P=\mathcal{O}\).</p>
<p>Are there any fast ways to compute \([n]P\)? The naive way is to add \(P\) to itself \(n\) times. But just as the repeated squaring method in modular exponentiation, we can also do this similar approach, and we call this the <strong>double-and-add</strong> algorithm.</p>
<div style="border-style:solid;border-color:lightslategray;margin:20px">
<p>let bits be the bit representation of \(n\)<br />
let \(P\) be the point to be multiplied<br />
let R = \(\mathcal{O}\).<br />
for i from 0 to bits.length():<br />
\(R = [2]R\)<br />
if bits[i] == 1:<br />
\(R = R + P\)<br />
return \(R\)</p>
</div>
<h2 id="order">Order</h2>
<p>We may have that \([n]P=\mathcal{O}\) for some positive \(n\). The smallest positive \(n\) such that \([n]P=\mathcal{O}\) is called the <strong>order</strong> of \(P\). For elliptic curve over finite field, a point will have finite order. If there are no \(n\) such that \([n]P=\mathcal{O}\), then we say the point has infinite order.</p>
<p>If the order of the point is equal to the number of points \(N\) on the curve, this means that \([m]P\) can reach all the points in the curve as \(m\) goes from \(1\) to \(N\), then we call this \(P\) a <strong>generator</strong> of the elliptic curve. Note that we do not always have one generator, as \(E(\mathbb{F}_p)\) may not be cyclic as mentioned above.</p>
<p>For example, we can use the curve \(y^2=x^3-x+1\) again, and we note that the point \((3,5)\) over the rationals has infinite order. Actually, we can even see that only the point at infinity (represented as \((0:1:0)\)) has finite order (actually order is just 1).</p>
<div class="sage">
<script type="text/x-sage">
E = EllipticCurve([-1,1])
P = E([3,5])
print("Order of (3,5):",P.order())
print("Points with finite order:", E.torsion_points())
</script>
</div>
<p>However, over \(\mathbb{F}_7\), we can find that the point \((3,5)\) has order 3.</p>
<div class="sage-2">
<script type="text/x-sage">
Z7 = Zmod(7)
E = EllipticCurve(Z7, [-1,1])
P = E(3,5)
print("Order of (3,5):",P.order())
</script>
</div>
<p>We can also find that \(E(\mathbb{F}_7)\) has 12 points, is isomorphic to the group \(\mathbb{Z}/12\mathbb{Z}\) (which is cyclic) and that \((5,3)\) is the generator of \(E(\mathbb{F}_7)\) (i.e. has order 12).</p>
<div class="sage-2">
<script type="text/x-sage">
print("Number of points in E:",E.order())
print("Structure of E:",E.abelian_group().short_name())
print("Generator of E:",E.gens())
</script>
</div>
<p>Elliptic curves over rationals may still have points of finite order. For example, for the curve \(y^2=x^3+1\), there are 6 points of finite order. The point \((2,-3)\) has order 6. We can verify this by multiplying 6 to the point \((2,-3)\) to get the point at infinity.</p>
<div class="sage">
<script type="text/x-sage">
E = EllipticCurve([0,1]) # y^2 = x^3 + 1
print("Points with finite order:", E.torsion_points())
print("Order of (2, -3):", E(2,-3).order())
print("[6](2, -3) =",6*E(2,-3))
</script>
</div>
<h1 id="elliptic-curve-over-mathbbq">Elliptic Curve over \(\mathbb{Q}\)</h1>
<p>In fact, a much stronger result is true.</p>
<p class="notice--primary"><strong>Theorem</strong> (Mordell-Weil): For \(K\) a global field, \(E(K)\) is finitely generated.</p>
<p>Let’s specialise to the field of rational numbers \(\mathbb{Q}\), which is indeed a global field, this says that \(E(\mathbb{Q})\) consists of points of finite order (the torsion subgroup), denoted \(E(K)_{\text{tors}}\), and the infinite part will be isomorphic to \(\mathbb{Z}^r\) for some non-negative integer \(r\).</p>
<p>For elliptic curves over \(\mathbb{Q}\), the points of finite order \(P=(x,y)\) will have the following two conditions:</p>
<blockquote class="notice--primary">
<p><strong>Corollary</strong> (Lutz, Nagell):</p>
<ol>
<li>\(x\) and \(y\) are both integers.</li>
<li>Either \([2]P=\mathcal{O}\), or \(y^2\) divides \(4A^3+27B^2\).</li>
</ol>
</blockquote>
<p>We have a strong theorem about the torsion subgroup:</p>
<blockquote class="notice--primary">
<p><strong>Theorem</strong> (Mazur):</p>
<p>The structure of the torsion subgroup of elliptic curve over \(\mathbb{Q}\) can only be (isomorphic to) one of the following 15 groups:</p>
<ol>
<li>\(\mathbb{Z}/n\mathbb{Z}\) for \(n=1,2,\cdots,9,10,12\).</li>
<li>\(\mathbb{Z}/2\mathbb{Z}\times \mathbb{Z}/2n\mathbb{Z}\) for \(n=1,2,3,4\).</li>
</ol>
</blockquote>
<p>But we shall explore no further in this direction.</p>
<h2 id="conclusion">Conclusion</h2>
<p>This time we have explored the very basic concepts of the elliptic curve, and how a group operation is defined over it. However this still leave some question unanswered.</p>
<p>Actually, what is the point at infinity really doing? It magically makes the elliptic curve into a group, because indeed sometimes you really do not have the third point of intersection of the line and the curve. Is there any geometric meaning?</p>
<p>Why are the points represented as \((x:y:1)\) and infinity as \((0:1:0)\)?</p>
<p>We will answer this question in the next part.</p>
<h1 id="references">References</h1>
<p>Silverman, J. H. (2009). <em>The arithmetic of elliptic curves</em> (Vol. 106, pp. xx+-513). New York: Springer.</p>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:2" role="doc-endnote">
<p>The correct definition of elliptic curve considers a field \(K\), then define an elliptic curve over \(K\), and consider all the solutions of the curve where the coordinates are in \(\bar{K}\), the algebraic closure of \(K\) (the smallest field containing \(K\) such that it is algebraically closed: every polynomial with coefficients in that field has a root). However we shall not consider this as in the case of \(\mathbb{Z}/p\mathbb{Z}\), we only care about solutions in \(\mathbb{Z}/p\mathbb{Z}\). <a href="#fnref:2" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>Cousin Wu Ka Lokhello@klwu.coElliptic Curve is one of the biggest topic in mathematics, for example number theory and geometry, and is used to, for example, proof the famous Fermat’s Last Theorem. It also spawns elliptic curve cryptography, which is widely-used. Understanding elliptic curve can be as simple as taking the rules for granted, but the underlying mathematics is quite complex. We shall have a look at the basic concepts of elliptic curve.Coppersmith’s Method (Part II): Choosing the Right Lattice (1)2021-11-30T00:00:00+08:002021-11-30T00:00:00+08:00https://klwu.co/maths-in-crypto/lattice-2<p>This time we will be proving the Coppersmith’s theorem using the proof method of Howgrave-Graham. We will use lattices and the lattice basis reduction algorithm to do it, and this will hopefully provide you some guidances in making use of lattice basis reductions for solving different problems (e.g. CTF problems).
<!--exerpt--></p>
<p>Prerequisite: <a href="/maths-in-crypto/lattice-1/">Coppersmith’s Method (Part I): Introduction</a></p>
<p>In this post, you just need to remember that the first vector returned by the LLL algorithm will satisfy \[ |b_1| \leq 2^{\frac{n-1}{4}}(\det L)^{\frac{1}{n}}\]And that LLL algorithm runs in polynomial time of \(n\) (the dimension of lattice) and \(\log\max\limits_{b_i\in B}\|b_i\|\) (the log of length of largest vector in the input).</p>
<p>The proofs we are going to do today are constructive, meaning that we are creating an algorithm that are able to solve our problem. The conditions are there because the algorithm requires it, and so the logic may seem reversed (in fact it is kind of self-referential) if you do not have much mathematical maturity. Kind of like this:
<img src="/assets/img/surprise_tool.png" alt="Surprise tool meme" /></p>
<hr />
<h1 id="howgrave-grahams-formulation">Howgrave-Graham’s Formulation</h1>
<p><strong>Recall</strong>: We use the convention that \(F(x)=\sum\limits_{i=0}^d a_i x^i\) be polynomial with integer coefficients with degree \(d\) (so \(a_d\neq 0\)). Let \(x_0\) be a root of \(F(x)\equiv 0\pmod{N}\) such that \(\lvert x_0\rvert < X\) for some integer \(X\). For that \(X\), we associate the polynomial \(F\) with the vector \(b_F = (a_0, a_1X, a_2X^2, \cdots, a_d X^d)\), and we may use the two notations interchangeably. The norm of a vector \(v=(v_1,\cdots v_n)\) is denoted \(\vert\vert v\rvert\rvert = \sqrt{\sum\limits_{i=1}^n v_i^2}\).</p>
<p class="notice--primary"><strong>Theorem (Howgrave-Graham)</strong> If \(\lvert\lvert b_F\rvert\rvert < \frac{N}{\sqrt{d+1}}\), then \(F(x_0)=0\) over the integers (instead of mod \(N\) only).</p>
<p>The proof can be founded in the first part and is not difficult, but let’s also include it here for self-containedness.</p>
<blockquote class="notice--success">
<p><strong>Proof</strong> First we note that \(\sum\limits_{i=1}^n x_i \leq \sqrt{n \cdot \sum\limits_{i=1}^n x_i^2}\) by Cauchy-Schawarz inequality. Then</p>
\[\begin{align*}
\lvert F(x_0)\rvert =& \left\lvert \sum\limits_{i=0}^d a_i x_0^i\right\rvert\\
\leq& \sum\limits_{i=0}^d \lvert a_i\rvert\ \lvert x_0^i\rvert\\
< &\sum\limits_{i=0}^d \lvert a_i\rvert X^i\\
\leq &\sqrt{d+1} \lvert\lvert b_F\rvert\rvert\text{ (By the above lemma)}\\
\leq &\sqrt{d+1}\frac{N}{\sqrt{d+1}}\\
=& N
\end{align*}\]
<p>So \(-N < F(x_0) < N\). Since \(F(x) \equiv 0\pmod{N}\) this means \(F(x_0) = 0\) (without mod).</p>
</blockquote>
<h2 id="first-try">First Try</h2>
<p>Here we are using the lattice vectors to correspond to a polynomial. If all the basis vectors \(b_i\) (or equivalently polynomial) that span a lattice \(\mathcal{L}\) have a common root \(\alpha\), then any vectors in the lattice will also have the root \(\alpha\) (so \(b_i(\alpha)=0\) for all \(i\)), since \(\forall v\in L\), \[v(\alpha) = \sum_i c_i b_i(\alpha) = \sum_i c_i\cdot 0 = 0\] We will exploit this property in our derivation.</p>
<p>Let \(\displaystyle B=\left[\begin{array}{ccccc}
N & & & \\
&N X & & & \\
&& N X^{2} & & \\
& & & \ddots & \\
&&&&NX^{d-1}\\
a_{0} & a_{1} X & a_{1} X^{2} & \cdots & a_{d-1} X^{d-1} & X^{d}
\end{array}\right]_{(d+1)\times(d+1)}\)</p>
<p>Since this matrix is lower-triangular, the determinant of \(B\) is the product of diagonals: \(\det B = N^d X^{\frac{d(d+1)}{2}}\).</p>
<blockquote class="notice--primary">
<p><strong>Theorem</strong> Let \(G(x)\) be the polynomial corresponding to \(b_{1}\) after LLL on \(L(B)\) . Set \(C_{1}(d)=2^{-\frac{1}{2}}(d+1)^{-\frac{1}{2}}\). If
\[\displaystyle X<C_{1}(d) N^{\frac{2}{d(d+1)}},\]</p>
<p>any root \(x_{0}\) of \(F(x) \bmod N\) such that \(\|x\|<X\) satisfies \(G(x)=0\) in \(\mathbb{Z}\).</p>
</blockquote>
<blockquote class="notice--success">
<p>Proof: After performing LLL on \(B\), the shortest vector \(b_1\) (which will correspond to a polynomial \(G\)) will satisfy \(\quad\left\|b_{1}\right\| \leq 2^{\frac{n-1}{4}}(\det L)^{\frac{1}{n}}=2^{\frac{d}{4}} N^{\frac{d}{d+1}} X^{\frac{d}{2}}\) (as \(n=d+1\)).
To satisfy Howgrave-Graham, need</p>
<div class="text-center">\[\left\|b_{1}\right\|<\frac{N}{\sqrt{d+1}}\]
</div>
<p>If \(\quad\left\|b_{1}\right\| \leq 2^{\frac{d}{4}} N^{\frac{d}{d+1}} X^{\frac{d}{2}} < \frac{N}{\sqrt{d+1}}\), then the condition will be satisfied. Now</p>
<div class="text-center">\[\begin{align*}
2^{\frac{d}{4}} N^{\frac{d}{d+1}} X^{\frac{d}{2}} < &\frac{N}{\sqrt{d+1}}\\
2^{\frac d4} X^{\frac d2} \sqrt{d+1} <& N^{1-\frac{d}{d+1}}\\
\left(2^{\frac 12}(d+1)^{\frac 1d}\right)^{\frac{d}{2}} X^{\frac d2} <& N^{\frac{1}{d+1}}\\
C_1(d)^{-\frac d2} X^{\frac d2} <& N^{\frac{1}{d+1}}\\
X^{\frac d2} <&C_1(d)^{\frac d2}N^{\frac{1}{d+1}}\\
X < & C_1(d) N^{\frac{2}{d+1}}
\end{align*}\]
</div>
<p>So \(X < C_1(d) N^{\frac{2}{d+1}}\) will ensure the condition in Howgrave-Graham theorem will hold, then the statement in the theorem will hold.\qed</p>
</blockquote>
<p>The seems like a good method to recover small roots of polynomials mod \(N\)! However, the bound for \(X\) is very small: we have \(X\approx N^{\frac{1}{d^2}}\). For example, for \(N=10001\) and \(d=3\), the bound for \(X\) is only \(2.07\), which is not useful at all.</p>
<h2 id="increasing-x">Increasing \(X\)</h2>
<p>The remedy here is to note that by increasing the dimension \(n\) of \(B\), the resulting bounding for \(X\) will be higher.</p>
<h3 id="first-idea">First Idea</h3>
<p>One idea would be to use higher order polynomials: apart from using the polynomials \(g_i(x)=Nx^{i}\) and \(F(x)\) itself, we also use the <strong>x-shifts</strong> of \(F\), i.e.\[xF(x), x^2F(x),\cdots,x^kF(x)\]
Then the right hand side Howgrave-Graham bound will be \(\frac{N}{\sqrt{d+1+k}}\). This way the resulting bound is higher, making \(X\approx N^{\frac{1}{2d-1}}\) (note that the power of the modulus is unchanged)! For example, again for \(N=10001, d=3\), if we add 3 x-shifts of \(F\), the new bound will be about \(3.11\).</p>
<h3 id="second-idea">Second Idea</h3>
<p>The second idea is to increase the power of \(N\) on the right hand side by taking powers of \(F\). This means that instead of considering polynomials mod \(N\), we are considering mod \(N^h\) for some positive integers \(h\). Note that \(F(x)\equiv 0\pmod{N}\iff N^{h-k}F^k(x)\equiv 0\pmod{N^h}\) for any \(0\leq k\leq h\). So we can certainly work mod \(N^h\), have a bigger bound for the small roots, and the small roots found is also a root of the original polynomial mod \(N\).</p>
<p>By combining the two ideas, we will get a much higher bound. This is exactly the method used in Coppersmith’s Theorem.</p>
<hr />
<h1 id="coppersmiths-theorem">Coppersmith’s Theorem</h1>
<p class="notice--primary"><strong>Theorem (Coppersmith)</strong>
Let \(0<\varepsilon < \min\{0.18,\frac 1d\}\). Let \(F(x)\) be a monic irreducible polynomial of degree \(d\) with root(s) \(x_0\) modulo \(N\) such that \(\lvert x_0\rvert < \frac 1 2 N^{\frac 1 d - \varepsilon}\). Then such root(s) can be found in polynomial time of \(d\), \(\frac 1 \varepsilon\) and \(\log N\).</p>
<p>We note that even though we have a \(\varepsilon\) term, we can “improve” the bounded to \(N^{\frac 1d}\) by exhaustive search on the top few bits of \(x_0\).</p>
<h2 id="proof">Proof</h2>
<p>Let \(h\) be an integer (to be determined later) depending on \(d\) and \(\varepsilon\). We consider the lattice containing vectors corresponding to the polynomials\[G_{ij}(x)=N^{h-1-j}F(x)^jx^i\]where \(0 \leq i<d, 0\leq j< h\). As above, if \(x_0\) is a solution to \(F(x)\equiv 0\pmod{N}\), then \(G_{ij}(x_0)\equiv 0\pmod{N^h}\). We also notice that the degree of \(G_{ij}\) is \(dj+i\), so the degree runs through 0 to \(dh-1\) exactly once.</p>
<h3 id="copying-the-previous-method">Copying the Previous Method</h3>
<p>In this way, the polynomials corresponding to (row) vectors \(b_{G_{ij}}\), and the matrix formed by the vectors can be rearranging the rows to form a lower triangular matrix with diagonals being \(N^{h-1-j}X^{dj+i}\). The determinant of this matrix is therefore \(N^{\frac{dh(h-1)}{2}}X^{dh}\), where this lattice has dimension \(dh\).</p>
<p>Now as before, run LLL on the lattice, and the first vector \(b_1\) will satisfy \(\|b_1\|<2^{\frac{dh-1}{4}}\left(\det L\right)^{\frac{1}{dh}}=2^{\frac{dh-1}{4}}N^{\frac 12 (h-1)}X^{\frac{dh-1}{2}}\). This vector corresponds to a polynomial \(G(x)\) with \(G(x_0)\equiv 0\pmod{h}\). If \(\|b_1\|\leq \frac{N^h}{\sqrt{dh}}\), then by Howgrave-Graham \(G(x_0)=0\) over the integers. So our goal becomes</p>
\[\begin{align*}
2^{\frac{dh-1}{4}}N^{\frac 12 (h-1)}X^{\frac{dh-1}{2}} <& \frac{N^h}{\sqrt{dh}}\\
\sqrt{dh} 2^{\frac{dh-1}{4}}X^{\frac{dh-1}{2}} <& N^{\frac 12 (h-1)}\\
c(d,h)X<& N^{\frac{h-1}{dh-1}}
\end{align*}\]
<p>Where \(c(d,h)=\left(\sqrt{dh}2^{\frac{dh-1}{4}}\right)^{\frac{2}{dh-1}}=\sqrt{2}(dh)^{\frac{1}{dh-1}}\).</p>
<h3 id="getting-varepsilon">Getting \(\varepsilon\)</h3>
<p>Now the power of \(N\) is</p>
\[\begin{align*}
\frac{h-1}{dh-1} =& \frac{d(h-1)}{d(dh-1)}\\
=& \frac{dh-1+1-d}{d(dh-1)}\\
=&\frac 1d - \frac{d-1}{d(dh-1)}
\end{align*}\]
<p>If we set \(\varepsilon = \frac{d-1}{d(dh-1)}\), then \(dh = \frac{d-1}{d\varepsilon}+1\). So \(c(d,h)\) becomes \[\sqrt{2}(dh)^{\frac{1}{dh-1}} = \sqrt{2}\left(1+\frac{d-1}{d\varepsilon}\right)^{\frac{d\varepsilon}{d-1}}=\sqrt{2}(1+u)^{1/u}\]
By letting \(u=\frac{d\varepsilon}{d-1}\). Going back to the original inequality on \(X\), we have \(c(d,h)X< N^{\frac{h-1}{dh-1}}\), substituting the variable nets \[X < \frac{1}{c(d,h)}N^{\frac 1d - \epsilon}\]</p>
<p>Our original theorem statement requires \(X<\frac 12 N^{\frac 1d - \epsilon}\), so it seems that we will need \(c(d,h)\leq 2\). Luckily \(c(d,h)=\sqrt{2}(1+\frac 1u)^u\), and \((1+\frac 1u)^u \leq \sqrt{2}\) for \(0\leq u\leq 0.18\). \(u=\frac{d\varepsilon}{d-1}\) so \(\varepsilon \leq (1-\frac 1d)0.18\) so we just need to make sure that \(\varepsilon \leq 0.18\), which is the case in the assumption.</p>
<h3 id="setting-h">Setting \(h\)</h3>
<p>Since \(h = \frac{d-1}{d^2\varepsilon}+\frac 1d = \frac{1}{d\varepsilon} - \frac{1}{d^2\varepsilon} + \frac 1d \approx \frac{1}{d\varepsilon}\), setting \(h\) to be larger than \(\frac{1}{d\varepsilon}\) will make Howgrave-Graham work.</p>
<p>For the polynomial time part, we note that the dimension of \(L\) (the lattice to be reduced) has dimension \(dh\approx\frac 1\varepsilon\), and the coefficients of the polynomials are bounded by \(N^h\). QED.</p>
<p>Next time we will try to prove the recovery of \(N=pq\) given partial knowledge of \(p\).</p>
<h1 id="reference">Reference</h1>
<p>Galbraith, S. D. (2012). <em>Mathematics of public key cryptography</em>. Cambridge University Press.</p>
<hr />
<h1 id="appendix-to-be-determined-later">Appendix: To be determined later…?</h1>
<p>Note that \(h\) is “to be determined later”. Indeed it seems that we should have chosen \(h\) first, then to check if the condition of Howgrave-Graham is satisfied given the constraints of the theorem. However we are actually deriving the value of \(h\) that fits the condition. Then if we go hack and substitute the instances of (the unknown) \(h\) with the values we got at the end, we will be able to complete the proof.</p>
<h2 id="example-limit-of-functions-in-calculus">Example: Limit of Functions in Calculus</h2>
<p>Here we end this post with an example of these “to be determined later” that appears in mathematics. In calculus (or actually basic mathematical analysis), we say the limit of a function \(f(x)\) at a point \(a\) is \(L\) if for any \(\varepsilon>0\), there exists a \(\delta>0\) (that depends on \(\varepsilon\) only), such that for any \(x\) such that \(0<\lvert x-a\rvert < \delta\), we have that \[\lvert f(x)-L\rvert < \varepsilon.\]</p>
<p>The statement just means that, intuitively, that the value of \(f(x)\) can get arbitrarily close to the value \(L\), if \(x\) is also close enough to \(a\). This is like a challenge-response protocol, where we give a desired distance away from \(L\), and we should provide a response that consists of the range of \(x\) (near \(a\)) that can achieve the desired distance.</p>
<p>For instance, let’s try to prove that \[\lim\limits_{x\to 1}\frac 1x = 1\]
with the definition.</p>
<p>For any \(\varepsilon>0\), \(\left\lvert \frac 1x - 1 \right\rvert = \left\lvert \frac{x-1}{x} \right\rvert = \frac{\lvert x-1 \rvert}{\lvert x \rvert}\).</p>
<p>If \(\lvert x-1\rvert < \frac 12\), then \(\frac 12 < x < \frac 32\). In particular \(x > \frac 1 2\), so with that assumption, we can have \(\frac{\lvert x-1 \rvert}{\lvert x \rvert} < 2\lvert x-1 \rvert\).</p>
<p>If further \(\lvert x-1 \rvert < \frac \varepsilon 2\), then \(2\lvert x-1 \rvert < \varepsilon\).</p>
<p>Now when we look back at the derivations, we find that \(\delta\) should be less than both \(\frac 12\) and \(\frac\varepsilon 2\), so that the requirement of \(\left\lvert \frac 1x - 1 \right\rvert < \varepsilon\) is satisfied. Let’s see how we will actually present the proof:</p>
<blockquote class="notice--success">
<p><strong>Proof</strong>: For any \(\varepsilon>0\), set \(\delta = \min\{\frac 12, \frac\varepsilon 2\}\). Then for any \(x\) such that \(0 < \lvert x-1\rvert < \delta\),</p>
\[\begin{align*}
\left\lvert \frac 1x - 1 \right\rvert =& \left\lvert \frac{x-1}{x} \right\rvert\\
=& \frac{\lvert x-1 \rvert}{\lvert x \rvert}\\
<& 2\lvert x-1 \rvert\\
<& \varepsilon
\end{align*}\]
<p>So \(\lim\limits_{x\to 1}\frac 1x = 1\) by definition.</p>
</blockquote>
<p>See how the logic is kind of reversed? This is indeed because of the fact that we have already derived the whole thing in order to figure out the required value of \(\delta\) to satisfy the condition.</p>Cousin Wu Ka Lokhello@klwu.coThis time we will be proving the Coppersmith’s theorem using the proof method of Howgrave-Graham. We will use lattices and the lattice basis reduction algorithm to do it, and this will hopefully provide you some guidances in making use of lattice basis reductions for solving different problems (e.g. CTF problems).Zetta-CTF: 🥥2021-11-20T00:00:00+08:002021-11-20T00:00:00+08:00https://klwu.co/writeup/zetta-coconut<p>The following challenge is from the Zetta-CTF <em>PHP: Horrific Puzzle</em>, during the <a href="https://www.vxcon.hk">VXCON</a> in Hong Kong, 27 Apr - 28 Apr 2019. The challenge is named 🥥. btw no one plays.
<!--exerpt--></p>
<p>I wrote this write-up back in 28 Apr 2019 after the CTF ends. At the time I was still very new to CTF (now also very new), so the formatting and the quality of this write-up is not so good. This is just for my archive purposes.</p>
<h2 id="challenge">Challenge</h2>
<p>The code reads</p>
<div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp"><?=</span><span class="o">@</span><span class="nb">preg_match</span><span class="p">(</span><span class="s1">'/^[\(\)\*\-\.\[\]\^]+$/'</span><span class="p">,(</span><span class="nv">$_</span><span class="o">=</span><span class="nv">$_GET</span><span class="p">[</span><span class="s2">"🥥"</span><span class="p">]))</span><span class="o">&&!</span><span class="p">(</span><span class="nb">strlen</span><span class="p">(</span><span class="nv">$_</span><span class="p">)</span><span class="o">>></span><span class="mi">10</span><span class="p">)</span><span class="o">?@</span><span class="k">eval</span><span class="p">(</span><span class="s2">"set_error_handler(function()</span><span class="si">{</span><span class="nv">exit();</span><span class="si">}</span><span class="s2">);error_get_last()&&exit();return </span><span class="nv">$_</span><span class="s2">;"</span><span class="p">)</span><span class="o">:!</span><span class="nb">highlight_file</span><span class="p">(</span><span class="k">__FILE__</span><span class="p">);</span>
</code></pre></div></div>
<p>In pseudocode, we can write this as</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>if input contains only ()*-.[]^ and length of input < 1024:
(with exit on error) eval input and print the return value
else:
show the source code
</code></pre></div></div>
<p>Our goal is to execute <code class="language-plaintext highlighter-rouge">/checksec.sh</code> (the common goal of the whole CTF).</p>
<p>In short, we can run wherever we want, but the command need to contain only <code class="language-plaintext highlighter-rouge">()*-.[]^</code>, and the command needs to be less than 1024 characters. Also, any error/warning/notice will cause the program to exit.</p>
<h2 id="is-this-jsfuck">Is this JSFuck?</h2>
<p>The idea of restricting the allowed characters is not new: perhaps the most famous one is JSFuck, which can turn any javascript code into valid js codes, but using <code class="language-plaintext highlighter-rouge">()[]!+</code>. Just looking from it, it seems that we are even better off than JSFuck, since we have more characters. But actually no. The reason is that we don’t have <code class="language-plaintext highlighter-rouge">+</code>, instead we get this awkward charset of <code class="language-plaintext highlighter-rouge">*-.^</code>. Even the simplest number <code class="language-plaintext highlighter-rouge">3</code> can only be constructed by <code class="language-plaintext highlighter-rouge">10-1-1-1-1-1-1-1</code> instead of the obvious <code class="language-plaintext highlighter-rouge">1+1</code>.</p>
<h2 id="constructing-the-easy-characters">Constructing the Easy Characters</h2>
<p>With inspirations from JSFuck, trial and error, and the great behavior of php , we can begin to assemble some simple characters:</p>
<p>0 can be expressed by <code class="language-plaintext highlighter-rouge">[]^[]</code>, and
1 can be expressed by <code class="language-plaintext highlighter-rouge">[]^[[]]</code>.</p>
<p>With the help of <code class="language-plaintext highlighter-rouge">.</code>, which can act as the concatenation operator for strings, we can express 10 by <code class="language-plaintext highlighter-rouge">[]^[].[]^[[]]</code>. Then we can get the other numbers by repeatedly subtract 1 from 10 multiple times.</p>
<p>So for example, 6 is \(10-1-1-1-1\) or equivalently, \(7-1\) , or <code class="language-plaintext highlighter-rouge">([]^[].[]^[[]])-[]^[[]]-[]^[[]]-[]^[[]]-[]^[[]]</code>. So we can construct all the numbers we want.</p>
<p>Now we want some English letters. Luckily we have floating point numbers, which has <code class="language-plaintext highlighter-rouge">INF</code> and scientific notations like <code class="language-plaintext highlighter-rouge">1.234E+45</code>. So we now we can use I,N,F,.,E,+ as well. Of course we cannot do something like <code class="language-plaintext highlighter-rouge">(INF)[0]</code> since INF is a number (a float), but we can concatenate a 0 at the back to make it a string, i.e. do <code class="language-plaintext highlighter-rouge">(INF . 0)[0]</code>, which is equivalent to <code class="language-plaintext highlighter-rouge">'INF0'[0]</code>. At the same time, observe that we can use <code class="language-plaintext highlighter-rouge">-</code> just by using <code class="language-plaintext highlighter-rouge">(-1 . 0)[0]</code>.</p>
<h2 id="creating-the-payload">Creating the Payload</h2>
<p>Now, by using what we have, we can <strong>try</strong> to construct our payload. We should do something like <code class="language-plaintext highlighter-rouge">exec('/checksec.sh')</code>. Although we can only input string, not identifiers, doing <code class="language-plaintext highlighter-rouge">'exec'('/checksec.sh')</code> will still result in a function call in php. Also, we can try to reduce the character we use by using filename substitution in bash, i.e. using <code class="language-plaintext highlighter-rouge">*</code> to match our file. So now we can reduce the payload to <code class="language-plaintext highlighter-rouge">'exec'('/*.*')</code>.</p>
<p>Now we need to construct the characters that we need: <code class="language-plaintext highlighter-rouge">exc/*.</code>. php functions are case insensitive so “EXC” will also work. It left us to construct “xc/*”. The main technique for doing so is to do xor:</p>
<p>\[x=y \wedge z \]</p>
<p>, where x is the character we want, and y, z are the characters we have. Notice that</p>
<p>\[x = y \wedge z \iff x \wedge y = z\]</p>
<p>So we can try to xor the characters we want with the characters we have, and hope that we get some characters that we have by some trial-and-error. For example: \(c \wedge N = -\), so \(c = N \wedge -\), and we have both ‘N’ and ‘-‘ already.</p>
<p>Another way is to get the characters we want by xor-ing neighboring characters by a small ‘number’, e.g.:</p>
<p>\[* = \cdot \wedge 0\times 04 = \cdot \wedge ( 4 \wedge 0)\]</p>
<p>Here we need the string <code class="language-plaintext highlighter-rouge">'1'</code> and <code class="language-plaintext highlighter-rouge">'5'</code>, so we need to do <code class="language-plaintext highlighter-rouge">'.' xor ((4 . 0) xor (0 . 0))</code>.</p>
<h2 id="php-behavior-lol">PHP behavior LOL</h2>
<p>By now we are able to construct all the characters needed for our payload. But if we check the length of our payload, it will be something like 12xx characters (originally with all lower case characters, the payload is 22xx characters long) ! We need to reduce the character count.</p>
<p>Now we turn to the hint by the author:</p>
<ul>
<li>There is a shorter payload than <code class="language-plaintext highlighter-rouge">/*.*</code></li>
<li>‘ABC’ <strong>xor</strong> ‘bc’ = ???</li>
<li>’/*’ can be constructed together in a weird way: <strong>xor</strong> is bitwise and it commutes</li>
</ul>
<p>For the first hint, notice that we can reduce the payload to just <code class="language-plaintext highlighter-rouge">/*h</code> and it will still expand to <code class="language-plaintext highlighter-rouge">/checksec.sh</code> correctly.</p>
<p>For the second hint, observe that in php, a longer string xor a shorter string gives us a shorter string. In fact, we made use of this fact in <code class="language-plaintext highlighter-rouge">'*' = '.' xor ((1 . 0) ^ (5 ^ 0))</code>.</p>
<p>The third hint (along with the second one) is the one that helped to drop the length to below 1024. First we know that we can construct <code class="language-plaintext highlighter-rouge">/</code> and <code class="language-plaintext highlighter-rouge">*</code> by <code class="language-plaintext highlighter-rouge">'/' = '-' ^ "\x02"</code> and <code class="language-plaintext highlighter-rouge">'*' = '.' ^ "\x04"</code>. A naive attempt is to combine to yield</p>
<p>\[/* = -. \wedge 0\times0204 = -. \wedge 24 \wedge 00\]</p>
<p>, but notice that the number of length of characters required to construct this is the same! Here, a big observation is that we can have</p>
<p>\[/* = -. \wedge 24 \wedge 00 = -4 \wedge 2. \wedge 00\]</p>
<p>Since we can use a long string for the xor, provided that we have a ‘00’ to restrict the length of the final output of the xor to 2, we can get 2. by finding a floating point number <code class="language-plaintext highlighter-rouge">2.???????E+???</code>, so we have</p>
<p>\[/* = -4 \wedge 2.\text{????}\textbf{E+}\text{????} \wedge 00\]</p>
<p>And constructing -4 and a floating point number starting with 2. take much less space than the individual numbers 2,4 and the character <code class="language-plaintext highlighter-rouge">-</code> and <code class="language-plaintext highlighter-rouge">.</code>. The final payload has length 928. On a high level, it is <code class="language-plaintext highlighter-rouge">'ExEc'('/*h')</code>. In full, the payload are as follows (for those who are too lazy to run the python script themselves):</p>
<details>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>((((([]^[[]]).([]^[]))**(([]^[[]]).([]^[]).([]^[])).([]^[]))[([]^[[]]).([]^[])-([]^[[]])-([]^[[]])-([]^[[]])-([]^[[]])-([]^[[]])-([]^[[]])-([]^[[]])]).((((([]^[[]]).([]^[])-([]^[[]]))**(([]^[[]]).([]^[]).([]^[]).([]^[])).([]^[]))[([]^[])])^(([]^[[]]).([]^[]))).(((([]^[[]]).([]^[]))**(([]^[[]]).([]^[]).([]^[])).([]^[]))[([]^[[]]).([]^[])-([]^[[]])-([]^[[]])-([]^[[]])-([]^[[]])-([]^[[]])-([]^[[]])-([]^[[]])]).((((([]^[[]]).([]^[])-([]^[[]]))**(([]^[[]]).([]^[]).([]^[]).([]^[])).([]^[]))[([]^[[]])])^(((-([]^[[]])).([]^[]))[([]^[])])))((((-(([]^[[]]).([]^[])-([]^[[]])-([]^[[]])-([]^[[]])-([]^[[]])-([]^[[]])-([]^[[]]))).([]^[]))^((((([]^[[]]).([]^[])-([]^[[]])))**(([]^[[]]).([]^[]).([]^[]))).([]^[]))^(([]^[]).([]^[]))).((((([]^[[]]).([]^[]))**(([]^[[]]).([]^[]).([]^[])).([]^[]))[([]^[[]])])^(((([]^[[]]).([]^[])-([]^[[]]))**(([]^[[]]).([]^[]).([]^[]).([]^[])).([]^[]))[([]^[[]]).([]^[[]])^(([]^[[]]).([]^[])-([]^[[]]))])))
</code></pre></div> </div>
</details>
<p>which do give us the flag as desired. You can find the code that generates the payload below:</p>
<details>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">zero</span> <span class="o">=</span> <span class="s">"([]^[])"</span>
<span class="n">one</span> <span class="o">=</span> <span class="s">"([]^[[]])"</span>
<span class="n">ten</span> <span class="o">=</span> <span class="sa">f</span><span class="s">"</span><span class="si">{</span><span class="n">one</span><span class="si">}</span><span class="s">.</span><span class="si">{</span><span class="n">zero</span><span class="si">}</span><span class="s">"</span>
<span class="n">nine</span> <span class="o">=</span> <span class="sa">f</span><span class="s">"</span><span class="si">{</span><span class="n">ten</span><span class="si">}</span><span class="s">-</span><span class="si">{</span><span class="n">one</span><span class="si">}</span><span class="s">"</span>
<span class="n">eight</span> <span class="o">=</span> <span class="sa">f</span><span class="s">"</span><span class="si">{</span><span class="n">nine</span><span class="si">}</span><span class="s">-</span><span class="si">{</span><span class="n">one</span><span class="si">}</span><span class="s">"</span>
<span class="n">seven</span> <span class="o">=</span> <span class="sa">f</span><span class="s">"</span><span class="si">{</span><span class="n">eight</span><span class="si">}</span><span class="s">-</span><span class="si">{</span><span class="n">one</span><span class="si">}</span><span class="s">"</span>
<span class="n">six</span> <span class="o">=</span> <span class="sa">f</span><span class="s">"</span><span class="si">{</span><span class="n">seven</span><span class="si">}</span><span class="s">-</span><span class="si">{</span><span class="n">one</span><span class="si">}</span><span class="s">"</span>
<span class="n">five</span> <span class="o">=</span> <span class="sa">f</span><span class="s">"</span><span class="si">{</span><span class="n">six</span><span class="si">}</span><span class="s">-</span><span class="si">{</span><span class="n">one</span><span class="si">}</span><span class="s">"</span>
<span class="n">four</span> <span class="o">=</span> <span class="sa">f</span><span class="s">"</span><span class="si">{</span><span class="n">five</span><span class="si">}</span><span class="s">-</span><span class="si">{</span><span class="n">one</span><span class="si">}</span><span class="s">"</span>
<span class="n">three</span> <span class="o">=</span> <span class="sa">f</span><span class="s">"</span><span class="si">{</span><span class="n">four</span><span class="si">}</span><span class="s">-</span><span class="si">{</span><span class="n">one</span><span class="si">}</span><span class="s">"</span>
<span class="n">two</span> <span class="o">=</span> <span class="sa">f</span><span class="s">"</span><span class="si">{</span><span class="n">one</span><span class="si">}</span><span class="s">.</span><span class="si">{</span><span class="n">one</span><span class="si">}</span><span class="s">^(</span><span class="si">{</span><span class="n">nine</span><span class="si">}</span><span class="s">)"</span> <span class="c1"># A small optimization over 3 - 1
</span>
<span class="n">INF</span> <span class="o">=</span> <span class="sa">f</span><span class="s">"(</span><span class="si">{</span><span class="n">nine</span><span class="si">}</span><span class="s">)**(</span><span class="si">{</span><span class="n">one</span><span class="si">}</span><span class="s">.</span><span class="si">{</span><span class="n">zero</span><span class="si">}</span><span class="s">.</span><span class="si">{</span><span class="n">zero</span><span class="si">}</span><span class="s">.</span><span class="si">{</span><span class="n">zero</span><span class="si">}</span><span class="s">)"</span>
<span class="n">I</span> <span class="o">=</span> <span class="sa">f</span><span class="s">"(</span><span class="si">{</span><span class="n">INF</span><span class="si">}</span><span class="s">.</span><span class="si">{</span><span class="n">zero</span><span class="si">}</span><span class="s">)[</span><span class="si">{</span><span class="n">zero</span><span class="si">}</span><span class="s">]"</span> <span class="c1"># used for x
</span><span class="n">N</span> <span class="o">=</span> <span class="sa">f</span><span class="s">"(</span><span class="si">{</span><span class="n">INF</span><span class="si">}</span><span class="s">.</span><span class="si">{</span><span class="n">zero</span><span class="si">}</span><span class="s">)[</span><span class="si">{</span><span class="n">one</span><span class="si">}</span><span class="s">]"</span> <span class="c1"># used for e,c
</span><span class="n">F</span> <span class="o">=</span> <span class="sa">f</span><span class="s">"(</span><span class="si">{</span><span class="n">INF</span><span class="si">}</span><span class="s">.</span><span class="si">{</span><span class="n">zero</span><span class="si">}</span><span class="s">)[</span><span class="si">{</span><span class="n">two</span><span class="si">}</span><span class="s">]"</span>
<span class="n">sf</span> <span class="o">=</span> <span class="sa">f</span><span class="s">"(</span><span class="si">{</span><span class="n">one</span><span class="si">}</span><span class="s">.</span><span class="si">{</span><span class="n">zero</span><span class="si">}</span><span class="s">)**(</span><span class="si">{</span><span class="n">one</span><span class="si">}</span><span class="s">.</span><span class="si">{</span><span class="n">zero</span><span class="si">}</span><span class="s">.</span><span class="si">{</span><span class="n">zero</span><span class="si">}</span><span class="s">).</span><span class="si">{</span><span class="n">zero</span><span class="si">}</span><span class="s">"</span> <span class="c1"># 1.0E+100
</span><span class="n">dot</span> <span class="o">=</span> <span class="sa">f</span><span class="s">"(</span><span class="si">{</span><span class="n">sf</span><span class="si">}</span><span class="s">)[</span><span class="si">{</span><span class="n">one</span><span class="si">}</span><span class="s">]"</span>
<span class="n">E</span> <span class="o">=</span> <span class="sa">f</span><span class="s">"(</span><span class="si">{</span><span class="n">sf</span><span class="si">}</span><span class="s">)[</span><span class="si">{</span><span class="n">three</span><span class="si">}</span><span class="s">]"</span>
<span class="n">plus</span> <span class="o">=</span> <span class="sa">f</span><span class="s">"(</span><span class="si">{</span><span class="n">sf</span><span class="si">}</span><span class="s">)[(</span><span class="si">{</span><span class="n">four</span><span class="si">}</span><span class="s">)]"</span>
<span class="n">minus</span> <span class="o">=</span> <span class="sa">f</span><span class="s">"((-</span><span class="si">{</span><span class="n">one</span><span class="si">}</span><span class="s">).</span><span class="si">{</span><span class="n">zero</span><span class="si">}</span><span class="s">)[</span><span class="si">{</span><span class="n">zero</span><span class="si">}</span><span class="s">]"</span>
<span class="n">sf2</span> <span class="o">=</span> <span class="sa">f</span><span class="s">"((</span><span class="si">{</span><span class="n">nine</span><span class="si">}</span><span class="s">))**(</span><span class="si">{</span><span class="n">one</span><span class="si">}</span><span class="s">.</span><span class="si">{</span><span class="n">zero</span><span class="si">}</span><span class="s">.</span><span class="si">{</span><span class="n">zero</span><span class="si">}</span><span class="s">)"</span> <span class="c1"># 2.<whatever>
</span>
<span class="n">slashstar</span> <span class="o">=</span> <span class="sa">f</span><span class="s">"((-(</span><span class="si">{</span><span class="n">four</span><span class="si">}</span><span class="s">)).</span><span class="si">{</span><span class="n">zero</span><span class="si">}</span><span class="s">)^((</span><span class="si">{</span><span class="n">sf2</span><span class="si">}</span><span class="s">).</span><span class="si">{</span><span class="n">zero</span><span class="si">}</span><span class="s">)^(</span><span class="si">{</span><span class="n">zero</span><span class="si">}</span><span class="s">.</span><span class="si">{</span><span class="n">zero</span><span class="si">}</span><span class="s">)"</span>
<span class="n">c</span> <span class="o">=</span> <span class="sa">f</span><span class="s">"(</span><span class="si">{</span><span class="n">N</span><span class="si">}</span><span class="s">)^(</span><span class="si">{</span><span class="n">minus</span><span class="si">}</span><span class="s">)"</span>
<span class="n">x</span> <span class="o">=</span> <span class="sa">f</span><span class="s">"(</span><span class="si">{</span><span class="n">I</span><span class="si">}</span><span class="s">)^(</span><span class="si">{</span><span class="n">one</span><span class="si">}</span><span class="s">.</span><span class="si">{</span><span class="n">zero</span><span class="si">}</span><span class="s">)"</span>
<span class="n">h</span> <span class="o">=</span> <span class="sa">f</span><span class="s">"(</span><span class="si">{</span><span class="n">dot</span><span class="si">}</span><span class="s">)^(</span><span class="si">{</span><span class="n">F</span><span class="si">}</span><span class="s">)"</span>
<span class="n">payload</span> <span class="o">=</span> <span class="sa">f</span><span class="s">"((</span><span class="si">{</span><span class="n">E</span><span class="si">}</span><span class="s">).(</span><span class="si">{</span><span class="n">x</span><span class="si">}</span><span class="s">).(</span><span class="si">{</span><span class="n">E</span><span class="si">}</span><span class="s">).(</span><span class="si">{</span><span class="n">c</span><span class="si">}</span><span class="s">))((</span><span class="si">{</span><span class="n">slashstar</span><span class="si">}</span><span class="s">).(</span><span class="si">{</span><span class="n">h</span><span class="si">}</span><span class="s">))"</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"total: </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">payload</span><span class="p">)</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">payload</span><span class="p">)</span>
</code></pre></div> </div>
</details>Cousin Wu Ka Lokhello@klwu.coThe following challenge is from the Zetta-CTF PHP: Horrific Puzzle, during the VXCON in Hong Kong, 27 Apr - 28 Apr 2019. The challenge is named 🥥. btw no one plays.Coppersmith’s Method (Part I): Introduction2021-11-19T00:00:00+08:002021-11-19T00:00:00+08:00https://klwu.co/maths-in-crypto/lattice-1<p>The Coppersmith’s method is an application of lattice basis reduction algorithms (like LLL) to find small solutions to polynomials modulo \(N\). The application of this method ranges from several attacks on RSA, to solving the hidden number problem (for Diffie-Hellman key exchange or (EC)DSA).
<!--exerpt--></p>
<p>You can find (sage) code that implements the below-mentioned attack on <a href="https://github.com/mimoo/RSA-and-LLL-attacks">https://github.com/mimoo/RSA-and-LLL-attacks</a>, although they used a weaker (read: more flexible) version of the statements.</p>
<p>Prerequisite: <a href="/maths-in-crypto/lattice-based-cryptography-basics/">Lattice-based Cryptography basics</a></p>
<h1 id="introduction-polynomial-mod-n">Introduction: Polynomial mod \(N\)</h1>
<p>Let’s say we have a polynomial \(f(x) = \sum_{i=0}^n a_i x^i\) where \(a_n\neq 0\). Then \(n\) is called the degree of \(f\). We can restrict the domain of \(x\) and the coefficient<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup> to integers mod \(n\), so we can talk about the roots of a polynomial \(f(x)\equiv 0\pmod{n}\).</p>
<ol>
<li>Without the modulo \(n\), we know that no general solutions with elementary operations exists for quintic polynomial or above. However, for integer solutions, we can always use Newton’s method to approximate a root then round off.</li>
<li>For \(n\) prime, the problem of finding roots of \(f(x)\pmod{n}\) is well-known and we have some <a href="https://en.wikipedia.org/wiki/Cantor%E2%80%93Zassenhaus_algorithm">fast algorithms</a> to recover all the roots.</li>
<li>For \(n=p^n\), we can first find the roots of \(f(x)\equiv 0\pmod{p}\), then lift the solutions up to mod \(p^n\) by Hensel’s Lifting Lemma.</li>
<li>For general composites \(n=\prod p_i^{n_i}\), we can use Chinese Remainder Theorem to combine all the roots mod the individual \(p_i^{n_i}\) to get the desired root.</li>
</ol>
<p>But how about if we do not know the factorization? The answer is that in general it is hard to solve polynomials if we do not have the factorization. To see why, we note that</p>
<div class="text-center">\[x^2\equiv 1\pmod{pq}\]
</div>
<p>has 4 solutions (\(p\) and \(q\) are primes). If \(a\) is a solution to \(f(x)=x^2-1\) mod \(pq\), then \((a-1)(a+1)\equiv 0\pmod{pq}\), so \(a-1\) is divisible by either \(p\) or \(q\) (but not both), then we can factor \(pq\). This shows that solving polynomials mod \(N\) with unknown factorization <em>in general</em> is as hard as factorizing large integers, which we also do not have a fast algorithm for. However, as always, if we can relax our conditions, we may be able to find some solutions.</p>
<p>In the coppersmith method case, we are restricted to finding small roots to a polynomial.</p>
<blockquote class="notice--warning">
<p><strong>Exercise</strong></p>
<ol>
<li>Show that if \(p\) is a prime, then \(x^2-1\equiv 0\pmod{p}\) has exactly 2 solutions (name \(1\) and \(-1\)).</li>
<li>Use the above to proof that if \(n=pq\) is a product of 2 primes, then \(x^2-1\equiv 0\pmod{n}\) has 4 solutions.</li>
</ol>
</blockquote>
<hr />
<h1 id="coppersmiths-theorem">Coppersmith’s Theorem</h1>
<p>Most specifically, let \(F(x)\) have integer coefficients be <em>monic</em> and <em>irreducible</em>. And we want to examine the equation</p>
<div class="text-center">\[F(x)\equiv 0\pmod{N}\]
</div>
<p class="notice--primary"><strong>Definition (Monic)</strong> A monic polynomial has the leading coefficient (the coefficient corresponding to the largest term) to be 1.</p>
<p>Actually if the leading coefficient is not 1, we can just multiply the whole function by the inverse of that coefficient, and the polynomial will have the same root (if the gcd of it and \(N\) is 1). If the gcd is not 1 then we just found a factor of \(N\).</p>
<p class="notice--primary"><strong>Definition (Irreducible)</strong> A polynomial \(F(x)\) (with integer coefficients) is irreducible if whenever \(F(x)=g(x)h(x)\), one of \(g(x)\) or \(h(x)\) must be the constant polynomial.</p>
<p>This looks like the usual prime number definition, because in the case of integers, prime and irreducible are the same. Here we make the distinction.</p>
<p>Then we can formulate the main result of Coppersmith.</p>
<p class="notice--primary"><strong>Theorem (Coppersmith)</strong>
Let \(0<\varepsilon < \min\{0.18,\frac 1d\}\). Let \(F(x)\) be a monic irreducible polynomial of degree \(d\) with root(s) \(x_0\) modulo \(N\) such that \(\lvert x_0\rvert < \frac 1 2 N^{\frac 1 d - \varepsilon}\). Then such root(s) can be found in polynomial time of \(d\), \(\frac 1 \varepsilon\) and \(\log N\).</p>
<hr />
<h1 id="application">Application</h1>
<h2 id="rsa-stereotyped-message">RSA: Stereotyped Message</h2>
<p>Let’s use the RSA setup (\(n=pq\), \(de\equiv 1\pmod{\phi(n)}\), \(E_k(m)=m^e\pmod{N}\), and let the public exponent \(e=3\). Say we have a small message \(m\) to
encrypt, and \(m < N^{\frac{1}{3}}\).</p>
<p>If we use textbook RSA, the plaintext can be directly recovered by taking cube root. Suppose we pad the message with ‘1’ bits on the left until the padded message has about the same size as \(N\). Then we have the equation</p>
<div class="text-center">\[(P+m)^3\equiv c\pmod{N}\]
</div>
<p>where we use \(P\) to denote the padding, so</p>
<div class="text-center">\[P={\underbrace{111\cdots 1}_{\lfloor\log_2 N\rfloor - k} \underbrace{00\cdots 0_2}_{k}}\]
</div>
<p>Rearrange to get</p>
<div class="text-center">\[(P+m)^3-c\equiv 0\pmod{N}\]
</div>
<p>which is a cubic polynomial! Check to see the condition of Coppersmith’s theorem is satisfied, so we can apply that to recover the plaintext.</p>
<h2 id="håstads-broadcast-attack">Håstad’s broadcast attack</h2>
<p>How about when the message is large? If the padding is linear (perhaps just append or prepend <strong>known</strong> bits), given at least \(e\) ciphertexts from the same plaintext, we can still recover the message.</p>
<p class="notice--primary"><strong>Theorem</strong> Given \(N_1,\cdots N_k\) are pairwise coprime integers, and \(g_i(x)\) be polynomials of degree at most \(q\). If \(M<\min\{N_1,\cdots,N_k\}\) and \(g_i(M)\equiv 0\pmod{N_i}\) for each i, and \(k>q\), then there exists an efficient algorithm to compute \(M\).</p>
<p class="notice--success"><strong>Proof</strong> Use Chinese remainder theorem to construct \(g(x)\) such that \(g(M)\equiv 0\pmod{N_1 N_2\cdots N_k}\), then \(M\) and \(g\) satisfies the requirement of Coppersmith’s theorem. Use Coppersmith’s method to recover \(M\).</p>
<hr />
<h1 id="bivariate-coppersmiths-theorem">Bivariate Coppersmith’s Theorem</h1>
<p>Another version of Coppersmith’s theorem concerns small roots to bivariate polynomials mod \(N\). Here we have \(F(x,y)\) with integer coefficients again. We can again find small roots for the polynomial mod \(N\).</p>
<p><strong>Theorem (Coppersmith)</strong></p>
<blockquote class="notice--primary">
<p>Let \(F(x,y)\) be polynomial with integer coefficients and \(d\) natural number such that both \(\deg_x F, \deg_y F \leq d\). Write \(F(x,y)=\sum_{0\leq i,j\leq d} F_{i,j} x^i y^j\) and define \(W=\max\limits_{0\leq i,j\leq d} \vert F_{i,j}\vert X^i Y^j\) for each \(X\) and \(Y\) natural numbers.</p>
<p>If \(XY<W^{\frac{2}{3d}}\) then we can find roots of \(F\) mod \(N\) such that the roots \((x_0,y_0)\) satisfies \(\vert x_0\vert \leq X\) and \(\vert y_0\vert \leq Y\) that runs in polynomial time of \(\log W\) and \(2^d\).</p>
</blockquote>
<h2 id="boneh-durfee-attack">Boneh-Durfee Attack</h2>
<p>Recall \(de = 1 + k\phi(n)\). Since \(\phi(n)=n-p-q+1\) we get \(de = 1+ k(n-p-q+1)\).Now reduce modulo $e$ and rearrange to get \(2k(\frac{n+1}{2} - \frac{p+q}{2})+1\equiv 0\pmod{e}\). This way we get a bivariate polynomial \(f(x,y)=2x(A+y)+1\pmod{e}\) with small roots \((x_0,y_0)=\left(k,-\frac{p+q}{2}\right)\). We can use Coppersmith’s method (provided that \(d<N^{0.292}\)) to recover the small root and recover \(d\).</p>
<blockquote class="notice--warning">
<p><strong>Exercise</strong>
Explain how to recover \(d\) given the small roots \(\left(k,-\frac{p+q}{2}\right)\).</p>
</blockquote>
<hr />
<h1 id="howgrave-grahams-theorem">Howgrave-Graham’s Theorem</h1>
<p>Another theorem related to the Coppersmith’s theorem is the Howgrave-Graham’s<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup> theorem. It allows for an easier proof and better results for Coppersmith’s theorem.</p>
<p>Let’s use the convention that \(F(x)=\sum\limits_{i=0}^d a_i x^i\) be polynomial with integer coefficients with degree \(d\) (so \(a_d\neq 0\)). Let \(x_0\) be a root of \(F(x)\equiv 0\pmod{N}\) such that \(\lvert x_0\rvert < X\) for some integer \(X\). For that \(X\), we associate the polynomial \(F\) with the vector \(b_F = (a_0, a_1X, a_2X^2, \cdots, a_d X^d)\). The norm of a vector \(v=(v_1,\cdots v_n)\) is denoted \(\vert\vert v\rvert\rvert = \sqrt{\sum\limits_{i=1}^n v_i^2}\).</p>
<p class="notice--primary"><strong>Theorem (Howgrave-Graham)</strong> If \(\lvert\lvert b_F\rvert\rvert < \frac{N}{\sqrt{d+1}}\), then \(F(x_0)=0\) over the integers (instead of mod \(N\) only).</p>
<blockquote class="notice--success">
<p><strong>Proof</strong> First we note that \(\sum\limits_{i=1}^n x_i \leq \sqrt{n \cdot \sum\limits_{i=1}^n x_i^2}\) by Cauchy-Schawarz inequality. Then</p>
\[\begin{align*}
\lvert F(x_0)\rvert =& \left\lvert \sum\limits_{i=0}^d a_i x_0^i\right\rvert\\
\leq& \sum\limits_{i=0}^d \lvert a_i\rvert\ \lvert x_0^i\rvert\\
< &\sum\limits_{i=0}^d \lvert a_i\rvert X^i\\
\leq &\sqrt{d+1} \lvert\lvert b_F\rvert\rvert\text{ (By the above lemma)}\\
\leq &\sqrt{d+1}\frac{N}{\sqrt{d+1}}\\
=& N
\end{align*}\]
<p>So \(-N < F(x_0) < N\). Since \(F(x) \equiv 0\pmod{N}\) this means \(F(x_0) = 0\) (without mod).</p>
</blockquote>
<h2 id="factor-npq-with-partial-knowledge-of-p">Factor \(N=pq\) with partial knowledge of \(p\)</h2>
<p>Assume we have an approximation \(\hat{p}\) of \(p\) such that \(p = \hat{p} + x_0\) with \(\lvert x_0\rvert < X\). For example, \(p\) is a \(2k-\)bit integer and \(\hat{p}\) has the same \(k\) MSB as \(p\) (so we know the first half bits of \(p\)), so \(\left\lvert p-\hat{p}\right\rvert < 2^k\). Then we can actually factor the \(4k\)-bit integer \(N=pq\).</p>
<p>We can define the (degree-1) polynomial \(f(x) = \hat{p} + x\), then recovering the small roots of \(f(x)\) mod \(p\) allows us to recover \(p\)! Of course, we do not know \(p\) (that’s the whole point).</p>
<p>However, if we can lift this polynomial to another polynomial \(G\) (with small roots modulo \(p^h\) for some integer \(h\)), such that it satisfies the condition in Howgrave-Graham’s theorem, then we can directly find the the root \(x_0\) with \(G(x_0) = 0\) (and \(f(x_0)\equiv 0\pmod{p^h}\) as well), so we can get \(p\) by doing \(p=\gcd(N, f(x_0))\). Thus we have a similar-looking theorem (originally proved by Coppersmith’s theorem, later improved by Howgrave-Graham):</p>
<p class="notice--primary"><strong>Theorem</strong> Let \(N=pq\), and \(p<q<2p\). Let \(0<\varepsilon < \frac 14\), and \(\hat{p}\) a natural number such that \(\left\lvert p-\hat{p}\right\rvert < \frac{1}{2\sqrt 2} N^{\frac 14 - \varepsilon}\). Then \(N\) can be factored in polynomial time of \(\log N\) and \(\frac 1\varepsilon\).</p>
<p>We will delay the discussion of the derivations for the second part.</p>
<h1 id="reference">Reference</h1>
<p>Galbraith, S. D. (2012). <em>Mathematics of public key cryptography</em>. Cambridge University Press.</p>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>We only study polynomials with integer coefficients, and if the input are integer, the output are also integers. However some other polynomials (with rational coefficients) can also give integer ouputs. Those polynomials (called <em>integer-valued polynomials</em>), while interesting on its own (see: Hilbert polynomial), are not the focus today. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p>Howgrave-Graham is one person, not two person named Howgrave and Graham. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>Cousin Wu Ka Lokhello@klwu.coThe Coppersmith’s method is an application of lattice basis reduction algorithms (like LLL) to find small solutions to polynomials modulo \(N\). The application of this method ranges from several attacks on RSA, to solving the hidden number problem (for Diffie-Hellman key exchange or (EC)DSA).Basics of Lattice-based Cryptography2021-11-18T00:00:00+08:002021-11-18T00:00:00+08:00https://klwu.co/maths-in-crypto/lattice-based-cryptography-basics<p>Lattice is a very important construct for modern day cryptography, especially when we know that in the future, quantum computers will break both ECC and RSA, cryptographic primitives that are not vulnerable to the Shor’s algorithm will be the future of cryptography.
<!--exerpt--></p>
<h1 id="prelude-vector-spaces">Prelude: Vector Spaces</h1>
<p>Recall a vector space \(V\) over a field (say \(\mathbb{R}\) or \(\mathbb{C}\)) is a set of “vectors”, with the addition of vectors and multiplication of a vector by a scalar, satisfying certain properties. Since we know in linear algebra that every \(n\)-dimensional vector space over \(\mathbb{R}\) is isomorphic to \(\mathbb{R}^n\), let’s focus on the case of \(\mathbb{R}^n\), which can be realized as a \(n\)-tuple of real numbers \((x_1,x_2,\cdots,x_n)\), where the \(x_i\)’s are real numbers.</p>
<hr />
<blockquote class="notice--primary">
<p><strong>Definition (Vector Space)</strong> A vector space \(V\) over a field \(K\) is an abelian group \((V,+)\) along with a scalar multiplication operator \(\cdot: K\times V\to V\) such that</p>
<ol>
<li>\(1\cdot v=v\qquad \forall v\in V\) and \(1\) the multiplicative identity in \(K\)</li>
<li>\(c\cdot(dv)=(cd)\cdot v\qquad \forall c,d\in K, v\in V\) (Compatibility)</li>
<li>\((c+d)\cdot v=c\cdot v + d\cdot v\qquad \forall c,d\in K, v\in V\) (Distributive Law 1)</li>
<li>\(c\cdot (v+w)=c\cdot v + c\cdot w\qquad\forall c\in K, v,w\in V\) (Distributive Law 2)</li>
</ol>
<p>We will omit the dot for simplicity. Also recall the definition of an abelian group \((V,+)\) is a set \(V\) with a binary operation \(+: V\times V\to V\) such that for every \(a,b,c\in V\),</p>
<ol>
<li>\((a+b)+c = a+(b+c)\) (Associativity)</li>
<li>\(\exists e\in V\) such that \(e+a=a+e=a\). Denote the element by \(0\). (Identity)</li>
<li>\(\forall a\in V\ \exists d\in V\) such that \(a+d=d+a=e\). Also denote the element by \((-a)\). (Inverse)</li>
<li>\(a+b=b+a\) (Commutivity)</li>
</ol>
</blockquote>
<p class="notice--primary"><strong>Theorem</strong> A vector space \(V\) of dimension \(n\) over \(K\) is isomorphic to \(K^n\) (as vector spaces).</p>
<hr />
<p>For \(n=2\), we can just imagine an infinite plane, where vectors are simply an arrow pointing from the origin to some points in the plane. This helps us imagine what lattice looks like in a moment.</p>
<p>For vector spaces, we can talk about the idea of a basis, which is a set of vectors that, if we multiply each vector by some suitable scalar and add them up, we can generate every vector in the vector space (Spanning set), and that the zero vector cannot be generated unless all the scalars we choose are 0 (Linearly independent set). For example, the set \(\{(0,1),(1,0)\}\) can generate every vector \((x,y)\) in \(\mathbb{R}^2\) plane, where \(x,y\) are real numbers, just by doing \(x(0,1)+y(1,0)\).</p>
<p>A standard theorem of linear algebra is that a finite dimensional vector space always has a basis, and that any bases has the same number of vectors, which is the dimension of the vector space. Also, any basis can be transformed to another basis of the same vector space by taking appropriate linear combination, such that the coefficients form an invertible matrix.</p>
<p>However, what if we restrict \(x\) and \(y\) to be integers? The set of vectors with integer entries certainly don’t form a vector space over \(\mathbb{R}\), because for example \(0.5(1,0)\) makes a vector of not in that set. In this case, we get a <strong>Lattice</strong>.</p>
<h1 id="lattices">Lattices</h1>
<h2 id="definitions">Definitions</h2>
<p>There are many equivalent definitions of lattices, but let’s use the simplest one.</p>
<blockquote class="notice--primary">
<p><strong>Definition (Lattice)</strong>
Given a basis of \(n\) vectors of real coordinates with \(n\) entries, the lattice generated by that basis is the set of integer linear combinations of the vectors. In other words, let \(\mathcal{B}=\{\vec{v_i}=\,^t(x_{i1},x_{i2},\cdots,x_{in}): 0\lt i\leq n, x_{ij}\in\mathbb{R}\ \forall i,j\}\), then the lattice \(\mathcal{L}\) is given by \(\mathcal{L}=\mathcal{L}(\mathcal B)=\{\sum_i a_i\vec{v_i}: a_i\in\mathbb{Z}\}\). We also write \(\mathcal{B}\) to denote a matrix formed by taking the columns as the basis vectors. So we can write
\(\mathcal{B}=\left[\begin{matrix}\vec{v_1}&\vec{v_2}&\cdots&\vec{v_n}\end{matrix}\right]=\left[\begin{matrix}x_{11}&x_{21}&\cdots&x_{n1}\\
x_{12}&x_{22}&\cdots&x_{n2}\\\vdots&\vdots&\ddots\\x_{1n}&x_{2n}&\cdots&x_{nn}\end{matrix}\right]\).</p>
<p>Of course you can also take the row as the basis vectors, then everything described later can still work by transposing all the involved matrices.</p>
</blockquote>
<p>For simplicity, we shall work with integer lattices, i.e. the basis vectors have integer entries, or \(\mathcal{L}\subset \mathbb{Z}^n\).</p>
<p>You can visualize lattices with this: <a href="/demo/lattice-demo-1/">Lattice Demonstration</a></p>
<h3 id="unimodular-matrix">Unimodular Matrix</h3>
<p>If two bases generate the same lattice \(\mathcal{L}\), then their corresponding matrix are related by multiplying a matrix with determinant \(\pm 1\). Matrix with unit determinant are called a <strong>unimodular matrix</strong>. We shall state this as a theorem (without proof).</p>
<p class="notice--primary"><strong>Theorem</strong>
Let \(\mathcal{B}\) and \(\mathcal{C}\) be two bases of some lattices. \(\mathcal{L(B)}=\mathcal{L(C)}\) iff there exists an integer matrix \(U\) such that \(\det U=\pm 1\) and \(\mathcal{B}=\mathcal{C}U\).</p>
<p>In fact, a basis can be transformed to another by taking elementary column operations, i.e. (1) Swapping columns, (2) multiplying the column by \(-1\), and (3) add an integer multiple of a column to another. It is left as exercise to the reader to verify that such operations corresponds to multiplying the basis matrix by a suitable unimodular matrix.</p>
<h3 id="fundamental-domain">Fundamental Domain</h3>
<p class="notice--primary"><strong>Definition</strong>
The fundamental domain (or the fundamental parallelepiped) of a lattice \(\mathcal{L}(B)\), \(B=\{v_1,\cdots,v_n\}\) is the set \(\mathcal{F}=\{\sum_{i=1}^n c_iv_i: 0\leq c_i < 1\}\).</p>
<p>The \(n\)-dimensional volume (perhaps the Lebesgue measure) of this fundamental parallelepiped is given by \(\text{Vol}(\mathcal{F})=\left\lvert\det B\right\rvert\) The volume is invariant when changing basis, so we can also denote the volume as \(\det \mathcal{L}\).</p>
<h2 id="hard-problems-in-lattices">Hard Problems in Lattices</h2>
<p>For any lattice \(\mathcal{L}=\mathcal{L(B)}\), we define the shortest distance of \(\mathcal{L}\) to be the minimum distance between any two different lattice points. Alternatively, it is the length of the shortest (non-zero) vector in the lattice (why?). We denote this quantity by \(\lambda(\mathcal{L})=\inf\limits_{\vec{v}\in\mathcal{L}-\{\vec{0}\}}\vert\vert\vec{v}\vert\vert\), where \(\vert\vert\vec{v}\vert\vert\) here is taken to be the usual Euclidean distance \(\ell_2\). With this quantity defined, we can state the two (hard) lattice problems.</p>
<p class="notice--primary"><strong>Closest Vector Problem (CVP)</strong>
Given a vector \(\vec{w}\in \mathbb{R}^n\) that is not in the lattice \(\mathcal{L}\), find a vector \(\vec{v}\in\mathcal{L}\) such that the distance between them are the shortest. i.e., \(\vert\vert\vec{v}-\vec{w}\vert\vert\) is minimal.</p>
<p class="notice--primary"><strong>Shortest Vector Problem (SVP)</strong>
Given the lattice \(\mathcal{L}\), find \(\vec{v}\in\mathcal{L}\) such that \(\vert\vert\vec{v}\vert\vert=\lambda(\mathcal{L})\).</p>
<p>To a similar vein, we shall define the problem, but the solution is not required to be exact, instead allow a certain error.</p>
<p class="notice--primary"><strong>Approximate Shortest vector problem (apprSVP)</strong>
Let \(\psi(n)\) be a function depending on \(n\) only, and \(\mathcal{L}\) to be a lattice of dimension \(n\). The \(\psi(n)\)-apprSVP is to find a vector \(\vec{v}\in\mathcal{L}\) such that it’s distance is less than \(\psi(n)\) times that of the shortest vector. i.e. \(\vert\vert\vec{v}\vert\vert\leq\psi(n)\lambda(\mathcal{L})\).</p>
<p>It is left as exercise to the reader to define the approximate version of CVP (apprCVP).</p>
<p>CVP is known to be a NP-hard problem, while SVP is NP-hard in a certain “randomized reduction hypothesis”. However in certain cases (specific lattices for example), the problems may be easier to crack. For example, for \(n=2\), Gauss’s lattice reduction algorithm can completely solve the shortest vector problem.</p>
<h2 id="hermites-theorem-and-gaussian-heuristics">Hermite’s Theorem and Gaussian Heuristics</h2>
<p>One may want to estimate the distance of the shortest vector in any given lattice. Indeed, there are results that give some approximations.</p>
<hr />
<p class="notice--primary"><strong>Theorem(Hermite’s Theorem)</strong>
For any lattice \(\mathcal{L}\), there exists a non-zero vector \(\vec{v}\in\mathcal{L}\) such that \(\vert\vert\vec{v}\vert\vert\leq \sqrt{n}\det(\mathcal{L})^{\frac 1 n}\).</p>
<p class="notice--primary"><strong>Definition(Hermite’s Constant)</strong>
For a given dimension \(n\), define the Hermite constant \(\gamma_n\) to be the smallest value such that every lattice \(\mathcal{L}\) of dimension \(n\) has a non-zero vector \(\vec{v}\in\mathcal{L}\) such that \(\vert\vert\vec{v}\vert\vert^2\leq \gamma_n \det(\mathcal{L})^{\frac 2 n}\). So Hermite’s theorem states that \(\gamma_n\leq n\).</p>
<hr />
<p>For large \(n\), we know that \(\frac{n}{2\pi e}\leq \gamma_n\leq \frac{n}{\pi e}\). In fact, due to a result from Gauss, we <strong>expect</strong> for large \(n\), the shortest vector will have the length of approximately \(\sqrt{\frac{n}{2\pi e}}\det(\mathcal{L})^{\frac 1 n}\).</p>
<h2 id="babais-algorithm">Babai’s Algorithm</h2>
<p>We first introduce the relation between \(\det \mathcal{L}\) and the basis vectors.</p>
<blockquote class="notice--primary">
<p><strong>Theorem</strong>
Let \(B=\{v_i,\cdots,v_n\}\) be the basis of a lattice \(\mathcal{L}(B)\). Then \(\det \mathcal{L}\leq \prod_{i=1}^n \|v_i\|\)</p>
<p>Equality holds iff the basis \(B\) is orthogonal, in other words every pair \(i\neq j\) has \(\langle v_i,v_j\rangle =0\).</p>
</blockquote>
<blockquote class="notice--primary">
<p><strong>Proposition-Definition (Orthogonality Defect)</strong> Define the orthogonality defect of a basis \(B\) of lattice \(\mathcal{L}(B)\) to be \(\delta(B)=\frac{\prod_{i=1}^n \|v_i\|}{\det \mathcal{L}}\)</p>
<p>We always have \(\delta(B)\geq 1\) (by Hadamard’s Inequality). We say a basis is non-orthogonal when \(\delta(B)\) is very large.</p>
</blockquote>
<p class="notice--primary"><strong>Theorem</strong>
Every lattice \(\mathcal{L}\) of dimension \(n\) has a basis \(B\) of \(\mathcal{L}\) such that \(\delta(B)\leq n^{\frac n 2}\)</p>
<p>If the basis is orthogonal, then note that if \(c_i\in\mathbb{Z}\) , \(\|c_1v_1+\cdots+c_nv_n\|^2= c_1^2 \|v_1\|^2+\cdots+c_n^2 \|v_n\|^2\), so the shortest vector in \(\mathcal{L}\) is just the shortest vector in \(B\). On the other hand, for \(w\in \mathbb{R}^n-\mathcal{L}\), write \(w=t_1v_1+\cdots +t_nv_n\) (\(t_i\in \mathbb{R}\)), then for \(v=\sum_{i=1}^n c_iv_i\in\mathcal{L}\),\(\|v-w\|^2=\sum_{i=1}^n (t_i-c_i)^2 \|v_i\|^2\) So CVP can be trivially solved by taking \(c_i\) to be the integer closest to \(t_i\).</p>
<p>Doing the same trick for nearly-orthogonal bases (i.e. \(\delta(B)\) is close to 1) is known as the <strong>Babai’s Algorithm</strong>.</p>
<p>For highly non-orthogonal bases, this trick will not work. While the Babai’s algorithm is simple, it is actually quite hard to analyze when it will work or fail. We shall see better methods for solving apprSVP and apprCVP soon.</p>
<h2 id="lattice-basis-reduction">Lattice Basis Reduction</h2>
<p>One idea to remedy the above problem is to transform the basis into a relatively orthogonal basis.</p>
<blockquote class="notice--primary">
<p><strong>Definition (LLL-Reduced)</strong>
Let \(\delta=\frac{3}{4}\) and \(\|\cdot\|\) be the standard Euclidean norm.</p>
<p>Let \(B=\{v_1,\cdots,v_n\}\) be a basis of lattice \(\mathcal{L}\) and \(B^*=\{v_1^*,\cdots,v_n^*\}\) be the orthogonal basis of \(\mathbb{R}^n\) (not of \(\mathcal{L}\) in general) after applying the Gram-Schmidt process. Then we say \(B\) is LLL-reduced if</p>
<ol>
<li>\(\lvert\mu_{i,j}\rvert\leq\frac{1}{2}\) (Size-reduced)</li>
<li>\(\delta\|v_{k-1}^*\|^2\leq \|v_k^*\|^2 + \mu_{k,k-1}^2\|v_{k-1}^*\|^2\) (Lovász condition)</li>
</ol>
<p>The Lovász condition can also be expressed as \(\|v_k^*\|^2\geq + (\delta - \mu_{k,k-1}^2)\|v_{k-1}^*\|^2\).</p>
</blockquote>
<blockquote class="notice--primary">
<p><strong>Theorem</strong>
If a basis \(B=\{v_1,\cdots,v_n\}\) of lattice \(\mathcal{L}\) is LLL-reduced, then</p>
<ol>
<li>
\[\prod_{i=1}^n\|v_i\|\leq 2^{\frac{n-1}{4}}\det\mathcal{L}\]
</li>
<li>
\[\|v_j\|\leq 2^{\frac{i-1}{2}}\|v_i^*\|\text{ for all }1\leq j\leq i\leq n\]
</li>
<li>
\[\|v_1\|\leq 2^{\frac{n-1}{4}}(\det\mathcal{L})^{\frac{1}{n}}\]
</li>
<li>
\[\|v_1\|\leq 2^{\frac{n-1}{2}}\lambda(\mathcal{L})\]
</li>
</ol>
</blockquote>
<p>The forth assertion implies that an LLL-reduced basis gives a solution
to the \(2^{\frac{n-1}{2}}-\)apprSVP.</p>
<p>On the other hand, given a basis \(B\) that is LLL-reduced of lattice \(\mathcal{L}\), we can solve the \(C^n-\)apprCVP for some constant \(C\) by directly applying the Babai’s Algorithm on the LLL-reduced basis.</p>
<h3 id="lll-algorithm">LLL Algorithm</h3>
<p>While the exact algorithm to find a LLL-reduced basis will
not be mentioned here, we state the following theorem:</p>
<blockquote class="notice--primary">
<p><strong>Theorem</strong>
Given a basis \(B\) of lattice \(\mathcal{L}\subset \mathbb{Z}^n\), a LLL-reduced basis can be found in polynomial time of \(n\) and \(\log\max\limits_{v_i\in B}\|v_i\|\).</p>
</blockquote>
<p>One important note is that empirically, LLL algorithm gives much better results than it guarantees in theory.</p>
<h1 id="reference">Reference</h1>
<p>Hoffstein, J., Pipher, J., Silverman, J. H., & Silverman, J. H. (2008). <em>An introduction to mathematical cryptography (Vol. 1)</em>. New York: Springer.</p>Cousin Wu Ka Lokhello@klwu.coLattice is a very important construct for modern day cryptography, especially when we know that in the future, quantum computers will break both ECC and RSA, cryptographic primitives that are not vulnerable to the Shor’s algorithm will be the future of cryptography.HKCERT CTF 2021 Selected Write-ups2021-11-16T00:00:00+08:002021-11-16T00:00:00+08:00https://klwu.co/writeup/hkcert2021<p>Last Friday to Sunday there was the HKCERT CTF competition 2021 held by HKCERT. I am not allowed to join the competition (for obvious reasons). Nonetheless I am able to play around with different challenges. On 16 Nov 2021 (Tue) I held a write-up session for this CTF, so I have prepared two write-ups to share: Long Story Short (Key backup service 1) and Braceless (Key backup service 2).
<!--exerpt--></p>
<h1 id="long-story-short">Long Story Short</h1>
<p>Challenge Files:</p>
<details>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">os</span>
<span class="kn">import</span> <span class="nn">random</span>
<span class="kn">from</span> <span class="nn">Crypto.Cipher</span> <span class="kn">import</span> <span class="n">AES</span>
<span class="kn">from</span> <span class="nn">Crypto.Util.number</span> <span class="kn">import</span> <span class="n">isPrime</span> <span class="k">as</span> <span class="n">is_prime</span>
<span class="kn">from</span> <span class="nn">Crypto.Util.Padding</span> <span class="kn">import</span> <span class="n">pad</span>
<span class="c1"># 256 bits for random-number generator
</span><span class="n">N</span> <span class="o">=</span> <span class="mh">0xcdc21452d0d82fbce447a874969ebb70bcc41a2199fbe74a2958d0d280000001</span>
<span class="n">G</span> <span class="o">=</span> <span class="mh">0x5191654c7d85905266b0a88aea88f94172292944674b97630853f919eeb1a070</span>
<span class="n">H</span> <span class="o">=</span> <span class="mh">0x7468657365206e756d6265727320617265206f6620636f757273652073757321</span>
<span class="c1"># More challenge-specific parameters
</span><span class="n">E</span> <span class="o">=</span> <span class="mi">17</span> <span class="c1"># The public modulus
</span><span class="n">CALLS</span> <span class="o">=</span> <span class="mi">17</span> <span class="c1"># The number of operations allowed
</span>
<span class="c1"># Generate a 512-bit prime
</span><span class="k">def</span> <span class="nf">generate_prime</span><span class="p">(</span><span class="n">seed</span><span class="p">):</span>
<span class="n">random</span><span class="p">.</span><span class="n">seed</span><span class="p">(</span><span class="n">seed</span><span class="p">)</span>
<span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
<span class="n">p</span> <span class="o">=</span> <span class="n">random</span><span class="p">.</span><span class="n">getrandbits</span><span class="p">(</span><span class="mi">512</span><span class="p">)</span> <span class="o">|</span> <span class="p">(</span><span class="mi">1</span><span class="o"><<</span><span class="mi">511</span><span class="p">)</span>
<span class="k">if</span> <span class="n">p</span> <span class="o">%</span> <span class="n">E</span> <span class="o">==</span> <span class="mi">1</span><span class="p">:</span> <span class="k">continue</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">is_prime</span><span class="p">(</span><span class="n">p</span><span class="p">):</span> <span class="k">continue</span>
<span class="k">return</span> <span class="n">p</span>
<span class="c1"># Defines a 1024-bit RSA key
</span><span class="k">class</span> <span class="nc">Key</span><span class="p">:</span>
<span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">p</span><span class="p">,</span> <span class="n">q</span><span class="p">):</span>
<span class="bp">self</span><span class="p">.</span><span class="n">p</span> <span class="o">=</span> <span class="n">p</span>
<span class="bp">self</span><span class="p">.</span><span class="n">q</span> <span class="o">=</span> <span class="n">q</span>
<span class="bp">self</span><span class="p">.</span><span class="n">n</span> <span class="o">=</span> <span class="n">p</span><span class="o">*</span><span class="n">q</span>
<span class="bp">self</span><span class="p">.</span><span class="n">e</span> <span class="o">=</span> <span class="n">E</span>
<span class="n">phi</span> <span class="o">=</span> <span class="p">(</span><span class="n">p</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="n">q</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span>
<span class="bp">self</span><span class="p">.</span><span class="n">d</span> <span class="o">=</span> <span class="nb">pow</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">e</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="n">phi</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">encrypt</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">m</span><span class="p">):</span>
<span class="k">return</span> <span class="nb">pow</span><span class="p">(</span><span class="n">m</span><span class="p">,</span> <span class="bp">self</span><span class="p">.</span><span class="n">e</span><span class="p">,</span> <span class="bp">self</span><span class="p">.</span><span class="n">n</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">decrypt</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">c</span><span class="p">):</span>
<span class="k">return</span> <span class="nb">pow</span><span class="p">(</span><span class="n">c</span><span class="p">,</span> <span class="bp">self</span><span class="p">.</span><span class="n">d</span><span class="p">,</span> <span class="bp">self</span><span class="p">.</span><span class="n">n</span><span class="p">)</span>
<span class="c1"># Defines an user
</span><span class="k">class</span> <span class="nc">User</span><span class="p">:</span>
<span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">master_secret</span><span class="p">):</span>
<span class="bp">self</span><span class="p">.</span><span class="n">master_secret</span> <span class="o">=</span> <span class="n">master_secret</span>
<span class="bp">self</span><span class="p">.</span><span class="n">key</span> <span class="o">=</span> <span class="bp">None</span>
<span class="k">def</span> <span class="nf">generate_key</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="nb">id</span> <span class="o">=</span> <span class="n">random</span><span class="p">.</span><span class="n">getrandbits</span><span class="p">(</span><span class="mi">256</span><span class="p">)</span>
<span class="bp">self</span><span class="p">.</span><span class="n">key</span> <span class="o">=</span> <span class="n">Key</span><span class="p">(</span>
<span class="n">generate_prime</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">master_secret</span> <span class="o">+</span> <span class="nb">int</span><span class="p">.</span><span class="n">to_bytes</span><span class="p">(</span><span class="nb">pow</span><span class="p">(</span><span class="n">G</span><span class="p">,</span> <span class="nb">id</span><span class="p">,</span> <span class="n">N</span><span class="p">),</span> <span class="mi">32</span><span class="p">,</span> <span class="s">'big'</span><span class="p">)),</span>
<span class="n">generate_prime</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">master_secret</span> <span class="o">+</span> <span class="nb">int</span><span class="p">.</span><span class="n">to_bytes</span><span class="p">(</span><span class="nb">pow</span><span class="p">(</span><span class="n">H</span><span class="p">,</span> <span class="nb">id</span><span class="p">,</span> <span class="n">N</span><span class="p">),</span> <span class="mi">32</span><span class="p">,</span> <span class="s">'big'</span><span class="p">))</span>
<span class="p">)</span>
<span class="k">def</span> <span class="nf">send</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">m</span><span class="p">):</span>
<span class="k">if</span> <span class="bp">self</span><span class="p">.</span><span class="n">key</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span> <span class="k">raise</span> <span class="nb">Exception</span><span class="p">(</span><span class="s">'no key is defined!'</span><span class="p">)</span>
<span class="n">m</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">m</span><span class="p">,</span> <span class="mi">16</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="nb">hex</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">key</span><span class="p">.</span><span class="n">encrypt</span><span class="p">(</span><span class="n">m</span><span class="p">)))</span>
<span class="k">def</span> <span class="nf">get_secret</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">if</span> <span class="bp">self</span><span class="p">.</span><span class="n">key</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span> <span class="k">raise</span> <span class="nb">Exception</span><span class="p">(</span><span class="s">'no key is defined!'</span><span class="p">)</span>
<span class="n">m</span> <span class="o">=</span> <span class="nb">int</span><span class="p">.</span><span class="n">from_bytes</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">master_secret</span><span class="p">,</span> <span class="s">'big'</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="nb">hex</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">key</span><span class="p">.</span><span class="n">encrypt</span><span class="p">(</span><span class="n">m</span><span class="p">)))</span>
<span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
<span class="n">flag</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="n">environ</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">'FLAG'</span><span class="p">,</span> <span class="s">'hkcert21{***REDACTED***}'</span><span class="p">)</span>
<span class="n">flag</span> <span class="o">=</span> <span class="n">pad</span><span class="p">(</span><span class="n">flag</span><span class="p">.</span><span class="n">encode</span><span class="p">(),</span> <span class="mi">16</span><span class="p">)</span>
<span class="n">master_secret</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="n">urandom</span><span class="p">(</span><span class="mi">32</span><span class="p">)</span>
<span class="n">admin</span> <span class="o">=</span> <span class="n">User</span><span class="p">(</span><span class="n">master_secret</span><span class="p">)</span>
<span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">CALLS</span><span class="p">):</span>
<span class="n">command</span> <span class="o">=</span> <span class="nb">input</span><span class="p">(</span><span class="s">'[cmd] '</span><span class="p">).</span><span class="n">split</span><span class="p">(</span><span class="s">' '</span><span class="p">)</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">if</span> <span class="n">command</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="s">'send'</span><span class="p">:</span>
<span class="c1"># Encrypts a hexed message
</span> <span class="n">admin</span><span class="p">.</span><span class="n">send</span><span class="p">(</span><span class="n">command</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span>
<span class="k">elif</span> <span class="n">command</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="s">'pkey'</span><span class="p">:</span>
<span class="c1"># Refreshs a new set of key
</span> <span class="n">admin</span><span class="p">.</span><span class="n">generate_key</span><span class="p">()</span>
<span class="k">elif</span> <span class="n">command</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="s">'backup'</span><span class="p">:</span>
<span class="c1"># Gets the encrypted master secret
</span> <span class="n">admin</span><span class="p">.</span><span class="n">get_secret</span><span class="p">()</span>
<span class="k">elif</span> <span class="n">command</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="s">'flag'</span><span class="p">:</span>
<span class="n">cipher</span> <span class="o">=</span> <span class="n">AES</span><span class="p">.</span><span class="n">new</span><span class="p">(</span><span class="n">master_secret</span><span class="p">,</span> <span class="n">AES</span><span class="p">.</span><span class="n">MODE_CBC</span><span class="p">,</span> <span class="sa">b</span><span class="s">'</span><span class="se">\0</span><span class="s">'</span><span class="o">*</span><span class="mi">16</span><span class="p">)</span>
<span class="n">encrypted_flag</span> <span class="o">=</span> <span class="n">cipher</span><span class="p">.</span><span class="n">encrypt</span><span class="p">(</span><span class="n">flag</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">encrypted_flag</span><span class="p">.</span><span class="nb">hex</span><span class="p">())</span>
<span class="k">except</span> <span class="nb">Exception</span> <span class="k">as</span> <span class="n">err</span><span class="p">:</span>
<span class="k">raise</span> <span class="n">err</span>
<span class="k">print</span><span class="p">(</span><span class="s">'nope'</span><span class="p">)</span>
<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">'__main__'</span><span class="p">:</span>
<span class="n">main</span><span class="p">()</span>
<span class="s">'''
Side-note: I knew this is _very_ similar to "Calm Down" in HKCERT CTF 2020, but rest assured that this is a different challenge.
https://github.com/samueltangz/ctf-archive-created/blob/master/20201006-hkcert-ctf/calm-down/env/chall.py
'''</span>
</code></pre></div> </div>
</details>
<p>The summarize the challenge, we have the flag that is encrypted with an 32-byte AES key <code class="language-plaintext highlighter-rouge">master_secret</code>. We can access the <code class="language-plaintext highlighter-rouge">master_secret</code> encrypted with a “random” (will get to that in <em>Braceless</em>) 1024-bit RSA key. The RSA key can be updated to another random key, but you do not have access to the public modulus (\(e\)=17). You can also ask the service to encrypt arbitrary integers (and get the result). You can only send 17 queries to the server in a session.</p>
<h2 id="chinese-remainder-theorem">Chinese Remainder Theorem</h2>
<blockquote class="notice--primary">
<p><strong>Theorem</strong>: Suppose \(n_1,\cdots,n_k\) is pairwise coprime \((\gcd(n_i,n_j)=1\) \(\forall i \neq j)\), then the system of congruence equations</p>
\[\begin{cases}
x\equiv a_1\pmod{n_1}\\
x\equiv a_2\pmod{n_2}\\
\vdots\\
x\equiv a_k\pmod{n_k}\\
\end{cases}\]
<p>has a unique solution \(x^{*}\) mod \(n_1n_2\cdots n_k\).</p>
</blockquote>
<h2 id="broadcast-attack">Broadcast Attack</h2>
<p>Suppose a plaintext is encrypted \(k\) times, where all the public exponent \(e\) are the same, and \(k\geq e\). i.e. the following holds:</p>
<div class="text-center">\[\begin{cases}
m^e\equiv a_1\pmod{n_1}\\
m^e\equiv a_2\pmod{n_2}\\
\vdots\\
m^e\equiv a_k\pmod{n_k}\\
\end{cases}\]
</div>
<p>Then by Chinese Remainder Theorem, we get \(x^{*}\) mod \(n_1n_2\cdots n_k\) that satisfies all the equations above. Then \(x^{*}\equiv a_i\pmod{n_i}\) for any \(i\), at the same time \(m^e\equiv a_i\pmod{n_i}\) obviously.</p>
<p>But \(m^e=\underbrace{m\cdot m\cdots m}_{e}\leq \underbrace{m\cdot m\cdots m}_{k} <n_1\cdot n_2\cdots n_k\), so \(x^{*}=m^e\) <strong>(without mod)</strong>, since the solution is unique mod \(n_1\cdots n_k\). So we get \(m=\sqrt[e]{x^{*}}\) just by taking the usual \(e\)-th root of \(x^*\).</p>
<h2 id="example-韓信點兵">Example: 韓信點兵</h2>
<p>The setting is like so:</p>
<blockquote class="text-center">
<p>今有物不知其數，三三數之剩二，五五數之剩三，七七數之剩二，問物幾何？</p>
</blockquote>
<p>Let’s translate that to maths:</p>
<div class="text-center">\[\begin{cases}
x\equiv 2\pmod{3}\\
x\equiv 3\pmod{5}\\
x\equiv 2\pmod{7}
\end{cases}\]
</div>
<p>What is the value of \(x\)?</p>
<p>By Chinese remainder theorem, we know that the solution is \(x\equiv 23\pmod{105}\). If we further assume that \(0\leq x < 105\), then we can immediately deduce that \(x=23\) (without the mod).</p>
<p>If there are no such assumption, then note that any \(x = 23 + 105k\) (\(k\) any integers) would satisfy the equation so you do not have a <strong>unique</strong> solution.</p>
<h2 id="the-challenge">The Challenge</h2>
<p>Here are the following operations that we can do (we can make a total of 17 operations):</p>
<ol>
<li>flag: Get the flag encrypted with an AES key master_secret</li>
<li>pkey: Change the current RSA public key (No default key, and \(e=17\))</li>
<li>send: Use RSA key to encrypt arbitrary integers</li>
<li>backup: Use RSA key to encrypt the AES key master_secret</li>
</ol>
<p>So the goal is to try to obtain the AES key in order to decrypt the flag.</p>
<h3 id="broadcast-attack-">Broadcast Attack ?</h3>
<p>So the first idea is maybe to launch an broadcast attack, since \(e=17\) is small. However, we have a lot of obstacles ahead of us.</p>
<ol>
<li>We are not given the public modulus (even when changing the key)</li>
<li>We do not have 17 equations. (We need some for printing the
encrypted flag and for some other operations)</li>
</ol>
<h3 id="obstacle-1">Obstacle 1</h3>
<p>Note that we can send -1 to encrypt. Since \(e\) is odd (Why?), and \(-1\equiv n-1\pmod{n}\), so encrypting -1 will yield</p>
\[E_k(-1)=(-1)^e\pmod{n}=n-1\]
<p>So we can send -1 to obtain thep ublic modulus every time we change keys!</p>
<h3 id="obstacle-2">Obstacle 2</h3>
<p>We need 1 operation for flag; then 1 for pkey, 1 to send -1 and 1 for backup to obtain 1 ciphertext-modulus pair. Then we can only have 5 ciphertext-modulus pairs (\(3\cdot 5 + 1 = 16\) already)</p>
<h3 id="broadcast-attack-works-anyway">Broadcast Attack Works Anyway</h3>
<p>Now we have the secret key \(m\), which is 32 bytes in length, and the RSA uses a 1024-bit public modulus which will be larger than \(2^{511*2}=2^{1022}\).</p>
<p>This means that for each public modulus \(n_i\), we have that \(m \leq 2^{256}\) so
\(m^{17} < 2^{256*17} = 2^{4352} < 2^{5110} = 2^{1022*5} < n^5\).
So we really only need 5 ciphertext-modulus pairs (instead of 16)! This is due to the fact that the original assumption of broadcast attack only have that the size of \(m\) is less than \(n\), but our plaintext in this case is much smaller than the modulus, so we do not need as much equations.</p>
<h2 id="the-attack">The Attack</h2>
<p>We use python with pwntools to aid the attack.</p>
<details>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">pwn</span> <span class="kn">import</span> <span class="o">*</span>
<span class="n">debug</span> <span class="o">=</span> <span class="bp">True</span>
<span class="n">E</span> <span class="o">=</span> <span class="mi">17</span>
<span class="k">if</span> <span class="n">debug</span><span class="p">:</span>
<span class="n">r</span> <span class="o">=</span> <span class="n">process</span><span class="p">([</span><span class="s">'python3'</span><span class="p">,</span> <span class="s">'long_story_short.py'</span><span class="p">])</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">server</span> <span class="o">=</span> <span class="s">"chalp.hkcert21.pwnable.hk"</span>
<span class="n">port</span> <span class="o">=</span> <span class="mi">28157</span>
<span class="n">r</span> <span class="o">=</span> <span class="n">remote</span><span class="p">(</span><span class="n">server</span><span class="p">,</span> <span class="n">port</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">get_flag</span><span class="p">():</span>
<span class="n">r</span><span class="p">.</span><span class="n">recvuntil</span><span class="p">(</span><span class="sa">b</span><span class="s">'[cmd] '</span><span class="p">)</span>
<span class="n">r</span><span class="p">.</span><span class="n">sendline</span><span class="p">(</span><span class="sa">b</span><span class="s">'flag'</span><span class="p">)</span>
<span class="k">return</span> <span class="nb">int</span><span class="p">(</span><span class="n">r</span><span class="p">.</span><span class="n">recvline</span><span class="p">().</span><span class="n">decode</span><span class="p">().</span><span class="n">strip</span><span class="p">(),</span><span class="mi">16</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">pkey</span><span class="p">():</span>
<span class="n">r</span><span class="p">.</span><span class="n">recvuntil</span><span class="p">(</span><span class="sa">b</span><span class="s">'[cmd] '</span><span class="p">)</span>
<span class="n">r</span><span class="p">.</span><span class="n">sendline</span><span class="p">(</span><span class="sa">b</span><span class="s">'pkey'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">send</span><span class="p">(</span><span class="n">msg</span><span class="p">):</span>
<span class="n">r</span><span class="p">.</span><span class="n">recvuntil</span><span class="p">(</span><span class="sa">b</span><span class="s">'[cmd] '</span><span class="p">)</span>
<span class="n">r</span><span class="p">.</span><span class="n">sendline</span><span class="p">(</span><span class="sa">b</span><span class="s">'send '</span> <span class="o">+</span> <span class="n">msg</span><span class="p">)</span>
<span class="k">return</span> <span class="nb">int</span><span class="p">(</span><span class="n">r</span><span class="p">.</span><span class="n">recvline</span><span class="p">().</span><span class="n">decode</span><span class="p">().</span><span class="n">strip</span><span class="p">(),</span> <span class="mi">16</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">backup</span><span class="p">():</span>
<span class="n">r</span><span class="p">.</span><span class="n">recvuntil</span><span class="p">(</span><span class="sa">b</span><span class="s">'[cmd] '</span><span class="p">)</span>
<span class="n">r</span><span class="p">.</span><span class="n">sendline</span><span class="p">(</span><span class="sa">b</span><span class="s">'backup'</span><span class="p">)</span>
<span class="k">return</span> <span class="nb">int</span><span class="p">(</span><span class="n">r</span><span class="p">.</span><span class="n">recvline</span><span class="p">().</span><span class="n">decode</span><span class="p">().</span><span class="n">strip</span><span class="p">(),</span> <span class="mi">16</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">round</span><span class="p">():</span>
<span class="n">pkey</span><span class="p">()</span>
<span class="n">_n</span> <span class="o">=</span> <span class="n">send</span><span class="p">(</span><span class="sa">b</span><span class="s">"-1"</span><span class="p">)</span>
<span class="n">enc</span> <span class="o">=</span> <span class="n">backup</span><span class="p">()</span>
<span class="n">n</span> <span class="o">=</span> <span class="n">_n</span> <span class="o">+</span> <span class="mi">1</span>
<span class="k">return</span> <span class="n">n</span><span class="p">,</span> <span class="n">enc</span>
<span class="n">enc_flag</span> <span class="o">=</span> <span class="n">get_flag</span><span class="p">()</span>
<span class="n">N</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">ENC</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">5</span><span class="p">):</span>
<span class="n">n</span><span class="p">,</span> <span class="n">enc</span> <span class="o">=</span> <span class="nb">round</span><span class="p">()</span>
<span class="n">N</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">n</span><span class="p">)</span>
<span class="n">ENC</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">enc</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">enc_flag</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s">"enc ="</span><span class="p">,</span> <span class="n">ENC</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s">"N ="</span><span class="p">,</span> <span class="n">N</span><span class="p">)</span>
</code></pre></div> </div>
</details>
<p>This is the extraction of the ciphertext-modulus pair and the encrypted flag.</p>
<p>Then we feed the pairs into sage for the Chinese Remainder Theorem. We just need to take the 17-th root of the result to obtain the AES key.</p>
<details>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">enc</span> <span class="o">=</span> <span class="p">[...]</span>
<span class="n">N</span> <span class="o">=</span> <span class="p">[...]</span>
<span class="n">sol</span> <span class="o">=</span> <span class="n">CRT_list</span><span class="p">(</span><span class="n">enc</span><span class="p">,</span> <span class="n">N</span><span class="p">)</span>
<span class="n">secret</span> <span class="o">=</span> <span class="n">sol</span><span class="p">.</span><span class="n">nth_root</span><span class="p">(</span><span class="mi">17</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">secret</span><span class="p">).</span><span class="n">to_bytes</span><span class="p">(</span><span class="mi">32</span><span class="p">,</span> <span class="s">"big"</span><span class="p">))</span>
</code></pre></div> </div>
</details>
<p>Finally We just decrypt to get the flag.</p>
<details>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">Crypto.Cipher</span> <span class="kn">import</span> <span class="n">AES</span>
<span class="n">enc</span> <span class="o">=</span> <span class="p">...</span>
<span class="n">secret</span> <span class="o">=</span> <span class="p">...</span>
<span class="n">cipher</span> <span class="o">=</span> <span class="n">AES</span><span class="p">.</span><span class="n">new</span><span class="p">(</span><span class="n">secret</span><span class="p">,</span> <span class="n">AES</span><span class="p">.</span><span class="n">MODE_CBC</span><span class="p">,</span> <span class="sa">b</span><span class="s">'</span><span class="se">\0</span><span class="s">'</span><span class="o">*</span><span class="mi">16</span><span class="p">)</span>
<span class="n">dec</span> <span class="o">=</span> <span class="n">cipher</span><span class="p">.</span><span class="n">decrypt</span><span class="p">(</span><span class="n">enc</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">dec</span><span class="p">)</span>
</code></pre></div> </div>
</details>
<p>flag: <code class="language-plaintext highlighter-rouge">hkcert21{y0u_d0nt_n33d_e_m3ss4g3s_f0r_br0adc4s7_4t7ack_wh3n_m_i5_sm41l}</code></p>
<hr />
<h1 id="braceless">Braceless</h1>
<p>The python file is basically the same as the one in long story short, except we are not the ones who interact with the server. Instead, we are given a transcript.log that stored a session.</p>
<details>
<div class="language-diff highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">13,14c13,14
</span><span class="gd">< E = 65537 # The public modulus
< CALLS = 65537 # The number of operations allowed
</span><span class="p">---
</span><span class="gi">> E = 17 # The public modulus
> CALLS = 17 # The number of operations allowed
</span><span class="p">99,100c99,100
</span><span class="gd">< Side-note: I knew this is _very_ similar to "Long Story Short" in HKCERT CTF 2021, but rest assured that this is a different challenge.
< ...:)
</span><span class="p">---
</span><span class="gi">> Side-note: I knew this is _very_ similar to "Calm Down" in HKCERT CTF 2020, but rest assured that this is a different challenge.
> https://github.com/samueltangz/ctf-archive-created/blob/master/20201006-hkcert-ctf/calm-down/env/chall.py
</span></code></pre></div> </div>
</details>
<p>The session first gets the encrypted flag, then repeatedly do “send 2, send 3 and backup” for 16384 times.</p>
<h2 id="getting-n">Getting N</h2>
<p>First we still do not have the modulus \(N\). However, we have the encryption of 2 and 3. Note that
\(\begin{align*}
2^{e} - (2^e\pmod{n}) =& an\\
3^{e} - (3^e\pmod{n}) =& bn\\
\end{align*}\)
For some positive integers \(a\) and \(b\). The left hand side of both equations we already know. To obtain \(n\), we simply take the gcd of the two results, and the result will be \(cn\), where \(c\) is some small integers. We can just perform a trial division to get rid of the small factors.</p>
<p>The code for recovering the modulus and the (RSA) encryption of the master secret:</p>
<details>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">math</span> <span class="kn">import</span> <span class="n">gcd</span>
<span class="n">e</span> <span class="o">=</span> <span class="mi">65537</span>
<span class="n">N</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">BACKUP</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">e2</span> <span class="o">=</span> <span class="nb">pow</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span><span class="n">e</span><span class="p">)</span>
<span class="n">e3</span> <span class="o">=</span> <span class="nb">pow</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span><span class="n">e</span><span class="p">)</span>
<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s">"transcript.log"</span><span class="p">,</span> <span class="s">"r"</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
<span class="n">f</span><span class="p">.</span><span class="n">readline</span><span class="p">()</span>
<span class="n">flag</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">f</span><span class="p">.</span><span class="n">readline</span><span class="p">(),</span> <span class="mi">16</span><span class="p">)</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
<span class="n">f</span><span class="p">.</span><span class="n">readline</span><span class="p">()</span>
<span class="n">f</span><span class="p">.</span><span class="n">readline</span><span class="p">()</span>
<span class="n">enc_2</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">f</span><span class="p">.</span><span class="n">readline</span><span class="p">(),</span><span class="mi">16</span><span class="p">)</span>
<span class="n">f</span><span class="p">.</span><span class="n">readline</span><span class="p">()</span>
<span class="n">enc_3</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">f</span><span class="p">.</span><span class="n">readline</span><span class="p">(),</span><span class="mi">16</span><span class="p">)</span>
<span class="n">f</span><span class="p">.</span><span class="n">readline</span><span class="p">()</span>
<span class="n">backup</span> <span class="o">=</span> <span class="n">f</span><span class="p">.</span><span class="n">readline</span><span class="p">()</span>
<span class="n">g</span> <span class="o">=</span> <span class="n">gcd</span><span class="p">(</span><span class="n">e2</span><span class="o">-</span><span class="n">enc_2</span><span class="p">,</span> <span class="n">e3</span><span class="o">-</span><span class="n">enc_3</span><span class="p">)</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">500</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="o">-</span><span class="mi">1</span><span class="p">):</span>
<span class="k">if</span> <span class="n">g</span> <span class="o">%</span> <span class="n">i</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
<span class="n">g</span> <span class="o">=</span> <span class="n">g</span> <span class="o">//</span> <span class="n">i</span>
<span class="n">N</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">g</span><span class="p">)</span>
<span class="n">BACKUP</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">backup</span><span class="p">,</span><span class="mi">16</span><span class="p">))</span>
<span class="k">except</span> <span class="nb">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s">"var.py"</span><span class="p">,</span> <span class="s">"w"</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
<span class="n">f</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="sa">f</span><span class="s">"flag= </span><span class="si">{</span><span class="n">flag</span><span class="si">}</span><span class="se">\n</span><span class="s">"</span><span class="p">)</span>
<span class="n">f</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="sa">f</span><span class="s">"N= </span><span class="si">{</span><span class="n">N</span><span class="si">}</span><span class="se">\n</span><span class="s">"</span><span class="p">)</span>
<span class="n">f</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="sa">f</span><span class="s">"BACKUP= </span><span class="si">{</span><span class="n">BACKUP</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
</code></pre></div> </div>
</details>
<p>Now we have a bunch of \(N\)’s and encryptions. We can’t really do Chinese remainder theorem here as \(e=65537\) is too large.</p>
<p class="notice--danger"><strong>Exercise</strong>: Copy the analysis for the long story short part to conclude why Chinese remainder theorem is not feasible in this case.</p>
<h2 id="the-anti-climatic">The Anti-Climatic</h2>
<p>With so many moduli, what can we do? One thing is to try to check their gcd to find any common factor. If there are any common factors among the moduli, then we can already recover the private exponent and thus the master_secret!</p>
<p>Let’s just use a simple python script to loop through all pairs of moduli and look for any common factors.</p>
<details>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">math</span> <span class="kn">import</span> <span class="n">gcd</span>
<span class="kn">from</span> <span class="nn">var</span> <span class="kn">import</span> <span class="n">flag</span><span class="p">,</span> <span class="n">N</span><span class="p">,</span> <span class="n">BACKUP</span>
<span class="kn">from</span> <span class="nn">itertools</span> <span class="kn">import</span> <span class="n">combinations</span>
<span class="kn">from</span> <span class="nn">Crypto.Cipher</span> <span class="kn">import</span> <span class="n">AES</span>
<span class="n">e</span> <span class="o">=</span> <span class="mi">65537</span>
<span class="n">flag</span> <span class="o">=</span> <span class="n">flag</span><span class="p">.</span><span class="n">to_bytes</span><span class="p">(</span><span class="mi">64</span><span class="p">,</span> <span class="s">'big'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">decrypt</span><span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="n">p</span><span class="p">,</span> <span class="n">enc</span><span class="p">):</span>
<span class="n">q</span> <span class="o">=</span> <span class="n">n</span> <span class="o">//</span> <span class="n">p</span>
<span class="n">phi</span> <span class="o">=</span> <span class="p">(</span><span class="n">p</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span><span class="o">*</span><span class="p">(</span><span class="n">q</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span>
<span class="n">d</span> <span class="o">=</span> <span class="nb">pow</span><span class="p">(</span><span class="n">e</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="n">phi</span><span class="p">)</span>
<span class="k">return</span> <span class="nb">pow</span><span class="p">(</span><span class="n">enc</span><span class="p">,</span> <span class="n">d</span><span class="p">,</span> <span class="n">n</span><span class="p">)</span>
<span class="k">for</span> <span class="n">enum1</span><span class="p">,</span> <span class="n">enum2</span> <span class="ow">in</span> <span class="n">combinations</span><span class="p">(</span><span class="nb">enumerate</span><span class="p">(</span><span class="n">N</span><span class="p">),</span> <span class="mi">2</span><span class="p">):</span>
<span class="n">i1</span><span class="p">,</span> <span class="n">n1</span> <span class="o">=</span> <span class="n">enum1</span>
<span class="n">i2</span><span class="p">,</span> <span class="n">n2</span> <span class="o">=</span> <span class="n">enum2</span>
<span class="n">g</span> <span class="o">=</span> <span class="n">gcd</span><span class="p">(</span><span class="n">n1</span><span class="p">,</span> <span class="n">n2</span><span class="p">)</span>
<span class="k">if</span> <span class="n">g</span> <span class="o">!=</span> <span class="mi">1</span><span class="p">:</span>
<span class="k">break</span>
<span class="n">g</span> <span class="o">=</span> <span class="n">gcd</span><span class="p">(</span><span class="n">N</span><span class="p">[</span><span class="n">i1</span><span class="p">],</span><span class="n">N</span><span class="p">[</span><span class="n">i2</span><span class="p">])</span>
<span class="n">dec1</span> <span class="o">=</span> <span class="n">decrypt</span><span class="p">(</span><span class="n">N</span><span class="p">[</span><span class="n">i1</span><span class="p">],</span> <span class="n">g</span><span class="p">,</span> <span class="n">BACKUP</span><span class="p">[</span><span class="n">i1</span><span class="p">])</span>
<span class="n">dec2</span> <span class="o">=</span> <span class="n">decrypt</span><span class="p">(</span><span class="n">N</span><span class="p">[</span><span class="n">i2</span><span class="p">],</span> <span class="n">g</span><span class="p">,</span> <span class="n">BACKUP</span><span class="p">[</span><span class="n">i2</span><span class="p">])</span>
<span class="k">assert</span> <span class="n">dec1</span> <span class="o">==</span> <span class="n">dec2</span>
<span class="n">secret</span> <span class="o">=</span> <span class="n">dec1</span><span class="p">.</span><span class="n">to_bytes</span><span class="p">(</span><span class="mi">32</span><span class="p">,</span> <span class="s">'big'</span><span class="p">)</span>
<span class="n">cipher</span> <span class="o">=</span> <span class="n">AES</span><span class="p">.</span><span class="n">new</span><span class="p">(</span><span class="n">secret</span><span class="p">,</span> <span class="n">AES</span><span class="p">.</span><span class="n">MODE_CBC</span><span class="p">,</span> <span class="sa">b</span><span class="s">'</span><span class="se">\0</span><span class="s">'</span><span class="o">*</span><span class="mi">16</span><span class="p">)</span>
<span class="n">dec</span> <span class="o">=</span> <span class="n">cipher</span><span class="p">.</span><span class="n">decrypt</span><span class="p">(</span><span class="n">flag</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">dec</span><span class="p">)</span>
</code></pre></div> </div>
</details>
<p>After 8 minutes, we actually get a common factor! So the problem is already solved…</p>
<h2 id="why">Why?</h2>
<p>Why is there a common factor? For that we look at the code for parameter generation:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="bp">self</span><span class="p">.</span><span class="n">key</span> <span class="o">=</span> <span class="n">Key</span><span class="p">(</span>
<span class="n">generate_prime</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">master_secret</span> <span class="o">+</span> <span class="nb">int</span><span class="p">.</span><span class="n">to_bytes</span><span class="p">(</span><span class="nb">pow</span><span class="p">(</span><span class="n">G</span><span class="p">,</span> <span class="nb">id</span><span class="p">,</span> <span class="n">N</span><span class="p">),</span> <span class="mi">32</span><span class="p">,</span> <span class="s">'big'</span><span class="p">)),</span>
<span class="n">generate_prime</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">master_secret</span> <span class="o">+</span> <span class="nb">int</span><span class="p">.</span><span class="n">to_bytes</span><span class="p">(</span><span class="nb">pow</span><span class="p">(</span><span class="n">H</span><span class="p">,</span> <span class="nb">id</span><span class="p">,</span> <span class="n">N</span><span class="p">),</span> <span class="mi">32</span><span class="p">,</span> <span class="s">'big'</span><span class="p">))</span>
<span class="p">)</span>
</code></pre></div></div>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">generate_prime</span><span class="p">(</span><span class="n">seed</span><span class="p">):</span>
<span class="n">random</span><span class="p">.</span><span class="n">seed</span><span class="p">(</span><span class="n">seed</span><span class="p">)</span>
<span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
<span class="n">p</span> <span class="o">=</span> <span class="n">random</span><span class="p">.</span><span class="n">getrandbits</span><span class="p">(</span><span class="mi">512</span><span class="p">)</span> <span class="o">|</span> <span class="p">(</span><span class="mi">1</span><span class="o"><<</span><span class="mi">511</span><span class="p">)</span>
<span class="k">if</span> <span class="n">p</span> <span class="o">%</span> <span class="n">E</span> <span class="o">==</span> <span class="mi">1</span><span class="p">:</span> <span class="k">continue</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">is_prime</span><span class="p">(</span><span class="n">p</span><span class="p">):</span> <span class="k">continue</span>
<span class="k">return</span> <span class="n">p</span>
</code></pre></div></div>
<p>Since the random is seeded by the input, and <code class="language-plaintext highlighter-rouge">master_secret</code> is fixed, so it comes down to whether the latter part is fixed! In this case we are using the function \(f(x) = G^x\pmod{n}\) for the latter part, where <code class="language-plaintext highlighter-rouge">id</code> is random. <code class="language-plaintext highlighter-rouge">id</code> is a random 256-byte integer, and \(N\) is also a 256-byte integer, we might (wrongly) assume, that \(G\) and \(H\) are primitive roots mod \(N\), and if that is the case, the chance of two \(f(x)\) and \(f(y)\) to be equal are quite low.</p>
<p>However, We actually have that:</p>
<div class="text-center">\[G^{33554432}\equiv 1\pmod{N}\]
</div>
<p>\(33554432\) is just a 25-bit number! By some math we know that the probability of at least one collision when we have \(16384\) random samples is actually \(1-\frac{33554432!}{33554432^{16384}(33554432-16384)!}\approx 98\%\). (This is the famous birthday problem)</p>
<p>So the primes are basically guaranteed to collide!</p>
<h2 id="unintended-solution">Unintended Solution…?</h2>
<p>The flag of this challenge is <code class="language-plaintext highlighter-rouge">hkcert21{y0u_d0nt_n33d_p41rw15e_9cd_1f_y0u_c4n_d0_i7_1n_b4tch}</code>. What is batch gcd?</p>
<p>As it turns out, there is a fast algorithm to compute if (and which) 1 of the \(N\) have any common factor with the other remaining \(N\)’s. If there are, then we can already factor one of the modulus. The idea of the algorithm is to, for the list \(X\) of integers, to compute</p>
<div class="text-center">\[\gcd\left(X[i],\prod_{k\neq i}X[k]\right)\]
</div>
<p>We just need to precompute the product of all the integers, then invoke gcd for \(n\) times (where \(n\) is the number of integers), as opposed to the naive pairwise comparison method, which requires \(\frac{n(n-1)}{2}\) gcd calls. However as the pairwise method works it doesn’t matter.</p>Cousin Wu Ka Lokhello@klwu.coLast Friday to Sunday there was the HKCERT CTF competition 2021 held by HKCERT. I am not allowed to join the competition (for obvious reasons). Nonetheless I am able to play around with different challenges. On 16 Nov 2021 (Tue) I held a write-up session for this CTF, so I have prepared two write-ups to share: Long Story Short (Key backup service 1) and Braceless (Key backup service 2).