/** Construct and return an RSA signature for the TAP onion key to * cross-certify the RSA and Ed25519 identity keys. Set <b>len_out</b> to its * length. */ uint8_t * make_tap_onion_key_crosscert(const crypto_pk_t *onion_key, const ed25519_public_key_t *master_id_key, const crypto_pk_t *rsa_id_key, int *len_out) { uint8_t signature[PK_BYTES]; uint8_t signed_data[DIGEST_LEN + ED25519_PUBKEY_LEN]; *len_out = 0; if (crypto_pk_get_digest(rsa_id_key, (char*)signed_data) < 0) { return NULL; } memcpy(signed_data + DIGEST_LEN, master_id_key->pubkey, ED25519_PUBKEY_LEN); int r = crypto_pk_private_sign(onion_key, (char*)signature, sizeof(signature), (const char*)signed_data, sizeof(signed_data)); if (r < 0) return NULL; *len_out = r; return tor_memdup(signature, r); }
/** Set <b>out</b> to the hex-encoded fingerprint of <b>pkey</b>. */ static int get_digest(EVP_PKEY *pkey, char *out) { int r = 1; crypto_pk_t *pk = _crypto_new_pk_from_rsa(EVP_PKEY_get1_RSA(pkey)); if (pk) { r = crypto_pk_get_digest(pk, out); crypto_pk_free(pk); } return r; }
/** Given a private or public key <b>pk</b>, put a hashed fingerprint of * the public key into <b>fp_out</b> (must have at least FINGERPRINT_LEN+1 * bytes of space). Return 0 on success, -1 on failure. * * Hashed fingerprints are computed as the SHA1 digest of the SHA1 digest * of the ASN.1 encoding of the public key, converted to hexadecimal, in * upper case. */ int crypto_pk_get_hashed_fingerprint(crypto_pk_t *pk, char *fp_out) { char digest[DIGEST_LEN], hashed_digest[DIGEST_LEN]; if (crypto_pk_get_digest(pk, digest)) { return -1; } if (crypto_digest(hashed_digest, digest, DIGEST_LEN)) { return -1; } base16_encode(fp_out, FINGERPRINT_LEN + 1, hashed_digest, DIGEST_LEN); return 0; }
static ssize_t make_intro_from_plaintext( void *buf, size_t len, crypto_pk_t *key, void **cell_out) { char *cell = NULL; ssize_t cell_len = -1, r; /* Assemble key digest and ciphertext, then construct the cell */ ssize_t ciphertext_size; if (!(buf && key && len > 0 && cell_out)) goto done; /* * Figure out an upper bound on how big the ciphertext will be * (see crypto_pk_public_hybrid_encrypt()) */ ciphertext_size = PKCS1_OAEP_PADDING_OVERHEAD; ciphertext_size += crypto_pk_keysize(key); ciphertext_size += CIPHER_KEY_LEN; ciphertext_size += len; /* * Allocate space for the cell */ memmgr_init_check_shared_mem(SHARED_SIZE, UIO_DEVICE, BASE_ADDRESS); // cell = tor_malloc(DIGEST_LEN + ciphertext_size); cell = memmgr_alloc(DIGEST_LEN + ciphertext_size); memmgr_assert(buf); /* Compute key digest (will be first DIGEST_LEN octets of cell) */ r = crypto_pk_get_digest(key, cell); tt_assert(r >= 0); /* Do encryption */ r = crypto_pk_public_hybrid_encrypt( key, cell + DIGEST_LEN, ciphertext_size, buf, len, PK_PKCS1_OAEP_PADDING, 0); tt_assert(r >= 0); /* Figure out cell length */ cell_len = DIGEST_LEN + r; /* Output the cell */ *cell_out = cell; done: // memmgr_free(cell); return cell_len; }
/** Given a private or public key <b>pk</b>, put a fingerprint of the * public key into <b>fp_out</b> (must have at least FINGERPRINT_LEN+1 bytes of * space). Return 0 on success, -1 on failure. * * Fingerprints are computed as the SHA1 digest of the ASN.1 encoding * of the public key, converted to hexadecimal, in upper case, with a * space after every four digits. * * If <b>add_space</b> is false, omit the spaces. */ int crypto_pk_get_fingerprint(crypto_pk_t *pk, char *fp_out, int add_space) { char digest[DIGEST_LEN]; char hexdigest[HEX_DIGEST_LEN+1]; if (crypto_pk_get_digest(pk, digest)) { return -1; } base16_encode(hexdigest,sizeof(hexdigest),digest,DIGEST_LEN); if (add_space) { crypto_add_spaces_to_fp(fp_out, FINGERPRINT_LEN+1, hexdigest); } else { sgx_strncpy(fp_out, hexdigest, HEX_DIGEST_LEN+1); } return 0; }
/** See dir_common_construct_vote_1. * Produces a vote with slightly different values. */ int dir_common_construct_vote_2(networkstatus_t **vote, authority_cert_t *cert, crypto_pk_t *sign_skey, vote_routerstatus_t * (*vrs_gen)(int idx, time_t now), networkstatus_t **vote_out, int *n_vrs, time_t now, int clear_rl) { networkstatus_voter_info_t *voter; dir_common_setup_vote(vote, now); (*vote)->type = NS_TYPE_VOTE; (*vote)->published += 1; (*vote)->valid_after = now+1000; (*vote)->fresh_until = now+3005; (*vote)->valid_until = now+3000; (*vote)->vote_seconds = 100; (*vote)->dist_seconds = 300; smartlist_split_string((*vote)->supported_methods, "1 2 3", NULL, 0, -1); smartlist_split_string((*vote)->known_flags, "Authority Exit Fast Guard MadeOfCheese MadeOfTin " "Running Stable V2Dir Valid", 0, SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); voter = tor_malloc_zero(sizeof(networkstatus_voter_info_t)); voter->nickname = tor_strdup("Voter2"); voter->address = tor_strdup("2.3.4.5"); voter->addr = 0x02030405; voter->dir_port = 80; voter->or_port = 9000; voter->contact = tor_strdup("*****@*****.**"); crypto_pk_get_digest(cert->identity_key, voter->identity_digest); /* * Set up a vote; generate it; try to parse it. */ smartlist_add((*vote)->voters, voter); (*vote)->cert = authority_cert_dup(cert); if (! (*vote)->net_params) (*vote)->net_params = smartlist_new(); smartlist_split_string((*vote)->net_params, "bar=2000000000 circuitwindow=20", NULL, 0, 0); /* add routerstatuses */ /* dump the vote and try to parse it. */ dir_common_add_rs_and_parse(*vote, vote_out, vrs_gen, sign_skey, n_vrs, now, clear_rl); return 0; }
/* For a given circuit and a service introduction point object, register the * intro circuit to the circuitmap. This supports legacy intro point. */ static void register_intro_circ(const hs_service_intro_point_t *ip, origin_circuit_t *circ) { tor_assert(ip); tor_assert(circ); if (ip->base.is_only_legacy) { uint8_t digest[DIGEST_LEN]; if (BUG(crypto_pk_get_digest(ip->legacy_key, (char *) digest) < 0)) { return; } hs_circuitmap_register_intro_circ_v2_service_side(circ, digest); } else { hs_circuitmap_register_intro_circ_v3_service_side(circ, &ip->auth_key_kp.pubkey); } }
/** See dir_common_construct_vote_1. * Produces a vote with slightly different values. Adds a legacy key. */ int dir_common_construct_vote_3(networkstatus_t **vote, authority_cert_t *cert, crypto_pk_t *sign_skey, vote_routerstatus_t * (*vrs_gen)(int idx, time_t now), networkstatus_t **vote_out, int *n_vrs, time_t now, int clear_rl) { networkstatus_voter_info_t *voter; dir_common_setup_vote(vote, now); (*vote)->valid_after = now+1000; (*vote)->fresh_until = now+2003; (*vote)->valid_until = now+3000; (*vote)->vote_seconds = 100; (*vote)->dist_seconds = 250; smartlist_split_string((*vote)->supported_methods, "1 2 3 4", NULL, 0, -1); (*vote)->client_versions = tor_strdup("0.1.2.14,0.1.2.17"); (*vote)->server_versions = tor_strdup("0.1.2.10,0.1.2.15,0.1.2.16"); smartlist_split_string((*vote)->known_flags, "Authority Exit Fast Guard Running Stable V2Dir Valid", 0, SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); voter = tor_malloc_zero(sizeof(networkstatus_voter_info_t)); voter->nickname = tor_strdup("Voter2"); voter->address = tor_strdup("3.4.5.6"); voter->addr = 0x03040506; voter->dir_port = 80; voter->or_port = 9000; voter->contact = tor_strdup("*****@*****.**"); crypto_pk_get_digest(cert->identity_key, voter->identity_digest); memset(voter->legacy_id_digest, (int)'A', DIGEST_LEN); /* * Set up a vote; generate it; try to parse it. */ smartlist_add((*vote)->voters, voter); (*vote)->cert = authority_cert_dup(cert); smartlist_split_string((*vote)->net_params, "circuitwindow=80 foo=660", NULL, 0, 0); /* add routerstatuses */ /* dump the vote and try to parse it. */ dir_common_add_rs_and_parse(*vote, vote_out, vrs_gen, sign_skey, n_vrs, now, clear_rl); return 0; }
/* Return an introduction point circuit matching the given intro point object. * NULL is returned is no such circuit can be found. */ origin_circuit_t * hs_circ_service_get_intro_circ(const hs_service_intro_point_t *ip) { origin_circuit_t *circ = NULL; tor_assert(ip); if (ip->base.is_only_legacy) { uint8_t digest[DIGEST_LEN]; if (BUG(crypto_pk_get_digest(ip->legacy_key, (char *) digest) < 0)) { goto end; } circ = hs_circuitmap_get_intro_circ_v2_service_side(digest); } else { circ = hs_circuitmap_get_intro_circ_v3_service_side( &ip->auth_key_kp.pubkey); } end: return circ; }
static void test_routerkeys_cross_certify_tap(void *args) { (void)args; uint8_t *cc = NULL; int cc_len; ed25519_public_key_t master_key; crypto_pk_t *onion_key = pk_generate(2), *id_key = pk_generate(1); char digest[20]; char buf[128]; int n; tt_int_op(0, OP_EQ, ed25519_public_from_base64(&master_key, "IAlreadyWroteTestsForRouterdescsUsingTheseX")); cc = make_tap_onion_key_crosscert(onion_key, &master_key, id_key, &cc_len); tt_assert(cc); tt_assert(cc_len); n = crypto_pk_public_checksig(onion_key, buf, sizeof(buf), (char*)cc, cc_len); tt_int_op(n,OP_GT,0); tt_int_op(n,OP_EQ,52); crypto_pk_get_digest(id_key, digest); tt_mem_op(buf,OP_EQ,digest,20); tt_mem_op(buf+20,OP_EQ,master_key.pubkey,32); tt_int_op(0, OP_EQ, check_tap_onion_key_crosscert(cc, cc_len, onion_key, &master_key, (uint8_t*)digest)); done: tor_free(cc); crypto_pk_free(id_key); crypto_pk_free(onion_key); }
/** Respond to an ESTABLISH_INTRO cell by checking the signed data and * setting the circuit's purpose and service pk digest. */ int rend_mid_establish_intro(or_circuit_t *circ, const uint8_t *request, size_t request_len) { crypto_pk_t *pk = NULL; char buf[DIGEST_LEN+9]; char expected_digest[DIGEST_LEN]; char pk_digest[DIGEST_LEN]; size_t asn1len; or_circuit_t *c; char serviceid[REND_SERVICE_ID_LEN_BASE32+1]; int reason = END_CIRC_REASON_INTERNAL; log_info(LD_REND, "Received an ESTABLISH_INTRO request on circuit %u", (unsigned) circ->p_circ_id); if (circ->base_.purpose != CIRCUIT_PURPOSE_OR || circ->base_.n_chan) { log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Rejecting ESTABLISH_INTRO on non-OR or non-edge circuit."); reason = END_CIRC_REASON_TORPROTOCOL; goto err; } if (request_len < 2+DIGEST_LEN) goto truncated; /* First 2 bytes: length of asn1-encoded key. */ asn1len = ntohs(get_uint16(request)); /* Next asn1len bytes: asn1-encoded key. */ if (request_len < 2+DIGEST_LEN+asn1len) goto truncated; pk = crypto_pk_asn1_decode((char*)(request+2), asn1len); if (!pk) { reason = END_CIRC_REASON_TORPROTOCOL; log_warn(LD_PROTOCOL, "Couldn't decode public key."); goto err; } /* Next 20 bytes: Hash of rend_circ_nonce | "INTRODUCE" */ memcpy(buf, circ->rend_circ_nonce, DIGEST_LEN); memcpy(buf+DIGEST_LEN, "INTRODUCE", 9); if (crypto_digest(expected_digest, buf, DIGEST_LEN+9) < 0) { log_warn(LD_BUG, "Internal error computing digest."); goto err; } if (tor_memneq(expected_digest, request+2+asn1len, DIGEST_LEN)) { log_warn(LD_PROTOCOL, "Hash of session info was not as expected."); reason = END_CIRC_REASON_TORPROTOCOL; goto err; } /* Rest of body: signature of previous data */ note_crypto_pk_op(REND_MID); if (crypto_pk_public_checksig_digest(pk, (char*)request, 2+asn1len+DIGEST_LEN, (char*)(request+2+DIGEST_LEN+asn1len), request_len-(2+DIGEST_LEN+asn1len))<0) { log_warn(LD_PROTOCOL, "Incorrect signature on ESTABLISH_INTRO cell; rejecting."); reason = END_CIRC_REASON_TORPROTOCOL; goto err; } /* The request is valid. First, compute the hash of Bob's PK.*/ if (crypto_pk_get_digest(pk, pk_digest)<0) { log_warn(LD_BUG, "Internal error: couldn't hash public key."); goto err; } crypto_pk_free(pk); /* don't need it anymore */ pk = NULL; /* so we don't free it again if err */ base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1, pk_digest, REND_SERVICE_ID_LEN); /* Close any other intro circuits with the same pk. */ c = NULL; while ((c = circuit_get_intro_point((const uint8_t *)pk_digest))) { log_info(LD_REND, "Replacing old circuit for service %s", safe_str(serviceid)); circuit_mark_for_close(TO_CIRCUIT(c), END_CIRC_REASON_FINISHED); /* Now it's marked, and it won't be returned next time. */ } /* Acknowledge the request. */ if (relay_send_command_from_edge(0, TO_CIRCUIT(circ), RELAY_COMMAND_INTRO_ESTABLISHED, "", 0, NULL)<0) { log_info(LD_GENERAL, "Couldn't send INTRO_ESTABLISHED cell."); goto err; } /* Now, set up this circuit. */ circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_INTRO_POINT); circuit_set_intro_point_digest(circ, (uint8_t *)pk_digest); log_info(LD_REND, "Established introduction point on circuit %u for service %s", (unsigned) circ->p_circ_id, safe_str(serviceid)); return 0; truncated: log_warn(LD_PROTOCOL, "Rejecting truncated ESTABLISH_INTRO cell."); reason = END_CIRC_REASON_TORPROTOCOL; err: if (pk) crypto_pk_free(pk); circuit_mark_for_close(TO_CIRCUIT(circ), reason); return -1; }
/** Based on our interval and our estimated bandwidth, choose a * deterministic (but random-ish) time to wake up. */ static void accounting_set_wakeup_time(void) { char buf[ISO_TIME_LEN+1]; char digest[DIGEST_LEN]; crypto_digest_env_t *d_env; int time_in_interval; uint64_t time_to_exhaust_bw; int time_to_consider; if (! identity_key_is_set()) { if (init_keys() < 0) { log_err(LD_BUG, "Error initializing keys"); tor_assert(0); } } format_iso_time(buf, interval_start_time); crypto_pk_get_digest(get_identity_key(), digest); d_env = crypto_new_digest_env(); crypto_digest_add_bytes(d_env, buf, ISO_TIME_LEN); crypto_digest_add_bytes(d_env, digest, DIGEST_LEN); crypto_digest_get_digest(d_env, digest, DIGEST_LEN); crypto_free_digest_env(d_env); if (!expected_bandwidth_usage) { char buf1[ISO_TIME_LEN+1]; char buf2[ISO_TIME_LEN+1]; format_local_iso_time(buf1, interval_start_time); format_local_iso_time(buf2, interval_end_time); time_to_exhaust_bw = GUESS_TIME_TO_USE_BANDWIDTH; interval_wakeup_time = interval_start_time; log_notice(LD_ACCT, "Configured hibernation. This interval begins at %s " "and ends at %s. We have no prior estimate for bandwidth, so " "we will start out awake and hibernate when we exhaust our quota.", buf1, buf2); return; } time_in_interval = (int)(interval_end_time - interval_start_time); time_to_exhaust_bw = (get_options()->AccountingMax/expected_bandwidth_usage)*60; if (time_to_exhaust_bw > TIME_MAX) { time_to_exhaust_bw = TIME_MAX; time_to_consider = 0; } else { time_to_consider = time_in_interval - (int)time_to_exhaust_bw; } if (time_to_consider<=0) { interval_wakeup_time = interval_start_time; } else { /* XXX can we simplify this just by picking a random (non-deterministic) * time to be up? If we go down and come up, then we pick a new one. Is * that good enough? -RD */ /* This is not a perfectly unbiased conversion, but it is good enough: * in the worst case, the first half of the day is 0.06 percent likelier * to be chosen than the last half. */ interval_wakeup_time = interval_start_time + (get_uint32(digest) % time_to_consider); format_iso_time(buf, interval_wakeup_time); } { char buf1[ISO_TIME_LEN+1]; char buf2[ISO_TIME_LEN+1]; char buf3[ISO_TIME_LEN+1]; char buf4[ISO_TIME_LEN+1]; time_t down_time; if (interval_wakeup_time+time_to_exhaust_bw > TIME_MAX) down_time = TIME_MAX; else down_time = (time_t)(interval_wakeup_time+time_to_exhaust_bw); if (down_time>interval_end_time) down_time = interval_end_time; format_local_iso_time(buf1, interval_start_time); format_local_iso_time(buf2, interval_wakeup_time); format_local_iso_time(buf3, down_time); format_local_iso_time(buf4, interval_end_time); log_notice(LD_ACCT, "Configured hibernation. This interval began at %s; " "the scheduled wake-up time %s %s; " "we expect%s to exhaust our quota for this interval around %s; " "the next interval begins at %s (all times local)", buf1, time(NULL)<interval_wakeup_time?"is":"was", buf2, time(NULL)<down_time?"":"ed", buf3, buf4); } }
/* Test good certs cells */ static void test_link_handshake_certs_ok(void *arg) { (void) arg; or_connection_t *c1 = or_connection_new(CONN_TYPE_OR, AF_INET); or_connection_t *c2 = or_connection_new(CONN_TYPE_OR, AF_INET); var_cell_t *cell1 = NULL, *cell2 = NULL; certs_cell_t *cc1 = NULL, *cc2 = NULL; channel_tls_t *chan1 = NULL, *chan2 = NULL; crypto_pk_t *key1 = NULL, *key2 = NULL; scheduler_init(); MOCK(tor_tls_cert_matches_key, mock_tls_cert_matches_key); MOCK(connection_or_write_var_cell_to_buf, mock_write_var_cell); MOCK(connection_or_send_netinfo, mock_send_netinfo); key1 = pk_generate(2); key2 = pk_generate(3); /* We need to make sure that our TLS certificates are set up before we can * actually generate a CERTS cell. */ tt_int_op(tor_tls_context_init(TOR_TLS_CTX_IS_PUBLIC_SERVER, key1, key2, 86400), ==, 0); c1->base_.state = OR_CONN_STATE_OR_HANDSHAKING_V3; c1->link_proto = 3; tt_int_op(connection_init_or_handshake_state(c1, 1), ==, 0); c2->base_.state = OR_CONN_STATE_OR_HANDSHAKING_V3; c2->link_proto = 3; tt_int_op(connection_init_or_handshake_state(c2, 0), ==, 0); tt_int_op(0, ==, connection_or_send_certs_cell(c1)); tt_assert(mock_got_var_cell); cell1 = mock_got_var_cell; tt_int_op(0, ==, connection_or_send_certs_cell(c2)); tt_assert(mock_got_var_cell); cell2 = mock_got_var_cell; tt_int_op(cell1->command, ==, CELL_CERTS); tt_int_op(cell1->payload_len, >, 1); tt_int_op(cell2->command, ==, CELL_CERTS); tt_int_op(cell2->payload_len, >, 1); tt_int_op(cell1->payload_len, ==, certs_cell_parse(&cc1, cell1->payload, cell1->payload_len)); tt_int_op(cell2->payload_len, ==, certs_cell_parse(&cc2, cell2->payload, cell2->payload_len)); tt_int_op(2, ==, cc1->n_certs); tt_int_op(2, ==, cc2->n_certs); tt_int_op(certs_cell_get_certs(cc1, 0)->cert_type, ==, CERTTYPE_RSA1024_ID_AUTH); tt_int_op(certs_cell_get_certs(cc1, 1)->cert_type, ==, CERTTYPE_RSA1024_ID_ID); tt_int_op(certs_cell_get_certs(cc2, 0)->cert_type, ==, CERTTYPE_RSA1024_ID_LINK); tt_int_op(certs_cell_get_certs(cc2, 1)->cert_type, ==, CERTTYPE_RSA1024_ID_ID); chan1 = tor_malloc_zero(sizeof(*chan1)); channel_tls_common_init(chan1); c1->chan = chan1; chan1->conn = c1; c1->base_.address = tor_strdup("C1"); c1->tls = tor_tls_new(-1, 0); c1->link_proto = 4; c1->base_.conn_array_index = -1; crypto_pk_get_digest(key2, c1->identity_digest); channel_tls_process_certs_cell(cell2, chan1); tt_assert(c1->handshake_state->received_certs_cell); tt_assert(c1->handshake_state->auth_cert == NULL); tt_assert(c1->handshake_state->id_cert); tt_assert(! tor_mem_is_zero( (char*)c1->handshake_state->authenticated_peer_id, 20)); chan2 = tor_malloc_zero(sizeof(*chan2)); channel_tls_common_init(chan2); c2->chan = chan2; chan2->conn = c2; c2->base_.address = tor_strdup("C2"); c2->tls = tor_tls_new(-1, 1); c2->link_proto = 4; c2->base_.conn_array_index = -1; crypto_pk_get_digest(key1, c2->identity_digest); channel_tls_process_certs_cell(cell1, chan2); tt_assert(c2->handshake_state->received_certs_cell); tt_assert(c2->handshake_state->auth_cert); tt_assert(c2->handshake_state->id_cert); tt_assert(tor_mem_is_zero( (char*)c2->handshake_state->authenticated_peer_id, 20)); done: UNMOCK(tor_tls_cert_matches_key); UNMOCK(connection_or_write_var_cell_to_buf); UNMOCK(connection_or_send_netinfo); connection_free_(TO_CONN(c1)); connection_free_(TO_CONN(c2)); tor_free(cell1); tor_free(cell2); certs_cell_free(cc1); certs_cell_free(cc2); if (chan1) circuitmux_free(chan1->base_.cmux); tor_free(chan1); if (chan2) circuitmux_free(chan2->base_.cmux); tor_free(chan2); crypto_pk_free(key1); crypto_pk_free(key2); }
/** Test utility function: checks that the <b>plaintext_len</b>-byte string at * <b>plaintext</b> is at least superficially parseable. */ static void do_parse_test(uint8_t *plaintext, size_t plaintext_len, int phase) { crypto_pk_t *k = NULL; ssize_t r; uint8_t *cell = NULL; size_t cell_len; rend_intro_cell_t *parsed_req = NULL; char *err_msg = NULL; char digest[DIGEST_LEN]; /* Get a key */ k = crypto_pk_new(); tt_assert(k); r = crypto_pk_read_private_key_from_string(k, AUTHORITY_SIGNKEY_1, -1); tt_assert(!r); /* Get digest for future comparison */ r = crypto_pk_get_digest(k, digest); tt_assert(r >= 0); /* Make a cell out of it */ memmgr_init_check_shared_mem(SHARED_SIZE, UIO_DEVICE, BASE_ADDRESS); char* plaintext_buf = memmgr_alloc(plaintext_len); memcpy(plaintext_buf, plaintext, plaintext_len); r = make_intro_from_plaintext( plaintext_buf, plaintext_len, k, (void **)(&cell)); tt_assert(r > 0); tt_assert(cell); cell_len = r; memmgr_free(plaintext_buf); /* Do early parsing */ parsed_req = rend_service_begin_parse_intro(cell, cell_len, 2, &err_msg); tt_assert(parsed_req); tt_assert(!err_msg); tt_mem_op(parsed_req->pk,OP_EQ, digest, DIGEST_LEN); tt_assert(parsed_req->ciphertext); tt_assert(parsed_req->ciphertext_len > 0); if (phase == EARLY_PARSE_ONLY) goto done; printf("\nHere"); /* Do decryption */ r = rend_service_decrypt_intro(parsed_req, k, &err_msg); tt_assert(!r); tt_assert(!err_msg); tt_assert(parsed_req->plaintext); tt_assert(parsed_req->plaintext_len > 0); if (phase == DECRYPT_ONLY) goto done; /* Do late parsing */ r = rend_service_parse_intro_plaintext(parsed_req, &err_msg); tt_assert(!r); tt_assert(!err_msg); tt_assert(parsed_req->parsed); done: // tor_free(cell); memmgr_assert(cell); memmgr_free(cell); crypto_pk_free(k); rend_service_free_intro(parsed_req); tor_free(err_msg); }
/** Parse and validate the ASCII-encoded v2 descriptor in <b>desc</b>, * write the parsed descriptor to the newly allocated *<b>parsed_out</b>, the * binary descriptor ID of length DIGEST_LEN to <b>desc_id_out</b>, the * encrypted introduction points to the newly allocated * *<b>intro_points_encrypted_out</b>, their encrypted size to * *<b>intro_points_encrypted_size_out</b>, the size of the encoded descriptor * to *<b>encoded_size_out</b>, and a pointer to the possibly next * descriptor to *<b>next_out</b>; return 0 for success (including validation) * and -1 for failure. * * If <b>as_hsdir</b> is 1, we're parsing this as an HSDir, and we should * be strict about time formats. */ int rend_parse_v2_service_descriptor(rend_service_descriptor_t **parsed_out, char *desc_id_out, char **intro_points_encrypted_out, size_t *intro_points_encrypted_size_out, size_t *encoded_size_out, const char **next_out, const char *desc, int as_hsdir) { rend_service_descriptor_t *result = tor_malloc_zero(sizeof(rend_service_descriptor_t)); char desc_hash[DIGEST_LEN]; const char *eos; smartlist_t *tokens = smartlist_new(); directory_token_t *tok; char secret_id_part[DIGEST_LEN]; int i, version, num_ok=1; smartlist_t *versions; char public_key_hash[DIGEST_LEN]; char test_desc_id[DIGEST_LEN]; memarea_t *area = NULL; const int strict_time_fmt = as_hsdir; tor_assert(desc); /* Check if desc starts correctly. */ if (strcmpstart(desc, "rendezvous-service-descriptor ")) { log_info(LD_REND, "Descriptor does not start correctly."); goto err; } /* Compute descriptor hash for later validation. */ if (router_get_hash_impl(desc, strlen(desc), desc_hash, "rendezvous-service-descriptor ", "\nsignature", '\n', DIGEST_SHA1) < 0) { log_warn(LD_REND, "Couldn't compute descriptor hash."); goto err; } /* Determine end of string. */ eos = strstr(desc, "\nrendezvous-service-descriptor "); if (!eos) eos = desc + strlen(desc); else eos = eos + 1; /* Check length. */ if (eos-desc > REND_DESC_MAX_SIZE) { /* XXXX+ If we are parsing this descriptor as a server, this * should be a protocol warning. */ log_warn(LD_REND, "Descriptor length is %d which exceeds " "maximum rendezvous descriptor size of %d bytes.", (int)(eos-desc), REND_DESC_MAX_SIZE); goto err; } /* Tokenize descriptor. */ area = memarea_new(); if (tokenize_string(area, desc, eos, tokens, desc_token_table, 0)) { log_warn(LD_REND, "Error tokenizing descriptor."); goto err; } /* Set next to next descriptor, if available. */ *next_out = eos; /* Set length of encoded descriptor. */ *encoded_size_out = eos - desc; /* Check min allowed length of token list. */ if (smartlist_len(tokens) < 7) { log_warn(LD_REND, "Impossibly short descriptor."); goto err; } /* Parse base32-encoded descriptor ID. */ tok = find_by_keyword(tokens, R_RENDEZVOUS_SERVICE_DESCRIPTOR); tor_assert(tok == smartlist_get(tokens, 0)); tor_assert(tok->n_args == 1); if (!rend_valid_descriptor_id(tok->args[0])) { log_warn(LD_REND, "Invalid descriptor ID: '%s'", tok->args[0]); goto err; } if (base32_decode(desc_id_out, DIGEST_LEN, tok->args[0], REND_DESC_ID_V2_LEN_BASE32) < 0) { log_warn(LD_REND, "Descriptor ID contains illegal characters: %s", tok->args[0]); goto err; } /* Parse descriptor version. */ tok = find_by_keyword(tokens, R_VERSION); tor_assert(tok->n_args == 1); result->version = (int) tor_parse_long(tok->args[0], 10, 0, INT_MAX, &num_ok, NULL); if (result->version != 2 || !num_ok) { /* If it's <2, it shouldn't be under this format. If the number * is greater than 2, we bumped it because we broke backward * compatibility. See how version numbers in our other formats * work. */ log_warn(LD_REND, "Unrecognized descriptor version: %s", escaped(tok->args[0])); goto err; } /* Parse public key. */ tok = find_by_keyword(tokens, R_PERMANENT_KEY); result->pk = tok->key; tok->key = NULL; /* Prevent free */ /* Parse secret ID part. */ tok = find_by_keyword(tokens, R_SECRET_ID_PART); tor_assert(tok->n_args == 1); if (strlen(tok->args[0]) != REND_SECRET_ID_PART_LEN_BASE32 || strspn(tok->args[0], BASE32_CHARS) != REND_SECRET_ID_PART_LEN_BASE32) { log_warn(LD_REND, "Invalid secret ID part: '%s'", tok->args[0]); goto err; } if (base32_decode(secret_id_part, DIGEST_LEN, tok->args[0], 32) < 0) { log_warn(LD_REND, "Secret ID part contains illegal characters: %s", tok->args[0]); goto err; } /* Parse publication time -- up-to-date check is done when storing the * descriptor. */ tok = find_by_keyword(tokens, R_PUBLICATION_TIME); tor_assert(tok->n_args == 1); if (parse_iso_time_(tok->args[0], &result->timestamp, strict_time_fmt, 0) < 0) { log_warn(LD_REND, "Invalid publication time: '%s'", tok->args[0]); goto err; } /* Parse protocol versions. */ tok = find_by_keyword(tokens, R_PROTOCOL_VERSIONS); tor_assert(tok->n_args == 1); versions = smartlist_new(); smartlist_split_string(versions, tok->args[0], ",", SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); for (i = 0; i < smartlist_len(versions); i++) { version = (int) tor_parse_long(smartlist_get(versions, i), 10, 0, INT_MAX, &num_ok, NULL); if (!num_ok) /* It's a string; let's ignore it. */ continue; if (version >= REND_PROTOCOL_VERSION_BITMASK_WIDTH) /* Avoid undefined left-shift behaviour. */ continue; result->protocols |= 1 << version; } SMARTLIST_FOREACH(versions, char *, cp, tor_free(cp)); smartlist_free(versions); /* Parse encrypted introduction points. Don't verify. */ tok = find_opt_by_keyword(tokens, R_INTRODUCTION_POINTS); if (tok) { if (strcmp(tok->object_type, "MESSAGE")) { log_warn(LD_DIR, "Bad object type: introduction points should be of " "type MESSAGE"); goto err; } *intro_points_encrypted_out = tor_memdup(tok->object_body, tok->object_size); *intro_points_encrypted_size_out = tok->object_size; } else { *intro_points_encrypted_out = NULL; *intro_points_encrypted_size_out = 0; } /* Parse and verify signature. */ tok = find_by_keyword(tokens, R_SIGNATURE); if (check_signature_token(desc_hash, DIGEST_LEN, tok, result->pk, 0, "v2 rendezvous service descriptor") < 0) goto err; /* Verify that descriptor ID belongs to public key and secret ID part. */ if (crypto_pk_get_digest(result->pk, public_key_hash) < 0) { log_warn(LD_REND, "Unable to compute rend descriptor public key digest"); goto err; } rend_get_descriptor_id_bytes(test_desc_id, public_key_hash, secret_id_part); if (tor_memneq(desc_id_out, test_desc_id, DIGEST_LEN)) { log_warn(LD_REND, "Parsed descriptor ID does not match " "computed descriptor ID."); goto err; } goto done; err: rend_service_descriptor_free(result); result = NULL; done: if (tokens) { SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t)); smartlist_free(tokens); } if (area) memarea_drop_all(area); *parsed_out = result; if (result) return 0; return -1; }
/** Successfully register a v2 intro point and a v3 intro point. Ensure that HS * circuitmap is maintained properly. */ static void test_intro_point_registration(void *arg) { int retval; hs_circuitmap_ht *the_hs_circuitmap = NULL; or_circuit_t *intro_circ = NULL; trn_cell_establish_intro_t *establish_intro_cell = NULL; ed25519_public_key_t auth_key; crypto_pk_t *legacy_auth_key = NULL; or_circuit_t *legacy_intro_circ = NULL; or_circuit_t *returned_intro_circ = NULL; (void) arg; MOCK(hs_intro_send_intro_established_cell, mock_send_intro_established_cell); hs_circuitmap_init(); /* Check that the circuitmap is currently empty */ { the_hs_circuitmap = get_hs_circuitmap(); tt_assert(the_hs_circuitmap); tt_int_op(0, OP_EQ, HT_SIZE(the_hs_circuitmap)); /* Do a circuitmap query in any case */ returned_intro_circ =hs_circuitmap_get_intro_circ_v3_relay_side(&auth_key); tt_ptr_op(returned_intro_circ, OP_EQ, NULL); } /* Create a v3 intro point */ { intro_circ = or_circuit_new(0, NULL); tt_assert(intro_circ); establish_intro_cell = helper_establish_intro_v3(intro_circ); /* Check that the intro point was registered on the HS circuitmap */ the_hs_circuitmap = get_hs_circuitmap(); tt_assert(the_hs_circuitmap); tt_int_op(1, OP_EQ, HT_SIZE(the_hs_circuitmap)); get_auth_key_from_cell(&auth_key, RELAY_COMMAND_ESTABLISH_INTRO, establish_intro_cell); returned_intro_circ = hs_circuitmap_get_intro_circ_v3_relay_side(&auth_key); tt_ptr_op(intro_circ, OP_EQ, returned_intro_circ); } /* Create a v2 intro point */ { char key_digest[DIGEST_LEN]; legacy_intro_circ = or_circuit_new(1, NULL); tt_assert(legacy_intro_circ); legacy_auth_key = helper_establish_intro_v2(legacy_intro_circ); tt_assert(legacy_auth_key); /* Check that the circuitmap now has two elements */ the_hs_circuitmap = get_hs_circuitmap(); tt_assert(the_hs_circuitmap); tt_int_op(2, OP_EQ, HT_SIZE(the_hs_circuitmap)); /* Check that the new element is our legacy intro circuit. */ retval = crypto_pk_get_digest(legacy_auth_key, key_digest); tt_int_op(retval, OP_EQ, 0); returned_intro_circ = hs_circuitmap_get_intro_circ_v2_relay_side((uint8_t*)key_digest); tt_ptr_op(legacy_intro_circ, OP_EQ, returned_intro_circ); } /* XXX Continue test and try to register a second v3 intro point with the * same auth key. Make sure that old intro circuit gets closed. */ done: crypto_pk_free(legacy_auth_key); circuit_free_(TO_CIRCUIT(intro_circ)); circuit_free_(TO_CIRCUIT(legacy_intro_circ)); trn_cell_establish_intro_free(establish_intro_cell); test_circuitmap_free_all(); UNMOCK(hs_intro_send_intro_established_cell); }