ConvertUTF.c
crypto.c
crypto-utils.c
+ crypto-utils-fallback.c
crypto-utils-openssl.c
error.c
fdlimit.c
ConvertUTF.c \
crypto.c \
crypto-utils.c \
+ crypto-utils-fallback.c \
crypto-utils-openssl.c \
error.c \
fdlimit.c \
tr_cryptoConstruct (&a, hash, false);
tr_cryptoConstruct (&b, hash, true);
- tr_cryptoComputeSecret (&a, tr_cryptoGetMyPublicKey (&b, &i));
- tr_cryptoComputeSecret (&b, tr_cryptoGetMyPublicKey (&a, &i));
+ check (tr_cryptoComputeSecret (&a, tr_cryptoGetMyPublicKey (&b, &i)));
+ check (tr_cryptoComputeSecret (&b, tr_cryptoGetMyPublicKey (&a, &i)));
tr_cryptoEncryptInit (&a);
tr_cryptoEncrypt (&a, sizeof (test1), test1, buf11);
--- /dev/null
+/*
+ * This file Copyright (C) Mnemosyne LLC
+ *
+ * It may be used under the GNU GPL versions 2 or 3
+ * or any future license endorsed by Mnemosyne LLC.
+ *
+ * $Id$
+ */
+
+/* This file is designed specifically to be included by other source files to
+ implement missing (or duplicate) functionality without exposing internal
+ details in header files. */
+
+#include <assert.h>
+
+#include "transmission.h"
+#include "crypto-utils.h"
+#include "utils.h"
+
+/***
+****
+***/
+
+#ifdef TR_CRYPTO_DH_SECRET_FALLBACK
+
+/* Most Diffie-Hellman backends handle secret key in the very same way: by
+ manually allocating memory for it and storing the value in plain form. */
+
+struct tr_dh_secret
+{
+ size_t key_length;
+ uint8_t key[];
+};
+
+static struct tr_dh_secret *
+tr_dh_secret_new (size_t key_length)
+{
+ struct tr_dh_secret * handle = tr_malloc (sizeof (struct tr_dh_secret) + key_length);
+ handle->key_length = key_length;
+ return handle;
+}
+
+static void
+tr_dh_secret_align (struct tr_dh_secret * handle,
+ size_t current_key_length)
+{
+ tr_dh_align_key (handle->key, current_key_length, handle->key_length);
+}
+
+bool
+tr_dh_secret_derive (tr_dh_secret_t raw_handle,
+ const void * prepend_data,
+ size_t prepend_data_size,
+ const void * append_data,
+ size_t append_data_size,
+ uint8_t * hash)
+{
+ struct tr_dh_secret * handle = raw_handle;
+
+ assert (handle != NULL);
+ assert (hash != NULL);
+
+ return tr_sha1 (hash,
+ prepend_data == NULL ? "" : prepend_data,
+ prepend_data == NULL ? 0 : (int) prepend_data_size,
+ handle->key, (int) handle->key_length,
+ append_data, append_data == NULL ? 0 : (int) append_data_size,
+ NULL);
+}
+
+void
+tr_dh_secret_free (tr_dh_secret_t handle)
+{
+ tr_free (handle);
+}
+
+#endif /* TR_CRYPTO_DH_SECRET_FALLBACK */
#include <assert.h>
+#include <openssl/bn.h>
+#include <openssl/dh.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/rand.h>
#include "log.h"
#include "utils.h"
+#define TR_CRYPTO_DH_SECRET_FALLBACK
+#include "crypto-utils-fallback.c"
+
/***
****
***/
****
***/
+tr_dh_ctx_t
+tr_dh_new (const uint8_t * prime_num,
+ size_t prime_num_length,
+ const uint8_t * generator_num,
+ size_t generator_num_length)
+{
+ DH * handle = DH_new ();
+
+ assert (prime_num != NULL);
+ assert (generator_num != NULL);
+
+ if (!check_pointer (handle->p = BN_bin2bn (prime_num, prime_num_length, NULL)) ||
+ !check_pointer (handle->g = BN_bin2bn (generator_num, generator_num_length, NULL)))
+ {
+ DH_free (handle);
+ handle = NULL;
+ }
+
+ return handle;
+}
+
+void
+tr_dh_free (tr_dh_ctx_t handle)
+{
+ if (handle == NULL)
+ return;
+
+ DH_free (handle);
+}
+
+bool
+tr_dh_make_key (tr_dh_ctx_t raw_handle,
+ size_t private_key_length,
+ uint8_t * public_key,
+ size_t * public_key_length)
+{
+ DH * handle = raw_handle;
+ int dh_size, my_public_key_length;
+
+ assert (handle != NULL);
+ assert (public_key != NULL);
+
+ handle->length = private_key_length * 8;
+
+ if (!check_result (DH_generate_key (handle)))
+ return false;
+
+ my_public_key_length = BN_bn2bin (handle->pub_key, public_key);
+ dh_size = DH_size (handle);
+
+ tr_dh_align_key (public_key, my_public_key_length, dh_size);
+
+ if (public_key_length != NULL)
+ *public_key_length = dh_size;
+
+ return true;
+}
+
+tr_dh_secret_t
+tr_dh_agree (tr_dh_ctx_t handle,
+ const uint8_t * other_public_key,
+ size_t other_public_key_length)
+{
+ struct tr_dh_secret * ret;
+ int dh_size, secret_key_length;
+ BIGNUM * other_key;
+
+ assert (handle != NULL);
+ assert (other_public_key != NULL);
+
+ if (!check_pointer (other_key = BN_bin2bn (other_public_key, other_public_key_length, NULL)))
+ return NULL;
+
+ dh_size = DH_size (handle);
+ ret = tr_dh_secret_new (dh_size);
+
+ secret_key_length = DH_compute_key (ret->key, other_key, handle);
+ if (check_result_neq (secret_key_length, -1))
+ {
+ tr_dh_secret_align (ret, secret_key_length);
+ }
+ else
+ {
+ tr_dh_secret_free (ret);
+ ret = NULL;
+ }
+
+ BN_free (other_key);
+ return ret;
+}
+
+/***
+****
+***/
+
bool
tr_rand_buffer (void * buffer,
size_t length)
#include <assert.h>
#include <stdarg.h>
#include <stdlib.h> /* abs (), srand (), rand () */
+#include <string.h> /* memmove (), memset () */
#include "transmission.h"
#include "crypto-utils.h"
****
***/
+void
+tr_dh_align_key (uint8_t * key_buffer,
+ size_t key_size,
+ size_t buffer_size)
+{
+ assert (key_size <= buffer_size);
+
+ /* DH can generate key sizes that are smaller than the size of
+ key buffer with exponentially decreasing probability, in which case
+ the msb's of key buffer need to be zeroed appropriately. */
+ if (key_size < buffer_size)
+ {
+ const size_t offset = buffer_size - key_size;
+ memmove (key_buffer + offset, key_buffer, key_size);
+ memset (key_buffer, 0, offset);
+ }
+}
+
+/***
+****
+***/
+
bool
tr_sha1 (uint8_t * hash,
const void * data1,
typedef void * tr_sha1_ctx_t;
/** @brief Opaque RC4 context type. */
typedef void * tr_rc4_ctx_t;
+ /** @brief Opaque DH context type. */
+typedef void * tr_dh_ctx_t;
+ /** @brief Opaque DH secret key type. */
+typedef void * tr_dh_secret_t;
/**
* @brief Generate a SHA1 hash from one or more chunks of memory.
void * output,
size_t length);
+/**
+ * @brief Allocate and initialize new Diffie-Hellman (DH) key exchange context.
+ */
+tr_dh_ctx_t tr_dh_new (const uint8_t * prime_num,
+ size_t prime_num_length,
+ const uint8_t * generator_num,
+ size_t generator_num_length);
+
+/**
+ * @brief Free DH key exchange context.
+ */
+void tr_dh_free (tr_dh_ctx_t handle);
+
+/**
+ * @brief Generate private and public DH keys, export public key.
+ */
+bool tr_dh_make_key (tr_dh_ctx_t handle,
+ size_t private_key_length,
+ uint8_t * public_key,
+ size_t * public_key_length);
+
+/**
+ * @brief Perform DH key exchange, generate secret key.
+ */
+tr_dh_secret_t tr_dh_agree (tr_dh_ctx_t handle,
+ const uint8_t * other_public_key,
+ size_t other_public_key_length);
+
+/**
+ * @brief Calculate SHA1 hash of DH secret key, prepending and/or appending
+ * given data to the key during calculation.
+ */
+bool tr_dh_secret_derive (tr_dh_secret_t handle,
+ const void * prepend_data,
+ size_t prepend_data_size,
+ const void * append_data,
+ size_t append_data_size,
+ uint8_t * hash);
+
+/**
+ * @brief Free DH secret key returned by @ref tr_dh_agree.
+ */
+void tr_dh_secret_free (tr_dh_secret_t handle);
+
+/**
+ * @brief Align DH key (big-endian number) to required length (internal, do not use).
+ */
+void tr_dh_align_key (uint8_t * key_buffer,
+ size_t key_size,
+ size_t buffer_size);
+
/**
* @brief Returns a random number in the range of [0...upper_bound).
*/
*/
#include <assert.h>
-#include <stdarg.h>
#include <string.h> /* memcpy (), memmove (), memset (), strcmp () */
-#include <openssl/bn.h>
-#include <openssl/dh.h>
-#include <openssl/err.h>
-
#include "transmission.h"
#include "crypto.h"
#include "crypto-utils.h"
-#include "log.h"
#include "utils.h"
-#define MY_NAME "tr_crypto"
-
/**
***
**/
-#define KEY_LEN 96
-
#define PRIME_LEN 96
-
-#define DH_PRIVKEY_LEN_MIN 16
#define DH_PRIVKEY_LEN 20
static const uint8_t dh_P[PRIME_LEN] =
***
**/
-#define logErrorFromSSL(...) \
- do { \
- if (tr_logLevelIsActive (TR_LOG_ERROR)) { \
- char buf[512]; \
- ERR_error_string_n (ERR_get_error (), buf, sizeof (buf)); \
- tr_logAddMessage (__FILE__, __LINE__, TR_LOG_ERROR, MY_NAME, "%s", buf); \
- } \
- } while (0)
-
static void
ensureKeyExists (tr_crypto * crypto)
{
if (crypto->dh == NULL)
{
- int len, offset;
- DH * dh = DH_new ();
-
- dh->p = BN_bin2bn (dh_P, sizeof (dh_P), NULL);
- if (dh->p == NULL)
- logErrorFromSSL ();
-
- dh->g = BN_bin2bn (dh_G, sizeof (dh_G), NULL);
- if (dh->g == NULL)
- logErrorFromSSL ();
-
- /* private DH value: strong random BN of DH_PRIVKEY_LEN*8 bits */
- dh->priv_key = BN_new ();
- do
- {
- if (BN_rand (dh->priv_key, DH_PRIVKEY_LEN * 8, -1, 0) != 1)
- logErrorFromSSL ();
- }
- while (BN_num_bits (dh->priv_key) < DH_PRIVKEY_LEN_MIN * 8);
-
- if (!DH_generate_key (dh))
- logErrorFromSSL ();
-
- /* DH can generate key sizes that are smaller than the size of
- P with exponentially decreasing probability, in which case
- the msb's of myPublicKey need to be zeroed appropriately. */
- len = BN_num_bytes (dh->pub_key);
- offset = KEY_LEN - len;
- assert (len <= KEY_LEN);
- memset (crypto->myPublicKey, 0, offset);
- BN_bn2bin (dh->pub_key, crypto->myPublicKey + offset);
-
- crypto->dh = dh;
+ size_t public_key_length;
+
+ crypto->dh = tr_dh_new (dh_P, sizeof (dh_P), dh_G, sizeof (dh_G));
+ tr_dh_make_key (crypto->dh, DH_PRIVKEY_LEN, crypto->myPublicKey, &public_key_length);
+
+ assert (public_key_length == KEY_LEN);
}
}
{
memset (crypto, 0, sizeof (tr_crypto));
- crypto->dh = NULL;
crypto->isIncoming = isIncoming;
tr_cryptoSetTorrentHash (crypto, torrentHash);
}
void
tr_cryptoDestruct (tr_crypto * crypto)
{
- if (crypto->dh != NULL)
- DH_free (crypto->dh);
+ tr_dh_secret_free (crypto->mySecret);
+ tr_dh_free (crypto->dh);
tr_rc4_free (crypto->enc_key);
tr_rc4_free (crypto->dec_key);
}
***
**/
-const uint8_t*
+bool
tr_cryptoComputeSecret (tr_crypto * crypto,
const uint8_t * peerPublicKey)
{
- DH * dh;
- int len;
- uint8_t secret[KEY_LEN];
- BIGNUM * bn = BN_bin2bn (peerPublicKey, KEY_LEN, NULL);
-
ensureKeyExists (crypto);
- dh = crypto->dh;
-
- assert (DH_size (dh) == KEY_LEN);
-
- len = DH_compute_key (secret, bn, dh);
- if (len == -1)
- {
- logErrorFromSSL ();
- }
- else
- {
- int offset;
- assert (len <= KEY_LEN);
- offset = KEY_LEN - len;
- memset (crypto->mySecret, 0, offset);
- memcpy (crypto->mySecret + offset, secret, len);
- crypto->mySecretIsSet = true;
- }
-
- BN_free (bn);
- return crypto->mySecret;
+ crypto->mySecret = tr_dh_agree (crypto->dh, peerPublicKey, KEY_LEN);
+ return crypto->mySecret != NULL;
}
const uint8_t*
uint8_t buf[SHA_DIGEST_LENGTH];
assert (crypto->torrentHashIsSet);
- assert (crypto->mySecretIsSet);
if (*setme == NULL)
*setme = tr_rc4_new ();
- if (tr_sha1 (buf,
- key, 4,
- crypto->mySecret, KEY_LEN,
- crypto->torrentHash, SHA_DIGEST_LENGTH,
- NULL))
+ if (tr_cryptoSecretKeySha1 (crypto,
+ key, 4,
+ crypto->torrentHash, SHA_DIGEST_LENGTH,
+ buf))
tr_rc4_set_key (*setme, buf, SHA_DIGEST_LENGTH);
}
tr_rc4_process (crypto->enc_key, buf_in, buf_out, buf_len);
}
+bool
+tr_cryptoSecretKeySha1 (const tr_crypto * crypto,
+ const void * prepend_data,
+ size_t prepend_data_size,
+ const void * append_data,
+ size_t append_data_size,
+ uint8_t * hash)
+{
+ assert (crypto != NULL);
+ assert (crypto->mySecret != NULL);
+
+ return tr_dh_secret_derive (crypto->mySecret,
+ prepend_data, prepend_data_size,
+ append_data, append_data_size,
+ hash);
+}
+
/**
***
**/
*** @{
**/
-#include <openssl/dh.h> /* DH */
-
enum
{
- KEY_LEN = 96
+ KEY_LEN = 96
};
/** @brief Holds state information for encrypted peer communications */
{
tr_rc4_ctx_t dec_key;
tr_rc4_ctx_t enc_key;
- DH * dh;
+ tr_dh_ctx_t dh;
uint8_t myPublicKey[KEY_LEN];
- uint8_t mySecret[KEY_LEN];
+ tr_dh_secret_t mySecret;
uint8_t torrentHash[SHA_DIGEST_LENGTH];
bool isIncoming;
bool torrentHashIsSet;
- bool mySecretIsSet;
}
tr_crypto;
bool tr_cryptoHasTorrentHash (const tr_crypto * crypto);
-const uint8_t* tr_cryptoComputeSecret (tr_crypto * crypto,
+bool tr_cryptoComputeSecret (tr_crypto * crypto,
const uint8_t * peerPublicKey);
const uint8_t* tr_cryptoGetMyPublicKey (const tr_crypto * crypto,
const void * buf_in,
void * buf_out);
+bool tr_cryptoSecretKeySha1 (const tr_crypto * crypto,
+ const void * prepend_data,
+ size_t prepend_data_size,
+ const void * append_data,
+ size_t append_data_size,
+ uint8_t * hash);
+
/* @} */
/**
tr_peerIo * io;
tr_crypto * crypto;
tr_session * session;
- uint8_t mySecret[KEY_LEN];
handshake_state_t state;
tr_encryption_mode encryptionMode;
uint16_t pad_c_len;
return 0;
}
+static void
+computeRequestHash (const tr_handshake * handshake,
+ const char * name,
+ uint8_t * hash)
+{
+ tr_cryptoSecretKeySha1 (handshake->crypto, name, 4, NULL, 0, hash);
+}
+
static int
readYb (tr_handshake * handshake, struct evbuffer * inbuf)
{
int isEncrypted;
- const uint8_t * secret;
uint8_t yb[KEY_LEN];
struct evbuffer * outbuf;
size_t needlen = HANDSHAKE_NAME_LEN;
/* compute the secret */
evbuffer_remove (inbuf, yb, KEY_LEN);
- secret = tr_cryptoComputeSecret (handshake->crypto, yb);
- memcpy (handshake->mySecret, secret, KEY_LEN);
+ tr_cryptoComputeSecret (handshake->crypto, yb);
/* now send these: HASH ('req1', S), HASH ('req2', SKEY) xor HASH ('req3', S),
* ENCRYPT (VC, crypto_provide, len (PadC), PadC, len (IA)), ENCRYPT (IA) */
/* HASH ('req1', S) */
{
uint8_t req1[SHA_DIGEST_LENGTH];
- tr_sha1 (req1, "req1", 4, secret, KEY_LEN, NULL);
+ computeRequestHash (handshake, "req1", req1);
evbuffer_add (outbuf, req1, SHA_DIGEST_LENGTH);
}
uint8_t buf[SHA_DIGEST_LENGTH];
tr_sha1 (req2, "req2", 4, tr_cryptoGetTorrentHash (handshake->crypto), SHA_DIGEST_LENGTH, NULL);
- tr_sha1 (req3, "req3", 4, secret, KEY_LEN, NULL);
+ computeRequestHash (handshake, "req3", req3);
for (i=0; i<SHA_DIGEST_LENGTH; ++i)
buf[i] = req2[i] ^ req3[i];
uint8_t ya[KEY_LEN];
uint8_t * walk, outbuf[KEY_LEN + PadB_MAXLEN];
const uint8_t * myKey;
- const uint8_t * secret;
int len;
dbgmsg (handshake, "in readYa... need %d, have %"TR_PRIuSIZE,
/* read the incoming peer's public key */
evbuffer_remove (inbuf, ya, KEY_LEN);
- secret = tr_cryptoComputeSecret (handshake->crypto, ya);
- memcpy (handshake->mySecret, secret, KEY_LEN);
- tr_sha1 (handshake->myReq1, "req1", 4, secret, KEY_LEN, NULL);
+ tr_cryptoComputeSecret (handshake->crypto, ya);
+ computeRequestHash (handshake, "req1", handshake->myReq1);
/* send our public key to the peer */
dbgmsg (handshake, "sending B->A: Diffie Hellman Yb, PadB");
* by building the latter and xor'ing it with what the peer sent us */
dbgmsg (handshake, "reading obfuscated torrent hash...");
evbuffer_remove (inbuf, req2, SHA_DIGEST_LENGTH);
- tr_sha1 (req3, "req3", 4, handshake->mySecret, KEY_LEN, NULL);
+ computeRequestHash (handshake, "req3", req3);
for (i=0; i<SHA_DIGEST_LENGTH; ++i)
obfuscatedTorrentHash[i] = req2[i] ^ req3[i];
if ((tor = tr_torrentFindFromObfuscatedHash (handshake->session, obfuscatedTorrentHash)))