[tor-commits] [stegotorus/master] Add ECDH implementation to crypt.cc/h (no tests yet)

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


commit 0775073adf3b2b13748af23516080cdfc3a30ee2
Author: Zack Weinberg <zackw at cmu.edu>
Date:   Tue Jun 5 16:22:10 2012 -0700

    Add ECDH implementation to crypt.cc/h (no tests yet)
---
 src/crypt.cc |  115 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/crypt.h  |   34 +++++++++++++++++
 2 files changed, 149 insertions(+), 0 deletions(-)

diff --git a/src/crypt.cc b/src/crypt.cc
index d4af5e0..8c7a6b9 100644
--- a/src/crypt.cc
+++ b/src/crypt.cc
@@ -8,8 +8,10 @@
 
 #include <openssl/engine.h>
 #include <openssl/err.h>
+#include <openssl/ecdh.h>
 #include <openssl/evp.h>
 #include <openssl/hmac.h>
+#include <openssl/objects.h>
 
 static bool crypto_initialized = false;
 static bool crypto_errs_initialized = false;
@@ -410,6 +412,107 @@ gcm_decryptor_impl::decrypt(uint8_t *out, const uint8_t *in, size_t inlen,
   return 0;
 }
 
+// We use the slightly lower-level EC_* / ECDH_* routines for
+// ecdh_message, instead of the EVP_PKEY_* routines, because we don't
+// need algorithmic agility, and it means we only have to puzzle out
+// one layer of completely undocumented APIs instead of two.
+namespace {
+  struct ecdh_message_impl : ecdh_message
+  {
+    EC_KEY *priv;
+    BN_CTX *ctx;
+    ecdh_message_impl(); // generate keypair from randomness
+
+    virtual ~ecdh_message_impl();
+    virtual void encode(uint8_t *xcoord_out) const;
+    virtual int combine(const uint8_t *other, uint8_t *secret_out) const;
+  };
+}
+
+ecdh_message_impl::ecdh_message_impl()
+  : priv(EC_KEY_new_by_curve_name(NID_secp224r1)),
+    ctx(BN_CTX_new())
+{
+  if (!priv || !ctx)
+    log_crypto_abort("ecdh_message::allocate data");
+  if (!EC_KEY_generate_key(priv))
+    log_crypto_abort("ecdh_message::generate priv");
+}
+
+/* static */ ecdh_message *
+ecdh_message::generate()
+{
+  REQUIRE_INIT_CRYPTO();
+  return new ecdh_message_impl();
+}
+
+ecdh_message::~ecdh_message() {}
+ecdh_message_impl::~ecdh_message_impl()
+{
+  EC_KEY_free(priv);
+  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);
+  if (!pub || !grp)
+    log_crypto_abort("ecdh_message_encode::extract pubkey");
+
+  BIGNUM *x = BN_new();
+  if (!x)
+    log_crypto_abort("ecdh_message_encode::allocate data");
+
+  if (!EC_POINT_get_affine_coordinates_GFp(grp, pub, x, 0, ctx))
+    log_crypto_abort("ecdh_message_encode::extract x-coordinate");
+
+  size_t sbytes = BN_num_bytes(x);
+  log_assert(sbytes <= EC_P224_LEN);
+  if (sbytes < EC_P224_LEN) {
+    memset(xcoord_out, 0, EC_P224_LEN - sbytes);
+    sbytes += EC_P224_LEN - sbytes;
+  }
+  size_t wbytes = BN_bn2bin(x, xcoord_out);
+  log_assert(sbytes == wbytes);
+
+  BN_free(x);
+}
+
+int
+ecdh_message_impl::combine(const uint8_t *xcoord_other,
+                           uint8_t *secret_out) const
+{
+  const EC_GROUP *grp = EC_KEY_get0_group(priv);
+  EC_POINT *pub = EC_POINT_new(grp);
+  if (!grp || !pub)
+    log_crypto_abort("ecdh_message_combine::allocate data");
+
+  int rv = -1;
+  BIGNUM *x = BN_bin2bn(xcoord_other, EC_P224_LEN, 0);
+  if (!x) {
+    log_crypto_warn("ecdh_message_combine::decode their x-coordinate");
+    goto done;
+  }
+
+  if (!EC_POINT_set_compressed_coordinates_GFp(grp, pub, x, 0, ctx)) {
+    log_crypto_warn("ecdh_message_combine::recover their point");
+    goto done;
+  }
+
+  if (!ECDH_compute_key(secret_out, EC_P224_LEN, pub, priv, 0)) {
+    log_crypto_warn("ecdh_message_combine::compute shared secret");
+    goto done;
+  }
+
+    rv = 0;
+ done:
+  BN_free(x);
+  EC_POINT_free(pub);
+  return rv;
+}
+
 namespace {
   struct key_generator_impl : key_generator
   {
@@ -462,6 +565,18 @@ key_generator::from_random_secret(const uint8_t *key,  size_t klen,
 }
 
 key_generator *
+key_generator::from_ecdh(const ecdh_message *mine, const uint8_t *theirs,
+                         const uint8_t *salt, size_t slen,
+                         const uint8_t *ctxt, size_t clen)
+{
+  MemBlock ss(EC_P224_LEN);
+  if (mine->combine(theirs, ss))
+    return 0;
+
+  return from_random_secret(ss, EC_P224_LEN, salt, slen, ctxt, clen);
+}
+
+key_generator *
 key_generator::from_passphrase(const uint8_t *phra, size_t plen,
                                const uint8_t *salt, size_t slen,
                                const uint8_t *ctxt, size_t clen)
diff --git a/src/crypt.h b/src/crypt.h
index 4da3309..35e3e37 100644
--- a/src/crypt.h
+++ b/src/crypt.h
@@ -8,6 +8,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;
 
 /**
  * Initialize cryptography library.  Must be called before anything that
@@ -140,6 +141,31 @@ private:
   gcm_decryptor& operator=(const gcm_decryptor&) DELETE_METHOD;
 };
 
+/** Encapsulation of an elliptic curve Diffie-Hellman message
+    (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();
+
+  /** 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. */
+  virtual void encode(uint8_t *xcoord_out) const = 0;
+
+  /** Combine our message with the wire-format message sent by our
+      peer, and produce the raw ECDH shared secret.  |xcoord_other|
+      must point to EC_P224_LEN bytes of data, and |secret_out| must
+      point to the same quantity of buffer space.  Normally you should
+      use key_generator::from_ecdh instead of calling this
+      directly.  */
+  virtual int combine(const uint8_t *xcoord_other, uint8_t *secret_out)
+    const = 0;
+};
+
 /** 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
@@ -166,6 +192,14 @@ struct key_generator
                                         const uint8_t *salt, size_t slen,
                                         const uint8_t *ctxt, size_t clen);
 
+  /** Construct a key generator from two (elliptic curve) Diffie-Hellman
+      messages. The salt and context arguments are the same as for
+      from_random_secret. */
+  static key_generator *from_ecdh(const ecdh_message *mine,
+                                  const uint8_t *theirs,
+                                  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





More information about the tor-commits mailing list