[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