summaryrefslogtreecommitdiff
path: root/templates/key-commitment.html
blob: 77f3037e964021eff4fdcf7bd9bdf04b11b60f2d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
<!DOCTYPE html>
<html>
  <head>
    <title>Forbidden Salamanders &middot; Key Commitment</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" type="text/css" href="/static/styles.css">
    <link rel="stylesheet" type="text/css" href="/forbidden-salamanders/static/styles.css">
    <link rel="shortcut icon" type="image/x-icon" href="/forbidden-salamanders/static/favicon.ico">
  </head>
  <body>
	<div class="container">
        <div>
            <div class="home">
                <a href="/forbidden-salamanders" class="home-title">Forbidden Salamanders</a>
                <span> at </span><a href="/">cyfraeviolae.org</a>
            </div>
            <div class="crumbs">
				<a href="/forbidden-salamanders/key-commitment"><strong>key commitment</strong></a>
                <span class="sep"> · </span>
                <a href="/forbidden-salamanders/nonce-reuse">nonce reuse</a>
                <span class="sep"> · </span>
                <a href="/forbidden-salamanders/mac-truncation">mac truncation</a>
                <span class="sep"> · </span>
                <a href="/git/forbidden-salamanders">source code</a>
            </div>
        </div>
        <p>
            <strong>Key commitment.</strong> One of
            our agents has infiltrated Roseacrucis&rsquo; inner circle, but all
            secret keys are required to be surrendered to the
            counterintelligence authority. Help her send AES-GCM ciphertexts back to
            the Library that decrypt to confidential information under one key,
            but innocuous banter under another.
        </p>
        <br>
		{% if form.errors %}
		<div class="errors">
			Errors:
			<ul>
				{% for name, errors in form.errors.items() %}
				{% for error in errors %}
				<li> {{name}}: {{ error }} </li>
				{% endfor %}
				{% endfor %}
			</ul>
		</div>
		{% endif %}
        <form action="/forbidden-salamanders/key-commitment" method="post" enctype="multipart/form-data">
			<div><em>
				The Library&rsquo;s agent chooses two files: one file that confidential information, and second file that looks innocuous.
			</em></div><br>

			<input type="radio" id="sample" name="mode" value="sample" required checked>
			<label for="sample">Use sample JPEG and BMP files:</label><br>
			<br>
			<img class="responsive-img" src="/forbidden-salamanders/static/axolotl.jpg">
			<img class="responsive-img" src="/forbidden-salamanders/static/kitten.bmp">
			<br>
			<br>
			<input type="radio" id="sample-pdf" name="mode" value="sample-pdf" required>
			<label for="sample-pdf">Use sample PDF files:</label><br>
            <a href="/forbidden-salamanders/static/bishop-sestina.pdf">bishop-sestina.pdf</a>
            <a href="/forbidden-salamanders/static/ashbery-sestina.pdf">ashbery-sestina.pdf</a>
            <br>
            <br>
			<input type="radio" id="custom" name="mode" value="custom" required>
			<label for="custom">Select custom JPEG and BMP:</label><br>

            <div>
            <label for="jpeg">JPEG file (&lt;200KB)</label>
			<input type="file" id="jpeg" name="jpeg" accept="image/jpeg">
            </div>

            <div>
            <label for="bmp">BMP file (&lt;50KB)</label>
			<input type="file" id="bmp" name="bmp" accept="image/bmp">
            </div>

            <br>
			<input type="radio" id="custom-pdf" name="mode" value="custom-pdf" required>
			<label for="custom-pdf">Select custom PDF and PDF:</label><br>

            <div>
            <label for="pdf1">First PDF file (&lt;200KB)</label>
			<input type="file" id="pdf1" name="pdf1" accept="application/pdf">
            </div>

            <div>
            <label for="pdf2">Second PDF file (&lt;200KB)</label>
			<input type="file" id="pdf2" name="pdf2" accept="application/pdf">
            </div>

			<br><div><em>
				The agent now computes a nonce, two keys, and constructs a single
				AES-GCM ciphertext. When decrypted under the first key, it will look
				identical to the first file; when decrypted under the second
				key, it will look identical to the second file.
			</em></div>
            <br>
            <div>
				<button type="submit">Download ciphertext</button>
            </div>
        </form>
		<form action="/forbidden-salamanders/key-commitment" method="get">
		<div>
			<button type="submit">Reset</button>
		</div>
		</form>
        <p>
            Parameters for JPEG and BMP:
            <br>
			Key 1: <code>8007941455b5af579bb12fff92ef31a3</code>
			<br>
			Key 2: <code>14ef746e8b1792e52b1d22ef124fae97</code>
			<br>
			Nonce: <code>4a4f5247454c424f52474553</code>
        </p>
        <p>
            Parameters for PDF and PDF:
            <br>
			Key 1: <code>c94a4dbd95faf02bdc0c39e0c0984299</code>
			<br>
			Key 2: <code>e4d26cdfbc732473103a5a887a755e19</code>
			<br>
			Nonce: <code>4a4f5247454c424f52474553</code>
        </p>
		<p>
            You can test your ciphertext with Go. You may need to alter the
            path of <code>polyglot.enc</code> to reflect your download
            directory.
		</p>
<pre class="demo">
curl -L -o /tmp/decrypt-aes-gcm.go https://cyfraeviolae.org/forbidden-salamanders/static/decrypt-aes-gcm.go
go build -o /tmp/decrypt-aes-gcm /tmp/decrypt-aes-gcm.go

# For JPEG and BMP
&lt; polyglot.enc &gt; /tmp/polyglot-first.jpg  /tmp/decrypt-aes-gcm 8007941455b5af579bb12fff92ef31a3 4a4f5247454c424f52474553
&lt; polyglot.enc &gt; /tmp/polyglot-second.bmp /tmp/decrypt-aes-gcm 14ef746e8b1792e52b1d22ef124fae97 4a4f5247454c424f52474553

# For PDF and PDF
&lt; polyglot.enc &gt; /tmp/polyglot-first.pdf  /tmp/decrypt-aes-gcm c94a4dbd95faf02bdc0c39e0c0984299 4a4f5247454c424f52474553
&lt; polyglot.enc &gt; /tmp/polyglot-second.pdf /tmp/decrypt-aes-gcm e4d26cdfbc732473103a5a887a755e19 4a4f5247454c424f52474553
</pre>
		<etails>
			<summary>
                Attack outline.
			</summary>
        <p>
            This attack was shown by Yevgeniy Dodis, Paul Grubbs, Thomas Ristenpart, and Joanne Woodage
            in <a href="https://eprint.iacr.org/2019/016">Fast Message Franking: From Invisible Salamanders to Encryptment</a>.
        </p>
        <h4>Colliding MACs</h4>
        <p>
            First, we will describe a general strategy to create a ciphertext that yields the same MAC
            with two different keys. Then we will show how to construct a ciphertext that yields
            meaningful results when decrypted with those two keys.
        </p>
        <p>
            Consider arbitrary keys \(k_1, k_2\), nonce \(n\), and ciphertext
            \(c\) (additional authenticated data can be accounted for in a
            straightforward manner). \(k_1, k_2\) are associated
            with authentication keys \(h_1, h_2\) and blinds \(s_1, s_2\), respectively.
        </p>
        <p>
            Given a ciphertext of three blocks, we will attempt to add a fourth ciphertext block \(c_3\), set the MACs equal to each other,
            and solve for \(c_3\). Remember that in \(\mathbb{F}_{2^{128}}\), addition is the same as subtraction.
        </p>
        <p>
            For the resulting ciphertext of four blocks, the MACs for each key are computed as
            \[
                mac_{1} = s_1 + \vert c\vert{}h_1 + c_3h_1^2 + c_2h_1^3 + c_1h_1^4 + c_0h_1^5
            \]
            \[
                mac_{2} = s_2 + \vert c\vert{}h_2 + c_3h_2^2 + c_2h_2^3 + c_1h_2^4 + c_0h_2^5
            \]
            where \(s\) is a constant depending on the AES-GCM key and the nonce, and \(h\)
            is the authentication key depending only on the AES-GCM key. We can now set the MACs equal to each other and solve for \(c_3\).
            \[
                s_1 + \vert c\vert{}h_1 + c_3h_1^2 + c_2h_1^3 + c_1h_1^4 + c_0h_1^5 = s_2 + \vert c\vert{}h_2 + c_3h_2^2 + c_2h_2^3 + c_1h_2^4 + c_0h_2^5
            \]
            \[
                c_3(h_1^2 + h_2^2) = (s_1 + s_2) + \vert c \vert (h_1 + h_2) + c_2(h_1^3+h_2^3) + c_1(h_1^4 + h_2^4) + c_0(h_1^5 + h_2^5)
            \]
            \[
                c_3 = \frac{(s_1 + s_2) + \vert c \vert (h_1 + h_2) + c_2(h_1^3+h_2^3) + c_1(h_1^4 + h_2^4) + c_0(h_1^5 + h_2^5)}{h_1^2 + h_2^2}
            \]
        </p>
        <p>
            Note that the choice to place the extra block in the final position was arbitrary. For the JPEG/BMP attack we will instead need
            to change the penultimate block rather than adding a block; the computation is similar.
        </p>
        <p>
            For the next phase, we construct a ciphertext that decrypts to one file under one key and another file under another.
            Recall that the ciphertext of AES-GCM, as in AES-CTR, is computed by taking the XOR of the keystream and the message. The keystream
            is computed from the cipher key and the nonce.
        </p>
        <p>
            First we will present the PDF and PDF collision (this is a new
            contribution); then we will present the JPEG and BMP collision
            (this was shown in the Invisible Salamanders paper).
        </p>
        <h4>PDF/PDF Collisions</h4>
        <p>
            The construction we show will not result in specification-valid PDFs, but nevertheless, most PDF viewers will render them as intended.
        </p>
        <p>
            The PDF file format starts with a header:
            <pre>
            %PDF-1.7
            %µ¶</pre>
            The header is followed by a sequence of objects. A simple object containing a stream is shown below, with the data
            for the object inserted at <code>[DATA]</code>.
            <pre>
            1 0 obj
            &lt;&lt;&gt;&gt;
            stream
            [DATA]
            endstream
            endobj</pre>
            At the end of the file, an <code>xref</code> table determines how the objects are layed out in space,
            and finally, the file ends with the line <code>%%EOF</code>, after which no more bytes are read by the PDF parser.
        </p>
        <p>
            Our strategy will be to place a new stream object at the beginning of the first PDF file. This object
            will include the entirety of the second PDF file. Because the
            <code>xref</code> table does not reference this new object, the
            first PDF will not attempt to render the additional data.
        </p>
        <p>
            Fix an arbitrary nonce \(n\) and key \(k_1\). We need our ciphertext header \(c_H\) to decrypt to the following
            plaintext \(m_H\) under \(k_1\). We choose the object ID <code>0 0
            obj</code> as it is meant to be reserved and unused.
            <pre>
            %PDF-1.7
            %µ¶

            0 0 obj
            &lt;&lt;&gt;&gt;
            stream</pre>
            Therefore we set \(c_H = \operatorname{AES-GCTR}(k_1, n, m_H)\),
            where \(\operatorname{AES-GCTR}\) returns the ciphertext portion of \(\operatorname{AES-GCM}\) but not the MAC.
        </p>
        <p>
            Since we will place the second PDF file afterwards, we need this header to be ignored when decrypted under the second key \(k_2\).
            PDF files can include comments, which start with <code>%</code> and extend until a newline <code>\n</code>, and these
            comments are ignored by the PDF parser.
        </p>
        <p>
			Randomly choose \(k_2\)s until the decryption of \(c_H\) under
			\(k_2\) yields a plaintext that starts with <code>%</code>,
            ends with <code>\n</code>, and does not contain any newlines in
            between. If we model the stream
            output as uniformly random, the expected number of attempts is
            \[ \frac{1}{\frac{1}{256}\frac{1}{256}\left(1-\frac{1}{256}\right)^{\vert m_H \vert-2}} \approx 76{,}045, \]
            which is possible in less than a minute on a desktop computer.
            Note that these keys and header are independent of the specific PDF files we wish
            to collide; thus, they can be precomputed.
        </p>
        <p>
            To the ciphertext header, we append the encryption of \(\textrm{PDF2}\) under \(k_2\).
            We need to end the stream object tag in the first PDF,
            so set \(m_E\) as
            <pre>
            endstream
            endobj</pre>
            and then append the encryption of \(m_2\) under \(k_1\), then the encryption of \(\textrm{PDF1}\)
            under \(k_1\).
        </p>
        <p>
            Finally, we pad the ciphertext with \(p\) bytes until it is a multiple of the block
            length (16 for AES-GCM), then append an extra block \(X\) so the MACs collide,
            as described earlier.
            Because PDF parsers stop reading immediately when
            they see <code>%%EOF</code>, the second PDF file will not be
            corrupted by the first PDF file appearing afterwards, nor will
            the first PDF file be corrupted by the MAC collision block appearing afterwards.
        </p>
        <p>
            We summarize the construction below.
            For the blank cells in the ciphertext row, use either the
            encryption of the first PDF cell under \(k_1\) or the second PDF cell under
            \(k_2\) as indicated.
            \[
                \begin{array}{|c|c|}\hline
                \mathsf{PDF 1} &m_H & &  m_{E} & \textrm{PDF1} &  \\
                C &     & & & & \mathtt{00}^{p} & X \\
                \mathsf{PDF 2} && \textrm{PDF2} & & & \\\hline
                \end{array}
            \]
        </p>
        <h4>JPEG/BMP Collisions</h4>
        <p>
            The basic strategy shown by the Invisible Salamanders paper is to
            place the JPEG bytes and BMP bytes at different locations,
            carefully arranging it so each parser will ignore the other data
            for the file. JPEG files can include comments, in which we will
            include the BMP data. The BMP parser will stop reading as soon as
            the indicated length of the BMP has been read, after which we will
            include the JPEG data. In each decrypted file, the data for the
            other image will be scrambled as we are using a different key, but
            it will not matter as the junk data will be in a location that is
            ignored by the image parser.
        </p>
        <p>
        All JPEG files start with the magic bytes \(\mathtt{ffd8}\) and end
        with \(\mathtt{ffd9}\). We will place a JPEG comment immediately
        after the initial magic bytes, which is indicated by \(\mathtt{fffe}\) and is followed by a 2-byte big-endian encoding of the comment length \(J\).
            Let \(J_i\) indicate the \(i\)th byte of \(J\); \(J_0\) being the least significant byte.
        </p>
        <p>
            All BMP files start with the magic bytes \(\mathtt{424d}\) followed by a 4-byte little-endian encoding of the file length.
            Because we need the BMP file to fit inside the JPEG comment, we set
            \[
                \begin{array}{|c|c|}\hline
                & 0 & 1 & 2 & 3 & 4 & 5 & \ldots & -2 & -1 \\\hline
                \mathsf{JPEG} & \mathtt{ff} & \mathtt{d8} & \mathtt{ff} & \mathtt{fe} & J_1 & J_0 & \ldots & \mathtt{ff} & \mathtt{d9} \\
                \mathsf{BMP} & \mathtt{42} & \mathtt{4d} & J_0 & J_1 & \mathtt{00} & \mathtt{00} & \ldots & & \\\hline
                \end{array}
            \]
        </p>
        <p>
            In addition to the file length at the beginning, BMP files also
            include the size of the color array (the pixels of the image) in
            the initial metadata. BMP parsers ignore any data after the color
            array is supposed to be over, even if the file length has not been
            exhausted yet. That means we can set \(J=\mathtt{ffff}=65{,}536\), and the
            resulting header will be valid for any BMP file less than \(J\) bytes.
        </p>
        <p>
            Since these headers must be in the same location at the start of the file,
            we search for two keys \(k_1, k_2\) and a nonce \(n\) such that
            \[ \operatorname{AES-GCTR}(k_1, n, \mathtt{ffd8fffeffff}) = \operatorname{AES-GCTR}(k_2, n, \mathtt{424dffff0000}), \]
            where \(\operatorname{AES-GCTR}\) returns the ciphertext portion of \(\operatorname{AES-GCM}\) but not the MAC.
        </p>
        <p>
            The easiest way to do this is via a
            birthday attack: fix an arbitrary nonce, then generate random keys
            for both the JPEG header and the BMP header. Encrypt each and store
            the ciphertext in a lookup table. Repeat until two keys are found that
            encrypt their respective headers to the same ciphertext bytes. The search
            takes less than a minute on a desktop computer.
        </p>
        <p>
            We have now computed the ciphertext header \(c_{H}\) and two keys
            which will decrypt it to the correct header bytes for both files.
            Note that \(c_{H}\) only depends on the <em>maximum</em> size of
            the BMP file, and thus can be precomputed.  The remainder of the
            attack that depends on the specific images is very fast.
        </p>
        <p>
			As explained before, we place the BMP bytes in the JPEG comment,
			add padding to finish the comment, and add the JPEG bytes after the comment is over.
            Below we show the structure of the ciphertext. For the blank cells in the ciphertext row,
            use either the encryption of the JPEG cell under \(k_1\) or the BMP cell under \(k_2\) as indicated.
            \[
                \begin{array}{|c|c|}\hline
				\mathsf{JPEG} && &  & \textrm{JPEG} & \mathtt{ffd9} \\
                C & c_H & & \mathtt{00}^{J-\vert \textrm{BMP}\vert}& & \\
                \mathsf{BMP} && \textrm{BMP} & & & \\\hline
                \end{array}
            \]
        </p>
        <p>
            Here, \(\textrm{JPEG}\) is the bytes of the JPEG file without the initial and final magic bytes,
            and similarly \(\textrm{BMP}\) is the bytes of the BMP file without the initial magic bytes.
        </p>
        <p>
            These ciphertexts do not have the same MAC yet. If we tried to use
            the strategy outlined at the beginning where we add an extra block
            at the end, the JPEG file would no longer end in \(\mathtt{ffd9}\)
            and would be invalid. Instead, we modify it to change
            the penultimate block. The collision algorithm will result in a
            ciphertext block \(X\).
        </p>
        <p>
            However, we don't want any data from the penultimate block to
            corrupt our JPEG image. After \(\textrm{JPEG}\) ends, we start
            another comment that will include the penultimate block, hiding it
            from the parser. Care must be taken to ensure the penultimate block
            really is on a block boundary. For AES-GCM, the block size is 16 bytes.
        </p>
        <p>
            Below is the final structure of the polyglot ciphertext.
            \[
            j' = 16 - (6 + J + \vert \textrm{JPEG} \vert + 2 + 2) \pmod{16}
            \]
            \[
            J' = j' + 16 + 14
            \]
            \[
                \begin{array}{|c|c|}\hline
				\mathsf{JPEG} && &  & \mathrm{JPEG} & \mathtt{fffe} & J' &  &  &  & \mathtt{ffd9} \\
                C & c_{H} &  & \mathtt{00}^{J-\vert \textrm{BMP}\vert}&  &  &  &\mathtt{00}^{J'-30} & X & \mathtt{00}^{14}&  \\
                \mathsf{BMP} && \textrm{BMP} & & \\\hline
                \end{array}
            \]
        </p>
        </details>
		<details>
			<summary>
                Show me the code.
			</summary>
        <pre>
from <a href="/git/forbidden-salamanders">aesgcmanalysis</a> import att_merge_jpg_bmp

with open('first.jpg', 'rb') as h:
    jpg = h.read()
with open('second.bmp', 'rb') as h:
    bmp = h.read()
c, mac = att_merge_jpg_bmp(jpg, bmp, aad=b"")</pre></details>
    <script id="MathJax-script" async src="/forbidden-salamanders/static/tex-chtml.js"></script>
  </body>
</html>