[tor-commits] [stegotorus/master] Import MKEM implementation from moeller-ref and write out the crypt.h interface for it. (Significant pieces of implementation still to do.)

zwol at torproject.org zwol at torproject.org
Fri Jul 20 23:17:08 UTC 2012


commit 818813ea2049df733f5e9a9015a1b82dc8ee2dda
Author: Zack Weinberg <zackw at 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





More information about the tor-commits mailing list