[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