tor-commits
Threads by month
- ----- 2025 -----
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
July 2012
- 14 participants
- 949 discussions
commit 576f50ab3f776af861f6822f9b8b37c7f1ef0ba8
Author: Zack Weinberg <zackw(a)cmu.edu>
Date: Tue Mar 20 20:47:23 2012 -0700
fix another OSX build issue >_<
---
src/rng.cc | 3 +++
1 files changed, 3 insertions(+), 0 deletions(-)
diff --git a/src/rng.cc b/src/rng.cc
index f5c0ca4..4901c33 100644
--- a/src/rng.cc
+++ b/src/rng.cc
@@ -9,6 +9,9 @@
#include <math.h>
#include <cryptopp/osrng.h>
+/* not sure why, but on OSX we only see std::isnan, not ::isnan */
+using std::isnan;
+
/* Note: this file wraps a C++ library into a C-style program and must
insulate that program from C++ semantics it is not prepared to handle;
most importantly, all exceptions must be converted to error codes. */
1
0
commit 0e8274174b022edc2c92b00b89955b4796d68c82
Author: Zack Weinberg <zackw(a)panix.com>
Date: Wed Mar 21 09:00:09 2012 -0700
Now compiles on OSX _and_ Linux
---
src/rng.cc | 14 +++++++++-----
1 files changed, 9 insertions(+), 5 deletions(-)
diff --git a/src/rng.cc b/src/rng.cc
index 4901c33..9b7ff57 100644
--- a/src/rng.cc
+++ b/src/rng.cc
@@ -6,12 +6,9 @@
#include "rng.h"
#include <limits>
-#include <math.h>
+#include <cmath>
#include <cryptopp/osrng.h>
-/* not sure why, but on OSX we only see std::isnan, not ::isnan */
-using std::isnan;
-
/* Note: this file wraps a C++ library into a C-style program and must
insulate that program from C++ semantics it is not prepared to handle;
most importantly, all exceptions must be converted to error codes. */
@@ -202,6 +199,13 @@ rng_double()
int
rng_range_geom(unsigned int hi, unsigned int xv)
{
+ using std::exp;
+ using std::log;
+ using std::floor;
+ using std::isnan;
+ using std::min;
+ using std::max;
+
log_assert(hi <= ((unsigned int)INT_MAX)+1);
log_assert(0 < xv && xv < hi);
@@ -228,5 +232,5 @@ rng_range_geom(unsigned int hi, unsigned int xv)
/* Round down for the geometric distribution, and clamp to [0, hi)
for great defensiveness. */
- return std::min(hi-1, std::max(0U, (unsigned int)floor(T)));
+ return min(hi-1, max(0U, (unsigned int)floor(T)));
}
1
0
commit 3ae30293e91c5b77dd29306045468682d4e3656e
Author: Zack Weinberg <zackw(a)panix.com>
Date: Sat Mar 24 21:16:16 2012 -0700
Crypto tweaks post-new chopper.
* It is now possible to create an encryption or decryption object directly
from a key generator.
* The dangerous, no longer required decrypt_unchecked() method has been
removed.
---
src/crypt.cc | 86 +++++++++++++++++++++++++++++++++++--------
src/crypt.h | 30 +++++++++++----
src/protocol/chop.cc | 33 ++++-------------
src/test/unittest_crypt.cc | 5 ---
4 files changed, 100 insertions(+), 54 deletions(-)
diff --git a/src/crypt.cc b/src/crypt.cc
index e81e8d4..f0f9c4f 100644
--- a/src/crypt.cc
+++ b/src/crypt.cc
@@ -14,6 +14,7 @@
#include <cryptopp/hmac.h>
#include <cryptopp/gcm.h>
#include <cryptopp/sha.h>
+#include <cryptopp/secblock.h>
// work around a bug in crypto++ 5.6.0's pwdbased.h
#if CRYPTOPP_VERSION < 561
@@ -29,6 +30,9 @@ public:
#include <cryptopp/pwdbased.h>
+// 32 bytes -> no extra memory allocation even for big keys, hopefully
+typedef CryptoPP::SecBlockWithHint<byte, 32> KeyBlock;
+
/* Note: this file wraps a C++ library into a C-style program and must
insulate that program from C++ semantics it is not prepared to handle;
most importantly, all exceptions must be converted to error codes. */
@@ -72,6 +76,21 @@ ecb_encryptor::create(const uint8_t *key, size_t keylen)
CATCH_ALL_EXCEPTIONS(0);
}
+ecb_encryptor *
+ecb_encryptor::create(key_generator *gen, size_t keylen)
+{
+ try {
+ KeyBlock key(keylen);
+ size_t got = gen->generate(key, keylen);
+ log_assert(got == keylen);
+
+ ecb_encryptor_impl *enc = new ecb_encryptor_impl;
+ enc->ctx.SetKey(key, keylen);
+ return enc;
+ }
+ CATCH_ALL_EXCEPTIONS(0);
+}
+
ecb_decryptor *
ecb_decryptor::create(const uint8_t *key, size_t keylen)
{
@@ -83,6 +102,21 @@ ecb_decryptor::create(const uint8_t *key, size_t keylen)
CATCH_ALL_EXCEPTIONS(0);
}
+ecb_decryptor *
+ecb_decryptor::create(key_generator *gen, size_t keylen)
+{
+ try {
+ KeyBlock key(keylen);
+ size_t got = gen->generate(key, keylen);
+ log_assert(got == keylen);
+
+ ecb_decryptor_impl *dec = new ecb_decryptor_impl;
+ dec->ctx.SetKey(key, keylen);
+ return dec;
+ }
+ CATCH_ALL_EXCEPTIONS(0);
+}
+
ecb_encryptor::~ecb_encryptor() {}
ecb_encryptor_impl::~ecb_encryptor_impl() {}
ecb_decryptor::~ecb_decryptor() {}
@@ -121,9 +155,6 @@ namespace {
virtual ~gcm_decryptor_impl();
virtual int decrypt(uint8_t *out, const uint8_t *in, size_t inlen,
const uint8_t *nonce, size_t nlen);
- virtual void decrypt_unchecked(uint8_t *out,
- const uint8_t *in, size_t inlen,
- const uint8_t *nonce, size_t nlen);
};
}
@@ -141,6 +172,24 @@ gcm_encryptor::create(const uint8_t *key, size_t keylen)
CATCH_ALL_EXCEPTIONS(0);
}
+gcm_encryptor *
+gcm_encryptor::create(key_generator *gen, size_t keylen)
+{
+ try {
+ KeyBlock key(keylen);
+ size_t got = gen->generate(key, keylen);
+ log_assert(got == keylen);
+
+ gcm_encryptor_impl *enc = new gcm_encryptor_impl;
+ // sadly, these are not checkable at compile time
+ log_assert(enc->ctx.DigestSize() == GCM_TAG_LEN);
+ log_assert(!enc->ctx.NeedsPrespecifiedDataLengths());
+ enc->ctx.SetKeyWithIV(key, keylen, dummy_iv, sizeof dummy_iv);
+ return enc;
+ }
+ CATCH_ALL_EXCEPTIONS(0);
+}
+
gcm_decryptor *
gcm_decryptor::create(const uint8_t *key, size_t keylen)
{
@@ -155,6 +204,24 @@ gcm_decryptor::create(const uint8_t *key, size_t keylen)
CATCH_ALL_EXCEPTIONS(0);
}
+gcm_decryptor *
+gcm_decryptor::create(key_generator *gen, size_t keylen)
+{
+ try {
+ KeyBlock key(keylen);
+ size_t got = gen->generate(key, keylen);
+ log_assert(got == keylen);
+
+ gcm_decryptor_impl *dec = new gcm_decryptor_impl;
+ // sadly, these are not checkable at compile time
+ log_assert(dec->ctx.DigestSize() == GCM_TAG_LEN);
+ log_assert(!dec->ctx.NeedsPrespecifiedDataLengths());
+ dec->ctx.SetKeyWithIV(key, keylen, dummy_iv, sizeof dummy_iv);
+ return dec;
+ }
+ CATCH_ALL_EXCEPTIONS(0);
+}
+
gcm_encryptor::~gcm_encryptor() {}
gcm_encryptor_impl::~gcm_encryptor_impl() {}
gcm_decryptor::~gcm_decryptor() {}
@@ -184,19 +251,6 @@ gcm_decryptor_impl::decrypt(uint8_t *out, const uint8_t *in, size_t inlen,
CATCH_ALL_EXCEPTIONS(-1);
}
-void
-gcm_decryptor_impl::decrypt_unchecked(uint8_t *out,
- const uint8_t *in, size_t inlen,
- const uint8_t *nonce, size_t nlen)
-{
- try {
- // there is no convenience function for this
- this->ctx.Resynchronize(nonce, nlen);
- this->ctx.ProcessData(out, in, inlen);
- }
- CATCH_ALL_EXCEPTIONS();
-}
-
typedef CryptoPP::HMAC<CryptoPP::SHA256> HMAC_SHA256;
typedef CryptoPP::PKCS5_PBKDF2_HMAC<CryptoPP::SHA256> PBKDF2_SHA256;
const size_t SHA256_LEN = CryptoPP::SHA256::DIGESTSIZE;
diff --git a/src/crypt.h b/src/crypt.h
index c0084e9..4136a35 100644
--- a/src/crypt.h
+++ b/src/crypt.h
@@ -8,6 +8,8 @@
const size_t AES_BLOCK_LEN = 16;
const size_t GCM_TAG_LEN = 16;
+struct key_generator;
+
struct ecb_encryptor
{
ecb_encryptor() {}
@@ -17,6 +19,11 @@ struct ecb_encryptor
as the symmetric key. 'keylen' must be 16, 24, or 32 bytes. */
static ecb_encryptor *create(const uint8_t *key, size_t keylen);
+ /** Return a new AES/ECB encryption state, generating a key of
+ length 'keylen' from the key generator 'gen'. 'keylen' must be
+ 16, 24, or 32 bytes. */
+ static ecb_encryptor *create(key_generator *gen, size_t keylen);
+
/** Encrypt exactly AES_BLOCK_LEN bytes of data in the buffer 'in' and
write the result to 'out'. */
virtual void encrypt(uint8_t *out, const uint8_t *in) = 0;
@@ -35,6 +42,11 @@ struct ecb_decryptor
as the symmetric key. 'keylen' must be 16, 24, or 32 bytes. */
static ecb_decryptor *create(const uint8_t *key, size_t keylen);
+ /** Return a new AES/ECB decryption state, generating a key of
+ length 'keylen' from the key generator 'gen'. 'keylen' must be
+ 16, 24, or 32 bytes. */
+ static ecb_decryptor *create(key_generator *gen, size_t keylen);
+
/** Decrypt exactly AES_BLOCK_LEN bytes of data in the buffer 'in' and
write the result to 'out'. */
virtual void decrypt(uint8_t *out, const uint8_t *in) = 0;
@@ -54,6 +66,11 @@ struct gcm_encryptor
as the symmetric key. 'keylen' must be 16, 24, or 32 bytes. */
static gcm_encryptor *create(const uint8_t *key, size_t keylen);
+ /** Return a new AES/GCM encryption state, generating a key of
+ length 'keylen' from the key generator 'gen'. 'keylen' must be
+ 16, 24, or 32 bytes. */
+ static gcm_encryptor *create(key_generator *gen, size_t keylen);
+
/** Encrypt 'inlen' bytes of data in the buffer 'in', writing the
result plus an authentication tag to the buffer 'out', whose
length must be at least 'inlen'+16 bytes. Use 'nonce'
@@ -76,6 +93,11 @@ struct gcm_decryptor
as the symmetric key. 'keylen' must be 16, 24, or 32 bytes. */
static gcm_decryptor *create(const uint8_t *key, size_t keylen);
+ /** Return a new AES/GCM decryption state, generating a key of
+ length 'keylen' from the key generator 'gen'. 'keylen' must be
+ 16, 24, or 32 bytes. */
+ static gcm_decryptor *create(key_generator *gen, size_t keylen);
+
/** Decrypt 'inlen' bytes of data in the buffer 'in'; the last 16
bytes of this buffer are assumed to be the authentication tag.
Write the result to the buffer 'out', whose length must be at
@@ -85,14 +107,6 @@ struct gcm_decryptor
virtual int decrypt(uint8_t *out, const uint8_t *in, size_t inlen,
const uint8_t *nonce, size_t nlen) = 0;
- /** Decrypt 'inlen' bytes of data in the buffer 'in' WITHOUT
- CHECKING THE AUTHENTICATION TAG. Arguments same as decrypt().
- This should be used only to decode just enough of an incoming
- block to know how long it's going to be and therefore where the
- tag begins. */
- virtual void decrypt_unchecked(uint8_t *out, const uint8_t *in, size_t inlen,
- const uint8_t *nonce, size_t nlen) = 0;
-
private:
gcm_decryptor(const gcm_decryptor&) DELETE_METHOD;
gcm_decryptor& operator=(const gcm_decryptor&) DELETE_METHOD;
diff --git a/src/protocol/chop.cc b/src/protocol/chop.cc
index e81214a..44e79a3 100644
--- a/src/protocol/chop.cc
+++ b/src/protocol/chop.cc
@@ -512,32 +512,17 @@ chop_config_t::circuit_create(size_t)
key_generator::from_passphrase((const uint8_t *)passphrase,
sizeof(passphrase) - 1,
0, 0, 0, 0);
- uint8_t kbuf[16];
if (mode == LSN_SIMPLE_SERVER) {
- log_assert(kgen->generate(kbuf, 16) == 16);
- ckt->send_crypt = gcm_encryptor::create(kbuf, 16);
-
- log_assert(kgen->generate(kbuf, 16) == 16);
- ckt->send_hdr_crypt = ecb_encryptor::create(kbuf, 16);
-
- log_assert(kgen->generate(kbuf, 16) == 16);
- ckt->recv_crypt = gcm_decryptor::create(kbuf, 16);
-
- log_assert(kgen->generate(kbuf, 16) == 16);
- ckt->recv_hdr_crypt = ecb_decryptor::create(kbuf, 16);
+ ckt->send_crypt = gcm_encryptor::create(kgen, 16);
+ ckt->send_hdr_crypt = ecb_encryptor::create(kgen, 16);
+ ckt->recv_crypt = gcm_decryptor::create(kgen, 16);
+ ckt->recv_hdr_crypt = ecb_decryptor::create(kgen, 16);
} else {
- log_assert(kgen->generate(kbuf, 16) == 16);
- ckt->recv_crypt = gcm_decryptor::create(kbuf, 16);
-
- log_assert(kgen->generate(kbuf, 16) == 16);
- ckt->recv_hdr_crypt = ecb_decryptor::create(kbuf, 16);
-
- log_assert(kgen->generate(kbuf, 16) == 16);
- ckt->send_crypt = gcm_encryptor::create(kbuf, 16);
-
- log_assert(kgen->generate(kbuf, 16) == 16);
- ckt->send_hdr_crypt = ecb_encryptor::create(kbuf, 16);
+ ckt->recv_crypt = gcm_decryptor::create(kgen, 16);
+ ckt->recv_hdr_crypt = ecb_decryptor::create(kgen, 16);
+ ckt->send_crypt = gcm_encryptor::create(kgen, 16);
+ ckt->send_hdr_crypt = ecb_encryptor::create(kgen, 16);
std::pair<chop_circuit_table::iterator, bool> out;
do {
@@ -551,7 +536,6 @@ chop_config_t::circuit_create(size_t)
out.first->second = ckt;
}
- memset(kbuf, 0, 16);
delete kgen;
return ckt;
}
@@ -1027,7 +1011,6 @@ chop_circuit_t::check_for_eof()
conn_send_eof(conn);
}
}
-
// If we're the client we have to keep trying to talk as long as we
// haven't both sent and received a FIN, or we might deadlock.
diff --git a/src/test/unittest_crypt.cc b/src/test/unittest_crypt.cc
index b903c11..2e50623 100644
--- a/src/test/unittest_crypt.cc
+++ b/src/test/unittest_crypt.cc
@@ -762,11 +762,6 @@ test_crypt_aesgcm_good_dec(void *)
tt_int_op(rv, ==, 0);
tt_mem_op(obuf, ==, testvecs[i].pt, testvecs[i].len);
- c->decrypt_unchecked(obuf,
- (const uint8_t *)testvecs[i].ct, testvecs[i].len,
- (const uint8_t *)testvecs[i].iv, 16);
- tt_mem_op(obuf, ==, testvecs[i].pt, testvecs[i].len);
-
delete c;
}
c = 0;
1
0
commit 4c7ad95c5fbc84a3b4b7ffaa5ff1c6dd90a85f53
Author: Zack Weinberg <zackw(a)cmu.edu>
Date: Tue Mar 20 21:00:42 2012 -0700
kludge _another_ OSX issue
---
src/crypt.cc | 6 +++++-
1 files changed, 5 insertions(+), 1 deletions(-)
diff --git a/src/crypt.cc b/src/crypt.cc
index 402ae58..d6eed49 100644
--- a/src/crypt.cc
+++ b/src/crypt.cc
@@ -5,6 +5,11 @@
#include "util.h"
#include "crypt.h"
+// temporary, I hope, kludge
+#ifdef __APPLE_CC__
+#define CRYPTOPP_DISABLE_X86ASM
+#endif
+
#include <cryptopp/aes.h>
#include <cryptopp/hmac.h>
#include <cryptopp/gcm.h>
@@ -101,7 +106,6 @@ ecb_decryptor_impl::decrypt(uint8_t *out, const uint8_t *in)
CATCH_ALL_EXCEPTIONS();
}
-
namespace {
struct gcm_encryptor_impl : gcm_encryptor
{
1
0

[stegotorus/master] Switch back to OpenSSL as cryptography library.
by zwol@torproject.org 20 Jul '12
by zwol@torproject.org 20 Jul '12
20 Jul '12
commit 069607870199763ab539fc7d458646a6c3ee5f4b
Author: Zack Weinberg <zackw(a)panix.com>
Date: Tue Mar 27 21:02:20 2012 -0700
Switch back to OpenSSL as cryptography library.
---
README.Linda | 4 +-
configure.ac | 9 +-
src/audit-globals.sh | 2 +
src/crypt.cc | 598 +++++++++++++++++++++++++++-----------------
src/crypt.h | 1 +
src/rng.cc | 260 +++++++++----------
src/rng.h | 2 +-
src/test/unittest.cc | 6 +-
src/test/unittest_crypt.cc | 6 +-
src/util.h | 1 +
10 files changed, 515 insertions(+), 374 deletions(-)
diff --git a/README.Linda b/README.Linda
index 83e0dad..3cb4d2e 100644
--- a/README.Linda
+++ b/README.Linda
@@ -22,9 +22,9 @@ automake (GNU automake) 1.11.1
$> sudo port install pkgconfig
- d) libcrypto++, libevent-2,
+ d) openssl >= 1.0.1, libevent-2
-$> sudo port install libcryptopp
+$> sudo port install openssl
$> sudo port install libevent
e) tor
diff --git a/configure.ac b/configure.ac
index d491479..9002078 100644
--- a/configure.ac
+++ b/configure.ac
@@ -43,16 +43,17 @@ AM_CONDITIONAL([INTEGRATION_TESTS], [test "$PYOS" = "posix"])
### Libraries ###
-# libcrypto++ 5.6.0 is the earliest version with GCM support.
-PKG_CHECK_MODULES([libcryptopp], [libcrypto++ >= 5.6.0])
+# Presently no need for libssl, only libcrypto.
+# We require version 1.0.1 for GCM support.
+PKG_CHECK_MODULES([libcrypto], [libcrypto >= 1.0.1])
# libevent 2.0 radically changed the API
PKG_CHECK_MODULES([libevent], [libevent >= 2.0])
# there's no good reason not to require the latest zlib, which is
# from 2009
PKG_CHECK_MODULES([libz], [zlib >= 1.2.3.4])
-LIBS="$libevent_LIBS $libcryptopp_LIBS $libz_LIBS"
-lib_CPPFLAGS="$libevent_CFLAGS $libcryptopp_CFLAGS $libz_CFLAGS"
+LIBS="$libevent_LIBS $libcrypto_LIBS $libz_LIBS"
+lib_CPPFLAGS="$libevent_CFLAGS $libcrypto_CFLAGS $libz_CFLAGS"
AC_SUBST(lib_CPPFLAGS)
# ntohl and a bunch of related functions require a special library on Windows.
diff --git a/src/audit-globals.sh b/src/audit-globals.sh
index e1de07d..ec08b21 100644
--- a/src/audit-globals.sh
+++ b/src/audit-globals.sh
@@ -40,6 +40,8 @@ sed '
/^util log_dest$/d
/^util log_min_sev$/d
/^util the_evdns_base$/d
+ /^crypt log_crypto()::initialized$/d
+ /^crypt init_crypto()::initialized$/d
# These are grandfathered; they need to be removed.
/^steg\/embed embed_init$/d
diff --git a/src/crypt.cc b/src/crypt.cc
index f0f9c4f..d964708 100644
--- a/src/crypt.cc
+++ b/src/crypt.cc
@@ -5,61 +5,137 @@
#include "util.h"
#include "crypt.h"
-// temporary, I hope, kludge
-#ifdef __APPLE_CC__
-#define CRYPTOPP_DISABLE_X86ASM
-#endif
-
-#include <cryptopp/aes.h>
-#include <cryptopp/hmac.h>
-#include <cryptopp/gcm.h>
-#include <cryptopp/sha.h>
-#include <cryptopp/secblock.h>
-
-// work around a bug in crypto++ 5.6.0's pwdbased.h
-#if CRYPTOPP_VERSION < 561
-namespace CryptoPP {
-class Integer {
-public:
- Integer& operator++();
- Integer& operator+(Integer const&);
- void Encode(...);
-};
+#include <openssl/engine.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/hmac.h>
+
+static void
+init_crypto()
+{
+ static bool initialized = false;
+ if (!initialized) {
+ initialized = true;
+ ENGINE_load_builtin_engines();
+ ENGINE_register_all_complete();
+ atexit(ENGINE_cleanup);
+
+ // we don't need to call OpenSSL_add_all_algorithms or EVP_cleanup,
+ // since we never look up ciphers by textual name.
+ }
}
-#endif
-#include <cryptopp/pwdbased.h>
+static void
+log_crypto()
+{
+ static bool initialized = false;
+ if (!initialized) {
+ initialized = true;
+ ERR_load_crypto_strings();
+ atexit(ERR_free_strings);
+ }
+ unsigned long err;
+ while ((err = ERR_get_error()) != 0)
+ log_warn("%s: %s: %s",
+ ERR_lib_error_string(err),
+ ERR_func_error_string(err),
+ ERR_reason_error_string(err));
+}
-// 32 bytes -> no extra memory allocation even for big keys, hopefully
-typedef CryptoPP::SecBlockWithHint<byte, 32> KeyBlock;
+static void ATTR_NORETURN
+log_crypto_abort(const char *msg)
+{
+ log_crypto();
+ log_abort("libcrypto error in %s", msg);
+}
-/* Note: this file wraps a C++ library into a C-style program and must
- insulate that program from C++ semantics it is not prepared to handle;
- most importantly, all exceptions must be converted to error codes. */
+static int
+log_crypto_warn(const char *msg)
+{
+ log_crypto();
+ log_warn("libcrypto error in %s", msg);
+ return -1;
+}
-#define CATCH_ALL_EXCEPTIONS(rv) \
- catch (std::exception& e) { \
- log_warn("%s: %s", __func__, e.what()); \
- } catch (...) { \
- log_warn("%s: exception of abnormal type", __func__); \
- } \
- return rv /* deliberate absence of semicolon */
+static const EVP_CIPHER *
+aes_ecb_by_size(size_t keylen)
+{
+ switch (keylen) {
+ case 16: return EVP_aes_128_ecb();
+ case 24: return EVP_aes_192_ecb();
+ case 32: return EVP_aes_256_ecb();
+ default:
+ log_abort("AES only supports 16, 24, or 32-byte keys");
+ }
+}
-// Crypto++ doesn't let us set a key without also setting an IV,
-// even though we will always override the IV later.
-static const uint8_t dummy_iv[16] = {};
+static const EVP_CIPHER *
+aes_gcm_by_size(size_t keylen)
+{
+ switch (keylen) {
+ case 16: return EVP_aes_128_gcm();
+ case 24: return EVP_aes_192_gcm();
+ case 32: return EVP_aes_256_gcm();
+ default:
+ log_abort("AES only supports 16, 24, or 32-byte keys");
+ }
+}
namespace {
+
+ // loosely based on crypto++'s SecByteBlock
+ class MemBlock {
+ public:
+ explicit MemBlock(size_t l) : data(new uint8_t[l]), len(l)
+ { memset(data, 0, l); }
+
+ MemBlock(const uint8_t *d, size_t l) : data(new uint8_t[l]), len(l)
+ { if (d) memcpy(data, d, l); else memset(data, 0, l); }
+
+ ~MemBlock()
+ { memset(data, 0, len); delete [] data; }
+
+ operator const void*() const
+ { return data; }
+ operator void*()
+ { return data; }
+
+ operator const uint8_t*() const
+ { return data; }
+ operator uint8_t*()
+ { return data; }
+
+ const uint8_t *begin() const
+ { return data; }
+ uint8_t *begin()
+ { return data; }
+
+ const uint8_t *end() const
+ { return data+len; }
+ uint8_t *end()
+ { return data+len; }
+
+ size_t size() const { return len; }
+
+ private:
+ MemBlock(MemBlock const&) DELETE_METHOD;
+
+ uint8_t *data;
+ size_t len;
+ };
+
struct ecb_encryptor_impl : ecb_encryptor
{
- CryptoPP::ECB_Mode<CryptoPP::AES>::Encryption ctx;
+ EVP_CIPHER_CTX ctx;
+ ecb_encryptor_impl() { EVP_CIPHER_CTX_init(&ctx); }
virtual ~ecb_encryptor_impl();
virtual void encrypt(uint8_t *out, const uint8_t *in);
};
struct ecb_decryptor_impl : ecb_decryptor
{
- CryptoPP::ECB_Mode<CryptoPP::AES>::Decryption ctx;
+ EVP_CIPHER_CTX ctx;
+ ecb_decryptor_impl() { EVP_CIPHER_CTX_init(&ctx); }
virtual ~ecb_decryptor_impl();
virtual void decrypt(uint8_t *out, const uint8_t *in);
};
@@ -68,82 +144,98 @@ namespace {
ecb_encryptor *
ecb_encryptor::create(const uint8_t *key, size_t keylen)
{
- try {
- ecb_encryptor_impl *enc = new ecb_encryptor_impl;
- enc->ctx.SetKey(key, keylen);
- return enc;
- }
- CATCH_ALL_EXCEPTIONS(0);
+ init_crypto();
+
+ ecb_encryptor_impl *enc = new ecb_encryptor_impl;
+ if (!EVP_EncryptInit_ex(&enc->ctx, aes_ecb_by_size(keylen), 0, key, 0))
+ log_crypto_abort("ecb_encryptor::create");
+ if (!EVP_CIPHER_CTX_set_padding(&enc->ctx, 0))
+ log_crypto_abort("ecb_encryptor::disable padding");
+
+ return enc;
}
ecb_encryptor *
ecb_encryptor::create(key_generator *gen, size_t keylen)
{
- try {
- KeyBlock key(keylen);
- size_t got = gen->generate(key, keylen);
- log_assert(got == keylen);
-
- ecb_encryptor_impl *enc = new ecb_encryptor_impl;
- enc->ctx.SetKey(key, keylen);
- return enc;
- }
- CATCH_ALL_EXCEPTIONS(0);
+ init_crypto();
+
+ MemBlock key(keylen);
+ size_t got = gen->generate(key, keylen);
+ log_assert(got == keylen);
+
+ ecb_encryptor_impl *enc = new ecb_encryptor_impl;
+ if (!EVP_EncryptInit_ex(&enc->ctx, aes_ecb_by_size(keylen), 0, key, 0))
+ log_crypto_abort("ecb_encryptor::create");
+ if (!EVP_CIPHER_CTX_set_padding(&enc->ctx, 0))
+ log_crypto_abort("ecb_encryptor::disable padding");
+
+ return enc;
}
ecb_decryptor *
ecb_decryptor::create(const uint8_t *key, size_t keylen)
{
- try {
- ecb_decryptor_impl *dec = new ecb_decryptor_impl;
- dec->ctx.SetKey(key, keylen);
- return dec;
- }
- CATCH_ALL_EXCEPTIONS(0);
+ init_crypto();
+
+ ecb_decryptor_impl *dec = new ecb_decryptor_impl;
+ if (!EVP_DecryptInit_ex(&dec->ctx, aes_ecb_by_size(keylen), 0, key, 0))
+ log_crypto_abort("ecb_decryptor::create");
+ if (!EVP_CIPHER_CTX_set_padding(&dec->ctx, 0))
+ log_crypto_abort("ecb_decryptor::disable padding");
+
+ return dec;
}
ecb_decryptor *
ecb_decryptor::create(key_generator *gen, size_t keylen)
{
- try {
- KeyBlock key(keylen);
- size_t got = gen->generate(key, keylen);
- log_assert(got == keylen);
-
- ecb_decryptor_impl *dec = new ecb_decryptor_impl;
- dec->ctx.SetKey(key, keylen);
- return dec;
- }
- CATCH_ALL_EXCEPTIONS(0);
+ init_crypto();
+
+ MemBlock key(keylen);
+ size_t got = gen->generate(key, keylen);
+ log_assert(got == keylen);
+
+ ecb_decryptor_impl *dec = new ecb_decryptor_impl;
+ if (!EVP_DecryptInit_ex(&dec->ctx, aes_ecb_by_size(keylen), 0, key, 0))
+ log_crypto_abort("ecb_decryptor::create");
+ if (!EVP_CIPHER_CTX_set_padding(&dec->ctx, 0))
+ log_crypto_abort("ecb_decryptor::disable padding");
+
+ return dec;
}
ecb_encryptor::~ecb_encryptor() {}
-ecb_encryptor_impl::~ecb_encryptor_impl() {}
+ecb_encryptor_impl::~ecb_encryptor_impl()
+{ EVP_CIPHER_CTX_cleanup(&ctx); }
+
ecb_decryptor::~ecb_decryptor() {}
-ecb_decryptor_impl::~ecb_decryptor_impl() {}
+ecb_decryptor_impl::~ecb_decryptor_impl()
+{ EVP_CIPHER_CTX_cleanup(&ctx); }
void
ecb_encryptor_impl::encrypt(uint8_t *out, const uint8_t *in)
{
- try {
- this->ctx.ProcessData(out, in, AES_BLOCK_LEN);
- }
- CATCH_ALL_EXCEPTIONS();
+ int olen;
+ if (!EVP_EncryptUpdate(&ctx, out, &olen, in, AES_BLOCK_LEN) ||
+ size_t(olen) != AES_BLOCK_LEN)
+ log_crypto_abort("ecb_encryptor::encrypt");
}
void
ecb_decryptor_impl::decrypt(uint8_t *out, const uint8_t *in)
{
- try {
- this->ctx.ProcessData(out, in, AES_BLOCK_LEN);
- }
- CATCH_ALL_EXCEPTIONS();
+ int olen;
+ if (!EVP_DecryptUpdate(&ctx, out, &olen, in, AES_BLOCK_LEN) ||
+ size_t(olen) != AES_BLOCK_LEN)
+ log_crypto_abort("ecb_decryptor::decrypt");
}
namespace {
struct gcm_encryptor_impl : gcm_encryptor
{
- CryptoPP::GCM<CryptoPP::AES>::Encryption ctx;
+ EVP_CIPHER_CTX ctx;
+ gcm_encryptor_impl() { EVP_CIPHER_CTX_init(&ctx); }
virtual ~gcm_encryptor_impl();
virtual void encrypt(uint8_t *out, const uint8_t *in, size_t inlen,
const uint8_t *nonce, size_t nlen);
@@ -151,158 +243,202 @@ namespace {
struct gcm_decryptor_impl : gcm_decryptor
{
- CryptoPP::GCM<CryptoPP::AES>::Decryption ctx;
+ EVP_CIPHER_CTX ctx;
+ gcm_decryptor_impl() { EVP_CIPHER_CTX_init(&ctx); }
virtual ~gcm_decryptor_impl();
virtual int decrypt(uint8_t *out, const uint8_t *in, size_t inlen,
const uint8_t *nonce, size_t nlen);
};
}
+// It *appears* (from inspecting the guts of libcrypto, *not* from the
+// documentation) that - at least for AES-GCM - it is legitimate to
+// call EVP_EncryptInit once with a key but no IV, and then once per
+// block with an IV but no key. If this doesn't turn out to work,
+// there are also some (completely undocumented, feh) EVP_CTRL_* ops
+// that might help, but in the worst case we may have to save the key
+// at creation time, and delay the EVP_EncryptInit call to when we
+// know both key and IV, i.e. at block-encryption time. This would be
+// unfortunate since it would entail doing AES key expansion once per
+// block instead of once per key.
+
+// It also *appears* (again, from inspecting the guts of libcrypto)
+// that we do not have to worry about EVP_EncryptFinal trying to do
+// PKCS padding when GCM is in use. If this is wrong we will have to
+// find some way to make it not happen, which might entail ditching
+// EVP. Feh, I say, feh.
+
gcm_encryptor *
gcm_encryptor::create(const uint8_t *key, size_t keylen)
{
- try {
- gcm_encryptor_impl *enc = new gcm_encryptor_impl;
- // sadly, these are not checkable at compile time
- log_assert(enc->ctx.DigestSize() == GCM_TAG_LEN);
- log_assert(!enc->ctx.NeedsPrespecifiedDataLengths());
- enc->ctx.SetKeyWithIV(key, keylen, dummy_iv, sizeof dummy_iv);
- return enc;
- }
- CATCH_ALL_EXCEPTIONS(0);
+ init_crypto();
+
+ gcm_encryptor_impl *enc = new gcm_encryptor_impl;
+ if (!EVP_EncryptInit_ex(&enc->ctx, aes_gcm_by_size(keylen), 0, key, 0))
+ log_crypto_abort("gcm_encryptor::create");
+
+ return enc;
}
gcm_encryptor *
gcm_encryptor::create(key_generator *gen, size_t keylen)
{
- try {
- KeyBlock key(keylen);
- size_t got = gen->generate(key, keylen);
- log_assert(got == keylen);
-
- gcm_encryptor_impl *enc = new gcm_encryptor_impl;
- // sadly, these are not checkable at compile time
- log_assert(enc->ctx.DigestSize() == GCM_TAG_LEN);
- log_assert(!enc->ctx.NeedsPrespecifiedDataLengths());
- enc->ctx.SetKeyWithIV(key, keylen, dummy_iv, sizeof dummy_iv);
- return enc;
- }
- CATCH_ALL_EXCEPTIONS(0);
+ init_crypto();
+
+ MemBlock key(keylen);
+ size_t got = gen->generate(key, keylen);
+ log_assert(got == keylen);
+
+ gcm_encryptor_impl *enc = new gcm_encryptor_impl;
+ if (!EVP_EncryptInit_ex(&enc->ctx, aes_gcm_by_size(keylen), 0, key, 0))
+ log_crypto_abort("gcm_encryptor::create");
+
+ return enc;
}
gcm_decryptor *
gcm_decryptor::create(const uint8_t *key, size_t keylen)
{
- try {
- gcm_decryptor_impl *dec = new gcm_decryptor_impl;
- // sadly, these are not checkable at compile time
- log_assert(dec->ctx.DigestSize() == GCM_TAG_LEN);
- log_assert(!dec->ctx.NeedsPrespecifiedDataLengths());
- dec->ctx.SetKeyWithIV(key, keylen, dummy_iv, sizeof dummy_iv);
- return dec;
- }
- CATCH_ALL_EXCEPTIONS(0);
+ init_crypto();
+
+ gcm_decryptor_impl *dec = new gcm_decryptor_impl;
+ if (!EVP_DecryptInit_ex(&dec->ctx, aes_gcm_by_size(keylen), 0, key, 0))
+ log_crypto_abort("gcm_decryptor::create");
+
+ return dec;
}
gcm_decryptor *
gcm_decryptor::create(key_generator *gen, size_t keylen)
{
- try {
- KeyBlock key(keylen);
- size_t got = gen->generate(key, keylen);
- log_assert(got == keylen);
-
- gcm_decryptor_impl *dec = new gcm_decryptor_impl;
- // sadly, these are not checkable at compile time
- log_assert(dec->ctx.DigestSize() == GCM_TAG_LEN);
- log_assert(!dec->ctx.NeedsPrespecifiedDataLengths());
- dec->ctx.SetKeyWithIV(key, keylen, dummy_iv, sizeof dummy_iv);
- return dec;
- }
- CATCH_ALL_EXCEPTIONS(0);
+ init_crypto();
+
+ MemBlock key(keylen);
+ size_t got = gen->generate(key, keylen);
+ log_assert(got == keylen);
+
+ gcm_decryptor_impl *dec = new gcm_decryptor_impl;
+ if (!EVP_DecryptInit_ex(&dec->ctx, aes_gcm_by_size(keylen), 0, key, 0))
+ log_crypto_abort("gcm_decryptor::create");
+
+ return dec;
}
gcm_encryptor::~gcm_encryptor() {}
-gcm_encryptor_impl::~gcm_encryptor_impl() {}
+gcm_encryptor_impl::~gcm_encryptor_impl()
+{ EVP_CIPHER_CTX_cleanup(&ctx); }
gcm_decryptor::~gcm_decryptor() {}
-gcm_decryptor_impl::~gcm_decryptor_impl() {}
+gcm_decryptor_impl::~gcm_decryptor_impl()
+{ EVP_CIPHER_CTX_cleanup(&ctx); }
void
gcm_encryptor_impl::encrypt(uint8_t *out, const uint8_t *in, size_t inlen,
const uint8_t *nonce, size_t nlen)
{
- try {
- this->ctx.EncryptAndAuthenticate(out, out + inlen, 16,
- nonce, nlen, 0, 0, in, inlen);
- }
- CATCH_ALL_EXCEPTIONS();
+ log_assert(inlen <= size_t(INT_MAX));
+
+ if (nlen != size_t(EVP_CIPHER_CTX_iv_length(&ctx)))
+ if (!EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_GCM_SET_IVLEN, nlen, 0))
+ log_crypto_abort("gcm_encryptor::reset nonce length");
+
+ if (!EVP_EncryptInit_ex(&ctx, 0, 0, 0, nonce))
+ log_crypto_abort("gcm_encryptor::set nonce");
+
+ int olen;
+ if (!EVP_EncryptUpdate(&ctx, 0, &olen, (const uint8_t *)"", 0) || olen != 0)
+ log_crypto_abort("gcm_encryptor::set null AAD");
+
+ if (!EVP_EncryptUpdate(&ctx, out, &olen, in, inlen) || size_t(olen) != inlen)
+ log_crypto_abort("gcm_encryptor::encrypt");
+
+ if (!EVP_EncryptFinal_ex(&ctx, out + inlen, &olen) || olen != 0)
+ log_crypto_abort("gcm_encryptor::finalize");
+
+ if (!EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_GCM_GET_TAG, 16, out + inlen))
+ log_crypto_abort("gcm_encryptor::write tag");
}
int
gcm_decryptor_impl::decrypt(uint8_t *out, const uint8_t *in, size_t inlen,
const uint8_t *nonce, size_t nlen)
{
- try {
- return this->ctx.DecryptAndVerify(out,
- in + inlen - 16, 16,
- nonce, nlen, 0, 0, in, inlen - 16)
- ? 0 : -1; // caller will log decryption failure
- }
- CATCH_ALL_EXCEPTIONS(-1);
-}
+ log_assert(inlen <= size_t(INT_MAX));
+
+ if (nlen != size_t(EVP_CIPHER_CTX_iv_length(&ctx)))
+ if (!EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_GCM_SET_IVLEN, nlen, 0))
+ log_crypto_abort("gcm_decryptor::reset nonce length");
+
+ if (!EVP_DecryptInit_ex(&ctx, 0, 0, 0, nonce))
+ return log_crypto_warn("gcm_decryptor::set nonce");
-typedef CryptoPP::HMAC<CryptoPP::SHA256> HMAC_SHA256;
-typedef CryptoPP::PKCS5_PBKDF2_HMAC<CryptoPP::SHA256> PBKDF2_SHA256;
-const size_t SHA256_LEN = CryptoPP::SHA256::DIGESTSIZE;
+ if (!EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_GCM_SET_TAG, 16,
+ (void *)(in + inlen - 16)))
+ return log_crypto_warn("gcm_decryptor::set tag");
+
+ int olen;
+ if (!EVP_DecryptUpdate(&ctx, 0, &olen, (const uint8_t *)"", 0) || olen != 0)
+ return log_crypto_warn("gcm_decryptor::set null AAD");
+
+ inlen -= 16;
+ if (!EVP_DecryptUpdate(&ctx, out, &olen, in, inlen) || size_t(olen) != inlen)
+ return log_crypto_warn("gcm_encryptor::decrypt");
+
+ if (!EVP_DecryptFinal_ex(&ctx, out + inlen, &olen) || olen != 0)
+ return log_crypto_warn("gcm_decryptor::check tag");
+
+ return 0;
+}
namespace {
struct key_generator_impl : key_generator
{
- HMAC_SHA256 expander;
- CryptoPP::SecByteBlock prevT;
- CryptoPP::SecByteBlock info;
+ HMAC_CTX expander;
+ MemBlock prevT;
+ MemBlock info;
+
uint8_t counter;
uint8_t leftover;
- bool dead : 1;
+ bool dead;
virtual ~key_generator_impl();
virtual size_t generate(uint8_t *buf, size_t len);
- key_generator_impl(const uint8_t *prk,
- const uint8_t *info, size_t ilen)
- : expander(prk, SHA256_LEN),
- prevT(0),
+ key_generator_impl(const uint8_t *prk, const uint8_t *info, size_t ilen)
+ : prevT(SHA256_LEN),
info(info, ilen),
counter(1),
leftover(0),
dead(false)
- {}
+ {
+ HMAC_CTX_init(&expander);
+ if (!HMAC_Init_ex(&expander, prk, SHA256_LEN, EVP_sha256(), 0))
+ log_crypto_abort("key_generator_impl::construction");
+ }
};
}
+static const uint8_t nosalt[SHA256_LEN] = {};
+
key_generator *
key_generator::from_random_secret(const uint8_t *key, size_t klen,
const uint8_t *salt, size_t slen,
const uint8_t *ctxt, size_t clen)
{
- try {
- HMAC_SHA256 extractor;
- static const uint8_t nosalt[SHA256_LEN] = {};
- uint8_t prk[SHA256_LEN];
-
- if (slen == 0) {
- salt = nosalt;
- slen = SHA256_LEN;
- }
+ log_assert(klen <= INT_MAX && slen < INT_MAX && clen < INT_MAX);
- extractor.SetKey(salt, slen);
- extractor.CalculateDigest(prk, key, klen);
+ MemBlock prk(SHA256_LEN);
- key_generator *r = new key_generator_impl(prk, ctxt, clen);
- memset(prk, 0, SHA256_LEN);
- return r;
+ if (slen == 0) {
+ salt = nosalt;
+ slen = SHA256_LEN;
}
- CATCH_ALL_EXCEPTIONS(0);
+
+ init_crypto();
+
+ if (HMAC(EVP_sha256(), salt, slen, key, klen, prk, 0) == 0)
+ log_crypto_abort("key_generator::from_random_secret");
+
+ return new key_generator_impl(prk, ctxt, clen);
}
key_generator *
@@ -310,30 +446,30 @@ key_generator::from_passphrase(const uint8_t *phra, size_t plen,
const uint8_t *salt, size_t slen,
const uint8_t *ctxt, size_t clen)
{
- try {
- PBKDF2_SHA256 extractor;
- static const uint8_t nosalt[SHA256_LEN] = {};
- uint8_t prk[SHA256_LEN];
-
- if (slen == 0) {
- salt = nosalt;
- slen = SHA256_LEN;
- }
+ // The PBKDF2-HMAC<hash> construction, ignoring the iteration
+ // process, is very similar to the HKDF-Extract<hash> construction;
+ // the biggest difference is that you key the HMAC with the
+ // passphrase rather than the salt. I *think* it is appropriate
+ // to just feed its output directly to the HKDF-Expand phase; an
+ // alternative would be to run PBKDF2 on the passphrase without a
+ // salt, then put the result through HKDF-Extract with the salt.
+
+ log_assert(plen <= INT_MAX && slen < INT_MAX);
+
+ MemBlock prk(SHA256_LEN);
- // The PBKDF2-HMAC<hash> construction, ignoring the iteration
- // process, is very similar to the HKDF-Extract<hash> construction;
- // the biggest difference is that you key the HMAC with the
- // passphrase rather than the salt. I *think* it is appropriate
- // to just feed its output directly to the HKDF-Expand phase; an
- // alternative would be to run PBKDF2 on the passphrase without a
- // salt, then put the result through HKDF-Extract with the salt.
- extractor.DeriveKey(prk, SHA256_LEN, 0, phra, plen, salt, slen, 1000);
-
- key_generator *r = new key_generator_impl(prk, ctxt, clen);
- memset(prk, 0, SHA256_LEN);
- return r;
+ if (slen == 0) {
+ salt = nosalt;
+ slen = SHA256_LEN;
}
- CATCH_ALL_EXCEPTIONS(0);
+
+ init_crypto();
+
+ if (!PKCS5_PBKDF2_HMAC((const char *)phra, plen, salt, slen,
+ 10000, EVP_sha256(), SHA256_LEN, prk))
+ log_crypto_abort("key_generator::from_passphrase");
+
+ return new key_generator_impl(prk, ctxt, clen);
}
size_t
@@ -343,45 +479,53 @@ key_generator_impl::generate(uint8_t *buf, size_t len)
memset(buf, 0, len);
return 0;
}
- try {
- size_t n = 0;
- if (leftover >= len) {
- memcpy(buf, prevT.end() - leftover, len);
- leftover -= len;
- return len;
- } else if (leftover) {
- memcpy(buf, prevT.end() - leftover, leftover);
- n += leftover;
- leftover = 0;
+
+ size_t n = 0;
+ if (leftover >= len) {
+ memcpy(buf, prevT.end() - leftover, len);
+ leftover -= len;
+ return len;
+ } else if (leftover) {
+ memcpy(buf, prevT.end() - leftover, leftover);
+ n += leftover;
+ leftover = 0;
+ }
+ while (n < len) {
+ // compute the next block
+ if (!HMAC_Update(&expander, info, info.size()))
+ log_crypto_abort("generate::apply info");
+ if (!HMAC_Update(&expander, &counter, 1))
+ log_crypto_abort("generate::apply counter");
+ if (!HMAC_Final(&expander, prevT, 0))
+ log_crypto_abort("generate::extract");
+
+ if (n + SHA256_LEN < len) {
+ memcpy(buf + n, prevT, SHA256_LEN);
+ n += SHA256_LEN;
+ } else {
+ leftover = SHA256_LEN - (len - n);
+ memcpy(buf + n, prevT, len - n);
+ n = len;
}
- while (n < len) {
- // generate the next block
- expander.Update(prevT, prevT.size());
- expander.Update(info, info.size());
- expander.Update(&counter, 1);
- counter++;
- prevT.New(SHA256_LEN);
- expander.Final(prevT);
-
- if (n + SHA256_LEN < len) {
- memcpy(buf + n, prevT, SHA256_LEN);
- n += SHA256_LEN;
- } else {
- leftover = SHA256_LEN - (len - n);
- memcpy(buf + n, prevT, len - n);
- n = len;
- }
- if (counter == 0) {
- if (n < len)
- memset(buf + n, 0, len - n);
- dead = true;
- break;
- }
+
+ // prepare to compute the next+1 block
+ counter++;
+ if (counter == 0) {
+ if (n < len)
+ memset(buf + n, 0, len - n);
+ dead = true;
+ break;
}
- return n;
+
+ if (!HMAC_Init_ex(&expander, 0, 0, 0, 0))
+ log_crypto_abort("generate::reset hmac");
+ if (!HMAC_Update(&expander, prevT, prevT.size()))
+ log_crypto_abort("generate::feedback");
}
- CATCH_ALL_EXCEPTIONS((memset(buf, 0, len), 0));
+
+ return n;
}
key_generator::~key_generator() {}
-key_generator_impl::~key_generator_impl() {}
+key_generator_impl::~key_generator_impl()
+{ HMAC_CTX_cleanup(&expander); }
diff --git a/src/crypt.h b/src/crypt.h
index 4136a35..b3bbcfa 100644
--- a/src/crypt.h
+++ b/src/crypt.h
@@ -7,6 +7,7 @@
const size_t AES_BLOCK_LEN = 16;
const size_t GCM_TAG_LEN = 16;
+const size_t SHA256_LEN = 32;
struct key_generator;
diff --git a/src/rng.cc b/src/rng.cc
index 9b7ff57..86a55c1 100644
--- a/src/rng.cc
+++ b/src/rng.cc
@@ -5,112 +5,105 @@
#include "util.h"
#include "rng.h"
-#include <limits>
#include <cmath>
-#include <cryptopp/osrng.h>
+#include <algorithm>
-/* Note: this file wraps a C++ library into a C-style program and must
- insulate that program from C++ semantics it is not prepared to handle;
- most importantly, all exceptions must be converted to error codes. */
+#include <openssl/rand.h>
-#define CATCH_ALL_EXCEPTIONS(rv) \
- catch (std::exception& e) { \
- log_warn("%s: %s", __func__, e.what()); \
- } catch (...) { \
- log_warn("%s: exception of abnormal type", __func__); \
- } \
- return rv /* deliberate absence of semicolon */
-
-static CryptoPP::AutoSeededRandomPool *rng;
-
-static void
-rng_teardown()
-{
- delete rng;
- rng = 0;
-}
-
-static void
-rng_init()
-{
- if (!rng) {
- rng = new CryptoPP::AutoSeededRandomPool;
- if (!rng)
- throw std::bad_alloc();
- atexit(rng_teardown);
- }
-}
+/* OpenSSL's rng is global, automatically seeds itself, and does not
+ appear to need to be torn down explicitly. */
/**
- Fills 'buf' with 'buflen' random bytes and returns 0 on success.
- Returns -1 on failure.
-*/
-int
+ * Fills 'buf' with 'buflen' random bytes. Cannot fail.
+ */
+void
rng_bytes(uint8_t *buf, size_t buflen)
{
- try {
- rng_init();
- rng->GenerateBlock(buf, buflen);
- return 0;
- }
- CATCH_ALL_EXCEPTIONS(-1);
+ log_assert(buflen < INT_MAX);
+ int rv = RAND_bytes(buf, (int)buflen);
+ log_assert(rv);
}
-
-/** Return a pseudorandom integer, chosen uniformly from the values
- * between 0 and <b>max</b>-1 inclusive. <b>max</b> must be between 1 and
- * INT_MAX+1, inclusive. */
+/**
+ * Return a pseudorandom integer, chosen uniformly from the values
+ * in the range [0, max). 'max' must be in the range [1, INT_MAX).
+ */
int
rng_int(unsigned int max)
{
- log_assert(max <= ((unsigned int)INT_MAX)+1);
- log_assert(max > 0); /* don't div by 0 */
-
- try {
- rng_init();
- return rng->GenerateWord32(0, max-1);
+ log_assert(max > 0 && max <= ((unsigned int)INT_MAX)+1);
+
+ /* rng_bytes will only give us a whole number of bytes, so to get a
+ uniformly random number in [0, max) we need to rejection-sample.
+ To minimize the number of rejections, we do the following: Find
+ the least k ("nbits") such that 2**k >= max, and the least b
+ ("nbytes") such that b*CHAR_BIT >= k. Generate b random bytes,
+ and rearrange them into an integer. Mask off all but the least k
+ bits. Accept the result if it is less than max. This way, the
+ probability of accepting each candidate result is always greater
+ than 0.5. */
+
+ unsigned int nbytes, nbits, mask, rv;
+ unsigned char buf[sizeof(int)];
+
+#if __GNUC__ >= 4
+ nbits = CHAR_BIT*sizeof(int) - __builtin_clz(max);
+#else
+#error "Need fallback for __builtin_clz"
+#endif
+ nbytes = (nbits / CHAR_BIT) + 1;
+ mask = (1U << nbits) - 1;
+
+ for (;;) {
+ rng_bytes(buf, nbytes);
+
+ rv = 0;
+ for (unsigned int i = 0; i < nbytes; i++)
+ rv = (rv << CHAR_BIT) | buf[i];
+
+ rv &= mask;
+
+ if (rv < max)
+ return rv;
}
- CATCH_ALL_EXCEPTIONS(-1);
}
-/** Return a pseudorandom integer, chosen uniformly from the values
+/**
+ * Return a pseudorandom integer, chosen uniformly from the values
* between 'min' and 'max-1', inclusive. 'max' must be between
- * 'min+1' and 'INT_MAX+1', inclusive. */
+ * 'min+1' and 'INT_MAX+1', inclusive.
+ */
int
rng_range(unsigned int min, unsigned int max)
{
log_assert(max <= ((unsigned int)INT_MAX)+1);
log_assert(max > min);
- try {
- rng_init();
- return rng->GenerateWord32(min, max-1);
- }
- CATCH_ALL_EXCEPTIONS(-1);
+ return min + rng_int(max - min);
}
-/** Internal use only (can be externalized if someone has a good use
- * for it): generate a random double-precision floating-point number
- * in the range (0.0, 1.0] (note that this is _not_ the usual convention,
- * but it saves a call to nextafter() in the sole current user).
- *
- * For what we use this for, it is important that we can, at least
- * potentially, generate _every_ representable real number in the
- * desired interval, with genuine uniformity. The usual tactic of
- * generating a random integer and dividing does not do this, because
- * the rational numbers produced by random()/MAX are evenly spaced on
- * the real line, but floating point numbers close to zero are *not*.
+/**
+ * Internal use only (can be externalized if someone has a good use
+ * for it): generate a random double-precision floating-point number
+ * in the range (0.0, 1.0] (note that this is _not_ the usual convention,
+ * but it saves a call to nextafter() in the sole current user).
*
- * For the same reason, the trick for avoiding division suggested
- * e.g. by "Common Lisp, the Language", generating a random number in
- * [1.0, 2.0) by overwriting the mantissa of a 1.0 and then
- * subtracting 1.0, does not help -- you can do the first step
- * precisely because the representable binary floating point numbers
- * between 1.0 and 2.0 *are* evenly spaced on the real line.
+ * For what we use this for, it is important that we can, at least
+ * potentially, generate _every_ representable real number in the
+ * desired interval, with genuine uniformity. The usual tactic of
+ * generating a random integer and dividing does not do this, because
+ * the rational numbers produced by random()/MAX are evenly spaced on
+ * the real line, but floating point numbers close to zero are *not*.
*
- * The more complicated, but correct, algorithm here was developed by
- * Allen B. Downey: http://allendowney.com/research/rand/
+ * For the same reason, the trick for avoiding division suggested
+ * e.g. by "Common Lisp, the Language", generating a random number in
+ * [1.0, 2.0) by overwriting the mantissa of a 1.0 and then
+ * subtracting 1.0, does not help -- you can do the first step
+ * precisely because the representable binary floating point numbers
+ * between 1.0 and 2.0 *are* evenly spaced on the real line.
*
+ * The more complicated, but correct, algorithm here was developed by
+ * Allen B. Downey: http://allendowney.com/research/rand/
*/
static double
rng_double()
@@ -122,7 +115,7 @@ rng_double()
bool get()
{
if (n == 0) {
- bits = rng->GenerateByte();
+ rng_bytes((uint8_t *)&bits, 1);
n = CHAR_BIT;
}
bool rv = bits & 1;
@@ -135,66 +128,64 @@ rng_double()
unsigned int n;
};
+ static_assert(sizeof(double) == sizeof(uint64_t),
+ "this code works only with 64-bit, IEEE double");
union ieee754_double {
double d;
uint64_t i;
};
- try {
- rng_init();
-
- /* Because of how the Crypto++ RNG works, it is convenient to
- generate the mantissa first, contra Downey, and use the
- leftover bits to seed the bit-generator that we use for the
- exponent; this does not change the algorithm fundamentally,
- because only the final adjustment step depends on both. */
-
- uint64_t mantissa = rng->GenerateWord32();
- uint32_t b = rng->GenerateWord32();
-
- mantissa |= uint64_t(b & 0x000FFFFF) << 32;
-
- /* This is the core of Downey's algorithm: 50% of the time we
- should generate the highest exponent of a number in (0,1) (note
- that _neither_ endpoint is included right now). 25% of the
- time, we should generate the second highest exponent, 12.5% of
- the time, we should generate the third highest, and so on. In
- other words, we should start with the highest exponent, flip a
- coin, and keep subtracting 1 until either we hit zero or the
- coin comes up heads.
-
- If anyone knows how to do this in _constant_ time, instead of
- variable time bounded by a constant, please tell me.
- */
-
- rngbit bits((b & 0xFFF00000) >> 20, 12);
- uint32_t exponent = 0x3FE; /* 1111111110 = 2^{-1} */
- do {
- if (bits.get()) break;
- } while (--exponent);
-
- /* Finally a slight adjustment: if the mantissa is zero, then
- half the time we should increment the exponent by one.
- Do this unconditionally if the exponent is also zero
- (so we never generate 0.0). */
- if (mantissa == 0 && (exponent == 0 || bits.get()))
- exponent++;
-
- /* Assemble and return the number. */
- union ieee754_double n;
- n.i = (uint64_t(exponent) << 52) | mantissa;
- return n.d;
- }
- CATCH_ALL_EXCEPTIONS(std::numeric_limits<double>::quiet_NaN());
+ /* It is convenient to generate the mantissa first, contra Downey,
+ and use the leftover bits to seed the bit-generator that we use
+ for the exponent; this does not change the algorithm
+ fundamentally, because only the final adjustment step depends
+ on both. */
+
+ uint64_t mantissa = 0;
+ rng_bytes((uint8_t *)&mantissa, sizeof(mantissa));
+
+ rngbit bits(uint32_t(mantissa >> 52), 12);
+ mantissa &= UINT64_C(0x000FFFFFFFFFFFFF);
+
+ /* This is the core of Downey's algorithm: 50% of the time we
+ should generate the highest exponent of a number in (0,1) (note
+ that _neither_ endpoint is included right now). 25% of the
+ time, we should generate the second highest exponent, 12.5% of
+ the time, we should generate the third highest, and so on. In
+ other words, we should start with the highest exponent, flip a
+ coin, and keep subtracting 1 until either we hit zero or the
+ coin comes up heads.
+
+ If anyone knows how to do this in _constant_ time, instead of
+ variable time bounded by a constant, please tell me.
+ */
+
+ uint32_t exponent = 0x3FE; /* 1111111110 = 2^{-1} */
+ do {
+ if (bits.get()) break;
+ } while (--exponent);
+
+ /* Finally a slight adjustment: if the mantissa is zero, then
+ half the time we should increment the exponent by one.
+ Do this unconditionally if the exponent is also zero
+ (so we never generate 0.0). */
+ if (mantissa == 0 && (exponent == 0 || bits.get()))
+ exponent++;
+
+ /* Assemble and return the number. */
+ union ieee754_double n;
+ n.i = (uint64_t(exponent) << 52) | mantissa;
+ return n.d;
}
-/** Return a random integer in the range [0, hi),
- * from a truncated geometric distribution whose expected value
- * (prior to truncation) is 'xv'.
- * (The rate parameter 'lambda' that's usually used to characterize
- * the geometric/exponential distribution is equal to 1/xv.)
- * 'hi' must be no more than INT_MAX+1, as for 'rng_range'.
- * 'xv' must be greater than 0 and less than 'hi'.
+/**
+ * Return a random integer in the range [0, hi),
+ * from a truncated geometric distribution whose expected value
+ * (prior to truncation) is 'xv'.
+ * (The rate parameter 'lambda' that's usually used to characterize
+ * the geometric/exponential distribution is equal to 1/xv.)
+ * 'hi' must be no more than INT_MAX+1, as for 'rng_range'.
+ * 'xv' must be greater than 0 and less than 'hi'.
*/
int
rng_range_geom(unsigned int hi, unsigned int xv)
@@ -202,7 +193,6 @@ rng_range_geom(unsigned int hi, unsigned int xv)
using std::exp;
using std::log;
using std::floor;
- using std::isnan;
using std::min;
using std::max;
@@ -210,8 +200,6 @@ rng_range_geom(unsigned int hi, unsigned int xv)
log_assert(0 < xv && xv < hi);
double U = rng_double();
- if (isnan(U))
- return -1;
/* The exponential distribution with expected value
xe = 1/log(1 + 1/xv)
diff --git a/src/rng.h b/src/rng.h
index 02b0947..5cc0a5a 100644
--- a/src/rng.h
+++ b/src/rng.h
@@ -6,7 +6,7 @@
#define RNG_H
/** Set b to contain n random bytes. */
-int rng_bytes(uint8_t *b, size_t n);
+void rng_bytes(uint8_t *b, size_t n);
/** Return a random integer in the range [0, max).
* 'max' must be between 1 and INT_MAX+1, inclusive.
diff --git a/src/test/unittest.cc b/src/test/unittest.cc
index 31d237a..d09c2a6 100644
--- a/src/test/unittest.cc
+++ b/src/test/unittest.cc
@@ -100,8 +100,12 @@ main(int argc, const char **argv)
char *logminsev;
logminsev = getenv("TT_LOG");
- if (logminsev)
+ if (logminsev) {
+ log_set_method(LOG_METHOD_STDERR, 0);
log_set_min_severity(logminsev);
+ } else {
+ log_set_method(LOG_METHOD_NULL, 0);
+ }
/* Ugly method to fix a Windows problem:
http://archives.seul.org/libevent/users/Oct-2010/msg00049.html */
diff --git a/src/test/unittest_crypt.cc b/src/test/unittest_crypt.cc
index 2e50623..0053646 100644
--- a/src/test/unittest_crypt.cc
+++ b/src/test/unittest_crypt.cc
@@ -968,10 +968,10 @@ test_crypt_rng(void *)
An entropy test wouldn't really help either.
I guess I'll just copy Tor's unit test methodology here :3 */
- uint8_t data1[100],data2[100];
+ uint8_t data1[100], data2[100];
- tt_int_op(0, ==, rng_bytes(data1, 100));
- tt_int_op(0, ==, rng_bytes(data2, 100));
+ rng_bytes(data1, 100);
+ rng_bytes(data2, 100);
tt_mem_op(data1, !=, data2, 100);
diff --git a/src/util.h b/src/util.h
index 392d15a..32750c1 100644
--- a/src/util.h
+++ b/src/util.h
@@ -8,6 +8,7 @@
#include "config.h"
#define __STDC_LIMIT_MACROS
+#define __STDC_CONSTANT_MACROS
#include <limits.h>
#include <stdarg.h> /* va_list */
#include <stddef.h> /* size_t, ptrdiff_t, offsetof, NULL */
1
0

20 Jul '12
commit 29396e18c427623b582c16ab26f89100f194e86f
Author: Zack Weinberg <zackw(a)panix.com>
Date: Tue Mar 6 13:56:45 2012 -0800
Add ECB support to the crypt.h API.
---
src/crypt.cc | 118 +++++++--
src/crypt.h | 97 +++++--
src/protocol/chop.cc | 12 +-
src/test/unittest_crypt.cc | 610 +++++++++++++++++++++++++++++++++++++++++++-
4 files changed, 765 insertions(+), 72 deletions(-)
diff --git a/src/crypt.cc b/src/crypt.cc
index 612eeee..402ae58 100644
--- a/src/crypt.cc
+++ b/src/crypt.cc
@@ -36,38 +36,100 @@ public:
} \
return rv /* deliberate absence of semicolon */
+// Crypto++ doesn't let us set a key without also setting an IV,
+// even though we will always override the IV later.
+static const uint8_t dummy_iv[16] = {};
+
+namespace {
+ struct ecb_encryptor_impl : ecb_encryptor
+ {
+ CryptoPP::ECB_Mode<CryptoPP::AES>::Encryption ctx;
+ virtual ~ecb_encryptor_impl();
+ virtual void encrypt(uint8_t *out, const uint8_t *in);
+ };
+
+ struct ecb_decryptor_impl : ecb_decryptor
+ {
+ CryptoPP::ECB_Mode<CryptoPP::AES>::Decryption ctx;
+ virtual ~ecb_decryptor_impl();
+ virtual void decrypt(uint8_t *out, const uint8_t *in);
+ };
+}
+
+ecb_encryptor *
+ecb_encryptor::create(const uint8_t *key, size_t keylen)
+{
+ try {
+ ecb_encryptor_impl *enc = new ecb_encryptor_impl;
+ enc->ctx.SetKey(key, keylen);
+ return enc;
+ }
+ CATCH_ALL_EXCEPTIONS(0);
+}
+
+ecb_decryptor *
+ecb_decryptor::create(const uint8_t *key, size_t keylen)
+{
+ try {
+ ecb_decryptor_impl *dec = new ecb_decryptor_impl;
+ dec->ctx.SetKey(key, keylen);
+ return dec;
+ }
+ CATCH_ALL_EXCEPTIONS(0);
+}
+
+ecb_encryptor::~ecb_encryptor() {}
+ecb_encryptor_impl::~ecb_encryptor_impl() {}
+ecb_decryptor::~ecb_decryptor() {}
+ecb_decryptor_impl::~ecb_decryptor_impl() {}
+
+void
+ecb_encryptor_impl::encrypt(uint8_t *out, const uint8_t *in)
+{
+ try {
+ this->ctx.ProcessData(out, in, AES_BLOCK_LEN);
+ }
+ CATCH_ALL_EXCEPTIONS();
+}
+
+void
+ecb_decryptor_impl::decrypt(uint8_t *out, const uint8_t *in)
+{
+ try {
+ this->ctx.ProcessData(out, in, AES_BLOCK_LEN);
+ }
+ CATCH_ALL_EXCEPTIONS();
+}
+
+
namespace {
- struct encryptor_impl : encryptor
+ struct gcm_encryptor_impl : gcm_encryptor
{
CryptoPP::GCM<CryptoPP::AES>::Encryption ctx;
+ virtual ~gcm_encryptor_impl();
virtual void encrypt(uint8_t *out, const uint8_t *in, size_t inlen,
const uint8_t *nonce, size_t nlen);
- virtual ~encryptor_impl();
};
- struct decryptor_impl : decryptor
+ struct gcm_decryptor_impl : gcm_decryptor
{
CryptoPP::GCM<CryptoPP::AES>::Decryption ctx;
+ virtual ~gcm_decryptor_impl();
virtual int decrypt(uint8_t *out, const uint8_t *in, size_t inlen,
const uint8_t *nonce, size_t nlen);
virtual void decrypt_unchecked(uint8_t *out,
const uint8_t *in, size_t inlen,
const uint8_t *nonce, size_t nlen);
- virtual ~decryptor_impl();
};
}
-// Crypto++ doesn't let us set a key without also setting an IV,
-// even though we will always override the IV later.
-static const uint8_t dummy_iv[16] = {};
-
-encryptor *
-encryptor::create(const uint8_t *key, size_t keylen)
+gcm_encryptor *
+gcm_encryptor::create(const uint8_t *key, size_t keylen)
{
try {
- encryptor_impl *enc = new encryptor_impl;
+ gcm_encryptor_impl *enc = new gcm_encryptor_impl;
// sadly, these are not checkable at compile time
- log_assert(enc->ctx.DigestSize() == 16);
+ log_assert(enc->ctx.DigestSize() == GCM_TAG_LEN);
log_assert(!enc->ctx.NeedsPrespecifiedDataLengths());
enc->ctx.SetKeyWithIV(key, keylen, dummy_iv, sizeof dummy_iv);
return enc;
@@ -75,13 +137,13 @@ encryptor::create(const uint8_t *key, size_t keylen)
CATCH_ALL_EXCEPTIONS(0);
}
-decryptor *
-decryptor::create(const uint8_t *key, size_t keylen)
+gcm_decryptor *
+gcm_decryptor::create(const uint8_t *key, size_t keylen)
{
try {
- decryptor_impl *dec = new decryptor_impl;
+ gcm_decryptor_impl *dec = new gcm_decryptor_impl;
// sadly, these are not checkable at compile time
- log_assert(dec->ctx.DigestSize() == 16);
+ log_assert(dec->ctx.DigestSize() == GCM_TAG_LEN);
log_assert(!dec->ctx.NeedsPrespecifiedDataLengths());
dec->ctx.SetKeyWithIV(key, keylen, dummy_iv, sizeof dummy_iv);
return dec;
@@ -89,14 +151,14 @@ decryptor::create(const uint8_t *key, size_t keylen)
CATCH_ALL_EXCEPTIONS(0);
}
-encryptor::~encryptor() {}
-encryptor_impl::~encryptor_impl() {}
-decryptor::~decryptor() {}
-decryptor_impl::~decryptor_impl() {}
+gcm_encryptor::~gcm_encryptor() {}
+gcm_encryptor_impl::~gcm_encryptor_impl() {}
+gcm_decryptor::~gcm_decryptor() {}
+gcm_decryptor_impl::~gcm_decryptor_impl() {}
void
-encryptor_impl::encrypt(uint8_t *out, const uint8_t *in, size_t inlen,
- const uint8_t *nonce, size_t nlen)
+gcm_encryptor_impl::encrypt(uint8_t *out, const uint8_t *in, size_t inlen,
+ const uint8_t *nonce, size_t nlen)
{
try {
this->ctx.EncryptAndAuthenticate(out, out + inlen, 16,
@@ -106,8 +168,8 @@ encryptor_impl::encrypt(uint8_t *out, const uint8_t *in, size_t inlen,
}
int
-decryptor_impl::decrypt(uint8_t *out, const uint8_t *in, size_t inlen,
- const uint8_t *nonce, size_t nlen)
+gcm_decryptor_impl::decrypt(uint8_t *out, const uint8_t *in, size_t inlen,
+ const uint8_t *nonce, size_t nlen)
{
try {
return this->ctx.DecryptAndVerify(out,
@@ -119,9 +181,9 @@ decryptor_impl::decrypt(uint8_t *out, const uint8_t *in, size_t inlen,
}
void
-decryptor_impl::decrypt_unchecked(uint8_t *out,
- const uint8_t *in, size_t inlen,
- const uint8_t *nonce, size_t nlen)
+gcm_decryptor_impl::decrypt_unchecked(uint8_t *out,
+ const uint8_t *in, size_t inlen,
+ const uint8_t *nonce, size_t nlen)
{
try {
// there is no convenience function for this
@@ -145,8 +207,8 @@ namespace {
uint8_t leftover;
bool dead : 1;
- virtual size_t generate(uint8_t *buf, size_t len);
virtual ~key_generator_impl();
+ virtual size_t generate(uint8_t *buf, size_t len);
key_generator_impl(const uint8_t *prk,
const uint8_t *info, size_t ilen)
diff --git a/src/crypt.h b/src/crypt.h
index e87de0a..c0084e9 100644
--- a/src/crypt.h
+++ b/src/crypt.h
@@ -1,4 +1,4 @@
-/* Copyright 2011 Nick Mathewson, George Kadianakis, Zack Weinberg
+/* Copyright 2011, 2012 Nick Mathewson, George Kadianakis, Zack Weinberg
See LICENSE for other credits and copying information
*/
@@ -8,55 +8,94 @@
const size_t AES_BLOCK_LEN = 16;
const size_t GCM_TAG_LEN = 16;
-struct encryptor
+struct ecb_encryptor
{
+ ecb_encryptor() {}
+ virtual ~ecb_encryptor();
+
+ /** Return a new AES/ECB encryption state using 'key' (of length 'keylen')
+ as the symmetric key. 'keylen' must be 16, 24, or 32 bytes. */
+ static ecb_encryptor *create(const uint8_t *key, size_t keylen);
+
+ /** Encrypt exactly AES_BLOCK_LEN bytes of data in the buffer 'in' and
+ write the result to 'out'. */
+ virtual void encrypt(uint8_t *out, const uint8_t *in) = 0;
+
+private:
+ ecb_encryptor(const ecb_encryptor&);
+ ecb_encryptor& operator=(const ecb_encryptor&);
+};
+
+struct ecb_decryptor
+{
+ ecb_decryptor() {}
+ virtual ~ecb_decryptor();
+
+ /** Return a new AES/ECB decryption state using 'key' (of length 'keylen')
+ as the symmetric key. 'keylen' must be 16, 24, or 32 bytes. */
+ static ecb_decryptor *create(const uint8_t *key, size_t keylen);
+
+ /** Decrypt exactly AES_BLOCK_LEN bytes of data in the buffer 'in' and
+ write the result to 'out'. */
+ virtual void decrypt(uint8_t *out, const uint8_t *in) = 0;
+
+private:
+ ecb_decryptor(const ecb_decryptor&) DELETE_METHOD;
+ ecb_decryptor& operator=(const ecb_decryptor&) DELETE_METHOD;
+};
+
+
+struct gcm_encryptor
+{
+ gcm_encryptor() {}
+ virtual ~gcm_encryptor();
+
/** Return a new AES/GCM encryption state using 'key' (of length 'keylen')
as the symmetric key. 'keylen' must be 16, 24, or 32 bytes. */
- static encryptor *create(const uint8_t *key, size_t keylen);
+ static gcm_encryptor *create(const uint8_t *key, size_t keylen);
/** Encrypt 'inlen' bytes of data in the buffer 'in', writing the
- result plus a MAC to the buffer 'out', whose length must be at
- least 'inlen'+16 bytes. Use 'nonce' (of length 'nlen') as the
- encryption nonce; 'nlen' must be at least 12 bytes. */
+ result plus an authentication tag to the buffer 'out', whose
+ length must be at least 'inlen'+16 bytes. Use 'nonce'
+ (of length 'nlen') as the encryption nonce; 'nlen' must be at
+ least 12 bytes. */
virtual void encrypt(uint8_t *out, const uint8_t *in, size_t inlen,
const uint8_t *nonce, size_t nlen) = 0;
- virtual ~encryptor();
-
- encryptor() {}
private:
- encryptor(const encryptor&) DELETE_METHOD;
- encryptor& operator=(const encryptor&) DELETE_METHOD;
+ gcm_encryptor(const gcm_encryptor&);
+ gcm_encryptor& operator=(const gcm_encryptor&);
};
-struct decryptor
+struct gcm_decryptor
{
+ gcm_decryptor() {}
+ virtual ~gcm_decryptor();
+
/** Return a new AES/GCM decryption state using 'key' (of length 'keylen')
as the symmetric key. 'keylen' must be 16, 24, or 32 bytes. */
- static decryptor *create(const uint8_t *key, size_t keylen);
-
- /** Decrypt 'inlen' bytes of data in the buffer 'in'; the last 16 bytes
- of this buffer are assumed to be the MAC. Write the result to the
- buffer 'out', whose length must be at least 'inlen'-16 bytes. Use
- 'nonce' (of length 'nlen') as the encryption nonce; as above, 'nlen'
- must be at least 12 bytes. Returns 0 if successful, -1 if the MAC
- did not validate. */
+ static gcm_decryptor *create(const uint8_t *key, size_t keylen);
+
+ /** Decrypt 'inlen' bytes of data in the buffer 'in'; the last 16
+ bytes of this buffer are assumed to be the authentication tag.
+ Write the result to the buffer 'out', whose length must be at
+ least 'inlen'-16 bytes. Use 'nonce' (of length 'nlen') as the
+ encryption nonce; as above, 'nlen' must be at least 12 bytes.
+ Returns 0 if successful, -1 if the authentication check fails. */
virtual int decrypt(uint8_t *out, const uint8_t *in, size_t inlen,
const uint8_t *nonce, size_t nlen) = 0;
- /** Decrypt 'inlen' bytes of data in the buffer 'in' WITHOUT CHECKING
- THE MAC. Arguments same as decrypt(). This should be used only to
- decode just enough of an incoming block to know how long it's going
- to be and therefore where the MAC begins. */
+ /** Decrypt 'inlen' bytes of data in the buffer 'in' WITHOUT
+ CHECKING THE AUTHENTICATION TAG. Arguments same as decrypt().
+ This should be used only to decode just enough of an incoming
+ block to know how long it's going to be and therefore where the
+ tag begins. */
virtual void decrypt_unchecked(uint8_t *out, const uint8_t *in, size_t inlen,
const uint8_t *nonce, size_t nlen) = 0;
- virtual ~decryptor();
-
- decryptor() {}
private:
- decryptor(const decryptor&) DELETE_METHOD;
- decryptor& operator=(const decryptor&) DELETE_METHOD;
+ gcm_decryptor(const gcm_decryptor&) DELETE_METHOD;
+ gcm_decryptor& operator=(const gcm_decryptor&) DELETE_METHOD;
};
/** Generate keying material from an initial key of some kind, a salt
diff --git a/src/protocol/chop.cc b/src/protocol/chop.cc
index 82badde..b4ec692 100644
--- a/src/protocol/chop.cc
+++ b/src/protocol/chop.cc
@@ -92,8 +92,8 @@ namespace {
{
chop_reassembly_elt reassembly_queue;
unordered_set<chop_conn_t *> downstreams;
- encryptor *send_crypt;
- decryptor *recv_crypt;
+ gcm_encryptor *send_crypt;
+ gcm_decryptor *recv_crypt;
chop_config_t *config;
uint64_t circuit_id;
@@ -947,11 +947,11 @@ chop_config_t::circuit_create(size_t)
ckt->config = this;
if (this->mode == LSN_SIMPLE_SERVER) {
- ckt->send_crypt = encryptor::create(s2c_key, 16);
- ckt->recv_crypt = decryptor::create(c2s_key, 16);
+ ckt->send_crypt = gcm_encryptor::create(s2c_key, 16);
+ ckt->recv_crypt = gcm_decryptor::create(c2s_key, 16);
} else {
- ckt->send_crypt = encryptor::create(c2s_key, 16);
- ckt->recv_crypt = decryptor::create(s2c_key, 16);
+ ckt->send_crypt = gcm_encryptor::create(c2s_key, 16);
+ ckt->recv_crypt = gcm_decryptor::create(s2c_key, 16);
while (!ckt->circuit_id)
rng_bytes((uint8_t *)&ckt->circuit_id, sizeof(uint64_t));
diff --git a/src/test/unittest_crypt.cc b/src/test/unittest_crypt.cc
index 600e207..b903c11 100644
--- a/src/test/unittest_crypt.cc
+++ b/src/test/unittest_crypt.cc
@@ -7,9 +7,599 @@
#include "crypt.h"
#include "rng.h"
-/* AES/GCM test vectors from
- http://csrc.nist.gov/groups/STM/cavp/documents/mac/gcmtestvectors.zip
-*/
+// AES/ECB test vectors from
+// http://csrc.nist.gov/groups/STM/cavp/documents/aes/KAT_AES.zip
+
+static void
+test_crypt_aesecb_varkey128(void *)
+{
+ // ECBVarKey128.rsp -- plaintext is always 16 zero bytes
+ const uint8_t plaintext[] = { 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0 };
+ struct testvec
+ {
+ const char *key;
+ const char *ciphertext;
+ };
+ const struct testvec testvecs[] = {
+ { "\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x0e\xdd\x33\xd3\xc6\x21\xe5\x46\x45\x5b\xd8\xba\x14\x18\xbe\xc8" },
+ { "\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x4b\xc3\xf8\x83\x45\x0c\x11\x3c\x64\xca\x42\xe1\x11\x2a\x9e\x87" },
+ { "\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x72\xa1\xda\x77\x0f\x5d\x7a\xc4\xc9\xef\x94\xd8\x22\xaf\xfd\x97" },
+ { "\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x97\x00\x14\xd6\x34\xe2\xb7\x65\x07\x77\xe8\xe8\x4d\x03\xcc\xd8" },
+ { "\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\xf1\x7e\x79\xae\xd0\xdb\x7e\x27\x9e\x95\x5b\x5f\x49\x38\x75\xa7" },
+ { "\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x9e\xd5\xa7\x51\x36\xa9\x40\xd0\x96\x3d\xa3\x79\xdb\x4a\xf2\x6a" },
+ { "\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\xc4\x29\x5f\x83\x46\x5c\x77\x55\xe8\xfa\x36\x4b\xac\x6a\x7e\xa5" },
+ { "\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\xb1\xd7\x58\x25\x6b\x28\xfd\x85\x0a\xd4\x94\x42\x08\xcf\x11\x55" },
+ { "\xff\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x42\xff\xb3\x4c\x74\x3d\xe4\xd8\x8c\xa3\x80\x11\xc9\x90\x89\x0b" },
+ { "\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x99\x58\xf0\xec\xea\x8b\x21\x72\xc0\xc1\x99\x5f\x91\x82\xc0\xf3" },
+ { "\xff\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x95\x6d\x77\x98\xfa\xc2\x0f\x82\xa8\x82\x3f\x98\x4d\x06\xf7\xf5" },
+ { "\xff\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\xa0\x1b\xf4\x4f\x2d\x16\xbe\x92\x8c\xa4\x4a\xaf\x7b\x9b\x10\x6b" },
+ { "\xff\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\xb5\xf1\xa3\x3e\x50\xd4\x0d\x10\x37\x64\xc7\x6b\xd4\xc6\xb6\xf8" },
+ { "\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x26\x37\x05\x0c\x9f\xc0\xd4\x81\x7e\x2d\x69\xde\x87\x8a\xee\x8d" },
+ { "\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x11\x3e\xcb\xe4\xa4\x53\x26\x9a\x0d\xd2\x60\x69\x46\x7f\xb5\xb5" },
+ { "\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x97\xd0\x75\x4f\xe6\x8f\x11\xb9\xe3\x75\xd0\x70\xa6\x08\xc8\x84" },
+ { "\xff\xff\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\xc6\xa0\xb3\xe9\x98\xd0\x50\x68\xa5\x39\x97\x78\x40\x52\x00\xb4" },
+ { "\xff\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\xdf\x55\x6a\x33\x43\x8d\xb8\x7b\xc4\x1b\x17\x52\xc5\x5e\x5e\x49" },
+ { "\xff\xff\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x90\xfb\x12\x8d\x3a\x1a\xf6\xe5\x48\x52\x1b\xb9\x62\xbf\x1f\x05" },
+ { "\xff\xff\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x26\x29\x8e\x9c\x1d\xb5\x17\xc2\x15\xfa\xdf\xb7\xd2\xa8\xd6\x91" },
+ { "\xff\xff\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\xa6\xcb\x76\x1d\x61\xf8\x29\x2d\x0d\xf3\x93\xa2\x79\xad\x03\x80" },
+ { "\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x12\xac\xd8\x9b\x13\xcd\x5f\x87\x26\xe3\x4d\x44\xfd\x48\x61\x08" },
+ { "\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x95\xb1\x70\x3f\xc5\x7b\xa0\x9f\xe0\xc3\x58\x0f\xeb\xdd\x7e\xd4" },
+ { "\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\xde\x11\x72\x2d\x89\x3e\x9f\x91\x21\xc3\x81\xbe\xcc\x1d\xa5\x9a" },
+ { "\xff\xff\xff\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x6d\x11\x4c\xcb\x27\xbf\x39\x10\x12\xe8\x97\x4c\x54\x6d\x9b\xf2" },
+ { "\xff\xff\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x5c\xe3\x7e\x17\xeb\x46\x46\xec\xfa\xc2\x9b\x9c\xc3\x8d\x93\x40" },
+ { "\xff\xff\xff\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x18\xc1\xb6\xe2\x15\x71\x22\x05\x6d\x02\x43\xd8\xa1\x65\xcd\xdb" },
+ { "\xff\xff\xff\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x99\x69\x3e\x6a\x59\xd1\x36\x6c\x74\xd8\x23\x56\x2d\x7e\x14\x31" },
+ { "\xff\xff\xff\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x6c\x7c\x64\xdc\x84\xa8\xbb\xa7\x58\xed\x17\xeb\x02\x5a\x57\xe3" },
+ { "\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\xe1\x7b\xc7\x9f\x30\xea\xab\x2f\xac\x2c\xbb\xe3\x45\x8d\x68\x7a" },
+ { "\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x11\x14\xbc\x20\x28\x00\x9b\x92\x3f\x0b\x01\x91\x5c\xe5\xe7\xc4" },
+ { "\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x9c\x28\x52\x4a\x16\xa1\xe1\xc1\x45\x29\x71\xca\xa8\xd1\x34\x76" },
+ { "\xff\xff\xff\xff\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\xed\x62\xe1\x63\x63\x63\x83\x60\xfd\xd6\xad\x62\x11\x27\x94\xf0" },
+ { "\xff\xff\xff\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x5a\x86\x88\xf0\xb2\xa2\xc1\x62\x24\xc1\x61\x65\x8f\xfd\x40\x44" },
+ { "\xff\xff\xff\xff\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x23\xf7\x10\x84\x2b\x9b\xb9\xc3\x2f\x26\x64\x8c\x78\x68\x07\xca" },
+ { "\xff\xff\xff\xff\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x44\xa9\x8b\xf1\x1e\x16\x3f\x63\x2c\x47\xec\x6a\x49\x68\x3a\x89" },
+ { "\xff\xff\xff\xff\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x0f\x18\xaf\xf9\x42\x74\x69\x6d\x9b\x61\x84\x8b\xd5\x0a\xc5\xe5" },
+ { "\xff\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x82\x40\x85\x71\xc3\xe2\x42\x45\x40\x20\x7f\x83\x3b\x6d\xda\x69" },
+ { "\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x30\x3f\xf9\x96\x94\x7f\x0c\x7d\x1f\x43\xc8\xf3\x02\x7b\x9b\x75" },
+ { "\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x7d\xf4\xda\xf4\xad\x29\xa3\x61\x5a\x9b\x6e\xce\x5c\x99\x51\x8a" },
+ { "\xff\xff\xff\xff\xff\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\xc7\x29\x54\xa4\x8d\x07\x74\xdb\x0b\x49\x71\xc5\x26\x26\x04\x15" },
+ { "\xff\xff\xff\xff\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x1d\xf9\xb7\x61\x12\xdc\x65\x31\xe0\x7d\x2c\xfd\xa0\x44\x11\xf0" },
+ { "\xff\xff\xff\xff\xff\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x8e\x4d\x8e\x69\x91\x19\xe1\xfc\x87\x54\x5a\x64\x7f\xb1\xd3\x4f" },
+ { "\xff\xff\xff\xff\xff\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\xe6\xc4\x80\x7a\xe1\x1f\x36\xf0\x91\xc5\x7d\x9f\xb6\x85\x48\xd1" },
+ { "\xff\xff\xff\xff\xff\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x8e\xbf\x73\xaa\xd4\x9c\x82\x00\x7f\x77\xa5\xc1\xcc\xec\x6a\xb4" },
+ { "\xff\xff\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x4f\xb2\x88\xcc\x20\x40\x04\x90\x01\xd2\xc7\x58\x5a\xd1\x23\xfc" },
+ { "\xff\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x04\x49\x71\x10\xef\xb9\xdc\xeb\x13\xe2\xb1\x3f\xb4\x46\x55\x64" },
+ { "\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x75\x55\x0e\x6c\xb5\xa8\x8e\x49\x63\x4c\x9a\xb6\x9e\xda\x04\x30" },
+ { "\xff\xff\xff\xff\xff\xff\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\xb6\x76\x84\x73\xce\x98\x43\xea\x66\xa8\x14\x05\xdd\x50\xb3\x45" },
+ { "\xff\xff\xff\xff\xff\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\xcb\x2f\x43\x03\x83\xf9\x08\x4e\x03\xa6\x53\x57\x1e\x06\x5d\xe6" },
+ { "\xff\xff\xff\xff\xff\xff\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\xff\x4e\x66\xc0\x7b\xae\x3e\x79\xfb\x7d\x21\x08\x47\xa3\xb0\xba" },
+ { "\xff\xff\xff\xff\xff\xff\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x7b\x90\x78\x51\x25\x50\x5f\xad\x59\xb1\x3c\x18\x6d\xd6\x6c\xe3" },
+ { "\xff\xff\xff\xff\xff\xff\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x8b\x52\x7a\x6a\xeb\xda\xec\x9e\xae\xf8\xed\xa2\xcb\x77\x83\xe5" },
+ { "\xff\xff\xff\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x43\xfd\xaf\x53\xeb\xbc\x98\x80\xc2\x28\x61\x7d\x6a\x9b\x54\x8b" },
+ { "\xff\xff\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x53\x78\x61\x04\xb9\x74\x4b\x98\xf0\x52\xc4\x6f\x1c\x85\x0d\x0b" },
+ { "\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\xb5\xab\x30\x13\xdd\x1e\x61\xdf\x06\xcb\xaf\x34\xca\x2a\xee\x78" },
+ { "\xff\xff\xff\xff\xff\xff\xff\x80\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x74\x70\x46\x9b\xe9\x72\x30\x30\xfd\xcc\x73\xa8\xcd\x4f\xbb\x10" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\xa3\x5a\x63\xf5\x34\x3e\xbe\x9e\xf8\x16\x7b\xcb\x48\xad\x12\x2e" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xe0\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\xfd\x86\x87\xf0\x75\x7a\x21\x0e\x9f\xdf\x18\x12\x04\xc3\x08\x63" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xf0\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x7a\x18\x1e\x84\xbd\x54\x57\xd2\x6a\x88\xfb\xae\x96\x01\x8f\xb0" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xf8\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x65\x33\x17\xb9\x36\x2b\x6f\x9b\x9e\x1a\x58\x0e\x68\xd4\x94\xb5" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x99\x5c\x9d\xc0\xb6\x89\xf0\x3c\x45\x86\x7b\x5f\xaa\x5c\x18\xd1" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x77\xa4\xd9\x6d\x56\xdd\xa3\x98\xb9\xaa\xbe\xcf\xc7\x57\x29\xfd" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x84\xbe\x19\xe0\x53\x63\x5f\x09\xf2\x66\x5e\x7b\xae\x85\xb4\x2d" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\x00\x00\x00\x00\x00\x00",
+ "\x32\xcd\x65\x28\x42\x92\x6a\xea\x4a\xa6\x13\x7b\xb2\xbe\x2b\x5e" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00\x00\x00\x00\x00\x00\x00",
+ "\x49\x3d\x4a\x4f\x38\xeb\xb3\x37\xd1\x0a\xa8\x4e\x91\x71\xa5\x54" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\x00\x00\x00\x00\x00\x00",
+ "\xd9\xbf\xf7\xff\x45\x4b\x0e\xc5\xa4\xa2\xa6\x95\x66\xe2\xcb\x84" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00\x00\x00\x00\x00\x00\x00",
+ "\x35\x35\xd5\x65\xac\xe3\xf3\x1e\xb2\x49\xba\x2c\xc6\x76\x5d\x7a" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xf8\x00\x00\x00\x00\x00\x00\x00",
+ "\xf6\x0e\x91\xfc\x32\x69\xee\xcf\x32\x31\xc6\xe9\x94\x56\x97\xc6" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00",
+ "\xab\x69\xcf\xad\xf5\x1f\x8e\x60\x4d\x9c\xc3\x71\x82\xf6\x63\x5a" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00",
+ "\x78\x66\x37\x3f\x24\xa0\xb6\xed\x56\xe0\xd9\x6f\xcd\xaf\xb8\x77" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00",
+ "\x1e\xa4\x48\xc2\xaa\xc9\x54\xf5\xd8\x12\xe9\xd7\x84\x94\x44\x6a" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\x00\x00\x00\x00\x00",
+ "\xac\xc5\x59\x9d\xd8\xac\x02\x23\x9a\x0f\xef\x4a\x36\xdd\x16\x68" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00\x00\x00\x00\x00\x00",
+ "\xd8\x76\x44\x68\xbb\x10\x38\x28\xcf\x7e\x14\x73\xce\x89\x50\x73" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\x00\x00\x00\x00\x00",
+ "\x1b\x0d\x02\x89\x36\x83\xb9\xf1\x80\x45\x8e\x4a\xa6\xb7\x39\x82" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00\x00\x00\x00\x00\x00",
+ "\x96\xd9\xb0\x17\xd3\x02\xdf\x41\x0a\x93\x7d\xcd\xb8\xbb\x6e\x43" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf8\x00\x00\x00\x00\x00\x00",
+ "\xef\x16\x23\xcc\x44\x31\x3c\xff\x44\x0b\x15\x94\xa7\xe2\x1c\xc6" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00",
+ "\x28\x4c\xa2\xfa\x35\x80\x7b\x8b\x0a\xe4\xd1\x9e\x11\xd7\xdb\xd7" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00",
+ "\xf2\xe9\x76\x87\x57\x55\xf9\x40\x1d\x54\xf3\x6e\x2a\x23\xa5\x94" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00",
+ "\xec\x19\x8a\x18\xe1\x0e\x53\x24\x03\xb7\xe2\x08\x87\xc8\xdd\x80" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\x00\x00\x00\x00",
+ "\x54\x5d\x50\xeb\xd9\x19\xe4\xa6\x94\x9d\x96\xad\x47\xe4\x6a\x80" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00\x00\x00\x00\x00",
+ "\xdb\xdf\xb5\x27\x06\x0e\x0a\x71\x00\x9c\x7b\xb0\xc6\x8f\x1d\x44" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\x00\x00\x00\x00",
+ "\x9c\xfa\x13\x22\xea\x33\xda\x21\x73\xa0\x24\xf2\xff\x0d\x89\x6d" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00\x00\x00\x00\x00",
+ "\x87\x85\xb1\xa7\x5b\x0f\x3b\xd9\x58\xdc\xd0\xe2\x93\x18\xc5\x21" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf8\x00\x00\x00\x00\x00",
+ "\x38\xf6\x7b\x9e\x98\xe4\xa9\x7b\x6d\xf0\x30\xa9\xfc\xdd\x01\x04" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfc\x00\x00\x00\x00\x00",
+ "\x19\x2a\xff\xfb\x2c\x88\x0e\x82\xb0\x59\x26\xd0\xfc\x6c\x44\x8b" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00",
+ "\x6a\x79\x80\xce\x7b\x10\x5c\xf5\x30\x95\x2d\x74\xda\xaf\x79\x8c" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00",
+ "\xea\x36\x95\xe1\x35\x1b\x9d\x68\x58\xbd\x95\x8c\xf5\x13\xef\x6c" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\x00\x00\x00",
+ "\x6d\xa0\x49\x0b\xa0\xba\x03\x43\xb9\x35\x68\x1d\x2c\xce\x5b\xa1" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00\x00\x00\x00",
+ "\xf0\xea\x23\xaf\x08\x53\x40\x11\xc6\x00\x09\xab\x29\xad\xa2\xf1" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\x00\x00\x00",
+ "\xff\x13\x80\x6c\xf1\x9c\xc3\x87\x21\x55\x4d\x7c\x0f\xcd\xcd\x4b" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00\x00\x00\x00",
+ "\x68\x38\xaf\x1f\x4f\x69\xba\xe9\xd8\x5d\xd1\x88\xdc\xdf\x06\x88" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf8\x00\x00\x00\x00",
+ "\x36\xcf\x44\xc9\x2d\x55\x0b\xfb\x1e\xd2\x8e\xf5\x83\xdd\xf5\xd7" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfc\x00\x00\x00\x00",
+ "\xd0\x6e\x31\x95\xb5\x37\x6f\x10\x9d\x5c\x4e\xc6\xc5\xd6\x2c\xed" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\x00\x00\x00\x00",
+ "\xc4\x40\xde\x01\x4d\x3d\x61\x07\x07\x27\x9b\x13\x24\x2a\x5c\x36" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00",
+ "\xf0\xc5\xc6\xff\xa5\xe0\xbd\x3a\x94\xc8\x8f\x6b\x6f\x7c\x16\xb9" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\x00\x00",
+ "\x3e\x40\xc3\x90\x1c\xd7\xef\xfc\x22\xbf\xfc\x35\xde\xe0\xb4\xd9" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00\x00\x00",
+ "\xb6\x33\x05\xc7\x2b\xed\xfa\xb9\x73\x82\xc4\x06\xd0\xc4\x9b\xc6" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\x00\x00",
+ "\x36\xbb\xaa\xb2\x2a\x6b\xd4\x92\x5a\x99\xa2\xb4\x08\xd2\xdb\xae" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00\x00\x00",
+ "\x30\x7c\x5b\x8f\xcd\x05\x33\xab\x98\xbc\x51\xe2\x7a\x6c\xe4\x61" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf8\x00\x00\x00",
+ "\x82\x9c\x04\xff\x4c\x07\x51\x3c\x0b\x3e\xf0\x5c\x03\xe3\x37\xb5" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfc\x00\x00\x00",
+ "\xf1\x7a\xf0\xe8\x95\xdd\xa5\xeb\x98\xef\xc6\x80\x66\xe8\x4c\x54" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\x00\x00\x00",
+ "\x27\x71\x67\xf3\x81\x2a\xff\xf1\xff\xac\xb4\xa9\x34\x37\x9f\xc3" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00",
+ "\x2c\xb1\xdc\x3a\x9c\x72\x97\x2e\x42\x5a\xe2\xef\x3e\xb5\x97\xcd" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\x00",
+ "\x36\xae\xaa\x3a\x21\x3e\x96\x8d\x4b\x5b\x67\x9d\x3a\x2c\x97\xfe" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00\x00",
+ "\x92\x41\xda\xca\x4f\xdd\x03\x4a\x82\x37\x2d\xb5\x0e\x1a\x0f\x3f" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\x00",
+ "\xc1\x45\x74\xd9\xcd\x00\xcf\x2b\x5a\x7f\x77\xe5\x3c\xd5\x78\x85" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00\x00",
+ "\x79\x3d\xe3\x92\x36\x57\x0a\xba\x83\xab\x9b\x73\x7c\xb5\x21\xc9" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf8\x00\x00",
+ "\x16\x59\x1c\x0f\x27\xd6\x0e\x29\xb8\x5a\x96\xc3\x38\x61\xa7\xef" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfc\x00\x00",
+ "\x44\xfb\x5c\x4d\x4f\x5c\xb7\x9b\xe5\xc1\x74\xa3\xb1\xc9\x73\x48" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\x00\x00",
+ "\x67\x4d\x2b\x61\x63\x3d\x16\x2b\xe5\x9d\xde\x04\x22\x2f\x47\x40" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00",
+ "\xb4\x75\x0f\xf2\x63\xa6\x5e\x1f\x9e\x92\x4c\xcf\xd9\x8f\x3e\x37" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00",
+ "\x62\xd0\x66\x2d\x6e\xae\xdd\xed\xeb\xae\x7f\x7e\xa3\xa4\xf6\xb6" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00",
+ "\x70\xc4\x6b\xb3\x06\x92\xbe\x65\x7f\x7e\xaa\x93\xeb\xad\x98\x97" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00",
+ "\x32\x39\x94\xcf\xb9\xda\x28\x5a\x5d\x96\x42\xe1\x75\x9b\x22\x4a" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00",
+ "\x1d\xbf\x57\x87\x7b\x7b\x17\x38\x5c\x85\xd0\xb5\x48\x51\xe3\x71" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf8\x00",
+ "\xdf\xa5\xc0\x97\xcd\xc1\x53\x2a\xc0\x71\xd5\x7b\x1d\x28\xd1\xbd" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfc\x00",
+ "\x3a\x0c\x53\xfa\x37\x31\x1f\xc1\x0b\xd2\xa9\x98\x1f\x51\x31\x74" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\x00",
+ "\xba\x4f\x97\x0c\x0a\x25\xc4\x18\x14\xbd\xae\x2e\x50\x6b\xe3\xb4" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00",
+ "\x2d\xce\x3a\xcb\x72\x7c\xd1\x3c\xcd\x76\xd4\x25\xea\x56\xe4\xf6" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x80",
+ "\x51\x60\x47\x4d\x50\x4b\x9b\x3e\xef\xb6\x8d\x35\xf2\x45\xf4\xb3" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc0",
+ "\x41\xa8\xa9\x47\x76\x66\x35\xde\xc3\x75\x53\xd9\xa6\xc0\xcb\xb7" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0",
+ "\x25\xd6\xcf\xe6\x88\x1f\x2b\xf4\x97\xdd\x14\xcd\x4d\xdf\x44\x5b" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf0",
+ "\x41\xc7\x8c\x13\x5e\xd9\xe9\x8c\x09\x66\x40\x64\x72\x65\xda\x1e" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf8",
+ "\x5a\x4d\x40\x4d\x89\x17\xe3\x53\xe9\x2a\x21\x07\x2c\x3b\x23\x05" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfc",
+ "\x02\xbc\x96\x84\x6b\x3f\xdc\x71\x64\x3f\x38\x4c\xd3\xcc\x3e\xaf" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe",
+ "\x9b\xa4\xa9\x14\x3f\x4e\x5d\x40\x48\x52\x1c\x4f\x88\x77\xd8\x8e" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff",
+ "\xa1\xf6\x25\x8c\x87\x7d\x5f\xcd\x89\x64\x48\x45\x38\xbf\xc9\x2c" },
+ { 0, 0 }
+ };
+
+ ecb_encryptor *e;
+ ecb_decryptor *d;
+ uint8_t eout[AES_BLOCK_LEN];
+ uint8_t dout[AES_BLOCK_LEN];
+ int i;
+
+ for (i = 0; testvecs[i].key; i++) {
+ e = ecb_encryptor::create((const uint8_t *)testvecs[i].key, 16);
+ tt_int_op(e, !=, 0);
+ e->encrypt(eout, plaintext);
+ delete e;
+ tt_mem_op(eout, ==, (const uint8_t *)testvecs[i].ciphertext, AES_BLOCK_LEN);
+
+ d = ecb_decryptor::create((const uint8_t *)testvecs[i].key, 16);
+ tt_int_op(d, !=, 0);
+ d->decrypt(dout, eout);
+ delete d;
+ tt_mem_op(dout, ==, plaintext, AES_BLOCK_LEN);
+ }
+
+ end:;
+}
+
+static void
+test_crypt_aesecb_vartxt128(void *)
+{
+ // ECBVarTxt128.rsp -- key is always 16 zero bytes
+ const uint8_t key[] = { 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0 };
+ struct testvec
+ {
+ const char *plaintext;
+ const char *ciphertext;
+ };
+ const struct testvec testvecs[] = {
+ { "\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x3a\xd7\x8e\x72\x6c\x1e\xc0\x2b\x7e\xbf\xe9\x2b\x23\xd9\xec\x34" },
+ { "\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\xaa\xe5\x93\x9c\x8e\xfd\xf2\xf0\x4e\x60\xb9\xfe\x71\x17\xb2\xc2" },
+ { "\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\xf0\x31\xd4\xd7\x4f\x5d\xcb\xf3\x9d\xaa\xf8\xca\x3a\xf6\xe5\x27" },
+ { "\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x96\xd9\xfd\x5c\xc4\xf0\x74\x41\x72\x7d\xf0\xf3\x3e\x40\x1a\x36" },
+ { "\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x30\xcc\xdb\x04\x46\x46\xd7\xe1\xf3\xcc\xea\x3d\xca\x08\xb8\xc0" },
+ { "\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x16\xae\x4c\xe5\x04\x2a\x67\xee\x8e\x17\x7b\x7c\x58\x7e\xcc\x82" },
+ { "\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\xb6\xda\x0b\xb1\x1a\x23\x85\x5d\x9c\x5c\xb1\xb4\xc6\x41\x2e\x0a" },
+ { "\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\xdb\x4f\x1a\xa5\x30\x96\x7d\x67\x32\xce\x47\x15\xeb\x0e\xe2\x4b" },
+ { "\xff\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\xa8\x17\x38\x25\x26\x21\xdd\x18\x0a\x34\xf3\x45\x5b\x4b\xaa\x2f" },
+ { "\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x77\xe2\xb5\x08\xdb\x7f\xd8\x92\x34\xca\xf7\x93\x9e\xe5\x62\x1a" },
+ { "\xff\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\xb8\x49\x9c\x25\x1f\x84\x42\xee\x13\xf0\x93\x3b\x68\x8f\xcd\x19" },
+ { "\xff\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x96\x51\x35\xf8\xa8\x1f\x25\xc9\xd6\x30\xb1\x75\x02\xf6\x8e\x53" },
+ { "\xff\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x8b\x87\x14\x5a\x01\xad\x1c\x6c\xed\xe9\x95\xea\x36\x70\x45\x4f" },
+ { "\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x8e\xae\x3b\x10\xa0\xc8\xca\x6d\x1d\x3b\x0f\xa6\x1e\x56\xb0\xb2" },
+ { "\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x64\xb4\xd6\x29\x81\x0f\xda\x6b\xaf\xdf\x08\xf3\xb0\xd8\xd2\xc5" },
+ { "\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\xd7\xe5\xdb\xd3\x32\x45\x95\xf8\xfd\xc7\xd7\xc5\x71\xda\x6c\x2a" },
+ { "\xff\xff\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\xf3\xf7\x23\x75\x26\x4e\x16\x7f\xca\x9d\xe2\xc1\x52\x7d\x96\x06" },
+ { "\xff\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x8e\xe7\x9d\xd4\xf4\x01\xff\x9b\x7e\xa9\x45\xd8\x66\x66\xc1\x3b" },
+ { "\xff\xff\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\xdd\x35\xce\xa2\x79\x99\x40\xb4\x0d\xb3\xf8\x19\xcb\x94\xc0\x8b" },
+ { "\xff\xff\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x69\x41\xcb\x6b\x3e\x08\xc2\xb7\xaf\xa5\x81\xeb\xdd\x60\x7b\x87" },
+ { "\xff\xff\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x2c\x20\xf4\x39\xf6\xbb\x09\x7b\x29\xb8\xbd\x6d\x99\xaa\xd7\x99" },
+ { "\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x62\x5d\x01\xf0\x58\xe5\x65\xf7\x7a\xe8\x63\x78\xbd\x2c\x49\xb3" },
+ { "\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\xc0\xb5\xfd\x98\x19\x0e\xf4\x5f\xbb\x43\x01\x43\x8d\x09\x59\x50" },
+ { "\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x13\x00\x1f\xf5\xd9\x98\x06\xef\xd2\x5d\xa3\x4f\x56\xbe\x85\x4b" },
+ { "\xff\xff\xff\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x3b\x59\x4c\x60\xf5\xc8\x27\x7a\x51\x13\x67\x7f\x94\x20\x8d\x82" },
+ { "\xff\xff\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\xe9\xc0\xfc\x18\x18\xe4\xaa\x46\xbd\x2e\x39\xd6\x38\xf8\x9e\x05" },
+ { "\xff\xff\xff\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\xf8\x02\x3e\xe9\xc3\xfd\xc4\x5a\x01\x9b\x4e\x98\x5c\x7e\x1a\x54" },
+ { "\xff\xff\xff\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x35\xf4\x01\x82\xab\x46\x62\xf3\x02\x3b\xae\xc1\xee\x79\x6b\x57" },
+ { "\xff\xff\xff\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x3a\xeb\xba\xd7\x30\x36\x49\xb4\x19\x4a\x69\x45\xc6\xcc\x36\x94" },
+ { "\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\xa2\x12\x4b\xea\x53\xec\x28\x34\x27\x9b\xed\x7f\x7e\xb0\xf9\x38" },
+ { "\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\xb9\xfb\x43\x99\xfa\x4f\xac\xc7\x30\x9e\x14\xec\x98\x36\x0b\x0a" },
+ { "\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\xc2\x62\x77\x43\x74\x20\xc5\xd6\x34\xf7\x15\xae\xa8\x1a\x91\x32" },
+ { "\xff\xff\xff\xff\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x17\x1a\x0e\x1b\x2d\xd4\x24\xf0\xe0\x89\xaf\x2c\x4c\x10\xf3\x2f" },
+ { "\xff\xff\xff\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x7c\xad\xbe\x40\x2d\x1b\x20\x8f\xe7\x35\xed\xce\x00\xae\xe7\xce" },
+ { "\xff\xff\xff\xff\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x43\xb0\x2f\xf9\x29\xa1\x48\x5a\xf6\xf5\xc6\xd6\x55\x8b\xaa\x0f" },
+ { "\xff\xff\xff\xff\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x09\x2f\xaa\xcc\x9b\xf4\x35\x08\xbf\x8f\xa8\x61\x3c\xa7\x5d\xea" },
+ { "\xff\xff\xff\xff\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\xcb\x2b\xf8\x28\x0f\x3f\x97\x42\xc7\xed\x51\x3f\xe8\x02\x62\x9c" },
+ { "\xff\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x21\x5a\x41\xee\x44\x2f\xa9\x92\xa6\xe3\x23\x98\x6d\xed\x3f\x68" },
+ { "\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\xf2\x1e\x99\xcf\x4f\x0f\x77\xce\xa8\x36\xe1\x1a\x2f\xe7\x5f\xb1" },
+ { "\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x95\xe3\xa0\xca\x90\x79\xe6\x46\x33\x1d\xf8\xb4\xe7\x0d\x2c\xd6" },
+ { "\xff\xff\xff\xff\xff\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x4a\xfe\x7f\x12\x0c\xe7\x61\x3f\x74\xfc\x12\xa0\x1a\x82\x80\x73" },
+ { "\xff\xff\xff\xff\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x82\x7f\x00\x0e\x75\xe2\xc8\xb9\xd4\x79\xbe\xed\x91\x3f\xe6\x78" },
+ { "\xff\xff\xff\xff\xff\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x35\x83\x0c\x8e\x7a\xae\xfe\x2d\x30\x31\x0e\xf3\x81\xcb\xf6\x91" },
+ { "\xff\xff\xff\xff\xff\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x19\x1a\xa0\xf2\xc8\x57\x01\x44\xf3\x86\x57\xea\x40\x85\xeb\xe5" },
+ { "\xff\xff\xff\xff\xff\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x85\x06\x2c\x2c\x90\x9f\x15\xd9\x26\x9b\x6c\x18\xce\x99\xc4\xf0" },
+ { "\xff\xff\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x67\x80\x34\xdc\x9e\x41\xb5\xa5\x60\xed\x23\x9e\xea\xb1\xbc\x78" },
+ { "\xff\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\xc2\xf9\x3a\x4c\xe5\xab\x6d\x5d\x56\xf1\xb9\x3c\xf1\x99\x11\xc1" },
+ { "\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x1c\x31\x12\xbc\xb0\xc1\xdc\xc7\x49\xd7\x99\x74\x36\x91\xbf\x82" },
+ { "\xff\xff\xff\xff\xff\xff\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x00\xc5\x5b\xd7\x5c\x7f\x9c\x88\x19\x89\xd3\xec\x19\x11\xc0\xd4" },
+ { "\xff\xff\xff\xff\xff\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\xea\x2e\x6b\x5e\xf1\x82\xb7\xdf\xf3\x62\x9a\xbd\x6a\x12\x04\x5f" },
+ { "\xff\xff\xff\xff\xff\xff\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x22\x32\x23\x27\xe0\x17\x80\xb1\x73\x97\xf2\x40\x87\xf8\xcc\x6f" },
+ { "\xff\xff\xff\xff\xff\xff\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\xc9\xca\xcb\x5c\xd1\x16\x92\xc3\x73\xb2\x41\x17\x68\x14\x9e\xe7" },
+ { "\xff\xff\xff\xff\xff\xff\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\xa1\x8e\x3d\xbb\xca\x57\x78\x60\xda\xb6\xb8\x0d\xa3\x13\x92\x56" },
+ { "\xff\xff\xff\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x79\xb6\x1c\x37\xbf\x32\x8e\xcc\xa8\xd7\x43\x26\x5a\x3d\x42\x5c" },
+ { "\xff\xff\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\xd2\xd9\x9c\x6b\xcc\x1f\x06\xfd\xa8\xe2\x7e\x8a\xe3\xf1\xcc\xc7" },
+ { "\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x1b\xfd\x4b\x91\xc7\x01\xfd\x6b\x61\xb7\xf9\x97\x82\x9d\x66\x3b" },
+ { "\xff\xff\xff\xff\xff\xff\xff\x80\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x11\x00\x5d\x52\xf2\x5f\x16\xbd\xc9\x54\x5a\x87\x6a\x63\x49\x0a" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x3a\x4d\x35\x4f\x02\xbb\x5a\x5e\x47\xd3\x96\x66\x86\x7f\x24\x6a" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xe0\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\xd4\x51\xb8\xd6\xe1\xe1\xa0\xeb\xb1\x55\xfb\xbf\x6e\x7b\x7d\xc3" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xf0\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x68\x98\xd4\xf4\x2f\xa7\xba\x6a\x10\xac\x05\xe8\x7b\x9f\x20\x80" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xf8\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\xb6\x11\x29\x5e\x73\x9c\xa7\xd9\xb5\x0f\x8e\x4c\x0e\x75\x4a\x3f" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x7d\x33\xfc\x7d\x8a\xbe\x3c\xa1\x93\x67\x59\xf8\xf5\xde\xaf\x20" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x3b\x5e\x0f\x56\x6d\xc9\x6c\x29\x8f\x0c\x12\x63\x75\x39\xb2\x5c" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\xf8\x07\xc3\xe7\x98\x5f\xe0\xf5\xa5\x0e\x2c\xdb\x25\xc5\x10\x9e" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\x00\x00\x00\x00\x00\x00",
+ "\x41\xf9\x92\xa8\x56\xfb\x27\x8b\x38\x9a\x62\xf5\xd2\x74\xd7\xe9" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00\x00\x00\x00\x00\x00\x00",
+ "\x10\xd3\xed\x7a\x6f\xe1\x5a\xb4\xd9\x1a\xcb\xc7\xd0\x76\x7a\xb1" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\x00\x00\x00\x00\x00\x00",
+ "\x21\xfe\xec\xd4\x5b\x2e\x67\x59\x73\xac\x33\xbf\x0c\x54\x24\xfc" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00\x00\x00\x00\x00\x00\x00",
+ "\x14\x80\xcb\x39\x55\xba\x62\xd0\x9e\xea\x66\x8f\x7c\x70\x88\x17" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xf8\x00\x00\x00\x00\x00\x00\x00",
+ "\x66\x40\x40\x33\xd6\xb7\x2b\x60\x93\x54\xd5\x49\x6e\x7e\xb5\x11" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00",
+ "\x1c\x31\x7a\x22\x0a\x7d\x70\x0d\xa2\xb1\xe0\x75\xb0\x02\x66\xe1" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00",
+ "\xab\x3b\x89\x54\x22\x33\xf1\x27\x1b\xf8\xfd\x0c\x0f\x40\x35\x45" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00",
+ "\xd9\x3e\xae\x96\x6f\xac\x46\xdc\xa9\x27\xd6\xb1\x14\xfa\x3f\x9e" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\x00\x00\x00\x00\x00",
+ "\x1b\xde\xc5\x21\x31\x65\x03\xd9\xd5\xee\x65\xdf\x3e\xa9\x4d\xdf" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00\x00\x00\x00\x00\x00",
+ "\xee\xf4\x56\x43\x1d\xea\x8b\x4a\xcf\x83\xbd\xae\x37\x17\xf7\x5f" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\x00\x00\x00\x00\x00",
+ "\x06\xf2\x51\x9a\x2f\xaf\xaa\x59\x6b\xfe\xf5\xcf\xa1\x5c\x21\xb9" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00\x00\x00\x00\x00\x00",
+ "\x25\x1a\x7e\xac\x7e\x2f\xe8\x09\xe4\xaa\x8d\x0d\x70\x12\x53\x1a" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf8\x00\x00\x00\x00\x00\x00",
+ "\x3b\xff\xc1\x6e\x4c\x49\xb2\x68\xa2\x0f\x8d\x96\xa6\x0b\x40\x58" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00",
+ "\xe8\x86\xf9\x28\x19\x99\xc5\xbb\x3b\x3e\x88\x62\xe2\xf7\xc9\x88" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00",
+ "\x56\x3b\xf9\x0d\x61\xbe\xef\x39\xf4\x8d\xd6\x25\xfc\xef\x13\x61" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00",
+ "\x4d\x37\xc8\x50\x64\x45\x63\xc6\x9f\xd0\xac\xd9\xa0\x49\x32\x5b" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\x00\x00\x00\x00",
+ "\xb8\x7c\x92\x1b\x91\x82\x9e\xf3\xb1\x3c\xa5\x41\xee\x11\x30\xa6" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00\x00\x00\x00\x00",
+ "\x2e\x65\xeb\x6b\x6e\xa3\x83\xe1\x09\xac\xcc\xe8\x32\x6b\x03\x93" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\x00\x00\x00\x00",
+ "\x9c\xa5\x47\xf7\x43\x9e\xdc\x3e\x25\x5c\x0f\x4d\x49\xaa\x89\x90" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00\x00\x00\x00\x00",
+ "\xa5\xe6\x52\x61\x4c\x93\x00\xf3\x78\x16\xb1\xf9\xfd\x0c\x87\xf9" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf8\x00\x00\x00\x00\x00",
+ "\x14\x95\x4f\x0b\x46\x97\x77\x6f\x44\x49\x4f\xe4\x58\xd8\x14\xed" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfc\x00\x00\x00\x00\x00",
+ "\x7c\x8d\x9a\xb6\xc2\x76\x17\x23\xfe\x42\xf8\xbb\x50\x6c\xbc\xf7" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00",
+ "\xdb\x7e\x19\x32\x67\x9f\xdd\x99\x74\x2a\xab\x04\xaa\x0d\x5a\x80" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00",
+ "\x4c\x6a\x1c\x83\xe5\x68\xcd\x10\xf2\x7c\x2d\x73\xde\xd1\x9c\x28" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\x00\x00\x00",
+ "\x90\xec\xbe\x61\x77\xe6\x74\xc9\x8d\xe4\x12\x41\x3f\x7a\xc9\x15" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00\x00\x00\x00",
+ "\x90\x68\x4a\x2a\xc5\x5f\xe1\xec\x2b\x8e\xbd\x56\x22\x52\x0b\x73" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\x00\x00\x00",
+ "\x74\x72\xf9\xa7\x98\x86\x07\xca\x79\x70\x77\x95\x99\x10\x35\xe6" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00\x00\x00\x00",
+ "\x56\xaf\xf0\x89\x87\x8b\xf3\x35\x2f\x8d\xf1\x72\xa3\xae\x47\xd8" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf8\x00\x00\x00\x00",
+ "\x65\xc0\x52\x6c\xbe\x40\x16\x1b\x80\x19\xa2\xa3\x17\x1a\xbd\x23" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfc\x00\x00\x00\x00",
+ "\x37\x7b\xe0\xbe\x33\xb4\xe3\xe3\x10\xb4\xaa\xbd\xa1\x73\xf8\x4f" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\x00\x00\x00\x00",
+ "\x94\x02\xe9\xaa\x6f\x69\xde\x65\x04\xda\x8d\x20\xc4\xfc\xaa\x2f" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00",
+ "\x12\x3c\x1f\x4a\xf3\x13\xad\x8c\x2c\xe6\x48\xb2\xe7\x1f\xb6\xe1" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\x00\x00",
+ "\x1f\xfc\x62\x6d\x30\x20\x3d\xcd\xb0\x01\x9f\xb8\x0f\x72\x6c\xf4" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00\x00\x00",
+ "\x76\xda\x1f\xbe\x3a\x50\x72\x8c\x50\xfd\x2e\x62\x1b\x5a\xd8\x85" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\x00\x00",
+ "\x08\x2e\xb8\xbe\x35\xf4\x42\xfb\x52\x66\x8e\x16\xa5\x91\xd1\xd6" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00\x00\x00",
+ "\xe6\x56\xf9\xec\xf5\xfe\x27\xec\x3e\x4a\x73\xd0\x0c\x28\x2f\xb3" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf8\x00\x00\x00",
+ "\x2c\xa8\x20\x9d\x63\x27\x4c\xd9\xa2\x9b\xb7\x4b\xcd\x77\x68\x3a" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfc\x00\x00\x00",
+ "\x79\xbf\x5d\xce\x14\xbb\x7d\xd7\x3a\x8e\x36\x11\xde\x7c\xe0\x26" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\x00\x00\x00",
+ "\x3c\x84\x99\x39\xa5\xd2\x93\x99\xf3\x44\xc4\xa0\xec\xa8\xa5\x76" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00",
+ "\xed\x3c\x0a\x94\xd5\x9b\xec\xe9\x88\x35\xda\x7a\xa4\xf0\x7c\xa2" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\x00",
+ "\x63\x91\x9e\xd4\xce\x10\x19\x64\x38\xb6\xad\x09\xd9\x9c\xd7\x95" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00\x00",
+ "\x76\x78\xf3\xa8\x33\xf1\x9f\xea\x95\xf3\xc6\x02\x9e\x2b\xc6\x10" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\x00",
+ "\x3a\xa4\x26\x83\x10\x67\xd3\x6b\x92\xbe\x7c\x5f\x81\xc1\x3c\x56" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00\x00",
+ "\x92\x72\xe2\xd2\xcd\xd1\x10\x50\x99\x8c\x84\x50\x77\xa3\x0e\xa0" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf8\x00\x00",
+ "\x08\x8c\x4b\x53\xf5\xec\x0f\xf8\x14\xc1\x9a\xda\xe7\xf6\x24\x6c" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfc\x00\x00",
+ "\x40\x10\xa5\xe4\x01\xfd\xf0\xa0\x35\x4d\xdb\xcc\x0d\x01\x2b\x17" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\x00\x00",
+ "\xa8\x7a\x38\x57\x36\xc0\xa6\x18\x9b\xd6\x58\x9b\xd8\x44\x5a\x93" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00",
+ "\x54\x5f\x2b\x83\xd9\x61\x6d\xcc\xf6\x0f\xa9\x83\x0e\x9c\xd2\x87" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00",
+ "\x4b\x70\x6f\x7f\x92\x40\x63\x52\x39\x40\x37\xa6\xd4\xf4\x68\x8d" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00",
+ "\xb7\x97\x2b\x39\x41\xc4\x4b\x90\xaf\xa7\xb2\x64\xbf\xba\x73\x87" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00",
+ "\x6f\x45\x73\x2c\xf1\x08\x81\x54\x6f\x0f\xd2\x38\x96\xd2\xbb\x60" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00",
+ "\x2e\x35\x79\xca\x15\xaf\x27\xf6\x4b\x3c\x95\x5a\x5b\xfc\x30\xba" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf8\x00",
+ "\x34\xa2\xc5\xa9\x1a\xe2\xae\xc9\x9b\x7d\x1b\x5f\xa6\x78\x04\x47" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfc\x00",
+ "\xa4\xd6\x61\x6b\xd0\x4f\x87\x33\x5b\x0e\x53\x35\x12\x27\xa9\xee" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\x00",
+ "\x7f\x69\x2b\x03\x94\x58\x67\xd1\x61\x79\xa8\xce\xfc\x83\xea\x3f" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00",
+ "\x3b\xd1\x41\xee\x84\xa0\xe6\x41\x4a\x26\xe7\xa4\xf2\x81\xf8\xa2" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x80",
+ "\xd1\x78\x8f\x57\x2d\x98\xb2\xb1\x6e\xc5\xd5\xf3\x92\x2b\x99\xbc" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc0",
+ "\x08\x33\xff\x6f\x61\xd9\x8a\x57\xb2\x88\xe8\xc3\x58\x6b\x85\xa6" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0",
+ "\x85\x68\x26\x17\x97\xde\x17\x6b\xf0\xb4\x3b\xec\xc6\x28\x5a\xfb" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf0",
+ "\xf9\xb0\xfd\xa0\xc4\xa8\x98\xf5\xb9\xe6\xf6\x61\xc4\xce\x4d\x07" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf8",
+ "\x8a\xde\x89\x59\x13\x68\x5c\x67\xc5\x26\x9f\x8a\xae\x42\x98\x3e" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfc",
+ "\x39\xbd\xe6\x7d\x5c\x8e\xd8\xa8\xb1\xc3\x7e\xb8\xfa\x9f\x5a\xc0" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe",
+ "\x5c\x00\x5e\x72\xc1\x41\x8c\x44\xf5\x69\xf2\xea\x33\xba\x54\xf3" },
+ { "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff",
+ "\x3f\x5b\x8c\xc9\xea\x85\x5a\x0a\xfa\x73\x47\xd2\x3e\x8d\x66\x4e" },
+ { 0, 0 }
+ };
+
+ ecb_encryptor *e = ecb_encryptor::create(key, 16);
+ ecb_decryptor *d = ecb_decryptor::create(key, 16);
+ uint8_t eout[AES_BLOCK_LEN];
+ uint8_t dout[AES_BLOCK_LEN];
+ int i;
+
+ tt_int_op(e, !=, 0);
+ tt_int_op(d, !=, 0);
+
+ for (i = 0; testvecs[i].plaintext; i++) {
+ e->encrypt(eout, (const uint8_t *)testvecs[i].plaintext);
+ tt_mem_op(eout, ==, (const uint8_t *)testvecs[i].ciphertext, AES_BLOCK_LEN);
+
+ d->decrypt(dout, eout);
+ tt_mem_op(dout, ==, (const uint8_t *)testvecs[i].plaintext, AES_BLOCK_LEN);
+ }
+
+ end:
+ delete e;
+ delete d;
+}
+
+
+// AES/GCM test vectors from
+// http://csrc.nist.gov/groups/STM/cavp/documents/mac/gcmtestvectors.zip
static void
test_crypt_aesgcm_enc(void *)
@@ -75,12 +665,12 @@ test_crypt_aesgcm_enc(void *)
{ 0, 0, 0, 0, 0, 0 }
};
- encryptor *c;
+ gcm_encryptor *c;
uint8_t obuf[144];
int i;
for (i = 0; testvecs[i].key; i++) {
- c = encryptor::create((const uint8_t *)testvecs[i].key, 16);
+ c = gcm_encryptor::create((const uint8_t *)testvecs[i].key, 16);
tt_int_op(c, !=, 0);
c->encrypt(obuf,
(const uint8_t *)testvecs[i].pt, testvecs[i].len,
@@ -158,12 +748,12 @@ test_crypt_aesgcm_good_dec(void *)
{ 0, 0, 0, 0, 0 }
};
- decryptor *c;
+ gcm_decryptor *c;
uint8_t obuf[128];
int i, rv;
for (i = 0; testvecs[i].key; i++) {
- c = decryptor::create((const uint8_t *)testvecs[i].key, 16);
+ c = gcm_decryptor::create((const uint8_t *)testvecs[i].key, 16);
tt_int_op(c, !=, 0);
rv = c->decrypt(obuf,
@@ -231,12 +821,12 @@ test_crypt_aesgcm_bad_dec(void *)
{ 0, 0, 0, 0 }
};
- decryptor *c;
+ gcm_decryptor *c;
uint8_t obuf[128];
int i, rv;
for (i = 0; testvecs[i].key; i++) {
- c = decryptor::create((const uint8_t *)testvecs[i].key, 16);
+ c = gcm_decryptor::create((const uint8_t *)testvecs[i].key, 16);
tt_int_op(c, !=, 0);
rv = c->decrypt(obuf,
@@ -398,6 +988,8 @@ test_crypt_rng(void *)
{ #name, test_crypt_##name, 0, 0, 0 }
struct testcase_t crypt_tests[] = {
+ T(aesecb_varkey128),
+ T(aesecb_vartxt128),
T(aesgcm_enc),
T(aesgcm_good_dec),
T(aesgcm_bad_dec),
1
0

[stegotorus/master] Revise chop.cc to be closer to what the paper says it does:
by zwol@torproject.org 20 Jul '12
by zwol@torproject.org 20 Jul '12
20 Jul '12
commit 49302db0cd095bbc02c631e1628542f4d8b003a9
Author: Zack Weinberg <zackw(a)panix.com>
Date: Sat Mar 24 20:27:50 2012 -0700
Revise chop.cc to be closer to what the paper says it does:
* new block format
* 32-bit circuit IDs
* separate header and payload encryption
* sequence numbers count by block, not byte
Still to do:
* proper handshakes
* rekeying
* "exactly what steg wants" block padding
---
src/crypt.cc | 4 +-
src/protocol/chop.cc | 2059 +++++++++++++++++++++++++-------------------------
2 files changed, 1038 insertions(+), 1025 deletions(-)
diff --git a/src/crypt.cc b/src/crypt.cc
index d6eed49..e81e8d4 100644
--- a/src/crypt.cc
+++ b/src/crypt.cc
@@ -273,9 +273,7 @@ key_generator::from_passphrase(const uint8_t *phra, size_t plen,
// to just feed its output directly to the HKDF-Expand phase; an
// alternative would be to run PBKDF2 on the passphrase without a
// salt, then put the result through HKDF-Extract with the salt.
- //
- // 1000 iterations or 50 ms, whichever is more
- extractor.DeriveKey(prk, SHA256_LEN, 0, phra, plen, salt, slen, 1000, 0.05);
+ extractor.DeriveKey(prk, SHA256_LEN, 0, phra, plen, salt, slen, 1000);
key_generator *r = new key_generator_impl(prk, ctxt, clen);
memset(prk, 0, SHA256_LEN);
diff --git a/src/protocol/chop.cc b/src/protocol/chop.cc
index b4ec692..e81214a 100644
--- a/src/protocol/chop.cc
+++ b/src/protocol/chop.cc
@@ -1,10 +1,9 @@
-/* Copyright 2011 Zack Weinberg
+/* Copyright 2011, 2012 Zack Weinberg
See LICENSE for other credits and copying information
The chopper is the core StegoTorus protocol implementation.
- For its design, see doc/chopper.tex. Note that it is still
- being implemented, and many things that are *intended* to change
- from the toy "roundrobin" protocol have not yet changed. */
+ For its design, see doc/chopper.txt. Note that it is still
+ being implemented, and may change incompatibly. */
#include "util.h"
#include "connections.h"
@@ -20,805 +19,363 @@
#include <event2/event.h>
#include <event2/buffer.h>
+#ifdef HAVE_EXECINFO_H
+#include <execinfo.h>
+#endif
+
using std::tr1::unordered_map;
using std::tr1::unordered_set;
using std::vector;
+using std::make_pair;
-/* Header serialization and deserialization */
-
-struct chop_header
+namespace
{
- uint64_t ckt_id;
- uint8_t pkt_iv[8];
- uint32_t offset;
- uint16_t length;
- uint16_t flags;
-};
-
-#define CHOP_WIRE_HDR_LEN (sizeof(struct chop_header))
-#define CHOP_BLOCK_OVERHD (CHOP_WIRE_HDR_LEN + GCM_TAG_LEN)
-#define CHOP_MAX_DATA (65535 - CHOP_BLOCK_OVERHD)
-#define CHOP_MAX_CHAFF 2048
-#define CHOP_F_SYN 0x0001
-#define CHOP_F_FIN 0x0002
-#define CHOP_F_CHAFF 0x0004
-/* further flags values are reserved */
-
-/* Reassembly queue. This is a doubly-linked circular list with a
- sentinel element at the head (identified by data == 0). List
- entries are sorted by offset. Gaps in so-far-received data
- are "in between" entries in the list. */
-
-struct chop_reassembly_elt
+/* Packets on the wire have a 16-byte header, consisting of a 32-bit
+ sequence number, two 16-bit length fields ("D" and "P"), an 8-bit
+ opcode ("F"), and a 56-bit check field. All numbers in this header
+ are serialized in network byte order.
+
+ | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F |
+ |Sequence Number| D | P | F | Check |
+
+ The header is encrypted with AES in ECB mode: this is safe because
+ the header is exactly one AES block long, the sequence number is
+ never repeated, the header-encryption key is not used for anything
+ else, and the high 24 bits of the sequence number, plus the check
+ field, constitute an 80-bit MAC. The receiver maintains a
+ 256-element sliding window of acceptable sequence numbers, which
+ begins one after the highest sequence number so far _processed_
+ (not received). If the sequence number is outside this window, or
+ the check field is not all-bits-zero, the packet is discarded. An
+ attacker's odds of being able to manipulate the D, P, or F fields
+ or the low bits of the sequence number are therefore less than one
+ in 2^80. Unlike TCP, our sequence numbers always start at zero on
+ a new (or freshly rekeyed) circuit, and increment by one per
+ _block_, not per byte of data. Furthermore, they do not wrap: a
+ rekeying cycle (which resets the sequence number) is required to
+ occur before the highest-received sequence number reaches 2^32.
+
+ Following the header are two variable-length payload sections,
+ "data" and "padding", whose length in bytes are given by the D and
+ P fields, respectively. These sections are encrypted, using a
+ different key, with AES in GCM mode. The *encrypted* packet header
+ doubles as the GCM nonce. The semantics of the "data" section's
+ contents, if any, are defined by the opcode F. The "padding"
+ section SHOULD be filled with zeroes by the sender; regardless, its
+ contents MUST be ignored by the receiver. Following these sections
+ is a 16-byte GCM authentication tag, computed over the data and
+ padding sections only, NOT the message header. */
+
+const size_t HEADER_LEN = 16;
+const size_t TRAILER_LEN = 16;
+const size_t SECTION_LEN = UINT16_MAX;
+const size_t MIN_BLOCK_SIZE = HEADER_LEN + TRAILER_LEN;
+const size_t MAX_BLOCK_SIZE = MIN_BLOCK_SIZE + SECTION_LEN*2;
+
+enum opcode_t
{
- struct chop_reassembly_elt *prev;
- struct chop_reassembly_elt *next;
- struct evbuffer *data;
- uint32_t offset;
- uint16_t length;
- uint16_t flags;
+ op_DAT = 0, // Pass data section along to upstream
+ op_FIN = 1, // No further transmissions (pass data along if any)
+ op_RST = 2, // Protocol error, close circuit now
+ op_RK1 = 3, // Commence rekeying
+ op_RK2 = 4, // Continue rekeying
+ op_RK3 = 5, // Conclude rekeying
+ op_RESERVED0 = 6, // 6 -- 127 reserved for future definition
+ op_STEG0 = 128, // 128 -- 255 reserved for steganography modules
+ op_LAST = 255
};
-/* Horrifically crude "encryption". Uses a compiled-in pair of
- encryption keys, no MAC, and recycles the circuit ID as a
- partial IV. To be replaced with something less laughable ASAP. */
-
-static const uint8_t c2s_key[] =
- "\x44\x69\x5f\x45\x41\x67\xe9\x69\x14\x6c\x5f\xd2\x41\x63\xc4\x02";
-static const uint8_t s2c_key[] =
- "\xfa\x31\x78\x6c\xb9\x4c\x66\x2a\xd0\x30\x59\xf7\x28\x22\x2f\x22";
-
-/* Connections and circuits */
-
-namespace {
- struct chop_config_t;
- struct chop_circuit_t;
- typedef unordered_map<uint64_t, chop_circuit_t *> chop_circuit_table;
-
- struct chop_conn_t : conn_t
- {
- chop_config_t *config;
- chop_circuit_t *upstream;
- steg_t *steg;
- struct evbuffer *recv_pending;
- struct event *must_transmit_timer;
- bool no_more_transmissions : 1;
-
- CONN_DECLARE_METHODS(chop);
- };
+class block_header
+{
+ uint8_t clear[16];
+ uint8_t ciphr[16];
- struct chop_circuit_t : circuit_t
+public:
+ block_header(uint32_t s, uint16_t d, uint16_t p, opcode_t f,
+ ecb_encryptor &ec)
{
- chop_reassembly_elt reassembly_queue;
- unordered_set<chop_conn_t *> downstreams;
- gcm_encryptor *send_crypt;
- gcm_decryptor *recv_crypt;
- chop_config_t *config;
-
- uint64_t circuit_id;
- uint32_t send_offset;
- uint32_t recv_offset;
- uint32_t dead_cycles;
- bool received_syn : 1;
- bool received_fin : 1;
- bool sent_syn : 1;
- bool sent_fin : 1;
- bool upstream_eof : 1;
-
- CIRCUIT_DECLARE_METHODS(chop);
-
- uint32_t axe_interval() {
- // This function must always return a number which is larger than
- // the maximum possible number that *our peer's* flush_interval()
- // could have returned; otherwise, we might axe the connection when
- // it was just that there was nothing to say for a while.
- // For simplicity's sake, right now we hardwire this to be 30 minutes.
- return 30 * 60 * 1000;
- }
- uint32_t flush_interval() {
- // 10*60*1000 lies between 2^19 and 2^20.
- uint32_t shift = std::max(1u, std::min(19u, dead_cycles));
- uint32_t xv = std::max(1u, std::min(10u * 60 * 1000, 1u << shift));
- return rng_range_geom(20 * 60 * 1000, xv) + 100;
+ if (f > op_LAST || (f >= op_RESERVED0 && f < op_STEG0)) {
+ memset(clear, 0xFF, sizeof clear); // invalid!
+ memset(ciphr, 0xFF, sizeof ciphr);
+ return;
}
- };
-
- struct chop_config_t : config_t
- {
- struct evutil_addrinfo *up_address;
- vector<struct evutil_addrinfo *> down_addresses;
- vector<const char *> steg_targets;
- chop_circuit_table circuits;
- CONFIG_DECLARE_METHODS(chop);
- };
-}
-
-PROTO_DEFINE_MODULE(chop);
+ // sequence number
+ clear[0] = (s >> 24) & 0xFF;
+ clear[1] = (s >> 16) & 0xFF;
+ clear[2] = (s >> 8) & 0xFF;
+ clear[3] = (s ) & 0xFF;
-/* Header serialization and deserialization */
+ // D field
+ clear[4] = (d >> 8) & 0xFF;
+ clear[5] = (d ) & 0xFF;
-static void
-chop_write_header(uint8_t *wire_header, const struct chop_header *hdr)
-{
- /* bits on the wire are in network byte order */
- wire_header[ 0] = (hdr->ckt_id & 0xFF00000000000000ull) >> 56;
- wire_header[ 1] = (hdr->ckt_id & 0x00FF000000000000ull) >> 48;
- wire_header[ 2] = (hdr->ckt_id & 0x0000FF0000000000ull) >> 40;
- wire_header[ 3] = (hdr->ckt_id & 0x000000FF00000000ull) >> 32;
- wire_header[ 4] = (hdr->ckt_id & 0x00000000FF000000ull) >> 24;
- wire_header[ 5] = (hdr->ckt_id & 0x0000000000FF0000ull) >> 16;
- wire_header[ 6] = (hdr->ckt_id & 0x000000000000FF00ull) >> 8;
- wire_header[ 7] = (hdr->ckt_id & 0x00000000000000FFull) >> 0;
-
- wire_header[ 8] = hdr->pkt_iv[0];
- wire_header[ 9] = hdr->pkt_iv[1];
- wire_header[10] = hdr->pkt_iv[2];
- wire_header[11] = hdr->pkt_iv[3];
- wire_header[12] = hdr->pkt_iv[4];
- wire_header[13] = hdr->pkt_iv[5];
- wire_header[14] = hdr->pkt_iv[6];
- wire_header[15] = hdr->pkt_iv[7];
-
- wire_header[16] = (hdr->offset & 0xFF000000u) >> 24;
- wire_header[17] = (hdr->offset & 0x00FF0000u) >> 16;
- wire_header[18] = (hdr->offset & 0x0000FF00u) >> 8;
- wire_header[19] = (hdr->offset & 0x000000FFu) >> 0;
-
- wire_header[20] = (hdr->length & 0xFF00u) >> 8;
- wire_header[21] = (hdr->length & 0x00FFu) >> 0;
- wire_header[22] = (hdr->flags & 0xFF00u) >> 8;
- wire_header[23] = (hdr->flags & 0x00FFu) >> 0;
-}
+ // P field
+ clear[6] = (p >> 8) & 0xFF;
+ clear[7] = (p ) & 0xFF;
-static int
-chop_peek_circuit_id(struct evbuffer *buf, struct chop_header *hdr)
-{
- uint8_t wire_id[8];
- if (evbuffer_copyout(buf, wire_id, 8) != 8)
- return -1;
- hdr->ckt_id = ((((uint64_t)wire_id[ 0]) << 56) +
- (((uint64_t)wire_id[ 1]) << 48) +
- (((uint64_t)wire_id[ 2]) << 40) +
- (((uint64_t)wire_id[ 3]) << 32) +
- (((uint64_t)wire_id[ 4]) << 24) +
- (((uint64_t)wire_id[ 5]) << 16) +
- (((uint64_t)wire_id[ 6]) << 8) +
- (((uint64_t)wire_id[ 7]) << 0));
- return 0;
-}
+ // F field
+ clear[8] = uint8_t(f);
-static int
-chop_decrypt_header(chop_circuit_t *ckt,
- struct evbuffer *buf,
- struct chop_header *hdr)
-{
- uint8_t wire_header[CHOP_WIRE_HDR_LEN];
- uint8_t decoded_header[CHOP_WIRE_HDR_LEN-16];
+ // Check field
+ memset(clear + 9, 0, 7);
- if (evbuffer_copyout(buf, wire_header, CHOP_WIRE_HDR_LEN)
- != CHOP_WIRE_HDR_LEN) {
- log_warn("not enough data copied out");
- return -1;
+ ec.encrypt(ciphr, clear);
}
- hdr->ckt_id = ((((uint64_t)wire_header[ 0]) << 56) +
- (((uint64_t)wire_header[ 1]) << 48) +
- (((uint64_t)wire_header[ 2]) << 40) +
- (((uint64_t)wire_header[ 3]) << 32) +
- (((uint64_t)wire_header[ 4]) << 24) +
- (((uint64_t)wire_header[ 5]) << 16) +
- (((uint64_t)wire_header[ 6]) << 8) +
- (((uint64_t)wire_header[ 7]) << 0));
-
- hdr->pkt_iv[0] = wire_header[ 8];
- hdr->pkt_iv[1] = wire_header[ 9];
- hdr->pkt_iv[2] = wire_header[10];
- hdr->pkt_iv[3] = wire_header[11];
- hdr->pkt_iv[4] = wire_header[12];
- hdr->pkt_iv[5] = wire_header[13];
- hdr->pkt_iv[6] = wire_header[14];
- hdr->pkt_iv[7] = wire_header[15];
-
- /* The full IV is the circuit ID plus packet ID *as it is on the
- wire*. */
- ckt->recv_crypt->decrypt_unchecked(decoded_header,
- wire_header + 16, CHOP_WIRE_HDR_LEN - 16,
- wire_header, 16);
-
- hdr->offset = ((((uint32_t)decoded_header[0]) << 24) +
- (((uint32_t)decoded_header[1]) << 16) +
- (((uint32_t)decoded_header[2]) << 8) +
- (((uint32_t)decoded_header[3]) << 0));
-
- hdr->length = ((((uint16_t)decoded_header[4]) << 8) +
- (((uint16_t)decoded_header[5]) << 0));
-
- hdr->flags = ((((uint16_t)decoded_header[6]) << 8) +
- (((uint16_t)decoded_header[7]) << 0));
-
- log_debug("decoded offset %u length %hu flags %04hx",
- hdr->offset, hdr->length, hdr->flags);
- return 0;
-}
-
-/* Transmit subroutines. */
-
-static chop_conn_t *
-chop_pick_connection(chop_circuit_t *ckt, size_t desired, size_t *blocksize)
-{
- size_t maxbelow = 0;
- size_t minabove = SIZE_MAX;
- chop_conn_t *targbelow = NULL;
- chop_conn_t *targabove = NULL;
-
- if (desired > CHOP_MAX_DATA)
- desired = CHOP_MAX_DATA;
-
- /* Find the best fit for the desired transmission from all the
- outbound connections' transmit rooms. */
- for (unordered_set<chop_conn_t *>::iterator i = ckt->downstreams.begin();
- i != ckt->downstreams.end(); i++) {
- chop_conn_t *conn = *i;
- /* We can only use candidates that have a steg target already. */
- if (conn->steg) {
- /* Find the connections whose transmit rooms are closest to the
- desired transmission length from both directions. */
- size_t room = conn->steg->transmit_room(conn);
- log_debug(conn, "offers %lu bytes (%s)", (unsigned long)room,
- conn->steg->name());
-
- if (room <= CHOP_BLOCK_OVERHD)
- room = 0;
- else
- room -= CHOP_BLOCK_OVERHD;
-
- if (room > CHOP_MAX_DATA)
- room = CHOP_MAX_DATA;
-
- if (room >= desired) {
- if (room < minabove) {
- minabove = room;
- targabove = conn;
- }
- } else {
- if (room > maxbelow) {
- maxbelow = room;
- targbelow = conn;
- }
- }
- } else {
- log_debug(conn, "offers 0 bytes (no steg)");
+ block_header(evbuffer *buf, ecb_decryptor &dc)
+ {
+ if (evbuffer_copyout(buf, ciphr, sizeof ciphr) != sizeof ciphr) {
+ memset(clear, 0xFF, sizeof clear);
+ memset(ciphr, 0xFF, sizeof ciphr);
+ return;
}
+ dc.decrypt(clear, ciphr);
}
- /* If we have a connection that can take all the data, use it.
- Otherwise, use the connection that can take as much of the data
- as possible. As a special case, if no connection can take data,
- targbelow, targabove, maxbelow, and minabove will all still have
- their initial values, so we'll return NULL and set blocksize to 0,
- which callers know how to handle. */
- if (targabove) {
- *blocksize = minabove;
- return targabove;
- } else {
- *blocksize = maxbelow;
- return targbelow;
- }
-}
-
-static int
-chop_send_block(chop_conn_t *dest,
- chop_circuit_t *ckt,
- struct evbuffer *source,
- struct evbuffer *block,
- uint16_t length,
- uint16_t flags)
-{
- chop_header hdr;
- struct evbuffer_iovec v;
- uint8_t *p;
-
- log_assert(evbuffer_get_length(block) == 0);
- log_assert(evbuffer_get_length(source) >= length);
- log_assert(dest->steg);
-
- /* We take special care not to modify 'source' if any step fails. */
- if (evbuffer_reserve_space(block,
- length + CHOP_WIRE_HDR_LEN + GCM_TAG_LEN,
- &v, 1) != 1)
- return -1;
- if (v.iov_len < length + CHOP_WIRE_HDR_LEN + GCM_TAG_LEN)
- goto fail;
-
- v.iov_len = length + CHOP_WIRE_HDR_LEN + GCM_TAG_LEN;
-
- hdr.ckt_id = ckt->circuit_id;
- hdr.offset = ckt->send_offset;
- hdr.length = length;
- hdr.flags = flags;
- rng_bytes(hdr.pkt_iv, 8);
- chop_write_header((uint8_t*)v.iov_base, &hdr);
-
- if (evbuffer_copyout(source, (uint8_t *)v.iov_base + CHOP_WIRE_HDR_LEN,
- length) != length)
- goto fail;
-
- p = (uint8_t *)v.iov_base;
- ckt->send_crypt->encrypt(p + 16, p + 16, length + CHOP_WIRE_HDR_LEN - 16,
- p, 16);
-
- if (evbuffer_commit_space(block, &v, 1))
- goto fail;
-
- if (dest->steg->transmit(block, dest))
- goto fail_committed;
-
- if (evbuffer_drain(source, length))
- /* this really should never happen, and we can't recover from it */
- log_abort(dest, "evbuffer_drain failed"); /* does not return */
-
- /* Cancel the must-transmit timer if it's pending; we have transmitted. */
- if (dest->must_transmit_timer)
- evtimer_del(dest->must_transmit_timer);
-
- if (!(flags & CHOP_F_CHAFF))
- ckt->send_offset += length;
- if (flags & CHOP_F_SYN)
- ckt->sent_syn = true;
- if (flags & CHOP_F_FIN)
- ckt->sent_fin = true;
- log_debug(dest, "sent %lu+%u byte block [flags %04hx]",
- (unsigned long)CHOP_WIRE_HDR_LEN, length, flags);
-
- return 0;
-
- fail:
- v.iov_len = 0;
- evbuffer_commit_space(block, &v, 1);
- fail_committed:
- evbuffer_drain(block, evbuffer_get_length(block));
- log_warn(dest, "allocation or buffer copy failed");
-
- return -1;
-}
-
-static int
-chop_send_blocks(chop_circuit_t *ckt)
-{
- struct evbuffer *xmit_pending = bufferevent_get_input(ckt->up_buffer);
- struct evbuffer *block;
- chop_conn_t *target;
- size_t avail;
- size_t blocksize;
- uint16_t flags;
+ uint32_t seqno() const
+ {
+ return ((uint32_t(clear[0]) << 24) |
+ (uint32_t(clear[1]) << 16) |
+ (uint32_t(clear[2]) << 8) |
+ (uint32_t(clear[3]) ));
- if (!(block = evbuffer_new())) {
- log_warn(ckt, "allocation failure");
- return -1;
}
- for (;;) {
- avail = evbuffer_get_length(xmit_pending);
- flags = ckt->sent_syn ? 0 : CHOP_F_SYN;
-
- log_debug(ckt, "%lu bytes to send", (unsigned long)avail);
-
- if (avail == 0)
- break;
-
- target = chop_pick_connection(ckt, avail, &blocksize);
- if (!target) {
- log_debug(ckt, "no target connection available");
- /* this is not an error; it can happen e.g. when the server has
- something to send immediately and the client hasn't spoken yet */
- break;
- }
-
- if (avail <= blocksize) {
- blocksize = avail;
- if (ckt->upstream_eof && !ckt->sent_fin)
- flags |= CHOP_F_FIN;
- }
-
- if (chop_send_block(target, ckt, xmit_pending, block, blocksize, flags)) {
- evbuffer_free(block);
- return -1;
- }
+ size_t dlen() const
+ {
+ return ((uint16_t(clear[4]) << 8) |
+ (uint16_t(clear[5]) ));
}
- evbuffer_free(block);
- avail = evbuffer_get_length(xmit_pending);
- if (avail)
- log_debug(ckt, "%lu bytes still waiting to be sent", (unsigned long)avail);
- return 0;
-}
-
-static int
-chop_send_targeted(chop_circuit_t *ckt, chop_conn_t *target, size_t blocksize)
-{
- struct evbuffer *xmit_pending = bufferevent_get_input(ckt->up_buffer);
- size_t avail = evbuffer_get_length(xmit_pending);
- struct evbuffer *block = evbuffer_new();
- uint16_t flags = 0;
-
- log_debug(target, "%lu bytes available, %lu bytes room",
- (unsigned long)avail, (unsigned long)blocksize);
- if (!block) {
- log_warn(target, "allocation failure");
- return -1;
+ size_t plen() const
+ {
+ return ((uint16_t(clear[6]) << 8) |
+ (uint16_t(clear[7]) ));
}
- if (!ckt->sent_syn)
- flags |= CHOP_F_SYN;
-
- if (avail) {
- if (avail <= blocksize) {
- blocksize = avail;
- if (ckt->upstream_eof && !ckt->sent_fin)
- flags |= CHOP_F_FIN;
- }
-
-
- if (chop_send_block(target, ckt, xmit_pending, block, blocksize, flags)) {
- evbuffer_free(block);
- return -1;
- }
-
- evbuffer_free(block);
- avail = evbuffer_get_length(xmit_pending);
- if (avail)
- log_debug(ckt, "%lu bytes still waiting to be sent",
- (unsigned long)avail);
- return 0;
-
- } else {
- struct evbuffer *chaff;
- struct evbuffer_iovec v;
-
- if (blocksize > CHOP_MAX_CHAFF)
- blocksize = CHOP_MAX_CHAFF;
-
- blocksize = rng_range(1, blocksize + 1);
- log_debug(target, "generating %lu bytes chaff", (unsigned long)blocksize);
-
- chaff = evbuffer_new();
- if (!chaff ||
- evbuffer_reserve_space(chaff, blocksize, &v, 1) != 1 ||
- v.iov_len < blocksize)
- goto fail;
-
- v.iov_len = blocksize;
- memset(v.iov_base, 0, v.iov_len);
- if (evbuffer_commit_space(chaff, &v, 1))
- goto fail;
-
- flags |= CHOP_F_CHAFF;
- if (ckt->upstream_eof && !ckt->sent_fin)
- flags |= CHOP_F_FIN;
- if (chop_send_block(target, ckt, chaff, block, blocksize, flags))
- goto fail;
-
- evbuffer_free(chaff);
- evbuffer_free(block);
- return 0;
-
- fail:
- log_warn(target, "failed to construct chaff block");
- if (chaff) evbuffer_free(chaff);
- if (block) evbuffer_free(block);
- return -1;
+ size_t total_len() const
+ {
+ return HEADER_LEN + TRAILER_LEN + dlen() + plen();
}
-}
-
-static int
-chop_send_chaff(chop_circuit_t *ckt)
-{
- size_t room;
- chop_conn_t *target = chop_pick_connection(ckt, 1, &room);
- if (!target) {
- /* If we have connections and we can't send, that means we're waiting
- for the server to respond. Just wait. */
- return 0;
+ opcode_t opcode() const
+ {
+ return opcode_t(clear[8]);
}
- return chop_send_targeted(ckt, target, room);
-}
-
-static void
-must_transmit_timer_cb(evutil_socket_t, short, void *arg)
-{
- chop_conn_t *conn = static_cast<chop_conn_t*>(arg);
- size_t room;
- if (!conn->upstream) {
- log_debug(conn, "must transmit, but no circuit (stale connection)");
- conn_do_flush(conn);
- return;
+ bool valid(uint64_t window) const
+ {
+ // This check must run in constant time.
+ uint8_t ck = (clear[ 9] | clear[10] | clear[11] | clear[12] |
+ clear[13] | clear[14] | clear[15]);
+ uint32_t delta = seqno() - window;
+ ck |= !!(delta & ~uint32_t(0xFF));
+ return !ck;
}
- if (!conn->steg) {
- log_warn(conn, "must transmit, but no steg module available");
- return;
- }
- room = conn->steg->transmit_room(conn);
- if (room <= CHOP_BLOCK_OVERHD) {
- log_warn(conn, "must transmit, but no transmit room");
- return;
+ const uint8_t *nonce() const
+ {
+ return ciphr;
}
- log_debug(conn, "must transmit");
- chop_send_targeted(conn->upstream, conn, room - CHOP_BLOCK_OVERHD);
-}
+ const uint8_t *cleartext() const
+ {
+ return clear;
+ }
+};
-/* Receive subroutines. */
+/* Most of a block's header information is processed before it reaches
+ the reassembly queue; the only things the queue needs to record are
+ the sequence number (which is stored implictly), the opcode, and an
+ evbuffer holding the data section. Zero-data blocks still get an
+ evbuffer, for simplicity's sake: a reassembly queue element holds a
+ received block if and only if its data pointer is non-null.
-/* True if s < t (mod 2**32). */
-static inline bool
-mod32_lt(uint32_t s, uint32_t t)
-{
- uint32_t d = t - s;
- return 0 < d && d < 0x80000000u;
-}
+ The reassembly queue is a 256-element circular buffer of
+ 'reassembly_elt' structs. This corresponds to the 256-element
+ sliding window of sequence numbers which may legitimately be
+ received at any time. */
-/* True if s <= t (mod 2**32). */
-static inline bool
-mod32_le(uint32_t s, uint32_t t)
+struct reassembly_elt
{
- uint32_t d = t - s;
- return d < 0x80000000u;
-}
+ evbuffer *data;
+ opcode_t op;
+};
-/** Add BLOCK to the reassembly queue at the appropriate location
- and merge adjacent blocks to the extent possible. */
-static int
-chop_reassemble_block(chop_circuit_t *ckt, struct evbuffer *block,
- chop_header *hdr)
+class reassembly_queue
{
- chop_reassembly_elt *queue = &ckt->reassembly_queue;
- chop_reassembly_elt *p, *q;
-
- if (hdr->flags & CHOP_F_CHAFF) {
- /* Chaff goes on the reassembly queue if it carries any flags that
- must be processed in sequence (SYN, FIN), but we throw away its
- contents. Doing all chaff-handling here simplifies the caller
- at the expense of slightly more buffer-management overhead. */
- if (!(hdr->flags & (CHOP_F_SYN|CHOP_F_FIN))) {
- log_debug(ckt, "discarding chaff with no flags");
- evbuffer_free(block);
- return 0;
- }
+ reassembly_elt cbuf[256];
+ uint32_t next_to_process;
- hdr->length = 0;
- evbuffer_drain(block, evbuffer_get_length(block));
- log_debug(ckt, "chaff with flags, treating length as 0");
- }
+ reassembly_queue(const reassembly_queue&) DELETE_METHOD;
+ reassembly_queue& operator=(const reassembly_queue&) DELETE_METHOD;
- /* SYN must occur at offset zero, may not be duplicated, and if we
- already have anything on the reassembly queue, it must come
- logically after this block. */
- if ((hdr->flags & CHOP_F_SYN) &&
- (hdr->offset > 0 ||
- (queue->next != queue &&
- ((queue->next->flags & CHOP_F_SYN) ||
- !mod32_le(hdr->offset + hdr->length, queue->next->offset))))) {
- log_warn(ckt, "protocol error: inappropriate SYN block");
- return -1;
+public:
+ reassembly_queue()
+ : next_to_process(0)
+ {
+ memset(cbuf, 0, sizeof cbuf);
}
- /* FIN may not be duplicated and must occur logically after everything
- we've already received. */
- if ((hdr->flags & CHOP_F_FIN) && queue->prev != queue &&
- ((queue->prev->flags & CHOP_F_FIN) ||
- !mod32_le(queue->prev->offset + queue->prev->length, hdr->offset))) {
- log_warn(ckt, "protocol error: inappropriate FIN block");
- return -1;
+ ~reassembly_queue()
+ {
+ for (int i = 0; i < 256; i++)
+ if (cbuf[i].data)
+ evbuffer_free(cbuf[i].data);
}
- /* Non-SYN/FIN must come after any SYN block presently in the queue
- and before any FIN block presently in the queue. */
- if (!(hdr->flags & (CHOP_F_SYN|CHOP_F_FIN)) && queue->next != queue &&
- (((queue->next->flags & CHOP_F_SYN) &&
- !mod32_le(queue->next->offset + queue->next->length, hdr->offset)) ||
- ((queue->prev->flags & CHOP_F_FIN) &&
- !mod32_le(hdr->offset + hdr->length, queue->prev->offset)))) {
- log_warn(ckt, "protocol error: inappropriate normal block");
- return -1;
+ // Remove the next block to be processed from the reassembly queue
+ // and return it. If we are out of blocks or the next block to
+ // process has not yet arrived, return an empty reassembly_elt.
+ // Caller is responsible for freeing the evbuffer in the
+ // reassembly_elt, if any.
+ reassembly_elt
+ remove_next()
+ {
+ reassembly_elt rv = { 0, op_DAT };
+ uint8_t front = next_to_process & 0xFF;
+ if (cbuf[front].data) {
+ rv = cbuf[front];
+ cbuf[front].data = 0;
+ cbuf[front].op = op_DAT;
+ next_to_process++;
+ }
+ return rv;
}
- for (p = queue->next; p != queue; p = p->next) {
- /* Try first to merge the new block into an existing one. */
- if (hdr->offset + hdr->length == p->offset)
- goto grow_front;
-
- if (hdr->offset == p->offset + p->length)
- goto grow_back;
-
- /* Does this block fit in between 'p->prev' and 'p'?
- Note: if 'p->prev->data' is NULL, it is the sentinel,
- and p->prev->offset is meaningless. */
- if (mod32_lt(hdr->offset + hdr->length, p->offset)) {
- if (!p->prev->data ||
- mod32_lt(p->prev->offset + p->prev->length, hdr->offset))
- break;
-
- /* protocol error: this block goes before 'p' but does not fit
- after 'p->prev' */
- log_warn(ckt, "protocol error: %u byte block does not fit at offset %u",
- hdr->length, hdr->offset);
- return -1;
+ // Insert a block into the reassembly queue at sequence number
+ // SEQNO, with opcode OP and data section DATA. Returns true if the
+ // block was successfully added to the queue, false if it is either
+ // outside the acceptable window or duplicates a block already on
+ // the queue (both of these cases indicate protocol errors).
+ // DATA is consumed no matter what the return value is.
+ bool
+ insert(uint32_t seqno, opcode_t op, evbuffer *data, conn_t *conn)
+ {
+ if (seqno - window() > 255) {
+ log_warn(conn, "block outside receive window");
+ evbuffer_free(data);
+ return false;
+ }
+ uint8_t front = next_to_process & 0xFF;
+ uint8_t pos = front + (seqno - window());
+ if (cbuf[pos].data) {
+ log_warn(conn, "duplicate block");
+ evbuffer_free(data);
+ return false;
}
- }
- /* This block goes before, but does not merge with, 'p'.
- Special case: if 'p' is the sentinel, we have not yet checked
- that this block goes after the last block in the list (aka p->prev). */
- if (!p->data && p->prev->data &&
- !mod32_lt(p->prev->offset + p->prev->length, hdr->offset)) {
- log_warn(ckt, "protocol error: %u byte block does not fit at offset %u "
- "(sentinel case)",
- hdr->length, hdr->offset);
- return -1;
+ cbuf[pos].data = data;
+ cbuf[pos].op = op;
+ return true;
}
- q = (chop_reassembly_elt *)xzalloc(sizeof(chop_reassembly_elt));
- q->data = block;
- q->offset = hdr->offset;
- q->length = hdr->length;
- q->flags = hdr->flags;
+ // Return the current lowest acceptable sequence number in the
+ // receive window. This is the value to be passed to
+ // block_header::valid().
+ uint32_t window() const { return next_to_process; }
- q->prev = p->prev;
- q->next = p;
- q->prev->next = q;
- q->next->prev = q;
- return 0;
-
- grow_back:
- if (evbuffer_add_buffer(p->data, block)) {
- log_warn(ckt, "failed to append to existing buffer");
- return -1;
- }
- evbuffer_free(block);
- p->length += hdr->length;
- p->flags |= hdr->flags;
-
- /* Can we now combine 'p' with its successor? */
- while (p->next->data && p->offset + p->length == p->next->offset) {
- q = p->next;
- if (evbuffer_add_buffer(p->data, q->data)) {
- log_warn(ckt, "failed to merge buffers");
- return -1;
+ // As the last step of a rekeying cycle, the expected next sequence number
+ // is reset to zero.
+ void reset()
+ {
+ for (int i = 0; i < 256; i++) {
+ log_assert(!cbuf[i].data);
}
- p->length += q->length;
- p->flags |= q->flags;
-
- evbuffer_free(q->data);
- q->next->prev = q->prev;
- q->prev->next = q->next;
- free(q);
+ next_to_process = 0;
}
- return 0;
+};
- grow_front:
- if (evbuffer_prepend_buffer(p->data, block)) {
- log_warn(ckt, "failed to prepend to existing buffer");
- return -1;
- }
- evbuffer_free(block);
- p->length += hdr->length;
- p->offset -= hdr->length;
- p->flags |= hdr->flags;
-
- /* Can we now combine 'p' with its predecessor? */
- while (p->prev->data && p->offset == p->prev->offset + p->prev->length) {
- q = p->prev;
- if (evbuffer_prepend_buffer(p->data, q->data)) {
- log_warn(ckt, "failed to merge buffers");
- return -1;
- }
- p->length += q->length;
- p->offset -= q->length;
- p->flags |= q->flags;
-
- evbuffer_free(q->data);
- q->next->prev = q->prev;
- q->prev->next = q->next;
- free(q);
- }
+// Protocol objects
- return 0;
-}
+struct chop_config_t;
+struct chop_circuit_t;
-/* Flush as much data toward upstream as we can. */
-static int
-chop_push_to_upstream(chop_circuit_t *ckt)
-{
- /* Only the first reassembly queue entry, if any, can possibly be
- ready to flush (because chop_reassemble_block ensures that there
- are gaps between all queue elements). */
- chop_reassembly_elt *ready = ckt->reassembly_queue.next;
- if (!ready->data || ckt->recv_offset != ready->offset) {
- log_debug(ckt, "no data pushable to upstream yet");
- return 0;
- }
+typedef unordered_map<uint32_t, chop_circuit_t *> chop_circuit_table;
- if (!ckt->received_syn) {
- if (!(ready->flags & CHOP_F_SYN)) {
- log_debug(ckt, "waiting for SYN");
- return 0;
- }
- log_debug(ckt, "processed SYN");
- ckt->received_syn = true;
- }
+struct chop_conn_t : conn_t
+{
+ chop_config_t *config;
+ chop_circuit_t *upstream;
+ steg_t *steg;
+ struct evbuffer *recv_pending;
+ struct event *must_send_timer;
+ bool sent_handshake : 1;
+ bool no_more_transmissions : 1;
+
+ CONN_DECLARE_METHODS(chop);
+
+ int recv_handshake();
+ int send(struct evbuffer *block);
+
+ void send();
+ bool must_send_p() const;
+ static void must_send_timeout(evutil_socket_t, short, void *arg);
+};
- log_debug(ckt, "can push %lu bytes to upstream",
- (unsigned long)evbuffer_get_length(ready->data));
- if (evbuffer_add_buffer(bufferevent_get_output(ckt->up_buffer),
- ready->data)) {
- log_warn(ckt, "failure pushing data to upstream");
- return -1;
+struct chop_circuit_t : circuit_t
+{
+ reassembly_queue recv_queue;
+ unordered_set<chop_conn_t *> downstreams;
+ gcm_encryptor *send_crypt;
+ ecb_encryptor *send_hdr_crypt;
+ gcm_decryptor *recv_crypt;
+ ecb_decryptor *recv_hdr_crypt;
+ chop_config_t *config;
+
+ uint32_t circuit_id;
+ uint32_t send_seq;
+ uint32_t dead_cycles;
+ bool received_fin : 1;
+ bool sent_fin : 1;
+ bool upstream_eof : 1;
+
+ CIRCUIT_DECLARE_METHODS(chop);
+
+ // Shortcut some unnecessary conversions for callers within this file.
+ void add_downstream(chop_conn_t *conn);
+ void drop_downstream(chop_conn_t *conn);
+
+ int send_special(opcode_t f, struct evbuffer *payload);
+ int send_targeted(chop_conn_t *conn);
+ int send_targeted(chop_conn_t *conn, size_t blocksize);
+ int send_targeted(chop_conn_t *conn, size_t d, size_t p, opcode_t f,
+ struct evbuffer *payload);
+
+ chop_conn_t *pick_connection(size_t desired, size_t *blocksize);
+
+ int process_queue();
+ int check_for_eof();
+
+ uint32_t axe_interval() {
+ // This function must always return a number which is larger than
+ // the maximum possible number that *our peer's* flush_interval()
+ // could have returned; otherwise, we might axe the connection when
+ // it was just that there was nothing to say for a while.
+ // For simplicity's sake, right now we hardwire this to be 30 minutes.
+ return 30 * 60 * 1000;
}
-
- ckt->dead_cycles = 0;
- ckt->recv_offset += ready->length;
-
- if (ready->flags & CHOP_F_FIN) {
- log_assert(!ckt->received_fin);
- log_assert(ready->next == &ckt->reassembly_queue);
- ckt->received_fin = true;
- log_debug(ckt, "processed FIN");
- circuit_recv_eof(ckt);
+ uint32_t flush_interval() {
+ // 10*60*1000 lies between 2^19 and 2^20.
+ uint32_t shift = std::max(1u, std::min(19u, dead_cycles));
+ uint32_t xv = std::max(1u, std::min(10u * 60 * 1000, 1u << shift));
+ return rng_range_geom(20 * 60 * 1000, xv) + 100;
}
+};
- log_assert(ready->next == &ckt->reassembly_queue ||
- ready->next->offset != ckt->recv_offset);
- ready->next->prev = ready->prev;
- ready->prev->next = ready->next;
-
- evbuffer_free(ready->data);
- free(ready);
- return 0;
-}
-
-/* Circuit handling */
-
-static int
-chop_find_or_make_circuit(chop_conn_t *conn, uint64_t circuit_id)
+struct chop_config_t : config_t
{
- chop_circuit_table::value_type in(circuit_id, 0);
- std::pair<chop_circuit_table::iterator, bool> out
- = conn->config->circuits.insert(in);
- chop_circuit_t *ck;
-
- if (!out.second) { // element already exists
- if (!out.first->second) {
- log_debug(conn, "stale circuit");
- return 0;
- }
- ck = out.first->second;
- log_debug(conn, "found circuit to %s", ck->up_peer);
- } else {
- ck = dynamic_cast<chop_circuit_t *>(circuit_create(conn->config, 0));
- if (!ck) {
- log_warn(conn, "failed to create new circuit");
- return -1;
- }
- if (circuit_open_upstream(ck)) {
- log_warn(conn, "failed to begin upstream connection");
- delete ck;
- return -1;
- }
- log_debug(conn, "created new circuit to %s", ck->up_peer);
- ck->circuit_id = circuit_id;
- out.first->second = ck;
- }
+ struct evutil_addrinfo *up_address;
+ vector<struct evutil_addrinfo *> down_addresses;
+ vector<const char *> steg_targets;
+ chop_circuit_table circuits;
- ck->add_downstream(conn);
- return 0;
-}
+ CONFIG_DECLARE_METHODS(chop);
+};
-/* Protocol methods */
+// Configuration methods
chop_config_t::chop_config_t()
{
@@ -833,7 +390,7 @@ chop_config_t::~chop_config_t()
i != down_addresses.end(); i++)
evutil_freeaddrinfo(*i);
- /* The strings in steg_targets are not on the heap. */
+ // The strings in steg_targets are not on the heap.
for (chop_circuit_table::iterator i = circuits.begin();
i != circuits.end(); i++)
@@ -854,28 +411,28 @@ chop_config_t::init(int n_options, const char *const *options)
}
if (!strcmp(options[0], "client")) {
- defport = "48988"; /* bf5c */
- this->mode = LSN_SIMPLE_CLIENT;
+ defport = "48988"; // bf5c
+ mode = LSN_SIMPLE_CLIENT;
listen_up = 1;
} else if (!strcmp(options[0], "socks")) {
- defport = "23548"; /* 5bf5 */
- this->mode = LSN_SOCKS_CLIENT;
+ defport = "23548"; // 5bf5
+ mode = LSN_SOCKS_CLIENT;
listen_up = 1;
} else if (!strcmp(options[0], "server")) {
- defport = "11253"; /* 2bf5 */
- this->mode = LSN_SIMPLE_SERVER;
+ defport = "11253"; // 2bf5
+ mode = LSN_SIMPLE_SERVER;
listen_up = 0;
} else
goto usage;
- this->up_address = resolve_address_port(options[1], 1, listen_up, defport);
- if (!this->up_address) {
+ up_address = resolve_address_port(options[1], 1, listen_up, defport);
+ if (!up_address) {
log_warn("chop: invalid up address: %s", options[1]);
goto usage;
}
- /* From here on out, arguments alternate between downstream
- addresses and steg targets. */
+ // From here on out, arguments alternate between downstream
+ // addresses and steg targets.
for (i = 2; i < n_options; i++) {
struct evutil_addrinfo *addr =
resolve_address_port(options[i], 1, !listen_up, NULL);
@@ -883,7 +440,7 @@ chop_config_t::init(int n_options, const char *const *options)
log_warn("chop: invalid down address: %s", options[i]);
goto usage;
}
- this->down_addresses.push_back(addr);
+ down_addresses.push_back(addr);
i++;
if (i == n_options) {
@@ -895,7 +452,7 @@ chop_config_t::init(int n_options, const char *const *options)
log_warn("chop: steganographer '%s' not supported", options[i]);
goto usage;
}
- this->steg_targets.push_back(options[i]);
+ steg_targets.push_back(options[i]);
}
return true;
@@ -917,12 +474,12 @@ chop_config_t::init(int n_options, const char *const *options)
struct evutil_addrinfo *
chop_config_t::get_listen_addrs(size_t n)
{
- if (this->mode == LSN_SIMPLE_SERVER) {
- if (n < this->down_addresses.size())
- return this->down_addresses[n];
+ if (mode == LSN_SIMPLE_SERVER) {
+ if (n < down_addresses.size())
+ return down_addresses[n];
} else {
if (n == 0)
- return this->up_address;
+ return up_address;
}
return 0;
}
@@ -930,58 +487,96 @@ chop_config_t::get_listen_addrs(size_t n)
struct evutil_addrinfo *
chop_config_t::get_target_addrs(size_t n)
{
- if (this->mode == LSN_SIMPLE_SERVER) {
+ if (mode == LSN_SIMPLE_SERVER) {
if (n == 0)
- return this->up_address;
+ return up_address;
} else {
- if (n < this->down_addresses.size())
- return this->down_addresses[n];
+ if (n < down_addresses.size())
+ return down_addresses[n];
}
return NULL;
}
+// Circuit methods
+
+const char passphrase[] =
+ "did you buy one of therapist reawaken chemists continually gamma pacifies?";
+
circuit_t *
chop_config_t::circuit_create(size_t)
{
chop_circuit_t *ckt = new chop_circuit_t;
ckt->config = this;
- if (this->mode == LSN_SIMPLE_SERVER) {
- ckt->send_crypt = gcm_encryptor::create(s2c_key, 16);
- ckt->recv_crypt = gcm_decryptor::create(c2s_key, 16);
- } else {
- ckt->send_crypt = gcm_encryptor::create(c2s_key, 16);
- ckt->recv_crypt = gcm_decryptor::create(s2c_key, 16);
- while (!ckt->circuit_id)
- rng_bytes((uint8_t *)&ckt->circuit_id, sizeof(uint64_t));
-
- chop_circuit_table::value_type in(ckt->circuit_id, 0);
- std::pair<chop_circuit_table::iterator, bool> out = circuits.insert(in);
- log_assert(out.second);
- out.first->second = ckt;
- }
- return ckt;
-}
+ key_generator *kgen =
+ key_generator::from_passphrase((const uint8_t *)passphrase,
+ sizeof(passphrase) - 1,
+ 0, 0, 0, 0);
+ uint8_t kbuf[16];
-chop_circuit_t::chop_circuit_t()
-{
- this->reassembly_queue.next = &this->reassembly_queue;
- this->reassembly_queue.prev = &this->reassembly_queue;
+ if (mode == LSN_SIMPLE_SERVER) {
+ log_assert(kgen->generate(kbuf, 16) == 16);
+ ckt->send_crypt = gcm_encryptor::create(kbuf, 16);
+
+ log_assert(kgen->generate(kbuf, 16) == 16);
+ ckt->send_hdr_crypt = ecb_encryptor::create(kbuf, 16);
+
+ log_assert(kgen->generate(kbuf, 16) == 16);
+ ckt->recv_crypt = gcm_decryptor::create(kbuf, 16);
+
+ log_assert(kgen->generate(kbuf, 16) == 16);
+ ckt->recv_hdr_crypt = ecb_decryptor::create(kbuf, 16);
+ } else {
+ log_assert(kgen->generate(kbuf, 16) == 16);
+ ckt->recv_crypt = gcm_decryptor::create(kbuf, 16);
+
+ log_assert(kgen->generate(kbuf, 16) == 16);
+ ckt->recv_hdr_crypt = ecb_decryptor::create(kbuf, 16);
+
+ log_assert(kgen->generate(kbuf, 16) == 16);
+ ckt->send_crypt = gcm_encryptor::create(kbuf, 16);
+
+ log_assert(kgen->generate(kbuf, 16) == 16);
+ ckt->send_hdr_crypt = ecb_encryptor::create(kbuf, 16);
+
+ std::pair<chop_circuit_table::iterator, bool> out;
+ do {
+ do {
+ rng_bytes((uint8_t *)&ckt->circuit_id, sizeof(ckt->circuit_id));
+ } while (!ckt->circuit_id);
+
+ out = circuits.insert(make_pair(ckt->circuit_id, (chop_circuit_t *)0));
+ } while (!out.second);
+
+ out.first->second = ckt;
+ }
+
+ memset(kbuf, 0, 16);
+ delete kgen;
+ return ckt;
}
-chop_circuit_t::~chop_circuit_t()
+chop_circuit_t::chop_circuit_t()
{
- chop_reassembly_elt *p, *q, *queue;
- chop_circuit_table::iterator out;
+}
- log_debug(this, "syn%c%c fin%c%c eof%c ds=%lu",
- sent_syn ? '+' : '-', received_syn ? '+' : '-',
- sent_fin ? '+' : '-', received_fin ? '+' : '-',
- upstream_eof ? '+' : '-',
- (unsigned long)downstreams.size());
+chop_circuit_t::~chop_circuit_t()
+{
+ if (!sent_fin || !received_fin || !upstream_eof) {
+ log_warn(this, "destroying active circuit: fin%c%c eof%c ds=%lu",
+ sent_fin ? '+' : '-', received_fin ? '+' : '-',
+ upstream_eof ? '+' : '-',
+ (unsigned long)downstreams.size());
+#ifdef HAVE_EXECINFO_H
+ int n;
+ void *backtracebuf[256];
+ n = backtrace(backtracebuf, sizeof backtracebuf / sizeof(void*));
+ backtrace_symbols_fd(backtracebuf, n, 2);
+#endif
+ }
- for (unordered_set<chop_conn_t *>::iterator i = this->downstreams.begin();
- i != this->downstreams.end(); i++) {
+ for (unordered_set<chop_conn_t *>::iterator i = downstreams.begin();
+ i != downstreams.end(); i++) {
chop_conn_t *conn = *i;
conn->upstream = NULL;
if (evbuffer_get_length(conn->outbound()) > 0)
@@ -990,28 +585,23 @@ chop_circuit_t::~chop_circuit_t()
delete conn;
}
- delete this->send_crypt;
- delete this->recv_crypt;
-
- queue = &this->reassembly_queue;
- for (q = p = queue->next; p != queue; p = q) {
- q = p->next;
- if (p->data)
- evbuffer_free(p->data);
- free(p);
- }
-
- /* The IDs for old circuits are preserved for a while (at present,
- indefinitely; FIXME: purge them on a timer) against the
- possibility that we'll get a junk connection for one of them
- right after we close it (same deal as the TIME_WAIT state in
- TCP). Note that we can hit this case for the *client* if the
- cover protocol includes a mandatory reply to every client
- message and the hidden channel closed s->c before c->s: the
- circuit will get destroyed on the client side after the c->s FIN,
- and the mandatory reply will be to a stale circuit. */
- out = this->config->circuits.find(this->circuit_id);
- log_assert(out != this->config->circuits.end());
+ delete send_crypt;
+ delete send_hdr_crypt;
+ delete recv_crypt;
+ delete recv_hdr_crypt;
+
+ // The IDs for old circuits are preserved for a while (at present,
+ // indefinitely; FIXME: purge them on a timer) against the
+ // possibility that we'll get a junk connection for one of them
+ // right after we close it (same deal as the TIME_WAIT state in
+ // TCP). Note that we can hit this case for the *client* if the
+ // cover protocol includes a mandatory reply to every client message
+ // and the hidden channel closed s->c before c->s: the circuit will
+ // get destroyed on the client side after the c->s FIN, and the
+ // mandatory reply will be to a stale circuit.
+ chop_circuit_table::iterator out;
+ out = config->circuits.find(circuit_id);
+ log_assert(out != config->circuits.end());
log_assert(out->second == this);
out->second = NULL;
}
@@ -1019,69 +609,442 @@ chop_circuit_t::~chop_circuit_t()
config_t *
chop_circuit_t::cfg() const
{
- return this->config;
+ return config;
}
void
-chop_circuit_t::add_downstream(conn_t *cn)
+chop_circuit_t::add_downstream(chop_conn_t *conn)
{
- chop_conn_t *conn = dynamic_cast<chop_conn_t *>(cn);
log_assert(conn);
log_assert(!conn->upstream);
-
conn->upstream = this;
- this->downstreams.insert(conn);
+ downstreams.insert(conn);
log_debug(this, "added connection <%d.%d> to %s, now %lu",
- this->serial, conn->serial, conn->peername,
- (unsigned long)this->downstreams.size());
+ serial, conn->serial, conn->peername,
+ (unsigned long)downstreams.size());
circuit_disarm_axe_timer(this);
}
void
-chop_circuit_t::drop_downstream(conn_t *cn)
+chop_circuit_t::add_downstream(conn_t *cn)
+{
+ add_downstream(dynamic_cast<chop_conn_t *>(cn));
+}
+
+void
+chop_circuit_t::drop_downstream(chop_conn_t *conn)
{
- chop_conn_t *conn = dynamic_cast<chop_conn_t *>(cn);
log_assert(conn);
log_assert(conn->upstream == this);
conn->upstream = NULL;
- this->downstreams.erase(conn);
+ downstreams.erase(conn);
log_debug(this, "dropped connection <%d.%d> to %s, now %lu",
- this->serial, conn->serial, conn->peername,
- (unsigned long)this->downstreams.size());
- /* If that was the last connection on this circuit AND we've both
- received and sent a FIN, close the circuit. Otherwise, if we're
- the server, arm a timer that will kill off this circuit in a
- little while if no new connections happen (we might've lost all
- our connections to protocol errors, or because the steg modules
- wanted them closed); if we're the client, send chaff in a bit,
- to enable further transmissions from the server. */
- if (this->downstreams.empty()) {
- if (this->sent_fin && this->received_fin) {
- if (evbuffer_get_length(bufferevent_get_output(this->up_buffer)) > 0)
- /* this may already have happened, but there's no harm in
- doing it again */
+ serial, conn->serial, conn->peername,
+ (unsigned long)downstreams.size());
+ // If that was the last connection on this circuit AND we've both
+ // received and sent a FIN, close the circuit. Otherwise, if we're
+ // the server, arm a timer that will kill off this circuit in a
+ // little while if no new connections happen (we might've lost all
+ // our connections to protocol errors, or because the steg modules
+ // wanted them closed); if we're the client, send chaff in a bit,
+ // to enable further transmissions from the server.
+ if (downstreams.empty()) {
+ if (sent_fin && received_fin) {
+ if (evbuffer_get_length(bufferevent_get_output(up_buffer)) > 0)
+ // this may already have happened, but there's no harm in
+ // doing it again
circuit_do_flush(this);
else
delete this;
- } else if (this->config->mode == LSN_SIMPLE_SERVER) {
- circuit_arm_axe_timer(this, this->axe_interval());
+ } else if (config->mode == LSN_SIMPLE_SERVER) {
+ circuit_arm_axe_timer(this, axe_interval());
} else {
- circuit_arm_flush_timer(this, this->flush_interval());
+ circuit_arm_flush_timer(this, flush_interval());
+ }
+ }
+}
+
+void
+chop_circuit_t::drop_downstream(conn_t *cn)
+{
+ drop_downstream(dynamic_cast<chop_conn_t *>(cn));
+}
+
+int
+chop_circuit_t::send()
+{
+ circuit_disarm_flush_timer(this);
+
+ if (downstreams.empty()) {
+ // We have no connections, but we must send. If we're the client,
+ // reopen our outbound connections; the on-connection event will
+ // bring us back here. If we're the server, we have to just
+ // twiddle our thumbs and hope the client reconnects.
+ log_debug(this, "no downstream connections");
+ if (config->mode != LSN_SIMPLE_SERVER)
+ circuit_reopen_downstreams(this);
+ else
+ circuit_arm_axe_timer(this, axe_interval());
+ return 0;
+ }
+
+ struct evbuffer *xmit_pending = bufferevent_get_input(up_buffer);
+ size_t avail = evbuffer_get_length(xmit_pending);
+ size_t avail0 = avail;
+
+ // Send at least one block, even if there is no real data to send.
+ do {
+ log_debug(this, "%lu bytes to send", (unsigned long)avail);
+ size_t blocksize;
+ chop_conn_t *target = pick_connection(avail, &blocksize);
+ if (!target) {
+ // this is not an error; it can happen e.g. when the server has
+ // something to send immediately and the client hasn't spoken yet
+ log_debug(this, "no target connection available");
+ break;
+ }
+
+ if (send_targeted(target, blocksize))
+ return -1;
+
+ avail = evbuffer_get_length(xmit_pending);
+ } while (avail > 0);
+
+ if (avail0 > avail) // we transmitted some real data
+ dead_cycles = 0;
+ else {
+ dead_cycles++;
+ log_debug(this, "%u dead cycles", dead_cycles);
+ }
+
+ return check_for_eof();
+}
+
+int
+chop_circuit_t::send_eof()
+{
+ upstream_eof = true;
+ return send();
+}
+
+int
+chop_circuit_t::send_special(opcode_t f, struct evbuffer *payload)
+{
+ size_t d = payload ? evbuffer_get_length(payload) : 0;
+ size_t blocksize;
+ log_assert(d <= SECTION_LEN);
+ chop_conn_t *conn = pick_connection(d, &blocksize);
+
+ if (!conn || (blocksize - MIN_BLOCK_SIZE < d)) {
+ log_warn("no usable connection for special block "
+ "(opcode %02x, need %lu bytes, have %lu)",
+ (unsigned int)f, (unsigned long)(d + MIN_BLOCK_SIZE),
+ (unsigned long)blocksize);
+ return -1;
+ }
+
+ return send_targeted(conn, d, (blocksize - MIN_BLOCK_SIZE) - d, f, payload);
+}
+
+int
+chop_circuit_t::send_targeted(chop_conn_t *conn)
+{
+ size_t avail = evbuffer_get_length(bufferevent_get_input(up_buffer));
+ if (avail > SECTION_LEN)
+ avail = SECTION_LEN;
+ avail += MIN_BLOCK_SIZE;
+
+ size_t room = conn->steg->transmit_room(conn);
+ if (room < MIN_BLOCK_SIZE) {
+ log_warn(conn, "send() called without enough transmit room "
+ "(have %lu, need %lu)", (unsigned long)room,
+ (unsigned long)MIN_BLOCK_SIZE);
+ return -1;
+ }
+ log_debug(conn, "offers %lu bytes (%s)", (unsigned long)room,
+ conn->steg->name());
+
+ if (room < avail)
+ avail = room;
+
+ return send_targeted(conn, avail);
+}
+
+int
+chop_circuit_t::send_targeted(chop_conn_t *conn, size_t blocksize)
+{
+ log_assert(blocksize >= MIN_BLOCK_SIZE && blocksize <= MAX_BLOCK_SIZE);
+
+ struct evbuffer *xmit_pending = bufferevent_get_input(up_buffer);
+ size_t avail = evbuffer_get_length(xmit_pending);
+ opcode_t op = op_DAT;
+
+ if (avail > SECTION_LEN)
+ avail = SECTION_LEN;
+ else if (upstream_eof && !sent_fin)
+ // this block will carry the last byte of real data to be sent in
+ // this direction; mark it as such
+ op = op_FIN;
+
+ return send_targeted(conn, avail, (blocksize - MIN_BLOCK_SIZE) - avail,
+ op, xmit_pending);
+}
+
+int
+chop_circuit_t::send_targeted(chop_conn_t *conn, size_t d, size_t p, opcode_t f,
+ struct evbuffer *payload)
+{
+ log_assert(payload || d == 0);
+ log_assert(d <= SECTION_LEN);
+ log_assert(p <= SECTION_LEN);
+
+ struct evbuffer *block = evbuffer_new();
+ if (!block) {
+ log_warn(conn, "memory allocation failure");
+ return -1;
+ }
+
+ size_t blocksize = d + p + MIN_BLOCK_SIZE;
+ struct evbuffer_iovec v;
+ if (evbuffer_reserve_space(block, blocksize, &v, 1) != 1 ||
+ v.iov_len < blocksize) {
+ log_warn(conn, "memory allocation failure");
+ return -1;
+ }
+ v.iov_len = blocksize;
+
+ block_header hdr(send_seq, d, p, f, *send_hdr_crypt);
+ log_assert(hdr.valid(send_seq));
+ memcpy(v.iov_base, hdr.nonce(), HEADER_LEN);
+
+ uint8_t encodebuf[SECTION_LEN*2];
+ if (payload) {
+ if (evbuffer_copyout(payload, encodebuf, d) != (ssize_t)d) {
+ log_warn(conn, "failed to extract payload");
+ evbuffer_free(block);
+ return -1;
+ }
+ }
+ memset(encodebuf + d, 0, p);
+ send_crypt->encrypt((uint8_t *)v.iov_base + HEADER_LEN, encodebuf,
+ d + p, hdr.nonce(), HEADER_LEN);
+ if (evbuffer_commit_space(block, &v, 1)) {
+ log_warn(conn, "failed to commit block buffer");
+ evbuffer_free(block);
+ return -1;
+ }
+
+ log_debug(conn, "transmitting block %u <d=%lu p=%lu f=%02x>",
+ hdr.seqno(), (unsigned long)hdr.dlen(), (unsigned long)hdr.plen(),
+ (uint8_t)hdr.opcode());
+
+ if (conn->send(block)) {
+ evbuffer_free(block);
+ return -1;
+ }
+
+ evbuffer_free(block);
+ evbuffer_drain(payload, d);
+
+ send_seq++;
+ if (f == op_FIN)
+ sent_fin = true;
+ return 0;
+}
+
+// N.B. 'desired' is the desired size of the _data section_, and
+// 'blocksize' on output is the size to make the _entire block_.
+chop_conn_t *
+chop_circuit_t::pick_connection(size_t desired, size_t *blocksize)
+{
+ size_t maxbelow = 0;
+ size_t minabove = MAX_BLOCK_SIZE + 1;
+ chop_conn_t *targbelow = 0;
+ chop_conn_t *targabove = 0;
+
+ if (desired > SECTION_LEN)
+ desired = SECTION_LEN;
+
+ desired += MIN_BLOCK_SIZE;
+
+ log_debug(this, "target block size %lu bytes", (unsigned long)desired);
+
+ // Find the best fit for the desired transmission from all the
+ // outbound connections' transmit rooms.
+ for (unordered_set<chop_conn_t *>::iterator i = downstreams.begin();
+ i != downstreams.end(); i++) {
+ chop_conn_t *conn = *i;
+ // We can only use candidates that have a steg target already.
+ if (conn->steg) {
+ // Find the connections whose transmit rooms are closest to the
+ // desired transmission length from both directions.
+ size_t room = conn->steg->transmit_room(conn);
+
+ if (room <= MIN_BLOCK_SIZE)
+ room = 0;
+
+ if (room > MAX_BLOCK_SIZE)
+ room = MAX_BLOCK_SIZE;
+
+ log_debug(conn, "offers %lu bytes (%s)", (unsigned long)room,
+ conn->steg->name());
+
+ if (room >= desired) {
+ if (room < minabove) {
+ minabove = room;
+ targabove = conn;
+ }
+ } else {
+ if (room > maxbelow) {
+ maxbelow = room;
+ targbelow = conn;
+ }
+ }
+ } else {
+ log_debug(conn, "offers 0 bytes (no steg)");
+ }
+ }
+
+ log_debug(this, "minabove %lu for <%u.%u> maxbelow %lu for <%u.%u>",
+ (unsigned long)minabove, serial, targabove ? targabove->serial :0,
+ (unsigned long)maxbelow, serial, targbelow ? targbelow->serial :0);
+
+ // If we have a connection that can take all the data, use it.
+ // Otherwise, use the connection that can take as much of the data
+ // as possible. As a special case, if no connection can take data,
+ // targbelow, targabove, maxbelow, and minabove will all still have
+ // their initial values, so we'll return NULL and set blocksize to 0,
+ // which callers know how to handle.
+ if (targabove) {
+ *blocksize = desired;
+ return targabove;
+ } else {
+ *blocksize = maxbelow;
+ return targbelow;
+ }
+}
+
+int
+chop_circuit_t::process_queue()
+{
+ reassembly_elt blk;
+ unsigned int count = 0;
+ bool pending_fin = false;
+ bool pending_error = false;
+ bool sent_error = false;
+ while ((blk = recv_queue.remove_next()).data) {
+ switch (blk.op) {
+ case op_FIN:
+ if (received_fin) {
+ log_info(this, "protocol error: duplicate FIN");
+ pending_error = true;
+ break;
+ }
+ log_debug(this, "received FIN");
+ pending_fin = true;
+ // fall through - block may have data
+ case op_DAT:
+ if (evbuffer_get_length(blk.data)) {
+ if (received_fin) {
+ log_info(this, "protocol error: data after FIN");
+ pending_error = true;
+ } else {
+ if (evbuffer_add_buffer(bufferevent_get_output(up_buffer),
+ blk.data)) {
+ log_warn(this, "buffer transfer failure");
+ pending_error = true;
+ }
+ }
+ }
+ break;
+
+ case op_RST:
+ log_info(this, "received RST; disconnecting circuit");
+ circuit_recv_eof(this);
+ pending_error = true;
+ break;
+
+ case op_RK1:
+ case op_RK2:
+ case op_RK3:
+ log_warn(this, "rekeying not yet implemented");
+ pending_error = true;
+ break;
+
+ default:
+ log_warn(this, "protocol error: unknown block opcode %x",
+ (unsigned int)blk.op);
+ pending_error = true;
+ break;
+ }
+
+ evbuffer_free(blk.data);
+
+ if (pending_fin && !received_fin) {
+ circuit_recv_eof(this);
+ received_fin = true;
}
+ if (pending_error && !sent_error) {
+ // there's no point sending an RST in response to an RST or a
+ // duplicate FIN
+ if (blk.op != op_RST && blk.op != op_FIN)
+ send_special(op_RST, 0);
+ sent_error = true;
+ }
+ count++;
}
+
+ log_debug(this, "processed %u blocks", count);
+ if (count > 0)
+ dead_cycles = 0;
+ if (sent_error)
+ return -1;
+
+ // It may have become possible to send queued data or a FIN.
+ if (evbuffer_get_length(bufferevent_get_input(up_buffer))
+ || (upstream_eof && !sent_fin))
+ return send();
+
+ return check_for_eof();
}
+int
+chop_circuit_t::check_for_eof()
+{
+ // If we're at EOF both ways, close all connections, sending first
+ // if necessary.
+ if (sent_fin && received_fin) {
+ circuit_disarm_flush_timer(this);
+ for (unordered_set<chop_conn_t *>::iterator i = downstreams.begin();
+ i != downstreams.end(); i++) {
+ chop_conn_t *conn = *i;
+ if (conn->must_send_p())
+ conn->send();
+ conn_send_eof(conn);
+ }
+ }
+
+
+ // If we're the client we have to keep trying to talk as long as we
+ // haven't both sent and received a FIN, or we might deadlock.
+ else if (config->mode != LSN_SIMPLE_SERVER)
+ circuit_arm_flush_timer(this, flush_interval());
+
+ return 0;
+}
+
+// Connection methods
+
conn_t *
chop_config_t::conn_create(size_t index)
{
chop_conn_t *conn = new chop_conn_t;
conn->config = this;
- conn->steg = steg_new(this->steg_targets.at(index),
- this->mode != LSN_SIMPLE_SERVER);
+ conn->steg = steg_new(steg_targets.at(index), mode != LSN_SIMPLE_SERVER);
if (!conn->steg) {
free(conn);
return 0;
@@ -1097,271 +1060,239 @@ chop_conn_t::chop_conn_t()
chop_conn_t::~chop_conn_t()
{
- if (this->upstream)
- this->upstream->drop_downstream(this);
- if (this->steg)
- delete this->steg;
- if (this->must_transmit_timer)
- event_free(this->must_transmit_timer);
- evbuffer_free(this->recv_pending);
+ if (upstream)
+ upstream->drop_downstream(this);
+ if (steg)
+ delete steg;
+ if (must_send_timer)
+ event_free(must_send_timer);
+ evbuffer_free(recv_pending);
}
circuit_t *
chop_conn_t::circuit() const
{
- return this->upstream;
+ return upstream;
}
int
chop_conn_t::maybe_open_upstream()
{
- /* We can't open the upstream until we have a circuit ID. */
+ // We can't open the upstream until we have a circuit ID.
+ return 0;
+}
+
+int
+chop_conn_t::send(struct evbuffer *block)
+{
+ if (!sent_handshake && config->mode != LSN_SIMPLE_SERVER) {
+ if (!upstream || upstream->circuit_id == 0)
+ log_abort(this, "handshake: can't happen: up%c cid=%u",
+ upstream ? '+' : '-',
+ upstream ? upstream->circuit_id : 0);
+ if (evbuffer_prepend(block, (void *)&upstream->circuit_id,
+ sizeof(upstream->circuit_id))) {
+ log_warn(this, "failed to prepend handshake to first block");
+ return -1;
+ }
+ }
+
+ if (steg->transmit(block, this)) {
+ log_warn(this, "failed to transmit block");
+ return -1;
+ }
+ sent_handshake = true;
+ if (must_send_timer)
+ evtimer_del(must_send_timer);
return 0;
}
int
chop_conn_t::handshake()
{
- /* Chop has no handshake as such, but like dsteg, we need to send
- _something_ from the client on at least one of the channels
- shortly after connection, because the server doesn't know which
- connections go with which circuits till it hears from us, _and_
- it doesn't know what steganography to use. We use a 1ms timeout
- instead of a 10ms timeout as in dsteg, because unlike there, the
- server can't even _connect to its upstream_ till it gets the
- first packet from the client. */
- if (this->config->mode != LSN_SIMPLE_SERVER)
- circuit_arm_flush_timer(this->upstream, 1);
+ // The actual handshake is generated in chop_conn_t::send so that it
+ // can be merged with a block if possible; however, we use this hook
+ // to ensure that the client sends _something_ ASAP after each new
+ // connection, because the server can't forward traffic, or even
+ // open a socket to its own upstream, until it knows which circuit
+ // to associate this new connection with. Note that in some cases
+ // it's possible for us to have _already_ sent something on this
+ // connection by the time we get called back! Don't do it twice.
+ if (config->mode != LSN_SIMPLE_SERVER && !sent_handshake)
+ send();
return 0;
}
int
-chop_circuit_t::send()
+chop_conn_t::recv_handshake()
{
- circuit_disarm_flush_timer(this);
+ log_assert(!upstream);
+ log_assert(config->mode == LSN_SIMPLE_SERVER);
- if (this->downstreams.empty()) {
- /* We have no connections, but we must send. If we're the client,
- reopen our outbound connections; the on-connection event will
- bring us back here. If we're the server, we have to just
- twiddle our thumbs and hope the client reconnects. */
- log_debug(this, "no downstream connections");
- if (this->config->mode != LSN_SIMPLE_SERVER)
- circuit_reopen_downstreams(this);
- else
- circuit_arm_axe_timer(this, this->axe_interval());
- return 0;
- }
+ uint32_t circuit_id;
+ if (evbuffer_remove(recv_pending, (void *)&circuit_id,
+ sizeof circuit_id) != sizeof circuit_id)
+ return -1;
- if (evbuffer_get_length(bufferevent_get_input(this->up_buffer)) == 0) {
- /* must-send timer expired and we still have nothing to say; send chaff */
- if (chop_send_chaff(this))
- return -1;
- this->dead_cycles++;
- log_debug(this, "%u dead cycles", this->dead_cycles);
- } else {
- if (chop_send_blocks(this))
- return -1;
- this->dead_cycles = 0;
- }
+ chop_circuit_table::value_type in(circuit_id, 0);
+ std::pair<chop_circuit_table::iterator, bool> out
+ = this->config->circuits.insert(in);
+ chop_circuit_t *ck;
- /* If we're at EOF, close all connections (sending first if
- necessary). If we're the client we have to keep trying to talk
- as long as we haven't both sent and received a FIN, or we might
- deadlock. */
- if (this->sent_fin && this->received_fin) {
- for (unordered_set<chop_conn_t *>::iterator i = this->downstreams.begin();
- i != this->downstreams.end(); i++) {
- chop_conn_t *conn = *i;
- if (conn->must_transmit_timer &&
- evtimer_pending(conn->must_transmit_timer, NULL))
- must_transmit_timer_cb(-1, 0, conn);
- conn_send_eof(conn);
+ if (!out.second) { // element already exists
+ if (!out.first->second) {
+ log_debug(this, "stale circuit");
+ return 0;
}
+ ck = out.first->second;
+ log_debug(this, "found circuit to %s", ck->up_peer);
} else {
- if (this->config->mode != LSN_SIMPLE_SERVER)
- circuit_arm_flush_timer(this, this->flush_interval());
+ ck = dynamic_cast<chop_circuit_t *>(circuit_create(this->config, 0));
+ if (!ck) {
+ log_warn(this, "failed to create new circuit");
+ return -1;
+ }
+ if (circuit_open_upstream(ck)) {
+ log_warn(this, "failed to begin upstream connection");
+ delete ck;
+ return -1;
+ }
+ log_debug(this, "created new circuit to %s", ck->up_peer);
+ ck->circuit_id = circuit_id;
+ out.first->second = ck;
}
- return 0;
-}
-int
-chop_circuit_t::send_eof()
-{
- this->upstream_eof = true;
- return this->send();
+ ck->add_downstream(this);
+ return 0;
}
int
chop_conn_t::recv()
{
- chop_circuit_t *ckt;
- chop_header hdr;
- struct evbuffer *block;
- size_t avail;
- uint8_t decodebuf[CHOP_MAX_DATA + CHOP_WIRE_HDR_LEN];
-
- if (this->steg->receive(this, this->recv_pending))
+ if (steg->receive(this, recv_pending))
return -1;
- if (!this->upstream) {
- log_debug(this, "finding circuit");
- if (chop_peek_circuit_id(this->recv_pending, &hdr)) {
- log_debug(this, "not enough data to find circuit yet");
- return 0;
- }
- if (chop_find_or_make_circuit(this, hdr.ckt_id))
+ if (!upstream) {
+ // Try to receive a handshake.
+ if (recv_handshake())
return -1;
- /* If we get here and this->circuit is not set, this is a connection
- for a stale circuit: that is, a new connection made by the
- client (to draw more data down from the server) that crossed
- with a server-to-client FIN. We can't decrypt the packet, but
- it's either chaff or a protocol error; either way we can just
- discard it. Since we will never reply, call conn_do_flush so
- the connection will be dropped as soon as we receive an EOF. */
- if (!this->upstream) {
- evbuffer_drain(this->recv_pending,
- evbuffer_get_length(this->recv_pending));
+
+ // If we get here and ->upstream is not set, this is a connection
+ // for a stale circuit: that is, a new connection made by the
+ // client (to draw more data down from the server) that crossed
+ // with a server-to-client FIN, the client-to-server FIN already
+ // having been received and processed. We no longer have the keys
+ // to decrypt anything after the handshake, but it's either chaff
+ // or a protocol error. Either way, we can just drop the
+ // connection, possibly sending a response if the cover protocol
+ // requires one.
+ if (!upstream) {
+ evbuffer_drain(recv_pending, evbuffer_get_length(recv_pending));
+ if (must_send_p())
+ send();
conn_do_flush(this);
return 0;
}
}
- ckt = this->upstream;
- log_debug(this, "circuit to %s", ckt->up_peer);
-
+ log_debug(this, "circuit to %s", upstream->up_peer);
for (;;) {
- avail = evbuffer_get_length(this->recv_pending);
+ size_t avail = evbuffer_get_length(recv_pending);
if (avail == 0)
break;
log_debug(this, "%lu bytes available", (unsigned long)avail);
- if (avail < CHOP_WIRE_HDR_LEN) {
- log_debug(this, "incomplete block");
+ if (avail < MIN_BLOCK_SIZE) {
+ log_debug(this, "incomplete block framing");
break;
}
- if (chop_decrypt_header(ckt, this->recv_pending, &hdr))
+ block_header hdr(recv_pending, *upstream->recv_hdr_crypt);
+ if (!hdr.valid(upstream->recv_queue.window())) {
+ const uint8_t *c = hdr.cleartext();
+ log_warn(this, "invalid block header: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
+ c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7],
+ c[8], c[9], c[10], c[11], c[12], c[13], c[14], c[15]);
return -1;
-
- if (avail < CHOP_WIRE_HDR_LEN + GCM_TAG_LEN + hdr.length) {
+ }
+ if (avail < hdr.total_len()) {
log_debug(this, "incomplete block (need %lu bytes)",
- (unsigned long)(CHOP_WIRE_HDR_LEN + GCM_TAG_LEN + hdr.length));
+ (unsigned long)hdr.total_len());
break;
}
- if (ckt->circuit_id != hdr.ckt_id) {
- log_warn(this, "protocol error: circuit id mismatch");
- return -1;
- }
-
- log_debug(this, "receiving block of %lu+%u bytes "
- "[offset %u flags %04hx]",
- (unsigned long)CHOP_WIRE_HDR_LEN + GCM_TAG_LEN,
- hdr.length, hdr.offset, hdr.flags);
-
- if (evbuffer_copyout(this->recv_pending, decodebuf,
- CHOP_WIRE_HDR_LEN + GCM_TAG_LEN + hdr.length)
- != (ssize_t)(CHOP_WIRE_HDR_LEN + GCM_TAG_LEN + hdr.length)) {
+ uint8_t decodebuf[MAX_BLOCK_SIZE];
+ if (evbuffer_drain(recv_pending, HEADER_LEN) ||
+ evbuffer_remove(recv_pending, decodebuf, hdr.total_len() - HEADER_LEN)
+ != (ssize_t)(hdr.total_len() - HEADER_LEN)) {
log_warn(this, "failed to copy block to decode buffer");
return -1;
}
- block = evbuffer_new();
- if (!block || evbuffer_expand(block, hdr.length)) {
- log_warn(this, "allocation failure");
+ if (upstream->recv_crypt->decrypt(decodebuf,
+ decodebuf, hdr.total_len() - HEADER_LEN,
+ hdr.nonce(), HEADER_LEN)) {
+ log_warn("MAC verification failure");
return -1;
}
- if (ckt->recv_crypt
- ->decrypt(decodebuf + 16, decodebuf + 16,
- hdr.length + CHOP_WIRE_HDR_LEN + GCM_TAG_LEN - 16,
- decodebuf, 16)) {
- log_warn(this, "MAC verification failure");
- evbuffer_free(block);
- return -1;
- }
-
- if (evbuffer_add(block, decodebuf + CHOP_WIRE_HDR_LEN, hdr.length)) {
- log_warn(this, "failed to transfer block to reassembly queue");
- evbuffer_free(block);
- return -1;
- }
-
- if (evbuffer_drain(this->recv_pending,
- CHOP_WIRE_HDR_LEN + GCM_TAG_LEN + hdr.length)) {
- log_warn(this, "failed to consume block from wire");
- evbuffer_free(block);
- return -1;
- }
+ log_debug(this, "receiving block %u <d=%lu p=%lu f=%u>",
+ hdr.seqno(), (unsigned long)hdr.dlen(), (unsigned long)hdr.plen(),
+ (unsigned int)hdr.opcode());
- if (chop_reassemble_block(ckt, block, &hdr)) {
- evbuffer_free(block);
+ evbuffer *data = evbuffer_new();
+ if (!data || (hdr.dlen() && evbuffer_add(data, decodebuf, hdr.dlen()))) {
+ log_warn(this, "failed to extract data from decode buffer");
+ evbuffer_free(data);
return -1;
}
- }
-
- if (chop_push_to_upstream(ckt))
- return -1;
- /* It may have now become possible to send queued data. */
- if (evbuffer_get_length(bufferevent_get_input(ckt->up_buffer)))
- ckt->send();
-
- /* If we're at EOF, close all connections (sending first if
- necessary). If we're the client we have to keep trying to talk
- as long as we haven't both sent and received a FIN, or we might
- deadlock. */
- else if (ckt->sent_fin && ckt->received_fin) {
- circuit_disarm_flush_timer(ckt);
- for (unordered_set<chop_conn_t *>::iterator i = ckt->downstreams.begin();
- i != ckt->downstreams.end(); i++) {
- chop_conn_t *conn = *i;
- if (conn->must_transmit_timer &&
- evtimer_pending(conn->must_transmit_timer, NULL))
- must_transmit_timer_cb(-1, 0, conn);
- conn_send_eof(conn);
- }
- } else {
- if (ckt->config->mode != LSN_SIMPLE_SERVER)
- circuit_arm_flush_timer(ckt, ckt->flush_interval());
+ if (!upstream->recv_queue.insert(hdr.seqno(), hdr.opcode(), data, this))
+ return -1; // insert() logs an error
}
- return 0;
+ return upstream->process_queue();
}
int
chop_conn_t::recv_eof()
{
- /* EOF on a _connection_ does not mean EOF on a _circuit_.
- EOF on a _circuit_ occurs when chop_push_to_upstream processes a FIN.
- We should only drop the connection from the circuit if we're no
- longer sending in the opposite direction. Also, we should not
- drop the connection if its must-transmit timer is still pending. */
- if (this->upstream) {
- chop_circuit_t *ckt = this->upstream;
-
- if (evbuffer_get_length(this->inbound()) > 0)
- if (this->recv())
- return -1;
-
- if ((ckt->sent_fin || this->no_more_transmissions) &&
- (!this->must_transmit_timer ||
- !evtimer_pending(this->must_transmit_timer, NULL)))
- ckt->drop_downstream(this);
+ // Consume any not-yet-processed incoming data. It's possible for
+ // us to get here before we've processed _any_ data -- including the
+ // handshake! -- from a new connection, so we have to do this before
+ // we look at ->upstream. */
+ if (evbuffer_get_length(inbound()) > 0) {
+ if (recv())
+ return -1;
+ // If there's anything left in the buffer at this point, it's a
+ // protocol error.
+ if (evbuffer_get_length(inbound()) > 0)
+ return -1;
}
+
+ // We should only drop the connection from the circuit if we're no
+ // longer sending covert data in the opposite direction _and_ the
+ // cover protocol does not need us to send a reply (i.e. the
+ // must_send_timer is not pending).
+ if (upstream && (upstream->sent_fin || no_more_transmissions) &&
+ !must_send_p())
+ upstream->drop_downstream(this);
+
return 0;
}
void
chop_conn_t::expect_close()
{
- /* do we need to do something here? */
+ // We currently don't need to do anything here.
+ // FIXME: figure out if this hook is _ever_ useful, and if not, remove it.
}
void
chop_conn_t::cease_transmission()
{
- this->no_more_transmissions = true;
+ no_more_transmissions = true;
+ if (must_send_timer)
+ evtimer_del(must_send_timer);
conn_do_flush(this);
}
@@ -1370,13 +1301,97 @@ chop_conn_t::transmit_soon(unsigned long milliseconds)
{
struct timeval tv;
- log_debug(this, "must transmit within %lu milliseconds", milliseconds);
+ log_debug(this, "must send within %lu milliseconds", milliseconds);
tv.tv_sec = milliseconds / 1000;
tv.tv_usec = (milliseconds % 1000) * 1000;
- if (!this->must_transmit_timer)
- this->must_transmit_timer = evtimer_new(this->config->base,
- must_transmit_timer_cb, this);
- evtimer_add(this->must_transmit_timer, &tv);
+ if (!must_send_timer)
+ must_send_timer = evtimer_new(config->base, must_send_timeout, this);
+ evtimer_add(must_send_timer, &tv);
+}
+
+void
+chop_conn_t::send()
+{
+ if (must_send_timer)
+ evtimer_del(must_send_timer);
+
+ if (!steg) {
+ log_warn(this, "send() called with no steg module available");
+ conn_do_flush(this);
+ return;
+ }
+
+ // When this happens, we must send _even if_ we have no upstream to
+ // provide us with data. For instance, to preserve the cover
+ // protocol, we must send an HTTP reply to each HTTP query that
+ // comes in for a stale circuit.
+ if (upstream) {
+ log_debug(this, "must send");
+ if (upstream->send_targeted(this))
+ conn_do_flush(this);
+
+ } else {
+ log_debug(this, "must send (no upstream)");
+
+ size_t room = steg->transmit_room(this);
+ if (room < MIN_BLOCK_SIZE) {
+ log_warn(this, "send() called without enough transmit room "
+ "(have %lu, need %lu)", (unsigned long)room,
+ (unsigned long)MIN_BLOCK_SIZE);
+ conn_do_flush(this);
+ return;
+ }
+
+ // Since we have no upstream, we can't encrypt anything; instead,
+ // generate random bytes and feed them straight to steg_transmit.
+ struct evbuffer *chaff = evbuffer_new();
+ struct evbuffer_iovec v;
+ if (!chaff || evbuffer_reserve_space(chaff, MIN_BLOCK_SIZE, &v, 1) != 1 ||
+ v.iov_len < MIN_BLOCK_SIZE) {
+ log_warn(this, "memory allocation failed");
+ if (chaff)
+ evbuffer_free(chaff);
+ conn_do_flush(this);
+ return;
+ }
+ v.iov_len = MIN_BLOCK_SIZE;
+ rng_bytes((uint8_t *)v.iov_base, MIN_BLOCK_SIZE);
+ if (evbuffer_commit_space(chaff, &v, 1)) {
+ log_warn(this, "evbuffer_commit_space failed");
+ if (chaff)
+ evbuffer_free(chaff);
+ conn_do_flush(this);
+ return;
+ }
+
+ if (steg->transmit(chaff, this))
+ conn_do_flush(this);
+
+ evbuffer_free(chaff);
+ }
+}
+
+bool
+chop_conn_t::must_send_p() const
+{
+ return must_send_timer && evtimer_pending(must_send_timer, 0);
+}
+
+/* static */ void
+chop_conn_t::must_send_timeout(evutil_socket_t, short, void *arg)
+{
+ static_cast<chop_conn_t *>(arg)->send();
}
+
+} // anonymous namespace
+
+PROTO_DEFINE_MODULE(chop);
+
+// Local Variables:
+// mode: c++
+// c-basic-offset: 2
+// c-file-style: "gnu"
+// c-file-offsets: ((innamespace . 0) (brace-list-open . 0))
+// End:
1
0

[stegotorus/master] Don't *insist* on C++11 support from the compiler.
by zwol@torproject.org 20 Jul '12
by zwol@torproject.org 20 Jul '12
20 Jul '12
commit ae3a7df0ffe51b248384f3c7d785d05be4778ac4
Author: Zack Weinberg <zackw(a)cmu.edu>
Date: Tue Mar 27 21:29:23 2012 -0700
Don't *insist* on C++11 support from the compiler.
---
Makefile.am | 4 +---
m4/cxxflags_stdcxx_11.m4 | 3 +--
2 files changed, 2 insertions(+), 5 deletions(-)
diff --git a/Makefile.am b/Makefile.am
index 978c180..377e37f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -4,9 +4,7 @@
ACLOCAL_AMFLAGS = -I m4 --install
-WARNINGS = -Werror -Wall -Wextra -Wformat=2
-
-AM_CXXFLAGS = -std=c++98 $(WARNINGS)
+AM_CXXFLAGS = -Werror -Wall -Wextra -Wformat=2
AM_CPPFLAGS = -I. -I$(srcdir)/src -D_FORTIFY_SOURCE=2 $(lib_CPPFLAGS)
LDADD = libstegotorus.a
diff --git a/m4/cxxflags_stdcxx_11.m4 b/m4/cxxflags_stdcxx_11.m4
index 1c5c0f2..383b708 100644
--- a/m4/cxxflags_stdcxx_11.m4
+++ b/m4/cxxflags_stdcxx_11.m4
@@ -10,7 +10,6 @@
#
# Check for baseline language coverage in the compiler for the C++11
# standard; if necessary, add switches to CXXFLAGS to enable support.
-# Errors out if no mode that supports C++11 baseline syntax can be found.
# The argument, if specified, indicates whether you insist on an extended
# mode (e.g. -std=gnu++11) or a strict conformance mode (e.g. -std=c++11).
# If neither is specified, you get whatever works, with preference for an
@@ -102,6 +101,6 @@ AC_DEFUN([AX_CXXFLAGS_STDCXX_11], [dnl
fi])
if test x$ac_success = xno; then
- AC_MSG_ERROR([*** A compiler with support for C++11 language features is required.])
+ AC_MSG_WARN([*** Compiler support for C++11 language features not detected.])
fi
])
1
0

[stegotorus/master] Add fallback logic for static_assert in compilers that don't support it.
by zwol@torproject.org 20 Jul '12
by zwol@torproject.org 20 Jul '12
20 Jul '12
commit 5ce688f4193828197130b4c91c7c32b53b5b5568
Author: Zack Weinberg <zackw(a)cmu.edu>
Date: Wed Mar 28 09:01:09 2012 -0700
Add fallback logic for static_assert in compilers that don't support it.
---
configure.ac | 1 +
m4/cxx_delete_method.m4 | 8 ++++--
m4/cxx_static_assert.m4 | 57 ++++++++++++++++++++++++++++++++++++++++++++++
m4/cxxflags_stdcxx_11.m4 | 4 ---
4 files changed, 63 insertions(+), 7 deletions(-)
diff --git a/configure.ac b/configure.ac
index 9002078..69f4150 100644
--- a/configure.ac
+++ b/configure.ac
@@ -68,6 +68,7 @@ AC_CHECK_HEADERS([execinfo.h],,, [AC_INCLUDES_DEFAULT()])
AX_CXXFLAGS_STDCXX_11([ext])
AX_CXX_DELETE_METHOD
+AX_CXX_STATIC_ASSERT
### Output ###
diff --git a/m4/cxx_delete_method.m4 b/m4/cxx_delete_method.m4
index 2f7decf..2d467e7 100644
--- a/m4/cxx_delete_method.m4
+++ b/m4/cxx_delete_method.m4
@@ -50,7 +50,8 @@ AC_DEFUN([AX_CXX_DELETE_METHOD], [dnl
[ax_cv_cxx_delete_method_syntax=yes],
[ax_cv_cxx_delete_method_syntax=no])])
# ... and this one should fail.
- AC_CACHE_CHECK(whether $CXX enforces method deletion,
+ if test x$ax_cv_cxx_delete_method_syntax = xyes; then
+ AC_CACHE_CHECK(whether $CXX enforces method deletion,
ax_cv_cxx_delete_method_enforced, [
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
struct foo {
@@ -62,8 +63,9 @@ AC_DEFUN([AX_CXX_DELETE_METHOD], [dnl
]])],
[ax_cv_cxx_delete_method_enforced=no],
[ax_cv_cxx_delete_method_enforced=yes])])
- if test $ax_cv_cxx_delete_method_syntax = yes &&
- test $ax_cv_cxx_delete_method_enforced = yes
+ fi
+ if test x$ax_cv_cxx_delete_method_syntax = xyes &&
+ test x$ax_cv_cxx_delete_method_enforced = xyes
then
AC_DEFINE([DELETE_METHOD], [= delete],
[Define as `= delete' if your compiler supports C++11 method
diff --git a/m4/cxx_static_assert.m4 b/m4/cxx_static_assert.m4
new file mode 100644
index 0000000..7bf69f8
--- /dev/null
+++ b/m4/cxx_static_assert.m4
@@ -0,0 +1,57 @@
+# SYNOPSIS
+#
+# AX_CXX_STATIC_ASSERT
+#
+# DESCRIPTION
+#
+# Detect whether the C++ compiler, in its present operating mode,
+# supports the C++11 'static_assert' construct. If it doesn't,
+# define 'static_assert' as a preprocessor macro which provides
+# more-or-less the same functionality.
+#
+# LICENSE
+#
+# Copyright (c) 2012 Zack Weinberg <zackw(a)panix.com>
+#
+# Copying and distribution of this file, with or without modification, are
+# permitted in any medium without royalty provided the copyright notice
+# and this notice are preserved. This file is offered as-is, without any
+# warranty.
+
+#serial 1
+
+AC_DEFUN([AX_CXX_STATIC_ASSERT], [dnl
+ AC_LANG_ASSERT([C++])dnl
+ AC_CACHE_CHECK(whether $CXX accepts static_assert, ax_cv_cxx_static_assert,
+ [AC_COMPILE_IFELSE([AC_LANG_SOURCE([dnl
+ template <typename T>
+ struct check
+ { static_assert(sizeof(int) <= sizeof(T), "not big enough"); };
+ check<int> ok;])],
+ [ax_cv_cxx_static_assert=yes], [ax_cv_cxx_static_assert=no])])
+ if test x$ax_cv_cxx_static_assert = xyes; then
+ AC_CACHE_CHECK(whether $CXX enforces static_assert, ax_cv_cxx_static_assert_e,
+ [AC_COMPILE_IFELSE([AC_LANG_SOURCE([dnl
+ template <typename T>
+ struct check
+ { static_assert(sizeof(char[2]) <= sizeof(T), "not big enough"); };
+ check<char> bad;])],
+ [ax_cv_cxx_static_assert_e=no], [ax_cv_cxx_static_assert_e=yes])])
+ fi
+ if test x$ax_cv_cxx_static_assert = xyes &&
+ test x$ax_cv_cxx_static_assert_e = xyes; then
+ AC_DEFINE(HAVE_STATIC_ASSERT, 1,
+ [Define to 1 if the C++ compiler supports static_assert.])
+ fi
+ AH_VERBATIM([HAVE_STATIC_ASSERT_],
+[#ifndef HAVE_STATIC_ASSERT
+# define static_assert(expr, msg) typedef char static_assert_id[(expr)?1:-1]
+# ifdef __COUNTER__
+# define static_assert_id static_assert_paste(static_assert_, __COUNTER__)
+# else
+# define static_assert_id static_assert_paste(static_assert_, __LINE__)
+# endif
+# define static_assert_paste(a,b) static_assert_paste_(a,b)
+# define static_assert_paste_(a,b) a##b
+#endif])
+])
diff --git a/m4/cxxflags_stdcxx_11.m4 b/m4/cxxflags_stdcxx_11.m4
index 383b708..7f06ed2 100644
--- a/m4/cxxflags_stdcxx_11.m4
+++ b/m4/cxxflags_stdcxx_11.m4
@@ -99,8 +99,4 @@ AC_DEFUN([AX_CXXFLAGS_STDCXX_11], [dnl
fi
done
fi])
-
- if test x$ac_success = xno; then
- AC_MSG_WARN([*** Compiler support for C++11 language features not detected.])
- fi
])
1
0

[stegotorus/master] Temporarily comment out some code not supported by Apple's gcc.
by zwol@torproject.org 20 Jul '12
by zwol@torproject.org 20 Jul '12
20 Jul '12
commit 25d5751d8effeafa50a628e1e78e3ca95aff2051
Author: Zack Weinberg <zackw(a)cmu.edu>
Date: Tue Mar 27 21:33:11 2012 -0700
Temporarily comment out some code not supported by Apple's gcc.
---
src/rng.cc | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/src/rng.cc b/src/rng.cc
index 86a55c1..149363e 100644
--- a/src/rng.cc
+++ b/src/rng.cc
@@ -128,8 +128,10 @@ rng_double()
unsigned int n;
};
+#if 0
static_assert(sizeof(double) == sizeof(uint64_t),
"this code works only with 64-bit, IEEE double");
+#endif
union ieee754_double {
double d;
uint64_t i;
1
0