Merge branch 'prop176-v2'

This commit is contained in:
Nick Mathewson 2011-10-10 23:22:25 -04:00
commit 3b704fd166
12 changed files with 1976 additions and 154 deletions

8
changes/prop176 Normal file
View File

@ -0,0 +1,8 @@
o Major features
- Implement a new handshake protocol for authenticating Tors to
each other over TLS. It should be more resistant to fingerprinting
than previous protocols, and should require less TLS hacking for
future Tor implementations. Implements Proposal 185.
- Allow variable-length padding cells to disguise the length of
Tor's TLS records. Implements part of Proposal 184.

View File

@ -1206,9 +1206,6 @@ crypto_pk_asn1_decode(const char *str, size_t len)
{
RSA *rsa;
unsigned char *buf;
/* This ifdef suppresses a type warning. Take out the first case once
* everybody is using OpenSSL 0.9.7 or later.
*/
const unsigned char *cp;
cp = buf = tor_malloc(len);
memcpy(buf,str,len);
@ -1249,6 +1246,32 @@ crypto_pk_get_digest(crypto_pk_env_t *pk, char *digest_out)
return 0;
}
/** Compute all digests of the DER encoding of <b>pk</b>, and store them
* in <b>digests_out</b>. Return 0 on success, -1 on failure. */
int
crypto_pk_get_all_digests(crypto_pk_env_t *pk, digests_t *digests_out)
{
unsigned char *buf, *bufp;
int len;
len = i2d_RSAPublicKey(pk->key, NULL);
if (len < 0)
return -1;
buf = bufp = tor_malloc(len+1);
len = i2d_RSAPublicKey(pk->key, &bufp);
if (len < 0) {
crypto_log_errors(LOG_WARN,"encoding public key");
tor_free(buf);
return -1;
}
if (crypto_digest_all(digests_out, (char*)buf, len) < 0) {
tor_free(buf);
return -1;
}
tor_free(buf);
return 0;
}
/** Copy <b>in</b> to the <b>outlen</b>-byte buffer <b>out</b>, adding spaces
* every four spaces. */
/* static */ void
@ -1714,6 +1737,74 @@ crypto_hmac_sha1(char *hmac_out,
(unsigned char*)hmac_out, NULL);
}
/** Compute the HMAC-SHA-256 of the <b>msg_len</b> bytes in <b>msg</b>, using
* the <b>key</b> of length <b>key_len</b>. Store the DIGEST_LEN-byte result
* in <b>hmac_out</b>.
*/
void
crypto_hmac_sha256(char *hmac_out,
const char *key, size_t key_len,
const char *msg, size_t msg_len)
{
#if (OPENSSL_VERSION_NUMBER >= 0x00908000l)
/* If we've got OpenSSL >=0.9.8 we can use its hmac implementation. */
tor_assert(key_len < INT_MAX);
tor_assert(msg_len < INT_MAX);
HMAC(EVP_sha256(), key, (int)key_len, (unsigned char*)msg, (int)msg_len,
(unsigned char*)hmac_out, NULL);
#else
/* OpenSSL doesn't have an EVP implementation for SHA256. We'll need
to do HMAC on our own.
HMAC isn't so hard: To compute HMAC(key, msg):
1. If len(key) > blocksize, key = H(key).
2. If len(key) < blocksize, right-pad key up to blocksize with 0 bytes.
3. let ipad = key xor 0x363636363636....36
let opad = key xor 0x5c5c5c5c5c5c....5c
The result is H(opad | H( ipad | msg ) )
*/
#define BLOCKSIZE 64
#define DIGESTSIZE 32
uint8_t k[BLOCKSIZE];
uint8_t pad[BLOCKSIZE];
uint8_t d[DIGESTSIZE];
int i;
SHA256_CTX st;
tor_assert(key_len < INT_MAX);
tor_assert(msg_len < INT_MAX);
if (key_len <= BLOCKSIZE) {
memset(k, 0, sizeof(k));
memcpy(k, key, key_len); /* not time invariant in key_len */
} else {
SHA256((const uint8_t *)key, key_len, k);
memset(k+DIGESTSIZE, 0, sizeof(k)-DIGESTSIZE);
}
for (i = 0; i < BLOCKSIZE; ++i)
pad[i] = k[i] ^ 0x36;
SHA256_Init(&st);
SHA256_Update(&st, pad, BLOCKSIZE);
SHA256_Update(&st, (uint8_t*)msg, msg_len);
SHA256_Final(d, &st);
for (i = 0; i < BLOCKSIZE; ++i)
pad[i] = k[i] ^ 0x5c;
SHA256_Init(&st);
SHA256_Update(&st, pad, BLOCKSIZE);
SHA256_Update(&st, d, DIGESTSIZE);
SHA256_Final((uint8_t*)hmac_out, &st);
/* Now clear everything. */
memset(k, 0, sizeof(k));
memset(pad, 0, sizeof(pad));
memset(d, 0, sizeof(d));
memset(&st, 0, sizeof(st));
#undef BLOCKSIZE
#undef DIGESTSIZE
#endif
}
/* DH */
/** Shared P parameter for our circuit-crypto DH key exchanges. */

View File

@ -150,6 +150,7 @@ int crypto_pk_private_hybrid_decrypt(crypto_pk_env_t *env, char *to,
int crypto_pk_asn1_encode(crypto_pk_env_t *pk, char *dest, size_t dest_len);
crypto_pk_env_t *crypto_pk_asn1_decode(const char *str, size_t len);
int crypto_pk_get_digest(crypto_pk_env_t *pk, char *digest_out);
int crypto_pk_get_all_digests(crypto_pk_env_t *pk, digests_t *digests_out);
int crypto_pk_get_fingerprint(crypto_pk_env_t *pk, char *fp_out,int add_space);
int crypto_pk_check_fingerprint_syntax(const char *s);
@ -195,6 +196,9 @@ void crypto_digest_assign(crypto_digest_env_t *into,
void crypto_hmac_sha1(char *hmac_out,
const char *key, size_t key_len,
const char *msg, size_t msg_len);
void crypto_hmac_sha256(char *hmac_out,
const char *key, size_t key_len,
const char *msg, size_t msg_len);
/* Key negotiation */
#define DH_TYPE_CIRCUIT 1

View File

@ -97,15 +97,27 @@ static int use_unsafe_renegotiation_op = 0;
* SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION? */
static int use_unsafe_renegotiation_flag = 0;
/** Structure that we use for a single certificate. */
struct tor_cert_t {
X509 *cert;
uint8_t *encoded;
size_t encoded_len;
unsigned pkey_digests_set : 1;
digests_t cert_digests;
digests_t pkey_digests;
};
/** Holds a SSL_CTX object and related state used to configure TLS
* connections.
*/
typedef struct tor_tls_context_t {
int refcnt;
SSL_CTX *ctx;
X509 *my_cert;
X509 *my_id_cert;
crypto_pk_env_t *key;
tor_cert_t *my_link_cert;
tor_cert_t *my_id_cert;
tor_cert_t *my_auth_cert;
crypto_pk_env_t *link_key;
crypto_pk_env_t *auth_key;
} tor_tls_context_t;
#define TOR_TLS_MAGIC 0x71571571
@ -198,6 +210,7 @@ static int tor_tls_context_init_one(tor_tls_context_t **ppcontext,
unsigned int key_lifetime);
static tor_tls_context_t *tor_tls_context_new(crypto_pk_env_t *identity,
unsigned int key_lifetime);
static int check_cert_lifetime_internal(const X509 *cert, int tolerance);
/** Global TLS contexts. We keep them here because nobody else needs
* to touch them. */
@ -670,6 +683,137 @@ static const int N_CLIENT_CIPHERS =
SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA)
#endif
/** Free all storage held in <b>cert</b> */
void
tor_cert_free(tor_cert_t *cert)
{
if (! cert)
return;
if (cert->cert)
X509_free(cert->cert);
tor_free(cert->encoded);
memset(cert, 0x03, sizeof(cert));
tor_free(cert);
}
/**
* Allocate a new tor_cert_t to hold the certificate "x509_cert".
*
* Steals a reference to x509_cert.
*/
static tor_cert_t *
tor_cert_new(X509 *x509_cert)
{
tor_cert_t *cert;
EVP_PKEY *pkey;
RSA *rsa;
int length, length2;
unsigned char *cp;
if (!x509_cert)
return NULL;
length = i2d_X509(x509_cert, NULL);
cert = tor_malloc_zero(sizeof(tor_cert_t));
if (length <= 0) {
tor_free(cert);
log_err(LD_CRYPTO, "Couldn't get length of encoded x509 certificate");
X509_free(x509_cert);
return NULL;
}
cert->encoded_len = (size_t) length;
cp = cert->encoded = tor_malloc(length);
length2 = i2d_X509(x509_cert, &cp);
tor_assert(length2 == length);
cert->cert = x509_cert;
crypto_digest_all(&cert->cert_digests,
(char*)cert->encoded, cert->encoded_len);
if ((pkey = X509_get_pubkey(x509_cert)) &&
(rsa = EVP_PKEY_get1_RSA(pkey))) {
crypto_pk_env_t *pk = _crypto_new_pk_env_rsa(rsa);
crypto_pk_get_all_digests(pk, &cert->pkey_digests);
cert->pkey_digests_set = 1;
crypto_free_pk_env(pk);
EVP_PKEY_free(pkey);
}
return cert;
}
/** Read a DER-encoded X509 cert, of length exactly <b>certificate_len</b>,
* from a <b>certificate</b>. Return a newly allocated tor_cert_t on success
* and NULL on failure. */
tor_cert_t *
tor_cert_decode(const uint8_t *certificate, size_t certificate_len)
{
X509 *x509;
const unsigned char *cp = (const unsigned char *)certificate;
tor_cert_t *newcert;
tor_assert(certificate);
if (certificate_len > INT_MAX)
return NULL;
#if OPENSSL_VERSION_NUMBER < 0x00908000l
/* This ifdef suppresses a type warning. Take out this case once everybody
* is using OpenSSL 0.9.8 or later. */
x509 = d2i_X509(NULL, (unsigned char**)&cp, (int)certificate_len);
#else
x509 = d2i_X509(NULL, &cp, (int)certificate_len);
#endif
if (!x509)
return NULL; /* Couldn't decode */
if (cp - certificate != (int)certificate_len) {
X509_free(x509);
return NULL; /* Didn't use all the bytes */
}
newcert = tor_cert_new(x509);
if (!newcert) {
return NULL;
}
if (newcert->encoded_len != certificate_len ||
fast_memneq(newcert->encoded, certificate, certificate_len)) {
/* Cert wasn't in DER */
tor_cert_free(newcert);
return NULL;
}
return newcert;
}
/** Set *<b>encoded_out</b> and *<b>size_out/b> to <b>cert</b>'s encoded DER
* representation and length, respectively. */
void
tor_cert_get_der(const tor_cert_t *cert,
const uint8_t **encoded_out, size_t *size_out)
{
tor_assert(cert);
tor_assert(encoded_out);
tor_assert(size_out);
*encoded_out = cert->encoded;
*size_out = cert->encoded_len;
}
/** Return a set of digests for the public key in <b>cert</b>, or NULL if this
* cert's public key is not one we know how to take the digest of. */
const digests_t *
tor_cert_get_id_digests(const tor_cert_t *cert)
{
if (cert->pkey_digests_set)
return &cert->pkey_digests;
else
return NULL;
}
/** Return a set of digests for the public key in <b>cert</b>. */
const digests_t *
tor_cert_get_cert_digests(const tor_cert_t *cert)
{
return &cert->cert_digests;
}
/** Remove a reference to <b>ctx</b>, and free it if it has no more
* references. */
static void
@ -678,13 +822,171 @@ tor_tls_context_decref(tor_tls_context_t *ctx)
tor_assert(ctx);
if (--ctx->refcnt == 0) {
SSL_CTX_free(ctx->ctx);
X509_free(ctx->my_cert);
X509_free(ctx->my_id_cert);
crypto_free_pk_env(ctx->key);
tor_cert_free(ctx->my_link_cert);
tor_cert_free(ctx->my_id_cert);
tor_cert_free(ctx->my_auth_cert);
crypto_free_pk_env(ctx->link_key);
crypto_free_pk_env(ctx->auth_key);
tor_free(ctx);
}
}
/** Set *<b>link_cert_out</b> and *<b>id_cert_out</b> to the link certificate
* and ID certificate that we're currently using for our V3 in-protocol
* handshake's certificate chain. If <b>server</b> is true, provide the certs
* that we use in server mode; otherwise, provide the certs that we use in
* client mode. */
int
tor_tls_get_my_certs(int server,
const tor_cert_t **link_cert_out,
const tor_cert_t **id_cert_out)
{
tor_tls_context_t *ctx = server ? server_tls_context : client_tls_context;
if (! ctx)
return -1;
if (link_cert_out)
*link_cert_out = server ? ctx->my_link_cert : ctx->my_auth_cert;
if (id_cert_out)
*id_cert_out = ctx->my_id_cert;
return 0;
}
/**
* Return the authentication key that we use to authenticate ourselves as a
* client in the V3 in-protocol handshake.
*/
crypto_pk_env_t *
tor_tls_get_my_client_auth_key(void)
{
if (! client_tls_context)
return NULL;
return client_tls_context->auth_key;
}
/**
* Return the public key that a cetificate certifies. Return NULL if the
* cert's key is not RSA.
*/
crypto_pk_env_t *
tor_tls_cert_get_key(tor_cert_t *cert)
{
crypto_pk_env_t *result = NULL;
EVP_PKEY *pkey = X509_get_pubkey(cert->cert);
RSA *rsa;
if (!pkey)
return NULL;
rsa = EVP_PKEY_get1_RSA(pkey);
if (!rsa) {
EVP_PKEY_free(pkey);
return NULL;
}
result = _crypto_new_pk_env_rsa(rsa);
EVP_PKEY_free(pkey);
return result;
}
/** Return true iff <b>a</b> and <b>b</b> represent the same public key. */
static int
pkey_eq(EVP_PKEY *a, EVP_PKEY *b)
{
/* We'd like to do this, but openssl 0.9.7 doesn't have it:
return EVP_PKEY_cmp(a,b) == 1;
*/
unsigned char *a_enc=NULL, *b_enc=NULL, *a_ptr, *b_ptr;
int a_len1, b_len1, a_len2, b_len2, result;
a_len1 = i2d_PublicKey(a, NULL);
b_len1 = i2d_PublicKey(b, NULL);
if (a_len1 != b_len1)
return 0;
a_ptr = a_enc = tor_malloc(a_len1);
b_ptr = b_enc = tor_malloc(b_len1);
a_len2 = i2d_PublicKey(a, &a_ptr);
b_len2 = i2d_PublicKey(b, &b_ptr);
tor_assert(a_len2 == a_len1);
tor_assert(b_len2 == b_len1);
result = tor_memeq(a_enc, b_enc, a_len1);
tor_free(a_enc);
tor_free(b_enc);
return result;
}
/** Return true iff the other side of <b>tls</b> has authenticated to us, and
* the key certified in <b>cert</b> is the same as the key they used to do it.
*/
int
tor_tls_cert_matches_key(const tor_tls_t *tls, const tor_cert_t *cert)
{
X509 *peercert = SSL_get_peer_certificate(tls->ssl);
EVP_PKEY *link_key = NULL, *cert_key = NULL;
int result;
if (!peercert)
return 0;
link_key = X509_get_pubkey(peercert);
cert_key = X509_get_pubkey(cert->cert);
result = link_key && cert_key && pkey_eq(cert_key, link_key);
X509_free(peercert);
if (link_key)
EVP_PKEY_free(link_key);
if (cert_key)
EVP_PKEY_free(cert_key);
return result;
}
/** Check whether <b>cert</b> is well-formed, currently live, and correctly
* signed by the public key in <b>signing_cert</b>. If <b>check_rsa_1024</b>,
* make sure that it has an RSA key with 1024 bits; otherwise, just check that
* the key is long enough. Return 1 if the cert is good, and 0 if it's bad or
* we couldn't check it. */
int
tor_tls_cert_is_valid(const tor_cert_t *cert,
const tor_cert_t *signing_cert,
int check_rsa_1024)
{
EVP_PKEY *cert_key;
EVP_PKEY *signing_key = X509_get_pubkey(signing_cert->cert);
int r, key_ok = 0;
if (!signing_key)
return 0;
r = X509_verify(cert->cert, signing_key);
EVP_PKEY_free(signing_key);
if (r <= 0)
return 0;
/* okay, the signature checked out right. Now let's check the check the
* lifetime. */
/*XXXX tolerance might be iffy here */
if (check_cert_lifetime_internal(cert->cert, 60*60) < 0)
return 0;
cert_key = X509_get_pubkey(cert->cert);
if (check_rsa_1024 && cert_key) {
RSA *rsa = EVP_PKEY_get1_RSA(cert_key);
if (rsa && BN_num_bits(rsa->n) == 1024)
key_ok = 1;
if (rsa)
RSA_free(rsa);
} else if (cert_key) {
int min_bits = 1024;
#ifdef EVP_PKEY_EC
if (EVP_PKEY_type(cert_key->type) == EVP_PKEY_EC)
min_bits = 128;
#endif
if (EVP_PKEY_bits(cert_key) >= min_bits)
key_ok = 1;
}
EVP_PKEY_free(cert_key);
if (!key_ok)
return 0;
/* XXXX compare DNs or anything? */
return 1;
}
/** Increase the reference count of <b>ctx</b>. */
static void
tor_tls_context_incref(tor_tls_context_t *ctx)
@ -785,37 +1087,54 @@ tor_tls_context_init_one(tor_tls_context_t **ppcontext,
static tor_tls_context_t *
tor_tls_context_new(crypto_pk_env_t *identity, unsigned int key_lifetime)
{
crypto_pk_env_t *rsa = NULL;
crypto_pk_env_t *rsa = NULL, *rsa_auth = NULL;
EVP_PKEY *pkey = NULL;
tor_tls_context_t *result = NULL;
X509 *cert = NULL, *idcert = NULL;
X509 *cert = NULL, *idcert = NULL, *authcert = NULL;
char *nickname = NULL, *nn2 = NULL;
tor_tls_init();
nickname = crypto_random_hostname(8, 20, "www.", ".net");
#ifdef DISABLE_V3_LINKPROTO_SERVERSIDE
nn2 = crypto_random_hostname(8, 20, "www.", ".net");
#else
nn2 = crypto_random_hostname(8, 20, "www.", ".com");
#endif
/* Generate short-term RSA key. */
/* Generate short-term RSA key for use with TLS. */
if (!(rsa = crypto_new_pk_env()))
goto error;
if (crypto_pk_generate_key(rsa)<0)
goto error;
/* Create certificate signed by identity key. */
/* Generate short-term RSA key for use in the in-protocol ("v3")
* authentication handshake. */
if (!(rsa_auth = crypto_new_pk_env()))
goto error;
if (crypto_pk_generate_key(rsa_auth)<0)
goto error;
/* Create a link certificate signed by identity key. */
cert = tor_tls_create_certificate(rsa, identity, nickname, nn2,
key_lifetime);
/* Create self-signed certificate for identity key. */
idcert = tor_tls_create_certificate(identity, identity, nn2, nn2,
IDENTITY_CERT_LIFETIME);
if (!cert || !idcert) {
/* Create an authentication certificate signed by identity key. */
authcert = tor_tls_create_certificate(rsa_auth, identity, nickname, nn2,
key_lifetime);
if (!cert || !idcert || !authcert) {
log(LOG_WARN, LD_CRYPTO, "Error creating certificate");
goto error;
}
result = tor_malloc_zero(sizeof(tor_tls_context_t));
result->refcnt = 1;
result->my_cert = X509_dup(cert);
result->my_id_cert = X509_dup(idcert);
result->key = crypto_pk_dup_key(rsa);
result->my_link_cert = tor_cert_new(X509_dup(cert));
result->my_id_cert = tor_cert_new(X509_dup(idcert));
result->my_auth_cert = tor_cert_new(X509_dup(authcert));
if (!result->my_link_cert || !result->my_id_cert || !result->my_auth_cert)
goto error;
result->link_key = crypto_pk_dup_key(rsa);
result->auth_key = crypto_pk_dup_key(rsa_auth);
#ifdef EVERYONE_HAS_AES
/* Tell OpenSSL to only use TLS1 */
@ -881,6 +1200,9 @@ tor_tls_context_new(crypto_pk_env_t *identity, unsigned int key_lifetime)
if (rsa)
crypto_free_pk_env(rsa);
if (rsa_auth)
crypto_free_pk_env(rsa_auth);
X509_free(authcert);
tor_free(nickname);
tor_free(nn2);
return result;
@ -893,12 +1215,16 @@ tor_tls_context_new(crypto_pk_env_t *identity, unsigned int key_lifetime)
EVP_PKEY_free(pkey);
if (rsa)
crypto_free_pk_env(rsa);
if (rsa_auth)
crypto_free_pk_env(rsa_auth);
if (result)
tor_tls_context_decref(result);
if (cert)
X509_free(cert);
if (idcert)
X509_free(idcert);
if (authcert)
X509_free(authcert);
return NULL;
}
@ -1569,9 +1895,21 @@ tor_tls_peer_has_cert(tor_tls_t *tls)
return 1;
}
/** Return the peer certificate, or NULL if there isn't one. */
tor_cert_t *
tor_tls_get_peer_cert(tor_tls_t *tls)
{
X509 *cert;
cert = SSL_get_peer_certificate(tls->ssl);
tls_log_errors(tls, LOG_WARN, LD_HANDSHAKE, "getting peer certificate");
if (!cert)
return NULL;
return tor_cert_new(cert);
}
/** Warn that a certificate lifetime extends through a certain range. */
static void
log_cert_lifetime(X509 *cert, const char *problem)
log_cert_lifetime(const X509 *cert, const char *problem)
{
BIO *bio = NULL;
BUF_MEM *buf;
@ -1718,25 +2056,14 @@ tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_env_t **identity_key)
int
tor_tls_check_lifetime(tor_tls_t *tls, int tolerance)
{
time_t now, t;
X509 *cert;
int r = -1;
now = time(NULL);
if (!(cert = SSL_get_peer_certificate(tls->ssl)))
goto done;
t = now + tolerance;
if (X509_cmp_time(X509_get_notBefore(cert), &t) > 0) {
log_cert_lifetime(cert, "not yet valid");
if (check_cert_lifetime_internal(cert, tolerance) < 0)
goto done;
}
t = now - tolerance;
if (X509_cmp_time(X509_get_notAfter(cert), &t) < 0) {
log_cert_lifetime(cert, "already expired");
goto done;
}
r = 0;
done:
@ -1748,6 +2075,30 @@ tor_tls_check_lifetime(tor_tls_t *tls, int tolerance)
return r;
}
/** Helper: check whether <b>cert</b> is currently live, give or take
* <b>tolerance</b> seconds. If it is live, return 0. If it is not live,
* log a message and return -1. */
static int
check_cert_lifetime_internal(const X509 *cert, int tolerance)
{
time_t now, t;
now = time(NULL);
t = now + tolerance;
if (X509_cmp_time(X509_get_notBefore(cert), &t) > 0) {
log_cert_lifetime(cert, "not yet valid");
return -1;
}
t = now - tolerance;
if (X509_cmp_time(X509_get_notAfter(cert), &t) < 0) {
log_cert_lifetime(cert, "already expired");
return -1;
}
return 0;
}
/** Return the number of bytes available for reading from <b>tls</b>.
*/
int
@ -1831,6 +2182,82 @@ tor_tls_used_v1_handshake(tor_tls_t *tls)
return 1;
}
/** Return true iff <b>name</b> is a DN of a kind that could only
* occur in a v3-handshake-indicating certificate */
static int
dn_indicates_v3_cert(X509_NAME *name)
{
#ifdef DISABLE_V3_LINKPROTO_CLIENTSIDE
(void)name;
return 0;
#else
X509_NAME_ENTRY *entry;
int n_entries;
ASN1_OBJECT *obj;
ASN1_STRING *str;
unsigned char *s;
int len, r;
n_entries = X509_NAME_entry_count(name);
if (n_entries != 1)
return 1; /* More than one entry in the DN. */
entry = X509_NAME_get_entry(name, 0);
obj = X509_NAME_ENTRY_get_object(entry);
if (OBJ_obj2nid(obj) != OBJ_txt2nid("commonName"))
return 1; /* The entry isn't a commonName. */
str = X509_NAME_ENTRY_get_data(entry);
len = ASN1_STRING_to_UTF8(&s, str);
if (len < 0)
return 0;
r = fast_memneq(s + len - 4, ".net", 4);
OPENSSL_free(s);
return r;
#endif
}
/** Return true iff the peer certificate we're received on <b>tls</b>
* indicates that this connection should use the v3 (in-protocol)
* authentication handshake.
*
* Only the connection initiator should use this, and only once the initial
* handshake is done; the responder detects a v1 handshake by cipher types,
* and a v3/v2 handshake by Versions cell vs renegotiation.
*/
int
tor_tls_received_v3_certificate(tor_tls_t *tls)
{
X509 *cert = SSL_get_peer_certificate(tls->ssl);
EVP_PKEY *key;
X509_NAME *issuer_name, *subject_name;
if (!cert) {
log_warn(LD_BUG, "Called on a connection with no peer certificate");
return 0;
}
subject_name = X509_get_subject_name(cert);
issuer_name = X509_get_issuer_name(cert);
if (X509_name_cmp(subject_name, issuer_name) == 0)
return 1; /* purportedly self signed */
if (dn_indicates_v3_cert(subject_name) ||
dn_indicates_v3_cert(issuer_name))
return 1; /* DN is fancy */
key = X509_get_pubkey(cert);
if (EVP_PKEY_bits(key) != 1024 ||
EVP_PKEY_type(key->type) != EVP_PKEY_RSA) {
EVP_PKEY_free(key);
return 1; /* Key is fancy */
}
EVP_PKEY_free(key);
return 0;
}
/** Return the number of server handshakes that we've noticed doing on
* <b>tls</b>. */
int
@ -1847,6 +2274,36 @@ tor_tls_server_got_renegotiate(tor_tls_t *tls)
return tls->got_renegotiate;
}
/** Set the DIGEST256_LEN buffer at <b>secrets_out</b> to the value used in
* the v3 handshake to prove that the client knows the TLS secrets for the
* connection <b>tls</b>. Return 0 on success, -1 on failure.
*/
int
tor_tls_get_tlssecrets(tor_tls_t *tls, uint8_t *secrets_out)
{
#define TLSSECRET_MAGIC "Tor V3 handshake TLS cross-certification"
char buf[128];
size_t len;
tor_assert(tls);
tor_assert(tls->ssl);
tor_assert(tls->ssl->s3);
tor_assert(tls->ssl->session);
/*
The value is an HMAC, using the TLS master key as the HMAC key, of
client_random | server_random | TLSSECRET_MAGIC
*/
memcpy(buf + 0, tls->ssl->s3->client_random, 32);
memcpy(buf + 32, tls->ssl->s3->server_random, 32);
memcpy(buf + 64, TLSSECRET_MAGIC, strlen(TLSSECRET_MAGIC) + 1);
len = 64 + strlen(TLSSECRET_MAGIC) + 1;
crypto_hmac_sha256((char*)secrets_out,
(char*)tls->ssl->session->master_key,
tls->ssl->session->master_key_length,
buf, len);
memset(buf, 0, sizeof(buf));
return 0;
}
/** Examine the amount of memory used and available for buffers in <b>tls</b>.
* Set *<b>rbuf_capacity</b> to the amount of storage allocated for the read
* buffer and *<b>rbuf_bytes</b> to the amount actually used.

View File

@ -17,6 +17,9 @@
/* Opaque structure to hold a TLS connection. */
typedef struct tor_tls_t tor_tls_t;
/* Opaque structure to hold an X509 certificate. */
typedef struct tor_cert_t tor_cert_t;
/* Possible return values for most tor_tls_* functions. */
#define _MIN_TOR_TLS_ERROR_VAL -9
#define TOR_TLS_ERROR_MISC -9
@ -63,6 +66,7 @@ void tor_tls_set_renegotiate_callback(tor_tls_t *tls,
int tor_tls_is_server(tor_tls_t *tls);
void tor_tls_free(tor_tls_t *tls);
int tor_tls_peer_has_cert(tor_tls_t *tls);
tor_cert_t *tor_tls_get_peer_cert(tor_tls_t *tls);
int tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_env_t **identity);
int tor_tls_check_lifetime(tor_tls_t *tls, int tolerance);
int tor_tls_read(tor_tls_t *tls, char *cp, size_t len);
@ -85,8 +89,10 @@ void tor_tls_get_buffer_sizes(tor_tls_t *tls,
size_t *wbuf_capacity, size_t *wbuf_bytes);
int tor_tls_used_v1_handshake(tor_tls_t *tls);
int tor_tls_received_v3_certificate(tor_tls_t *tls);
int tor_tls_get_num_server_handshakes(tor_tls_t *tls);
int tor_tls_server_got_renegotiate(tor_tls_t *tls);
int tor_tls_get_tlssecrets(tor_tls_t *tls, uint8_t *secrets_out);
/* Log and abort if there are unhandled TLS errors in OpenSSL's error stack.
*/
@ -104,5 +110,21 @@ struct bufferevent *tor_tls_init_bufferevent(tor_tls_t *tls,
int filter);
#endif
void tor_cert_free(tor_cert_t *cert);
tor_cert_t *tor_cert_decode(const uint8_t *certificate, size_t certificate_len);
void tor_cert_get_der(const tor_cert_t *cert,
const uint8_t **encoded_out, size_t *size_out);
const digests_t *tor_cert_get_id_digests(const tor_cert_t *cert);
const digests_t *tor_cert_get_cert_digests(const tor_cert_t *cert);
int tor_tls_get_my_certs(int server,
const tor_cert_t **link_cert_out,
const tor_cert_t **id_cert_out);
crypto_pk_env_t *tor_tls_get_my_client_auth_key(void);
crypto_pk_env_t *tor_tls_cert_get_key(tor_cert_t *cert);
int tor_tls_cert_matches_key(const tor_tls_t *tls, const tor_cert_t *cert);
int tor_tls_cert_is_valid(const tor_cert_t *cert,
const tor_cert_t *signing_cert,
int check_rsa_1024);
#endif

View File

@ -1005,6 +1005,32 @@ fetch_from_buf(char *string, size_t string_len, buf_t *buf)
return (int)buf->datalen;
}
/** True iff the cell command <b>command</b> is one that implies a variable-length
* cell in Tor link protocol <b>linkproto</b>. */
static inline int
cell_command_is_var_length(uint8_t command, int linkproto)
{
/* If linkproto is v2 (2), CELL_VERSIONS is the only variable-length cells work as
* implemented here. If it's 1, there are no variable-length cells. Tor
* does not support other versions right now, and so can't negotiate them.
*/
switch (linkproto) {
case 1:
/* Link protocol version 1 has no variable-length cells. */
return 0;
case 2:
/* In link protocol version 2, VERSIONS is the only variable-length cell */
return command == CELL_VERSIONS;
case 0:
case 3:
default:
/* In link protocol version 3 and later, and in version "unknown",
* commands 128 and higher indicate variable-length. VERSIONS is
* grandfathered in. */
return command == CELL_VERSIONS || command >= 128;
}
}
/** Check <b>buf</b> for a variable-length cell according to the rules of link
* protocol version <b>linkproto</b>. If one is found, pull it off the buffer
* and assign a newly allocated var_cell_t to *<b>out</b>, and return 1.
@ -1019,12 +1045,6 @@ fetch_var_cell_from_buf(buf_t *buf, var_cell_t **out, int linkproto)
var_cell_t *result;
uint8_t command;
uint16_t length;
/* If linkproto is unknown (0) or v2 (2), variable-length cells work as
* implemented here. If it's 1, there are no variable-length cells. Tor
* does not support other versions right now, and so can't negotiate them.
*/
if (linkproto == 1)
return 0;
check();
*out = NULL;
if (buf->datalen < VAR_CELL_HEADER_SIZE)
@ -1032,7 +1052,7 @@ fetch_var_cell_from_buf(buf_t *buf, var_cell_t **out, int linkproto)
peek_from_buf(hdr, sizeof(hdr), buf);
command = get_uint8(hdr+2);
if (!(CELL_COMMAND_IS_VAR_LENGTH(command)))
if (!(cell_command_is_var_length(command, linkproto)))
return 0;
length = ntohs(get_uint16(hdr+3));
@ -1101,8 +1121,6 @@ fetch_var_cell_from_evbuffer(struct evbuffer *buf, var_cell_t **out,
uint16_t cell_length;
var_cell_t *cell;
int result = 0;
if (linkproto == 1)
return 0;
*out = NULL;
buf_len = evbuffer_get_length(buf);
@ -1113,7 +1131,7 @@ fetch_var_cell_from_evbuffer(struct evbuffer *buf, var_cell_t **out,
tor_assert(n >= VAR_CELL_HEADER_SIZE);
command = get_uint8(hdr+2);
if (!(CELL_COMMAND_IS_VAR_LENGTH(command))) {
if (!(cell_command_is_var_length(command, linkproto))) {
goto done;
}

View File

@ -46,6 +46,15 @@ uint64_t stats_n_versions_cells_processed = 0;
/** How many CELL_NETINFO cells have we received, ever? */
uint64_t stats_n_netinfo_cells_processed = 0;
/** How many CELL_VPADDING cells have we received, ever? */
uint64_t stats_n_vpadding_cells_processed = 0;
/** How many CELL_CERTS cells have we received, ever? */
uint64_t stats_n_cert_cells_processed = 0;
/** How many CELL_AUTH_CHALLENGE cells have we received, ever? */
uint64_t stats_n_auth_challenge_cells_processed = 0;
/** How many CELL_AUTHENTICATE cells have we received, ever? */
uint64_t stats_n_authenticate_cells_processed = 0;
/* These are the main functions for processing cells */
static void command_process_create_cell(cell_t *cell, or_connection_t *conn);
static void command_process_created_cell(cell_t *cell, or_connection_t *conn);
@ -54,6 +63,12 @@ static void command_process_destroy_cell(cell_t *cell, or_connection_t *conn);
static void command_process_versions_cell(var_cell_t *cell,
or_connection_t *conn);
static void command_process_netinfo_cell(cell_t *cell, or_connection_t *conn);
static void command_process_cert_cell(var_cell_t *cell,
or_connection_t *conn);
static void command_process_auth_challenge_cell(var_cell_t *cell,
or_connection_t *conn);
static void command_process_authenticate_cell(var_cell_t *cell,
or_connection_t *conn);
#ifdef KEEP_TIMING_STATS
/** This is a wrapper function around the actual function that processes the
@ -93,7 +108,7 @@ command_time_process_cell(cell_t *cell, or_connection_t *conn, int *time,
void
command_process_cell(cell_t *cell, or_connection_t *conn)
{
int handshaking = (conn->_base.state == OR_CONN_STATE_OR_HANDSHAKING);
int handshaking = (conn->_base.state != OR_CONN_STATE_OPEN);
#ifdef KEEP_TIMING_STATS
/* how many of each cell have we seen so far this second? needs better
* name. */
@ -134,9 +149,18 @@ command_process_cell(cell_t *cell, or_connection_t *conn)
#endif
/* Reject all but VERSIONS and NETINFO when handshaking. */
/* (VERSIONS should actually be impossible; it's variable-length.) */
if (handshaking && cell->command != CELL_VERSIONS &&
cell->command != CELL_NETINFO)
cell->command != CELL_NETINFO) {
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
"Received unexpected cell command %d in state %s; ignoring it.",
(int)cell->command,
conn_state_to_string(CONN_TYPE_OR,conn->_base.state));
return;
}
if (conn->_base.state == OR_CONN_STATE_OR_HANDSHAKING_V3)
or_handshake_state_record_cell(conn->handshake_state, cell, 1);
switch (cell->command) {
case CELL_PADDING:
@ -206,20 +230,79 @@ command_process_var_cell(var_cell_t *cell, or_connection_t *conn)
}
#endif
/* reject all when not handshaking. */
if (conn->_base.state != OR_CONN_STATE_OR_HANDSHAKING)
return;
switch (conn->_base.state)
{
case OR_CONN_STATE_OR_HANDSHAKING_V2:
if (cell->command != CELL_VERSIONS)
return;
break;
case OR_CONN_STATE_TLS_HANDSHAKING:
/* If we're using bufferevents, it's entirely possible for us to
* notice "hey, data arrived!" before we notice "hey, the handshake
* finished!" And we need to be accepting both at once to handle both
* the v2 and v3 handshakes. */
/* fall through */
case OR_CONN_STATE_TLS_SERVER_RENEGOTIATING:
if (cell->command != CELL_VERSIONS) {
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
"Received a non-VERSIONS cell with command %d in state %s; "
"ignoring it.",
(int)cell->command,
conn_state_to_string(CONN_TYPE_OR,conn->_base.state));
return;
}
break;
case OR_CONN_STATE_OR_HANDSHAKING_V3:
if (cell->command != CELL_AUTHENTICATE)
or_handshake_state_record_var_cell(conn->handshake_state, cell, 1);
break; /* Everything is allowed */
case OR_CONN_STATE_OPEN:
if (conn->link_proto < 3) {
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
"Received a variable-length cell with command %d in state %s "
"with link protocol %d; ignoring it.",
(int)cell->command,
conn_state_to_string(CONN_TYPE_OR,conn->_base.state),
(int)conn->link_proto);
return;
}
break;
default:
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
"Received var-length cell with command %d in unexpected state "
"%s [%d]; ignoring it.",
(int)cell->command,
conn_state_to_string(CONN_TYPE_OR,conn->_base.state),
(int)conn->_base.state);
return;
}
switch (cell->command) {
case CELL_VERSIONS:
++stats_n_versions_cells_processed;
PROCESS_CELL(versions, cell, conn);
break;
case CELL_VPADDING:
++stats_n_vpadding_cells_processed;
PROCESS_CELL(versions, cell, conn);
break;
case CELL_CERT:
++stats_n_cert_cells_processed;
PROCESS_CELL(cert, cell, conn);
break;
case CELL_AUTH_CHALLENGE:
++stats_n_auth_challenge_cells_processed;
PROCESS_CELL(auth_challenge, cell, conn);
break;
case CELL_AUTHENTICATE:
++stats_n_authenticate_cells_processed;
PROCESS_CELL(authenticate, cell, conn);
break;
default:
log_warn(LD_BUG,
log_fn(LOG_INFO, LD_PROTOCOL,
"Variable-length cell of unknown type (%d) received.",
cell->command);
tor_fragile_assert();
break;
}
}
@ -505,14 +588,40 @@ command_process_versions_cell(var_cell_t *cell, or_connection_t *conn)
{
int highest_supported_version = 0;
const uint8_t *cp, *end;
const int started_here = connection_or_nonopen_was_started_here(conn);
if (conn->link_proto != 0 ||
conn->_base.state != OR_CONN_STATE_OR_HANDSHAKING ||
(conn->handshake_state && conn->handshake_state->received_versions)) {
log_fn(LOG_PROTOCOL_WARN, LD_OR,
"Received a VERSIONS cell on a connection with its version "
"already set to %d; dropping", (int) conn->link_proto);
return;
}
switch (conn->_base.state)
{
case OR_CONN_STATE_OR_HANDSHAKING_V2:
break;
case OR_CONN_STATE_TLS_HANDSHAKING:
case OR_CONN_STATE_TLS_SERVER_RENEGOTIATING:
if (started_here) {
log_fn(LOG_PROTOCOL_WARN, LD_OR,
"Received a versions cell while TLS-handshaking not in "
"OR_HANDSHAKING_V3 on a connection we originated.");
}
conn->_base.state = OR_CONN_STATE_OR_HANDSHAKING_V3;
if (connection_init_or_handshake_state(conn, started_here) < 0) {
connection_mark_for_close(TO_CONN(conn));
return;
}
or_handshake_state_record_var_cell(conn->handshake_state, cell, 1);
break;
case OR_CONN_STATE_OR_HANDSHAKING_V3:
break;
default:
log_fn(LOG_PROTOCOL_WARN, LD_OR,
"VERSIONS cell while in unexpected state");
return;
}
tor_assert(conn->handshake_state);
end = cell->payload + cell->payload_len;
for (cp = cell->payload; cp+1 < end; ++cp) {
@ -534,19 +643,87 @@ command_process_versions_cell(var_cell_t *cell, or_connection_t *conn)
"That's crazily non-compliant. Closing connection.");
connection_mark_for_close(TO_CONN(conn));
return;
} else if (highest_supported_version < 3 &&
conn->_base.state == OR_CONN_STATE_OR_HANDSHAKING_V3) {
log_fn(LOG_PROTOCOL_WARN, LD_OR,
"Negotiated link protocol 2 or lower after doing a v3 TLS "
"handshake. Closing connection.");
connection_mark_for_close(TO_CONN(conn));
return;
}
conn->link_proto = highest_supported_version;
conn->handshake_state->received_versions = 1;
log_info(LD_OR, "Negotiated version %d with %s:%d; sending NETINFO.",
highest_supported_version,
safe_str_client(conn->_base.address),
conn->_base.port);
tor_assert(conn->link_proto >= 2);
if (conn->link_proto == 2) {
log_info(LD_OR, "Negotiated version %d with %s:%d; sending NETINFO.",
highest_supported_version,
safe_str_client(conn->_base.address),
conn->_base.port);
if (connection_or_send_netinfo(conn) < 0) {
connection_mark_for_close(TO_CONN(conn));
return;
if (connection_or_send_netinfo(conn) < 0) {
connection_mark_for_close(TO_CONN(conn));
return;
}
} else {
const int send_versions = !started_here;
/* If we want to authenticate, send a CERTS cell */
const int send_certs = !started_here || public_server_mode(get_options());
/* If we're a relay that got a connection, ask for authentication. */
const int send_chall = !started_here && public_server_mode(get_options());
/* If our certs cell will authenticate us, or if we have no intention of
* authenticating, send a netinfo cell right now. */
const int send_netinfo =
!(started_here && public_server_mode(get_options()));
const int send_any =
send_versions || send_certs || send_chall || send_netinfo;
tor_assert(conn->link_proto >= 3);
log_info(LD_OR, "Negotiated version %d with %s:%d; %s%s%s%s%s",
highest_supported_version,
safe_str_client(conn->_base.address),
conn->_base.port,
send_any ? "Sending cells:" : "Waiting for CERTS cell",
send_versions ? " VERSIONS" : "",
send_certs ? " CERTS" : "",
send_chall ? " AUTH_CHALLENGE" : "",
send_netinfo ? " NETINFO" : "");
#ifdef DISABLE_V3_LINKPROTO_SERVERSIDE
if (1) {
connection_mark_for_close(TO_CONN(conn));
return;
}
#endif
if (send_versions) {
if (connection_or_send_versions(conn, 1) < 0) {
log_warn(LD_OR, "Couldn't send versions cell");
connection_mark_for_close(TO_CONN(conn));
return;
}
}
if (send_certs) {
if (connection_or_send_cert_cell(conn) < 0) {
log_warn(LD_OR, "Couldn't send cert cell");
connection_mark_for_close(TO_CONN(conn));
return;
}
}
if (send_chall) {
if (connection_or_send_auth_challenge_cell(conn) < 0) {
log_warn(LD_OR, "Couldn't send auth_challenge cell");
connection_mark_for_close(TO_CONN(conn));
return;
}
}
if (send_netinfo) {
if (connection_or_send_netinfo(conn) < 0) {
log_warn(LD_OR, "Couldn't send netinfo cell");
connection_mark_for_close(TO_CONN(conn));
return;
}
}
}
}
@ -572,13 +749,41 @@ command_process_netinfo_cell(cell_t *cell, or_connection_t *conn)
conn->link_proto == 0 ? "non-versioned" : "a v1");
return;
}
if (conn->_base.state != OR_CONN_STATE_OR_HANDSHAKING) {
if (conn->_base.state != OR_CONN_STATE_OR_HANDSHAKING_V2 &&
conn->_base.state != OR_CONN_STATE_OR_HANDSHAKING_V3) {
log_fn(LOG_PROTOCOL_WARN, LD_OR,
"Received a NETINFO cell on non-handshaking connection; dropping.");
return;
}
tor_assert(conn->handshake_state &&
conn->handshake_state->received_versions);
if (conn->_base.state == OR_CONN_STATE_OR_HANDSHAKING_V3) {
tor_assert(conn->link_proto >= 3);
if (conn->handshake_state->started_here) {
if (!conn->handshake_state->authenticated) {
log_fn(LOG_PROTOCOL_WARN, LD_OR, "Got a NETINFO cell from server, "
"but no authentication. Closing the connection.");
connection_mark_for_close(TO_CONN(conn));
return;
}
} else {
/* we're the server. If the client never authenticated, we have
some housekeeping to do.*/
if (!conn->handshake_state->authenticated) {
tor_assert(tor_digest_is_zero(
(const char*)conn->handshake_state->authenticated_peer_id));
connection_or_set_circid_type(conn, NULL);
connection_or_init_conn_from_address(conn,
&conn->_base.addr,
conn->_base.port,
(const char*)conn->handshake_state->authenticated_peer_id,
0);
}
}
}
/* Decode the cell. */
timestamp = ntohl(get_uint32(cell->payload));
if (labs(now - conn->handshake_state->sent_versions_at) < 180) {
@ -651,13 +856,386 @@ command_process_netinfo_cell(cell_t *cell, or_connection_t *conn)
* trustworthy. */
(void)my_apparent_addr;
if (connection_or_set_state_open(conn)<0)
if (connection_or_set_state_open(conn)<0) {
log_fn(LOG_PROTOCOL_WARN, LD_OR, "Got good NETINFO cell from %s:%d; but "
"was unable to make the OR connection become open.",
safe_str_client(conn->_base.address),
conn->_base.port);
connection_mark_for_close(TO_CONN(conn));
else
} else {
log_info(LD_OR, "Got good NETINFO cell from %s:%d; OR connection is now "
"open, using protocol version %d",
"open, using protocol version %d. Its ID digest is %s",
safe_str_client(conn->_base.address),
conn->_base.port, (int)conn->link_proto);
conn->_base.port, (int)conn->link_proto,
hex_str(conn->identity_digest, DIGEST_LEN));
}
assert_connection_ok(TO_CONN(conn),time(NULL));
}
/** Process a CERT cell from an OR connection.
*
* If the other side should not have sent us a CERT cell, or the cell is
* malformed, or it is supposed to authenticate the TLS key but it doesn't,
* then mark the connection.
*
* If the cell has a good cert chain and we're doing a v3 handshake, then
* store the certificates in or_handshake_state. If this is the client side
* of the connection, we then authenticate the server or mark the connection.
* If it's the server side, wait for an AUTHENTICATE cell.
*/
static void
command_process_cert_cell(var_cell_t *cell, or_connection_t *conn)
{
#define ERR(s) \
do { \
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, \
"Received a bad CERT cell from %s:%d: %s", \
safe_str(conn->_base.address), conn->_base.port, (s)); \
connection_mark_for_close(TO_CONN(conn)); \
goto err; \
} while (0)
tor_cert_t *link_cert = NULL;
tor_cert_t *id_cert = NULL;
tor_cert_t *auth_cert = NULL;
uint8_t *ptr;
int n_certs, i;
if (conn->_base.state != OR_CONN_STATE_OR_HANDSHAKING_V3)
ERR("We're not doing a v3 handshake!");
if (conn->link_proto < 3)
ERR("We're not using link protocol >= 3");
if (conn->handshake_state->received_cert_cell)
ERR("We already got one");
if (conn->handshake_state->authenticated) {
/* Should be unreachable, but let's make sure. */
ERR("We're already authenticated!");
}
if (cell->payload_len < 1)
ERR("It had no body");
if (cell->circ_id)
ERR("It had a nonzero circuit ID");
n_certs = cell->payload[0];
ptr = cell->payload + 1;
for (i = 0; i < n_certs; ++i) {
uint8_t cert_type;
uint16_t cert_len;
if (ptr + 3 > cell->payload + cell->payload_len) {
goto truncated;
}
cert_type = *ptr;
cert_len = ntohs(get_uint16(ptr+1));
if (ptr + 3 + cert_len > cell->payload + cell->payload_len) {
goto truncated;
}
if (cert_type == OR_CERT_TYPE_TLS_LINK ||
cert_type == OR_CERT_TYPE_ID_1024 ||
cert_type == OR_CERT_TYPE_AUTH_1024) {
tor_cert_t *cert = tor_cert_decode(ptr + 3, cert_len);
if (!cert) {
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
"Received undecodable certificate in CERT cell from %s:%d",
safe_str(conn->_base.address), conn->_base.port);
} else {
if (cert_type == OR_CERT_TYPE_TLS_LINK) {
if (link_cert) {
tor_cert_free(cert);
ERR("Too many TLS_LINK certificates");
}
link_cert = cert;
} else if (cert_type == OR_CERT_TYPE_ID_1024) {
if (id_cert) {
tor_cert_free(cert);
ERR("Too many ID_1024 certificates");
}
id_cert = cert;
} else if (cert_type == OR_CERT_TYPE_AUTH_1024) {
if (auth_cert) {
tor_cert_free(cert);
ERR("Too many AUTH_1024 certificates");
}
auth_cert = cert;
} else {
tor_cert_free(cert);
}
}
}
ptr += 3 + cert_len;
continue;
truncated:
ERR("It ends in the middle of a certificate");
}
if (conn->handshake_state->started_here) {
if (! (id_cert && link_cert))
ERR("The certs we wanted were missing");
/* Okay. We should be able to check the certificates now. */
if (! tor_tls_cert_matches_key(conn->tls, link_cert)) {
ERR("The link certificate didn't match the TLS public key");
}
if (! tor_tls_cert_is_valid(link_cert, id_cert, 0))
ERR("The link certificate was not valid");
if (! tor_tls_cert_is_valid(id_cert, id_cert, 1))
ERR("The ID certificate was not valid");
conn->handshake_state->authenticated = 1;
{
const digests_t *id_digests = tor_cert_get_id_digests(id_cert);
crypto_pk_env_t *identity_rcvd;
if (!id_digests)
ERR("Couldn't compute digests for key in ID cert");
identity_rcvd = tor_tls_cert_get_key(id_cert);
memcpy(conn->handshake_state->authenticated_peer_id,
id_digests->d[DIGEST_SHA1], DIGEST_LEN);
connection_or_set_circid_type(conn, identity_rcvd);
crypto_free_pk_env(identity_rcvd);
}
if (connection_or_client_learned_peer_id(conn,
conn->handshake_state->authenticated_peer_id) < 0)
ERR("Problem setting or checking peer id");
log_info(LD_OR, "Got some good certificates from %s:%d: Authenticated it.",
safe_str(conn->_base.address), conn->_base.port);
conn->handshake_state->id_cert = id_cert;
id_cert = NULL;
} else {
if (! (id_cert && auth_cert))
ERR("The certs we wanted were missing");
/* Remember these certificates so we can check an AUTHENTICATE cell */
conn->handshake_state->id_cert = id_cert;
conn->handshake_state->auth_cert = auth_cert;
if (! tor_tls_cert_is_valid(auth_cert, id_cert, 1))
ERR("The authentication certificate was not valid");
if (! tor_tls_cert_is_valid(id_cert, id_cert, 1))
ERR("The ID certificate was not valid");
log_info(LD_OR, "Got some good certificates from %s:%d: "
"Waiting for AUTHENTICATE.",
safe_str(conn->_base.address), conn->_base.port);
/* XXXX check more stuff? */
id_cert = auth_cert = NULL;
}
conn->handshake_state->received_cert_cell = 1;
err:
tor_cert_free(id_cert);
tor_cert_free(link_cert);
tor_cert_free(auth_cert);
#undef ERR
}
/** Process an AUTH_CHALLENGE cell from an OR connection.
*
* If we weren't supposed to get one (for example, because we're not the
* originator of the connection), or it's ill-formed, or we aren't doing a v3
* handshake, mark the connection. If the cell is well-formed but we don't
* want to authenticate, just drop it. If the cell is well-formed *and* we
* want to authenticate, send an AUTHENTICATE cell. */
static void
command_process_auth_challenge_cell(var_cell_t *cell, or_connection_t *conn)
{
int n_types, i, use_type = -1;
uint8_t *cp;
#define ERR(s) \
do { \
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, \
"Received a bad AUTH_CHALLENGE cell from %s:%d: %s", \
safe_str(conn->_base.address), conn->_base.port, (s)); \
connection_mark_for_close(TO_CONN(conn)); \
return; \
} while (0)
if (conn->_base.state != OR_CONN_STATE_OR_HANDSHAKING_V3)
ERR("We're not currently doing a v3 handshake");
if (conn->link_proto < 3)
ERR("We're not using link protocol >= 3");
if (! conn->handshake_state->started_here)
ERR("We didn't originate this connection");
if (conn->handshake_state->received_auth_challenge)
ERR("We already received one");
if (! conn->handshake_state->received_cert_cell)
ERR("We haven't gotten a CERTS cell yet");
if (cell->payload_len < OR_AUTH_CHALLENGE_LEN + 2)
ERR("It was too short");
if (cell->circ_id)
ERR("It had a nonzero circuit ID");
n_types = ntohs(get_uint16(cell->payload + OR_AUTH_CHALLENGE_LEN));
if (cell->payload_len < OR_AUTH_CHALLENGE_LEN + 2 + 2*n_types)
ERR("It looks truncated");
/* Now see if there is an authentication type we can use */
cp=cell->payload+OR_AUTH_CHALLENGE_LEN+2;
for (i=0; i < n_types; ++i, cp += 2) {
uint16_t authtype = ntohs(get_uint16(cp));
if (authtype == AUTHTYPE_RSA_SHA256_TLSSECRET)
use_type = authtype;
}
conn->handshake_state->received_auth_challenge = 1;
if (use_type && public_server_mode(get_options())) {
log_info(LD_OR, "Got an AUTH_CHALLENGE cell from %s:%d: Sending "
"authentication",
safe_str(conn->_base.address), conn->_base.port);
if (connection_or_send_authenticate_cell(conn, use_type) < 0) {
log_warn(LD_OR, "Couldn't send authenticate cell");
connection_mark_for_close(TO_CONN(conn));
return;
}
if (connection_or_send_netinfo(conn) < 0) {
log_warn(LD_OR, "Couldn't send netinfo cell");
connection_mark_for_close(TO_CONN(conn));
return;
}
} else {
log_info(LD_OR, "Got an AUTH_CHALLENGE cell from %s:%d: Not authenticating",
safe_str(conn->_base.address), conn->_base.port);
}
#undef ERR
}
/** Process an AUTHENTICATE cell from an OR connection.
*
* If it's ill-formed or we weren't supposed to get one or we're not doing a
* v3 handshake, then mark the connection. If it does not authenticate the
* other side of the connection successfully (because it isn't signed right,
* we didn't get a CERT cell, etc) mark the connection. Otherwise, accept
* the identity of the router on the other side of the connection.
*/
static void
command_process_authenticate_cell(var_cell_t *cell, or_connection_t *conn)
{
uint8_t expected[V3_AUTH_FIXED_PART_LEN];
const uint8_t *auth;
int authlen;
#define ERR(s) \
do { \
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, \
"Received a bad AUTHETNICATE cell from %s:%d: %s", \
safe_str(conn->_base.address), conn->_base.port, (s)); \
connection_mark_for_close(TO_CONN(conn)); \
return; \
} while (0)
if (conn->_base.state != OR_CONN_STATE_OR_HANDSHAKING_V3)
ERR("We're not doing a v3 handshake");
if (conn->link_proto < 3)
ERR("We're not using link protocol >= 3");
if (conn->handshake_state->started_here)
ERR("We originated this connection");
if (conn->handshake_state->received_authenticate)
ERR("We already got one!");
if (conn->handshake_state->authenticated) {
/* Should be impossible given other checks */
ERR("The peer is already authenticated");
}
if (! conn->handshake_state->received_cert_cell)
ERR("We never got a cert cell");
if (conn->handshake_state->auth_cert == NULL)
ERR("We never got an authentication certificate");
if (conn->handshake_state->id_cert == NULL)
ERR("We never got an identity certificate");
if (cell->payload_len < 4)
ERR("Cell was way too short");
auth = cell->payload;
{
uint16_t type = ntohs(get_uint16(auth));
uint16_t len = ntohs(get_uint16(auth+2));
if (4 + len > cell->payload_len)
ERR("Authenticator was truncated");
if (type != AUTHTYPE_RSA_SHA256_TLSSECRET)
ERR("Authenticator type was not recognized");
auth += 4;
authlen = len;
}
if (authlen < V3_AUTH_BODY_LEN + 1)
ERR("Authenticator was too short");
if (connection_or_compute_authenticate_cell_body(
conn, expected, sizeof(expected), NULL, 1) < 0)
ERR("Couldn't compute expected AUTHENTICATE cell body");
if (tor_memneq(expected, auth, sizeof(expected)))
ERR("Some field in the AUTHENTICATE cell body was not as expected");
{
crypto_pk_env_t *pk = tor_tls_cert_get_key(
conn->handshake_state->auth_cert);
char d[DIGEST256_LEN];
char *signed_data;
size_t keysize;
int signed_len;
crypto_digest256(d, (char*)auth, V3_AUTH_BODY_LEN, DIGEST_SHA256);
keysize = crypto_pk_keysize(pk);
signed_data = tor_malloc(keysize);
signed_len = crypto_pk_public_checksig(pk, signed_data, keysize,
(char*)auth + V3_AUTH_BODY_LEN,
authlen - V3_AUTH_BODY_LEN);
if (signed_len < 0) {
tor_free(signed_data);
ERR("Signature wasn't valid");
}
if (signed_len < DIGEST256_LEN) {
tor_free(signed_data);
ERR("Not enough data was signed");
}
/* Note that we deliberately allow *more* than DIGEST256_LEN bytes here,
* in case they're later used to hold a SHA3 digest or something. */
if (tor_memneq(signed_data, d, DIGEST256_LEN)) {
tor_free(signed_data);
ERR("Signature did not match data to be signed.");
}
tor_free(signed_data);
}
/* Okay, we are authenticated. */
conn->handshake_state->received_authenticate = 1;
conn->handshake_state->authenticated = 1;
conn->handshake_state->digest_received_data = 0;
{
crypto_pk_env_t *identity_rcvd =
tor_tls_cert_get_key(conn->handshake_state->id_cert);
const digests_t *id_digests =
tor_cert_get_id_digests(conn->handshake_state->id_cert);
/* This must exist; we checked key type when reading the cert. */
tor_assert(id_digests);
memcpy(conn->handshake_state->authenticated_peer_id,
id_digests->d[DIGEST_SHA1], DIGEST_LEN);
connection_or_set_circid_type(conn, identity_rcvd);
crypto_free_pk_env(identity_rcvd);
connection_or_init_conn_from_address(conn,
&conn->_base.addr,
conn->_base.port,
(const char*)conn->handshake_state->authenticated_peer_id,
0);
log_info(LD_OR, "Got an AUTHENTICATE cell from %s:%d: Looks good.",
safe_str(conn->_base.address), conn->_base.port);
}
#undef ERR
}

View File

@ -140,10 +140,13 @@ conn_state_to_string(int type, int state)
case OR_CONN_STATE_PROXY_HANDSHAKING: return "handshaking (proxy)";
case OR_CONN_STATE_TLS_HANDSHAKING: return "handshaking (TLS)";
case OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING:
return "renegotiating (TLS)";
return "renegotiating (TLS, v2 handshake)";
case OR_CONN_STATE_TLS_SERVER_RENEGOTIATING:
return "waiting for renegotiation (TLS)";
case OR_CONN_STATE_OR_HANDSHAKING: return "handshaking (Tor)";
return "waiting for renegotiation or V3 handshake";
case OR_CONN_STATE_OR_HANDSHAKING_V2:
return "handshaking (Tor, v2 handshake)";
case OR_CONN_STATE_OR_HANDSHAKING_V3:
return "handshaking (Tor, v3 handshake)";
case OR_CONN_STATE_OPEN: return "open";
}
break;

View File

@ -35,10 +35,8 @@
#endif
static int connection_tls_finish_handshake(or_connection_t *conn);
static int connection_or_launch_v3_or_handshake(or_connection_t *conn);
static int connection_or_process_cells_from_inbuf(or_connection_t *conn);
static int connection_or_send_versions(or_connection_t *conn);
static int connection_init_or_handshake_state(or_connection_t *conn,
int started_here);
static int connection_or_check_valid_tls_handshake(or_connection_t *conn,
int started_here,
char *digest_rcvd_out);
@ -378,8 +376,8 @@ connection_or_process_inbuf(or_connection_t *conn)
}
return ret;
#ifdef USE_BUFFEREVENTS
case OR_CONN_STATE_TLS_SERVER_RENEGOTIATING:
#ifdef USE_BUFFEREVENTS
if (tor_tls_server_got_renegotiate(conn->tls))
connection_or_tls_renegotiated_cb(conn->tls, conn);
if (conn->_base.marked_for_close)
@ -387,7 +385,8 @@ connection_or_process_inbuf(or_connection_t *conn)
/* fall through. */
#endif
case OR_CONN_STATE_OPEN:
case OR_CONN_STATE_OR_HANDSHAKING:
case OR_CONN_STATE_OR_HANDSHAKING_V2:
case OR_CONN_STATE_OR_HANDSHAKING_V3:
return connection_or_process_cells_from_inbuf(conn);
default:
return 0; /* don't do anything */
@ -439,7 +438,8 @@ connection_or_finished_flushing(or_connection_t *conn)
switch (conn->_base.state) {
case OR_CONN_STATE_PROXY_HANDSHAKING:
case OR_CONN_STATE_OPEN:
case OR_CONN_STATE_OR_HANDSHAKING:
case OR_CONN_STATE_OR_HANDSHAKING_V2:
case OR_CONN_STATE_OR_HANDSHAKING_V3:
break;
default:
log_err(LD_BUG,"Called in unexpected state %d.", conn->_base.state);
@ -627,7 +627,7 @@ connection_or_update_token_buckets(smartlist_t *conns,
/** If we don't necessarily know the router we're connecting to, but we
* have an addr/port/id_digest, then fill in as much as we can. Start
* by checking to see if this describes a router we know. */
static void
void
connection_or_init_conn_from_address(or_connection_t *conn,
const tor_addr_t *addr, uint16_t port,
const char *id_digest,
@ -1180,16 +1180,22 @@ connection_tls_continue_handshake(or_connection_t *conn)
if (! tor_tls_used_v1_handshake(conn->tls)) {
if (!tor_tls_is_server(conn->tls)) {
if (conn->_base.state == OR_CONN_STATE_TLS_HANDSHAKING) {
log_debug(LD_OR, "Done with initial SSL handshake (client-side). "
"Requesting renegotiation.");
conn->_base.state = OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING;
goto again;
if (tor_tls_received_v3_certificate(conn->tls)) {
log_info(LD_OR, "Client got a v3 cert! Moving on to v3 "
"handshake.");
return connection_or_launch_v3_or_handshake(conn);
} else {
log_debug(LD_OR, "Done with initial SSL handshake (client-side). "
"Requesting renegotiation.");
conn->_base.state = OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING;
goto again;
}
}
// log_notice(LD_OR,"Done. state was %d.", conn->_base.state);
} else {
/* improved handshake, but not a client. */
/* v2/v3 handshake, but not a client. */
log_debug(LD_OR, "Done with initial SSL handshake (server-side). "
"Expecting renegotiation.");
"Expecting renegotiation or VERSIONS cell");
tor_tls_set_renegotiate_callback(conn->tls,
connection_or_tls_renegotiated_cb,
conn);
@ -1234,17 +1240,24 @@ connection_or_handle_event_cb(struct bufferevent *bufev, short event,
if (! tor_tls_used_v1_handshake(conn->tls)) {
if (!tor_tls_is_server(conn->tls)) {
if (conn->_base.state == OR_CONN_STATE_TLS_HANDSHAKING) {
conn->_base.state = OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING;
tor_tls_unblock_renegotiation(conn->tls);
if (bufferevent_ssl_renegotiate(conn->_base.bufev)<0) {
log_warn(LD_OR, "Start_renegotiating went badly.");
connection_mark_for_close(TO_CONN(conn));
if (tor_tls_received_v3_certificate(conn->tls)) {
log_info(LD_OR, "Client got a v3 cert!");
if (connection_or_launch_v3_or_handshake(conn) < 0)
connection_mark_for_close(TO_CONN(conn));
return;
} else {
conn->_base.state = OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING;
tor_tls_unblock_renegotiation(conn->tls);
if (bufferevent_ssl_renegotiate(conn->_base.bufev)<0) {
log_warn(LD_OR, "Start_renegotiating went badly.");
connection_mark_for_close(TO_CONN(conn));
}
tor_tls_unblock_renegotiation(conn->tls);
return; /* ???? */
}
tor_tls_unblock_renegotiation(conn->tls);
return; /* ???? */
}
} else if (tor_tls_get_num_server_handshakes(conn->tls) == 1) {
/* improved handshake, as a server. Only got one handshake, so
/* v2 or v3 handshake, as a server. Only got one handshake, so
* wait for the next one. */
tor_tls_set_renegotiate_callback(conn->tls,
connection_or_tls_renegotiated_cb,
@ -1256,7 +1269,7 @@ connection_or_handle_event_cb(struct bufferevent *bufev, short event,
const int handshakes = tor_tls_get_num_server_handshakes(conn->tls);
tor_assert(handshakes >= 2);
if (handshakes == 2) {
/* improved handshake, as a server. Two handshakes happened already,
/* v2 handshake, as a server. Two handshakes happened already,
* so we treat renegotiation as done.
*/
connection_or_tls_renegotiated_cb(conn->tls, conn);
@ -1300,6 +1313,29 @@ connection_or_nonopen_was_started_here(or_connection_t *conn)
return !tor_tls_is_server(conn->tls);
}
/** Set the circid_type field of <b>conn</b> (which determines which part of
* the circuit ID space we're willing to use) based on comparing our ID to
* <b>identity_rcvd</b> */
void
connection_or_set_circid_type(or_connection_t *conn,
crypto_pk_env_t *identity_rcvd)
{
const int started_here = connection_or_nonopen_was_started_here(conn);
crypto_pk_env_t *our_identity =
started_here ? get_tlsclient_identity_key() :
get_server_identity_key();
if (identity_rcvd) {
if (crypto_pk_cmp_keys(our_identity, identity_rcvd)<0) {
conn->circ_id_type = CIRC_ID_TYPE_LOWER;
} else {
conn->circ_id_type = CIRC_ID_TYPE_HIGHER;
}
} else {
conn->circ_id_type = CIRC_ID_TYPE_NEITHER;
}
}
/** <b>Conn</b> just completed its handshake. Return 0 if all is well, and
* return -1 if he is lying, broken, or otherwise something is wrong.
*
@ -1337,10 +1373,7 @@ connection_or_check_valid_tls_handshake(or_connection_t *conn,
started_here ? conn->_base.address :
safe_str_client(conn->_base.address);
const char *conn_type = started_here ? "outgoing" : "incoming";
crypto_pk_env_t *our_identity =
started_here ? get_tlsclient_identity_key() :
get_server_identity_key();
int has_cert = 0, has_identity=0;
int has_cert = 0;
check_no_tls_errors();
has_cert = tor_tls_peer_has_cert(conn->tls);
@ -1375,21 +1408,46 @@ connection_or_check_valid_tls_handshake(or_connection_t *conn,
}
if (identity_rcvd) {
has_identity = 1;
crypto_pk_get_digest(identity_rcvd, digest_rcvd_out);
if (crypto_pk_cmp_keys(our_identity, identity_rcvd)<0) {
conn->circ_id_type = CIRC_ID_TYPE_LOWER;
} else {
conn->circ_id_type = CIRC_ID_TYPE_HIGHER;
}
crypto_free_pk_env(identity_rcvd);
} else {
memset(digest_rcvd_out, 0, DIGEST_LEN);
conn->circ_id_type = CIRC_ID_TYPE_NEITHER;
}
if (started_here && tor_digest_is_zero(conn->identity_digest)) {
connection_or_set_identity_digest(conn, digest_rcvd_out);
connection_or_set_circid_type(conn, identity_rcvd);
crypto_free_pk_env(identity_rcvd);
if (started_here)
return connection_or_client_learned_peer_id(conn,
(const uint8_t*)digest_rcvd_out);
return 0;
}
/** Called when we (as a connection initiator) have definitively,
* authenticatedly, learned that ID of the Tor instance on the other
* side of <b>conn</b> is <b>peer_id</b>. For v1 and v2 handshakes,
* this is right after we get a certificate chain in a TLS handshake
* or renegotiation. For v3 handshakes, this is right after we get a
* certificate chain in a CERT cell.
*
* If we want any particular ID before, record the one we got.
*
* If we wanted an ID, but we didn't get it, log a warning and return -1.
*
* If we're testing reachability, remember what we learned.
*
* Return 0 on success, -1 on failure.
*/
int
connection_or_client_learned_peer_id(or_connection_t *conn,
const uint8_t *peer_id)
{
int as_expected = 1;
const or_options_t *options = get_options();
int severity = server_mode(options) ? LOG_PROTOCOL_WARN : LOG_WARN;
if (tor_digest_is_zero(conn->identity_digest)) {
connection_or_set_identity_digest(conn, (const char*)peer_id);
tor_free(conn->nickname);
conn->nickname = tor_malloc(HEX_DIGEST_LEN+2);
conn->nickname[0] = '$';
@ -1401,43 +1459,39 @@ connection_or_check_valid_tls_handshake(or_connection_t *conn,
/* if it's a bridge and we didn't know its identity fingerprint, now
* we do -- remember it for future attempts. */
learned_router_identity(&conn->_base.addr, conn->_base.port,
digest_rcvd_out);
(const char*)peer_id);
}
if (started_here) {
int as_advertised = 1;
tor_assert(has_cert);
tor_assert(has_identity);
if (tor_memneq(digest_rcvd_out, conn->identity_digest, DIGEST_LEN)) {
/* I was aiming for a particular digest. I didn't get it! */
char seen[HEX_DIGEST_LEN+1];
char expected[HEX_DIGEST_LEN+1];
base16_encode(seen, sizeof(seen), digest_rcvd_out, DIGEST_LEN);
base16_encode(expected, sizeof(expected), conn->identity_digest,
DIGEST_LEN);
log_fn(severity, LD_HANDSHAKE,
"Tried connecting to router at %s:%d, but identity key was not "
"as expected: wanted %s but got %s.",
conn->_base.address, conn->_base.port, expected, seen);
entry_guard_register_connect_status(conn->identity_digest, 0, 1,
time(NULL));
control_event_or_conn_status(conn, OR_CONN_EVENT_FAILED,
END_OR_CONN_REASON_OR_IDENTITY);
if (!authdir_mode_tests_reachability(options))
control_event_bootstrap_problem("foo", END_OR_CONN_REASON_OR_IDENTITY);
as_advertised = 0;
}
if (authdir_mode_tests_reachability(options)) {
dirserv_orconn_tls_done(conn->_base.address, conn->_base.port,
digest_rcvd_out, as_advertised);
}
if (!as_advertised)
return -1;
if (tor_memneq(peer_id, conn->identity_digest, DIGEST_LEN)) {
/* I was aiming for a particular digest. I didn't get it! */
char seen[HEX_DIGEST_LEN+1];
char expected[HEX_DIGEST_LEN+1];
base16_encode(seen, sizeof(seen), (const char*)peer_id, DIGEST_LEN);
base16_encode(expected, sizeof(expected), conn->identity_digest,
DIGEST_LEN);
log_fn(severity, LD_HANDSHAKE,
"Tried connecting to router at %s:%d, but identity key was not "
"as expected: wanted %s but got %s.",
conn->_base.address, conn->_base.port, expected, seen);
entry_guard_register_connect_status(conn->identity_digest, 0, 1,
time(NULL));
control_event_or_conn_status(conn, OR_CONN_EVENT_FAILED,
END_OR_CONN_REASON_OR_IDENTITY);
if (!authdir_mode_tests_reachability(options))
control_event_bootstrap_problem("foo", END_OR_CONN_REASON_OR_IDENTITY);
as_expected = 0;
}
if (authdir_mode_tests_reachability(options)) {
dirserv_orconn_tls_done(conn->_base.address, conn->_base.port,
(const char*)peer_id, as_expected);
}
if (!as_expected)
return -1;
return 0;
}
/** The tls handshake is finished.
/** The v1/v2 TLS handshake is finished.
*
* Make sure we are happy with the person we just handshaked with.
*
@ -1447,6 +1501,8 @@ connection_or_check_valid_tls_handshake(or_connection_t *conn,
* If all is successful, call circuit_n_conn_done() to handle events
* that have been pending on the <tls handshake completion. Also set the
* directory to be dirty (only matters if I'm an authdirserver).
*
* If this is a v2 TLS handshake, send a versions cell.
*/
static int
connection_tls_finish_handshake(or_connection_t *conn)
@ -1476,25 +1532,49 @@ connection_tls_finish_handshake(or_connection_t *conn)
tor_tls_block_renegotiation(conn->tls);
return connection_or_set_state_open(conn);
} else {
conn->_base.state = OR_CONN_STATE_OR_HANDSHAKING;
conn->_base.state = OR_CONN_STATE_OR_HANDSHAKING_V2;
if (connection_init_or_handshake_state(conn, started_here) < 0)
return -1;
if (!started_here) {
connection_or_init_conn_from_address(conn, &conn->_base.addr,
conn->_base.port, digest_rcvd, 0);
}
return connection_or_send_versions(conn);
return connection_or_send_versions(conn, 0);
}
}
/**
* Called as client when initial TLS handshake is done, and we notice
* that we got a v3-handshake signalling certificate from the server.
* Set up structures, do bookkeeping, and send the versions cell.
* Return 0 on success and -1 on failure.
*/
static int
connection_or_launch_v3_or_handshake(or_connection_t *conn)
{
tor_assert(connection_or_nonopen_was_started_here(conn));
tor_assert(tor_tls_received_v3_certificate(conn->tls));
circuit_build_times_network_is_live(&circ_times);
conn->_base.state = OR_CONN_STATE_OR_HANDSHAKING_V3;
if (connection_init_or_handshake_state(conn, 1) < 0)
return -1;
return connection_or_send_versions(conn, 1);
}
/** Allocate a new connection handshake state for the connection
* <b>conn</b>. Return 0 on success, -1 on failure. */
static int
int
connection_init_or_handshake_state(or_connection_t *conn, int started_here)
{
or_handshake_state_t *s;
s = conn->handshake_state = tor_malloc_zero(sizeof(or_handshake_state_t));
s->started_here = started_here ? 1 : 0;
s->digest_sent_data = 1;
s->digest_received_data = 1;
return 0;
}
@ -1504,10 +1584,89 @@ or_handshake_state_free(or_handshake_state_t *state)
{
if (!state)
return;
crypto_free_digest_env(state->digest_sent);
crypto_free_digest_env(state->digest_received);
tor_cert_free(state->auth_cert);
tor_cert_free(state->id_cert);
memset(state, 0xBE, sizeof(or_handshake_state_t));
tor_free(state);
}
/**
* Remember that <b>cell</b> has been transmitted (if <b>incoming</b> is
* false) or received (if <b>incoming is true) during a V3 handshake using
* <b>state</b>.
*
* (We don't record the cell, but we keep a digest of everything sent or
* received during the v3 handshake, and the client signs it in an
* authenticate cell.)
*/
void
or_handshake_state_record_cell(or_handshake_state_t *state,
const cell_t *cell,
int incoming)
{
crypto_digest_env_t *d, **dptr;
packed_cell_t packed;
if (incoming) {
if (!state->digest_received_data)
return;
} else {
if (!state->digest_sent_data)
return;
}
if (!incoming) {
log_warn(LD_BUG, "We shouldn't be sending any non-variable-length cells "
"while making a handshake digest. But we think we are sending "
"one with type %d.", (int)cell->command);
}
dptr = incoming ? &state->digest_received : &state->digest_sent;
if (! *dptr)
*dptr = crypto_new_digest256_env(DIGEST_SHA256);
d = *dptr;
/* Re-packing like this is a little inefficient, but we don't have to do
this very often at all. */
cell_pack(&packed, cell);
crypto_digest_add_bytes(d, packed.body, sizeof(packed.body));
memset(&packed, 0, sizeof(packed));
}
/** Remember that a variable-length <b>cell</b> has been transmitted (if
* <b>incoming</b> is false) or received (if <b>incoming is true) during a V3
* handshake using <b>state</b>.
*
* (We don't record the cell, but we keep a digest of everything sent or
* received during the v3 handshake, and the client signs it in an
* authenticate cell.)
*/
void
or_handshake_state_record_var_cell(or_handshake_state_t *state,
const var_cell_t *cell,
int incoming)
{
crypto_digest_env_t *d, **dptr;
char buf[VAR_CELL_HEADER_SIZE];
if (incoming) {
if (!state->digest_received_data)
return;
} else {
if (!state->digest_sent_data)
return;
}
dptr = incoming ? &state->digest_received : &state->digest_sent;
if (! *dptr)
*dptr = crypto_new_digest256_env(DIGEST_SHA256);
d = *dptr;
var_cell_pack_header(cell, buf);
crypto_digest_add_bytes(d, buf, sizeof(buf));
crypto_digest_add_bytes(d, (const char *)cell->payload, cell->payload_len);
memset(buf, 0, sizeof(buf));
}
/** Set <b>conn</b>'s state to OR_CONN_STATE_OPEN, and tell other subsystems
* as appropriate. Called when we are done with all TLS and OR handshaking.
*/
@ -1575,6 +1734,9 @@ connection_or_write_cell_to_buf(const cell_t *cell, or_connection_t *conn)
connection_write_to_buf(networkcell.body, CELL_NETWORK_SIZE, TO_CONN(conn));
if (conn->_base.state == OR_CONN_STATE_OR_HANDSHAKING_V3)
or_handshake_state_record_cell(conn->handshake_state, cell, 0);
if (cell->command != CELL_PADDING)
conn->timestamp_last_added_nonpadding = approx_time();
}
@ -1594,6 +1756,8 @@ connection_or_write_var_cell_to_buf(const var_cell_t *cell,
connection_write_to_buf(hdr, sizeof(hdr), TO_CONN(conn));
connection_write_to_buf((char*)cell->payload,
cell->payload_len, TO_CONN(conn));
if (conn->_base.state == OR_CONN_STATE_OR_HANDSHAKING_V3)
or_handshake_state_record_var_cell(conn->handshake_state, cell, 0);
if (cell->command != CELL_PADDING)
conn->timestamp_last_added_nonpadding = approx_time();
}
@ -1678,7 +1842,7 @@ connection_or_send_destroy(circid_t circ_id, or_connection_t *conn, int reason)
}
/** Array of recognized link protocol versions. */
static const uint16_t or_protocol_versions[] = { 1, 2 };
static const uint16_t or_protocol_versions[] = { 1, 2, 3 };
/** Number of versions in <b>or_protocol_versions</b>. */
static const int n_or_protocol_versions =
(int)( sizeof(or_protocol_versions)/sizeof(uint16_t) );
@ -1697,20 +1861,33 @@ is_or_protocol_version_known(uint16_t v)
}
/** Send a VERSIONS cell on <b>conn</b>, telling the other host about the
* link protocol versions that this Tor can support. */
static int
connection_or_send_versions(or_connection_t *conn)
* link protocol versions that this Tor can support.
*
* If <b>v3_plus</b>, this is part of a V3 protocol handshake, so only
* allow protocol version v3 or later. If not <b>v3_plus</b>, this is
* not part of a v3 protocol handshake, so don't allow protocol v3 or
* later.
**/
int
connection_or_send_versions(or_connection_t *conn, int v3_plus)
{
var_cell_t *cell;
int i;
int n_versions = 0;
const int min_version = v3_plus ? 3 : 0;
const int max_version = v3_plus ? UINT16_MAX : 2;
tor_assert(conn->handshake_state &&
!conn->handshake_state->sent_versions_at);
cell = var_cell_new(n_or_protocol_versions * 2);
cell->command = CELL_VERSIONS;
for (i = 0; i < n_or_protocol_versions; ++i) {
uint16_t v = or_protocol_versions[i];
set_uint16(cell->payload+(2*i), htons(v));
if (v < min_version || v > max_version)
continue;
set_uint16(cell->payload+(2*n_versions), htons(v));
++n_versions;
}
cell->payload_len = n_versions * 2;
connection_or_write_var_cell_to_buf(cell, conn);
conn->handshake_state->sent_versions_at = time(NULL);
@ -1730,6 +1907,8 @@ connection_or_send_netinfo(or_connection_t *conn)
int len;
uint8_t *out;
tor_assert(conn->handshake_state);
memset(&cell, 0, sizeof(cell_t));
cell.command = CELL_NETINFO;
@ -1756,8 +1935,284 @@ connection_or_send_netinfo(or_connection_t *conn)
*out = 0;
}
conn->handshake_state->digest_sent_data = 0;
connection_or_write_cell_to_buf(&cell, conn);
return 0;
}
/** Send a CERT cell on the connection <b>conn</b>. Return 0 on success, -1
* on failure. */
int
connection_or_send_cert_cell(or_connection_t *conn)
{
const tor_cert_t *link_cert = NULL, *id_cert = NULL;
const uint8_t *link_encoded = NULL, *id_encoded = NULL;
size_t link_len, id_len;
var_cell_t *cell;
size_t cell_len;
ssize_t pos;
int server_mode;
tor_assert(conn->_base.state == OR_CONN_STATE_OR_HANDSHAKING_V3);
if (! conn->handshake_state)
return -1;
server_mode = ! conn->handshake_state->started_here;
if (tor_tls_get_my_certs(server_mode, &link_cert, &id_cert) < 0)
return -1;
tor_cert_get_der(link_cert, &link_encoded, &link_len);
tor_cert_get_der(id_cert, &id_encoded, &id_len);
cell_len = 1 /* 1 octet: num certs in cell */ +
2 * ( 1 + 2 ) /* For each cert: 1 octet for type, 2 for length */ +
link_len + id_len;
cell = var_cell_new(cell_len);
cell->command = CELL_CERT;
cell->payload[0] = 2;
pos = 1;
if (server_mode)
cell->payload[pos] = OR_CERT_TYPE_TLS_LINK; /* Link cert */
else
cell->payload[pos] = OR_CERT_TYPE_AUTH_1024; /* client authentication */
set_uint16(&cell->payload[pos+1], htons(link_len));
memcpy(&cell->payload[pos+3], link_encoded, link_len);
pos += 3 + link_len;
cell->payload[pos] = OR_CERT_TYPE_ID_1024; /* ID cert */
set_uint16(&cell->payload[pos+1], htons(id_len));
memcpy(&cell->payload[pos+3], id_encoded, id_len);
pos += 3 + id_len;
tor_assert(pos == (int)cell_len); /* Otherwise we just smashed the heap */
connection_or_write_var_cell_to_buf(cell, conn);
var_cell_free(cell);
return 0;
}
/** Send an AUTH_CHALLENGE cell on the connection <b>conn</b>. Return 0
* on success, -1 on failure. */
int
connection_or_send_auth_challenge_cell(or_connection_t *conn)
{
var_cell_t *cell;
uint8_t *cp;
uint8_t challenge[OR_AUTH_CHALLENGE_LEN];
tor_assert(conn->_base.state == OR_CONN_STATE_OR_HANDSHAKING_V3);
if (! conn->handshake_state)
return -1;
if (crypto_rand((char*)challenge, OR_AUTH_CHALLENGE_LEN) < 0)
return -1;
cell = var_cell_new(OR_AUTH_CHALLENGE_LEN + 4);
cell->command = CELL_AUTH_CHALLENGE;
memcpy(cell->payload, challenge, OR_AUTH_CHALLENGE_LEN);
cp = cell->payload + OR_AUTH_CHALLENGE_LEN;
set_uint16(cp, htons(1)); /* We recognize one authentication type. */
set_uint16(cp+2, htons(AUTHTYPE_RSA_SHA256_TLSSECRET));
connection_or_write_var_cell_to_buf(cell, conn);
var_cell_free(cell);
memset(challenge, 0, sizeof(challenge));
return 0;
}
/** Compute the main body of an AUTHENTICATE cell that a client can use
* to authenticate itself on a v3 handshake for <b>conn</b>. Write it to the
* <b>outlen</b>-byte buffer at <b>out</b>.
*
* If <b>server</b> is true, only calculate the first
* V3_AUTH_FIXED_PART_LEN bytes -- the part of the authenticator that's
* determined by the rest of the handshake, and which match the provided value
* exactly.
*
* If <b>server</b> is false and <b>signing_key</b> is NULL, calculate the
* first V3_AUTH_BODY_LEN bytes of the authenticator (that is, everything
* that should be signed), but don't actually sign it.
*
* If <b>server</b> is false and <b>signing_key</b> is provided, calculate the
* entire authenticator, signed with <b>signing_key</b>.
* DOCDOC return value
*/
int
connection_or_compute_authenticate_cell_body(or_connection_t *conn,
uint8_t *out, size_t outlen,
crypto_pk_env_t *signing_key,
int server)
{
uint8_t *ptr;
/* assert state is reasonable XXXX */
if (outlen < V3_AUTH_FIXED_PART_LEN ||
(!server && outlen < V3_AUTH_BODY_LEN))
return -1;
ptr = out;
/* Type: 8 bytes. */
memcpy(ptr, "AUTH0001", 8);
ptr += 8;
{
const tor_cert_t *id_cert=NULL, *link_cert=NULL;
const digests_t *my_digests, *their_digests;
const uint8_t *my_id, *their_id, *client_id, *server_id;
if (tor_tls_get_my_certs(0, &link_cert, &id_cert))
return -1;
my_digests = tor_cert_get_id_digests(id_cert);
their_digests = tor_cert_get_id_digests(conn->handshake_state->id_cert);
tor_assert(my_digests);
tor_assert(their_digests);
my_id = (uint8_t*)my_digests->d[DIGEST_SHA256];
their_id = (uint8_t*)their_digests->d[DIGEST_SHA256];
client_id = server ? their_id : my_id;
server_id = server ? my_id : their_id;
/* Client ID digest: 32 octets. */
memcpy(ptr, client_id, 32);
ptr += 32;
/* Server ID digest: 32 octets. */
memcpy(ptr, server_id, 32);
ptr += 32;
}
{
crypto_digest_env_t *server_d, *client_d;
if (server) {
server_d = conn->handshake_state->digest_sent;
client_d = conn->handshake_state->digest_received;
} else {
client_d = conn->handshake_state->digest_sent;
server_d = conn->handshake_state->digest_received;
}
/* Server log digest : 32 octets */
crypto_digest_get_digest(server_d, (char*)ptr, 32);
ptr += 32;
/* Client log digest : 32 octets */
crypto_digest_get_digest(client_d, (char*)ptr, 32);
ptr += 32;
}
{
/* Digest of cert used on TLS link : 32 octets. */
const tor_cert_t *cert = NULL;
tor_cert_t *freecert = NULL;
if (server) {
tor_tls_get_my_certs(1, &cert, NULL);
} else {
freecert = tor_tls_get_peer_cert(conn->tls);
cert = freecert;
}
if (!cert)
return -1;
memcpy(ptr, tor_cert_get_cert_digests(cert)->d[DIGEST_SHA256], 32);
if (freecert)
tor_cert_free(freecert);
ptr += 32;
}
/* HMAC of clientrandom and serverrandom using master key : 32 octets */
tor_tls_get_tlssecrets(conn->tls, ptr);
ptr += 32;
tor_assert(ptr - out == V3_AUTH_FIXED_PART_LEN);
if (server)
return V3_AUTH_FIXED_PART_LEN; // ptr-out
/* Time: 8 octets. */
{
uint64_t now = time(NULL);
if ((time_t)now < 0)
return -1;
set_uint32(ptr, htonl((uint32_t)(now>>32)));
set_uint32(ptr+4, htonl((uint32_t)now));
ptr += 8;
}
/* Nonce: 16 octets. */
crypto_rand((char*)ptr, 16);
ptr += 16;
tor_assert(ptr - out == V3_AUTH_BODY_LEN);
if (!signing_key)
return V3_AUTH_BODY_LEN; // ptr - out
{
int siglen;
char d[32];
crypto_digest256(d, (char*)out, ptr-out, DIGEST_SHA256);
siglen = crypto_pk_private_sign(signing_key,
(char*)ptr, outlen - (ptr-out),
d, 32);
if (siglen < 0)
return -1;
ptr += siglen;
tor_assert(ptr <= out+outlen);
return (int)(ptr - out);
}
}
/** Send an AUTHENTICATE cell on the connection <b>conn</b>. Return 0 on
* success, -1 on failure */
int
connection_or_send_authenticate_cell(or_connection_t *conn, int authtype)
{
var_cell_t *cell;
crypto_pk_env_t *pk = tor_tls_get_my_client_auth_key();
int authlen;
size_t cell_maxlen;
/* XXXX make sure we're actually supposed to send this! */
if (!pk) {
log_warn(LD_BUG, "Unable to compute authenticate cell: no client auth key");
return -1;
}
if (authtype != AUTHTYPE_RSA_SHA256_TLSSECRET) {
log_warn(LD_BUG, "Tried to send authenticate cell with unknown "
"authentication type %d", authtype);
return -1;
}
cell_maxlen = 4 + /* overhead */
V3_AUTH_BODY_LEN + /* Authentication body */
crypto_pk_keysize(pk) + /* Max signature length */
16 /* add a few extra bytes just in case. */;
cell = var_cell_new(cell_maxlen);
cell->command = CELL_AUTHENTICATE;
set_uint16(cell->payload, htons(AUTHTYPE_RSA_SHA256_TLSSECRET));
/* skip over length ; we don't know that yet. */
authlen = connection_or_compute_authenticate_cell_body(conn,
cell->payload+4,
cell_maxlen-4,
pk,
0 /* not server */);
if (authlen < 0) {
log_warn(LD_BUG, "Unable to compute authenticate cell!");
var_cell_free(cell);
return -1;
}
tor_assert(authlen + 4 <= cell->payload_len);
set_uint16(cell->payload+2, htons(authlen));
cell->payload_len = authlen + 4;
connection_or_write_var_cell_to_buf(cell, conn);
var_cell_free(cell);
return 0;
}

View File

@ -41,7 +41,23 @@ void connection_or_report_broken_states(int severity, int domain);
int connection_tls_start_handshake(or_connection_t *conn, int receiving);
int connection_tls_continue_handshake(or_connection_t *conn);
int connection_init_or_handshake_state(or_connection_t *conn, int started_here);
void connection_or_init_conn_from_address(or_connection_t *conn,
const tor_addr_t *addr, uint16_t port,
const char *id_digest,
int started_here);
int connection_or_client_learned_peer_id(or_connection_t *conn,
const uint8_t *peer_id);
void connection_or_set_circid_type(or_connection_t *conn,
crypto_pk_env_t *identity_rcvd);
void or_handshake_state_free(or_handshake_state_t *state);
void or_handshake_state_record_cell(or_handshake_state_t *state,
const cell_t *cell,
int incoming);
void or_handshake_state_record_var_cell(or_handshake_state_t *state,
const var_cell_t *cell,
int incoming);
int connection_or_set_state_open(or_connection_t *conn);
void connection_or_write_cell_to_buf(const cell_t *cell,
or_connection_t *conn);
@ -49,7 +65,16 @@ void connection_or_write_var_cell_to_buf(const var_cell_t *cell,
or_connection_t *conn);
int connection_or_send_destroy(circid_t circ_id, or_connection_t *conn,
int reason);
int connection_or_send_versions(or_connection_t *conn, int v3_plus);
int connection_or_send_netinfo(or_connection_t *conn);
int connection_or_send_cert_cell(or_connection_t *conn);
int connection_or_send_auth_challenge_cell(or_connection_t *conn);
int connection_or_compute_authenticate_cell_body(or_connection_t *conn,
uint8_t *out, size_t outlen,
crypto_pk_env_t *signing_key,
int server);
int connection_or_send_authenticate_cell(or_connection_t *conn, int type);
int is_or_protocol_version_known(uint16_t version);
void cell_pack(packed_cell_t *dest, const cell_t *src);

View File

@ -283,22 +283,27 @@ typedef enum {
#define OR_CONN_STATE_CONNECTING 1
/** State for a connection to an OR: waiting for proxy handshake to complete */
#define OR_CONN_STATE_PROXY_HANDSHAKING 2
/** State for a connection to an OR or client: SSL is handshaking, not done
/** State for an OR connection client: SSL is handshaking, not done
* yet. */
#define OR_CONN_STATE_TLS_HANDSHAKING 3
/** State for a connection to an OR: We're doing a second SSL handshake for
* renegotiation purposes. */
* renegotiation purposes. (V2 handshake only.) */
#define OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING 4
/** State for a connection at an OR: We're waiting for the client to
* renegotiate. */
* renegotiate (to indicate a v2 handshake) or send a versions cell (to
* indicate a v3 handshake) */
#define OR_CONN_STATE_TLS_SERVER_RENEGOTIATING 5
/** State for a connection to an OR: We're done with our SSL handshake, but we
* haven't yet negotiated link protocol versions and sent a netinfo cell.
*/
#define OR_CONN_STATE_OR_HANDSHAKING 6
/** State for a connection to an OR: Ready to send/receive cells. */
#define OR_CONN_STATE_OPEN 7
#define _OR_CONN_STATE_MAX 7
/** State for an OR connection: We're done with our SSL handshake, we've done
* renegotiation, but we haven't yet negotiated link protocol versions and
* sent a netinfo cell. */
#define OR_CONN_STATE_OR_HANDSHAKING_V2 6
/** State for an OR connection: We're done with our SSL handshake, but we
* haven't yet negotiated link protocol versions, done a V3 handshake, and
* sent a netinfo cell. */
#define OR_CONN_STATE_OR_HANDSHAKING_V3 7
/** State for an OR connection: Ready to send/receive cells. */
#define OR_CONN_STATE_OPEN 8
#define _OR_CONN_STATE_MAX 8
#define _EXIT_CONN_STATE_MIN 1
/** State for an exit connection: waiting for response from DNS farm. */
@ -820,9 +825,10 @@ typedef enum {
#define CELL_NETINFO 8
#define CELL_RELAY_EARLY 9
/** True iff the cell command <b>x</b> is one that implies a variable-length
* cell. */
#define CELL_COMMAND_IS_VAR_LENGTH(x) ((x) == CELL_VERSIONS)
#define CELL_VPADDING 128
#define CELL_CERT 129
#define CELL_AUTH_CHALLENGE 130
#define CELL_AUTHENTICATE 131
/** How long to test reachability before complaining to the user. */
#define TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT (20*60)
@ -1078,7 +1084,47 @@ typedef struct listener_connection_t {
} listener_connection_t;
/** Stores flags and information related to the portion of a v2 Tor OR
/** Minimum length of the random part of an AUTH_CHALLENGE cell. */
#define OR_AUTH_CHALLENGE_LEN 32
/**
* @name Certificate types for CERT cells.
*
* These values are defined by the protocol, and affect how an X509
* certificate in a CERT cell is interpreted and used.
*
* @{ */
/** A certificate that authenticates a TLS link key. The subject key
* must match the key used in the TLS handshake; it must be signed by
* the identity key. */
#define OR_CERT_TYPE_TLS_LINK 1
/** A self-signed identity certificate. The subject key must be a
* 1024-bit RSA key. */
#define OR_CERT_TYPE_ID_1024 2
/** A certificate that authenticates a key used in an AUTHENTICATE cell
* in the v3 handshake. The subject key must be a 1024-bit RSA key; it
* must be signed by the identity key */
#define OR_CERT_TYPE_AUTH_1024 3
/**@}*/
/** The one currently supported type of AUTHENTICATE cell. It contains
* a bunch of structures signed with an RSA1024 key. The signed
* structures include a HMAC using negotiated TLS secrets, and a digest
* of all cells sent or received before the AUTHENTICATE cell (including
* the random server-generated AUTH_CHALLENGE cell).
*/
#define AUTHTYPE_RSA_SHA256_TLSSECRET 1
/** The length of the part of the AUTHENTICATE cell body that the client and
* server can generate independently (when using RSA_SHA256_TLSSECRET). It
* contains everything except the client's timestamp, the client's randomly
* generated nonce, and the signature. */
#define V3_AUTH_FIXED_PART_LEN (8+(32*6))
/** The length of the part of the AUTHENTICATE cell body that the client
* signs. */
#define V3_AUTH_BODY_LEN (V3_AUTH_FIXED_PART_LEN + 8 + 16)
/** Stores flags and information related to the portion of a v2/v3 Tor OR
* connection handshake that happens after the TLS handshake is finished.
*/
typedef struct or_handshake_state_t {
@ -1089,6 +1135,52 @@ typedef struct or_handshake_state_t {
unsigned int started_here : 1;
/** True iff we have received and processed a VERSIONS cell. */
unsigned int received_versions : 1;
/** True iff we have received and processed an AUTH_CHALLENGE cell */
unsigned int received_auth_challenge : 1;
/** True iff we have received and processed a CERT cell. */
unsigned int received_cert_cell : 1;
/** True iff we have received and processed an AUTHENTICATE cell */
unsigned int received_authenticate : 1;
/* True iff we've received valid authentication to some identity. */
unsigned int authenticated : 1;
/** True iff we should feed outgoing cells into digest_sent and
* digest_received respectively.
*
* From the server's side of the v3 handshake, we want to capture everything
* from the VERSIONS cell through and including the AUTH_CHALLENGE cell.
* From the client's, we want to capture everything from the VERSIONS cell
* through but *not* including the AUTHENTICATE cell.
*
* @{ */
unsigned int digest_sent_data : 1;
unsigned int digest_received_data : 1;
/**@}*/
/** Identity digest that we have received and authenticated for our peer
* on this connection. */
uint8_t authenticated_peer_id[DIGEST_LEN];
/** Digests of the cells that we have sent or received as part of a V3
* handshake. Used for making and checking AUTHENTICATE cells.
*
* @{
*/
crypto_digest_env_t *digest_sent;
crypto_digest_env_t *digest_received;
/** @} */
/** Certificates that a connection initiator sent us in a CERT cell; we're
* holding on to them until we get an AUTHENTICATE cell.
*
* @{
*/
/** The cert for the key that's supposed to sign the AUTHENTICATE cell */
tor_cert_t *auth_cert;
/** A self-signed identity certificate */
tor_cert_t *id_cert;
/**@}*/
} or_handshake_state_t;
/** Subtype of connection_t for an "OR connection" -- that is, one that speaks

View File

@ -231,7 +231,7 @@ test_crypto_sha(void)
{
crypto_digest_env_t *d1 = NULL, *d2 = NULL;
int i;
char key[80];
char key[160];
char digest[32];
char data[50];
char d_out1[DIGEST_LEN], d_out2[DIGEST256_LEN];
@ -276,6 +276,75 @@ test_crypto_sha(void)
test_streq(hex_str(digest, 20),
"AA4AE5E15272D00E95705637CE8A3B55ED402112");
/* Test HMAC-SHA256 with test cases from wikipedia and RFC 4231 */
/* Case empty (wikipedia) */
crypto_hmac_sha256(digest, "", 0, "", 0);
test_streq(hex_str(digest, 32),
"B613679A0814D9EC772F95D778C35FC5FF1697C493715653C6C712144292C5AD");
/* Case quick-brown (wikipedia) */
crypto_hmac_sha256(digest, "key", 3,
"The quick brown fox jumps over the lazy dog", 43);
test_streq(hex_str(digest, 32),
"F7BC83F430538424B13298E6AA6FB143EF4D59A14946175997479DBC2D1A3CD8");
/* "Test Case 1" from RFC 4231 */
memset(key, 0x0b, 20);
crypto_hmac_sha256(digest, key, 20, "Hi There", 8);
test_memeq_hex(digest,
"b0344c61d8db38535ca8afceaf0bf12b"
"881dc200c9833da726e9376c2e32cff7");
/* "Test Case 2" from RFC 4231 */
memset(key, 0x0b, 20);
crypto_hmac_sha256(digest, "Jefe", 4, "what do ya want for nothing?", 28);
test_memeq_hex(digest,
"5bdcc146bf60754e6a042426089575c7"
"5a003f089d2739839dec58b964ec3843");
/* "Test case 3" from RFC 4231 */
memset(key, 0xaa, 20);
memset(data, 0xdd, 50);
crypto_hmac_sha256(digest, key, 20, data, 50);
test_memeq_hex(digest,
"773ea91e36800e46854db8ebd09181a7"
"2959098b3ef8c122d9635514ced565fe");
/* "Test case 4" from RFC 4231 */
base16_decode(key, 25,
"0102030405060708090a0b0c0d0e0f10111213141516171819", 50);
memset(data, 0xcd, 50);
crypto_hmac_sha256(digest, key, 25, data, 50);
test_memeq_hex(digest,
"82558a389a443c0ea4cc819899f2083a"
"85f0faa3e578f8077a2e3ff46729665b");
/* "Test case 5" from RFC 4231 */
memset(key, 0x0c, 20);
crypto_hmac_sha256(digest, key, 20, "Test With Truncation", 20);
test_memeq_hex(digest,
"a3b6167473100ee06e0c796c2955552b");
/* "Test case 6" from RFC 4231 */
memset(key, 0xaa, 131);
crypto_hmac_sha256(digest, key, 131,
"Test Using Larger Than Block-Size Key - Hash Key First",
54);
test_memeq_hex(digest,
"60e431591ee0b67f0d8a26aacbf5b77f"
"8e0bc6213728c5140546040f0ee37f54");
/* "Test case 7" from RFC 4231 */
memset(key, 0xaa, 131);
crypto_hmac_sha256(digest, key, 131,
"This is a test using a larger than block-size key and a "
"larger than block-size data. The key needs to be hashed "
"before being used by the HMAC algorithm.", 152);
test_memeq_hex(digest,
"9b09ffa71b942fcb27635fbcd5b0e944"
"bfdc63644f0713938a7f51535c3a35e2");
/* Incremental digest code. */
d1 = crypto_new_digest_env();
test_assert(d1);