sigcommon.c (5907B)
1 /* Copyright (c) 2001 Matej Pfajfar. 2 * Copyright (c) 2001-2004, Roger Dingledine. 3 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. 4 * Copyright (c) 2007-2021, The Tor Project, Inc. */ 5 /* See LICENSE for licensing information */ 6 7 /** 8 * \file sigcommon.c 9 * \brief Shared hashing, signing, and signature-checking code for directory 10 * objects. 11 **/ 12 13 #define SIGCOMMON_PRIVATE 14 15 #include "core/or/or.h" 16 #include "feature/dirparse/parsecommon.h" 17 #include "feature/dirparse/sigcommon.h" 18 19 /** Helper function for <b>router_get_hash_impl</b>: given <b>s</b>, 20 * <b>s_len</b>, <b>start_str</b>, <b>end_str</b>, and <b>end_c</b> with the 21 * same semantics as in that function, set *<b>start_out</b> (inclusive) and 22 * *<b>end_out</b> (exclusive) to the boundaries of the string to be hashed. 23 * 24 * Return 0 on success and -1 on failure. 25 */ 26 int 27 router_get_hash_impl_helper(const char *s, size_t s_len, 28 const char *start_str, 29 const char *end_str, char end_c, 30 int log_severity, 31 const char **start_out, const char **end_out) 32 { 33 const char *start, *end; 34 start = tor_memstr(s, s_len, start_str); 35 if (!start) { 36 log_fn(log_severity,LD_DIR, 37 "couldn't find start of hashed material \"%s\"",start_str); 38 return -1; 39 } 40 if (start != s && *(start-1) != '\n') { 41 log_fn(log_severity,LD_DIR, 42 "first occurrence of \"%s\" is not at the start of a line", 43 start_str); 44 return -1; 45 } 46 end = tor_memstr(start+strlen(start_str), 47 s_len - (start-s) - strlen(start_str), end_str); 48 if (!end) { 49 log_fn(log_severity,LD_DIR, 50 "couldn't find end of hashed material \"%s\"",end_str); 51 return -1; 52 } 53 end = memchr(end+strlen(end_str), end_c, s_len - (end-s) - strlen(end_str)); 54 if (!end) { 55 log_fn(log_severity,LD_DIR, 56 "couldn't find EOL"); 57 return -1; 58 } 59 ++end; 60 61 *start_out = start; 62 *end_out = end; 63 return 0; 64 } 65 66 /** Compute the digest of the substring of <b>s</b> taken from the first 67 * occurrence of <b>start_str</b> through the first instance of c after the 68 * first subsequent occurrence of <b>end_str</b>; store the 20-byte or 32-byte 69 * result in <b>digest</b>; return 0 on success. 70 * 71 * If no such substring exists, return -1. 72 */ 73 int 74 router_get_hash_impl(const char *s, size_t s_len, char *digest, 75 const char *start_str, 76 const char *end_str, char end_c, 77 digest_algorithm_t alg) 78 { 79 const char *start=NULL, *end=NULL; 80 if (router_get_hash_impl_helper(s,s_len,start_str,end_str,end_c,LOG_WARN, 81 &start,&end)<0) 82 return -1; 83 84 return router_compute_hash_final(digest, start, end-start, alg); 85 } 86 87 /** Compute the digest of the <b>len</b>-byte directory object at 88 * <b>start</b>, using <b>alg</b>. Store the result in <b>digest</b>, which 89 * must be long enough to hold it. */ 90 MOCK_IMPL(STATIC int, 91 router_compute_hash_final,(char *digest, 92 const char *start, size_t len, 93 digest_algorithm_t alg)) 94 { 95 if (alg == DIGEST_SHA1) { 96 if (crypto_digest(digest, start, len) < 0) { 97 log_warn(LD_BUG,"couldn't compute digest"); 98 return -1; 99 } 100 } else { 101 if (crypto_digest256(digest, start, len, alg) < 0) { 102 log_warn(LD_BUG,"couldn't compute digest"); 103 return -1; 104 } 105 } 106 107 return 0; 108 } 109 110 /** As router_get_hash_impl, but compute all hashes. */ 111 int 112 router_get_hashes_impl(const char *s, size_t s_len, common_digests_t *digests, 113 const char *start_str, 114 const char *end_str, char end_c) 115 { 116 const char *start=NULL, *end=NULL; 117 if (router_get_hash_impl_helper(s,s_len,start_str,end_str,end_c,LOG_WARN, 118 &start,&end)<0) 119 return -1; 120 121 if (crypto_common_digests(digests, start, end-start)) { 122 log_warn(LD_BUG,"couldn't compute digests"); 123 return -1; 124 } 125 126 return 0; 127 } 128 129 MOCK_IMPL(STATIC int, 130 signed_digest_equals, (const uint8_t *d1, const uint8_t *d2, size_t len)) 131 { 132 return tor_memeq(d1, d2, len); 133 } 134 135 /** Check whether the object body of the token in <b>tok</b> has a good 136 * signature for <b>digest</b> using key <b>pkey</b>. 137 * If <b>CST_NO_CHECK_OBJTYPE</b> is set, do not check 138 * the object type of the signature object. Use <b>doctype</b> as the type of 139 * the document when generating log messages. Return 0 on success, negative 140 * on failure. 141 */ 142 MOCK_IMPL(int, 143 check_signature_token,(const char *digest, 144 ssize_t digest_len, 145 directory_token_t *tok, 146 crypto_pk_t *pkey, 147 int flags, 148 const char *doctype)) 149 { 150 char *signed_digest; 151 size_t keysize; 152 const int check_objtype = ! (flags & CST_NO_CHECK_OBJTYPE); 153 154 tor_assert(pkey); 155 tor_assert(tok); 156 tor_assert(digest); 157 tor_assert(doctype); 158 159 if (check_objtype) { 160 if (strcmp(tok->object_type, "SIGNATURE")) { 161 log_warn(LD_DIR, "Bad object type on %s signature", doctype); 162 return -1; 163 } 164 } 165 166 keysize = crypto_pk_keysize(pkey); 167 signed_digest = tor_malloc(keysize); 168 if (crypto_pk_public_checksig(pkey, signed_digest, keysize, 169 tok->object_body, tok->object_size) 170 < digest_len) { 171 log_warn(LD_DIR, "Error reading %s: invalid signature.", doctype); 172 tor_free(signed_digest); 173 return -1; 174 } 175 // log_debug(LD_DIR,"Signed %s hash starts %s", doctype, 176 // hex_str(signed_digest,4)); 177 if (! signed_digest_equals((const uint8_t *)digest, 178 (const uint8_t *)signed_digest, digest_len)) { 179 log_warn(LD_DIR, "Error reading %s: signature does not match.", doctype); 180 tor_free(signed_digest); 181 return -1; 182 } 183 tor_free(signed_digest); 184 return 0; 185 }