summaryrefslogtreecommitdiff
path: root/nonce-reuse.html
diff options
context:
space:
mode:
Diffstat (limited to 'nonce-reuse.html')
-rw-r--r--nonce-reuse.html140
1 files changed, 92 insertions, 48 deletions
diff --git a/nonce-reuse.html b/nonce-reuse.html
index 0795386..21c794d 100644
--- a/nonce-reuse.html
+++ b/nonce-reuse.html
@@ -48,7 +48,10 @@
nonces are reused, the keystream will be identical, allowing us to
recover plaintext via
<a href="https://samwho.dev/blog/toying-with-cryptography-crib-dragging/">
- crib dragging</a>, which makes this attack particularly effective.
+ crib dragging</a>, which makes this attack particularly effective:
+ \[
+ c' = c \oplus m \oplus m'.
+ \]
</p>
<p>
However, we still need to compute a new MAC over the forged ciphertext.
@@ -65,35 +68,32 @@
\[
mac + mac' = (s + s') + (len + len')h + (c_1 + c'_1)h^2 + (c_0+c'_0)h^3,
\]
- Since \(s = s'\) and \(x+x=0 \in \mathbb{F}_{2^{128}}\), we are
+ Since \(s = s'\) and \(x+x=0\) in \(\mathbb{F}_{2^{128}}\), we are
left with the polynomial equation
\[
0 = (mac + mac') + (len + len')h + (c_1 + c'_1)h^2 + (c_0+c'_0)h^3
\]
where all variables are known other than \(h\). Thus, recovering \(h\)
- is a simple matter of finding the roots by <a href="https://en.wikipedia.org/wiki/Factorization_of_polynomials_over_finite_fields">factoring the
- polynomial</a> using a computer algebra system such as SageMath.
+ is a matter of finding the roots by <a href="https://en.wikipedia.org/wiki/Factorization_of_polynomials_over_finite_fields">factoring the
+ polynomial</a>.
</p>
<p>
We plug \(h\) back into the first equation to recover \(s\),
and finally, we can forge the MAC for arbitary ciphertext under the
- same nonce.
+ same nonce. Note that there may be multiple possible monomial roots;
+ in this case, one can check each possibility online.
</p>
</details>
<br>
<form>
<div>
- <input type="button" value="Autofill example">
- </div>
-
- <div>
<label for="key">Key (16 bytes in hex)</label>
- <input id="key" type="text">
+ <input id="key" type="text" value="59454c4c4f575f5355424d4152494e45">
</div>
<div>
<label for="nonce">Nonce (12 bytes in hex)</label>
- <input id="nonce" type="text">
+ <input id="nonce" type="text" value="4a4f5247454c424f52474553">
</div>
<div>
@@ -102,27 +102,17 @@
</div>
<div>
- <label for="aad1">First additional authenticated data (in ASCII)</label>
- <input id="aad1" type="text">
- </div>
-
- <div>
<label for="m2">Second message (in ASCII)</label>
<input id="m2" type="text">
</div>
<div>
- <label for="aad2">Second additional authenticated data (in ASCII)</label>
- <input id="aad2" type="text">
+ <label for="mf">Forged message; shorter than the first message (in ASCII)</label>
+ <input id="mf" type="text">
</div>
<div>
- <label for="c">Forged ciphertext (in hex)</label>
- <input id="c" type="text">
- </div>
-
- <div>
- <input type="button" value="Compute authentication key and forge MAC">
+ <input type="button" value="Recover authentication key and forge MAC">
</div>
</form>
<div>
@@ -130,46 +120,100 @@
<input id="h" type="text" disabled value="deadbeef">
</div>
<div>
+ <label for="mf">Forged ciphertext, assuming first message is known</label>
+ <input id="mf" type="text">
+ </div>
+ <div>
<label for="mac">Forged MAC</label>
<input id="mac" type="text" disabled value="deadbeef">
</div>
<br>
+ <details>
+ <summary>
+ Show me the code.
+ </summary>
<pre>
-from <a href="#">aesgcmanalysis</a> import xor, gcm_encrypt, nonce_reuse_recover_secrets, forge_gmac
-k = b"YELLOW_SUBMARINE"
-nonce = b"JORGELBORGES"
-m1 = b"""The universe (which others call the Library) is composed of an indefinite and
-perhaps infinite number of hexagonal galleries, with vast air shafts between,
-surrounded by very low railings."""
+from <a href="/git/forbidden-salamanders">aesgcmanalysis</a> import xor, gcm_encrypt, gcm_decrypt, nonce_reuse_recover_secrets
+
+k = b"tlonorbistertius"
+nonce = b"jorgelborges"
+m1 = b"The universe (which others call the Library)"
aad1 = b"The Anatomy of Melancholy"
-m2 = b"From any of the hexagons one can see, interminably, the upper and lower floors."
+m2 = b"From any of the hexagons one can see, interminably"
aad2 = b"Letizia Alvarez de Toledo"
-c1, mac1 = gcm_encrypt(k, nonce, m1, aad1)
-c2, mac2 = gcm_encrypt(k, nonce, m2, aad2)
+
+c1, mac1 = gcm_encrypt(k, nonce, aad1, m1)
+c2, mac2 = gcm_encrypt(k, nonce, aad2, m2)
# Recover the authentication key and blind from public information
-h, s = nonce_reuse_recover_secrets(nonce, aad1, aad2, c1, c2, mac1, mac2)
+possible_secrets = nonce_reuse_recover_secrets(nonce, aad1, aad2, c1, c2, mac1, mac2)
# Forge the ciphertext
-m_forged = b"As was natural, this inordinate hope was followed by an excessive depression."
-c_forged = xor(c2, xor(m2, m_forged))
+m_forged = b"As was natural, this inordinate hope"
+assert len(m_forged) <= len(m1)
+c_forged = xor(c1, xor(m1, m_forged))
aad_forged = b"You who read me, are You sure of understanding my language?"
-mac_forged = forge_gmac(nonce, h, s, c_forged, aad_forged)
-assert gcm_decrypt(k, nonce, c_forged, aad_forged, mac_forged) == m_forged
- </pre>
- <details>
- <summary>
- Show me the code.
- </summary>
- </details>
+
+# Check possible candidates for authentication key
+succeeded = False
+for h, s in possible_secrets:
+ mac_forged = gmac(h, s, aad_forged, c_forged)
+ try:
+ assert gcm_decrypt(k, nonce, aad_forged, c_forged, mac_forged) == m_forged
+ succeeded = True
+ print(c_forged.hex(), mac_forged.hex())
+ except AssertionError:
+ pass
+assert succeeded</pre></details>
<details>
<summary>
Show me the math.
</summary>
- see homepage
- The forged ciphertext can be computed as a xor from original ciphertext,
- which should be particularly easy as nonce reuse reveals the XOR and then
- crib dragging.
+ <p>
+ A description of the construction of GMAC can be found at the <a
+ href="/forbidden-salamanders">mission homepage</a>.
+ </p>
+ <p>
+ Once the polynomial difference is computed, one can use SageMath
+ to compute the factors:
+ </p>
+ <pre>
+K = GF(2**128, name='x', modulus=x^128+x^7+x^2+x+1)
+x = K.gen()
+S = PolynomialRing(K, 'y')
+y = S.gen()
+p = (x^127 + x^125 + x^121 + x^118 + x^117 + x^116 + x^113 + x^111 + x^108 + x^105 + x^102 + x^99 + x^97 + x^95 + x^89 + x^87 +
+ x^85 + x^84 + x^81 + x^78 + x^73 + x^71 + x^69 + x^67 + x^65 + x^63 + x^62 + x^59 + x^57 + x^55 + x^54 + x^53 + x^52 + x^51 +
+x^49 + x^47 + x^46 + x^45 + x^43 + x^41 + x^39 + x^38 + x^37 + x^36 + x^33 + x^29 + x^28 + x^25 + x^21 + x^20 + x^17 + x^15 + x
+^13 + x^9 + x^7 + x^4 + x^3 + x)*y^5 + (x^127 + x^125 + x^121 + x^118 + x^117 + x^116 + x^113 + x^111 + x^108 + x^105 + x^102 +
+ x^99 + x^97 + x^95 + x^89 + x^87 + x^85 + x^84 + x^81 + x^78 + x^73 + x^71 + x^69 + x^67 + x^65 + x^63 + x^62 + x^59 + x^57 +
+x^55 + x^54 + x^53 + x^52 + x^51 + x^49 + x^47 + x^46 + x^45 + x^43 + x^41 + x^39 + x^38 + x^37 + x^36 + x^33 + x^29 + x^28 + x
+^25 + x^21 + x^20 + x^17 + x^15 + x^13 + x^9 + x^7 + x^4 + x^3 + x)*y^4 + (x^127 + x^125 + x^124 + x^123 + x^122 + x^120 + x^11
+9 + x^118 + x^115 + x^112 + x^111 + x^110 + x^109 + x^108 + x^106 + x^101 + x^100 + x^99 + x^98 + x^96 + x^93 + x^88 + x^87 + x
+^84 + x^83 + x^82 + x^78 + x^77 + x^74 + x^71 + x^70 + x^69 + x^68 + x^65 + x^62 + x^61 + x^60 + x^57 + x^55 + x^53 + x^50 + x^
+49 + x^47 + x^46 + x^44 + x^43 + x^41 + x^40 + x^39 + x^37 + x^36 + x^35 + x^34 + x^33 + x^31 + x^30 + x^25 + x^19 + x^16 + x^1
+4 + x^13 + x^12 + x^11 + x^10 + x^5 + x^2 + x + 1)*y^3 + (x^124 + x^123 + x^121 + x^120 + x^119 + x^118 + x^117 + x^116 + x^115
+ + x^113 + x^112 + x^110 + x^107 + x^106 + x^104 + x^103 + x^101 + x^99 + x^98 + x^95 + x^94 + x^83 + x^82 + x^81 + x^80 + x^79
+ + x^77 + x^76 + x^75 + x^73 + x^72 + x^67 + x^64 + x^63 + x^62 + x^61 + x^59 + x^57 + x^56 + x^54 + x^53 + x^51 + x^49 + x^48
++ x^46 + x^45 + x^44 + x^42 + x^36 + x^35 + x^34 + x^33 + x^31 + x^28 + x^22 + x^21 + x^17 + x^12 + x^11 + x^10 + x^8 + x^5 + x
+^4 + 1)*y^2 + (x^120 + x^119 + x^56 + x^55)*y + (x^127 + x^126 + x^125 + x^124 + x^123 + x^118 + x^117 + x^116 + x^115 + x^114
++ x^113 + x^105 + x^103 + x^98 + x^96 + x^94 + x^91 + x^90 + x^89 + x^87 + x^86 + x^85 + x^83 + x^81 + x^80 + x^78 + x^77 + x^7
+0 + x^66 + x^63 + x^62 + x^59 + x^58 + x^54 + x^53 + x^52 + x^50 + x^47 + x^45 + x^44 + x^42 + x^40 + x^39 + x^37 + x^35 + x^34
+ + x^30 + x^29 + x^27 + x^26 + x^25 + x^23 + x^18 + x^16 + x^14 + x^13 + x^12 + x^10 + x^9 + x^8 + x^7 + x^6 + x^5 + x^4 + x^3
++ 1)
+for factor, _ in p.factor():
+ if factor.degree() == 1:
+ print('Authentication key:', factor - y)</pre>
+ <p>
+ However, the library powering this demonstration implements <a href="https://en.wikipedia.org/wiki/Factorization_of_polynomials_over_finite_fields">polynomial factoring over finite fields</a>, which is an edifying exercise.
+ </p>
+ <p>
+ We present advice for those who wish to implement polynomial factorization as well:
+ </p>
+ <ul>
+ <li>The gcd of two polynomials is unique only up to multiplication by a non-zero constant because &ldquo;greater&rdquo; is defined for polynomials in terms of degree. When used in algorithms, gcd refers to the <em>monic</em> gcd, which is unique.</li>
+ <li>The <a href="https://math.stackexchange.com/a/943626/1084004">inverse Frobenius automorphism</a> (i.e., square root) in \(\mathbb{F}_{2^{128}}\) is given by \(\sqrt{x} = x^{2^{127}})\).</li>
+ </ul>
</details>
<script>
MathJax = {