/** Run unit tests for misc crypto formatting functionality (base64, base32, * fingerprints, etc) */ static void test_crypto_formats(void) { char *data1 = NULL, *data2 = NULL, *data3 = NULL; int i, j, idx; data1 = tor_malloc(1024); data2 = tor_malloc(1024); data3 = tor_malloc(1024); test_assert(data1 && data2 && data3); /* Base64 tests */ memset(data1, 6, 1024); for (idx = 0; idx < 10; ++idx) { i = base64_encode(data2, 1024, data1, idx); test_assert(i >= 0); j = base64_decode(data3, 1024, data2, i); test_eq(j,idx); test_memeq(data3, data1, idx); } strlcpy(data1, "Test string that contains 35 chars.", 1024); strlcat(data1, " 2nd string that contains 35 chars.", 1024); i = base64_encode(data2, 1024, data1, 71); test_assert(i >= 0); j = base64_decode(data3, 1024, data2, i); test_eq(j, 71); test_streq(data3, data1); test_assert(data2[i] == '\0'); crypto_rand(data1, DIGEST_LEN); memset(data2, 100, 1024); digest_to_base64(data2, data1); test_eq(BASE64_DIGEST_LEN, strlen(data2)); test_eq(100, data2[BASE64_DIGEST_LEN+2]); memset(data3, 99, 1024); test_eq(digest_from_base64(data3, data2), 0); test_memeq(data1, data3, DIGEST_LEN); test_eq(99, data3[DIGEST_LEN+1]); test_assert(digest_from_base64(data3, "###") < 0); /* Encoding SHA256 */ crypto_rand(data2, DIGEST256_LEN); memset(data2, 100, 1024); digest256_to_base64(data2, data1); test_eq(BASE64_DIGEST256_LEN, strlen(data2)); test_eq(100, data2[BASE64_DIGEST256_LEN+2]); memset(data3, 99, 1024); test_eq(digest256_from_base64(data3, data2), 0); test_memeq(data1, data3, DIGEST256_LEN); test_eq(99, data3[DIGEST256_LEN+1]); /* Base32 tests */ strlcpy(data1, "5chrs", 1024); /* bit pattern is: [35 63 68 72 73] -> * [00110101 01100011 01101000 01110010 01110011] * By 5s: [00110 10101 10001 10110 10000 11100 10011 10011] */ base32_encode(data2, 9, data1, 5); test_streq(data2, "gvrwq4tt"); strlcpy(data1, "\xFF\xF5\x6D\x44\xAE\x0D\x5C\xC9\x62\xC4", 1024); base32_encode(data2, 30, data1, 10); test_streq(data2, "772w2rfobvomsywe"); /* Base16 tests */ strlcpy(data1, "6chrs\xff", 1024); base16_encode(data2, 13, data1, 6); test_streq(data2, "3663687273FF"); strlcpy(data1, "f0d678affc000100", 1024); i = base16_decode(data2, 8, data1, 16); test_eq(i,0); test_memeq(data2, "\xf0\xd6\x78\xaf\xfc\x00\x01\x00",8); /* now try some failing base16 decodes */ test_eq(-1, base16_decode(data2, 8, data1, 15)); /* odd input len */ test_eq(-1, base16_decode(data2, 7, data1, 16)); /* dest too short */ strlcpy(data1, "f0dz!8affc000100", 1024); test_eq(-1, base16_decode(data2, 8, data1, 16)); tor_free(data1); tor_free(data2); tor_free(data3); /* Add spaces to fingerprint */ { data1 = tor_strdup("ABCD1234ABCD56780000ABCD1234ABCD56780000"); test_eq(strlen(data1), 40); data2 = tor_malloc(FINGERPRINT_LEN+1); add_spaces_to_fp(data2, FINGERPRINT_LEN+1, data1); test_streq(data2, "ABCD 1234 ABCD 5678 0000 ABCD 1234 ABCD 5678 0000"); tor_free(data1); tor_free(data2); } done: tor_free(data1); tor_free(data2); tor_free(data3); }
/** Helper: write the router-status information in <b>rs</b> into a newly * allocated character buffer. Use the same format as in network-status * documents. If <b>version</b> is non-NULL, add a "v" line for the platform. * * consensus_method is the current consensus method when format is * NS_V3_CONSENSUS or NS_V3_CONSENSUS_MICRODESC. It is ignored for other * formats: pass ROUTERSTATUS_FORMAT_NO_CONSENSUS_METHOD. * * Return 0 on success, -1 on failure. * * The format argument has one of the following values: * NS_V2 - Output an entry suitable for a V2 NS opinion document * NS_V3_CONSENSUS - Output the first portion of a V3 NS consensus entry * for consensus_method. * NS_V3_CONSENSUS_MICRODESC - Output the first portion of a V3 microdesc * consensus entry for consensus_method. * NS_V3_VOTE - Output a complete V3 NS vote. If <b>vrs</b> is present, * it contains additional information for the vote. * NS_CONTROL_PORT - Output a NS document for the control port. */ char * routerstatus_format_entry(const routerstatus_t *rs, const char *version, const char *protocols, routerstatus_format_type_t format, int consensus_method, const vote_routerstatus_t *vrs) { char *summary; char *result = NULL; char published[ISO_TIME_LEN+1]; char identity64[BASE64_DIGEST_LEN+1]; char digest64[BASE64_DIGEST_LEN+1]; smartlist_t *chunks = smartlist_new(); format_iso_time(published, rs->published_on); digest_to_base64(identity64, rs->identity_digest); digest_to_base64(digest64, rs->descriptor_digest); smartlist_add_asprintf(chunks, "r %s %s %s%s%s %s %d %d\n", rs->nickname, identity64, (format==NS_V3_CONSENSUS_MICRODESC)?"":digest64, (format==NS_V3_CONSENSUS_MICRODESC)?"":" ", published, fmt_addr32(rs->addr), (int)rs->or_port, (int)rs->dir_port); /* TODO: Maybe we want to pass in what we need to build the rest of * this here, instead of in the caller. Then we could use the * networkstatus_type_t values, with an additional control port value * added -MP */ /* V3 microdesc consensuses only have "a" lines in later consensus methods */ if (format == NS_V3_CONSENSUS_MICRODESC && consensus_method < MIN_METHOD_FOR_A_LINES_IN_MICRODESC_CONSENSUS) goto done; /* Possible "a" line. At most one for now. */ if (!tor_addr_is_null(&rs->ipv6_addr)) { smartlist_add_asprintf(chunks, "a %s\n", fmt_addrport(&rs->ipv6_addr, rs->ipv6_orport)); } if (format == NS_V3_CONSENSUS || format == NS_V3_CONSENSUS_MICRODESC) goto done; smartlist_add_asprintf(chunks, "s%s%s%s%s%s%s%s%s%s%s%s\n", /* These must stay in alphabetical order. */ rs->is_authority?" Authority":"", rs->is_bad_exit?" BadExit":"", rs->is_exit?" Exit":"", rs->is_fast?" Fast":"", rs->is_possible_guard?" Guard":"", rs->is_hs_dir?" HSDir":"", rs->is_flagged_running?" Running":"", rs->is_stable?" Stable":"", rs->is_staledesc?" StaleDesc":"", rs->is_v2_dir?" V2Dir":"", rs->is_valid?" Valid":""); /* length of "opt v \n" */ #define V_LINE_OVERHEAD 7 if (version && strlen(version) < MAX_V_LINE_LEN - V_LINE_OVERHEAD) { smartlist_add_asprintf(chunks, "v %s\n", version); } if (protocols) { smartlist_add_asprintf(chunks, "pr %s\n", protocols); } if (format != NS_V2) { const routerinfo_t* desc = router_get_by_id_digest(rs->identity_digest); uint32_t bw_kb; if (format != NS_CONTROL_PORT) { /* Blow up more or less nicely if we didn't get anything or not the * thing we expected. */ if (!desc) { char id[HEX_DIGEST_LEN+1]; char dd[HEX_DIGEST_LEN+1]; base16_encode(id, sizeof(id), rs->identity_digest, DIGEST_LEN); base16_encode(dd, sizeof(dd), rs->descriptor_digest, DIGEST_LEN); log_warn(LD_BUG, "Cannot get any descriptor for %s " "(wanted descriptor %s).", id, dd); goto err; } /* This assert could fire for the control port, because * it can request NS documents before all descriptors * have been fetched. Therefore, we only do this test when * format != NS_CONTROL_PORT. */ if (tor_memneq(desc->cache_info.signed_descriptor_digest, rs->descriptor_digest, DIGEST_LEN)) { char rl_d[HEX_DIGEST_LEN+1]; char rs_d[HEX_DIGEST_LEN+1]; char id[HEX_DIGEST_LEN+1]; base16_encode(rl_d, sizeof(rl_d), desc->cache_info.signed_descriptor_digest, DIGEST_LEN); base16_encode(rs_d, sizeof(rs_d), rs->descriptor_digest, DIGEST_LEN); base16_encode(id, sizeof(id), rs->identity_digest, DIGEST_LEN); log_err(LD_BUG, "descriptor digest in routerlist does not match " "the one in routerstatus: %s vs %s " "(router %s)\n", rl_d, rs_d, id); tor_assert(tor_memeq(desc->cache_info.signed_descriptor_digest, rs->descriptor_digest, DIGEST_LEN)); } } if (format == NS_CONTROL_PORT && rs->has_bandwidth) { bw_kb = rs->bandwidth_kb; } else { tor_assert(desc); bw_kb = router_get_advertised_bandwidth_capped(desc) / 1000; } smartlist_add_asprintf(chunks, "w Bandwidth=%d", bw_kb); if (format == NS_V3_VOTE && vrs && vrs->has_measured_bw) { smartlist_add_asprintf(chunks, " Measured=%d", vrs->measured_bw_kb); } /* Write down guardfraction information if we have it. */ if (format == NS_V3_VOTE && vrs && vrs->status.has_guardfraction) { smartlist_add_asprintf(chunks, " GuardFraction=%d", vrs->status.guardfraction_percentage); } smartlist_add_strdup(chunks, "\n"); if (desc) { summary = policy_summarize(desc->exit_policy, AF_INET); smartlist_add_asprintf(chunks, "p %s\n", summary); tor_free(summary); } if (format == NS_V3_VOTE && vrs) { if (tor_mem_is_zero((char*)vrs->ed25519_id, ED25519_PUBKEY_LEN)) { smartlist_add_strdup(chunks, "id ed25519 none\n"); } else { char ed_b64[BASE64_DIGEST256_LEN+1]; digest256_to_base64(ed_b64, (const char*)vrs->ed25519_id); smartlist_add_asprintf(chunks, "id ed25519 %s\n", ed_b64); } } } done: result = smartlist_join_strings(chunks, "", 0, NULL); err: SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp)); smartlist_free(chunks); return result; }