/* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. * Copyright (c) 2007-2018, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** * \file sigcommon.c * \brief Shared hashing, signing, and signature-checking code for directory * objects. **/ #define SIGCOMMON_PRIVATE #include "core/or/or.h" #include "feature/dirparse/parsecommon.h" #include "feature/dirparse/sigcommon.h" /** Helper function for router_get_hash_impl: given s, * s_len, start_str, end_str, and end_c with the * same semantics as in that function, set *start_out (inclusive) and * *end_out (exclusive) to the boundaries of the string to be hashed. * * Return 0 on success and -1 on failure. */ int router_get_hash_impl_helper(const char *s, size_t s_len, const char *start_str, const char *end_str, char end_c, int log_severity, const char **start_out, const char **end_out) { const char *start, *end; start = tor_memstr(s, s_len, start_str); if (!start) { log_fn(log_severity,LD_DIR, "couldn't find start of hashed material \"%s\"",start_str); return -1; } if (start != s && *(start-1) != '\n') { log_fn(log_severity,LD_DIR, "first occurrence of \"%s\" is not at the start of a line", start_str); return -1; } end = tor_memstr(start+strlen(start_str), s_len - (start-s) - strlen(start_str), end_str); if (!end) { log_fn(log_severity,LD_DIR, "couldn't find end of hashed material \"%s\"",end_str); return -1; } end = memchr(end+strlen(end_str), end_c, s_len - (end-s) - strlen(end_str)); if (!end) { log_fn(log_severity,LD_DIR, "couldn't find EOL"); return -1; } ++end; *start_out = start; *end_out = end; return 0; } /** Compute the digest of the substring of s taken from the first * occurrence of start_str through the first instance of c after the * first subsequent occurrence of end_str; store the 20-byte or 32-byte * result in digest; return 0 on success. * * If no such substring exists, return -1. */ int router_get_hash_impl(const char *s, size_t s_len, char *digest, const char *start_str, const char *end_str, char end_c, digest_algorithm_t alg) { const char *start=NULL, *end=NULL; if (router_get_hash_impl_helper(s,s_len,start_str,end_str,end_c,LOG_WARN, &start,&end)<0) return -1; return router_compute_hash_final(digest, start, end-start, alg); } /** Compute the digest of the len-byte directory object at * start, using alg. Store the result in digest, which * must be long enough to hold it. */ MOCK_IMPL(STATIC int, router_compute_hash_final,(char *digest, const char *start, size_t len, digest_algorithm_t alg)) { if (alg == DIGEST_SHA1) { if (crypto_digest(digest, start, len) < 0) { log_warn(LD_BUG,"couldn't compute digest"); return -1; } } else { if (crypto_digest256(digest, start, len, alg) < 0) { log_warn(LD_BUG,"couldn't compute digest"); return -1; } } return 0; } /** As router_get_hash_impl, but compute all hashes. */ int router_get_hashes_impl(const char *s, size_t s_len, common_digests_t *digests, const char *start_str, const char *end_str, char end_c) { const char *start=NULL, *end=NULL; if (router_get_hash_impl_helper(s,s_len,start_str,end_str,end_c,LOG_WARN, &start,&end)<0) return -1; if (crypto_common_digests(digests, start, end-start)) { log_warn(LD_BUG,"couldn't compute digests"); return -1; } return 0; } MOCK_IMPL(STATIC int, signed_digest_equals, (const uint8_t *d1, const uint8_t *d2, size_t len)) { return tor_memeq(d1, d2, len); } /** Check whether the object body of the token in tok has a good * signature for digest using key pkey. * If CST_NO_CHECK_OBJTYPE is set, do not check * the object type of the signature object. Use doctype as the type of * the document when generating log messages. Return 0 on success, negative * on failure. */ int check_signature_token(const char *digest, ssize_t digest_len, directory_token_t *tok, crypto_pk_t *pkey, int flags, const char *doctype) { char *signed_digest; size_t keysize; const int check_objtype = ! (flags & CST_NO_CHECK_OBJTYPE); tor_assert(pkey); tor_assert(tok); tor_assert(digest); tor_assert(doctype); if (check_objtype) { if (strcmp(tok->object_type, "SIGNATURE")) { log_warn(LD_DIR, "Bad object type on %s signature", doctype); return -1; } } keysize = crypto_pk_keysize(pkey); signed_digest = tor_malloc(keysize); if (crypto_pk_public_checksig(pkey, signed_digest, keysize, tok->object_body, tok->object_size) < digest_len) { log_warn(LD_DIR, "Error reading %s: invalid signature.", doctype); tor_free(signed_digest); return -1; } // log_debug(LD_DIR,"Signed %s hash starts %s", doctype, // hex_str(signed_digest,4)); if (! signed_digest_equals((const uint8_t *)digest, (const uint8_t *)signed_digest, digest_len)) { log_warn(LD_DIR, "Error reading %s: signature does not match.", doctype); tor_free(signed_digest); return -1; } tor_free(signed_digest); return 0; }