From 81024f43ec3a3ab32683764cb925606bfcb603d7 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 14 Sep 2011 14:44:42 -0400 Subject: [PATCH] Basic function to write authenticate cells Also, tweak the cert cell code to send auth certs --- src/or/connection_or.c | 195 ++++++++++++++++++++++++++++++++++++++++- src/or/connection_or.h | 5 ++ 2 files changed, 197 insertions(+), 3 deletions(-) diff --git a/src/or/connection_or.c b/src/or/connection_or.c index dcb838b277..c72d89d7ce 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -1776,13 +1776,14 @@ connection_or_send_cert_cell(or_connection_t *conn) var_cell_t *cell; size_t cell_len; int pos; + int server_mode; tor_assert(conn->_base.state == OR_CONN_STATE_OR_HANDSHAKING_V3); if (! conn->handshake_state) return -1; - if (tor_tls_get_my_certs(! conn->handshake_state->started_here, - &link_cert, &id_cert) < 0) + 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); @@ -1795,7 +1796,10 @@ connection_or_send_cert_cell(or_connection_t *conn) cell->payload[0] = 2; pos = 1; - cell->payload[pos] = OR_CERT_TYPE_TLS_LINK; /* Link cert */ + 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; @@ -1842,3 +1846,188 @@ connection_or_send_auth_challenge_cell(or_connection_t *conn) return 0; } +/** DOCDOC */ +#define V3_HS_AUTH_FIXED_PART_LEN (8+(32*6)) +#define V3_HS_AUTH_BODY_LEN (V3_HS_AUTH_FIXED_PART_LEN + 8 + 16) + +#define AUTHTYPE_RSA_SHA256_TLSSECRET 1 + +/** Compute the main body of an AUTHENTICATE cell that a client can use + * to authenticate itself on a v3 handshake for conn. Write it to the + * outlen-byte buffer at out. + * + * If server is true, only calculate the first + * V3_HS_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 server is false and signing_key is NULL, calculate the + * first V3_HS_AUTH_BODY_LEN bytes of the authenticator (that is, everything + * that should be signed), but don't actually sign it. + * + * If server is false and signing_key is provided, calculate the + * entire authenticator, signed with signing_key. + */ +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_HS_AUTH_FIXED_PART_LEN || + (!server && outlen < V3_HS_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 uint8_t *my_id, *their_id, *client_id, *server_id; + if (tor_tls_get_my_certs(0, &link_cert, &id_cert)) + return -1; + my_id = (uint8_t*)tor_cert_get_id_digests(id_cert)->d[DIGEST_SHA256]; + their_id = (uint8_t*) + tor_cert_get_id_digests(conn->handshake_state->id_cert)->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_HS_AUTH_FIXED_PART_LEN); + + if (server) + return 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_HS_AUTH_BODY_LEN); + + if (!signing_key) + return 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 ptr - out; + } +} + +/** Send an AUTHENTICATE cell on the connection conn. Return 0 on + * success, -1 on failure */ +int +connection_or_send_authenticate_cell(or_connection_t *conn) +{ + var_cell_t *cell; + crypto_pk_env_t *pk = tor_tls_get_my_client_auth_key(); + int authlen; + int cell_maxlen; + /* XXXX make sure we're actually supposed to send this! */ + + if (!pk) + return -1;/*XXXX log*/ + cell_maxlen = 4 + /* overhead */ + V3_HS_AUTH_BODY_LEN + /* Authentication body */ + crypto_pk_keysize(pk) + /* Max signature length */ + 16 /* just in case XXXX */ ; + + cell = var_cell_new(cell_maxlen); + 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) { + /* XXXX log */ + 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; +} diff --git a/src/or/connection_or.h b/src/or/connection_or.h index 6e50e29e40..ba441c45df 100644 --- a/src/or/connection_or.h +++ b/src/or/connection_or.h @@ -52,6 +52,11 @@ int connection_or_send_destroy(circid_t circ_id, or_connection_t *conn, 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 is_or_protocol_version_known(uint16_t version);