diff options
author | David Goulet <dgoulet@torproject.org> | 2016-05-03 10:18:45 -0400 |
---|---|---|
committer | David Goulet <dgoulet@torproject.org> | 2016-06-20 15:26:58 -0400 |
commit | b3b4ffce2e23bfb6a2af374cd8bfa5981628a342 (patch) | |
tree | 69f8d91ec6966e850a789600cfc1eb4e33fbb7c2 /src/or/shared_random.c | |
parent | 9744a40f7aa71f874b9f3eeb3c5e7d0899c8d409 (diff) | |
download | tor-b3b4ffce2e23bfb6a2af374cd8bfa5981628a342.tar.gz tor-b3b4ffce2e23bfb6a2af374cd8bfa5981628a342.zip |
prop250: Add memory and disk state in new files
This commit introduces two new files with their header.
"shared_random.c" contains basic functions to initialize the state and allow
commit decoding for the disk state to be able to parse them from disk.
"shared_random_state.c" contains everything that has to do with the state
for both our memory and disk. Lots of helper functions as well as a
mechanism to query the state in a synchronized way.
Signed-off-by: David Goulet <dgoulet@torproject.org>
Signed-off-by: George Kadianakis <desnacked@riseup.net>
Diffstat (limited to 'src/or/shared_random.c')
-rw-r--r-- | src/or/shared_random.c | 286 |
1 files changed, 286 insertions, 0 deletions
diff --git a/src/or/shared_random.c b/src/or/shared_random.c new file mode 100644 index 0000000000..447ab27f44 --- /dev/null +++ b/src/or/shared_random.c @@ -0,0 +1,286 @@ +/* Copyright (c) 2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file shared_random.c + * + * \brief Functions and data structure needed to accomplish the shared + * random protocol as defined in proposal #250. + **/ + +#define SHARED_RANDOM_PRIVATE + +#include "or.h" +#include "shared_random.h" +#include "config.h" +#include "confparse.h" +#include "networkstatus.h" +#include "routerkeys.h" +#include "router.h" +#include "routerlist.h" +#include "shared_random_state.h" + +/* Allocate a new commit object and initializing it with <b>identity</b> + * that MUST be provided. The digest algorithm is set to the default one + * that is supported. The rest is uninitialized. This never returns NULL. */ +static sr_commit_t * +commit_new(const char *rsa_identity_fpr) +{ + sr_commit_t *commit; + + tor_assert(rsa_identity_fpr); + + commit = tor_malloc_zero(sizeof(*commit)); + commit->alg = SR_DIGEST_ALG; + strlcpy(commit->rsa_identity_fpr, rsa_identity_fpr, + sizeof(commit->rsa_identity_fpr)); + return commit; +} + +/* Parse the encoded commit. The format is: + * base64-encode( TIMESTAMP || H(REVEAL) ) + * + * If successfully decoded and parsed, commit is updated and 0 is returned. + * On error, return -1. */ +STATIC int +commit_decode(const char *encoded, sr_commit_t *commit) +{ + int decoded_len = 0; + size_t offset = 0; + /* XXX: Needs two extra bytes for the base64 decode calculation matches + * the binary length once decoded. #17868. */ + char b64_decoded[SR_COMMIT_LEN + 2]; + + tor_assert(encoded); + tor_assert(commit); + + if (strlen(encoded) > SR_COMMIT_BASE64_LEN) { + /* This means that if we base64 decode successfully the reveiced commit, + * we'll end up with a bigger decoded commit thus unusable. */ + goto error; + } + + /* Decode our encoded commit. Let's be careful here since _encoded_ is + * coming from the network in a dirauth vote so we expect nothing more + * than the base64 encoded length of a commit. */ + decoded_len = base64_decode(b64_decoded, sizeof(b64_decoded), + encoded, strlen(encoded)); + if (decoded_len < 0) { + log_warn(LD_BUG, "SR: Commit from authority %s can't be decoded.", + commit->rsa_identity_fpr); + goto error; + } + + if (decoded_len != SR_COMMIT_LEN) { + log_warn(LD_BUG, "SR: Commit from authority %s decoded length doesn't " + "match the expected length (%d vs %d).", + commit->rsa_identity_fpr, decoded_len, SR_COMMIT_LEN); + goto error; + } + + /* First is the timestamp (8 bytes). */ + commit->commit_ts = (time_t) tor_ntohll(get_uint64(b64_decoded)); + offset += sizeof(uint64_t); + /* Next is hashed reveal. */ + memcpy(commit->hashed_reveal, b64_decoded + offset, + sizeof(commit->hashed_reveal)); + /* Copy the base64 blob to the commit. Useful for voting. */ + strlcpy(commit->encoded_commit, encoded, sizeof(commit->encoded_commit)); + + return 0; + + error: + return -1; +} + +/* Parse the b64 blob at <b>encoded</b> containing reveal information and + * store the information in-place in <b>commit</b>. Return 0 on success else + * a negative value. */ +STATIC int +reveal_decode(const char *encoded, sr_commit_t *commit) +{ + int decoded_len = 0; + /* XXX: Needs two extra bytes for the base64 decode calculation matches + * the binary length once decoded. #17868. */ + char b64_decoded[SR_REVEAL_LEN + 2]; + + tor_assert(encoded); + tor_assert(commit); + + if (strlen(encoded) > SR_REVEAL_BASE64_LEN) { + /* This means that if we base64 decode successfully the received reveal + * value, we'll end up with a bigger decoded value thus unusable. */ + goto error; + } + + /* Decode our encoded reveal. Let's be careful here since _encoded_ is + * coming from the network in a dirauth vote so we expect nothing more + * than the base64 encoded length of our reveal. */ + decoded_len = base64_decode(b64_decoded, sizeof(b64_decoded), + encoded, strlen(encoded)); + if (decoded_len < 0) { + log_warn(LD_BUG, "SR: Reveal from authority %s can't be decoded.", + commit->rsa_identity_fpr); + goto error; + } + + if (decoded_len != SR_REVEAL_LEN) { + log_warn(LD_BUG, "SR: Reveal from authority %s decoded length is " + "doesn't match the expected length (%d vs %d)", + commit->rsa_identity_fpr, decoded_len, SR_REVEAL_LEN); + goto error; + } + + commit->reveal_ts = (time_t) tor_ntohll(get_uint64(b64_decoded)); + /* Copy the last part, the random value. */ + memcpy(commit->random_number, b64_decoded + 8, + sizeof(commit->random_number)); + /* Also copy the whole message to use during verification */ + strlcpy(commit->encoded_reveal, encoded, sizeof(commit->encoded_reveal)); + + return 0; + + error: + return -1; +} + +/* Cleanup both our global state and disk state. */ +static void +sr_cleanup(void) +{ + sr_state_free(); +} + +/* Free a commit object. */ +void +sr_commit_free(sr_commit_t *commit) +{ + if (commit == NULL) { + return; + } + /* Make sure we do not leave OUR random number in memory. */ + memwipe(commit->random_number, 0, sizeof(commit->random_number)); + tor_free(commit); +} + +/* Parse a list of arguments from a SRV value either from a vote, consensus + * or from our disk state and return a newly allocated srv object. NULL is + * returned on error. + * + * The arguments' order: + * num_reveals, value + */ +sr_srv_t * +sr_parse_srv(const smartlist_t *args) +{ + char *value; + int num_reveals, ok; + sr_srv_t *srv = NULL; + + tor_assert(args); + + if (smartlist_len(args) < 2) { + goto end; + } + + /* First argument is the number of reveal values */ + num_reveals = (int)tor_parse_long(smartlist_get(args, 0), + 10, 0, INT32_MAX, &ok, NULL); + if (!ok) { + goto end; + } + srv = tor_malloc_zero(sizeof(*srv)); + srv->num_reveals = num_reveals; + + /* Second and last argument is the shared random value it self. */ + value = smartlist_get(args, 1); + base16_decode((char *) srv->value, sizeof(srv->value), value, + HEX_DIGEST256_LEN); + end: + return srv; +} + +/* Parse a commit from a vote or from our disk state and return a newly + * allocated commit object. NULL is returned on error. + * + * The commit's data is in <b>args</b> and the order matters very much: + * algname, RSA fingerprint, commit value[, reveal value] + */ +sr_commit_t * +sr_parse_commit(const smartlist_t *args) +{ + char *value; + digest_algorithm_t alg; + const char *rsa_identity_fpr; + sr_commit_t *commit = NULL; + + if (smartlist_len(args) < 3) { + goto error; + } + + /* First argument is the algorithm. */ + value = smartlist_get(args, 0); + alg = crypto_digest_algorithm_parse_name(value); + if (alg != SR_DIGEST_ALG) { + log_warn(LD_BUG, "SR: Commit algorithm %s is not recognized.", + escaped(value)); + goto error; + } + + /* Second argument is the RSA fingerprint of the auth */ + rsa_identity_fpr = smartlist_get(args, 1); + if (base16_decode(digest, DIGEST_LEN, rsa_identity_fpr, + HEX_DIGEST_LEN) < 0) { + log_warn(LD_DIR, "SR: RSA fingerprint '%s' not decodable", + rsa_identity_fpr); + goto error; + } + /* Let's make sure, for extra safety, that this fingerprint is known to + * us. Even though this comes from a vote, doesn't hurt to be + * extracareful. */ + if (trusteddirserver_get_by_v3_auth_digest(digest) == NULL) { + log_warn(LD_DIR, "SR: Fingerprint %s is not from a recognized " + "authority. Discarding commit.", + rsa_identity_fpr); + goto error; + } + + /* Allocate commit since we have a valid identity now. */ + commit = commit_new(rsa_identity_fpr); + + /* Third argument is the commitment value base64-encoded. */ + value = smartlist_get(args, 2); + if (commit_decode(value, commit) < 0) { + goto error; + } + + /* (Optional) Fourth argument is the revealed value. */ + if (smartlist_len(args) > 3) { + value = smartlist_get(args, 3); + if (reveal_decode(value, commit) < 0) { + goto error; + } + } + + return commit; + + error: + sr_commit_free(commit); + return NULL; +} + +/* Initialize shared random subsystem. This MUST be called early in the boot + * process of tor. Return 0 on success else -1 on error. */ +int +sr_init(int save_to_disk) +{ + return sr_state_init(save_to_disk, 1); +} + +/* Save our state to disk and cleanup everything. */ +void +sr_save_and_cleanup(void) +{ + sr_state_save(); + sr_cleanup(); +} |