tor-commits
Threads by month
- ----- 2025 -----
- August
- July
- 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

[stegotorus/master] Document deliberate non-checks in configure.ac. Add a stub subprocess-windows.c and related build goo.
by zwol@torproject.org 20 Jul '12
by zwol@torproject.org 20 Jul '12
20 Jul '12
commit 0f5123a7c19257e70569dc02534cbc1d9be7bd90
Author: Zack Weinberg <zackw(a)cmu.edu>
Date: Tue Jun 5 11:11:20 2012 -0700
Document deliberate non-checks in configure.ac. Add a stub subprocess-windows.c and related build goo.
---
Makefile.am | 7 ++++-
configure.ac | 63 +++++++++++++++++++++++++++++++++++++++++++++
src/subprocess-unix.cc | 5 +--
src/subprocess-windows.cc | 5 +++
4 files changed, 76 insertions(+), 4 deletions(-)
diff --git a/Makefile.am b/Makefile.am
index 64d1d33..d01d4f9 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -37,11 +37,16 @@ libstegotorus_a_SOURCES = \
src/protocol.cc \
src/rng.cc \
src/socks.cc \
- src/subprocess-unix.cc \
src/steg.cc \
src/util.cc \
$(PROTOCOLS) $(STEGANOGRAPHERS)
+if WINDOWS
+libstegotorus_a_SOURCES += src/subprocess-windows.cc
+else
+libstegotorus_a_SOURCES += src/subprocess-unix.cc
+endif
+
nodist_libstegotorus_a_SOURCES = protolist.cc steglist.cc
stegotorus_SOURCES = \
diff --git a/configure.ac b/configure.ac
index 0c858dc..ab56311 100644
--- a/configure.ac
+++ b/configure.ac
@@ -10,12 +10,75 @@ AC_LANG([C++])
AM_INIT_AUTOMAKE([foreign nostdinc silent-rules subdir-objects])
AM_MAINTAINER_MODE([enable])
+dnl This script deliberately does not check for a bunch of things
+dnl that 'autoscan' thinks we ought to check for. Here is the list,
+dnl with a rationale for each:
+dnl
+dnl Defined by C89, therefore unnecessary to check for nowadays:
+dnl
+dnl AC_CHECK_FUNCS([atexit floor memset strcasecmp strchr strerror
+dnl strrchr strstr strtoul])
+dnl AC_CHECK_HEADERS([limits.h stddef.h stdlib.h string.h])
+dnl AC_CHECK_TYPES([ptrdiff_t])
+dnl AC_TYPE_SIZE_T
+dnl
+dnl We don't make use of the unspecified-behavior corner cases that
+dnl these pin down:
+dnl
+dnl AC_FUNC_MALLOC
+dnl AC_FUNC_REALLOC
+dnl
+dnl Defined by C++ since cfront, therefore unnecessary to check for
+dnl in a C++ program:
+dnl
+dnl AC_C_INLINE
+dnl AC_HEADER_STDBOOL # 'bool', 'true', and 'false', not the header
+dnl
+dnl Defined by Unix98, therefore adequately handled by #ifdef _WIN32
+dnl (FIXME: Windows has not been tested in some time and is likely to
+dnl be broken):
+dnl
+dnl AC_CHECK_HEADERS([fcntl.h unistd.h arpa/inet.h netinet/in.h
+dnl sys/types.h sys/stat.h sys/un.h sys/wait.h])
+dnl AC_CHECK_FUNCS([gettimeofday])
+dnl AC_FUNC_FORK
+dnl AC_TYPE_PID_T
+dnl
+dnl libevent handles for us (FIXME: we probably shouldn't rely on this):
+dnl
+dnl AC_CHECK_HEADERS([stdint.h])
+dnl AC_CHECK_FUNCS([socket])
+dnl AC_TYPE_SSIZE_T
+dnl AC_TYPE_UINT8_T
+dnl AC_TYPE_UINT16_T
+dnl AC_TYPE_UINT32_T
+dnl AC_TYPE_UINT64_T
+dnl
+dnl The fallback is inappropriate for our use case (it would copy
+dnl several enormous files), it is only required for 'make check'
+dnl in an out-of-tree build, and it's slated to go away RSN anyway:
+dnl
+dnl AC_PROG_LN_S
+
### Compiler and language features ###
AC_PROG_CC
AC_PROG_CXX
AC_PROG_CXXCPP
+# Make a conditional for whether we're on Windows or not, so we can
+# select the right version of certain files.
+AC_CACHE_CHECK([for Windows], ac_cv_system_windows,
+ [AC_PREPROC_IFELSE([AC_LANG_SOURCE([[
+ /* _WIN32 is defined for both Win32 and Win64 */
+ #ifndef _WIN32
+ #error "this is not Windows"
+ #endif
+ ]])],
+ [ac_cv_system_windows=yes],
+ [ac_cv_system_windows=no])])
+AM_CONDITIONAL(WINDOWS, test $ac_cv_system_windows = yes)
+
AX_SYS_EXTENSIONS
AC_SYS_LARGEFILE
diff --git a/src/subprocess-unix.cc b/src/subprocess-unix.cc
index 7e4b8a2..7e075d9 100644
--- a/src/subprocess-unix.cc
+++ b/src/subprocess-unix.cc
@@ -6,9 +6,8 @@
* See LICENSE for other credits and copying information.
*/
-// N.B. This file will have to be rewritten more-or-less from scratch
-// for the Windows port. It should be acceptably portable to all Unix
-// implementations still in wide use.
+// This file should be acceptably portable to all Unix implementations
+// still in wide use.
#include "util.h"
#include "subprocess.h"
diff --git a/src/subprocess-windows.cc b/src/subprocess-windows.cc
new file mode 100644
index 0000000..ec21296
--- /dev/null
+++ b/src/subprocess-windows.cc
@@ -0,0 +1,5 @@
+/* Copyright 2012 SRI International
+ * See LICENSE for other credits and copying information.
+ */
+
+#error "Subprocess creation for Windows not yet implemented."
1
0

[stegotorus/master] Import MKEM implementation from moeller-ref and write out the crypt.h interface for it. (Significant pieces of implementation still to do.)
by zwol@torproject.org 20 Jul '12
by zwol@torproject.org 20 Jul '12
20 Jul '12
commit 818813ea2049df733f5e9a9015a1b82dc8ee2dda
Author: Zack Weinberg <zackw(a)cmu.edu>
Date: Thu Jun 7 17:17:54 2012 -0700
Import MKEM implementation from moeller-ref and write out the crypt.h interface for it. (Significant pieces of implementation still to do.)
---
Makefile.am | 1 +
src/crypt.h | 144 ++++++++++++++++--
src/mkem.cc | 492 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/mkem.h | 117 ++++++++++++++
4 files changed, 739 insertions(+), 15 deletions(-)
diff --git a/Makefile.am b/Makefile.am
index d01d4f9..d21fdde 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -33,6 +33,7 @@ libstegotorus_a_SOURCES = \
src/compression.cc \
src/connections.cc \
src/crypt.cc \
+ src/mkem.cc \
src/network.cc \
src/protocol.cc \
src/rng.cc \
diff --git a/src/crypt.h b/src/crypt.h
index cc9309a..869107a 100644
--- a/src/crypt.h
+++ b/src/crypt.h
@@ -9,6 +9,7 @@ const size_t AES_BLOCK_LEN = 16;
const size_t GCM_TAG_LEN = 16;
const size_t SHA256_LEN = 32;
const size_t EC_P224_LEN = 28;
+const size_t MKE_MSG_LEN = 21;
/**
* Initialize cryptography library. Must be called before anything that
@@ -41,9 +42,6 @@ struct key_generator;
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);
@@ -57,6 +55,9 @@ struct ecb_encryptor
write the result to 'out'. */
virtual void encrypt(uint8_t *out, const uint8_t *in) = 0;
+ virtual ~ecb_encryptor();
+protected:
+ ecb_encryptor() {}
private:
ecb_encryptor(const ecb_encryptor&);
ecb_encryptor& operator=(const ecb_encryptor&);
@@ -64,9 +65,6 @@ private:
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);
@@ -80,6 +78,9 @@ struct ecb_decryptor
write the result to 'out'. */
virtual void decrypt(uint8_t *out, const uint8_t *in) = 0;
+ virtual ~ecb_decryptor();
+protected:
+ ecb_decryptor() {}
private:
ecb_decryptor(const ecb_decryptor&) DELETE_METHOD;
ecb_decryptor& operator=(const ecb_decryptor&) DELETE_METHOD;
@@ -88,9 +89,6 @@ private:
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 gcm_encryptor *create(const uint8_t *key, size_t keylen);
@@ -108,6 +106,9 @@ struct gcm_encryptor
virtual void encrypt(uint8_t *out, const uint8_t *in, size_t inlen,
const uint8_t *nonce, size_t nlen) = 0;
+ virtual ~gcm_encryptor();
+protected:
+ gcm_encryptor() {}
private:
gcm_encryptor(const gcm_encryptor&);
gcm_encryptor& operator=(const gcm_encryptor&);
@@ -115,9 +116,6 @@ private:
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 gcm_decryptor *create(const uint8_t *key, size_t keylen);
@@ -136,6 +134,9 @@ struct gcm_decryptor
virtual int decrypt(uint8_t *out, const uint8_t *in, size_t inlen,
const uint8_t *nonce, size_t nlen) = 0;
+ virtual ~gcm_decryptor();
+protected:
+ gcm_decryptor() {}
private:
gcm_decryptor(const gcm_decryptor&) DELETE_METHOD;
gcm_decryptor& operator=(const gcm_decryptor&) DELETE_METHOD;
@@ -145,9 +146,6 @@ private:
(we use NIST P-224). */
struct ecdh_message
{
- ecdh_message() {}
- virtual ~ecdh_message();
-
/** Generate a new Diffie-Hellman message from randomness. */
static ecdh_message *generate();
@@ -170,8 +168,99 @@ struct ecdh_message
directly. */
virtual int combine(const uint8_t *xcoord_other, uint8_t *secret_out)
const = 0;
+
+ virtual ~ecdh_message();
+protected:
+ ecdh_message() {}
+private:
+ ecdh_message(const ecdh_message&) DELETE_METHOD;
+ ecdh_message& operator=(const ecdh_message&) DELETE_METHOD;
+};
+
+/** Moeller key encapsulation generator: takes a public key and a source
+ of weak entropy, produces temporary key material and key encapsulation
+ messages. */
+struct mke_generator
+{
+ /** Return a new encapsulation generator based on the public key
+ 'key', which should be a C string of the form produced by
+ 'mke_decoder::pubkey()', and a key_generator. The object
+ retains references to both arguments, so make sure their
+ lifetimes exceed that of this object. You are encouraged
+ to use key_generator::from_rng() for this. */
+ static mke_generator *create(const char *pubkey, key_generator *gen);
+
+ /** Retrieve the public key. This will be the same pointer as was
+ passed to create(). */
+ virtual const char *pubkey() const;
+
+ /** Retrieve the length of the public key (you could call strlen(),
+ but this may be more efficient). */
+ virtual size_t pklen() const;
+
+ /** Generate temporary key material and an encapsulated key message.
+ This does NOT carry out key derivation; you probably want to use
+ key_generator::from_mke() instead. The 'message' argument must
+ point to at least MKE_MSG_LEN bytes of storage, and the 'secret'
+ argument must point to at least twice that much storage. */
+ virtual int generate(uint8_t *secret, uint8_t *message) const;
+
+ /** Extract the padding from a previously-generated encapsulated key
+ message. Cannot fail. Do not attempt to interpret the byte
+ returned; just pack it somewhere in the data encrypted with the
+ derived key material, so that the receiver can verify it. */
+ virtual uint8_t extract_padding(uint8_t *message) const;
+
+ virtual ~mke_generator();
+protected:
+ mke_generator() {}
+private:
+ mke_generator(const mke_generator&) DELETE_METHOD;
+ mke_generator& operator=(const mke_generator&) DELETE_METHOD;
};
+/** Moeller key encapsulation decoder: creates a keypair when
+ instantiated, can be asked to emit the public key, can decode the
+ counterpart's key encapsulation messages into temporary key material. */
+struct mke_decoder
+{
+ /** Return a new encapsulation decoder. Generates a new keypair
+ from a source of strong entropy. */
+ static mke_decoder *create();
+
+ /** Emit the public key. The return value is a C-string.
+ The storage for this string belongs to the mke_decoder object.
+ Its contents are unspecified, but it uses only the characters
+ ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=,
+ */
+ virtual const char *pubkey() const;
+
+ /** Retrieve the length of the public key (you could call strlen(),
+ but this may be more efficient). */
+ virtual size_t pklen() const;
+
+ /** Decode an encapsulated key message. This does NOT carry out key
+ derivation; you probably want to use key_generator::from_mke()
+ instead. The 'message' argument must point to at least
+ MKE_MSG_LEN bytes of data, and the 'secret' argument must point
+ to at least twice that much storage. */
+ virtual int decode(uint8_t *secret, uint8_t *message) const;
+
+ /** Extract the padding from an encapsulated key message.
+ Cannot fail. Do not attempt to interpret the byte returned;
+ just verify it by comparing it with a byte somewhere in the
+ data encrypted with the derived key material. */
+ virtual uint8_t extract_padding(uint8_t *message) const;
+
+ virtual ~mke_decoder();
+protected:
+ mke_decoder() {}
+private:
+ mke_decoder(const mke_decoder&) DELETE_METHOD;
+ mke_decoder& operator=(const mke_decoder&) DELETE_METHOD;
+};
+
+
/** Generate keying material from an initial key of some kind, a salt
value, and a context value, all of which are formally bitstrings.
See http://tools.ietf.org/html/rfc5869 for the requirements on the
@@ -206,6 +295,31 @@ struct key_generator
const uint8_t *salt, size_t slen,
const uint8_t *ctxt, size_t clen);
+ /** Construct a key generator from a Moeller key generator, and as a
+ side effect, emit the key encapsulation message. Will use the
+ public key for the salt. The 'message_out' argument must point
+ to at least MKE_MSG_LEN bytes of storage. */
+ static key_generator *from_mke(const mke_generator *gen,
+ uint8_t *message_out,
+ const uint8_t *ctxt, size_t clen);
+
+ /** Construct a key generator from a Moeller key decoder and a
+ received key encapsulation message. Will use the public key for
+ the salt. The 'message' argument must point to at least
+ MKE_MSG_LEN bytes of data. */
+ static key_generator *from_mke(const mke_decoder *gen,
+ uint8_t *message,
+ const uint8_t *ctxt, size_t clen);
+
+ /** Construct a key generator from the global random number
+ generator. This should be used in contexts where a great deal
+ of key material may be required but its strength is not terribly
+ important; it reduces the demand on the entropy source. Key
+ generators created by this factory will automatically reseed
+ themselves when they hit the HKDF upper limit. */
+ static key_generator *from_rng(const uint8_t *salt, size_t slen,
+ const uint8_t *ctxt, size_t clen);
+
/** Write LEN bytes of key material to BUF. May be called
repeatedly. Note that HKDF has a hard upper limit on the total
amount of key material it can generate. The return value is
diff --git a/src/mkem.cc b/src/mkem.cc
new file mode 100644
index 0000000..a17558d
--- /dev/null
+++ b/src/mkem.cc
@@ -0,0 +1,492 @@
+/* Copyright 2012 SRI International
+ * Based on the public-domain reference implementation of Moeller 2004
+ * to be found at http://github.com/zackw/moeller-ref/
+ * See LICENSE for other credits and copying information
+ */
+
+#include "util.h"
+#include "crypt.h"
+#include "mkem.h"
+
+#include <openssl/rand.h>
+
+/* Encapsulation of a set of elliptic curve parameters. */
+
+struct mk_curve_params
+{
+ /* generating polynomial, aka reducing polynomial, aka modulus: bignum */
+ const uint8_t *m;
+ size_t L_m;
+
+ /* elliptic curve coefficient 'b': bignum */
+ const uint8_t *b;
+ size_t L_b;
+
+ /* curve group large primes: bignum */
+ const uint8_t *p0;
+ size_t L_p0;
+ const uint8_t *p1;
+ size_t L_p1;
+
+ /* curve group sizes: bignum */
+ const uint8_t *n0;
+ size_t L_n0;
+ const uint8_t *n1;
+ size_t L_n1;
+
+ /* curve group generators: points (SEC1 compressed format) */
+ const uint8_t *g0;
+ size_t L_g0;
+ const uint8_t *g1;
+ size_t L_g1;
+};
+
+/* MK_CURVE_nbits_index constants correspond to particular curves
+ for which this algorithm is defined. Currently there is only one. */
+
+enum MKEMCurve {
+ MK_CURVE_163_0 /* original 163-bit curve from Moeller 2004 */
+};
+
+
+/* All the known curves that can be used with this algorithm are
+ defined by mk_curve_params objects in this array. */
+
+/* work around lack of compound literals in C89 */
+#define S_(c) #c
+#define S(c) S_(\x##c)
+
+/* 21-byte hexadecimal bignum */
+#define N21(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u) \
+ (const uint8_t *)(S(a) S(b) S(c) S(d) S(e) S(f) S(g) \
+ S(h) S(i) S(j) S(k) S(l) S(m) S(n) \
+ S(o) S(p) S(q) S(r) S(s) S(t) S(u)), 21
+
+/* 21+1-byte compressed hexadecimal curve point */
+#define P21(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u) \
+ (const uint8_t *)(S(02) \
+ S(a) S(b) S(c) S(d) S(e) S(f) S(g) \
+ S(h) S(i) S(j) S(k) S(l) S(m) S(n) \
+ S(o) S(p) S(q) S(r) S(s) S(t) S(u)), 22
+
+const mk_curve_params mk_curves[] = {
+/* MK_CURVE_163_0:
+ p0 = 2923003274661805836407371179614143033958162426611, n0 = p0*4
+ p1 = 5846006549323611672814736302501978089331135490587, n1 = p1*2 */
+{
+ N21(08,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,c9), /* m */
+ N21(05,84,6d,0f,da,25,53,61,60,67,11,bf,7a,99,b0,72,2e,2e,c8,f7,6b), /* b */
+
+ N21(02,00,00,00,00,00,00,00,00,00,01,40,a3,f2,a0,c6,ce,d9,ce,ea,f3), /* p0 */
+ N21(03,ff,ff,ff,ff,ff,ff,ff,ff,ff,fd,7e,b8,1a,be,72,62,4c,62,2a,1b), /* p1 */
+
+ N21(08,00,00,00,00,00,00,00,00,00,05,02,8f,ca,83,1b,3b,67,3b,ab,cc), /* n0 */
+ N21(07,ff,ff,ff,ff,ff,ff,ff,ff,ff,fa,fd,70,35,7c,e4,c4,98,c4,54,36), /* n1 */
+
+ P21(00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,01), /* g0 */
+ P21(00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,02) /* g1 */
+},
+
+};
+
+#undef S_
+#undef S
+#undef N21
+#undef P21
+
+#define FAILZ(expr) if ((expr) == 0) goto fail;
+
+MKEMParams::MKEMParams(BN_CTX *ctx)
+ : ctx(ctx),
+ m(0), b(0), a0(0), a1(0), p0(0), p1(0), n0(0), n1(0), maxu(0),
+ c0(0), c1(0), g0(0), g1(0),
+ msgsize(0), pad_bits(0), pad_mask(0), curve_bit(0)
+{
+ const mk_curve_params *p = &mk_curves[MK_CURVE_163_0];
+ size_t bitsize, bytesize, bitcap, k;
+
+ FAILZ(m = BN_bin2bn(p->m, p->L_m, 0));
+ FAILZ(b = BN_bin2bn(p->b, p->L_b, 0));
+ FAILZ(a0 = BN_new()); FAILZ(BN_zero((BIGNUM *)a0));
+ FAILZ(a1 = BN_value_one());
+ FAILZ(p0 = BN_bin2bn(p->p0, p->L_p0, 0));
+ FAILZ(p1 = BN_bin2bn(p->p1, p->L_p1, 0));
+ FAILZ(n0 = BN_bin2bn(p->n0, p->L_n0, 0));
+ FAILZ(n1 = BN_bin2bn(p->n1, p->L_n1, 0));
+
+ FAILZ(c0 = EC_GROUP_new_curve_GF2m(m, a0, b, ctx));
+ FAILZ(c1 = EC_GROUP_new_curve_GF2m(m, a1, b, ctx));
+
+ FAILZ(g0 = EC_POINT_new(c0));
+ FAILZ(EC_POINT_oct2point(c0, (EC_POINT *)g0, p->g0, p->L_g0, ctx));
+ FAILZ(g1 = EC_POINT_new(c1));
+ FAILZ(EC_POINT_oct2point(c1, (EC_POINT *)g1, p->g1, p->L_g1, ctx));
+
+ /* Calculate the upper limit for the random integer U input to
+ MKEM_generate_message_u.
+
+ The paper calls for us to choose between curve 0 and curve 1 with
+ probability proportional to the number of points on that curve, and
+ then choose a random integer in the range 0 < u < n{curve}. The
+ easiest way to do this accurately is to choose a random integer in the
+ range [1, n0 + n1 - 2]. If it is less than n0, MKEM_generate_message_u
+ will use it unmodified with curve 0. If it is greater than or equal
+ to n0, MKEM_generate_message_u will subtract n0-1, leaving a number in
+ the range [1, n1-1], and use that with curve 1. */
+
+ BIGNUM *mu;
+ FAILZ(mu = BN_dup(n0));
+ FAILZ(BN_add(mu, mu, n1));
+ FAILZ(BN_sub(mu, mu, BN_value_one()));
+ FAILZ(BN_sub(mu, mu, BN_value_one()));
+ maxu = mu;
+
+ /* Calculate the maximum size of a message and the padding mask applied
+ to the high byte of each message. See MKEM_generate_message_u for
+ further exposition. */
+ bitsize = EC_GROUP_get_degree(c0);
+ if ((size_t)EC_GROUP_get_degree(c1) != bitsize)
+ goto fail;
+
+ bytesize = (bitsize + 7) / 8;
+ bitcap = bytesize * 8;
+ k = bitcap - bitsize;
+ if (k == 0)
+ goto fail;
+
+ msgsize = bytesize;
+ pad_bits = k - 1;
+ pad_mask = ~((1 << (8 - pad_bits)) - 1);
+ curve_bit = 1 << (8 - k);
+ return;
+
+ fail:
+ log_crypto_abort("MKEMParams constructor");
+}
+
+MKEMParams::~MKEMParams()
+{
+ /* None of the values in an MKEMParams are secret, so don't bother
+ clearing them. */
+
+ /* We do not own 'ctx'. */
+
+ if (m) BN_free((BIGNUM *)m);
+ if (b) BN_free((BIGNUM *)b);
+ if (a0) BN_free((BIGNUM *)a0);
+ /* a1 is the static BN_value_one() constant and should not be freed. */
+ if (p0) BN_free((BIGNUM *)p0);
+ if (p1) BN_free((BIGNUM *)p1);
+ if (n0) BN_free((BIGNUM *)n0);
+ if (n1) BN_free((BIGNUM *)n1);
+ if (maxu) BN_free((BIGNUM *)maxu);
+
+ if (c0) EC_GROUP_free((EC_GROUP *)c1);
+ if (c1) EC_GROUP_free((EC_GROUP *)c1);
+
+ if (g0) EC_POINT_free((EC_POINT *)g0);
+ if (g1) EC_POINT_free((EC_POINT *)g1);
+
+ memset(this, 0, sizeof(*this));
+}
+
+
+MKEM::~MKEM()
+{
+ /* s0 and s1 are secret. p0 and p1 are not secret, but clear them
+ anyway. */
+ if (s0) BN_clear_free((BIGNUM *)s0);
+ if (s1) BN_clear_free((BIGNUM *)s1);
+
+ if (p0) EC_POINT_clear_free((EC_POINT *)p0);
+ if (p1) EC_POINT_clear_free((EC_POINT *)p1);
+
+ memset(this, 0, sizeof(*this));
+}
+
+/* The secret integers s0 and s1 must be in the range 0 < s < n for
+ some n, and must be relatively prime to that n. We know a priori
+ that n is of the form 2**k * p for some small integer k and prime
+ p. Therefore, it suffices to choose a random integer in the range
+ [0, n/2), multiply by two and add one (enforcing oddness), and then
+ reject values which are divisible by p. */
+static BIGNUM *
+random_s(const BIGNUM *n, const BIGNUM *p, BN_CTX *c)
+{
+ BIGNUM h, m, *r;
+
+ BN_init(&h);
+ BN_init(&m);
+ FAILZ(r = BN_new());
+ FAILZ(BN_copy(&h, n));
+ FAILZ(BN_rshift1(&h, &h));
+
+ do {
+ FAILZ(BN_rand_range(r, &h));
+ FAILZ(BN_lshift1(r, r));
+ FAILZ(BN_add(r, r, BN_value_one()));
+ FAILZ(BN_nnmod(&m, r, p, c));
+ } while (BN_is_zero(&m));
+
+ BN_clear(&h);
+ BN_clear(&m);
+ return r;
+
+ fail:
+ BN_clear(&h);
+ BN_clear(&m);
+ if (r) BN_clear_free(r);
+ return 0;
+}
+
+void
+MKEM::load_secret_key()
+{
+ FAILZ(params); FAILZ(s0); FAILZ(s1);
+
+ FAILZ(p0 = EC_POINT_new(params->c0));
+ FAILZ(p1 = EC_POINT_new(params->c1));
+ FAILZ(EC_POINT_mul(params->c0, (EC_POINT *)p0, 0, params->g0, s0,
+ params->ctx));
+ FAILZ(EC_POINT_mul(params->c1, (EC_POINT *)p1, 0, params->g1, s1,
+ params->ctx));
+ return;
+
+ fail:
+ log_crypto_abort("MKEM::MKEM(secret)");
+}
+
+MKEM::MKEM(const MKEMParams *params,
+ const uint8_t *s0v, size_t s0l,
+ const uint8_t *s1v, size_t s1l,
+ const struct MKEMPrivateKeyLoad&)
+ : params(params),
+ s0(BN_bin2bn(s0v, s0l, 0)),
+ s1(BN_bin2bn(s1v, s1l, 0)),
+ p0(0), p1(0)
+{
+ load_secret_key();
+}
+
+MKEM::MKEM(const MKEMParams *params)
+ : params(params),
+ s0(random_s(params->n0, params->p0, params->ctx)),
+ s1(random_s(params->n1, params->p1, params->ctx)),
+ p0(0), p1(0)
+{
+ load_secret_key();
+}
+
+MKEM::MKEM(const MKEMParams *params,
+ const uint8_t *p0v, size_t p0l,
+ const uint8_t *p1v, size_t p1l)
+ : params(params), s0(0), s1(0), p0(0), p1(0)
+{
+ EC_POINT *pp0 = EC_POINT_new(params->c0);
+ EC_POINT *pp1 = EC_POINT_new(params->c1);
+
+ FAILZ(pp0); FAILZ(pp1);
+ FAILZ(EC_POINT_oct2point(params->c0, pp0, p0v, p0l, params->ctx));
+ FAILZ(EC_POINT_oct2point(params->c1, pp1, p1v, p1l, params->ctx));
+
+ p0 = pp0;
+ p1 = pp1;
+ return;
+
+ fail:
+ log_crypto_abort("MKEM::MKEM(public)");
+}
+
+int
+MKEM::export_public_key(uint8_t *p0o, uint8_t *p1o) const
+{
+ size_t vsize = params->msgsize + 1;
+
+ if (EC_POINT_point2oct(params->c0, p0, POINT_CONVERSION_COMPRESSED,
+ p0o, vsize, params->ctx) != vsize ||
+ EC_POINT_point2oct(params->c1, p1, POINT_CONVERSION_COMPRESSED,
+ p1o, vsize, params->ctx) != vsize)
+ return -1;
+ return 0;
+}
+
+/* Write the BIGNUM 'b' to 'to', padded at the high end so that the
+ result occupies _exactly_ 'sz' bytes. If 'b' requires more than
+ 'sz' bytes it is an error. */
+static size_t
+bn2bin_padhi(const BIGNUM *b, uint8_t *to, size_t sz)
+{
+ size_t n = BN_num_bytes(b);
+ if (n > sz)
+ return 0;
+ if (n < sz) {
+ memset(to, 0, sz - n);
+ to += sz - n;
+ }
+ return BN_bn2bin(b, to) + (sz - n);
+}
+
+int
+MKEM::export_secret_key(uint8_t *s0o, uint8_t *s1o) const
+{
+ if (!s0 || !s1) return -1;
+
+ if (bn2bin_padhi(s0, s0o, params->msgsize) != params->msgsize ||
+ bn2bin_padhi(s1, s1o, params->msgsize) != params->msgsize)
+ return -1;
+ return 0;
+}
+
+int
+MKEM::generate(uint8_t *secret, uint8_t *message) const
+{
+ BIGNUM u;
+ uint8_t pad;
+ int rv = -1;
+ BN_init(&u);
+ if (BN_rand_range(&u, params->maxu) &&
+ BN_add(&u, &u, BN_value_one()) &&
+ RAND_bytes(&pad, 1) &&
+ !generate(&u, pad, secret, message))
+ rv = 0;
+
+ BN_clear(&u);
+ return rv;
+}
+
+int
+MKEM::generate(const BIGNUM *uraw, uint8_t pad,
+ uint8_t *secret, uint8_t *message) const
+{
+ BIGNUM u, x, y;
+ int use_curve0 = (BN_cmp(uraw, params->n0) < 0);
+ const EC_GROUP *ca;
+ const EC_POINT *ga;
+ const EC_POINT *pa;
+ EC_POINT *q = 0, *r = 0;
+ size_t mlen = params->msgsize;
+ int rv;
+
+ BN_init(&u);
+ BN_init(&x);
+ BN_init(&y);
+
+ if (use_curve0) {
+ ca = params->c0;
+ ga = params->g0;
+ pa = p0;
+ FAILZ(BN_copy(&u, uraw));
+ } else {
+ ca = params->c1;
+ ga = params->g1;
+ pa = p1;
+ FAILZ(BN_sub(&u, uraw, params->n0));
+ FAILZ(BN_add(&u, &u, BN_value_one()));
+ }
+
+ FAILZ(q = EC_POINT_new(ca));
+ FAILZ(r = EC_POINT_new(ca));
+ FAILZ(EC_POINT_mul(ca, q, 0, ga, &u, params->ctx));
+ FAILZ(EC_POINT_mul(ca, r, 0, pa, &u, params->ctx));
+
+ FAILZ(EC_POINT_get_affine_coordinates_GF2m(ca, q, &x, &y, params->ctx));
+ if (bn2bin_padhi(&x, message, mlen) != mlen)
+ goto fail;
+ if (message[0] & (params->pad_mask|params->curve_bit)) /* see below */
+ goto fail;
+ memcpy(secret, message, mlen);
+
+ FAILZ(EC_POINT_get_affine_coordinates_GF2m(ca, r, &x, &y, params->ctx));
+ if (bn2bin_padhi(&x, secret + mlen, mlen) != mlen)
+ goto fail;
+
+ /* K high bits of the message will be zero. Fill in the high K-1
+ of them with random bits from the pad, and use the lowest bit
+ to identify the curve in use. That bit will have a bias on the
+ order of 2^{-d/2} where d is the bit-degree of the curve; 2^{-81}
+ for the only curve presently implemented. This is acceptably
+ small since an elliptic curve of d bits gives only about d/2 bits
+ of security anyway, and is much better than allowing a timing
+ attack via the recipient having to attempt point decompression
+ twice for curve 1 but only once for curve 0 (or, alternatively,
+ doubling the time required for all decryptions). */
+
+ pad &= params->pad_mask;
+ pad |= (use_curve0 ? 0 : params->curve_bit);
+ message[0] |= pad;
+
+ rv = 0;
+ done:
+ BN_clear(&u);
+ BN_clear(&x);
+ BN_clear(&y);
+ if (q) EC_POINT_clear_free(q);
+ if (r) EC_POINT_clear_free(r);
+ return rv;
+
+ fail:
+ log_crypto_warn("MKEM::generate");
+ memset(message, 0, mlen);
+ memset(secret, 0, mlen * 2);
+ rv = -1;
+ goto done;
+}
+
+int
+MKEM::decode(uint8_t *secret, const uint8_t *message) const
+{
+ int use_curve0 = !(message[0] & params->curve_bit);
+ const EC_GROUP *ca = use_curve0 ? params->c0 : params->c1;
+ const BIGNUM *sa = use_curve0 ? s0 : s1;
+ EC_POINT *q = 0, *r = 0;
+ uint8_t *unpadded = 0;
+ BIGNUM x, y;
+ size_t mlen = params->msgsize;
+ int rv;
+
+ if (!s0 || !s1) /* secret key not available */
+ return -1;
+
+ BN_init(&x);
+ BN_init(&y);
+ FAILZ(q = EC_POINT_new(ca));
+ FAILZ(r = EC_POINT_new(ca));
+ FAILZ(unpadded = (uint8_t *)xmalloc(mlen + 1));
+
+ /* Copy the message, erase the padding bits, and put an 0x02 byte on
+ the front so we can use EC_POINT_oct2point to recover the
+ y-coordinate. */
+ unpadded[0] = 0x02;
+ unpadded[1] = (message[0] & ~(params->pad_mask|params->curve_bit));
+ memcpy(&unpadded[2], &message[1], mlen - 1);
+
+ FAILZ(EC_POINT_oct2point(ca, q, unpadded, mlen + 1,
+ params->ctx));
+ FAILZ(EC_POINT_mul(ca, r, 0, q, sa, params->ctx));
+
+ FAILZ(EC_POINT_get_affine_coordinates_GF2m(ca, q, &x, &y, params->ctx));
+ if (bn2bin_padhi(&x, secret, mlen) != mlen)
+ goto fail;
+
+ FAILZ(EC_POINT_get_affine_coordinates_GF2m(ca, r, &x, &y, params->ctx));
+ if (bn2bin_padhi(&x, secret + mlen, mlen) != mlen)
+ goto fail;
+
+ rv = 0;
+ done:
+ if (unpadded) {
+ memset(unpadded, 0, mlen + 1);
+ free(unpadded);
+ }
+ if (q) EC_POINT_clear_free(q);
+ if (r) EC_POINT_clear_free(r);
+ BN_clear(&x);
+ BN_clear(&y);
+ return rv;
+
+ fail:
+ log_crypto_warn("MKEM::decode");
+ rv = -1;
+ memset(secret, 0, mlen * 2);
+ goto done;
+}
diff --git a/src/mkem.h b/src/mkem.h
new file mode 100644
index 0000000..8e9d6a6
--- /dev/null
+++ b/src/mkem.h
@@ -0,0 +1,117 @@
+/* Copyright 2012 SRI International
+ * Based on the public-domain reference implementation of Moeller 2004
+ * to be found at http://github.com/zackw/moeller-ref/
+ * See LICENSE for other credits and copying information
+ */
+
+#ifndef MKEM_H
+#define MKEM_H
+
+/* NOTE: The APIs defined in this header should not be used directly.
+ Use the crypt.h 'mke_generator' and 'mke_decoder' objects instead. */
+
+#include <openssl/bn.h>
+#include <openssl/ec.h>
+
+struct MKEMParams
+{
+ BN_CTX *ctx;
+
+ const BIGNUM *m;
+ const BIGNUM *b;
+ const BIGNUM *a0;
+ const BIGNUM *a1;
+ const BIGNUM *p0;
+ const BIGNUM *p1;
+ const BIGNUM *n0;
+ const BIGNUM *n1;
+ const BIGNUM *maxu;
+
+ const EC_GROUP *c0;
+ const EC_GROUP *c1;
+
+ const EC_POINT *g0;
+ const EC_POINT *g1;
+
+ size_t msgsize;
+ unsigned int pad_bits;
+ uint8_t pad_mask;
+ uint8_t curve_bit;
+
+ MKEMParams(BN_CTX *ctx);
+ ~MKEMParams();
+
+private:
+ MKEMParams(const MKEMParams&) DELETE_METHOD;
+ MKEMParams& operator=(const MKEMParams&) DELETE_METHOD;
+};
+
+// needed to distinguish two constructor overloads
+struct MKEMPrivateKeyLoad {};
+const struct MKEMPrivateKeyLoad PRIVATE_KEY = {};
+
+struct MKEM
+{
+ const MKEMParams *params;
+ const BIGNUM *s0;
+ const BIGNUM *s1;
+ const EC_POINT *p0;
+ const EC_POINT *p1;
+
+ ~MKEM();
+
+ /** Generate a brand new keypair from randomness. */
+ MKEM(const MKEMParams *params);
+
+ /** Load a secret key expressed as two integers (s0, s1), and
+ regenerate the public key from it. */
+ MKEM(const MKEMParams *params,
+ const uint8_t *s0, size_t s0l,
+ const uint8_t *s1, size_t s1l,
+ const struct MKEMPrivateKeyLoad&);
+
+ /** Load a public key expressed as two elliptic curve points (p0, p1).
+ Since the secret key is not available, MKEM_export_secret_key and
+ MKEM_decode_message will fail if called on this MKEM. */
+ MKEM(const MKEMParams *params,
+ const uint8_t *p0, size_t p0l,
+ const uint8_t *p1, size_t p1l);
+
+ /** Export the public key as a pair of points. The byte buffers
+ must each point to at least params->msgsize+1 bytes of storage. */
+ int export_public_key(uint8_t *p1, uint8_t *p2) const;
+
+ /** Export the secret key as a pair of integers. The byte buffers
+ must each point to at least params->msgsize bytes of storage. */
+ int export_secret_key(uint8_t *s0, uint8_t *s1) const;
+
+ /** Generate secret material K and encrypted message kk from randomness.
+ This does NOT carry out key derivation; the "secret" output is what
+ the paper describes as $\mathfrak{k} || encode(x_R)$, not KDF of that.
+ The 'message' argument must point to at least params->msgsize
+ bytes of storage, and the 'secret' argument must point to twice
+ that much storage. */
+ int generate(uint8_t *secret, uint8_t *message) const;
+
+ /** Same, but work from a preselected integer 'u', which must be in
+ the closed interval [1, params->maxu], and an extra byte's worth
+ of random bits for padding.
+
+ This is exposed only for the sake of known-answer tests. Use of
+ non-random 'u' or 'pad' invalidates system properties, as does
+ reuse of either value. */
+ int generate(const BIGNUM *u, uint8_t pad,
+ uint8_t *secret, uint8_t *message) const;
+
+ /* Decode an encrypted message. As with MKEM_generate_message, the
+ result is NOT run through a KDF. */
+ int decode(uint8_t *secret, const uint8_t *message) const;
+
+private:
+ void load_secret_key();
+
+ MKEM(const MKEM&) DELETE_METHOD;
+ MKEM& operator=(const MKEM&) DELETE_METHOD;
+};
+
+#endif
1
0
commit 83cd5f99b7e08c064962f985a92e4b47dc2276fd
Author: Zack Weinberg <zackw(a)cmu.edu>
Date: Thu Jun 7 13:59:44 2012 -0700
Add unit tests for ECDH.
---
src/crypt.cc | 89 ++++++++--
src/crypt.h | 6 +
src/test/unittest_crypt.cc | 433 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 515 insertions(+), 13 deletions(-)
diff --git a/src/crypt.cc b/src/crypt.cc
index 8c7a6b9..ec59511 100644
--- a/src/crypt.cc
+++ b/src/crypt.cc
@@ -419,45 +419,105 @@ gcm_decryptor_impl::decrypt(uint8_t *out, const uint8_t *in, size_t inlen,
namespace {
struct ecdh_message_impl : ecdh_message
{
- EC_KEY *priv;
+ EC_KEY *key;
BN_CTX *ctx;
- ecdh_message_impl(); // generate keypair from randomness
+ ecdh_message_impl();
+ ecdh_message_impl(const uint8_t *secret);
virtual ~ecdh_message_impl();
virtual void encode(uint8_t *xcoord_out) const;
virtual int combine(const uint8_t *other, uint8_t *secret_out) const;
+
+ int regen_pubkey();
};
}
ecdh_message_impl::ecdh_message_impl()
- : priv(EC_KEY_new_by_curve_name(NID_secp224r1)),
+ : key(EC_KEY_new_by_curve_name(NID_secp224r1)),
ctx(BN_CTX_new())
{
- if (!priv || !ctx)
+ if (!key || !ctx)
log_crypto_abort("ecdh_message::allocate data");
- if (!EC_KEY_generate_key(priv))
- log_crypto_abort("ecdh_message::generate priv");
+ if (!EC_KEY_generate_key(key))
+ log_crypto_abort("ecdh_message::generate key");
}
-/* static */ ecdh_message *
+ecdh_message_impl::ecdh_message_impl(const uint8_t *secret)
+ : key(EC_KEY_new_by_curve_name(NID_secp224r1)),
+ ctx(BN_CTX_new())
+{
+ BIGNUM *sb = BN_bin2bn(secret, EC_P224_LEN, 0);
+ if (!key || !ctx || !sb)
+ log_crypto_abort("ecdh_message::allocate data");
+
+ if (!EC_KEY_set_private_key(key, sb))
+ log_crypto_abort("ecdh_message::set privkey");
+
+ BN_clear_free(sb);
+}
+
+// EC_KEY_set_private_key *does not* regenerate the public key, nor is
+// there any official or unofficial EC_KEY method to do so. So we have
+// to do it by hand. This is separate from the constructor so that it
+// can fail by returning -1 (since we still can't use exceptions).
+// Fortunately this is only for testing.
+int
+ecdh_message_impl::regen_pubkey()
+{
+ const EC_GROUP *grp = EC_KEY_get0_group(key);
+ if (!grp)
+ return -1;
+
+ const BIGNUM *priv = EC_KEY_get0_private_key(key);
+ if (!priv)
+ return -1;
+
+ EC_POINT *pub = EC_POINT_new(grp);
+ if (!pub)
+ return -1;
+
+ if (!EC_POINT_mul(grp, pub, priv, 0, 0, ctx))
+ return -1;
+
+ EC_KEY_set_public_key(key, pub);
+ EC_POINT_clear_free(pub);
+
+ return EC_KEY_check_key(key) ? 0 : -1;
+}
+
+ecdh_message *
ecdh_message::generate()
{
REQUIRE_INIT_CRYPTO();
return new ecdh_message_impl();
}
+ecdh_message *
+ecdh_message::load_secret(const uint8_t *secret)
+{
+ REQUIRE_INIT_CRYPTO();
+ ecdh_message_impl *imp = new ecdh_message_impl(secret);
+
+ if (imp->regen_pubkey()) {
+ log_crypto_warn("regenerate public key");
+ delete imp;
+ return 0;
+ }
+ return imp;
+}
+
ecdh_message::~ecdh_message() {}
ecdh_message_impl::~ecdh_message_impl()
{
- EC_KEY_free(priv);
+ EC_KEY_free(key);
BN_CTX_free(ctx);
}
void
ecdh_message_impl::encode(uint8_t *xcoord_out) const
{
- const EC_POINT *pub = EC_KEY_get0_public_key(priv);
- const EC_GROUP *grp = EC_KEY_get0_group(priv);
+ const EC_POINT *pub = EC_KEY_get0_public_key(key);
+ const EC_GROUP *grp = EC_KEY_get0_group(key);
if (!pub || !grp)
log_crypto_abort("ecdh_message_encode::extract pubkey");
@@ -484,9 +544,12 @@ int
ecdh_message_impl::combine(const uint8_t *xcoord_other,
uint8_t *secret_out) const
{
- const EC_GROUP *grp = EC_KEY_get0_group(priv);
+ const EC_GROUP *grp = EC_KEY_get0_group(key);
+ if (!grp)
+ log_crypto_abort("ecdh_message_combine::retrieve curve group");
+
EC_POINT *pub = EC_POINT_new(grp);
- if (!grp || !pub)
+ if (!pub)
log_crypto_abort("ecdh_message_combine::allocate data");
int rv = -1;
@@ -501,7 +564,7 @@ ecdh_message_impl::combine(const uint8_t *xcoord_other,
goto done;
}
- if (!ECDH_compute_key(secret_out, EC_P224_LEN, pub, priv, 0)) {
+ if (!ECDH_compute_key(secret_out, EC_P224_LEN, pub, key, 0)) {
log_crypto_warn("ecdh_message_combine::compute shared secret");
goto done;
}
diff --git a/src/crypt.h b/src/crypt.h
index 35e3e37..cc9309a 100644
--- a/src/crypt.h
+++ b/src/crypt.h
@@ -151,6 +151,12 @@ struct ecdh_message
/** Generate a new Diffie-Hellman message from randomness. */
static ecdh_message *generate();
+ /** Generate a new Diffie-Hellman message from a specified secret
+ value (in the form of a big-endian byte string, EC_P224_LEN
+ bytes long). This is provided for testing purposes only; it
+ should not be used in normal operation. */
+ static ecdh_message *load_secret(const uint8_t *secret);
+
/** Encode a Diffie-Hellman message to the wire format. This
produces only the x-coordinate of the chosen curve point.
The argument must point to EC_P224_LEN bytes of buffer space. */
diff --git a/src/test/unittest_crypt.cc b/src/test/unittest_crypt.cc
index c411386..f1fed0f 100644
--- a/src/test/unittest_crypt.cc
+++ b/src/test/unittest_crypt.cc
@@ -834,6 +834,437 @@ test_crypt_aesgcm_bad_dec(void *)
end:;
}
+/* ECDH/P224 test vectors from
+ http://csrc.nist.gov/groups/STM/cavp/documents/keymgmt/kastestvectors.zip
+ specifically, the P224 vectors in
+ KASValidityTest_ECCEphemeralUnified_NOKC_ZZOnly_init.fax */
+
+static void
+test_crypt_ecdh_p224_good(void *)
+{
+ struct testvec
+ {
+ const char *dA;
+ const char *xA;
+ const char *dB;
+ const char *xB;
+ const char *z;
+ };
+ const struct testvec testvecs[] = {
+ { "\x05\x82\x59\xe4\x16\x0b\x78\x3b\x90\xec\x2e\xe9\x3a\xa1"
+ "\xd8\x51\x49\x60\xbe\x5c\x99\x59\xf5\x01\xa1\xe0\xc4\x76",
+ "\xae\x04\xaa\x96\x54\x89\xc3\x21\x29\xed\x86\x70\xa6\x3c"
+ "\xf6\x57\x01\xda\x2f\x4d\xa3\xe7\xb9\x0f\x4d\x08\x70\x55",
+ "\xa4\x08\x18\x43\x0a\x49\x72\xb5\x02\xd2\x3c\xdd\x62\x2a"
+ "\xd7\x3e\xdb\x75\x7f\x52\xa0\xc0\x7c\x85\xdd\x2a\x0d\x48",
+ "\x26\xde\xf8\xd1\x5e\x96\xde\x9a\x19\x50\x26\x01\x85\xce"
+ "\x3e\xe1\x47\x8c\xb5\xb4\xb1\x4a\x42\x37\x90\xf4\x24\xcc",
+ "\xf8\xbe\x1b\x1f\xd0\xa1\x8b\xa5\x52\x19\x10\x39\x58\x77"
+ "\xa5\xf9\x56\xbc\x80\x3a\x56\x8e\x16\xb0\xd6\xac\x13\x11" },
+
+ { "\xd6\x74\xf9\x1b\xf9\xdd\x9b\x99\x8c\x70\xa3\x04\x02\xc2"
+ "\x18\x9b\x04\xfe\xad\xd3\x05\xee\xb3\x4d\x25\x74\x31\xa9",
+ "\xab\x4a\xb8\xee\x1c\x20\x7a\xfe\x55\xbf\x5b\xf8\xc1\xb3"
+ "\x20\x7d\x6e\x6d\xcb\xad\x5b\x67\x83\xb3\x3d\xc1\x42\x6c",
+ "\x84\xf5\x5f\x59\xe4\xe0\xd8\x24\xfa\xda\x3c\x65\xd6\xc6"
+ "\xb4\x13\xa2\x98\x80\x09\x71\x2b\x0e\x05\xdf\x1f\x4a\x76",
+ "\x96\xf6\xd4\xfe\x22\x90\x0c\x0d\xf6\x09\x9c\x19\xa7\x4f"
+ "\x66\xf3\xf1\x56\x16\xeb\x3c\xaf\xb4\x79\xea\x2c\x81\xee",
+ "\xd6\xee\x73\xac\x1d\x8f\x6c\x45\x25\xc3\x55\x96\x44\x53"
+ "\xd6\x1d\xe9\x6b\xdb\x7a\x20\xb0\x62\xfd\x61\xc7\xde\x84" },
+
+ { "\xd1\x99\x4e\xf8\xc1\xab\x97\x77\xc6\x25\x1d\xc0\x3f\xf5"
+ "\xbe\xc8\xf3\xbf\x62\x3e\x06\xd2\x29\xc5\xd6\x15\xc3\x96",
+ "\x5c\x9e\x74\x34\x5b\xd7\xe3\x02\xe1\xf2\xf7\xba\x48\x78"
+ "\xb7\x0c\x05\xf7\x40\x3d\x44\xf2\x6c\xef\x16\xe4\xb1\x21",
+ "\x8e\x17\x32\xa5\x68\xf3\xe1\xb9\x75\x06\x56\x90\x7a\x1d"
+ "\x77\x0b\x83\x2f\xc9\x3c\x42\xfe\xfb\xb5\x69\x6b\x50\x14",
+ "\x5e\x5d\x65\x39\x48\x7a\x45\x0e\x5a\xa0\x67\x98\x68\x6b"
+ "\x81\x6b\x36\xe4\x73\x73\xc9\x3c\xea\x68\xaa\xa4\x6b\x56",
+ "\x0c\x2b\xa0\x69\x10\xa1\xe6\x34\x60\x49\xfd\x6c\x82\x64"
+ "\x5a\xa6\xbe\x55\x71\x41\x09\x04\x8e\x3e\x77\x3c\xbb\x76" },
+
+ { "\xae\x94\x56\x83\x48\x85\xf7\xb3\xc1\x32\xa9\x05\x97\xb5"
+ "\xb1\x5f\x00\x75\x7a\x6b\x01\x08\xc5\x23\x05\x69\x8a\xff",
+ "\x32\xfa\x65\x28\x51\x48\xde\xb2\x25\xef\x7a\x8e\xbb\x59"
+ "\x19\x24\xf4\x21\x12\x66\xc9\x23\x2e\x5e\x56\xfc\x0f\xa7",
+ "\xdc\x56\x0c\x5d\x5a\x65\xd0\x7c\xad\xec\x18\xd0\xfa\xe4"
+ "\xd0\x0a\x27\xb9\x9b\xe6\x25\xf9\xcd\x24\xce\x09\x6f\x60",
+ "\x29\x9f\x28\xfb\xab\xcf\x4a\x4f\x99\x12\x53\x5c\x81\x08"
+ "\xe4\x18\xf0\x36\xf1\xd3\x75\x11\x7e\x87\xe8\x59\x5f\x79",
+ "\x15\x52\x19\xe1\x51\x7d\xa8\x85\x68\xa1\x03\x44\xd8\xa9"
+ "\x27\xdd\x9a\x45\x94\xe6\x4a\x68\x52\xcb\x6d\xa8\x33\x45" },
+
+ { "\x73\x02\x93\xbb\x30\x87\xfb\x88\x2a\xb8\xf2\xf4\x6c\xb1"
+ "\x4a\x51\x2b\x55\x29\xd8\x30\x24\x67\xb5\x92\xb1\xa5\x4b",
+ "\xa7\x40\xfa\x8a\x31\xcc\xd7\xbf\x77\x85\x7f\x24\xed\x2b"
+ "\xfd\x17\xb5\x65\x49\x5e\x75\xbd\x1e\x55\x4b\x6b\xa9\xa5",
+ "\xdb\x37\xa8\x7d\x80\x12\xa0\xcb\xf2\x55\x82\x69\x17\xb3"
+ "\xb9\xa2\x8f\x4a\x51\xdf\x58\x96\x3c\xde\x03\xf4\xa4\x46",
+ "\x3c\xc2\xe8\xbf\xcc\xac\x84\x59\xbd\x4f\x44\xfe\x58\x48"
+ "\xdd\x97\xb6\x7e\xa7\xf3\x78\x72\x2c\xb0\x81\xf9\x9d\x2f",
+ "\xb9\xbd\x07\xcd\x07\x0d\x9f\xcb\x90\x85\xa6\x36\x07\xf8"
+ "\x4a\xa7\x3a\x82\x32\xaf\x33\x28\x34\xd0\x4f\x00\xe3\xd2" },
+
+ { "\x31\x4e\x8e\xb8\xf2\x97\x6f\x2d\xfd\x1b\x3c\xfb\xd6\x9c"
+ "\xf4\xbd\xce\x9f\x1e\x9a\x6e\xe5\x40\x15\x3a\xca\x3c\x2e",
+ "\x24\x03\x1c\x12\xca\xea\xcf\x66\x29\xc8\x78\x36\x61\x65"
+ "\xca\x4f\x76\xf2\x1a\x40\x85\xc0\xbe\x1a\x83\x58\x76\xd7",
+ "\x54\x22\x6d\x6f\x24\x0a\x0e\x6d\x05\x22\x39\xd3\x8f\xf2"
+ "\xff\x18\x55\x7f\xd2\xed\xcd\xe4\xfc\x4f\x85\xf4\x54\x9f",
+ "\xab\x87\xed\xad\x74\x9a\x6c\x2c\xc7\x4a\x92\xb0\x83\x06"
+ "\x6b\x68\xd3\x5c\x2c\x91\x89\x9a\xff\xff\xc3\x22\xb9\x6f",
+ "\x35\x63\x39\x89\x49\xc4\x0a\xa4\x88\x7b\xda\xdb\xf8\x65"
+ "\x49\xbb\x58\xa1\x66\x16\x56\x60\x51\x07\xfb\xc7\x2a\x56" },
+
+ { "\x1f\x04\xc0\xe3\x51\x45\x7f\x27\x2f\xff\xdc\xff\x4f\x81"
+ "\xd7\x38\x56\x51\x6f\x31\x18\x8d\xbf\xec\xcc\xec\x83\x7e",
+ "\x8e\xc7\x5b\x09\xf0\x40\xa6\xb3\x24\x6e\x98\x57\x96\xfb"
+ "\x26\xbe\x66\x20\x08\x29\x1a\x00\xfe\x64\xe8\xff\xb8\xeb",
+ "\x7f\x1d\x7a\xeb\x23\xe8\x23\x01\xc0\x56\x18\x31\x91\x9a"
+ "\x1d\xb8\x69\x06\xb0\x7e\x42\xf9\xa3\x59\x17\x4b\x38\x5d",
+ "\x12\xdb\x42\x99\x03\x52\x81\x59\x17\xee\x7e\x0b\x23\xa7"
+ "\x7d\x5a\x04\xc7\x87\x62\x07\x0e\x6b\xdb\x11\x04\x75\x60",
+ "\xe0\x93\xfe\xde\xff\xff\xc3\xf1\xb9\x01\x9b\x0c\x03\x2f"
+ "\x64\x07\x25\xe6\x54\x4c\x5e\x6f\x1b\x39\x76\xaf\xe9\xfa" },
+
+ { "\x7e\x31\x5c\x03\xd3\xf3\x2d\x62\xa1\xa2\x53\xce\x37\x30"
+ "\x78\x8a\x94\x3d\xe0\x51\x03\xb0\xae\x2d\xf6\x06\x9c\x1e",
+ "\x9b\x3b\x5d\x53\x83\x17\xeb\x4e\x05\xfe\xb6\x40\xe1\xfc"
+ "\x72\x10\x81\xb5\x11\x79\xa5\xba\x46\x83\x94\x6c\x03\x36",
+ "\x88\x39\x5f\xd4\x54\x96\xb7\xb0\x0e\xa6\x18\xfd\xf9\x96"
+ "\x5f\xf9\x27\x28\x44\xf4\xac\xb6\xd4\x48\x3e\x8a\xe5\xa5",
+ "\xd8\xc0\xd0\xe8\x70\x7b\x13\x18\xdc\x24\xd7\xb4\x3d\x1c"
+ "\xce\x32\x9c\x13\xc5\xcc\x49\xca\x96\x16\xe1\x7b\x04\xbf",
+ "\x00\xff\x17\x5c\x7b\x27\x99\x56\xc5\x43\xe7\xfb\x0c\xfd"
+ "\x26\xf8\x8b\x98\xd6\x34\x50\x02\x4b\xbf\x9e\x65\x01\xf4" },
+
+ { "\x29\x1a\x48\x68\xef\x47\x24\x85\x51\x1c\xb3\x81\x86\x83"
+ "\x53\x98\x3e\xa2\xb6\xff\x52\xf4\x1f\x98\x47\xf1\x79\x2c",
+ "\xf3\x86\x89\xd0\xb9\xa3\xa0\x39\x57\x84\x62\xaf\x92\xf5"
+ "\xa9\x28\xea\x23\x4d\x93\xe3\xed\x4c\xb2\x41\x39\xd1\xd0",
+ "\xc1\x6a\xbc\x67\x50\x7f\x0a\xff\xd5\x56\x9d\x90\x12\x98"
+ "\x8d\x8a\xcd\x76\x2c\xa0\x04\x57\x90\xe9\xcb\xac\xf4\x50",
+ "\x7a\x9b\x85\x5b\xef\x01\x0e\xde\xf1\xb4\x85\x64\xb2\x0c"
+ "\xb2\xe4\x06\x68\xe0\xe5\xde\x16\xa2\x55\xf4\x14\x23\x5d",
+ "\xf5\x21\x7a\x73\x8a\xd9\x63\xcd\x0d\xed\xf4\xd6\x18\x5b"
+ "\x19\x0d\x77\x6e\xb8\x5a\x57\x88\xad\x7f\xba\x2e\x8a\x2c" },
+
+ { "\x11\x62\x98\x59\xee\x09\xef\xbe\x60\xdb\x65\x58\xb9\xbd"
+ "\x80\xcc\x9a\x30\x96\xc0\xbc\x9e\x69\x50\xfa\xd9\xa8\x2d",
+ "\x1a\x2f\x4b\x80\x6b\xa2\x34\xc0\x50\xa3\xf7\x50\x52\x99"
+ "\x5a\xb8\x63\x13\xc5\x11\xa8\x41\x2e\xa0\x5f\x32\x03\x8b",
+ "\x48\x72\x4a\x5b\x79\x22\xbd\x20\x4b\xe2\x95\x3d\x47\xa4"
+ "\x5a\xf5\xaa\x9b\x0a\x72\x04\x55\x1f\xe3\x7f\xcb\x91\xa7",
+ "\x8f\x18\x48\xfa\x23\x8e\xf8\x36\xe1\x45\xcd\x32\x72\xf2"
+ "\x51\x4e\x04\x56\xc0\x67\x5c\x43\xe5\x41\x2e\xa2\xaa\x8e",
+ "\x28\xa8\xc7\x03\x0d\x70\x88\x65\xf1\xd4\x6a\xb9\x67\xd0"
+ "\xf6\xdd\x45\x70\x06\x72\xb7\xbf\xaa\x64\xa9\x7a\x01\x23" },
+
+ { "\x17\x4c\x96\xb0\x2a\xba\x8c\xab\x99\xdd\x8c\xbc\xa8\x6b"
+ "\xfa\x8b\x0d\x3f\x4f\x4c\x9a\x8a\x1b\x75\x9d\x53\x6c\x18",
+ "\xf4\x83\xb9\x4b\x60\x26\xae\x11\xab\xed\x33\x90\x68\x16"
+ "\xfb\x63\x0c\x29\x7b\x79\x01\x82\x23\xb6\x3d\x06\xc0\x79",
+ "\x0f\x6e\x7b\x5b\x6c\xad\xe8\xfc\xd9\xb0\x93\x28\x73\xff"
+ "\xab\xdc\x69\x75\x24\x00\x1e\x03\x12\x46\xc9\xbf\x29\x79",
+ "\x98\x84\x39\xbb\xde\xee\x89\x45\x0c\xb2\x0a\x58\xa0\x96"
+ "\x0a\x74\x19\xb6\x17\xa8\xe2\x33\xef\xf5\x8b\x23\xf5\x85",
+ "\xfa\x28\xc6\x51\xeb\xbb\x03\xb3\xd1\xb5\xc7\x32\xbe\x7c"
+ "\xfd\x06\xec\xd6\x68\x5d\xae\x19\xc4\x45\x1e\x30\xd9\x69" },
+
+ { "\x47\xa0\xc4\xf2\x9f\x82\x31\x3f\x37\x6a\x3f\x45\x9a\x52"
+ "\xa8\x15\x9c\x19\x41\xb5\xdd\x51\xa5\xb2\x51\x80\xf4\xda",
+ "\x6d\xb1\x09\xdf\xb3\x13\xb7\x9b\x96\x4b\x03\xad\x15\x92"
+ "\x58\xfe\xdd\xa0\xe3\x2c\xb1\x8d\xef\x87\xa5\xe3\xdd\x6e",
+ "\x18\xc0\x86\x35\x8b\x6b\x26\xf0\xe7\xa8\x24\x23\xd0\xe2"
+ "\xf8\x65\x29\x01\xe4\xba\x16\xd8\x39\x92\x9b\x4a\x59\x77",
+ "\xf7\x00\x7e\xcd\x09\x3d\xe4\x8c\x79\xd6\x1e\x0e\x47\xc5"
+ "\x68\x31\xcf\xc0\x9d\x5a\x66\x7f\x9a\xd3\x95\xef\x4d\x1f",
+ "\xe8\xfa\xd8\xdd\xb1\xde\x6c\x24\x42\x96\x16\x05\xd1\xb7"
+ "\xf6\xe9\xc8\xc6\xa7\x29\x87\xfc\x93\xd7\xa2\xf4\xd9\xf9" },
+
+ { "\x7e\x96\x75\x75\x58\x4f\xb8\x8a\xcf\x3f\xe6\x92\x43\xbe"
+ "\x13\x21\xa3\xea\xa6\x8c\xe1\xe6\x8d\x72\x4c\x8e\x77\xe8",
+ "\xa6\x52\x08\xef\xdc\x22\x8e\x99\x5c\x96\x3e\xf3\x48\xd7"
+ "\x82\x8f\x1d\x6f\x53\x51\x1e\xd1\xe1\xdd\x7e\x99\xdd\xe1",
+ "\xb5\x00\xc1\xa6\x3d\x44\xa9\xec\x42\x81\xed\x2f\x68\x7e"
+ "\xdc\x47\x6e\x0d\x2d\xf3\xb3\x10\x57\x6a\xe8\xaf\x63\x67",
+ "\xfa\x27\x37\xe3\x19\x5d\xa2\x69\xfb\x48\xa3\x8a\x82\x0e"
+ "\x8c\xab\x93\xec\x9f\x90\xc5\x1a\xff\x55\x26\x6f\x4e\x85",
+ "\xc2\x8a\xc6\x40\x02\xa3\x07\xae\xf0\x4b\x85\x11\xbc\x82"
+ "\xc9\xdd\x34\x3a\x2f\x4f\x3f\xc6\xa5\x48\x6c\x6d\xaa\x68" },
+
+ { "\xf5\x69\x65\xa6\x9f\x1e\xac\x36\x55\x5d\x24\x2b\x85\xe7"
+ "\x5c\x1d\x17\xfe\x09\x2a\xb4\xee\xeb\xec\x98\xca\x43\x82",
+ "\xe0\x02\xd5\x9d\xa5\x39\xf1\xe9\x35\xc8\xf4\xfb\xfa\x2c"
+ "\x75\x37\x5d\xcb\xd4\xc9\xbf\xb1\xc4\x56\x5e\xc2\x10\x81",
+ "\x14\xe8\xf5\x8a\x0a\xc5\xf9\xce\xab\xde\xa2\xf1\xb7\x50"
+ "\xba\xf1\x2e\x45\xd5\x59\x65\x7e\xa1\xfa\x9c\xbb\x9f\xe2",
+ "\xbb\x25\xd7\x58\xfd\x4a\x48\xbc\x6c\xf8\xc0\xbc\x70\xd7"
+ "\x9d\x6a\xca\x1a\x6d\xd7\x12\xc3\x61\x56\x69\x27\x90\xe5",
+ "\x7a\x85\x53\x35\x47\x89\xbb\x75\xde\xb3\xed\x7f\x41\xa3"
+ "\x73\xa6\x40\x64\x77\x68\xe2\x4a\x38\x08\x1f\x5a\xea\x89" },
+
+ { "\x31\xf4\x63\x6e\x71\x16\x5a\x78\xc6\x92\x92\xdd\xb8\x58"
+ "\x19\x22\xd1\x5e\x0f\xd4\x2d\x5f\x1a\x24\xbd\x8e\x15\xcd",
+ "\xfa\x03\x95\x8e\x0d\x74\x22\x86\xff\xa6\x64\x01\x7c\x0a"
+ "\x0a\x87\xed\x61\x18\x92\xcb\xdd\xa2\xfd\x44\x48\x2b\x81",
+ "\xbe\xb0\x14\x2b\xbc\x7a\x5f\x91\xcf\x30\x45\xc0\x99\xaf"
+ "\xf8\x8f\x77\xb2\xa0\xc5\x3c\xcc\xe3\x8e\xa1\x88\x54\x01",
+ "\x9f\xe8\x75\xc3\xb1\x35\x33\x9a\xc3\x56\xa6\xbd\xac\xfe"
+ "\x53\x59\xb8\xa0\x92\xac\x5d\xd4\x1c\xe5\xaa\x05\x84\x07",
+ "\x43\x0b\xa3\x5c\x07\x62\xe9\xa0\xd3\xfd\x4b\x3a\x32\x2c"
+ "\x89\x32\x2e\x78\x33\x96\xa4\xee\xb6\x57\x03\xca\xc3\xda" },
+
+ { "\xab\x39\xec\xa9\x01\xf7\x3b\xce\xc0\xc9\x0d\x26\xa4\x36"
+ "\x58\x2d\xb8\x00\x88\x95\xdd\x95\x25\x00\x0c\xdf\x47\x7d",
+ "\x06\x3c\x88\x08\x34\x4b\xdb\xa2\x7f\xa7\xb8\xd1\xd9\x4a"
+ "\x43\xf6\x98\xe8\xb4\x7b\x19\x3d\x85\x60\x12\xa3\x96\xd4",
+ "\xf2\x1c\xb9\x06\xb4\xd7\xaa\xf2\x90\x8a\xa5\xe4\x34\xa6"
+ "\x26\xd5\x98\xf4\x4b\x5a\x5b\x8a\xf2\x90\xae\xef\x78\xe3",
+ "\x94\x0a\xfb\x7d\xba\x27\xcc\x67\x23\xc3\xdd\x08\xc3\x46"
+ "\xb0\xe2\x12\xef\x33\x40\x21\x0e\x0c\x4f\xf7\xc8\x74\x77",
+ "\x51\xc8\x06\x43\xdf\x8c\x8d\x61\x95\xbc\x08\x00\x95\xd9"
+ "\x9a\xb3\x87\xda\x50\x55\x83\xf0\x2f\x19\x2d\x62\x36\x65" },
+
+ { "\xd4\x69\x26\x18\x98\x17\x1d\x69\x5a\xfb\x6e\x04\xd4\xfd"
+ "\x46\xe0\x4b\xce\xfb\xd9\x39\x8a\x63\x34\x43\xd7\x8d\x48",
+ "\x05\xec\x47\xa2\xfd\xf0\x52\x81\xbc\x87\xd0\x85\xbd\xb5"
+ "\xdc\xc8\xa2\xe0\x64\x21\xbd\x69\x85\x5e\xe3\xc3\x05\x26",
+ "\x83\xe3\x71\x1c\xa0\x52\x4b\x51\x70\x2a\xea\x58\xd4\x81"
+ "\x73\x5e\xac\x71\x23\x46\x84\xb2\x5b\x76\x15\xf0\x2d\x3a",
+ "\x6a\x8c\xae\x26\x50\x88\xea\xa2\xba\x02\x32\x1f\x23\x9b"
+ "\xc6\x40\x3f\xc2\x51\xd6\xca\xbe\x84\x71\x58\xa8\xac\x94",
+ "\x1a\xb3\xff\xe4\xf1\xe6\x81\xe4\x69\x60\x30\x0d\x1e\x5b"
+ "\xd6\xde\xb6\x05\x98\x99\xc6\xa5\xee\x6d\xc5\xf4\x95\x75" },
+
+ { "\xc1\x93\x35\xc9\x1b\x13\x1e\xd5\x67\x1e\x26\x5b\x60\x0c"
+ "\x61\xb7\x7a\xde\xd2\x52\x5b\xb5\xa7\xb1\xb6\xeb\xf4\xca",
+ "\x8e\x46\x21\x7c\x96\xd8\xa6\xe1\x6e\x3a\x74\x43\x67\x15"
+ "\xb2\x77\xa7\x29\x5c\xcd\xea\xf2\xa3\x66\x08\x81\xdc\xeb",
+ "\xb1\xc7\x1a\x00\x38\x5d\x90\xbe\x93\x25\xc8\x9c\x0e\xa6"
+ "\xea\xb9\x6c\xdb\x71\x18\x61\x3f\xe8\x46\x0c\x2c\x8b\x2e",
+ "\x77\xa6\x39\xb9\x63\x30\x1c\xbf\x6a\xac\x08\xad\x03\x8b"
+ "\x2e\xbe\x8a\x32\x15\x4c\x8d\x6b\x2b\xe3\x5b\x2a\xfb\xa3",
+ "\xff\x0f\x01\x88\x07\x26\xa4\x5f\xa5\x56\xed\xd4\x8a\x2b"
+ "\x59\x0a\xc9\xa5\x01\x74\x16\x1d\x3e\x52\xab\x73\x7a\x16" },
+
+ { "\xa3\xc0\x62\xa4\xcb\x5e\x40\x7f\x7d\x99\xe8\xf9\xe3\x73"
+ "\x86\xb2\x76\x97\x05\x5d\x87\x39\x28\x25\xc6\xe1\x86\xbf",
+ "\x1f\x98\x95\x87\x82\x4c\xb6\x2c\x03\xa0\x18\xab\xd4\xd3"
+ "\x19\xdc\x2b\x08\x2e\xda\x66\x66\x2b\xfb\x7a\x65\x95\xfe",
+ "\x87\xb3\x39\xb3\xa4\x81\x5d\x51\x4a\xcf\xe8\x7b\xd6\x53"
+ "\xbe\xe1\xcc\xd3\x3a\x1d\xb4\x24\xc9\xea\x3f\x67\x53\xc8",
+ "\xca\xe1\x6b\xc9\x21\xa8\x51\x38\x15\x9d\x62\xd9\x20\xbb"
+ "\x73\x6b\xa4\x4c\x73\x71\x72\x5f\xa9\xc9\x59\xee\x1f\xcc",
+ "\x3a\xde\x1c\x85\x1d\x9a\x58\x39\xa9\xff\x5f\x12\x8b\x8d"
+ "\xa8\x05\x09\x5c\x76\xb7\x7b\x85\xa8\x1b\x68\x2e\x8f\xa7" },
+
+ { "\xcd\x16\xea\xa0\x2c\xed\xaf\x67\x4f\x35\x32\xf4\xe7\xca"
+ "\x73\xe4\x01\xf0\xc4\xf5\xa6\xed\xd9\xa6\xd7\x0e\xec\xf8",
+ "\x9c\xc1\x4d\x9d\x8b\x1b\x9e\xcf\xe2\x96\x08\x45\x0c\x90"
+ "\xbc\x8e\x51\xf3\x7d\x0b\x74\xf9\x99\x61\x7e\xc4\xb3\x74",
+ "\x2c\x97\x35\x91\x60\x17\xbc\xa8\x51\xdd\xb8\x2c\x30\x9a"
+ "\x52\x47\x3e\xd3\xfd\x88\x46\x1f\xed\x66\x81\x42\xb0\x84",
+ "\xe9\xff\x59\x72\xc0\x5e\x4e\x6d\x79\x24\x61\xce\x67\x1b"
+ "\x75\x0d\xf2\x04\xcb\xe0\xa2\x73\x1d\xcc\xd6\x06\x42\xfd",
+ "\xe6\x76\xda\xbe\x28\x25\x7e\x14\x1d\xe3\xff\x7b\xd8\x95"
+ "\xdb\x55\x43\x37\xcf\x56\x71\x48\xe7\x87\x80\x9e\xd0\x3a" },
+
+ { "\x23\x6d\x4c\x9b\x98\xcf\x11\xa3\x79\x61\x04\x5a\xbf\xe3"
+ "\x99\xe4\xa6\xab\xc8\xab\x2a\x7e\xbc\x80\xf8\xc8\x8e\x71",
+ "\x9c\xc7\x3c\x49\x82\x1c\x1c\x10\xb5\xf5\x79\x4f\xa5\xd3"
+ "\xde\x6f\x5e\xbc\x4e\x78\x0e\x47\x68\x92\xfb\xcd\x2f\x22",
+ "\xc0\xa9\xb0\x20\xe5\x79\x9e\x7f\xc6\xea\x7a\x44\x79\xb7"
+ "\x66\x13\x90\x16\x66\x74\x60\xf4\xbf\x01\xfc\x45\x21\xfc",
+ "\x39\x61\xe9\xee\x46\x75\x38\x4a\x76\xec\xf5\x29\x8b\xa0"
+ "\x43\x03\x5e\x27\x44\x38\x4c\xd0\xf9\x84\xc4\xf0\xb3\xe7",
+ "\x08\x6c\xd8\x6d\x1c\xd6\x9e\x86\x60\xb4\xf7\xae\x62\xec"
+ "\x2f\xca\xe4\xd6\x3b\xd6\x7d\x46\x78\x2e\xb2\xa4\xd8\x21" },
+
+ { "\x04\xcd\xb9\x7e\x47\x58\xc8\x32\x85\x33\x6a\xb6\xe5\x85"
+ "\xbc\x51\xd0\xa1\x78\x04\xbc\x02\x81\x90\x25\x83\x95\x8a",
+ "\x38\x75\xd5\xc9\x01\x40\x03\x71\x83\xb7\xa6\x59\xfb\xaf"
+ "\x79\xd0\x1c\x86\x27\xbe\xdb\xc1\x88\xb7\x01\xf5\xf7\xca",
+ "\xd3\xfc\x0f\xd8\x2c\x8a\xf1\x09\x82\x58\xde\x13\xb8\x54"
+ "\x39\xe3\x3c\xf5\xb3\xb7\xe3\x5f\x8a\xdb\xf7\x1f\x7a\x06",
+ "\xc9\xcb\xa6\xd3\x91\x4d\x59\x0b\x51\x0f\xe3\x22\x59\xf6"
+ "\xd1\xe2\x4d\xa1\xa9\x93\xe9\xae\x7c\xb3\x85\xad\x15\x4d",
+ "\x2f\x8e\x72\x0f\xa0\xd8\x0b\xb0\xa0\x67\x20\x23\x22\xa7"
+ "\x07\xe7\xa0\xe7\x2e\xae\xfb\xd0\xd8\x2f\xbb\x58\xb8\x83" },
+
+ { "\x94\x58\xed\x7f\xbf\x97\x86\xe8\xf1\xbc\x56\xea\xfd\x69"
+ "\xc9\xa8\x3b\x73\x07\x41\x98\x0f\xdd\xfc\x30\x76\xaa\xee",
+ "\x24\x66\x17\x73\x35\x0d\x72\x8e\xd2\x95\x48\x48\x2f\xf6"
+ "\x76\xe9\x77\xd6\xa0\x76\xcc\x59\xde\xb7\xe8\x2e\x00\xc0",
+ "\xfd\x5e\x7a\x2c\x5b\xb2\x2f\x43\x7d\x3b\xc6\xf0\xaf\xba"
+ "\xa5\x69\x58\xb6\x29\x5b\x03\x5c\x7e\xd8\x61\xbc\x85\xe3",
+ "\xdf\x81\x0c\xd0\xa3\xca\xf2\x5f\xac\x62\x73\x21\x0a\x18"
+ "\x09\xae\x1d\x69\xf3\xfe\xe0\x88\x7b\xc7\xe0\x2b\xc3\x94",
+ "\xfe\xb0\x44\x61\xae\x77\x2f\x66\x67\x27\x76\xdf\xd9\x11"
+ "\x0d\x11\x32\x31\x2f\x67\x4f\xff\xf3\x79\x93\x9b\xfe\xd4" },
+
+ { "\x29\x2e\xa4\x45\xba\xb6\xd4\xa1\x10\xe9\x39\x0e\x6d\x9e"
+ "\x0a\xcb\xd6\x0f\x19\x91\xa6\x42\xcc\x33\x9d\x0c\xc7\x66",
+ "\x7c\x99\x6a\xbe\xab\x4a\x1a\xbc\x16\x64\x03\xe1\xd5\xbd"
+ "\x77\x84\x75\x25\x6b\xa5\xe5\xce\x39\x36\xd3\x2f\x5f\x41",
+ "\x2c\x74\x9f\x27\x53\x0d\x05\x96\xe1\xf3\xb3\x7d\x1b\xf8"
+ "\xac\x30\x90\xcb\xe6\xcb\xc8\x7d\x6c\x3e\x69\x6f\xb1\xd8",
+ "\xba\x49\x93\x24\xda\x93\x4a\x0c\x1f\x56\xac\x2e\x08\x4c"
+ "\xf6\xca\xc6\xc1\x2b\x91\xe7\xf5\xea\x92\xb9\xcc\xac\xaf",
+ "\x2e\x90\x90\x2a\x26\xab\x5d\x13\xe7\x1b\x4b\x73\xaf\xf4"
+ "\xd0\x82\x32\xce\x06\x6b\x0e\x89\x90\xca\x79\x12\x8d\x80" },
+
+ { 0, 0, 0, 0, 0 }
+ };
+
+ ecdh_message *a, *b;
+ int i, rv;
+ uint8_t obuf[EC_P224_LEN];
+
+ for (i = 0; testvecs[i].dA; i++) {
+ a = ecdh_message::load_secret((const uint8_t *)testvecs[i].dA);
+ tt_int_op(a, !=, 0);
+
+ b = ecdh_message::load_secret((const uint8_t *)testvecs[i].dB);
+ tt_int_op(b, !=, 0);
+
+ a->encode(obuf);
+ tt_mem_op(obuf, ==, testvecs[i].xA, EC_P224_LEN);
+ b->encode(obuf);
+ tt_mem_op(obuf, ==, testvecs[i].xB, EC_P224_LEN);
+
+ rv = a->combine((const uint8_t *)testvecs[i].xB, obuf);
+ tt_int_op(rv, ==, 0);
+ tt_mem_op(obuf, ==, testvecs[i].z, EC_P224_LEN);
+
+ rv = b->combine((const uint8_t *)testvecs[i].xA, obuf);
+ tt_int_op(rv, ==, 0);
+ tt_mem_op(obuf, ==, testvecs[i].z, EC_P224_LEN);
+
+ delete a;
+ delete b;
+ }
+
+ end:;
+}
+
+static void
+test_crypt_ecdh_p224_bad(void *)
+{
+ enum { bad_x = 1, bad_z };
+ struct testvec
+ {
+ const char *dA;
+ const char *xA;
+ const char *dB;
+ const char *xB;
+ const char *z;
+ unsigned int what;
+ };
+ const struct testvec testvecs[] = {
+ { "\x8d\x17\x56\x30\xd4\xf8\x31\xc3\x3c\x47\xd3\x1c\xaf\xdb"
+ "\xd9\xf2\x6f\xd0\xc6\x0d\x82\x4c\x69\x6d\x0f\x50\x2a\xbe",
+ "\xd5\x50\x88\x33\x86\x53\xb4\xd3\x78\x88\xf9\x57\x83\xae"
+ "\x36\x70\xeb\x66\x31\xe9\x3c\x75\x91\xab\x4b\x33\x9b\x75",
+ "\xcd\xc7\x8f\x44\x4a\xf1\xa3\x29\x25\x97\x41\x73\xb4\x26"
+ "\x3d\xc7\x79\x1b\x54\x37\xf6\xd6\xf4\xc5\x4f\xd2\x4a\xb9",
+ "\x72\x8d\x13\x5f\xbe\xdf\x2f\xdd\x31\x16\x6f\xd5\x05\x5a"
+ "\x74\x6d\x2b\xf9\x0f\xde\xf2\xdd\xc1\x65\xea\x99\x9a\xc9",
+ "\xfd\x72\x21\x93\x90\x0d\x35\xf0\x79\x52\x8f\x11\x36\x1d"
+ "\xd2\xa8\x7a\x58\xbe\x6b\x34\xb9\xae\xa2\x18\x97\x5b\x4a",
+ bad_x },
+
+ { "\xf0\x13\x22\xb9\xcb\x1b\x17\xe5\xf5\xa6\x14\xe5\x18\x62"
+ "\xd6\x32\x6f\xe7\x64\x9e\xdd\xd8\xe7\x08\x65\xba\xba\xd0",
+ "\x8d\xf9\x90\x92\xb0\x22\x4b\x70\xc2\x26\x26\x27\x18\xce"
+ "\x9b\x4d\xa4\xfb\xbb\xc1\x7b\xb8\x5e\x21\x1f\xff\xe2\x33",
+ "\xb9\xda\x33\x22\x0c\xf1\x79\xc5\x01\x3e\xe5\x01\x69\x4a"
+ "\x70\xf0\xf2\x29\x4c\xa6\x39\x25\x21\x89\x8f\x7d\x0f\xb1",
+ "\x5f\xd7\xee\x0b\x48\x96\xbd\xc1\x72\x4c\xb5\x92\x3a\xe3"
+ "\xda\x8f\x75\xe8\xd5\x63\xa9\x51\xa7\x7e\xb5\xc4\x87\x5c",
+ "\x84\xc9\xfb\x7a\x82\x7a\x4e\xb7\x18\x00\x2a\x47\x0e\xdd"
+ "\x9e\xf0\xe8\xcd\x33\xf0\xc5\xe7\xe5\x1b\x80\xaa\x59\x4e",
+ bad_x },
+
+ { "\xc1\xf3\x83\x84\x0f\x6c\x39\x1b\xdb\x45\x70\x05\x5e\x54"
+ "\x7e\xc3\x84\x75\x8b\x25\x1c\x9c\x0e\x5e\x2e\x14\x53\x3d",
+ "\x5f\xae\xf4\x8b\xef\x7a\x4d\x4a\x8d\xb3\xd6\x4b\xea\xc1"
+ "\x72\x17\x99\xbb\xf0\x83\xd8\xa3\x70\x7e\x84\x71\xa1\xe2",
+ "\x55\x30\xfc\xf5\x2f\x1a\x02\x00\xd4\x6b\x66\x69\x8a\xa7"
+ "\xb4\x20\x1c\x8d\xee\x1c\x41\xff\x27\x1e\x8f\xac\xc7\x5c",
+ "\x3b\x57\xae\x47\x2d\xb8\x1a\xf0\x20\x0b\x8d\x53\xf9\x1d"
+ "\x5b\x5b\xe6\x13\xe7\xff\xc1\x33\x19\xe9\x14\x8b\xe9\x10",
+ "\xa6\xb3\x02\x59\xa8\x41\x77\x22\x68\xe2\xd4\xe3\x10\xb6"
+ "\x19\x05\xa7\x58\x36\xf9\xf9\x74\x73\x33\x1b\x9d\x62\xd9",
+ bad_z },
+
+ { "\x71\xa4\xc0\xbd\x85\xa9\xfb\x8e\x33\xc5\x4c\xdd\x73\x1f"
+ "\x98\x7c\xb8\xa2\x44\xc5\x2c\x27\x7d\x97\x67\x7e\x4b\x43",
+ "\x30\xd3\x18\xbe\x17\x7b\xa5\xb5\x13\x69\x87\x89\xc6\xfc"
+ "\x4e\x54\xd8\x92\x57\xd2\x62\xdf\x2f\x09\xab\x0d\x21\x16",
+ "\x4f\xf7\x7e\xb6\x4d\x97\xd7\x1c\x12\xb4\x52\x6e\xc6\xde"
+ "\x2e\x5f\x97\xbe\x5f\xcb\x5d\x16\x44\x74\x7a\x5a\x40\x03",
+ "\x32\x14\x21\x90\xe1\xda\xbb\x40\x73\x27\xc4\x4a\xb3\x18"
+ "\x82\x2a\x15\x62\x68\x38\x4c\x86\xc5\x5a\x33\xcc\x8d\x0a",
+ "\x86\x8d\x73\xd8\xca\xce\x1b\x87\xa5\xfe\x16\xcb\xf3\x49"
+ "\x9e\x4a\x69\x85\xef\x6c\xae\xe0\xf0\x92\x46\xb4\xb0\x22",
+ bad_z },
+
+ { 0, 0, 0, 0, 0, 0 }
+ };
+
+ ecdh_message *a, *b;
+ int i, rv;
+ uint8_t obuf[EC_P224_LEN];
+
+ for (i = 0; testvecs[i].dA; i++) {
+ a = ecdh_message::load_secret((const uint8_t *)testvecs[i].dA);
+ tt_int_op(a, !=, 0);
+
+ b = ecdh_message::load_secret((const uint8_t *)testvecs[i].dB);
+ tt_int_op(b, !=, 0);
+
+ a->encode(obuf);
+ if (testvecs[i].what == bad_x) {
+ tt_mem_op(obuf, !=, testvecs[i].xA, EC_P224_LEN);
+ } else {
+ tt_mem_op(obuf, ==, testvecs[i].xA, EC_P224_LEN);
+ }
+
+ b->encode(obuf);
+ tt_mem_op(obuf, ==, testvecs[i].xB, EC_P224_LEN);
+
+ memset(obuf, 0, EC_P224_LEN);
+ rv = a->combine((const uint8_t *)testvecs[i].xB, obuf);
+ tt_int_op(rv, ==, 0);
+ if (testvecs[i].what == bad_z) {
+ tt_mem_op(obuf, !=, testvecs[i].z, EC_P224_LEN);
+ } else {
+ tt_mem_op(obuf, ==, testvecs[i].z, EC_P224_LEN);
+ }
+
+ memset(obuf, 0, EC_P224_LEN);
+ rv = b->combine((const uint8_t *)testvecs[i].xA, obuf);
+ if (testvecs[i].what == bad_x) {
+ tt_int_op(rv, !=, 0);
+ tt_mem_op(obuf, !=, testvecs[i].z, EC_P224_LEN);
+ } else {
+ tt_int_op(rv, ==, 0);
+ if (testvecs[i].what == bad_z) {
+ tt_mem_op(obuf, !=, testvecs[i].z, EC_P224_LEN);
+ } else {
+ tt_mem_op(obuf, ==, testvecs[i].z, EC_P224_LEN);
+ }
+ }
+
+ delete a;
+ delete b;
+ }
+
+ end:;
+}
+
/* HKDF-SHA256 test vectors from http://tools.ietf.org/html/rfc5869 */
static void
test_crypt_hkdf(void *)
@@ -988,6 +1419,8 @@ struct testcase_t crypt_tests[] = {
T(aesgcm_enc),
T(aesgcm_good_dec),
T(aesgcm_bad_dec),
+ T(ecdh_p224_good),
+ T(ecdh_p224_bad),
T(hkdf),
T(rng),
END_OF_TESTCASES
1
0

[stegotorus/master] changed pdfwrap and pdfunwrap to use zlib to encode data in stream objects
by zwol@torproject.org 20 Jul '12
by zwol@torproject.org 20 Jul '12
20 Jul '12
commit e1024c2d7e14fad7f832454326a4a9ea94e1e736
Author: Steven Cheung <steven.cheung(a)sri.com>
Date: Sun Jun 10 06:49:45 2012 -0700
changed pdfwrap and pdfunwrap to use zlib to encode data in stream objects
---
src/steg/pdfSteg.cc | 180 ++++++++++++++++++++++++++++++++------------------
1 files changed, 115 insertions(+), 65 deletions(-)
diff --git a/src/steg/pdfSteg.cc b/src/steg/pdfSteg.cc
index 658c319..2f67051 100644
--- a/src/steg/pdfSteg.cc
+++ b/src/steg/pdfSteg.cc
@@ -7,19 +7,21 @@
#include "connections.h"
#include "payloads.h"
#include <event2/buffer.h>
+#include "compression.h"
/* pdfSteg: A PDF-based steganography module */
#define PDF_DELIMITER '?'
#define PDF_DELIMITER2 '.'
-#define STREAM_BEGIN ">>stream"
-#define STREAM_BEGIN_SIZE 8
+#define STREAM_BEGIN "stream"
+#define STREAM_BEGIN_SIZE 6
#define STREAM_END "endstream"
#define STREAM_END_SIZE 9
#define DEBUG
+
/*
* pdf_add_delimiter processes the input buffer (inbuf) of length
* inbuflen, copies it to output buffer (outbuf) of size outbufsize,
@@ -158,6 +160,38 @@ pdf_remove_delimiter(const char *inbuf, size_t inbuflen,
return cnt;
}
+
+/*
+ * strInBinaryRewind looks for char array pattern of length patternLen
+ * in a char array blob of length blobLen in the *reverse* direction
+ *
+ * return a pointer for the first occurrence of pattern in blob,
+ * starting from the end of blob, if found; otherwise, return NULL
+ *
+ */
+char *
+strInBinaryRewind (const char *pattern, unsigned int patternLen,
+ const char *blob, unsigned int blobLen) {
+ int found = 0;
+ char *cp;
+
+ if (patternLen < 1 || blobLen < 1) return 0;
+ cp = (char *) blob + blobLen - 1;
+ while (cp >= blob) {
+ if (cp - (patternLen-1) < blob) break;
+ if (*cp == pattern[patternLen-1]) {
+ if (memcmp(cp-(patternLen-1), pattern, patternLen-1) == 0) {
+ found = 1;
+ break;
+ }
+ }
+ cp--;
+ }
+ if (found) return (cp-(patternLen-1));
+ else return NULL;
+}
+
+
/*
* pdf_wrap embeds data of length dlen inside the stream objects of the PDF
* document (length plen) that appears in the body of a HTTP msg, and
@@ -172,29 +206,30 @@ pdf_wrap(const char *data, size_t dlen,
const char *pdfTemplate, size_t plen,
char *outbuf, size_t outbufsize)
{
- char data2[dlen*2+2];
- const char *tp, *dp, *plimit;
- char *op, *streamStart, *streamEnd;
- size_t data2len, cnt, size, size2;
- ssize_t rv;
+ int data2size = 2*dlen+10;
+ // see rfc 1950 for zlib format, in addition to compressed data, we have
+ // 2-byte compression method and flags +
+ // 4-byte dict ID +
+ // 4-byte ADLER32 checksum
+ char data2[data2size];
+ const char *tp, *plimit;
+ char *op, *streamStart, *streamEnd, *filterStart;
+ size_t data2len, size;
+ int np;
if (dlen > SIZE_T_CEILING || plen > SIZE_T_CEILING ||
outbufsize > SIZE_T_CEILING)
return -1;
- // assumption: pdf_wrap is length-preserving
- if (outbufsize < plen) return -1;
-
- rv = pdf_add_delimiter(data, dlen, data2, HTTP_MSG_BUF_SIZE,
- PDF_DELIMITER, PDF_DELIMITER2);
- if (rv < 1)
+ data2len = compress((const uint8_t *)data, dlen,
+ (uint8_t *)data2, data2size, c_format_zlib);
+ if ((int)data2len < 0) {
+ log_warn("compress failed and returned %lu", data2len);
return -1;
- data2len = rv;
+ }
op = outbuf; // current pointer for output buffer
tp = pdfTemplate; // current pointer for http msg template
- dp = data2; // current pointer for data2
- cnt = 0; // number of data char encoded
plimit = pdfTemplate+plen;
while (tp < plimit) {
@@ -205,43 +240,47 @@ pdf_wrap(const char *data, size_t dlen,
return -1;
}
- // copy everything between tp and "stream" (inclusive) to outbuf
- size = streamStart - tp + STREAM_BEGIN_SIZE;
- memcpy(op, tp, size);
- op += size;
- tp = streamStart + STREAM_BEGIN_SIZE;
-
streamEnd = strInBinary(STREAM_END, STREAM_END_SIZE, tp, plimit-tp);
if (streamEnd == NULL) {
log_warn("Cannot find endstream in pdf");
return -1;
}
- // count the number of usable char between tp and streamEnd
- size = streamEnd-tp;
-
- // encoding data in the stream obj
- if (size > 0) {
- size2 = data2len - cnt;
- if (size < size2) {
- memcpy(op, dp, size);
- op += size; tp += size; dp += size;
- memcpy(op, tp, STREAM_END_SIZE);
- op += STREAM_END_SIZE; tp += STREAM_END_SIZE;
- cnt += size;
- } else { // done encoding data
- memcpy(op, dp, size2);
- op += size2; tp += size2; dp += size2;
- cnt += size2;
- break;
- }
- log_debug("Encoded %lu bytes in pdf", (unsigned long)size);
- } else { // empty stream
- memcpy(op, tp, STREAM_END_SIZE);
- op += STREAM_END_SIZE; tp += STREAM_END_SIZE;
+ filterStart = strInBinaryRewind(" obj", 4, tp, streamStart-tp);
+ if (filterStart == NULL) {
+ log_warn("Cannot find obj\n");
+ return -1;
+ } else {
+ // copy everything between tp and up and and including "obj" to outbuf
+ size = filterStart - tp + 4;
+ memcpy(op, tp, size);
+ op[size] = 0;
+ op += size;
+
+ // write meta-data for stream object
+ np = sprintf(op, " <<\n/Length %d\n/Filter /FlateDecode\n>>\nstream\n", (int)data2len);
+ if (np < 0) {
+ log_warn("sprintf failed\n");
+ return -1;
+ }
+ op += np;
+
+ // copy compressed data to outbuf
+ memcpy(op, data2, data2len);
+ op += data2len;
+
+ // write endstream to outbuf
+ np = sprintf(op, "\nendstream");
+ if (np < 0) {
+ log_warn("sprintf failed\n");
+ return -1;
+ }
+ op += np;
}
- if (cnt >= data2len) break; // this shouldn't happen ...
+ // done with encoding data
+ tp = streamEnd+STREAM_END_SIZE;
+ break;
}
// copy the rest of pdfTemplate to outbuf
@@ -261,9 +300,11 @@ pdf_unwrap(const char *data, size_t dlen,
char *outbuf, size_t outbufsize)
{
const char *dp, *dlimit;
- char *op, *streamStart, *streamEnd, *olimit;
+ char *op, *streamStart, *streamEnd;
size_t cnt, size, size2;
- bool endFlag, escape = false;
+
+ int streamObjStartSkip=0;
+ int streamObjEndSkip=0;
if (dlen > SIZE_T_CEILING || outbufsize > SIZE_T_CEILING)
return -1;
@@ -272,8 +313,8 @@ pdf_unwrap(const char *data, size_t dlen,
op = outbuf; // current pointer for outbuf
cnt = 0; // number of char decoded
dlimit = data+dlen;
- olimit = outbuf+outbufsize;
+
while (dp < dlimit) {
// find the next stream obj
streamStart = strInBinary(STREAM_BEGIN, STREAM_BEGIN_SIZE, dp, dlimit-dp);
@@ -283,31 +324,40 @@ pdf_unwrap(const char *data, size_t dlen,
}
dp = streamStart + STREAM_BEGIN_SIZE;
+
+ // streamObjStartSkip = size of end-of-line (EOL) char(s) after ">>stream"
+ if ( *dp == '\r' && *(dp+1) == '\n' ) { // Windows-style EOL
+ streamObjStartSkip = 2;
+ } else if ( *dp == '\n' ) { // Unix-style EOL
+ streamObjStartSkip = 1;
+ }
+
+ dp = dp + streamObjStartSkip;
+
streamEnd = strInBinary(STREAM_END, STREAM_END_SIZE, dp, dlimit-dp);
if (streamEnd == NULL) {
log_warn("Cannot find endstream in pdf");
return -1;
}
- // count the number of usable char between tp and streamEnd
- size = streamEnd-dp;
+ // streamObjEndSkip = size of end-of-line (EOL) char(s) at the end of stream obj
+ if (*(streamEnd-2) == '\r' && *(streamEnd-1) == '\n') {
+ streamObjEndSkip = 2;
+ } else if (*(streamEnd-1) == '\n') {
+ streamObjEndSkip = 1;
+ }
- if (size > 0) {
- ssize_t rv = pdf_remove_delimiter(dp, size, op, olimit-op, PDF_DELIMITER,
- &endFlag, &escape);
- if (rv < 0)
- return -1;
+ // compute the size of stream obj payload
+ size = (streamEnd-streamObjEndSkip) - dp;
- size2 = rv;
- cnt += size2;
- if (endFlag) { // Done decoding
- break;
- } else { // Continue decoding
- op += size2;
- dp = streamEnd + STREAM_END_SIZE;
- }
- } else { // empty stream obj
- dp = streamEnd + STREAM_END_SIZE;
+ size2 = decompress((const uint8_t *)dp, size, (uint8_t *)op, outbufsize);
+ if ((int)size2 < 0) {
+ log_warn("decompress failed; size2 = %d\n", (int)size2);
+ return -1;
+ } else {
+ op += size2;
+ cnt = size2;
+ break; // done decoding
}
}
1
0

20 Jul '12
commit cd8e68731a8a4a536ba28758e0538d53bb6afc94
Author: Steven Cheung <steven.cheung(a)sri.com>
Date: Sun Jun 10 11:26:36 2012 -0700
fixed test data for test_pdf_wrap_unwrap()
---
src/test/unittest_pdfsteg.cc | 4 +---
1 files changed, 1 insertions(+), 3 deletions(-)
diff --git a/src/test/unittest_pdfsteg.cc b/src/test/unittest_pdfsteg.cc
index 36a1167..7333fbc 100644
--- a/src/test/unittest_pdfsteg.cc
+++ b/src/test/unittest_pdfsteg.cc
@@ -36,8 +36,7 @@ static void
test_pdf_wrap_unwrap(void *)
{
const char *pdf =
- "[PDFHDR][STUFFS1]>>streamABCDEFGHIJYYendstream"
- "[STUFFS2]>>streamABCDEFGHIJYYendstream[STUFF3][PDFTRAILER]";
+ "[PDFHDR][STUFFS1]1 0 obj <<\n/Length 12\n/Filter /FlateDecode\n>>\nstream\nABCDEFGHIJYY>>endstream\n[STUFF2][PDFTRAILER]";
const char *const tests[] = {
"12345",
@@ -61,7 +60,6 @@ test_pdf_wrap_unwrap(void *)
out, sizeof out);
tt_int_op(rv, >, 0);
r1 = rv;
- tt_int_op(r1, ==, strlen(pdf));
rv = pdf_unwrap(out, r1, orig, sizeof orig);
tt_int_op(rv, >, 0);
1
0

[stegotorus/master] fixing compiler errors when on 32-bit machine and explicit casts for log messages are needed
by zwol@torproject.org 20 Jul '12
by zwol@torproject.org 20 Jul '12
20 Jul '12
commit a0b21894457ef2ea4a3f98d7a1bd4a54861f2967
Author: Linda Briesemeister <linda.briesemeister(a)sri.com>
Date: Mon Jun 11 12:29:34 2012 -0500
fixing compiler errors when on 32-bit machine and explicit casts for log messages are needed
---
src/protocol/null.cc | 4 ++--
src/steg/embed.cc | 8 ++++----
src/steg/http.cc | 2 +-
src/steg/pdfSteg.cc | 2 +-
4 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/src/protocol/null.cc b/src/protocol/null.cc
index d8fe98b..c5ec831 100644
--- a/src/protocol/null.cc
+++ b/src/protocol/null.cc
@@ -196,7 +196,7 @@ int
null_circuit_t::send()
{
log_debug(this, "sending %lu bytes",
- evbuffer_get_length(bufferevent_get_input(this->up_buffer)));
+ (unsigned long)evbuffer_get_length(bufferevent_get_input(this->up_buffer)));
return evbuffer_add_buffer(this->downstream->outbound(),
bufferevent_get_input(this->up_buffer));
@@ -268,7 +268,7 @@ int
null_conn_t::recv()
{
log_assert(this->upstream);
- log_debug(this, "receiving %lu bytes", evbuffer_get_length(this->inbound()));
+ log_debug(this, "receiving %lu bytes", (unsigned long)evbuffer_get_length(this->inbound()));
return evbuffer_add_buffer(bufferevent_get_output(this->upstream->up_buffer),
this->inbound());
}
diff --git a/src/steg/embed.cc b/src/steg/embed.cc
index 327c7c4..463148d 100644
--- a/src/steg/embed.cc
+++ b/src/steg/embed.cc
@@ -82,15 +82,15 @@ embed_steg_config_t::embed_steg_config_t(config_t *cfg)
for (vector<trace_t>::iterator p = traces.begin(); p != traces.end(); ++p) {
int num_pkt;
if (fscanf(trace_file, "%d", &num_pkt) < 1)
- log_abort("couldn't read number of packets in trace %ld",
- p - traces.begin());
+ log_abort("couldn't read number of packets in trace %li",
+ (long)(p - traces.begin()));
p->pkt_sizes.resize(num_pkt);
p->pkt_times.resize(num_pkt);
for (int i = 0; i < num_pkt; i++)
if (fscanf(trace_file, "%hd %d", &p->pkt_sizes[i], &p->pkt_times[i]) < 1)
- log_abort("couldn't read trace entry %ld/%d",
- p - traces.begin(), i);
+ log_abort("couldn't read trace entry %li/%d",
+ (long)(p - traces.begin()), i);
}
log_debug("read %d traces", num_traces);
diff --git a/src/steg/http.cc b/src/steg/http.cc
index cfeaf93..171bdcb 100644
--- a/src/steg/http.cc
+++ b/src/steg/http.cc
@@ -329,7 +329,7 @@ http_client_cookie_transmit (http_steg_t *s, struct evbuffer *source,
return -1;
}
log_debug(conn, "cookie input %ld encoded %d final %d",
- sbuflen, len, cookie_len);
+ (long)sbuflen, len, cookie_len);
log_debug(conn, "cookie encoded: %s", data2);
log_debug(conn, "cookie final: %s", cookiebuf);
diff --git a/src/steg/pdfSteg.cc b/src/steg/pdfSteg.cc
index 2f67051..f76da99 100644
--- a/src/steg/pdfSteg.cc
+++ b/src/steg/pdfSteg.cc
@@ -224,7 +224,7 @@ pdf_wrap(const char *data, size_t dlen,
data2len = compress((const uint8_t *)data, dlen,
(uint8_t *)data2, data2size, c_format_zlib);
if ((int)data2len < 0) {
- log_warn("compress failed and returned %lu", data2len);
+ log_warn("compress failed and returned %lu", (unsigned long)data2len);
return -1;
}
1
0
commit e3a7a80ff7666f836b921c343ef8bf28ffe886f6
Author: Zack Weinberg <zackw(a)cmu.edu>
Date: Tue Jun 12 17:48:56 2012 -0400
Accept and ignore --server-key
---
src/protocol/chop.cc | 7 +++++++
1 files changed, 7 insertions(+), 0 deletions(-)
diff --git a/src/protocol/chop.cc b/src/protocol/chop.cc
index f547303..67ed2b3 100644
--- a/src/protocol/chop.cc
+++ b/src/protocol/chop.cc
@@ -428,6 +428,13 @@ chop_config_t::init(int n_options, const char *const *options)
} else
goto usage;
+ // if in client mode, accept and ignore --server-key=
+ if (mode != LSN_SIMPLE_SERVER &&
+ !strncmp(options[1], "--server-key=", 13)) {
+ options++;
+ n_options--;
+ }
+
up_address = resolve_address_port(options[1], 1, listen_up, defport);
if (!up_address) {
log_warn("chop: invalid up address: %s", options[1]);
1
0
commit 4f3ccfc31308ea4703300cbbfeb7a463eb0d9dfb
Author: Zack Weinberg <zackw(a)cmu.edu>
Date: Wed Jun 13 17:29:17 2012 -0400
Two subtle chopper bugfixes:
* Don't kill the connection just because not enough data has arrived
yet for us to desteg the handshake message.
* If there's no data to send, but there is an EOF to send, and none
of the existing connections can send, we need to open a new connection.
---
src/protocol/chop.cc | 23 +++++++++++++++--------
1 files changed, 15 insertions(+), 8 deletions(-)
diff --git a/src/protocol/chop.cc b/src/protocol/chop.cc
index 20ba563..a30dd94 100644
--- a/src/protocol/chop.cc
+++ b/src/protocol/chop.cc
@@ -705,14 +705,16 @@ chop_circuit_t::send()
dead_cycles++;
log_debug(this, "%u dead cycles", dead_cycles);
- // If there was real data and we didn't make any progress on it,
- // or if there are no downstream connections at all, and we're the
- // client, try opening new connections. If we're the server, we
- // have to just twiddle our thumbs and hope the client does that.
- // Note that due to the sliding window of receive blocks, there is
- // a hard upper limit of 127 outstanding connections (that is,
- // half the receive window).
- if ((avail0 > 0 && downstreams.size() < 127) || downstreams.empty()) {
+ // If there was real data or an EOF to send, and we didn't make
+ // any progress on it, or if there are no downstream connections
+ // at all, and we're the client, try opening new connections. If
+ // we're the server, we have to just twiddle our thumbs and hope
+ // the client does that. Note that due to the sliding window of
+ // receive blocks, there is a hard upper limit of 127 outstanding
+ // connections (that is, half the receive window).
+ if (downstreams.empty() ||
+ (downstreams.size() < 127 &&
+ (avail0 > 0 || (upstream_eof && !sent_fin)))) {
if (config->mode != LSN_SIMPLE_SERVER)
circuit_reopen_downstreams(this);
else
@@ -1213,6 +1215,11 @@ chop_conn_t::recv()
if (steg->receive(recv_pending))
return -1;
+ // If that succeeded but did not copy anything into recv_pending,
+ // wait for more data.
+ if (evbuffer_get_length(recv_pending) == 0)
+ return 0;
+
if (!upstream) {
// Try to receive a handshake.
if (recv_handshake())
1
0
commit 9526b5db6ed5281dade13e4914087f49d7d16d2a
Author: Zack Weinberg <zackw(a)cmu.edu>
Date: Wed Jun 13 11:24:12 2012 -0400
Minor code-formatting fixes.
---
src/protocol/null.cc | 3 ++-
src/steg/embed.cc | 4 ++--
2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/src/protocol/null.cc b/src/protocol/null.cc
index c5ec831..ca6cc74 100644
--- a/src/protocol/null.cc
+++ b/src/protocol/null.cc
@@ -268,7 +268,8 @@ int
null_conn_t::recv()
{
log_assert(this->upstream);
- log_debug(this, "receiving %lu bytes", (unsigned long)evbuffer_get_length(this->inbound()));
+ log_debug(this, "receiving %lu bytes",
+ (unsigned long)evbuffer_get_length(this->inbound()));
return evbuffer_add_buffer(bufferevent_get_output(this->upstream->up_buffer),
this->inbound());
}
diff --git a/src/steg/embed.cc b/src/steg/embed.cc
index 463148d..4734edb 100644
--- a/src/steg/embed.cc
+++ b/src/steg/embed.cc
@@ -82,14 +82,14 @@ embed_steg_config_t::embed_steg_config_t(config_t *cfg)
for (vector<trace_t>::iterator p = traces.begin(); p != traces.end(); ++p) {
int num_pkt;
if (fscanf(trace_file, "%d", &num_pkt) < 1)
- log_abort("couldn't read number of packets in trace %li",
+ log_abort("couldn't read number of packets in trace %ld",
(long)(p - traces.begin()));
p->pkt_sizes.resize(num_pkt);
p->pkt_times.resize(num_pkt);
for (int i = 0; i < num_pkt; i++)
if (fscanf(trace_file, "%hd %d", &p->pkt_sizes[i], &p->pkt_times[i]) < 1)
- log_abort("couldn't read trace entry %li/%d",
+ log_abort("couldn't read trace entry %ld/%d",
(long)(p - traces.begin()), i);
}
1
0

[stegotorus/master] Add support for daemonizing and writing a pid file.
by zwol@torproject.org 20 Jul '12
by zwol@torproject.org 20 Jul '12
20 Jul '12
commit 337fadeede2d4418ae569173cdf19ed2465444e3
Author: Zack Weinberg <zackw(a)cmu.edu>
Date: Wed Jun 13 22:09:58 2012 -0400
Add support for daemonizing and writing a pid file.
---
src/audit-globals.sh | 2 +
src/main.cc | 53 +++++++++++++++++++-----
src/subprocess-unix.cc | 105 ++++++++++++++++++++++++++++++++++++++++++++++++
src/subprocess.h | 39 ++++++++++++++++++
4 files changed, 188 insertions(+), 11 deletions(-)
diff --git a/src/audit-globals.sh b/src/audit-globals.sh
index 27cab3a..7c28f21 100644
--- a/src/audit-globals.sh
+++ b/src/audit-globals.sh
@@ -39,7 +39,9 @@ sed '
/^crypt crypto_initialized$/d
/^crypt crypto_errs_initialized$/d
/^main allow_kq$/d
+ /^main daemon_mode$/d
/^main handle_signal_cb(int, short, void\*)::got_sigint$/d
+ /^main pidfile_name$/d
/^main registration_helper$/d
/^main the_event_base$/d
/^network listeners$/d
diff --git a/src/main.cc b/src/main.cc
index dd69e68..42687e1 100644
--- a/src/main.cc
+++ b/src/main.cc
@@ -37,6 +37,8 @@ using std::string;
static struct event_base *the_event_base;
static bool allow_kq = false;
+static bool daemon_mode = false;
+static string pidfile_name;
static string registration_helper;
/**
@@ -234,7 +236,11 @@ usage(void)
"--log-min-severity=warn|info|debug ~ set minimum logging severity\n"
"--no-log ~ disable logging\n"
"--timestamp-logs ~ add timestamps to all log messages\n"
- "--allow-kqueue ~ allow use of kqueue(2) (may be buggy)\n");
+ "--allow-kqueue ~ allow use of kqueue(2) (may be buggy)\n"
+ "--registration-helper=<helper> ~ use <helper> to register with "
+ "a relay database\n"
+ "--pid-file=<file> ~ write process ID to <file> after startup\n"
+ "--daemon ~ run as a daemon");
exit(1);
}
@@ -256,7 +262,8 @@ handle_generic_args(const char *const *argv)
bool logsev_set = false;
bool allow_kq_set = false;
bool timestamps_set = false;
- bool registration_helper_set=false;
+ bool registration_helper_set = false;
+ bool pidfile_set = false;
int i = 1;
while (argv[i] &&
@@ -278,21 +285,19 @@ handle_generic_args(const char *const *argv)
fprintf(stderr, "you've already set a min. log severity!\n");
exit(1);
}
- if (log_set_min_severity((char *)argv[i]+19) < 0) {
- fprintf(stderr, "error at setting logging severity");
+ if (log_set_min_severity(argv[i]+19) < 0) {
+ fprintf(stderr, "invalid min. log severity '%s'", argv[i]+19);
exit(1);
}
logsev_set = true;
} else if (!strcmp(argv[i], "--no-log")) {
- if (logsev_set) {
- fprintf(stderr, "you've already set a min. log severity!\n");
+ if (logsev_set || logmethod_set) {
+ fprintf(stderr, "can't ask for both some logs and no logs!\n");
exit(1);
}
- if (log_set_method(LOG_METHOD_NULL, NULL) < 0) {
- fprintf(stderr, "error at setting logging severity.\n");
- exit(1);
- }
- logsev_set = true;
+ log_set_method(LOG_METHOD_NULL, NULL);
+ logsev_set = true;
+ logmethod_set = true;
} else if (!strcmp(argv[i], "--timestamp-logs")) {
if (timestamps_set) {
fprintf(stderr, "you've already asked for timestamps!\n");
@@ -314,6 +319,19 @@ handle_generic_args(const char *const *argv)
}
registration_helper = string(argv[i]+22);
registration_helper_set = true;
+ } else if (!strncmp(argv[i], "--pid-file=", 11)) {
+ if (pidfile_set) {
+ fprintf(stderr, "you've already set a pid file!\n");
+ exit(1);
+ }
+ pidfile_name = string(argv[i]+11);
+ pidfile_set = true;
+ } else if (!strcmp(argv[i], "--daemon")) {
+ if (daemon_mode) {
+ fprintf(stderr, "you've already requested daemon mode!\n");
+ exit(1);
+ }
+ daemon_mode = true;
} else {
fprintf(stderr, "unrecognizable argument '%s'\n", argv[i]);
exit(1);
@@ -321,6 +339,12 @@ handle_generic_args(const char *const *argv)
i++;
}
+ /* Cross-option consistency checks. */
+ if (daemon_mode && !logmethod_set) {
+ log_warn("cannot log to stderr in daemon mode");
+ log_set_method(LOG_METHOD_NULL, NULL);
+ }
+
return i;
}
@@ -380,6 +404,13 @@ main(int, const char *const *argv)
log_assert(configs.size() > 0);
/* Configurations have been established; proceed with initialization. */
+ if (daemon_mode)
+ daemonize();
+
+ pidfile pf(pidfile_name);
+ if (!pf)
+ log_warn("failed to create pid-file '%s': %s", pf.pathname().c_str(),
+ pf.errmsg());
init_crypto();
diff --git a/src/subprocess-unix.cc b/src/subprocess-unix.cc
index 7e075d9..573b167 100644
--- a/src/subprocess-unix.cc
+++ b/src/subprocess-unix.cc
@@ -13,6 +13,7 @@
#include "subprocess.h"
#include <map>
+#include <sstream>
#include <sys/stat.h>
#include <dirent.h>
@@ -465,3 +466,107 @@ get_environ(const char *exclude)
return result;
}
+
+void
+daemonize()
+{
+ if (getppid() == 1) // already a daemon
+ return;
+
+ // Close standard I/O file descriptors and reopen them on /dev/null.
+ // We do this before forking (a) to avoid any possibility of
+ // double-flushed stdio buffers, and (b) so we can exit
+ // unsuccessfully in the unlikely event of a failure.
+ fflush(NULL); // flush all open stdio buffers
+
+ close(0);
+ if (open("/dev/null", O_RDONLY) != 0)
+ log_abort("/dev/null: %s", strerror(errno));
+
+ close(1);
+ if (open("/dev/null", O_WRONLY) != 1)
+ log_abort("/dev/null: %s", strerror(errno));
+
+ // N.B. log_abort might be writing somewhere other than stderr.
+ // In fact, we rather hope it is, 'cos otherwise all logs from
+ // the child are gonna go to the bit bucket.
+ close(2);
+ if (open("/dev/null", O_WRONLY) != 2)
+ log_abort("/dev/null: %s", strerror(errno));
+
+ pid_t pid = fork();
+ if (pid < 0)
+ log_abort("fork failed: %s", strerror(errno));
+
+ if (pid > 0) // Parent
+ // The use of _exit instead of exit here is deliberate.
+ // It's the process that carries on from this function that
+ // should do atexit cleanups (eventually).
+ _exit(0);
+
+ // Become a session leader, and then fork one more time and exit in
+ // the parent. (This puts the process that will actually be the
+ // daemon in an orphaned process group. On some systems, this is
+ // necessary to ensure that the daemon can never acquire a controlling
+ // terminal again. XXX On some systems will the grandchild receive
+ // an unwanted, probably-fatal SIGHUP when its session leader exits?)
+ setsid();
+ if (fork())
+ _exit(0);
+
+ // For the moment we do not chdir anywhere, because the HTTP steg expects
+ // to find its traces relative to the cwd. FIXME.
+}
+
+pidfile::pidfile(std::string const& p)
+ : path(p), errcode(0)
+{
+ if (path.empty())
+ return;
+
+ std::ostringstream ss;
+ ss << getpid() << '\n';
+ const char *b = ss.str().c_str();
+ size_t n = ss.str().size();
+
+ int f = open(path.c_str(), O_WRONLY|O_CREAT|O_EXCL, 0666);
+ if (f == -1) {
+ errcode = errno;
+ return;
+ }
+
+ do {
+ ssize_t r = write(f, b, n);
+ if (r < 0) {
+ errcode = errno;
+ close(f);
+ remove(path.c_str());
+ return;
+ }
+ n -= r;
+ b += r;
+ } while (n > 0);
+
+ // Sadly, close() can fail, and in this case it actually matters.
+ if (close(f)) {
+ errcode = errno;
+ remove(path.c_str());
+ }
+}
+
+pidfile::~pidfile()
+{
+ if (!errcode && !path.empty())
+ remove(path.c_str());
+}
+
+pidfile::operator bool() const
+{
+ return !errcode;
+}
+
+const char *
+pidfile::errmsg() const
+{
+ return errcode ? strerror(errcode) : 0;
+}
diff --git a/src/subprocess.h b/src/subprocess.h
index 95fe848..73eeece 100644
--- a/src/subprocess.h
+++ b/src/subprocess.h
@@ -65,4 +65,43 @@ struct subprocess
// begins with those characters will be excluded from the result.
extern std::vector<std::string> get_environ(const char *exclude = 0);
+// These are in here because they involve process management 'under the hood',
+// and because (like other process management) their Unix and Windows
+// implementations have to be radically different.
+
+// Turn into a daemon; detach from the parent process and any
+// controlling terminal. Closes standard I/O streams and reopens them
+// to /dev/null. If this returns, it succeeded.
+extern void daemonize();
+
+// Instantiating this class causes a file to be created at the specified
+// pathname, which contains the decimal process ID of the current process.
+// On destruction, the file is deleted.
+//
+// If you're going to call daemonize(), you need to do it _before_ creating
+// one of these, because daemonize() changes the process ID.
+
+class pidfile
+{
+public:
+ pidfile(const std::string& p);
+ ~pidfile();
+
+ const std::string& pathname() const { return path; }
+
+ // True if pid-file creation succeeded.
+ operator bool() const;
+
+ // If pid-file creation did *not* succeed, returns the underlying system
+ // error message. You should combine that with the pathname and some
+ // text to the effect that this is a process ID file for the actual error
+ // message printed to the user.
+ // If pid-file creation *did* succeed, returns NULL.
+ const char *errmsg() const;
+
+private:
+ std::string path;
+ int errcode;
+};
+
#endif
1
0