diff --git a/doc/tor.1.txt b/doc/tor.1.txt index 13f2bdd60c..e7c08f5046 100644 --- a/doc/tor.1.txt +++ b/doc/tor.1.txt @@ -1857,6 +1857,13 @@ is non-zero): this. If this option is set to 0, Tor will try to pick a reasonable default based on your system's physical memory. (Default: 0) +[[SigningKeyLifetime]] **SigningKeyLifetime** __N__ **days**|**weeks**|**months**:: + For how long should each Ed25519 signing key be valid? Tor uses a + permanent master identity key that can be kept offline, and periodically + generates new "signing" keys that it uses online. This option + configures their lifetime. + (Default: 30 days) + DIRECTORY SERVER OPTIONS ------------------------ @@ -2349,6 +2356,23 @@ The following options are used for running a testing Tor network. authority on a testing network. Overrides the usual default lower bound of 4 KB. (Default: 0) +[[TestingLinkCertLifetime]] **TestingLinkCertifetime** __N__ **seconds**|**minutes**|**hours**|**days**|**weeks**|**months**:: + Overrides the default lifetime for the certificates used to authenticate + our X509 link cert with our ed25519 signing key. + (Default: 2 days) + +[[TestingAuthKeyLifetime]] **TestingAuthKeyLifetime** __N__ **seconds**|**minutes**|**hours**|**days**|**weeks**|**months**:: + Overrides the default lifetime for a signing Ed25519 TLS Link authentication + key. + (Default: 2 days) + +[[TestingLinkKeySlop]] **TestingLinkKeySlop** __N__ **seconds**|**minutes**|**hours**:: +[[TestingAuthKeySlop]] **TestingAuthKeySlop** __N__ **seconds**|**minutes**|**hours**:: +[[TestingSigningKeySlop]] **TestingSigningKeySlop** __N__ **seconds**|**minutes**|**hours**:: + How early before the official expiration of a an Ed25519 signing key do + we replace it and issue a new key? + (Default: 3 hours for link and auth; 1 day for signing.) + SIGNALS ------- diff --git a/scripts/codegen/makedesc.py b/scripts/codegen/makedesc.py index 833951945b..d4ba21efae 100644 --- a/scripts/codegen/makedesc.py +++ b/scripts/codegen/makedesc.py @@ -14,6 +14,18 @@ import binascii import ctypes import ctypes.util import hashlib +import optparse +import os +import re +import struct +import time +import UserDict + +import slow_ed25519 +import slownacl_curve25519 +import ed25519_exts_ref + +# Pull in the openssl stuff we need. crypt = ctypes.CDLL(ctypes.util.find_library('crypto')) BIO_s_mem = crypt.BIO_s_mem @@ -24,6 +36,15 @@ BIO_new = crypt.BIO_new BIO_new.argtypes = [ctypes.c_void_p] BIO_new.restype = ctypes.c_void_p +crypt.BIO_free.argtypes = [ctypes.c_void_p] +crypt.BIO_free.restype = ctypes.c_int + +crypt.BIO_ctrl.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.c_long, ctypes.c_void_p ] +crypt.BIO_ctrl.restype = ctypes.c_long + +crypt.PEM_write_bio_RSAPublicKey.argtypes = [ ctypes.c_void_p, ctypes.c_void_p ] +crypt.PEM_write_bio_RSAPublicKey.restype = ctypes.c_int + RSA_generate_key = crypt.RSA_generate_key RSA_generate_key.argtypes = [ctypes.c_int, ctypes.c_ulong, ctypes.c_void_p, ctypes.c_void_p] RSA_generate_key.restype = ctypes.c_void_p @@ -39,6 +60,14 @@ i2d_RSAPublicKey.argtypes = [ ] i2d_RSAPublicKey.restype = ctypes.c_int + +def rsa_sign(msg, rsa): + buf = ctypes.create_string_buffer(1024) + n = RSA_private_encrypt(len(msg), msg, buf, rsa, 1) + if n <= 0: + raise Exception() + return buf.raw[:n] + def b64(x): x = base64.b64encode(x) res = [] @@ -51,29 +80,188 @@ def bio_extract(bio): length = crypt.BIO_ctrl(bio, 3, 0, ctypes.byref(buf)) return ctypes.string_at(buf, length) -def make_key(e=65537): +def make_rsa_key(e=65537): rsa = crypt.RSA_generate_key(1024, e, None, None) bio = BIO_new(BIO_s_mem()) crypt.PEM_write_bio_RSAPublicKey(bio, rsa) pem = bio_extract(bio).rstrip() crypt.BIO_free(bio) - buf = ctypes.create_string_buffer(1024) pBuf = ctypes.c_char_p(ctypes.addressof(buf)) n = crypt.i2d_RSAPublicKey(rsa, ctypes.byref(pBuf)) s = buf.raw[:n] digest = hashlib.sha1(s).digest() - return (rsa,pem,digest) +def makeEdSigningKeyCert(sk_master, pk_master, pk_signing, date, + includeSigning=False, certType=1): + assert len(pk_signing) == len(pk_master) == 32 + expiration = struct.pack("!L", date//3600) + if includeSigning: + extensions = "\x01\x00\x20\x04\x00%s"%(pk_master) + else: + extensions = "\x00" + signed = "\x01%s%s\x01%s%s" % ( + chr(certType), expiration, pk_signing, extensions) + signature = ed25519_exts_ref.signatureWithESK(signed, sk_master, pk_master) + assert len(signature) == 64 + return signed+signature + +def objwrap(identifier, body): + return ("-----BEGIN {0}-----\n" + "{1}" + "-----END {0}-----").format(identifier, body) + +MAGIC1 = "<<<<<>>>>>" +MAGIC2 = "<<<<>>>>" + +class OnDemandKeys(object): + def __init__(self, certDate=None): + if certDate is None: + certDate = time.time() + 86400 + self.certDate = certDate + self.rsa_id = None + self.rsa_onion_key = None + self.ed_id_sk = None + self.ntor_sk = None + self.ntor_crosscert = None + self.rsa_crosscert_ed = None + self.rsa_crosscert_noed = None + + @property + def RSA_IDENTITY(self): + if self.rsa_id is None: + self.rsa_id, self.rsa_ident_pem, self.rsa_id_digest = make_rsa_key() + + return self.rsa_ident_pem + + @property + def RSA_ID_DIGEST(self): + self.RSA_IDENTITY + return self.rsa_id_digest + + @property + def RSA_FINGERPRINT_NOSPACE(self): + return binascii.b2a_hex(self.RSA_ID_DIGEST).upper() + + @property + def RSA_ONION_KEY(self): + if self.rsa_onion_key is None: + self.rsa_onion_key, self.rsa_onion_pem, _ = make_rsa_key() + + return self.rsa_onion_pem + + @property + def RSA_FINGERPRINT(self): + hexdigest = self.RSA_FINGERPRINT_NOSPACEK + return " ".join(hexdigest[i:i+4] for i in range(0,len(hexdigest),4)) + + @property + def RSA_SIGNATURE(self): + return MAGIC1 + + @property + def ED_SIGNATURE(self): + return MAGIC2 + + @property + def NTOR_ONION_KEY(self): + if self.ntor_sk is None: + self.ntor_sk = slownacl_curve25519.Private() + self.ntor_pk = self.ntor_sk.get_public() + return base64.b64encode(self.ntor_pk.serialize()) + + @property + def ED_CERT(self): + if self.ed_id_sk is None: + self.ed_id_sk = ed25519_exts_ref.expandSK(os.urandom(32)) + self.ed_signing_sk = ed25519_exts_ref.expandSK(os.urandom(32)) + self.ed_id_pk = ed25519_exts_ref.publickeyFromESK(self.ed_id_sk) + self.ed_signing_pk = ed25519_exts_ref.publickeyFromESK(self.ed_signing_sk) + self.ed_cert = makeEdSigningKeyCert(self.ed_id_sk, self.ed_id_pk, self.ed_signing_pk, self.certDate, includeSigning=True, certType=4) + + return objwrap('ED25519 CERT', b64(self.ed_cert)) + + @property + def NTOR_CROSSCERT(self): + if self.ntor_crosscert is None: + self.ED_CERT + self.NTOR_ONION_KEY + + ed_privkey = self.ntor_sk.serialize() + os.urandom(32) + ed_pub0 = ed25519_exts_ref.publickeyFromESK(ed_privkey) + sign = (ord(ed_pub0[31]) & 255) >> 7 + + self.ntor_crosscert = makeEdSigningKeyCert(self.ntor_sk.serialize() + os.urandom(32), ed_pub0, self.ed_id_pk, self.certDate, certType=10) + self.ntor_crosscert_sign = sign + + return objwrap('ED25519 CERT', b64(self.ntor_crosscert)) + + @property + def NTOR_CROSSCERT_SIGN(self): + self.NTOR_CROSSCERT + return self.ntor_crosscert_sign + + @property + def RSA_CROSSCERT_NOED(self): + if self.rsa_crosscert_noed is None: + self.RSA_ONION_KEY + signed = self.RSA_ID_DIGEST + self.rsa_crosscert_noed = rsa_sign(signed, self.rsa_onion_key) + return objwrap("CROSSCERT",b64(self.rsa_crosscert_noed)) + + @property + def RSA_CROSSCERT_ED(self): + if self.rsa_crosscert_ed is None: + self.RSA_ONION_KEY + self.ED_CERT + signed = self.RSA_ID_DIGEST + self.ed_id_pk + self.rsa_crosscert_ed = rsa_sign(signed, self.rsa_onion_key) + return objwrap("CROSSCERT",b64(self.rsa_crosscert_ed)) + + def sign_desc(self, body): + idx = body.rfind("\nrouter-sig-ed25519 ") + if idx >= 0: + self.ED_CERT + signed_part = body[:idx+len("\nrouter-sig-ed25519 ")] + signed_part = "Tor router descriptor signature v1" + signed_part + digest = hashlib.sha256(signed_part).digest() + ed_sig = ed25519_exts_ref.signatureWithESK(digest, + self.ed_signing_sk, self.ed_signing_pk) + + body = body.replace(MAGIC2, base64.b64encode(ed_sig).replace("=","")) + + idx = body.rindex("\nrouter-signature") + end_of_sig = body.index("\n", idx+1) + + signed_part = body[:end_of_sig+1] + + digest = hashlib.sha1(signed_part).digest() + assert len(digest) == 20 + + rsasig = rsa_sign(digest, self.rsa_id) + + body = body.replace(MAGIC1, objwrap("SIGNATURE", b64(rsasig))) + + return body + + def signdesc(body, args_out=None): rsa, ident_pem, id_digest = make_key() _, onion_pem, _ = make_key() + need_ed = '{ED25519-CERT}' in body or '{ED25519-SIGNATURE}' in body + if need_ed: + sk_master = os.urandom(32) + sk_signing = os.urandom(32) + pk_master = slow_ed25519.pubkey(sk_master) + pk_signing = slow_ed25519.pubkey(sk_signing) + hexdigest = binascii.b2a_hex(id_digest).upper() fingerprint = " ".join(hexdigest[i:i+4] for i in range(0,len(hexdigest),4)) MAGIC = "<<<<<>>>>>" + MORE_MAGIC = "<<<<>>>>" args = { "RSA-IDENTITY" : ident_pem, "ONION-KEY" : onion_pem, @@ -81,6 +269,11 @@ def signdesc(body, args_out=None): "FINGERPRINT-NOSPACE" : hexdigest, "RSA-SIGNATURE" : MAGIC } + if need_ed: + args['ED25519-CERT'] = makeEdSigningKeyCert( + sk_master, pk_master, pk_signing) + args['ED25519-SIGNATURE'] = MORE_MAGIC + if args_out: args_out.update(args) body = body.format(**args) @@ -104,115 +297,55 @@ def signdesc(body, args_out=None): return body.rstrip() -def emit_ri(name, body, args_out=None): - print "const char %s[] ="%name - body = "\n".join(line.rstrip() for line in body.split("\n"))+"\n" - b = signdesc(body, args_out) - for line in b.split("\n"): - print ' "%s\\n"'%line +def print_c_string(ident, body): + print "static const char %s[] =" % ident + for line in body.split("\n"): + print ' "%s\\n"' %(line) print " ;" +def emit_ri(name, body): + info = OnDemandKeys() + body = body.format(d=info) + body = info.sign_desc(body) + print_c_string("EX_RI_%s"%name.upper(), body) + def emit_ei(name, body): - args = { 'NAME' : name } - emit_ri(name, body, args) - args['key'] = "\n".join( - ' "%s\\n"'%line for line in args['RSA-IDENTITY'].split("\n")) - print """ -const char {NAME}_fp[] = "{FINGERPRINT-NOSPACE}"; -const char {NAME}_key[] = -{key};""".format(**args) + info = OnDemandKeys() + body = body.format(d=info) + body = info.sign_desc(body) + print_c_string("EX_EI_%s"%name.upper(), body) -if 0: - emit_ri("minimal", - """\ -router fred 127.0.0.1 9001 0 9002 -signing-key -{RSA-IDENTITY} -onion-key -{ONION-KEY} -published 2014-10-05 12:00:00 -bandwidth 1000 1000 1000 -reject *:* -router-signature -{RSA-SIGNATURE} -""") + print 'const char EX_EI_{NAME}_FP[] = "{d.RSA_FINGERPRINT_NOSPACE}";'.format( + d=info, NAME=name.upper()) + print_c_string("EX_EI_%s_KEY"%name.upper(), info.RSA_IDENTITY) -if 0: - emit_ri("maximal", - """\ -router fred 127.0.0.1 9001 0 9002 -signing-key -{RSA-IDENTITY} -onion-key -{ONION-KEY} -published 2014-10-05 12:00:00 -bandwidth 1000 1000 1000 -reject 127.0.0.1:* -accept *:80 -reject *:* -ipv6-policy accept 80,100,101 -ntor-onion-key s7rSohmz9SXn8WWh1EefTHIsWePthsEntQi0WL+ScVw -uptime 1000 -hibernating 0 -unrecognized-keywords are just dandy in this format -platform Tor 0.2.4.23 on a Banana PC Jr 6000 Series -contact O.W.Jones -fingerprint {FINGERPRINT} -read-history 900 1,2,3,4 -write-history 900 1,2,3,4 -extra-info-digest AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -hidden-service-dir -allow-single-hop-exits -family $AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA $BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB -caches-extra-info -or-address [::1:2:3:4]:9999 -or-address 127.0.0.99:10000 -opt fred is a fine router -router-signature -{RSA-SIGNATURE} -""") +def analyze(s): + fields = {} + while s.startswith(":::"): + first,s=s.split("\n", 1) + m = re.match(r'^:::(\w+)=(.*)',first) + if not m: + raise ValueError(first) + k,v = m.groups() + fields[k] = v + return fields, s -if 0: - emit_ei("maximal", -"""\ -extra-info bob {FINGERPRINT-NOSPACE} -published 2014-10-05 20:07:00 -opt foobarbaz -read-history 900 1,2,3 -write-history 900 1,2,3 -dirreq-v2-ips 1 -dirreq-v3-ips 100 -dirreq-v3-reqs blahblah -dirreq-v2-share blahblah -dirreq-v3-share blahblah -dirreq-v2-resp djfkdj -dirreq-v3-resp djfkdj -dirreq-v2-direct-dl djfkdj -dirreq-v3-direct-dl djfkdj -dirreq-v2-tunneled-dl djfkdj -dirreq-v3-tunneled-dl djfkdj -dirreq-stats-end foobar -entry-ips jfsdfds -entry-stats-end ksdflkjfdkf -cell-stats-end FOO -cell-processed-cells FOO -cell-queued-cells FOO -cell-time-in-queue FOO -cell-circuits-per-decile FOO -exit-stats-end FOO -exit-kibibytes-written FOO -exit-kibibytes-read FOO -exit-streams-opened FOO -router-signature -{RSA-SIGNATURE} -""") +def process_file(s): + fields, s = analyze(s) + try: + name = fields['name'] + tp = fields['type'] + except KeyError: + raise ValueError("missing required field") -if 0: - emit_ei("minimal", -"""\ -extra-info bob {FINGERPRINT-NOSPACE} -published 2014-10-05 20:07:00 -router-signature -{RSA-SIGNATURE} -""") + if tp == 'ei': + emit_ei(name, s) + elif tp == 'ri': + emit_ri(name, s) + else: + raise ValueError("unrecognized type") +if __name__ == '__main__': + import sys + for fn in sys.argv[1:]: + process_file(open(fn).read()) diff --git a/scripts/codegen/run_trunnel.sh b/scripts/codegen/run_trunnel.sh index 5f694ce6c9..d2669931e9 100755 --- a/scripts/codegen/run_trunnel.sh +++ b/scripts/codegen/run_trunnel.sh @@ -5,7 +5,7 @@ if test "x$TRUNNEL_PATH" != "x"; then export PYTHONPATH fi -python -m trunnel --require-version=1.2 ./src/trunnel/*.trunnel +python -m trunnel --require-version=1.4 ./src/trunnel/*.trunnel -python -m trunnel --require-version=1.2 --write-c-files --target-dir=./src/ext/trunnel/ +python -m trunnel --require-version=1.4 --write-c-files --target-dir=./src/ext/trunnel/ diff --git a/src/common/container.c b/src/common/container.c index 864fd8a552..082afb51ee 100644 --- a/src/common/container.c +++ b/src/common/container.c @@ -208,6 +208,19 @@ smartlist_string_pos(const smartlist_t *sl, const char *element) return -1; } +/** If element is the same pointer as an element of sl, return + * that element's index. Otherwise, return -1. */ +int +smartlist_pos(const smartlist_t *sl, const void *element) +{ + int i; + if (!sl) return -1; + for (i=0; i < sl->num_used; i++) + if (element == sl->list[i]) + return i; + return -1; +} + /** Return true iff sl has some element E such that * !strcasecmp(E,element) */ diff --git a/src/common/container.h b/src/common/container.h index 457b5e4ea0..125900c8ca 100644 --- a/src/common/container.h +++ b/src/common/container.h @@ -38,6 +38,7 @@ void smartlist_reverse(smartlist_t *sl); void smartlist_string_remove(smartlist_t *sl, const char *element); int smartlist_contains(const smartlist_t *sl, const void *element); int smartlist_contains_string(const smartlist_t *sl, const char *element); +int smartlist_pos(const smartlist_t *sl, const void *element); int smartlist_string_pos(const smartlist_t *, const char *elt); int smartlist_contains_string_case(const smartlist_t *sl, const char *element); int smartlist_contains_int_as_string(const smartlist_t *sl, int num); diff --git a/src/common/crypto.c b/src/common/crypto.c index fcd862f045..d77d5e20dc 100644 --- a/src/common/crypto.c +++ b/src/common/crypto.c @@ -829,7 +829,7 @@ crypto_pk_public_exponent_ok(crypto_pk_t *env) * Note that this may leak information about the keys through timing. */ int -crypto_pk_cmp_keys(crypto_pk_t *a, crypto_pk_t *b) +crypto_pk_cmp_keys(const crypto_pk_t *a, const crypto_pk_t *b) { int result; char a_is_non_null = (a != NULL) && (a->key != NULL); @@ -855,19 +855,19 @@ crypto_pk_cmp_keys(crypto_pk_t *a, crypto_pk_t *b) * Note that this may leak information about the keys through timing. */ int -crypto_pk_eq_keys(crypto_pk_t *a, crypto_pk_t *b) +crypto_pk_eq_keys(const crypto_pk_t *a, const crypto_pk_t *b) { return (crypto_pk_cmp_keys(a, b) == 0); } /** Return the size of the public key modulus in env, in bytes. */ size_t -crypto_pk_keysize(crypto_pk_t *env) +crypto_pk_keysize(const crypto_pk_t *env) { tor_assert(env); tor_assert(env->key); - return (size_t) RSA_size(env->key); + return (size_t) RSA_size((RSA*)env->key); } /** Return the size of the public key modulus of env, in bits. */ @@ -996,7 +996,7 @@ crypto_pk_private_decrypt(crypto_pk_t *env, char *to, * at least the length of the modulus of env. */ int -crypto_pk_public_checksig(crypto_pk_t *env, char *to, +crypto_pk_public_checksig(const crypto_pk_t *env, char *to, size_t tolen, const char *from, size_t fromlen) { @@ -1068,7 +1068,7 @@ crypto_pk_public_checksig_digest(crypto_pk_t *env, const char *data, * at least the length of the modulus of env. */ int -crypto_pk_private_sign(crypto_pk_t *env, char *to, size_t tolen, +crypto_pk_private_sign(const crypto_pk_t *env, char *to, size_t tolen, const char *from, size_t fromlen) { int r; @@ -1083,7 +1083,7 @@ crypto_pk_private_sign(crypto_pk_t *env, char *to, size_t tolen, r = RSA_private_encrypt((int)fromlen, (unsigned char*)from, (unsigned char*)to, - env->key, RSA_PKCS1_PADDING); + (RSA*)env->key, RSA_PKCS1_PADDING); if (r<0) { crypto_log_errors(LOG_WARN, "generating RSA signature"); return -1; @@ -1297,7 +1297,7 @@ crypto_pk_get_digest(const crypto_pk_t *pk, char *digest_out) unsigned char *buf = NULL; int len; - len = i2d_RSAPublicKey(pk->key, &buf); + len = i2d_RSAPublicKey((RSA*)pk->key, &buf); if (len < 0 || buf == NULL) return -1; if (crypto_digest(digest_out, (char*)buf, len) < 0) { @@ -1795,7 +1795,24 @@ crypto_digest_assign(crypto_digest_t *into, * out_len must be \<= DIGEST256_LEN. */ void crypto_digest_smartlist(char *digest_out, size_t len_out, - const smartlist_t *lst, const char *append, + const smartlist_t *lst, + const char *append, + digest_algorithm_t alg) +{ + crypto_digest_smartlist_prefix(digest_out, len_out, NULL, lst, append, alg); +} + +/** Given a list of strings in lst, set the len_out-byte digest + * at digest_out to the hash of the concatenation of: the + * optional string prepend, those strings, + * and the optional string append, computed with the algorithm + * alg. + * out_len must be \<= DIGEST256_LEN. */ +void +crypto_digest_smartlist_prefix(char *digest_out, size_t len_out, + const char *prepend, + const smartlist_t *lst, + const char *append, digest_algorithm_t alg) { crypto_digest_t *d; @@ -1803,6 +1820,8 @@ crypto_digest_smartlist(char *digest_out, size_t len_out, d = crypto_digest_new(); else d = crypto_digest256_new(alg); + if (prepend) + crypto_digest_add_bytes(d, prepend, strlen(prepend)); SMARTLIST_FOREACH(lst, const char *, cp, crypto_digest_add_bytes(d, cp, strlen(cp))); if (append) @@ -2673,6 +2692,65 @@ base64_encode(char *dest, size_t destlen, const char *src, size_t srclen, return (int) enclen; } +/** As base64_encode, but do not add any internal spaces or external padding + * to the output stream. */ +int +base64_encode_nopad(char *dest, size_t destlen, + const uint8_t *src, size_t srclen) +{ + int n = base64_encode(dest, destlen, (const char*) src, srclen, 0); + if (n <= 0) + return n; + tor_assert((size_t)n < destlen && dest[n] == 0); + char *in, *out; + in = out = dest; + while (*in) { + if (*in == '=' || *in == '\n') { + ++in; + } else { + *out++ = *in++; + } + } + *out = 0; + + tor_assert(out - dest <= INT_MAX); + + return (int)(out - dest); +} + +/** As base64_decode, but do not require any padding on the input */ +int +base64_decode_nopad(uint8_t *dest, size_t destlen, + const char *src, size_t srclen) +{ + if (srclen > SIZE_T_CEILING - 4) + return -1; + char *buf = tor_malloc(srclen + 4); + memcpy(buf, src, srclen+1); + size_t buflen; + switch (srclen % 4) + { + case 0: + default: + buflen = srclen; + break; + case 1: + tor_free(buf); + return -1; + case 2: + memcpy(buf+srclen, "==", 3); + buflen = srclen + 2; + break; + case 3: + memcpy(buf+srclen, "=", 2); + buflen = srclen + 1; + break; + } + int n = base64_decode((char*)dest, destlen, buf, buflen); + tor_free(buf); + return n; +} + #undef BASE64_OPENSSL_LINELEN /** @{ */ @@ -2797,6 +2875,7 @@ base64_decode(char *dest, size_t destlen, const char *src, size_t srclen) /** Base64 encode DIGEST_LINE bytes from digest, remove the trailing = * characters, and store the nul-terminated result in the first * BASE64_DIGEST_LEN+1 bytes of d64. */ +/* XXXX unify with crypto_format.c code */ int digest_to_base64(char *d64, const char *digest) { @@ -2810,6 +2889,7 @@ digest_to_base64(char *d64, const char *digest) /** Given a base64 encoded, nul-terminated digest in d64 (without * trailing newline or = characters), decode it and store the result in the * first DIGEST_LEN bytes at digest. */ +/* XXXX unify with crypto_format.c code */ int digest_from_base64(char *digest, const char *d64) { @@ -2821,7 +2901,8 @@ digest_from_base64(char *digest, const char *d64) /** Base64 encode DIGEST256_LINE bytes from digest, remove the * trailing = characters, and store the nul-terminated result in the first - * BASE64_DIGEST256_LEN+1 bytes of d64. */ + * BASE64_DIGEST256_LEN+1 bytes of d64. */ + /* XXXX unify with crypto_format.c code */ int digest256_to_base64(char *d64, const char *digest) { @@ -2835,6 +2916,7 @@ digest256_to_base64(char *d64, const char *digest) /** Given a base64 encoded, nul-terminated digest in d64 (without * trailing newline or = characters), decode it and store the result in the * first DIGEST256_LEN bytes at digest. */ +/* XXXX unify with crypto_format.c code */ int digest256_from_base64(char *digest, const char *d64) { diff --git a/src/common/crypto.h b/src/common/crypto.h index 05572f4565..b953ab93e7 100644 --- a/src/common/crypto.h +++ b/src/common/crypto.h @@ -146,9 +146,9 @@ int crypto_pk_write_private_key_to_filename(crypto_pk_t *env, const char *fname); int crypto_pk_check_key(crypto_pk_t *env); -int crypto_pk_cmp_keys(crypto_pk_t *a, crypto_pk_t *b); -int crypto_pk_eq_keys(crypto_pk_t *a, crypto_pk_t *b); -size_t crypto_pk_keysize(crypto_pk_t *env); +int crypto_pk_cmp_keys(const crypto_pk_t *a, const crypto_pk_t *b); +int crypto_pk_eq_keys(const crypto_pk_t *a, const crypto_pk_t *b); +size_t crypto_pk_keysize(const crypto_pk_t *env); int crypto_pk_num_bits(crypto_pk_t *env); crypto_pk_t *crypto_pk_dup_key(crypto_pk_t *orig); crypto_pk_t *crypto_pk_copy_full(crypto_pk_t *orig); @@ -160,11 +160,11 @@ int crypto_pk_public_encrypt(crypto_pk_t *env, char *to, size_t tolen, int crypto_pk_private_decrypt(crypto_pk_t *env, char *to, size_t tolen, const char *from, size_t fromlen, int padding, int warnOnFailure); -int crypto_pk_public_checksig(crypto_pk_t *env, char *to, size_t tolen, +int crypto_pk_public_checksig(const crypto_pk_t *env, char *to, size_t tolen, const char *from, size_t fromlen); int crypto_pk_public_checksig_digest(crypto_pk_t *env, const char *data, size_t datalen, const char *sig, size_t siglen); -int crypto_pk_private_sign(crypto_pk_t *env, char *to, size_t tolen, +int crypto_pk_private_sign(const crypto_pk_t *env, char *to, size_t tolen, const char *from, size_t fromlen); int crypto_pk_private_sign_digest(crypto_pk_t *env, char *to, size_t tolen, const char *from, size_t fromlen); @@ -209,6 +209,11 @@ int crypto_digest256(char *digest, const char *m, size_t len, digest_algorithm_t algorithm); int crypto_digest_all(digests_t *ds_out, const char *m, size_t len); struct smartlist_t; +void crypto_digest_smartlist_prefix(char *digest_out, size_t len_out, + const char *prepend, + const struct smartlist_t *lst, + const char *append, + digest_algorithm_t alg); void crypto_digest_smartlist(char *digest_out, size_t len_out, const struct smartlist_t *lst, const char *append, digest_algorithm_t alg); @@ -278,6 +283,11 @@ size_t base64_encode_size(size_t srclen, int flags); int base64_encode(char *dest, size_t destlen, const char *src, size_t srclen, int flags); int base64_decode(char *dest, size_t destlen, const char *src, size_t srclen); +int base64_encode_nopad(char *dest, size_t destlen, + const uint8_t *src, size_t srclen); +int base64_decode_nopad(uint8_t *dest, size_t destlen, + const char *src, size_t srclen); + /** Characters that can appear (case-insensitively) in a base32 encoding. */ #define BASE32_CHARS "abcdefghijklmnopqrstuvwxyz234567" void base32_encode(char *dest, size_t destlen, const char *src, size_t srclen); diff --git a/src/common/crypto_ed25519.c b/src/common/crypto_ed25519.c index f2e6945ac8..6b93751dda 100644 --- a/src/common/crypto_ed25519.c +++ b/src/common/crypto_ed25519.c @@ -351,3 +351,24 @@ ed25519_pubkey_read_from_file(ed25519_public_key_t *pubkey_out, return 0; } +/** Release all storage held for kp. */ +void +ed25519_keypair_free(ed25519_keypair_t *kp) +{ + if (! kp) + return; + + memwipe(kp, 0, sizeof(*kp)); + tor_free(kp); +} + +/** Return true iff key1 and key2 are the same public key. */ +int +ed25519_pubkey_eq(const ed25519_public_key_t *key1, + const ed25519_public_key_t *key2) +{ + tor_assert(key1); + tor_assert(key2); + return tor_memeq(key1->pubkey, key2->pubkey, ED25519_PUBKEY_LEN); +} + diff --git a/src/common/crypto_ed25519.h b/src/common/crypto_ed25519.h index 7efa74bff5..8ffb9f26f3 100644 --- a/src/common/crypto_ed25519.h +++ b/src/common/crypto_ed25519.h @@ -6,6 +6,7 @@ #include "testsupport.h" #include "torint.h" +#include "crypto_curve25519.h" #define ED25519_PUBKEY_LEN 32 #define ED25519_SECKEY_LEN 64 @@ -60,7 +61,7 @@ int ed25519_checksig(const ed25519_signature_t *signature, */ typedef struct { /** The public key that supposedly generated the signature. */ - ed25519_public_key_t *pubkey; + const ed25519_public_key_t *pubkey; /** The signature to check. */ ed25519_signature_t signature; /** The message that the signature is supposed to have been applied to. */ @@ -87,13 +88,22 @@ int ed25519_public_blind(ed25519_public_key_t *out, const ed25519_public_key_t *inp, const uint8_t *param); -#define ED25519_BASE64_LEN 43 +/* XXXX move these to crypto_format.h */ +#define ED25519_BASE64_LEN 43 int ed25519_public_from_base64(ed25519_public_key_t *pkey, const char *input); int ed25519_public_to_base64(char *output, const ed25519_public_key_t *pkey); +/* XXXX move these to crypto_format.h */ +#define ED25519_SIG_BASE64_LEN 86 + +int ed25519_signature_from_base64(ed25519_signature_t *sig, + const char *input); +int ed25519_signature_to_base64(char *output, + const ed25519_signature_t *sig); + /* XXXX read encrypted, write encrypted. */ int ed25519_seckey_write_to_file(const ed25519_secret_key_t *seckey, @@ -109,5 +119,10 @@ int ed25519_pubkey_read_from_file(ed25519_public_key_t *pubkey_out, char **tag_out, const char *filename); +void ed25519_keypair_free(ed25519_keypair_t *kp); + +int ed25519_pubkey_eq(const ed25519_public_key_t *key1, + const ed25519_public_key_t *key2); + #endif diff --git a/src/common/crypto_format.c b/src/common/crypto_format.c index 35a7854404..e825132cb9 100644 --- a/src/common/crypto_format.c +++ b/src/common/crypto_format.c @@ -65,3 +65,42 @@ ed25519_public_to_base64(char *output, return digest256_to_base64(output, (const char *)pkey->pubkey); } +/** Encode the signature sig into the buffer at output, + * which must have space for ED25519_SIG_BASE64_LEN bytes of encoded signature, + * plus one byte for a terminating NUL. Return 0 on success, -1 on failure. + */ +int +ed25519_signature_to_base64(char *output, + const ed25519_signature_t *sig) +{ + char buf[256]; + int n = base64_encode_nopad(buf, sizeof(buf), sig->sig, ED25519_SIG_LEN); + tor_assert(n == ED25519_SIG_BASE64_LEN); + memcpy(output, buf, ED25519_SIG_BASE64_LEN+1); + return 0; +} + +/** Try to decode the string input into an ed25519 signature. On + * success, store the value in sig and return 0. Otherwise return + * -1. */ +int +ed25519_signature_from_base64(ed25519_signature_t *sig, + const char *input) +{ + + if (strlen(input) != ED25519_SIG_BASE64_LEN) + return -1; + char buf[ED25519_SIG_BASE64_LEN+3]; + memcpy(buf, input, ED25519_SIG_BASE64_LEN); + buf[ED25519_SIG_BASE64_LEN+0] = '='; + buf[ED25519_SIG_BASE64_LEN+1] = '='; + buf[ED25519_SIG_BASE64_LEN+2] = 0; + char decoded[128]; + int n = base64_decode(decoded, sizeof(decoded), buf, strlen(buf)); + if (n < 0 || n != ED25519_SIG_LEN) + return -1; + memcpy(sig->sig, decoded, ED25519_SIG_LEN); + + return 0; +} + diff --git a/src/common/tortls.c b/src/common/tortls.c index 2b8daaaf06..098df9db6c 100644 --- a/src/common/tortls.c +++ b/src/common/tortls.c @@ -117,7 +117,7 @@ #endif /** Structure that we use for a single certificate. */ -struct tor_cert_t { +struct tor_x509_cert_t { X509 *cert; uint8_t *encoded; size_t encoded_len; @@ -132,9 +132,9 @@ struct tor_cert_t { typedef struct tor_tls_context_t { int refcnt; SSL_CTX *ctx; - tor_cert_t *my_link_cert; - tor_cert_t *my_id_cert; - tor_cert_t *my_auth_cert; + tor_x509_cert_t *my_link_cert; + tor_x509_cert_t *my_id_cert; + tor_x509_cert_t *my_auth_cert; crypto_pk_t *link_key; crypto_pk_t *auth_key; } tor_tls_context_t; @@ -765,7 +765,7 @@ static const int N_CLIENT_CIPHERS = ARRAY_LENGTH(CLIENT_CIPHER_INFO_LIST); /** Free all storage held in cert */ void -tor_cert_free(tor_cert_t *cert) +tor_x509_cert_free(tor_x509_cert_t *cert) { if (! cert) return; @@ -777,14 +777,14 @@ tor_cert_free(tor_cert_t *cert) } /** - * Allocate a new tor_cert_t to hold the certificate "x509_cert". + * Allocate a new tor_x509_cert_t to hold the certificate "x509_cert". * * Steals a reference to x509_cert. */ -static tor_cert_t * -tor_cert_new(X509 *x509_cert) +static tor_x509_cert_t * +tor_x509_cert_new(X509 *x509_cert) { - tor_cert_t *cert; + tor_x509_cert_t *cert; EVP_PKEY *pkey; RSA *rsa; int length; @@ -794,7 +794,7 @@ tor_cert_new(X509 *x509_cert) return NULL; length = i2d_X509(x509_cert, &buf); - cert = tor_malloc_zero(sizeof(tor_cert_t)); + cert = tor_malloc_zero(sizeof(tor_x509_cert_t)); if (length <= 0 || buf == NULL) { tor_free(cert); log_err(LD_CRYPTO, "Couldn't get length of encoded x509 certificate"); @@ -824,14 +824,14 @@ tor_cert_new(X509 *x509_cert) } /** Read a DER-encoded X509 cert, of length exactly certificate_len, - * from a certificate. Return a newly allocated tor_cert_t on success - * and NULL on failure. */ -tor_cert_t * -tor_cert_decode(const uint8_t *certificate, size_t certificate_len) + * from a certificate. Return a newly allocated tor_x509_cert_t on + * success and NULL on failure. */ +tor_x509_cert_t * +tor_x509_cert_decode(const uint8_t *certificate, size_t certificate_len) { X509 *x509; const unsigned char *cp = (const unsigned char *)certificate; - tor_cert_t *newcert; + tor_x509_cert_t *newcert; tor_assert(certificate); check_no_tls_errors(); @@ -846,14 +846,14 @@ tor_cert_decode(const uint8_t *certificate, size_t certificate_len) X509_free(x509); goto err; /* Didn't use all the bytes */ } - newcert = tor_cert_new(x509); + newcert = tor_x509_cert_new(x509); if (!newcert) { goto err; } if (newcert->encoded_len != certificate_len || fast_memneq(newcert->encoded, certificate, certificate_len)) { /* Cert wasn't in DER */ - tor_cert_free(newcert); + tor_x509_cert_free(newcert); goto err; } return newcert; @@ -865,7 +865,7 @@ tor_cert_decode(const uint8_t *certificate, size_t certificate_len) /** Set *encoded_out and *size_out to cert's encoded DER * representation and length, respectively. */ void -tor_cert_get_der(const tor_cert_t *cert, +tor_x509_cert_get_der(const tor_x509_cert_t *cert, const uint8_t **encoded_out, size_t *size_out) { tor_assert(cert); @@ -878,7 +878,7 @@ tor_cert_get_der(const tor_cert_t *cert, /** Return a set of digests for the public key in cert, or NULL if this * cert's public key is not one we know how to take the digest of. */ const digests_t * -tor_cert_get_id_digests(const tor_cert_t *cert) +tor_x509_cert_get_id_digests(const tor_x509_cert_t *cert) { if (cert->pkey_digests_set) return &cert->pkey_digests; @@ -888,7 +888,7 @@ tor_cert_get_id_digests(const tor_cert_t *cert) /** Return a set of digests for the public key in cert. */ const digests_t * -tor_cert_get_cert_digests(const tor_cert_t *cert) +tor_x509_cert_get_cert_digests(const tor_x509_cert_t *cert) { return &cert->cert_digests; } @@ -901,9 +901,9 @@ tor_tls_context_decref(tor_tls_context_t *ctx) tor_assert(ctx); if (--ctx->refcnt == 0) { SSL_CTX_free(ctx->ctx); - tor_cert_free(ctx->my_link_cert); - tor_cert_free(ctx->my_id_cert); - tor_cert_free(ctx->my_auth_cert); + tor_x509_cert_free(ctx->my_link_cert); + tor_x509_cert_free(ctx->my_id_cert); + tor_x509_cert_free(ctx->my_auth_cert); crypto_pk_free(ctx->link_key); crypto_pk_free(ctx->auth_key); tor_free(ctx); @@ -917,8 +917,8 @@ tor_tls_context_decref(tor_tls_context_t *ctx) * client mode. */ int tor_tls_get_my_certs(int server, - const tor_cert_t **link_cert_out, - const tor_cert_t **id_cert_out) + const tor_x509_cert_t **link_cert_out, + const tor_x509_cert_t **id_cert_out) { tor_tls_context_t *ctx = server ? server_tls_context : client_tls_context; if (! ctx) @@ -947,7 +947,7 @@ tor_tls_get_my_client_auth_key(void) * certifies. Return NULL if the cert's key is not RSA. */ crypto_pk_t * -tor_tls_cert_get_key(tor_cert_t *cert) +tor_tls_cert_get_key(tor_x509_cert_t *cert) { crypto_pk_t *result = NULL; EVP_PKEY *pkey = X509_get_pubkey(cert->cert); @@ -967,8 +967,8 @@ tor_tls_cert_get_key(tor_cert_t *cert) /** Return true iff the other side of tls has authenticated to us, and * the key certified in cert is the same as the key they used to do it. */ -int -tor_tls_cert_matches_key(const tor_tls_t *tls, const tor_cert_t *cert) +MOCK_IMPL(int, +tor_tls_cert_matches_key,(const tor_tls_t *tls, const tor_x509_cert_t *cert)) { X509 *peercert = SSL_get_peer_certificate(tls->ssl); EVP_PKEY *link_key = NULL, *cert_key = NULL; @@ -997,8 +997,8 @@ tor_tls_cert_matches_key(const tor_tls_t *tls, const tor_cert_t *cert) * we couldn't check it. */ int tor_tls_cert_is_valid(int severity, - const tor_cert_t *cert, - const tor_cert_t *signing_cert, + const tor_x509_cert_t *cert, + const tor_x509_cert_t *signing_cert, int check_rsa_1024) { check_no_tls_errors(); @@ -1209,9 +1209,9 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime, result = tor_malloc_zero(sizeof(tor_tls_context_t)); result->refcnt = 1; if (!is_client) { - result->my_link_cert = tor_cert_new(X509_dup(cert)); - result->my_id_cert = tor_cert_new(X509_dup(idcert)); - result->my_auth_cert = tor_cert_new(X509_dup(authcert)); + result->my_link_cert = tor_x509_cert_new(X509_dup(cert)); + result->my_id_cert = tor_x509_cert_new(X509_dup(idcert)); + result->my_auth_cert = tor_x509_cert_new(X509_dup(authcert)); if (!result->my_link_cert || !result->my_id_cert || !result->my_auth_cert) goto error; result->link_key = crypto_pk_dup_key(rsa); @@ -2346,15 +2346,15 @@ tor_tls_peer_has_cert(tor_tls_t *tls) } /** Return the peer certificate, or NULL if there isn't one. */ -tor_cert_t * -tor_tls_get_peer_cert(tor_tls_t *tls) +MOCK_IMPL(tor_x509_cert_t *, +tor_tls_get_peer_cert,(tor_tls_t *tls)) { X509 *cert; cert = SSL_get_peer_certificate(tls->ssl); tls_log_errors(tls, LOG_WARN, LD_HANDSHAKE, "getting peer certificate"); if (!cert) return NULL; - return tor_cert_new(cert); + return tor_x509_cert_new(cert); } /** Warn that a certificate lifetime extends through a certain range. */ @@ -2772,8 +2772,8 @@ tor_tls_server_got_renegotiate(tor_tls_t *tls) * the v3 handshake to prove that the client knows the TLS secrets for the * connection tls. Return 0 on success, -1 on failure. */ -int -tor_tls_get_tlssecrets(tor_tls_t *tls, uint8_t *secrets_out) +MOCK_IMPL(int, +tor_tls_get_tlssecrets,(tor_tls_t *tls, uint8_t *secrets_out)) { #define TLSSECRET_MAGIC "Tor V3 handshake TLS cross-certification" char buf[128]; diff --git a/src/common/tortls.h b/src/common/tortls.h index 9216e83150..124b77160f 100644 --- a/src/common/tortls.h +++ b/src/common/tortls.h @@ -19,7 +19,7 @@ typedef struct tor_tls_t tor_tls_t; /* Opaque structure to hold an X509 certificate. */ -typedef struct tor_cert_t tor_cert_t; +typedef struct tor_x509_cert_t tor_x509_cert_t; /* Possible return values for most tor_tls_* functions. */ #define MIN_TOR_TLS_ERROR_VAL_ -9 @@ -72,7 +72,7 @@ void tor_tls_set_renegotiate_callback(tor_tls_t *tls, int tor_tls_is_server(tor_tls_t *tls); void tor_tls_free(tor_tls_t *tls); int tor_tls_peer_has_cert(tor_tls_t *tls); -tor_cert_t *tor_tls_get_peer_cert(tor_tls_t *tls); +MOCK_DECL(tor_x509_cert_t *,tor_tls_get_peer_cert,(tor_tls_t *tls)); int tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_t **identity); int tor_tls_check_lifetime(int severity, tor_tls_t *tls, int past_tolerance, @@ -102,7 +102,7 @@ int tor_tls_used_v1_handshake(tor_tls_t *tls); int tor_tls_received_v3_certificate(tor_tls_t *tls); int tor_tls_get_num_server_handshakes(tor_tls_t *tls); int tor_tls_server_got_renegotiate(tor_tls_t *tls); -int tor_tls_get_tlssecrets(tor_tls_t *tls, uint8_t *secrets_out); +MOCK_DECL(int,tor_tls_get_tlssecrets,(tor_tls_t *tls, uint8_t *secrets_out)); /* Log and abort if there are unhandled TLS errors in OpenSSL's error stack. */ @@ -120,22 +120,23 @@ struct bufferevent *tor_tls_init_bufferevent(tor_tls_t *tls, int filter); #endif -void tor_cert_free(tor_cert_t *cert); -tor_cert_t *tor_cert_decode(const uint8_t *certificate, +void tor_x509_cert_free(tor_x509_cert_t *cert); +tor_x509_cert_t *tor_x509_cert_decode(const uint8_t *certificate, size_t certificate_len); -void tor_cert_get_der(const tor_cert_t *cert, +void tor_x509_cert_get_der(const tor_x509_cert_t *cert, const uint8_t **encoded_out, size_t *size_out); -const digests_t *tor_cert_get_id_digests(const tor_cert_t *cert); -const digests_t *tor_cert_get_cert_digests(const tor_cert_t *cert); +const digests_t *tor_x509_cert_get_id_digests(const tor_x509_cert_t *cert); +const digests_t *tor_x509_cert_get_cert_digests(const tor_x509_cert_t *cert); int tor_tls_get_my_certs(int server, - const tor_cert_t **link_cert_out, - const tor_cert_t **id_cert_out); + const tor_x509_cert_t **link_cert_out, + const tor_x509_cert_t **id_cert_out); crypto_pk_t *tor_tls_get_my_client_auth_key(void); -crypto_pk_t *tor_tls_cert_get_key(tor_cert_t *cert); -int tor_tls_cert_matches_key(const tor_tls_t *tls, const tor_cert_t *cert); +crypto_pk_t *tor_tls_cert_get_key(tor_x509_cert_t *cert); +MOCK_DECL(int,tor_tls_cert_matches_key,(const tor_tls_t *tls, + const tor_x509_cert_t *cert)); int tor_tls_cert_is_valid(int severity, - const tor_cert_t *cert, - const tor_cert_t *signing_cert, + const tor_x509_cert_t *cert, + const tor_x509_cert_t *signing_cert, int check_rsa_1024); const char *tor_tls_get_ciphersuite_name(tor_tls_t *tls); diff --git a/src/or/channel.c b/src/or/channel.c index bf0387f10e..af095026e4 100644 --- a/src/or/channel.c +++ b/src/or/channel.c @@ -4431,10 +4431,10 @@ channel_num_circuits(channel_t *chan) * This is called when setting up a channel and replaces the old * connection_or_set_circid_type() */ -void -channel_set_circid_type(channel_t *chan, - crypto_pk_t *identity_rcvd, - int consider_identity) +MOCK_IMPL(void, +channel_set_circid_type,(channel_t *chan, + crypto_pk_t *identity_rcvd, + int consider_identity)) { int started_here; crypto_pk_t *our_identity; diff --git a/src/or/channel.h b/src/or/channel.h index ecc2a092e4..2b38ca7e19 100644 --- a/src/or/channel.h +++ b/src/or/channel.h @@ -562,8 +562,9 @@ int channel_matches_extend_info(channel_t *chan, extend_info_t *extend_info); int channel_matches_target_addr_for_extend(channel_t *chan, const tor_addr_t *target); unsigned int channel_num_circuits(channel_t *chan); -void channel_set_circid_type(channel_t *chan, crypto_pk_t *identity_rcvd, - int consider_identity); +MOCK_DECL(void,channel_set_circid_type,(channel_t *chan, + crypto_pk_t *identity_rcvd, + int consider_identity)); void channel_timestamp_client(channel_t *chan); void channel_update_xmit_queue_size(channel_t *chan); diff --git a/src/or/channeltls.c b/src/or/channeltls.c index 1cf697ccc5..ecf02182fc 100644 --- a/src/or/channeltls.c +++ b/src/or/channeltls.c @@ -13,6 +13,8 @@ #define TOR_CHANNEL_INTERNAL_ +#define CHANNELTLS_PRIVATE + #include "or.h" #include "channel.h" #include "channeltls.h" @@ -22,6 +24,7 @@ #include "connection.h" #include "connection_or.h" #include "control.h" +#include "link_handshake.h" #include "relay.h" #include "rephist.h" #include "router.h" @@ -48,9 +51,6 @@ uint64_t stats_n_authorize_cells_processed = 0; /** Active listener, if any */ channel_listener_t *channel_tls_listener = NULL; -/* Utility function declarations */ -static void channel_tls_common_init(channel_tls_t *tlschan); - /* channel_tls_t method declarations */ static void channel_tls_close_method(channel_t *chan); @@ -92,12 +92,6 @@ static void channel_tls_process_versions_cell(var_cell_t *cell, channel_tls_t *tlschan); static void channel_tls_process_netinfo_cell(cell_t *cell, channel_tls_t *tlschan); -static void channel_tls_process_certs_cell(var_cell_t *cell, - channel_tls_t *tlschan); -static void channel_tls_process_auth_challenge_cell(var_cell_t *cell, - channel_tls_t *tlschan); -static void channel_tls_process_authenticate_cell(var_cell_t *cell, - channel_tls_t *tlschan); static int command_allowed_before_handshake(uint8_t command); static int enter_v3_handshake_with_cell(var_cell_t *cell, channel_tls_t *tlschan); @@ -107,7 +101,7 @@ static int enter_v3_handshake_with_cell(var_cell_t *cell, * and channel_tls_handle_incoming(). */ -static void +STATIC void channel_tls_common_init(channel_tls_t *tlschan) { channel_t *chan; @@ -1747,16 +1741,17 @@ channel_tls_process_netinfo_cell(cell_t *cell, channel_tls_t *chan) * If it's the server side, wait for an AUTHENTICATE cell. */ -static void +STATIC void channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan) { - tor_cert_t *link_cert = NULL; - tor_cert_t *id_cert = NULL; - tor_cert_t *auth_cert = NULL; - uint8_t *ptr; +#define MAX_CERT_TYPE_WANTED OR_CERT_TYPE_AUTH_1024 + tor_x509_cert_t *certs[MAX_CERT_TYPE_WANTED + 1]; int n_certs, i; + certs_cell_t *cc = NULL; + int send_netinfo = 0; + memset(certs, 0, sizeof(certs)); tor_assert(cell); tor_assert(chan); tor_assert(chan->conn); @@ -1786,63 +1781,41 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan) if (cell->circ_id) ERR("It had a nonzero circuit ID"); - n_certs = cell->payload[0]; - ptr = cell->payload + 1; + if (certs_cell_parse(&cc, cell->payload, cell->payload_len) < 0) + ERR("It couldn't be parsed."); + + n_certs = cc->n_certs; + for (i = 0; i < n_certs; ++i) { - uint8_t cert_type; - uint16_t cert_len; - if (cell->payload_len < 3) - goto truncated; - if (ptr > cell->payload + cell->payload_len - 3) { - goto truncated; - } - cert_type = *ptr; - cert_len = ntohs(get_uint16(ptr+1)); - if (cell->payload_len < 3 + cert_len) - goto truncated; - if (ptr > cell->payload + cell->payload_len - cert_len - 3) { - goto truncated; - } - if (cert_type == OR_CERT_TYPE_TLS_LINK || - cert_type == OR_CERT_TYPE_ID_1024 || - cert_type == OR_CERT_TYPE_AUTH_1024) { - tor_cert_t *cert = tor_cert_decode(ptr + 3, cert_len); - if (!cert) { - log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, - "Received undecodable certificate in CERTS cell from %s:%d", - safe_str(chan->conn->base_.address), - chan->conn->base_.port); + certs_cell_cert_t *c = certs_cell_get_certs(cc, i); + + uint16_t cert_type = c->cert_type; + uint16_t cert_len = c->cert_len; + uint8_t *cert_body = certs_cell_cert_getarray_body(c); + + if (cert_type > MAX_CERT_TYPE_WANTED) + continue; + + tor_x509_cert_t *cert = tor_x509_cert_decode(cert_body, cert_len); + if (!cert) { + log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, + "Received undecodable certificate in CERTS cell from %s:%d", + safe_str(chan->conn->base_.address), + chan->conn->base_.port); + } else { + if (certs[cert_type]) { + tor_x509_cert_free(cert); + ERR("Duplicate x509 certificate"); } else { - if (cert_type == OR_CERT_TYPE_TLS_LINK) { - if (link_cert) { - tor_cert_free(cert); - ERR("Too many TLS_LINK certificates"); - } - link_cert = cert; - } else if (cert_type == OR_CERT_TYPE_ID_1024) { - if (id_cert) { - tor_cert_free(cert); - ERR("Too many ID_1024 certificates"); - } - id_cert = cert; - } else if (cert_type == OR_CERT_TYPE_AUTH_1024) { - if (auth_cert) { - tor_cert_free(cert); - ERR("Too many AUTH_1024 certificates"); - } - auth_cert = cert; - } else { - tor_cert_free(cert); - } + certs[cert_type] = cert; } } - ptr += 3 + cert_len; - continue; - - truncated: - ERR("It ends in the middle of a certificate"); } + tor_x509_cert_t *id_cert = certs[OR_CERT_TYPE_ID_1024]; + tor_x509_cert_t *auth_cert = certs[OR_CERT_TYPE_AUTH_1024]; + tor_x509_cert_t *link_cert = certs[OR_CERT_TYPE_TLS_LINK]; + if (chan->conn->handshake_state->started_here) { int severity; if (! (id_cert && link_cert)) @@ -1867,7 +1840,7 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan) chan->conn->handshake_state->authenticated = 1; { - const digests_t *id_digests = tor_cert_get_id_digests(id_cert); + const digests_t *id_digests = tor_x509_cert_get_id_digests(id_cert); crypto_pk_t *identity_rcvd; if (!id_digests) ERR("Couldn't compute digests for key in ID cert"); @@ -1891,7 +1864,7 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan) safe_str(chan->conn->base_.address), chan->conn->base_.port); chan->conn->handshake_state->id_cert = id_cert; - id_cert = NULL; + certs[OR_CERT_TYPE_ID_1024] = NULL; if (!public_server_mode(get_options())) { /* If we initiated the connection and we are not a public server, we @@ -1918,7 +1891,7 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan) chan->conn->handshake_state->id_cert = id_cert; chan->conn->handshake_state->auth_cert = auth_cert; - id_cert = auth_cert = NULL; + certs[OR_CERT_TYPE_ID_1024] = certs[OR_CERT_TYPE_AUTH_1024] = NULL; } chan->conn->handshake_state->received_certs_cell = 1; @@ -1932,9 +1905,10 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan) } err: - tor_cert_free(id_cert); - tor_cert_free(link_cert); - tor_cert_free(auth_cert); + for (unsigned i = 0; i < ARRAY_LENGTH(certs); ++i) { + tor_x509_cert_free(certs[i]); + } + certs_cell_free(cc); #undef ERR } @@ -1949,11 +1923,11 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan) * want to authenticate, send an AUTHENTICATE cell and then a NETINFO cell. */ -static void +STATIC void channel_tls_process_auth_challenge_cell(var_cell_t *cell, channel_tls_t *chan) { int n_types, i, use_type = -1; - uint8_t *cp; + auth_challenge_cell_t *ac = NULL; tor_assert(cell); tor_assert(chan); @@ -1966,7 +1940,7 @@ channel_tls_process_auth_challenge_cell(var_cell_t *cell, channel_tls_t *chan) safe_str(chan->conn->base_.address), \ chan->conn->base_.port, (s)); \ connection_or_close_for_error(chan->conn, 0); \ - return; \ + goto done; \ } while (0) if (chan->conn->base_.state != OR_CONN_STATE_OR_HANDSHAKING_V3) @@ -1979,19 +1953,17 @@ channel_tls_process_auth_challenge_cell(var_cell_t *cell, channel_tls_t *chan) ERR("We already received one"); if (!(chan->conn->handshake_state->received_certs_cell)) ERR("We haven't gotten a CERTS cell yet"); - if (cell->payload_len < OR_AUTH_CHALLENGE_LEN + 2) - ERR("It was too short"); if (cell->circ_id) ERR("It had a nonzero circuit ID"); - n_types = ntohs(get_uint16(cell->payload + OR_AUTH_CHALLENGE_LEN)); - if (cell->payload_len < OR_AUTH_CHALLENGE_LEN + 2 + 2*n_types) - ERR("It looks truncated"); + if (auth_challenge_cell_parse(&ac, cell->payload, cell->payload_len) < 0) + ERR("It was not well-formed."); + + n_types = ac->n_methods; /* Now see if there is an authentication type we can use */ - cp = cell->payload+OR_AUTH_CHALLENGE_LEN + 2; - for (i = 0; i < n_types; ++i, cp += 2) { - uint16_t authtype = ntohs(get_uint16(cp)); + for (i = 0; i < n_types; ++i) { + uint16_t authtype = auth_challenge_cell_get_methods(ac, i); if (authtype == AUTHTYPE_RSA_SHA256_TLSSECRET) use_type = authtype; } @@ -2002,7 +1974,7 @@ channel_tls_process_auth_challenge_cell(var_cell_t *cell, channel_tls_t *chan) /* If we're not a public server then we don't want to authenticate on a connection we originated, and we already sent a NETINFO cell when we got the CERTS cell. We have nothing more to do. */ - return; + goto done; } if (use_type >= 0) { @@ -2016,7 +1988,7 @@ channel_tls_process_auth_challenge_cell(var_cell_t *cell, channel_tls_t *chan) log_warn(LD_OR, "Couldn't send authenticate cell"); connection_or_close_for_error(chan->conn, 0); - return; + goto done; } } else { log_info(LD_OR, @@ -2029,9 +2001,12 @@ channel_tls_process_auth_challenge_cell(var_cell_t *cell, channel_tls_t *chan) if (connection_or_send_netinfo(chan->conn) < 0) { log_warn(LD_OR, "Couldn't send netinfo cell"); connection_or_close_for_error(chan->conn, 0); - return; + goto done; } +done: + auth_challenge_cell_free(ac); + #undef ERR } @@ -2045,10 +2020,10 @@ channel_tls_process_auth_challenge_cell(var_cell_t *cell, channel_tls_t *chan) * the identity of the router on the other side of the connection. */ -static void +STATIC void channel_tls_process_authenticate_cell(var_cell_t *cell, channel_tls_t *chan) { - uint8_t expected[V3_AUTH_FIXED_PART_LEN]; + uint8_t expected[V3_AUTH_FIXED_PART_LEN+256]; const uint8_t *auth; int authlen; @@ -2104,11 +2079,13 @@ channel_tls_process_authenticate_cell(var_cell_t *cell, channel_tls_t *chan) if (authlen < V3_AUTH_BODY_LEN + 1) ERR("Authenticator was too short"); - if (connection_or_compute_authenticate_cell_body( - chan->conn, expected, sizeof(expected), NULL, 1) < 0) + ssize_t bodylen = + connection_or_compute_authenticate_cell_body( + chan->conn, expected, sizeof(expected), NULL, 1); + if (bodylen < 0 || bodylen != V3_AUTH_FIXED_PART_LEN) ERR("Couldn't compute expected AUTHENTICATE cell body"); - if (tor_memneq(expected, auth, sizeof(expected))) + if (tor_memneq(expected, auth, bodylen)) ERR("Some field in the AUTHENTICATE cell body was not as expected"); { @@ -2154,7 +2131,7 @@ channel_tls_process_authenticate_cell(var_cell_t *cell, channel_tls_t *chan) crypto_pk_t *identity_rcvd = tor_tls_cert_get_key(chan->conn->handshake_state->id_cert); const digests_t *id_digests = - tor_cert_get_id_digests(chan->conn->handshake_state->id_cert); + tor_x509_cert_get_id_digests(chan->conn->handshake_state->id_cert); /* This must exist; we checked key type when reading the cert. */ tor_assert(id_digests); diff --git a/src/or/channeltls.h b/src/or/channeltls.h index 507429420b..a0df9faac2 100644 --- a/src/or/channeltls.h +++ b/src/or/channeltls.h @@ -52,5 +52,15 @@ void channel_tls_update_marks(or_connection_t *conn); /* Cleanup at shutdown */ void channel_tls_free_all(void); +#ifdef CHANNELTLS_PRIVATE +STATIC void channel_tls_process_certs_cell(var_cell_t *cell, + channel_tls_t *tlschan); +STATIC void channel_tls_process_auth_challenge_cell(var_cell_t *cell, + channel_tls_t *tlschan); +STATIC void channel_tls_common_init(channel_tls_t *tlschan); +STATIC void channel_tls_process_authenticate_cell(var_cell_t *cell, + channel_tls_t *tlschan); +#endif + #endif diff --git a/src/or/config.c b/src/or/config.c index e4a2d1c5ae..ef249a653b 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -300,6 +300,7 @@ static config_var_t option_vars_[] = { VAR("ServerTransportPlugin", LINELIST, ServerTransportPlugin, NULL), V(ServerTransportListenAddr, LINELIST, NULL), V(ServerTransportOptions, LINELIST, NULL), + V(SigningKeyLifetime, INTERVAL, "30 days"), V(Socks4Proxy, STRING, NULL), V(Socks5Proxy, STRING, NULL), V(Socks5ProxyUsername, STRING, NULL), @@ -358,6 +359,13 @@ static config_var_t option_vars_[] = { V(TestingTorNetwork, BOOL, "0"), V(TestingMinExitFlagThreshold, MEMUNIT, "0"), V(TestingMinFastFlagThreshold, MEMUNIT, "0"), + + V(TestingLinkCertLifetime, INTERVAL, "2 days"), + V(TestingAuthKeyLifetime, INTERVAL, "2 days"), + V(TestingLinkKeySlop, INTERVAL, "3 hours"), + V(TestingAuthKeySlop, INTERVAL, "3 hours"), + V(TestingSigningKeySlop, INTERVAL, "1 day"), + V(OptimisticData, AUTOBOOL, "auto"), V(PortForwarding, BOOL, "0"), V(PortForwardingHelper, FILENAME, "tor-fw-helper"), @@ -3688,8 +3696,20 @@ options_validate(or_options_t *old_options, or_options_t *options, CHECK_DEFAULT(TestingDescriptorMaxDownloadTries); CHECK_DEFAULT(TestingMicrodescMaxDownloadTries); CHECK_DEFAULT(TestingCertMaxDownloadTries); + CHECK_DEFAULT(TestingAuthKeyLifetime); + CHECK_DEFAULT(TestingLinkCertLifetime); + CHECK_DEFAULT(TestingSigningKeySlop); + CHECK_DEFAULT(TestingAuthKeySlop); + CHECK_DEFAULT(TestingLinkKeySlop); #undef CHECK_DEFAULT + if (options->SigningKeyLifetime < options->TestingSigningKeySlop*2) + REJECT("SigningKeyLifetime is too short."); + if (options->TestingLinkCertLifetime < options->TestingAuthKeySlop*2) + REJECT("LinkCertLifetime is too short."); + if (options->TestingAuthKeyLifetime < options->TestingLinkKeySlop*2) + REJECT("TestingAuthKeyLifetime is too short."); + if (options->TestingV3AuthInitialVotingInterval < MIN_VOTE_INTERVAL_TESTING_INITIAL) { REJECT("TestingV3AuthInitialVotingInterval is insanely low."); diff --git a/src/or/config.h b/src/or/config.h index 74b28f45ca..0ee1e1a3c4 100644 --- a/src/or/config.h +++ b/src/or/config.h @@ -61,6 +61,10 @@ char *options_get_datadir_fname2_suffix(const or_options_t *options, * get_datadir_fname2_suffix. */ #define get_datadir_fname2(sub1,sub2) \ get_datadir_fname2_suffix((sub1), (sub2), NULL) +/** Return a newly allocated string containing datadir/sub1/sub2 relative to + * opts. See get_datadir_fname2_suffix. */ +#define options_get_datadir_fname2(opts,sub1,sub2) \ + options_get_datadir_fname2_suffix((opts),(sub1), (sub2), NULL) /** Return a newly allocated string containing datadir/sub1suffix. See * get_datadir_fname2_suffix. */ #define get_datadir_fname_suffix(sub1, suffix) \ diff --git a/src/or/connection_or.c b/src/or/connection_or.c index e0dff1c915..48128d6335 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -30,6 +30,7 @@ #include "entrynodes.h" #include "geoip.h" #include "main.h" +#include "link_handshake.h" #include "networkstatus.h" #include "nodelist.h" #include "reasons.h" @@ -1318,8 +1319,8 @@ connection_or_close_normally(or_connection_t *orconn, int flush) * the error state. */ -void -connection_or_close_for_error(or_connection_t *orconn, int flush) +MOCK_IMPL(void, +connection_or_close_for_error,(or_connection_t *orconn, int flush)) { channel_t *chan = NULL; @@ -1879,8 +1880,8 @@ or_handshake_state_free(or_handshake_state_t *state) return; crypto_digest_free(state->digest_sent); crypto_digest_free(state->digest_received); - tor_cert_free(state->auth_cert); - tor_cert_free(state->id_cert); + tor_x509_cert_free(state->auth_cert); + tor_x509_cert_free(state->id_cert); memwipe(state, 0xBE, sizeof(or_handshake_state_t)); tor_free(state); } @@ -2013,9 +2014,9 @@ connection_or_write_cell_to_buf(const cell_t *cell, or_connection_t *conn) * conn's outbuf. Right now, this DOES NOT support cells that * affect a circuit. */ -void -connection_or_write_var_cell_to_buf(const var_cell_t *cell, - or_connection_t *conn) +MOCK_IMPL(void, +connection_or_write_var_cell_to_buf,(const var_cell_t *cell, + or_connection_t *conn)) { int n; char hdr[VAR_CELL_MAX_HEADER_SIZE]; @@ -2158,8 +2159,8 @@ connection_or_send_versions(or_connection_t *conn, int v3_plus) /** Send a NETINFO cell on conn, telling the other server what we know * about their address, our address, and the current time. */ -int -connection_or_send_netinfo(or_connection_t *conn) +MOCK_IMPL(int, +connection_or_send_netinfo,(or_connection_t *conn)) { cell_t cell; time_t now = time(NULL); @@ -2228,7 +2229,7 @@ connection_or_send_netinfo(or_connection_t *conn) int connection_or_send_certs_cell(or_connection_t *conn) { - const tor_cert_t *link_cert = NULL, *id_cert = NULL; + const tor_x509_cert_t *link_cert = NULL, *id_cert = NULL; const uint8_t *link_encoded = NULL, *id_encoded = NULL; size_t link_len, id_len; var_cell_t *cell; @@ -2243,8 +2244,8 @@ connection_or_send_certs_cell(or_connection_t *conn) server_mode = ! conn->handshake_state->started_here; if (tor_tls_get_my_certs(server_mode, &link_cert, &id_cert) < 0) return -1; - tor_cert_get_der(link_cert, &link_encoded, &link_len); - tor_cert_get_der(id_cert, &id_encoded, &id_len); + tor_x509_cert_get_der(link_cert, &link_encoded, &link_len); + tor_x509_cert_get_der(id_cert, &id_encoded, &id_len); cell_len = 1 /* 1 byte: num certs in cell */ + 2 * ( 1 + 2 ) /* For each cert: 1 byte for type, 2 for length */ + @@ -2280,28 +2281,37 @@ connection_or_send_certs_cell(or_connection_t *conn) int connection_or_send_auth_challenge_cell(or_connection_t *conn) { - var_cell_t *cell; - uint8_t *cp; - uint8_t challenge[OR_AUTH_CHALLENGE_LEN]; + var_cell_t *cell = NULL; + int r = -1; tor_assert(conn->base_.state == OR_CONN_STATE_OR_HANDSHAKING_V3); if (! conn->handshake_state) return -1; - if (crypto_rand((char*)challenge, OR_AUTH_CHALLENGE_LEN) < 0) - return -1; - cell = var_cell_new(OR_AUTH_CHALLENGE_LEN + 4); + auth_challenge_cell_t *ac = auth_challenge_cell_new(); + + if (crypto_rand((char*)ac->challenge, sizeof(ac->challenge)) < 0) + goto done; + + auth_challenge_cell_add_methods(ac, AUTHTYPE_RSA_SHA256_TLSSECRET); + auth_challenge_cell_set_n_methods(ac, + auth_challenge_cell_getlen_methods(ac)); + + cell = var_cell_new(auth_challenge_cell_encoded_len(ac)); + ssize_t len = auth_challenge_cell_encode(cell->payload, cell->payload_len, + ac); + if (len != cell->payload_len) + goto done; cell->command = CELL_AUTH_CHALLENGE; - memcpy(cell->payload, challenge, OR_AUTH_CHALLENGE_LEN); - cp = cell->payload + OR_AUTH_CHALLENGE_LEN; - set_uint16(cp, htons(1)); /* We recognize one authentication type. */ - set_uint16(cp+2, htons(AUTHTYPE_RSA_SHA256_TLSSECRET)); connection_or_write_var_cell_to_buf(cell, conn); - var_cell_free(cell); - memwipe(challenge, 0, sizeof(challenge)); + r = 0; - return 0; + done: + var_cell_free(cell); + auth_challenge_cell_free(ac); + + return r; } /** Compute the main body of an AUTHENTICATE cell that a client can use @@ -2328,28 +2338,28 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn, crypto_pk_t *signing_key, int server) { - uint8_t *ptr; + auth1_t *auth = NULL; + auth_ctx_t *ctx = auth_ctx_new(); + int result; /* assert state is reasonable XXXX */ - if (outlen < V3_AUTH_FIXED_PART_LEN || - (!server && outlen < V3_AUTH_BODY_LEN)) - return -1; + ctx->is_ed = 0; - ptr = out; + auth = auth1_new(); /* Type: 8 bytes. */ - memcpy(ptr, "AUTH0001", 8); - ptr += 8; + memcpy(auth1_getarray_type(auth), "AUTH0001", 8); { - const tor_cert_t *id_cert=NULL, *link_cert=NULL; + const tor_x509_cert_t *id_cert=NULL, *link_cert=NULL; const digests_t *my_digests, *their_digests; const uint8_t *my_id, *their_id, *client_id, *server_id; if (tor_tls_get_my_certs(server, &link_cert, &id_cert)) return -1; - my_digests = tor_cert_get_id_digests(id_cert); - their_digests = tor_cert_get_id_digests(conn->handshake_state->id_cert); + my_digests = tor_x509_cert_get_id_digests(id_cert); + their_digests = + tor_x509_cert_get_id_digests(conn->handshake_state->id_cert); tor_assert(my_digests); tor_assert(their_digests); my_id = (uint8_t*)my_digests->d[DIGEST_SHA256]; @@ -2359,12 +2369,10 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn, server_id = server ? my_id : their_id; /* Client ID digest: 32 octets. */ - memcpy(ptr, client_id, 32); - ptr += 32; + memcpy(auth->cid, client_id, 32); /* Server ID digest: 32 octets. */ - memcpy(ptr, server_id, 32); - ptr += 32; + memcpy(auth->sid, server_id, 32); } { @@ -2378,73 +2386,101 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn, } /* Server log digest : 32 octets */ - crypto_digest_get_digest(server_d, (char*)ptr, 32); - ptr += 32; + crypto_digest_get_digest(server_d, (char*)auth->slog, 32); /* Client log digest : 32 octets */ - crypto_digest_get_digest(client_d, (char*)ptr, 32); - ptr += 32; + crypto_digest_get_digest(client_d, (char*)auth->clog, 32); } { /* Digest of cert used on TLS link : 32 octets. */ - const tor_cert_t *cert = NULL; - tor_cert_t *freecert = NULL; + const tor_x509_cert_t *cert = NULL; + tor_x509_cert_t *freecert = NULL; if (server) { tor_tls_get_my_certs(1, &cert, NULL); } else { freecert = tor_tls_get_peer_cert(conn->tls); cert = freecert; } - if (!cert) - return -1; - memcpy(ptr, tor_cert_get_cert_digests(cert)->d[DIGEST_SHA256], 32); + if (!cert) { + log_warn(LD_OR, "Unable to find cert when making AUTH1 data."); + goto err; + } + + memcpy(auth->scert, + tor_x509_cert_get_cert_digests(cert)->d[DIGEST_SHA256], 32); if (freecert) - tor_cert_free(freecert); - ptr += 32; + tor_x509_cert_free(freecert); } /* HMAC of clientrandom and serverrandom using master key : 32 octets */ - tor_tls_get_tlssecrets(conn->tls, ptr); - ptr += 32; - - tor_assert(ptr - out == V3_AUTH_FIXED_PART_LEN); - - if (server) - return V3_AUTH_FIXED_PART_LEN; // ptr-out + tor_tls_get_tlssecrets(conn->tls, auth->tlssecrets); /* 8 octets were reserved for the current time, but we're trying to get out * of the habit of sending time around willynilly. Fortunately, nothing * checks it. That's followed by 16 bytes of nonce. */ - crypto_rand((char*)ptr, 24); - ptr += 24; + crypto_rand((char*)auth->rand, 24); - tor_assert(ptr - out == V3_AUTH_BODY_LEN); - - if (!signing_key) - return V3_AUTH_BODY_LEN; // ptr - out - - { - int siglen; - char d[32]; - crypto_digest256(d, (char*)out, ptr-out, DIGEST_SHA256); - siglen = crypto_pk_private_sign(signing_key, - (char*)ptr, outlen - (ptr-out), - d, 32); - if (siglen < 0) - return -1; - - ptr += siglen; - tor_assert(ptr <= out+outlen); - return (int)(ptr - out); + ssize_t len; + if ((len = auth1_encode(out, outlen, auth, ctx)) < 0) { + log_warn(LD_OR, "Unable to encode signed part of AUTH1 data."); + goto err; } + + if (server) { + auth1_t *tmp = NULL; + ssize_t len2 = auth1_parse(&tmp, out, len, ctx); + if (!tmp) { + log_warn(LD_OR, "Unable to parse signed part of AUTH1 data."); + goto err; + } + result = (int) (tmp->end_of_fixed_part - out); + auth1_free(tmp); + if (len2 != len) { + log_warn(LD_OR, "Mismatched length when re-parsing AUTH1 data."); + goto err; + } + goto done; + } + + if (signing_key) { + auth1_setlen_sig(auth, crypto_pk_keysize(signing_key)); + + char d[32]; + crypto_digest256(d, (char*)out, len, DIGEST_SHA256); + int siglen = crypto_pk_private_sign(signing_key, + (char*)auth1_getarray_sig(auth), + auth1_getlen_sig(auth), + d, 32); + if (siglen < 0) { + log_warn(LD_OR, "Unable to sign AUTH1 data."); + return -1; + } + + auth1_setlen_sig(auth, siglen); + + len = auth1_encode(out, outlen, auth, ctx); + if (len < 0) { + log_warn(LD_OR, "Unable to encode signed AUTH1 data."); + goto err; + } + } + result = (int) len; + goto done; + + err: + result = -1; + done: + auth1_free(auth); + auth_ctx_free(ctx); + return result; } /** Send an AUTHENTICATE cell on the connection conn. Return 0 on * success, -1 on failure */ -int -connection_or_send_authenticate_cell(or_connection_t *conn, int authtype) +MOCK_IMPL(int, +connection_or_send_authenticate_cell,(or_connection_t *conn, int authtype)) { var_cell_t *cell; crypto_pk_t *pk = tor_tls_get_my_client_auth_key(); diff --git a/src/or/connection_or.h b/src/or/connection_or.h index fc261c6bac..3877fd5a13 100644 --- a/src/or/connection_or.h +++ b/src/or/connection_or.h @@ -43,7 +43,8 @@ MOCK_DECL(or_connection_t *, const char *id_digest, channel_tls_t *chan)); void connection_or_close_normally(or_connection_t *orconn, int flush); -void connection_or_close_for_error(or_connection_t *orconn, int flush); +MOCK_DECL(void,connection_or_close_for_error, + (or_connection_t *orconn, int flush)); void connection_or_report_broken_states(int severity, int domain); @@ -77,17 +78,18 @@ void or_handshake_state_record_var_cell(or_connection_t *conn, int connection_or_set_state_open(or_connection_t *conn); void connection_or_write_cell_to_buf(const cell_t *cell, or_connection_t *conn); -void connection_or_write_var_cell_to_buf(const var_cell_t *cell, - or_connection_t *conn); +MOCK_DECL(void,connection_or_write_var_cell_to_buf,(const var_cell_t *cell, + or_connection_t *conn)); int connection_or_send_versions(or_connection_t *conn, int v3_plus); -int connection_or_send_netinfo(or_connection_t *conn); +MOCK_DECL(int,connection_or_send_netinfo,(or_connection_t *conn)); int connection_or_send_certs_cell(or_connection_t *conn); int connection_or_send_auth_challenge_cell(or_connection_t *conn); int connection_or_compute_authenticate_cell_body(or_connection_t *conn, uint8_t *out, size_t outlen, crypto_pk_t *signing_key, int server); -int connection_or_send_authenticate_cell(or_connection_t *conn, int type); +MOCK_DECL(int,connection_or_send_authenticate_cell, + (or_connection_t *conn, int type)); int is_or_protocol_version_known(uint16_t version); diff --git a/src/or/dircollate.c b/src/or/dircollate.c new file mode 100644 index 0000000000..20dfb357a8 --- /dev/null +++ b/src/or/dircollate.c @@ -0,0 +1,257 @@ +/* Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2014, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file dircollate.c + * + * \brief Collation code for figuring out which identities to vote for in + * the directory voting process. + */ + +#define DIRCOLLATE_PRIVATE +#include "dircollate.h" +#include "dirvote.h" + +static void dircollator_collate_by_rsa(dircollator_t *dc); +static void dircollator_collate_by_ed25519(dircollator_t *dc); + +typedef struct ddmap_entry_s { + HT_ENTRY(ddmap_entry_s) node; + uint8_t d[DIGEST_LEN + DIGEST256_LEN]; + vote_routerstatus_t *vrs_lst[FLEXIBLE_ARRAY_MEMBER]; +} ddmap_entry_t; + +struct double_digest_map_s *by_both_ids; + +static void +ddmap_entry_free(ddmap_entry_t *e) +{ + tor_free(e); +} + +static ddmap_entry_t * +ddmap_entry_new(int n_votes) +{ + return tor_malloc_zero(STRUCT_OFFSET(ddmap_entry_t, vrs_lst) + + sizeof(vote_routerstatus_t *) * n_votes); +} + +static unsigned +ddmap_entry_hash(const ddmap_entry_t *ent) +{ + return (unsigned) siphash24g(ent->d, sizeof(ent->d)); +} + +static unsigned +ddmap_entry_eq(const ddmap_entry_t *a, const ddmap_entry_t *b) +{ + return fast_memeq(a->d, b->d, sizeof(a->d)); +} + +static void +ddmap_entry_set_digests(ddmap_entry_t *ent, + const uint8_t *rsa_sha1, + const uint8_t *ed25519) +{ + memcpy(ent->d, rsa_sha1, DIGEST_LEN); + memcpy(ent->d + DIGEST_LEN, ed25519, DIGEST256_LEN); +} + +HT_PROTOTYPE(double_digest_map, ddmap_entry_s, node, ddmap_entry_hash, ddmap_entry_eq); +HT_GENERATE2(double_digest_map, ddmap_entry_s, node, ddmap_entry_hash, ddmap_entry_eq, 0.6, tor_reallocarray, tor_free_); +static void +dircollator_add_routerstatus(dircollator_t *dc, + int vote_num, + networkstatus_t *vote, + vote_routerstatus_t *vrs) +{ + const char *id = vrs->status.identity_digest; + + (void) vote; + vote_routerstatus_t **vrs_lst = digestmap_get(dc->by_rsa_sha1, id); + if (NULL == vrs_lst) { + vrs_lst = tor_calloc(sizeof(vote_routerstatus_t *), dc->n_votes); + digestmap_set(dc->by_rsa_sha1, id, vrs_lst); + } + tor_assert(vrs_lst[vote_num] == NULL); + vrs_lst[vote_num] = vrs; + + const uint8_t *ed = vrs->ed25519_id; + + if (tor_mem_is_zero((char*)ed, DIGEST256_LEN)) + return; + + ddmap_entry_t search, *found; + memset(&search, 0, sizeof(search)); + ddmap_entry_set_digests(&search, (const uint8_t *)id, ed); + found = HT_FIND(double_digest_map, &dc->by_both_ids, &search); + if (NULL == found) { + found = ddmap_entry_new(dc->n_votes); + ddmap_entry_set_digests(found, (const uint8_t *)id, ed); + HT_INSERT(double_digest_map, &dc->by_both_ids, found); + } + vrs_lst = found->vrs_lst; + tor_assert(vrs_lst[vote_num] == NULL); + vrs_lst[vote_num] = vrs; +} + +dircollator_t * +dircollator_new(int n_votes, int n_authorities) +{ + dircollator_t *dc = tor_malloc_zero(sizeof(dircollator_t)); + + tor_assert(n_votes <= n_authorities); + + dc->n_votes = n_votes; + dc->n_authorities = n_authorities; + + dc->by_rsa_sha1 = digestmap_new(); + HT_INIT(double_digest_map, &dc->by_both_ids); + + return dc; +} + +void +dircollator_free(dircollator_t *dc) +{ + if (!dc) + return; + + if (dc->by_collated_rsa_sha1 != dc->by_rsa_sha1) + digestmap_free(dc->by_collated_rsa_sha1, NULL); + + digestmap_free(dc->by_rsa_sha1, tor_free_); + + ddmap_entry_t **e, **next, *this; + for (e = HT_START(double_digest_map, &dc->by_both_ids); + e != NULL; e = next) { + this = *e; + next = HT_NEXT_RMV(double_digest_map, &dc->by_both_ids, e); + ddmap_entry_free(this); + } + HT_CLEAR(double_digest_map, &dc->by_both_ids); + + tor_free(dc); +} + +void +dircollator_add_vote(dircollator_t *dc, networkstatus_t *v) +{ + tor_assert(v->type == NS_TYPE_VOTE); + tor_assert(dc->next_vote_num < dc->n_votes); + tor_assert(!dc->is_collated); + + const int votenum = dc->next_vote_num++; + + SMARTLIST_FOREACH_BEGIN(v->routerstatus_list, vote_routerstatus_t *, vrs) { + dircollator_add_routerstatus(dc, votenum, v, vrs); + } SMARTLIST_FOREACH_END(vrs); +} + +void +dircollator_collate(dircollator_t *dc, int consensus_method) +{ + tor_assert(!dc->is_collated); + dc->all_rsa_sha1_lst = smartlist_new(); + + if (consensus_method < MIN_METHOD_FOR_ED25519_ID_VOTING + 10/*XXX*/) + dircollator_collate_by_rsa(dc); + else + dircollator_collate_by_ed25519(dc); + + smartlist_sort_digests(dc->all_rsa_sha1_lst); + dc->is_collated = 1; +} + +static void +dircollator_collate_by_rsa(dircollator_t *dc) +{ + const int total_authorities = dc->n_authorities; + + DIGESTMAP_FOREACH(dc->by_rsa_sha1, k, vote_routerstatus_t **, vrs_lst) { + int n = 0, i; + for (i = 0; i < dc->n_votes; ++i) { + if (vrs_lst[i] != NULL) + ++n; + } + + if (n <= total_authorities / 2) + continue; + + smartlist_add(dc->all_rsa_sha1_lst, (char *)k); + } DIGESTMAP_FOREACH_END; + + dc->by_collated_rsa_sha1 = dc->by_rsa_sha1; +} + +static void +dircollator_collate_by_ed25519(dircollator_t *dc) +{ + const int total_authorities = dc->n_authorities; + digestmap_t *rsa_digests = digestmap_new(); + + ddmap_entry_t **iter; + + HT_FOREACH(iter, double_digest_map, &dc->by_both_ids) { + ddmap_entry_t *ent = *iter; + int n = 0, i; + for (i = 0; i < dc->n_votes; ++i) { + if (ent->vrs_lst[i] != NULL) + ++n; + } + + if (n <= total_authorities / 2) + continue; + + vote_routerstatus_t **vrs_lst2 = digestmap_get(dc->by_rsa_sha1, + (char*)ent->d); + tor_assert(vrs_lst2); + + for (i = 0; i < dc->n_votes; ++i) { + if (ent->vrs_lst[i] != NULL) { + ent->vrs_lst[i]->ed25519_reflects_consensus = 1; + } else if (vrs_lst2[i] && ! vrs_lst2[i]->has_ed25519_listing) { + ent->vrs_lst[i] = vrs_lst2[i]; + } + } + + digestmap_set(rsa_digests, (char*)ent->d, ent->vrs_lst); + smartlist_add(dc->all_rsa_sha1_lst, ent->d); + } + + DIGESTMAP_FOREACH(dc->by_rsa_sha1, k, vote_routerstatus_t **, vrs_lst) { + if (digestmap_get(rsa_digests, k) != NULL) + continue; + + int n = 0, i; + for (i = 0; i < dc->n_votes; ++i) { + if (vrs_lst[i] != NULL) + ++n; + } + + if (n <= total_authorities / 2) + continue; + + digestmap_set(rsa_digests, k, vrs_lst); + smartlist_add(dc->all_rsa_sha1_lst, (char *)k); + } DIGESTMAP_FOREACH_END; + + dc->by_collated_rsa_sha1 = rsa_digests; +} + +int +dircollator_n_routers(dircollator_t *dc) +{ + return smartlist_len(dc->all_rsa_sha1_lst); +} + +vote_routerstatus_t ** +dircollator_get_votes_for_router(dircollator_t *dc, int idx) +{ + tor_assert(idx < smartlist_len(dc->all_rsa_sha1_lst)); + return digestmap_get(dc->by_collated_rsa_sha1, + smartlist_get(dc->all_rsa_sha1_lst, idx)); +} + diff --git a/src/or/dircollate.h b/src/or/dircollate.h new file mode 100644 index 0000000000..9eba37a010 --- /dev/null +++ b/src/or/dircollate.h @@ -0,0 +1,49 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2014, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file dirvote.h + * \brief Header file for dirvote.c. + **/ + +#ifndef TOR_DIRCOLLATE_H +#define TOR_DIRCOLLATE_H + +#include "testsupport.h" +#include "or.h" + +typedef struct dircollator_s dircollator_t; + +dircollator_t *dircollator_new(int n_votes, int n_authorities); +void dircollator_free(dircollator_t *obj); +void dircollator_add_vote(dircollator_t *dc, networkstatus_t *v); + +void dircollator_collate(dircollator_t *dc, int consensus_method); + +int dircollator_n_routers(dircollator_t *dc); +vote_routerstatus_t **dircollator_get_votes_for_router(dircollator_t *dc, + int idx); + +#ifdef DIRCOLLATE_PRIVATE +struct ddmap_entry_s; +typedef HT_HEAD(double_digest_map, ddmap_entry_s) double_digest_map_t; +struct dircollator_s { + /**DOCDOC */ + int is_collated; + int n_votes; + int n_authorities; + + int next_vote_num; + digestmap_t *by_rsa_sha1; + struct double_digest_map by_both_ids; + + digestmap_t *by_collated_rsa_sha1; + + smartlist_t *all_rsa_sha1_lst; +}; +#endif + +#endif diff --git a/src/or/dirserv.c b/src/or/dirserv.c index a024be8342..bee67cf749 100644 --- a/src/or/dirserv.c +++ b/src/or/dirserv.c @@ -18,6 +18,7 @@ #include "dirserv.h" #include "dirvote.h" #include "hibernate.h" +#include "keypin.h" #include "microdesc.h" #include "networkstatus.h" #include "nodelist.h" @@ -27,6 +28,7 @@ #include "routerlist.h" #include "routerparse.h" #include "routerset.h" +#include "torcert.h" /** * \file dirserv.c @@ -225,6 +227,16 @@ dirserv_load_fingerprint_file(void) return 0; } +/* If this is set, then we don't allow routers that have advertised an Ed25519 + * identity to stop doing so. This is going to be essential for good identity + * security: otherwise anybody who can attack RSA-1024 but not Ed25519 could + * just sign fake descriptors missing the Ed25519 key. But we won't actually + * be able to prevent that kind of thing until we're confident that there + * isn't actually a legit reason to downgrade to 0.2.5. So for now, we have + * to leave this #undef. + */ +#undef DISABLE_DISABLING_ED25519 + /** Check whether router has a nickname/identity key combination that * we recognize from the fingerprint list, or an IP we automatically act on * according to our configuration. Return the appropriate router status. @@ -243,6 +255,36 @@ dirserv_router_get_status(const routerinfo_t *router, const char **msg) return FP_REJECT; } + if (router->signing_key_cert) { + /* This has an ed25519 identity key. */ + if (KEYPIN_MISMATCH == + keypin_check((const uint8_t*)router->cache_info.identity_digest, + router->signing_key_cert->signing_key.pubkey)) { + if (msg) { + *msg = "Ed25519 identity key or RSA identity key has changed."; + } + log_warn(LD_DIR, "Router %s uploaded a descriptor with a Ed25519 key " + "but the keys don't match what they were before.", + router_describe(router)); + return FP_REJECT; + } + } else { + /* No ed25519 key */ + if (KEYPIN_MISMATCH == keypin_check_lone_rsa( + (const uint8_t*)router->cache_info.identity_digest)) { + log_warn(LD_DIR, "Router %s uploaded a descriptor with no Ed25519 key, " + "when we previously knew an Ed25519 for it. Ignoring for now, " + "since Tor 0.2.6 is under development.", + router_describe(router)); +#ifdef DISABLE_DISABLING_ED25519 + if (msg) { + *msg = "Ed25519 identity key has disappeared."; + } + return FP_REJECT; +#endif + } + } + return dirserv_get_status_impl(d, router->nickname, router->addr, router->or_port, router->platform, msg, 1); @@ -578,6 +620,28 @@ dirserv_add_descriptor(routerinfo_t *ri, const char **msg, const char *source) return ROUTER_IS_ALREADY_KNOWN; } + /* Do keypinning again ... this time, to add the pin if appropriate */ + int keypin_status; + if (ri->signing_key_cert) { + keypin_status = keypin_check_and_add( + (const uint8_t*)ri->cache_info.identity_digest, + ri->signing_key_cert->signing_key.pubkey); + } else { + keypin_status = keypin_check_lone_rsa( + (const uint8_t*)ri->cache_info.identity_digest); +#ifndef DISABLE_DISABLING_ED25519 + if (keypin_status == KEYPIN_MISMATCH) + keypin_status = KEYPIN_NOT_FOUND; +#endif + } + if (keypin_status == KEYPIN_MISMATCH) { + log_info(LD_DIRSERV, "Dropping descriptor from %s (source: %s) because " + "its key did not match an older RSA/Ed25519 keypair", + router_describe(ri), source); + *msg = "Looks like your keypair does not match its older value."; + return ROUTER_AUTHDIR_REJECTS; + } + /* Make a copy of desc, since router_add_to_routerlist might free * ri and its associated signed_descriptor_t. */ desc = tor_strndup(ri->cache_info.signed_descriptor_body, desclen); @@ -1929,6 +1993,16 @@ routerstatus_format_entry(const routerstatus_t *rs, const char *version, 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(chunks, tor_strdup("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: @@ -2751,6 +2825,11 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, listbadexits, vote_on_hsdirs); + if (ri->signing_key_cert) { + memcpy(vrs->ed25519_id, ri->signing_key_cert->signing_key.pubkey, + ED25519_PUBKEY_LEN); + } + if (digestmap_get(omit_as_sybil, ri->cache_info.identity_digest)) clear_status_flags_on_sybil(rs); diff --git a/src/or/dirvote.c b/src/or/dirvote.c index 3009026ee7..e037794fc7 100644 --- a/src/or/dirvote.c +++ b/src/or/dirvote.c @@ -6,6 +6,7 @@ #define DIRVOTE_PRIVATE #include "or.h" #include "config.h" +#include "dircollate.h" #include "directory.h" #include "dirserv.h" #include "dirvote.h" @@ -17,6 +18,7 @@ #include "routerlist.h" #include "routerparse.h" #include "entrynodes.h" /* needed for guardfraction methods */ +#include "torcert.h" /** * \file dirvote.c @@ -1138,6 +1140,7 @@ networkstatus_compute_consensus(smartlist_t *votes, char *params = NULL; char *packages = NULL; int added_weights = 0; + dircollator_t *collator = NULL; tor_assert(flavor == FLAV_NS || flavor == FLAV_MICRODESC); tor_assert(total_authorities >= smartlist_len(votes)); @@ -1493,12 +1496,24 @@ networkstatus_compute_consensus(smartlist_t *votes, } ); + /* Populate the collator */ + collator = dircollator_new(smartlist_len(votes), total_authorities); + SMARTLIST_FOREACH_BEGIN(votes, networkstatus_t *, v) { + dircollator_add_vote(collator, v); + } SMARTLIST_FOREACH_END(v); + + dircollator_collate(collator, consensus_method); + /* Now go through all the votes */ flag_counts = tor_calloc(smartlist_len(flags), sizeof(int)); - while (1) { + const int num_routers = dircollator_n_routers(collator); + for (i = 0; i < num_routers; ++i) { + vote_routerstatus_t **vrs_lst = + dircollator_get_votes_for_router(collator, i); + vote_routerstatus_t *rs; routerstatus_t rs_out; - const char *lowest_id = NULL; + const char *current_rsa_id = NULL; const char *chosen_version; const char *chosen_name = NULL; int exitsummary_disagreement = 0; @@ -1506,23 +1521,9 @@ networkstatus_compute_consensus(smartlist_t *votes, int is_guard = 0, is_exit = 0, is_bad_exit = 0; int naming_conflict = 0; int n_listing = 0; - int i; char microdesc_digest[DIGEST256_LEN]; tor_addr_port_t alt_orport = {TOR_ADDR_NULL, 0}; - /* Of the next-to-be-considered digest in each voter, which is first? */ - SMARTLIST_FOREACH(votes, networkstatus_t *, v, { - if (index[v_sl_idx] < size[v_sl_idx]) { - rs = smartlist_get(v->routerstatus_list, index[v_sl_idx]); - if (!lowest_id || - fast_memcmp(rs->status.identity_digest, - lowest_id, DIGEST_LEN) < 0) - lowest_id = rs->status.identity_digest; - } - }); - if (!lowest_id) /* we're out of routers. */ - break; - memset(flag_counts, 0, sizeof(int)*smartlist_len(flags)); smartlist_clear(matching_descs); smartlist_clear(chosen_flags); @@ -1532,29 +1533,25 @@ networkstatus_compute_consensus(smartlist_t *votes, num_guardfraction_inputs = 0; /* Okay, go through all the entries for this digest. */ - SMARTLIST_FOREACH_BEGIN(votes, networkstatus_t *, v) { - if (index[v_sl_idx] >= size[v_sl_idx]) - continue; /* out of entries. */ - rs = smartlist_get(v->routerstatus_list, index[v_sl_idx]); - if (fast_memcmp(rs->status.identity_digest, lowest_id, DIGEST_LEN)) - continue; /* doesn't include this router. */ - /* At this point, we know that we're looking at a routerstatus with - * identity "lowest". - */ - ++index[v_sl_idx]; + for (int voter_idx = 0; voter_idx < smartlist_len(votes); ++voter_idx) { + if (vrs_lst[voter_idx] == NULL) + continue; /* This voter had nothig to say about this entry. */ + rs = vrs_lst[voter_idx]; ++n_listing; + current_rsa_id = rs->status.identity_digest; + smartlist_add(matching_descs, rs); if (rs->version && rs->version[0]) smartlist_add(versions, rs->version); /* Tally up all the flags. */ - for (i = 0; i < n_voter_flags[v_sl_idx]; ++i) { - if (rs->flags & (U64_LITERAL(1) << i)) - ++flag_counts[flag_map[v_sl_idx][i]]; + for (int flag = 0; flag < n_voter_flags[voter_idx]; ++flag) { + if (rs->flags & (U64_LITERAL(1) << flag)) + ++flag_counts[flag_map[voter_idx][flag]]; } - if (named_flag[v_sl_idx] >= 0 && - (rs->flags & (U64_LITERAL(1) << named_flag[v_sl_idx]))) { + if (named_flag[voter_idx] >= 0 && + (rs->flags & (U64_LITERAL(1) << named_flag[voter_idx]))) { if (chosen_name && strcmp(chosen_name, rs->status.nickname)) { log_notice(LD_DIR, "Conflict on naming for router: %s vs %s", chosen_name, rs->status.nickname); @@ -1575,7 +1572,7 @@ networkstatus_compute_consensus(smartlist_t *votes, if (rs->status.has_bandwidth) bandwidths_kb[num_bandwidths++] = rs->status.bandwidth_kb; - } SMARTLIST_FOREACH_END(v); + } /* We don't include this router at all unless more than half of * the authorities we believe in list it. */ @@ -1589,8 +1586,9 @@ networkstatus_compute_consensus(smartlist_t *votes, microdesc_digest, &alt_orport); /* Copy bits of that into rs_out. */ memset(&rs_out, 0, sizeof(rs_out)); - tor_assert(fast_memeq(lowest_id, rs->status.identity_digest,DIGEST_LEN)); - memcpy(rs_out.identity_digest, lowest_id, DIGEST_LEN); + tor_assert(fast_memeq(current_rsa_id, + rs->status.identity_digest,DIGEST_LEN)); + memcpy(rs_out.identity_digest, current_rsa_id, DIGEST_LEN); memcpy(rs_out.descriptor_digest, rs->status.descriptor_digest, DIGEST_LEN); rs_out.addr = rs->status.addr; @@ -1614,7 +1612,7 @@ networkstatus_compute_consensus(smartlist_t *votes, const char *d = strmap_get_lc(name_to_id_map, rs_out.nickname); if (!d) { is_named = is_unnamed = 0; - } else if (fast_memeq(d, lowest_id, DIGEST_LEN)) { + } else if (fast_memeq(d, current_rsa_id, DIGEST_LEN)) { is_named = 1; is_unnamed = 0; } else { is_named = 0; is_unnamed = 1; @@ -1980,6 +1978,7 @@ networkstatus_compute_consensus(smartlist_t *votes, done: + dircollator_free(collator); tor_free(client_versions); tor_free(server_versions); tor_free(packages); @@ -3487,9 +3486,18 @@ dirvote_create_microdescriptor(const routerinfo_t *ri, int consensus_method) } if (consensus_method >= MIN_METHOD_FOR_ID_HASH_IN_MD) { - char idbuf[BASE64_DIGEST_LEN+1]; - digest_to_base64(idbuf, ri->cache_info.identity_digest); - smartlist_add_asprintf(chunks, "id rsa1024 %s\n", idbuf); + char idbuf[ED25519_BASE64_LEN+1]; + const char *keytype; + if (consensus_method >= MIN_METHOD_FOR_ED25519_ID_IN_MD && + ri->signing_key_cert && + ri->signing_key_cert->signing_key_included) { + keytype = "ed25519"; + ed25519_public_to_base64(idbuf, &ri->signing_key_cert->signing_key); + } else { + keytype = "rsa1024"; + digest_to_base64(idbuf, ri->cache_info.identity_digest); + } + smartlist_add_asprintf(chunks, "id %s %s\n", keytype, idbuf); } output = smartlist_join_strings(chunks, "", 0, NULL); @@ -3562,7 +3570,8 @@ static const struct consensus_method_range_t { {MIN_METHOD_FOR_A_LINES, MIN_METHOD_FOR_P6_LINES - 1}, {MIN_METHOD_FOR_P6_LINES, MIN_METHOD_FOR_NTOR_KEY - 1}, {MIN_METHOD_FOR_NTOR_KEY, MIN_METHOD_FOR_ID_HASH_IN_MD - 1}, - {MIN_METHOD_FOR_ID_HASH_IN_MD, MAX_SUPPORTED_CONSENSUS_METHOD}, + {MIN_METHOD_FOR_ID_HASH_IN_MD, MIN_METHOD_FOR_ED25519_ID_IN_MD - 1}, + {MIN_METHOD_FOR_ED25519_ID_IN_MD, MAX_SUPPORTED_CONSENSUS_METHOD}, {-1, -1} }; diff --git a/src/or/dirvote.h b/src/or/dirvote.h index 542563b708..0fb2b2599b 100644 --- a/src/or/dirvote.h +++ b/src/or/dirvote.h @@ -55,7 +55,7 @@ #define MIN_SUPPORTED_CONSENSUS_METHOD 13 /** The highest consensus method that we currently support. */ -#define MAX_SUPPORTED_CONSENSUS_METHOD 20 +#define MAX_SUPPORTED_CONSENSUS_METHOD 21 /** Lowest consensus method where microdesc consensuses omit any entry * with no microdesc. */ @@ -86,6 +86,13 @@ * GuardFraction information in microdescriptors. */ #define MIN_METHOD_FOR_GUARDFRACTION 20 +/** Lowest consensus method where authorities may include an "id" line for + * ed25519 identities in microdescriptors. */ +#define MIN_METHOD_FOR_ED25519_ID_IN_MD 21 +/** Lowest consensus method where authorities vote on ed25519 ids and ensure + * ed25519 id consistency. */ +#define MIN_METHOD_FOR_ED25519_ID_VOTING MIN_METHOD_FOR_ED25519_ID_IN_MD + /** Default bandwidth to clip unmeasured bandwidths to using method >= * MIN_METHOD_TO_CLIP_UNMEASURED_BW. (This is not a consensus method; do not * get confused with the above macros.) */ diff --git a/src/or/include.am b/src/or/include.am index 0ce3cdc5c3..6bbf78871c 100644 --- a/src/or/include.am +++ b/src/or/include.am @@ -43,6 +43,7 @@ LIBTOR_A_SOURCES = \ src/or/connection_or.c \ src/or/control.c \ src/or/cpuworker.c \ + src/or/dircollate.c \ src/or/directory.c \ src/or/dirserv.c \ src/or/dirvote.c \ @@ -53,6 +54,7 @@ LIBTOR_A_SOURCES = \ src/or/entrynodes.c \ src/or/ext_orport.c \ src/or/hibernate.c \ + src/or/keypin.c \ src/or/main.c \ src/or/microdesc.c \ src/or/networkstatus.c \ @@ -71,12 +73,14 @@ LIBTOR_A_SOURCES = \ src/or/rephist.c \ src/or/replaycache.c \ src/or/router.c \ + src/or/routerkeys.c \ src/or/routerlist.c \ src/or/routerparse.c \ src/or/routerset.c \ src/or/scheduler.c \ src/or/statefile.c \ src/or/status.c \ + src/or/torcert.c \ src/or/onion_ntor.c \ $(evdns_source) \ $(tor_platform_source) @@ -84,11 +88,6 @@ LIBTOR_A_SOURCES = \ src_or_libtor_a_SOURCES = $(LIBTOR_A_SOURCES) src_or_libtor_testing_a_SOURCES = $(LIBTOR_A_SOURCES) -#libtor_a_LIBADD = $(top_builddir)/common/libor.a \ -# $(top_builddir)/common/libor-crypto.a \ -# $(top_builddir)/common/libor-event.a - - src_or_tor_SOURCES = src/or/tor_main.c AM_CPPFLAGS += -I$(srcdir)/src/or -Isrc/or @@ -109,7 +108,7 @@ src_or_libtor_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) src_or_tor_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ @TOR_LDFLAGS_libevent@ src_or_tor_LDADD = src/or/libtor.a src/common/libor.a \ src/common/libor-crypto.a $(LIBDONNA) \ - src/common/libor-event.a \ + src/common/libor-event.a src/trunnel/libor-trunnel.a \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ \ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ @TOR_SYSTEMD_LIBS@ @@ -120,7 +119,7 @@ src_or_tor_cov_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) src_or_tor_cov_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ @TOR_LDFLAGS_libevent@ src_or_tor_cov_LDADD = src/or/libtor-testing.a src/common/libor-testing.a \ src/common/libor-crypto-testing.a $(LIBDONNA) \ - src/common/libor-event-testing.a \ + src/common/libor-event-testing.a src/trunnel/libor-trunnel-testing.a \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ \ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ @TOR_SYSTEMD_LIBS@ TESTING_TOR_BINARY = $(top_builddir)/src/or/tor-cov @@ -148,6 +147,7 @@ ORHEADERS = \ src/or/connection_or.h \ src/or/control.h \ src/or/cpuworker.h \ + src/or/dircollate.h \ src/or/directory.h \ src/or/dirserv.h \ src/or/dirvote.h \ @@ -159,6 +159,7 @@ ORHEADERS = \ src/or/geoip.h \ src/or/entrynodes.h \ src/or/hibernate.h \ + src/or/keypin.h \ src/or/main.h \ src/or/microdesc.h \ src/or/networkstatus.h \ @@ -180,12 +181,15 @@ ORHEADERS = \ src/or/rephist.h \ src/or/replaycache.h \ src/or/router.h \ + src/or/routerkeys.h \ src/or/routerlist.h \ + src/or/routerkeys.h \ src/or/routerset.h \ src/or/routerparse.h \ src/or/scheduler.h \ src/or/statefile.h \ - src/or/status.h + src/or/status.h \ + src/or/torcert.h noinst_HEADERS+= $(ORHEADERS) micro-revision.i diff --git a/src/or/keypin.c b/src/or/keypin.c new file mode 100644 index 0000000000..7b0c0c7dcf --- /dev/null +++ b/src/or/keypin.c @@ -0,0 +1,419 @@ +/* Copyright (c) 2014, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#define KEYPIN_PRIVATE + +#include "orconfig.h" +#include "compat.h" +#include "crypto.h" +#include "di_ops.h" +#include "ht.h" +#include "keypin.h" +#include "siphash.h" +#include "torint.h" +#include "torlog.h" +#include "util.h" + +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif + +#ifdef _WIN32 +#include +#endif + +/** + * @file keypin.c + * @brief Key-pinning for RSA and Ed25519 identity keys at directory + * authorities. + * + * This module implements a key-pinning mechanism to ensure that it's safe + * to use RSA keys as identitifers even as we migrate to Ed25519 keys. It + * remembers, for every Ed25519 key we've seen, what the associated Ed25519 + * key is. This way, if we see a different Ed25519 key with that RSA key, + * we'll know that there's a mismatch. + * + * We persist these entries to disk using a simple format, where each line + * has a base64-encoded RSA SHA1 hash, then a base64-endoded Ed25519 key. + * Empty lines, misformed lines, and lines beginning with # are + * ignored. Lines beginning with @ are reserved for future extensions. + */ + +static int keypin_journal_append_entry(const uint8_t *rsa_id_digest, + const uint8_t *ed25519_id_key); +static int keypin_check_and_add_impl(const uint8_t *rsa_id_digest, + const uint8_t *ed25519_id_key, + int do_not_add); + +static HT_HEAD(rsamap, keypin_ent_st) the_rsa_map = HT_INITIALIZER(); +static HT_HEAD(edmap, keypin_ent_st) the_ed_map = HT_INITIALIZER(); + +/** Hashtable helper: compare two keypin table entries and return true iff + * they have the same RSA key IDs. */ +static INLINE int +keypin_ents_eq_rsa(const keypin_ent_t *a, const keypin_ent_t *b) +{ + return tor_memeq(a->rsa_id, b->rsa_id, sizeof(a->rsa_id)); +} + +/** Hashtable helper: hash a keypin table entries based on its RSA key ID */ +static INLINE unsigned +keypin_ent_hash_rsa(const keypin_ent_t *a) +{ +return (unsigned) siphash24g(a->rsa_id, sizeof(a->rsa_id)); +} + +/** Hashtable helper: compare two keypin table entries and return true iff + * they have the same ed25519 keys */ +static INLINE int +keypin_ents_eq_ed(const keypin_ent_t *a, const keypin_ent_t *b) +{ + return tor_memeq(a->ed25519_key, b->ed25519_key, sizeof(a->ed25519_key)); +} + +/** Hashtable helper: hash a keypin table entries based on its ed25519 key */ +static INLINE unsigned +keypin_ent_hash_ed(const keypin_ent_t *a) +{ +return (unsigned) siphash24g(a->ed25519_key, sizeof(a->ed25519_key)); +} + +HT_PROTOTYPE(rsamap, keypin_ent_st, rsamap_node, keypin_ent_hash_rsa, + keypin_ents_eq_rsa); +HT_GENERATE2(rsamap, keypin_ent_st, rsamap_node, keypin_ent_hash_rsa, + keypin_ents_eq_rsa, 0.6, tor_reallocarray, tor_free_); + +HT_PROTOTYPE(edmap, keypin_ent_st, edmap_node, keypin_ent_hash_ed, + keypin_ents_eq_ed); +HT_GENERATE2(edmap, keypin_ent_st, edmap_node, keypin_ent_hash_ed, + keypin_ents_eq_ed, 0.6, tor_reallocarray, tor_free_); + +/** + * Check whether we already have an entry in the key pinning table for a + * router with RSA ID digest rsa_id_digest or for ed25519 key + * ed25519_id_key. If we have an entry that matches both keys, + * return KEYPIN_FOUND. If we find an entry that matches one key but + * not the other, return KEYPIN_MISMATCH. If we have no entry for either + * key, add such an entry to the table and return KEYPIN_ADDED. + */ +int +keypin_check_and_add(const uint8_t *rsa_id_digest, + const uint8_t *ed25519_id_key) +{ + return keypin_check_and_add_impl(rsa_id_digest, ed25519_id_key, 0); +} + +/** + * As keypin_check_and_add, but do not add. Return KEYPIN_NOT_FOUND if + * we would add. + */ +int +keypin_check(const uint8_t *rsa_id_digest, + const uint8_t *ed25519_id_key) +{ + return keypin_check_and_add_impl(rsa_id_digest, ed25519_id_key, 1); +} + +/** + * Helper: implements keypin_check and keypin_check_and_add. + */ +static int +keypin_check_and_add_impl(const uint8_t *rsa_id_digest, + const uint8_t *ed25519_id_key, + int do_not_add) +{ + keypin_ent_t search, *ent; + memset(&search, 0, sizeof(search)); + memcpy(search.rsa_id, rsa_id_digest, sizeof(search.rsa_id)); + memcpy(search.ed25519_key, ed25519_id_key, sizeof(search.ed25519_key)); + + /* Search by RSA key digest first */ + ent = HT_FIND(rsamap, &the_rsa_map, &search); + if (ent) { + tor_assert(fast_memeq(ent->rsa_id, rsa_id_digest, sizeof(ent->rsa_id))); + if (tor_memeq(ent->ed25519_key, ed25519_id_key,sizeof(ent->ed25519_key))) { + return KEYPIN_FOUND; /* Match on both keys. Great. */ + } else { + return KEYPIN_MISMATCH; /* Found RSA with different Ed key */ + } + } + + /* See if we know a different RSA key for this ed key */ + ent = HT_FIND(edmap, &the_ed_map, &search); + if (ent) { + /* If we got here, then the ed key matches and the RSA doesn't */ + tor_assert(fast_memeq(ent->ed25519_key, ed25519_id_key, + sizeof(ent->ed25519_key))); + tor_assert(fast_memneq(ent->rsa_id, rsa_id_digest, sizeof(ent->rsa_id))); + return KEYPIN_MISMATCH; + } + + /* Okay, this one is new to us. */ + if (do_not_add) + return KEYPIN_NOT_FOUND; + + ent = tor_memdup(&search, sizeof(search)); + keypin_add_entry_to_map(ent); + keypin_journal_append_entry(rsa_id_digest, ed25519_id_key); + return KEYPIN_ADDED; +} + +/** + * Helper: add ent to the hash tables. + */ +MOCK_IMPL(STATIC void, +keypin_add_entry_to_map, (keypin_ent_t *ent)) +{ + HT_INSERT(rsamap, &the_rsa_map, ent); + HT_INSERT(edmap, &the_ed_map, ent); +} + +/** + * Check whether we already have an entry in the key pinning table for a + * router with RSA ID digest rsa_id_digest. If we have no such entry, + * return KEYPIN_NOT_FOUND. If we find an entry that matches the RSA key but + * which has an ed25519 key, return KEYPIN_MISMATCH. + */ +int +keypin_check_lone_rsa(const uint8_t *rsa_id_digest) +{ + keypin_ent_t search, *ent; + memset(&search, 0, sizeof(search)); + memcpy(search.rsa_id, rsa_id_digest, sizeof(search.rsa_id)); + + /* Search by RSA key digest first */ + ent = HT_FIND(rsamap, &the_rsa_map, &search); + if (ent) { + return KEYPIN_MISMATCH; + } else { + return KEYPIN_NOT_FOUND; + } +} + +/** Open fd to the keypinning journal file. */ +static int keypin_journal_fd = -1; + +/** Open the key-pinning journal to append to fname. Return 0 on + * success, -1 on failure. */ +int +keypin_open_journal(const char *fname) +{ + /* O_SYNC ??*/ + int fd = tor_open_cloexec(fname, O_WRONLY|O_CREAT, 0600); + if (fd < 0) + goto err; + + if (tor_fd_seekend(fd) < 0) + goto err; + + /* Add a newline in case the last line was only partially written */ + if (write(fd, "\n", 1) < 1) + goto err; + + /* Add something about when we opened this file. */ + char buf[80]; + char tbuf[ISO_TIME_LEN+1]; + format_iso_time(tbuf, approx_time()); + tor_snprintf(buf, sizeof(buf), "@opened-at %s\n", tbuf); + if (write_all(fd, buf, strlen(buf), 0) < 0) + goto err; + + keypin_journal_fd = fd; + return 0; + err: + if (fd >= 0) + close(fd); + return -1; +} + +/** Close the keypinning journal file. */ +int +keypin_close_journal(void) +{ + if (keypin_journal_fd >= 0) + close(keypin_journal_fd); + keypin_journal_fd = -1; + return 0; +} + +/** Length of a keypinning journal line, including terminating newline. */ +#define JOURNAL_LINE_LEN (BASE64_DIGEST_LEN + BASE64_DIGEST256_LEN + 2) + +/** Add an entry to the keypinning journal to map rsa_id_digest and + * ed25519_id_key. */ +static int +keypin_journal_append_entry(const uint8_t *rsa_id_digest, + const uint8_t *ed25519_id_key) +{ + if (keypin_journal_fd == -1) + return -1; + char line[JOURNAL_LINE_LEN]; + digest_to_base64(line, (const char*)rsa_id_digest); + line[BASE64_DIGEST_LEN] = ' '; + digest256_to_base64(line + BASE64_DIGEST_LEN + 1, + (const char*)ed25519_id_key); + line[BASE64_DIGEST_LEN+1+BASE64_DIGEST256_LEN] = '\n'; + + if (write_all(keypin_journal_fd, line, JOURNAL_LINE_LEN, 0)<0) { + log_warn(LD_DIRSERV, "Error while adding a line to the key-pinning " + "journal: %s", strerror(errno)); + keypin_close_journal(); + return -1; + } + + return 0; +} + +/** Load a journal from the size-byte region at data. Return 0 + * on success, -1 on failure. */ +STATIC int +keypin_load_journal_impl(const char *data, size_t size) +{ + const char *start = data, *end = data + size, *next; + + int n_corrupt_lines = 0; + int n_entries = 0; + int n_duplicates = 0; + int n_conflicts = 0; + + for (const char *cp = start; cp < end; cp = next) { + const char *eol = memchr(cp, '\n', end-cp); + const char *eos = eol ? eol : end; + const size_t len = eos - cp; + + next = eol ? eol + 1 : end; + + if (len == 0) { + continue; + } + + if (*cp == '@') { + /* Lines that start with @ are reserved. Ignore for now. */ + continue; + } + if (*cp == '#') { + /* Lines that start with # are comments. */ + continue; + } + + /* Is it the right length? (The -1 here is for the newline.) */ + if (len != JOURNAL_LINE_LEN - 1) { + /* Lines with a bad length are corrupt unless they are empty. + * Ignore them either way */ + for (const char *s = cp; s < eos; ++s) { + if (! TOR_ISSPACE(*s)) { + ++n_corrupt_lines; + break; + } + } + continue; + } + + keypin_ent_t *ent = keypin_parse_journal_line(cp); + + if (ent == NULL) { + ++n_corrupt_lines; + continue; + } + + const keypin_ent_t *ent2; + if ((ent2 = HT_FIND(rsamap, &the_rsa_map, ent))) { + if (fast_memeq(ent2->ed25519_key, ent->ed25519_key, DIGEST256_LEN)) { + ++n_duplicates; + } else { + ++n_conflicts; + } + tor_free(ent); + continue; + } else if (HT_FIND(edmap, &the_ed_map, ent)) { + tor_free(ent); + ++n_conflicts; + continue; + } + + keypin_add_entry_to_map(ent); + ++n_entries; + } + + int severity = (n_corrupt_lines || n_duplicates) ? LOG_WARN : LOG_INFO; + tor_log(severity, LD_DIRSERV, + "Loaded %d entries from keypin journal. " + "Found %d corrupt lines, %d duplicates, and %d conflicts.", + n_entries, n_corrupt_lines, n_duplicates, n_conflicts); + + return 0; +} + +/** + * Load a journal from the file called fname. Return 0 on success, + * -1 on failure. + */ +int +keypin_load_journal(const char *fname) +{ + tor_mmap_t *map = tor_mmap_file(fname); + if (!map) { + if (errno == ENOENT) + return 0; + else + return -1; + } + int r = keypin_load_journal_impl(map->data, map->size); + tor_munmap_file(map); + return r; +} + +/** Parse a single keypinning journal line entry from cp. The input + * does not need to be NUL-terminated, but it does need to have + * KEYPIN_JOURNAL_LINE_LEN -1 bytes available to read. Return a new entry + * on success, and NULL on failure. + */ +STATIC keypin_ent_t * +keypin_parse_journal_line(const char *cp) +{ + /* XXXX assumes !USE_OPENSSL_BASE64 */ + keypin_ent_t *ent = tor_malloc_zero(sizeof(keypin_ent_t)); + + if (base64_decode((char*)ent->rsa_id, sizeof(ent->rsa_id), + cp, BASE64_DIGEST_LEN) != DIGEST_LEN || + cp[BASE64_DIGEST_LEN] != ' ' || + base64_decode((char*)ent->ed25519_key, sizeof(ent->ed25519_key), + cp+BASE64_DIGEST_LEN+1, BASE64_DIGEST256_LEN) != DIGEST256_LEN) { + tor_free(ent); + return NULL; + } else { + return ent; + } +} + +/** Remove all entries from the keypinning table.*/ +void +keypin_clear(void) +{ + int bad_entries = 0; + { + keypin_ent_t **ent, **next, *this; + for (ent = HT_START(rsamap, &the_rsa_map); ent != NULL; ent = next) { + this = *ent; + next = HT_NEXT_RMV(rsamap, &the_rsa_map, ent); + + keypin_ent_t *other_ent = HT_REMOVE(edmap, &the_ed_map, this); + bad_entries += (other_ent != this); + + tor_free(this); + } + } + bad_entries += HT_SIZE(&the_ed_map); + + HT_CLEAR(edmap,&the_ed_map); + HT_CLEAR(rsamap,&the_rsa_map); + + if (bad_entries) { + log_warn(LD_BUG, "Found %d discrepencies in the the keypin database.", + bad_entries); + } +} + diff --git a/src/or/keypin.h b/src/or/keypin.h new file mode 100644 index 0000000000..2a5b3f1786 --- /dev/null +++ b/src/or/keypin.h @@ -0,0 +1,46 @@ +/* Copyright (c) 2014, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#ifndef TOR_KEYPIN_H +#define TOR_KEYPIN_H + +#include "testsupport.h" + +int keypin_check_and_add(const uint8_t *rsa_id_digest, + const uint8_t *ed25519_id_key); +int keypin_check(const uint8_t *rsa_id_digest, + const uint8_t *ed25519_id_key); + +int keypin_open_journal(const char *fname); +int keypin_close_journal(void); +int keypin_load_journal(const char *fname); +void keypin_clear(void); +int keypin_check_lone_rsa(const uint8_t *rsa_id_digest); + +#define KEYPIN_FOUND 0 +#define KEYPIN_ADDED 1 +#define KEYPIN_MISMATCH -1 +#define KEYPIN_NOT_FOUND -2 + +#ifdef KEYPIN_PRIVATE + +/** + * In-memory representation of a key-pinning table entry. + */ +typedef struct keypin_ent_st { + HT_ENTRY(keypin_ent_st) rsamap_node; + HT_ENTRY(keypin_ent_st) edmap_node; + /** SHA1 hash of the RSA key */ + uint8_t rsa_id[DIGEST_LEN]; + /** Ed2219 key. */ + uint8_t ed25519_key[DIGEST256_LEN]; +} keypin_ent_t; + +STATIC keypin_ent_t * keypin_parse_journal_line(const char *cp); +STATIC int keypin_load_journal_impl(const char *data, size_t size); + +MOCK_DECL(STATIC void, keypin_add_entry_to_map, (keypin_ent_t *ent)); +#endif + +#endif + diff --git a/src/or/main.c b/src/or/main.c index 74e6b33397..bbee8e0fb9 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -37,6 +37,7 @@ #include "entrynodes.h" #include "geoip.h" #include "hibernate.h" +#include "keypin.h" #include "main.h" #include "microdesc.h" #include "networkstatus.h" @@ -51,6 +52,7 @@ #include "rendservice.h" #include "rephist.h" #include "router.h" +#include "routerkeys.h" #include "routerlist.h" #include "routerparse.h" #include "scheduler.h" @@ -1223,10 +1225,13 @@ typedef struct { time_t check_descriptor; /** When do we next launch DNS wildcarding checks? */ time_t check_for_correct_dns; + /** When do we next make sure our Ed25519 keys aren't about to expire? */ + time_t check_ed_keys; + } time_to_t; static time_to_t time_to = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; /** Reset all the time_to's so we'll do all our actions again as if we @@ -1297,6 +1302,18 @@ run_scheduled_events(time_t now) router_upload_dir_desc_to_dirservers(0); } + if (is_server && time_to.check_ed_keys < now) { + if (should_make_new_ed_keys(options, now)) { + if (load_ed_keys(options, now) < 0 || + generate_ed_link_cert(options, now)) { + log_err(LD_OR, "Unable to update Ed25519 keys! Exiting."); + tor_cleanup(); + exit(0); + } + } + time_to.check_ed_keys = now + 30; + } + if (!should_delay_dir_fetches(options, NULL) && time_to.try_getting_descriptors < now) { update_all_descriptor_downloads(now); @@ -2015,6 +2032,23 @@ do_main_loop(void) /* initialize the bootstrap status events to know we're starting up */ control_event_bootstrap(BOOTSTRAP_STATUS_STARTING, 0); + /* Initialize the keypinning log. */ + if (authdir_mode_v3(get_options())) { + char *fname = get_datadir_fname("key-pinning-entries"); + int r = 0; + if (keypin_load_journal(fname)<0) { + log_err(LD_DIR, "Error loading key-pinning journal: %s",strerror(errno)); + r = -1; + } + if (keypin_open_journal(fname)<0) { + log_err(LD_DIR, "Error opening key-pinning journal: %s",strerror(errno)); + r = -1; + } + tor_free(fname); + if (r) + return r; + } + if (trusted_dirs_reload_certs()) { log_warn(LD_DIR, "Couldn't load all cached v3 certificates. Starting anyway."); @@ -2695,6 +2729,7 @@ tor_free_all(int postfork) config_free_all(); or_state_free_all(); router_free_all(); + routerkeys_free_all(); policies_free_all(); } if (!postfork) { @@ -2752,6 +2787,7 @@ tor_cleanup(void) or_state_save(now); if (authdir_mode_tests_reachability(options)) rep_hist_record_mtbf_data(now, 0); + keypin_close_journal(); } #ifdef USE_DMALLOC dmalloc_log_stats(); diff --git a/src/or/microdesc.c b/src/or/microdesc.c index 0511e870d1..ee48f6a419 100644 --- a/src/or/microdesc.c +++ b/src/or/microdesc.c @@ -738,6 +738,7 @@ microdesc_free_(microdesc_t *md, const char *fname, int lineno) if (md->onion_pkey) crypto_pk_free(md->onion_pkey); tor_free(md->onion_curve25519_pkey); + tor_free(md->ed25519_identity_pkey); if (md->body && md->saved_location != SAVED_IN_CACHE) tor_free(md->body); diff --git a/src/or/or.h b/src/or/or.h index af3496765e..81e1c1c1db 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -96,6 +96,7 @@ #include "ht.h" #include "replaycache.h" #include "crypto_curve25519.h" +#include "crypto_ed25519.h" #include "tor_queue.h" /* These signals are defined to help handle_control_signal work. @@ -1353,6 +1354,8 @@ typedef struct listener_connection_t { * in the v3 handshake. The subject key must be a 1024-bit RSA key; it * must be signed by the identity key */ #define OR_CERT_TYPE_AUTH_1024 3 +/** DOCDOC */ +#define OR_CERT_TYPE_RSA_ED_CROSSCERT 7 /**@}*/ /** The one currently supported type of AUTHENTICATE cell. It contains @@ -1428,9 +1431,9 @@ typedef struct or_handshake_state_t { * @{ */ /** The cert for the key that's supposed to sign the AUTHENTICATE cell */ - tor_cert_t *auth_cert; + tor_x509_cert_t *auth_cert; /** A self-signed identity certificate */ - tor_cert_t *id_cert; + tor_x509_cert_t *id_cert; /**@}*/ } or_handshake_state_t; @@ -2023,6 +2026,8 @@ typedef int16_t country_t; /** Information about another onion router in the network. */ typedef struct { signed_descriptor_t cache_info; + /** A SHA256-digest of the extrainfo (if any) */ + char extra_info_digest256[DIGEST256_LEN]; char *nickname; /**< Human-readable OR name. */ uint32_t addr; /**< IPv4 address of OR, in host order. */ @@ -2040,6 +2045,11 @@ typedef struct { crypto_pk_t *identity_pkey; /**< Public RSA key for signing. */ /** Public curve25519 key for onions */ curve25519_public_key_t *onion_curve25519_pkey; + /** Certificate for ed25519 signing key */ + struct tor_cert_st *signing_key_cert; + /** What's the earliest expiration time on all the certs in this + * routerinfo? */ + time_t cert_expiration_time; char *platform; /**< What software/operating system is this OR using? */ @@ -2099,8 +2109,12 @@ typedef struct { /** Information needed to keep and cache a signed extra-info document. */ typedef struct extrainfo_t { signed_descriptor_t cache_info; + /** SHA256 digest of this document */ + uint8_t digest256[DIGEST256_LEN]; /** The router's nickname. */ char nickname[MAX_NICKNAME_LEN+1]; + /** Certificate for ed25519 signing key */ + struct tor_cert_st *signing_key_cert; /** True iff we found the right key for this extra-info, verified the * signature, and found it to be bad. */ unsigned int bad_sig : 1; @@ -2245,6 +2259,8 @@ typedef struct microdesc_t { crypto_pk_t *onion_pkey; /** As routerinfo_t.onion_curve25519_pkey */ curve25519_public_key_t *onion_curve25519_pkey; + /** Ed25519 identity key, if included. */ + ed25519_public_key_t *ed25519_identity_pkey; /** As routerinfo_t.ipv6_add */ tor_addr_t ipv6_addr; /** As routerinfo_t.ipv6_orport */ @@ -2359,9 +2375,13 @@ typedef struct vote_routerstatus_t { char *version; /**< The version that the authority says this router is * running. */ unsigned int has_measured_bw:1; /**< The vote had a measured bw */ + unsigned int has_ed25519_listing:1; /** DOCDOC */ + unsigned int ed25519_reflects_consensus:1; /** DOCDOC */ uint32_t measured_bw_kb; /**< Measured bandwidth (capacity) of the router */ /** The hash or hashes that the authority claims this microdesc has. */ vote_microdesc_hash_t *microdesc; + /** Ed25519 identity for this router, or zero if it has none. */ + uint8_t ed25519_id[ED25519_PUBKEY_LEN]; } vote_routerstatus_t; /** A signature of some document by an authority. */ @@ -4261,6 +4281,21 @@ typedef struct { * XXXX Eventually, the default will be 0. */ int ExitRelay; + + /** For how long (seconds) do we declare our singning keys to be valid? */ + int SigningKeyLifetime; + /** For how long (seconds) do we declare our link keys to be valid? */ + int TestingLinkCertLifetime; + /** For how long (seconds) do we declare our auth keys to be valid? */ + int TestingAuthKeyLifetime; + + /** How long before signing keys expire will we try to make a new one? */ + int TestingSigningKeySlop; + /** How long before link keys expire will we try to make a new one? */ + int TestingLinkKeySlop; + /** How long before auth keys expire will we try to make a new one? */ + int TestingAuthKeySlop; + } or_options_t; /** Persistent state for an onion router, as saved to disk. */ @@ -5065,6 +5100,8 @@ typedef enum was_router_added_t { /* Router descriptor was rejected because it was older than * OLD_ROUTER_DESC_MAX_AGE. */ ROUTER_WAS_TOO_OLD = -7, /* note contrast with 'NOT_NEW' */ + /* DOCDOC */ + ROUTER_CERTS_EXPIRED = -8 } was_router_added_t; /********************************* routerparse.c ************************/ diff --git a/src/or/router.c b/src/or/router.c index 24b7c750f6..6532f97d24 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -26,9 +26,11 @@ #include "relay.h" #include "rephist.h" #include "router.h" +#include "routerkeys.h" #include "routerlist.h" #include "routerparse.h" #include "statefile.h" +#include "torcert.h" #include "transports.h" #include "routerset.h" @@ -204,6 +206,8 @@ set_server_identity_key(crypto_pk_t *k) static void assert_identity_keys_ok(void) { + if (1) + return; tor_assert(client_identitykey); if (public_server_mode(get_options())) { /* assert that we have set the client and server keys to be equal */ @@ -863,6 +867,10 @@ init_keys(void) set_client_identity_key(prkey); } + /* 1d. Load all ed25519 keys */ + if (load_ed_keys(options,now) < 0) + return -1; + /* 2. Read onion key. Make it if none is found. */ keydir = get_datadir_fname2("keys", "secret_onion_key"); log_info(LD_GENERAL,"Reading/making onion key \"%s\"...",keydir); @@ -928,6 +936,13 @@ init_keys(void) return -1; } + /* 3b. Get an ed25519 link certificate. Note that we need to do this + * after we set up the TLS context */ + if (generate_ed_link_cert(options, now) < 0) { + log_err(LD_GENERAL,"Couldn't make link cert"); + return -1; + } + /* 4. Build our router descriptor. */ /* Must be called after keys are initialized. */ mydesc = router_get_my_descriptor(); @@ -1872,6 +1887,8 @@ router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e) routerinfo_free(ri); return -1; } + ri->signing_key_cert = tor_cert_dup(get_master_signing_key_cert()); + get_platform_str(platform, sizeof(platform)); ri->platform = tor_strdup(platform); @@ -1962,10 +1979,12 @@ router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e) ei->cache_info.is_extrainfo = 1; strlcpy(ei->nickname, get_options()->Nickname, sizeof(ei->nickname)); ei->cache_info.published_on = ri->cache_info.published_on; + ei->signing_key_cert = tor_cert_dup(get_master_signing_key_cert()); memcpy(ei->cache_info.identity_digest, ri->cache_info.identity_digest, DIGEST_LEN); if (extrainfo_dump_to_string(&ei->cache_info.signed_descriptor_body, - ei, get_server_identity_key()) < 0) { + ei, get_server_identity_key(), + get_master_signing_keypair()) < 0) { log_warn(LD_BUG, "Couldn't generate extra-info descriptor."); extrainfo_free(ei); ei = NULL; @@ -1975,6 +1994,10 @@ router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e) router_get_extrainfo_hash(ei->cache_info.signed_descriptor_body, ei->cache_info.signed_descriptor_len, ei->cache_info.signed_descriptor_digest); + crypto_digest256((char*) ei->digest256, + ei->cache_info.signed_descriptor_body, + ei->cache_info.signed_descriptor_len, + DIGEST_SHA256); } /* Now finish the router descriptor. */ @@ -1982,12 +2005,18 @@ router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e) memcpy(ri->cache_info.extra_info_digest, ei->cache_info.signed_descriptor_digest, DIGEST_LEN); + memcpy(ri->extra_info_digest256, + ei->digest256, + DIGEST256_LEN); } else { /* ri was allocated with tor_malloc_zero, so there is no need to * zero ri->cache_info.extra_info_digest here. */ } - if (! (ri->cache_info.signed_descriptor_body = router_dump_router_to_string( - ri, get_server_identity_key()))) { + if (! (ri->cache_info.signed_descriptor_body = + router_dump_router_to_string(ri, get_server_identity_key(), + get_onion_key(), + get_current_curve25519_keypair(), + get_master_signing_keypair())) ) { log_warn(LD_BUG, "Couldn't generate router descriptor."); routerinfo_free(ri); extrainfo_free(ei); @@ -2328,22 +2357,28 @@ get_platform_str(char *platform, size_t len) */ char * router_dump_router_to_string(routerinfo_t *router, - crypto_pk_t *ident_key) + const crypto_pk_t *ident_key, + const crypto_pk_t *tap_key, + const curve25519_keypair_t *ntor_keypair, + const ed25519_keypair_t *signing_keypair) { char *address = NULL; char *onion_pkey = NULL; /* Onion key, PEM-encoded. */ char *identity_pkey = NULL; /* Identity key, PEM-encoded. */ - char digest[DIGEST_LEN]; + char digest[DIGEST256_LEN]; char published[ISO_TIME_LEN+1]; char fingerprint[FINGERPRINT_LEN+1]; - int has_extra_info_digest; - char extra_info_digest[HEX_DIGEST_LEN+1]; + char *extra_info_line = NULL; size_t onion_pkeylen, identity_pkeylen; char *family_line = NULL; char *extra_or_address = NULL; const or_options_t *options = get_options(); smartlist_t *chunks = NULL; char *output = NULL; + const int emit_ed_sigs = signing_keypair && router->signing_key_cert; + char *ed_cert_line = NULL; + char *rsa_tap_cc_line = NULL; + char *ntor_cc_line = NULL; /* Make sure the identity key matches the one in the routerinfo. */ if (!crypto_pk_eq_keys(ident_key, router->identity_pkey)) { @@ -2351,6 +2386,16 @@ router_dump_router_to_string(routerinfo_t *router, "match router's public key!"); goto err; } + if (emit_ed_sigs) { + if (!router->signing_key_cert->signing_key_included || + !ed25519_pubkey_eq(&router->signing_key_cert->signed_key, + &signing_keypair->pubkey)) { + log_warn(LD_BUG, "Tried to sign a router descriptor with a mismatched " + "ed25519 key chain %d", + router->signing_key_cert->signing_key_included); + goto err; + } + } /* record our fingerprint, so we can include it in the descriptor */ if (crypto_pk_get_fingerprint(router->identity_pkey, fingerprint, 1)<0) { @@ -2358,6 +2403,22 @@ router_dump_router_to_string(routerinfo_t *router, goto err; } + if (emit_ed_sigs) { + /* Encode ed25519 signing cert */ + char ed_cert_base64[256]; + if (base64_encode(ed_cert_base64, sizeof(ed_cert_base64), + (const char*)router->signing_key_cert->encoded, + router->signing_key_cert->encoded_len, + BASE64_ENCODE_MULTILINE) < 0) { + log_err(LD_BUG,"Couldn't base64-encode signing key certificate!"); + goto err; + } + tor_asprintf(&ed_cert_line, "identity-ed25519\n" + "-----BEGIN ED25519 CERT-----\n" + "%s" + "-----END ED25519 CERT-----\n", ed_cert_base64); + } + /* PEM-encode the onion key */ if (crypto_pk_write_public_key_to_string(router->onion_pkey, &onion_pkey,&onion_pkeylen)<0) { @@ -2372,6 +2433,69 @@ router_dump_router_to_string(routerinfo_t *router, goto err; } + /* Cross-certify with RSA key */ + if (tap_key && router->signing_key_cert && + router->signing_key_cert->signing_key_included) { + char buf[256]; + int tap_cc_len = 0; + uint8_t *tap_cc = + make_tap_onion_key_crosscert(tap_key, + &router->signing_key_cert->signing_key, + router->identity_pkey, + &tap_cc_len); + if (!tap_cc) { + log_warn(LD_BUG,"make_tap_onion_key_crosscert failed!"); + goto err; + } + + if (base64_encode(buf, sizeof(buf), (const char*)tap_cc, tap_cc_len, + BASE64_ENCODE_MULTILINE) < 0) { + log_warn(LD_BUG,"base64_encode(rsa_crosscert) failed!"); + tor_free(tap_cc); + goto err; + } + tor_free(tap_cc); + + tor_asprintf(&rsa_tap_cc_line, + "onion-key-crosscert\n" + "-----BEGIN CROSSCERT-----\n" + "%s" + "-----END CROSSCERT-----\n", buf); + } + + /* Cross-certify with onion keys */ + if (ntor_keypair && router->signing_key_cert && + router->signing_key_cert->signing_key_included) { + int sign = 0; + char buf[256]; + /* XXXX Base the expiration date on the actual onion key expiration time?*/ + tor_cert_t *cert = + make_ntor_onion_key_crosscert(ntor_keypair, + &router->signing_key_cert->signing_key, + router->cache_info.published_on, + MIN_ONION_KEY_LIFETIME, &sign); + if (!cert) { + log_warn(LD_BUG,"make_ntor_onion_key_crosscert failed!"); + goto err; + } + tor_assert(sign == 0 || sign == 1); + + if (base64_encode(buf, sizeof(buf), + (const char*)cert->encoded, cert->encoded_len, + BASE64_ENCODE_MULTILINE)<0) { + log_warn(LD_BUG,"base64_encode(ntor_crosscert) failed!"); + tor_cert_free(cert); + goto err; + } + tor_cert_free(cert); + + tor_asprintf(&ntor_cc_line, + "ntor-onion-key-crosscert %d\n" + "-----BEGIN ED25519 CERT-----\n" + "%s" + "-----END ED25519 CERT-----\n", sign, buf); + } + /* Encode the publication time. */ format_iso_time(published, router->cache_info.published_on); @@ -2384,12 +2508,19 @@ router_dump_router_to_string(routerinfo_t *router, family_line = tor_strdup(""); } - has_extra_info_digest = - ! tor_digest_is_zero(router->cache_info.extra_info_digest); - - if (has_extra_info_digest) { + if (!tor_digest_is_zero(router->cache_info.extra_info_digest)) { + char extra_info_digest[HEX_DIGEST_LEN+1]; base16_encode(extra_info_digest, sizeof(extra_info_digest), router->cache_info.extra_info_digest, DIGEST_LEN); + if (!tor_digest256_is_zero(router->extra_info_digest256)) { + char d256_64[BASE64_DIGEST256_LEN+1]; + digest256_to_base64(d256_64, router->extra_info_digest256); + tor_asprintf(&extra_info_line, "extra-info-digest %s %s\n", + extra_info_digest, d256_64); + } else { + tor_asprintf(&extra_info_line, "extra-info-digest %s\n", + extra_info_digest); + } } if (router->ipv6_orport && @@ -2411,20 +2542,23 @@ router_dump_router_to_string(routerinfo_t *router, smartlist_add_asprintf(chunks, "router %s %s %d 0 %d\n" "%s" + "%s" "platform %s\n" "protocols Link 1 2 Circuit 1\n" "published %s\n" "fingerprint %s\n" "uptime %ld\n" "bandwidth %d %d %d\n" - "%s%s%s%s" + "%s%s" "onion-key\n%s" "signing-key\n%s" + "%s%s" "%s%s%s%s", router->nickname, address, router->or_port, decide_to_advertise_dirport(options, router->dir_port), + ed_cert_line ? ed_cert_line : "", extra_or_address ? extra_or_address : "", router->platform, published, @@ -2433,12 +2567,12 @@ router_dump_router_to_string(routerinfo_t *router, (int) router->bandwidthrate, (int) router->bandwidthburst, (int) router->bandwidthcapacity, - has_extra_info_digest ? "extra-info-digest " : "", - has_extra_info_digest ? extra_info_digest : "", - has_extra_info_digest ? "\n" : "", + extra_info_line ? extra_info_line : "", (options->DownloadExtraInfo || options->V3AuthoritativeDir) ? "caches-extra-info\n" : "", onion_pkey, identity_pkey, + rsa_tap_cc_line ? rsa_tap_cc_line : "", + ntor_cc_line ? ntor_cc_line : "", family_line, we_are_hibernating() ? "hibernating 1\n" : "", options->HidServDirectoryV2 ? "hidden-service-dir\n" : "", @@ -2481,7 +2615,24 @@ router_dump_router_to_string(routerinfo_t *router, tor_free(p6); } - /* Sign the descriptor */ + /* Sign the descriptor with Ed25519 */ + if (emit_ed_sigs) { + smartlist_add(chunks, tor_strdup("router-sig-ed25519 ")); + crypto_digest_smartlist_prefix(digest, DIGEST256_LEN, + ED_DESC_SIGNATURE_PREFIX, + chunks, "", DIGEST_SHA256); + ed25519_signature_t sig; + char buf[ED25519_SIG_BASE64_LEN+1]; + if (ed25519_sign(&sig, (const uint8_t*)digest, DIGEST256_LEN, + signing_keypair) < 0) + goto err; + if (ed25519_signature_to_base64(buf, &sig) < 0) + goto err; + + smartlist_add_asprintf(chunks, "%s\n", buf); + } + + /* Sign the descriptor with RSA */ smartlist_add(chunks, tor_strdup("router-signature\n")); crypto_digest_smartlist(digest, DIGEST_LEN, chunks, "", DIGEST_SHA1); @@ -2533,6 +2684,10 @@ router_dump_router_to_string(routerinfo_t *router, tor_free(onion_pkey); tor_free(identity_pkey); tor_free(extra_or_address); + tor_free(ed_cert_line); + tor_free(rsa_tap_cc_line); + tor_free(ntor_cc_line); + tor_free(extra_info_line); return output; } @@ -2676,7 +2831,8 @@ load_stats_file(const char *filename, const char *end_line, time_t now, * success, negative on failure. */ int extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo, - crypto_pk_t *ident_key) + crypto_pk_t *ident_key, + const ed25519_keypair_t *signing_keypair) { const or_options_t *options = get_options(); char identity[HEX_DIGEST_LEN+1]; @@ -2686,18 +2842,45 @@ extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo, int result; static int write_stats_to_extrainfo = 1; char sig[DIROBJ_MAX_SIG_LEN+1]; - char *s, *pre, *contents, *cp, *s_dup = NULL; + char *s = NULL, *pre, *contents, *cp, *s_dup = NULL; time_t now = time(NULL); smartlist_t *chunks = smartlist_new(); extrainfo_t *ei_tmp = NULL; + const int emit_ed_sigs = signing_keypair && extrainfo->signing_key_cert; + char *ed_cert_line = NULL; base16_encode(identity, sizeof(identity), extrainfo->cache_info.identity_digest, DIGEST_LEN); format_iso_time(published, extrainfo->cache_info.published_on); bandwidth_usage = rep_hist_get_bandwidth_lines(); + if (emit_ed_sigs) { + if (!extrainfo->signing_key_cert->signing_key_included || + !ed25519_pubkey_eq(&extrainfo->signing_key_cert->signed_key, + &signing_keypair->pubkey)) { + log_warn(LD_BUG, "Tried to sign a extrainfo descriptor with a " + "mismatched ed25519 key chain %d", + extrainfo->signing_key_cert->signing_key_included); + goto err; + } + char ed_cert_base64[256]; + if (base64_encode(ed_cert_base64, sizeof(ed_cert_base64), + (const char*)extrainfo->signing_key_cert->encoded, + extrainfo->signing_key_cert->encoded_len, + BASE64_ENCODE_MULTILINE) < 0) { + log_err(LD_BUG,"Couldn't base64-encode signing key certificate!"); + goto err; + } + tor_asprintf(&ed_cert_line, "identity-ed25519\n" + "-----BEGIN ED25519 CERT-----\n" + "%s" + "-----END ED25519 CERT-----\n", ed_cert_base64); + } else { + ed_cert_line = tor_strdup(""); + } - tor_asprintf(&pre, "extra-info %s %s\npublished %s\n%s", + tor_asprintf(&pre, "extra-info %s %s\n%spublished %s\n%s", extrainfo->nickname, identity, + ed_cert_line, published, bandwidth_usage); tor_free(bandwidth_usage); smartlist_add(chunks, pre); @@ -2757,6 +2940,23 @@ extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo, } } + if (emit_ed_sigs) { + char digest[DIGEST256_LEN]; + smartlist_add(chunks, tor_strdup("router-sig-ed25519 ")); + crypto_digest_smartlist_prefix(digest, DIGEST256_LEN, + ED_DESC_SIGNATURE_PREFIX, + chunks, "", DIGEST_SHA256); + ed25519_signature_t sig; + char buf[ED25519_SIG_BASE64_LEN+1]; + if (ed25519_sign(&sig, (const uint8_t*)digest, DIGEST256_LEN, + signing_keypair) < 0) + goto err; + if (ed25519_signature_to_base64(buf, &sig) < 0) + goto err; + + smartlist_add_asprintf(chunks, "%s\n", buf); + } + smartlist_add(chunks, tor_strdup("router-signature\n")); s = smartlist_join_strings(chunks, "", 0, NULL); @@ -2805,7 +3005,8 @@ extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo, "adding statistics to this or any future " "extra-info descriptors."); write_stats_to_extrainfo = 0; - result = extrainfo_dump_to_string(s_out, extrainfo, ident_key); + result = extrainfo_dump_to_string(s_out, extrainfo, ident_key, + signing_keypair); goto done; } else { log_warn(LD_BUG, "We just generated an extrainfo descriptor we " @@ -2827,6 +3028,7 @@ extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo, SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp)); smartlist_free(chunks); tor_free(s_dup); + tor_free(ed_cert_line); extrainfo_free(ei_tmp); return result; diff --git a/src/or/router.h b/src/or/router.h index 73b43bc751..61b35d6b5a 100644 --- a/src/or/router.h +++ b/src/or/router.h @@ -92,7 +92,10 @@ int router_pick_published_address(const or_options_t *options, uint32_t *addr); int router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e); int router_rebuild_descriptor(int force); char *router_dump_router_to_string(routerinfo_t *router, - crypto_pk_t *ident_key); + const crypto_pk_t *ident_key, + const crypto_pk_t *tap_key, + const curve25519_keypair_t *ntor_keypair, + const ed25519_keypair_t *signing_keypair); char *router_dump_exit_policy_to_string(const routerinfo_t *router, int include_ipv4, int include_ipv6); @@ -107,7 +110,8 @@ int router_has_addr(const routerinfo_t *router, const tor_addr_t *addr); int router_has_orport(const routerinfo_t *router, const tor_addr_port_t *orport); int extrainfo_dump_to_string(char **s, extrainfo_t *extrainfo, - crypto_pk_t *ident_key); + crypto_pk_t *ident_key, + const ed25519_keypair_t *signing_keypair); int is_legal_nickname(const char *s); int is_legal_nickname_or_hexdigest(const char *s); int is_legal_hexdigest(const char *s); diff --git a/src/or/routerkeys.c b/src/or/routerkeys.c new file mode 100644 index 0000000000..556ab45732 --- /dev/null +++ b/src/or/routerkeys.c @@ -0,0 +1,648 @@ +/* Copyright (c) 2014, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "or.h" +#include "config.h" +#include "router.h" +#include "routerkeys.h" +#include "torcert.h" + +/** + * Read an ed25519 key and associated certificates from files beginning with + * fname, with certificate type cert_type. On failure, return + * NULL; on success return the keypair. + * + * If INIT_ED_KEY_CREATE is set in flags, then create the key (and + * certificate if requested) if it doesn't exist, and save it to disk. + * + * If INIT_ED_KEY_NEEDCERT is set in flags, load/create a certificate + * too and store it in *cert_out. Fail if the cert can't be + * found/created. To create a certificate, signing_key must be set to + * the key that should sign it; now to the current time, and + * lifetime to the lifetime of the key. + * + * If INIT_ED_KEY_REPLACE is set in flags, then create and save new key + * whether we can read the old one or not. + * + * If INIT_ED_KEY_EXTRA_STRONG is set in flags, set the extra_strong + * flag when creating the secret key. + * + * If INIT_ED_KEY_INCLUDE_SIGNING_KEY_IN_CERT is set in flags, and + * we create a new certificate, create it with the signing key embedded. + * + * If INIT_ED_KEY_SPLIT is set in flags, and we create a new key, + * store the public key in a separate file from the secret key. + * + * If INIT_ED_KEY_MISSING_SECRET_OK is set in flags, and we find a + * public key file but no secret key file, return successfully anyway. + * + * If INIT_ED_KEY_OMIT_SECRET is set in flags, do not even try to + * load or return a secret key (but create and save on if needed). + */ +ed25519_keypair_t * +ed_key_init_from_file(const char *fname, uint32_t flags, + int severity, + const ed25519_keypair_t *signing_key, + time_t now, + time_t lifetime, + uint8_t cert_type, + struct tor_cert_st **cert_out) +{ + char *secret_fname = NULL; + char *public_fname = NULL; + char *cert_fname = NULL; + int created_pk = 0, created_sk = 0, created_cert = 0; + const int try_to_load = ! (flags & INIT_ED_KEY_REPLACE); + + char tag[8]; + tor_snprintf(tag, sizeof(tag), "type%d", (int)cert_type); + + tor_cert_t *cert = NULL; + char *got_tag = NULL; + ed25519_keypair_t *keypair = tor_malloc_zero(sizeof(ed25519_keypair_t)); + + tor_asprintf(&secret_fname, "%s_secret_key", fname); + tor_asprintf(&public_fname, "%s_public_key", fname); + tor_asprintf(&cert_fname, "%s_cert", fname); + + /* Try to read the secret key. */ + const int have_secret = try_to_load && + !(flags & INIT_ED_KEY_OMIT_SECRET) && + ed25519_seckey_read_from_file(&keypair->seckey, + &got_tag, secret_fname) == 0; + + if (have_secret) { + if (strcmp(got_tag, tag)) { + tor_log(severity, LD_OR, "%s has wrong tag", secret_fname); + goto err; + } + /* Derive the public key */ + if (ed25519_public_key_generate(&keypair->pubkey, &keypair->seckey)<0) { + tor_log(severity, LD_OR, "%s can't produce a public key", secret_fname); + goto err; + } + } + + /* If it's absent and that's okay, try to read the pubkey. */ + int found_public = 0; + if (!have_secret && try_to_load) { + tor_free(got_tag); + found_public = ed25519_pubkey_read_from_file(&keypair->pubkey, + &got_tag, public_fname) == 0; + if (found_public && strcmp(got_tag, tag)) { + tor_log(severity, LD_OR, "%s has wrong tag", public_fname); + goto err; + } + } + + /* If the secret key is absent and it's not allowed to be, fail. */ + if (!have_secret && found_public && !(flags & INIT_ED_KEY_MISSING_SECRET_OK)) + goto err; + + /* If it's absent, and we're not supposed to make a new keypair, fail. */ + if (!have_secret && !found_public && !(flags & INIT_ED_KEY_CREATE)) + goto err; + + /* if it's absent, make a new keypair and save it. */ + if (!have_secret && !found_public) { + const int split = !! (flags & INIT_ED_KEY_SPLIT); + tor_free(keypair); + keypair = ed_key_new(signing_key, flags, now, lifetime, + cert_type, &cert); + if (!keypair) { + tor_log(severity, LD_OR, "Couldn't create keypair"); + goto err; + } + + created_pk = created_sk = created_cert = 1; + if (ed25519_seckey_write_to_file(&keypair->seckey, secret_fname, tag) < 0 + || + (split && + ed25519_pubkey_write_to_file(&keypair->pubkey, public_fname, tag) < 0) + || + (cert && + crypto_write_tagged_contents_to_file(cert_fname, "ed25519v1-cert", + tag, cert->encoded, cert->encoded_len) < 0)) { + tor_log(severity, LD_OR, "Couldn't write keys or cert to file."); + goto err; + } + goto done; + } + + /* If we're not supposed to get a cert, we're done. */ + if (! (flags & INIT_ED_KEY_NEEDCERT)) + goto done; + + /* Read a cert. */ + uint8_t certbuf[256]; + ssize_t cert_body_len = crypto_read_tagged_contents_from_file( + cert_fname, "ed25519v1-cert", + &got_tag, certbuf, sizeof(certbuf)); + if (cert_body_len >= 0 && !strcmp(got_tag, tag)) + cert = tor_cert_parse(certbuf, cert_body_len); + + /* If we got it, check it to the extent we can. */ + if (cert) { + int bad_cert = 0; + + if (! cert) { + tor_log(severity, LD_OR, "Cert was unparseable"); + bad_cert = 1; + } else if (!tor_memeq(cert->signed_key.pubkey, keypair->pubkey.pubkey, + ED25519_PUBKEY_LEN)) { + tor_log(severity, LD_OR, "Cert was for wrong key"); + bad_cert = 1; + } else if (tor_cert_checksig(cert, &signing_key->pubkey, now) < 0 && + (signing_key || cert->cert_expired)) { + tor_log(severity, LD_OR, "Can't check certificate"); + bad_cert = 1; + } + + if (bad_cert) { + tor_cert_free(cert); + cert = NULL; + } + } + + /* If we got a cert, we're done. */ + if (cert) + goto done; + + /* If we didn't get a cert, and we're not supposed to make one, fail. */ + if (!signing_key || !(flags & INIT_ED_KEY_CREATE)) + goto err; + + /* We have keys but not a certificate, so make one. */ + uint32_t cert_flags = 0; + if (flags & INIT_ED_KEY_INCLUDE_SIGNING_KEY_IN_CERT) + cert_flags |= CERT_FLAG_INCLUDE_SIGNING_KEY; + cert = tor_cert_create(signing_key, cert_type, + &keypair->pubkey, + now, lifetime, + cert_flags); + + if (! cert) + goto err; + + /* Write it to disk. */ + created_cert = 1; + if (crypto_write_tagged_contents_to_file(cert_fname, "ed25519v1-cert", + tag, cert->encoded, cert->encoded_len) < 0) { + tor_log(severity, LD_OR, "Couldn't write cert to disk."); + goto err; + } + + done: + if (cert_out) + *cert_out = cert; + else + tor_cert_free(cert); + + goto cleanup; + + err: + memwipe(keypair, 0, sizeof(*keypair)); + tor_free(keypair); + tor_cert_free(cert); + if (cert_out) + *cert_out = NULL; + if (created_sk) + unlink(secret_fname); + if (created_pk) + unlink(public_fname); + if (created_cert) + unlink(cert_fname); + + cleanup: + tor_free(secret_fname); + tor_free(public_fname); + tor_free(cert_fname); + + return keypair; +} + +/** + * Create a new signing key and (optionally) certficiate; do not read or write + * from disk. See ed_key_init_from_file() for more information. + */ +ed25519_keypair_t * +ed_key_new(const ed25519_keypair_t *signing_key, + uint32_t flags, + time_t now, + time_t lifetime, + uint8_t cert_type, + struct tor_cert_st **cert_out) +{ + if (cert_out) + *cert_out = NULL; + + const int extra_strong = !! (flags & INIT_ED_KEY_EXTRA_STRONG); + ed25519_keypair_t *keypair = tor_malloc_zero(sizeof(ed25519_keypair_t)); + if (ed25519_keypair_generate(keypair, extra_strong) < 0) + goto err; + + if (! (flags & INIT_ED_KEY_NEEDCERT)) + return keypair; + + tor_assert(signing_key); + tor_assert(cert_out); + uint32_t cert_flags = 0; + if (flags & INIT_ED_KEY_INCLUDE_SIGNING_KEY_IN_CERT) + cert_flags |= CERT_FLAG_INCLUDE_SIGNING_KEY; + tor_cert_t *cert = tor_cert_create(signing_key, cert_type, + &keypair->pubkey, + now, lifetime, + cert_flags); + if (! cert) + goto err; + + *cert_out = cert; + return keypair; + + err: + tor_free(keypair); + return NULL; +} + +static ed25519_keypair_t *master_identity_key = NULL; +static ed25519_keypair_t *master_signing_key = NULL; +static ed25519_keypair_t *current_auth_key = NULL; +static tor_cert_t *signing_key_cert = NULL; +static tor_cert_t *link_cert_cert = NULL; +static tor_cert_t *auth_key_cert = NULL; + +static uint8_t *rsa_ed_crosscert = NULL; +static size_t rsa_ed_crosscert_len = 0; + +/** + * Running as a server: load, reload, or refresh our ed25519 keys and + * certificates, creating and saving new ones as needed. + */ +int +load_ed_keys(const or_options_t *options, time_t now) +{ + ed25519_keypair_t *id = NULL; + ed25519_keypair_t *sign = NULL; + ed25519_keypair_t *auth = NULL; + const ed25519_keypair_t *sign_signing_key_with_id = NULL; + const ed25519_keypair_t *use_signing = NULL; + const tor_cert_t *check_signing_cert = NULL; + tor_cert_t *sign_cert = NULL; + tor_cert_t *auth_cert = NULL; + +#define FAIL(msg) do { \ + log_warn(LD_OR, (msg)); \ + goto err; \ + } while (0) +#define SET_KEY(key, newval) do { \ + ed25519_keypair_free(key); \ + key = (newval); \ + } while (0) +#define SET_CERT(cert, newval) do { \ + tor_cert_free(cert); \ + cert = (newval); \ + } while (0) +#define EXPIRES_SOON(cert, interval) \ + (!(cert) || (cert)->valid_until < now + (interval)) + + /* XXXX support encrypted identity keys fully */ + + /* First try to get the signing key to see how it is. */ + if (master_signing_key) { + check_signing_cert = signing_key_cert; + use_signing = master_signing_key; + } else { + sign = ed_key_init_from_file( + options_get_datadir_fname2(options, "keys", "ed25519_signing"), + INIT_ED_KEY_NEEDCERT| + INIT_ED_KEY_INCLUDE_SIGNING_KEY_IN_CERT, + LOG_INFO, + NULL, 0, 0, CERT_TYPE_ID_SIGNING, &sign_cert); + check_signing_cert = sign_cert; + use_signing = sign; + } + + const int need_new_signing_key = + NULL == use_signing || + EXPIRES_SOON(check_signing_cert, 0); + const int want_new_signing_key = + need_new_signing_key || + EXPIRES_SOON(check_signing_cert, options->TestingSigningKeySlop); + + { + uint32_t flags = + (INIT_ED_KEY_CREATE|INIT_ED_KEY_SPLIT| + INIT_ED_KEY_EXTRA_STRONG); + if (! need_new_signing_key) + flags |= INIT_ED_KEY_MISSING_SECRET_OK; + if (! want_new_signing_key) + flags |= INIT_ED_KEY_OMIT_SECRET; + + id = ed_key_init_from_file( + options_get_datadir_fname2(options, "keys", "ed25519_master_id"), + flags, + LOG_WARN, NULL, 0, 0, 0, NULL); + if (!id) + FAIL("Missing identity key"); + if (tor_mem_is_zero((char*)id->seckey.seckey, sizeof(id->seckey))) + sign_signing_key_with_id = NULL; + else + sign_signing_key_with_id = id; + } + + if (need_new_signing_key && NULL == sign_signing_key_with_id) + FAIL("Can't load master key make a new signing key."); + + if (want_new_signing_key && sign_signing_key_with_id) { + uint32_t flags = (INIT_ED_KEY_CREATE| + INIT_ED_KEY_REPLACE| + INIT_ED_KEY_EXTRA_STRONG| + INIT_ED_KEY_NEEDCERT| + INIT_ED_KEY_INCLUDE_SIGNING_KEY_IN_CERT); + sign = ed_key_init_from_file( + options_get_datadir_fname2(options, "keys", "ed25519_signing"), + flags, LOG_WARN, + sign_signing_key_with_id, now, + options->SigningKeyLifetime, + CERT_TYPE_ID_SIGNING, &sign_cert); + if (!sign) + FAIL("Missing signing key"); + use_signing = sign; + } else if (want_new_signing_key) { + static ratelim_t missing_master = RATELIM_INIT(3600); + log_fn_ratelim(&missing_master, LOG_WARN, LD_OR, + "Signing key will expire soon, but I can't load the " + "master key to sign a new one!"); + } + + tor_assert(use_signing); + + /* At this point we no longer need our secret identity key. So wipe + * it, if we loaded it in the first place. */ + memwipe(id->seckey.seckey, 0, sizeof(id->seckey)); + + if (!rsa_ed_crosscert && server_mode(options)) { + uint8_t *crosscert; + ssize_t crosscert_len = tor_make_rsa_ed25519_crosscert(&id->pubkey, + get_server_identity_key(), + now+10*365*86400,/*XXXX*/ + &crosscert); + rsa_ed_crosscert_len = crosscert_len; + rsa_ed_crosscert = crosscert; + } + + if (!current_auth_key || + EXPIRES_SOON(auth_key_cert, options->TestingAuthKeySlop)) { + auth = ed_key_new(use_signing, INIT_ED_KEY_NEEDCERT, + now, + options->TestingAuthKeyLifetime, + CERT_TYPE_SIGNING_AUTH, &auth_cert); + + if (!auth) + FAIL("Can't create auth key"); + } + + /* We've generated or loaded everything. Put them in memory. */ + + if (! master_identity_key) { + SET_KEY(master_identity_key, id); + } else { + tor_free(id); + } + if (sign) { + SET_KEY(master_signing_key, sign); + SET_CERT(signing_key_cert, sign_cert); + } + if (auth) { + SET_KEY(current_auth_key, auth); + SET_CERT(auth_key_cert, auth_cert); + } + + return 0; + err: + ed25519_keypair_free(id); + ed25519_keypair_free(sign); + ed25519_keypair_free(auth); + tor_cert_free(sign_cert); + tor_cert_free(auth_cert); + return -1; +} + +/**DOCDOC*/ +int +generate_ed_link_cert(const or_options_t *options, time_t now) +{ + const tor_x509_cert_t *link = NULL, *id = NULL; + tor_cert_t *link_cert = NULL; + + if (tor_tls_get_my_certs(1, &link, &id) < 0 || link == NULL) { + log_warn(LD_OR, "Can't get my x509 link cert."); + return -1; + } + + const digests_t *digests = tor_x509_cert_get_cert_digests(link); + + if (link_cert_cert && + ! EXPIRES_SOON(link_cert_cert, options->TestingLinkKeySlop) && + fast_memeq(digests->d[DIGEST_SHA256], link_cert_cert->signed_key.pubkey, + DIGEST256_LEN)) { + return 0; + } + + ed25519_public_key_t dummy_key; + memcpy(dummy_key.pubkey, digests->d[DIGEST_SHA256], DIGEST256_LEN); + + link_cert = tor_cert_create(get_master_signing_keypair(), + CERT_TYPE_SIGNING_LINK, + &dummy_key, + now, + options->TestingLinkCertLifetime, 0); + + if (link_cert) { + SET_CERT(link_cert_cert, link_cert); + } + return 0; +} + +#undef FAIL +#undef SET_KEY +#undef SET_CERT + +int +should_make_new_ed_keys(const or_options_t *options, const time_t now) +{ + if (!master_identity_key || + !master_signing_key || + !current_auth_key || + !link_cert_cert || + EXPIRES_SOON(signing_key_cert, options->TestingSigningKeySlop) || + EXPIRES_SOON(auth_key_cert, options->TestingAuthKeySlop) || + EXPIRES_SOON(link_cert_cert, options->TestingLinkKeySlop)) + return 1; + + const tor_x509_cert_t *link = NULL, *id = NULL; + + if (tor_tls_get_my_certs(1, &link, &id) < 0 || link == NULL) + return 1; + + const digests_t *digests = tor_x509_cert_get_cert_digests(link); + + if (!fast_memeq(digests->d[DIGEST_SHA256], + link_cert_cert->signed_key.pubkey, + DIGEST256_LEN)) { + return 1; + } + + return 0; +} + +#undef EXPIRES_SOON + +const ed25519_public_key_t * +get_master_identity_key(void) +{ + if (!master_identity_key) + return NULL; + return &master_identity_key->pubkey; +} + +const ed25519_keypair_t * +get_master_signing_keypair(void) +{ + return master_signing_key; +} + +const struct tor_cert_st * +get_master_signing_key_cert(void) +{ + return signing_key_cert; +} + +const ed25519_keypair_t * +get_current_auth_keypair(void) +{ + return current_auth_key; +} + +const tor_cert_t * +get_current_link_cert_cert(void) +{ + return link_cert_cert; +} + +const tor_cert_t * +get_current_auth_key_cert(void) +{ + return auth_key_cert; +} + +void +get_master_rsa_crosscert(const uint8_t **cert_out, + size_t *size_out) +{ + *cert_out = rsa_ed_crosscert; + *size_out = rsa_ed_crosscert_len; +} + +/** Construct cross-certification for the master identity key with + * the ntor onion key. Store the sign of the corresponding ed25519 public key + * in *sign_out. */ +tor_cert_t * +make_ntor_onion_key_crosscert(const curve25519_keypair_t *onion_key, + const ed25519_public_key_t *master_id_key, time_t now, time_t lifetime, + int *sign_out) +{ + tor_cert_t *cert = NULL; + ed25519_keypair_t ed_onion_key; + + if (ed25519_keypair_from_curve25519_keypair(&ed_onion_key, sign_out, + onion_key) < 0) + goto end; + + cert = tor_cert_create(&ed_onion_key, CERT_TYPE_ONION_ID, master_id_key, + now, lifetime, 0); + + end: + memwipe(&ed_onion_key, 0, sizeof(ed_onion_key)); + return cert; +} + +/** Construct and return an RSA signature for the TAP onion key to + * cross-certify the RSA and Ed25519 identity keys. Set len_out 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; + crypto_pk_get_digest(rsa_id_key, (char*)signed_data); + 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); +} + +/** Check whether an RSA-TAP cross-certification is correct. Return 0 if it + * is, -1 if it isn't. */ +int +check_tap_onion_key_crosscert(const uint8_t *crosscert, + int crosscert_len, + const crypto_pk_t *onion_pkey, + const ed25519_public_key_t *master_id_pkey, + const uint8_t *rsa_id_digest) +{ + uint8_t *cc = tor_malloc(crypto_pk_keysize(onion_pkey)); + int cc_len = + crypto_pk_public_checksig(onion_pkey, + (char*)cc, + crypto_pk_keysize(onion_pkey), + (const char*)crosscert, + crosscert_len); + if (cc_len < 0) { + goto err; + } + if (cc_len < DIGEST_LEN + ED25519_PUBKEY_LEN) { + log_warn(LD_DIR, "Short signature on cross-certification with TAP key"); + goto err; + } + if (tor_memneq(cc, rsa_id_digest, DIGEST_LEN) || + tor_memneq(cc + DIGEST_LEN, master_id_pkey->pubkey, + ED25519_PUBKEY_LEN)) { + log_warn(LD_DIR, "Incorrect cross-certification with TAP key"); + goto err; + } + + tor_free(cc); + return 0; + err: + tor_free(cc); + return -1; +} + +void +routerkeys_free_all(void) +{ + ed25519_keypair_free(master_identity_key); + ed25519_keypair_free(master_signing_key); + ed25519_keypair_free(current_auth_key); + tor_cert_free(signing_key_cert); + tor_cert_free(link_cert_cert); + tor_cert_free(auth_key_cert); + + master_identity_key = master_signing_key = NULL; + current_auth_key = NULL; + signing_key_cert = link_cert_cert = auth_key_cert = NULL; +} + diff --git a/src/or/routerkeys.h b/src/or/routerkeys.h new file mode 100644 index 0000000000..b45a22ac12 --- /dev/null +++ b/src/or/routerkeys.h @@ -0,0 +1,67 @@ +/* Copyright (c) 2014, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#ifndef TOR_ROUTERKEYS_H +#define TOR_ROUTERKEYS_H + +#include "crypto_ed25519.h" + +#define INIT_ED_KEY_CREATE (1u<<0) +#define INIT_ED_KEY_REPLACE (1u<<1) +#define INIT_ED_KEY_SPLIT (1u<<2) +#define INIT_ED_KEY_MISSING_SECRET_OK (1u<<3) +#define INIT_ED_KEY_NEEDCERT (1u<<4) +#define INIT_ED_KEY_EXTRA_STRONG (1u<<5) +#define INIT_ED_KEY_INCLUDE_SIGNING_KEY_IN_CERT (1u<<6) +#define INIT_ED_KEY_OMIT_SECRET (1u<<7) + +struct tor_cert_st; +ed25519_keypair_t *ed_key_init_from_file(const char *fname, uint32_t flags, + int severity, + const ed25519_keypair_t *signing_key, + time_t now, + time_t lifetime, + uint8_t cert_type, + struct tor_cert_st **cert_out); +ed25519_keypair_t *ed_key_new(const ed25519_keypair_t *signing_key, + uint32_t flags, + time_t now, + time_t lifetime, + uint8_t cert_type, + struct tor_cert_st **cert_out); +const ed25519_public_key_t *get_master_identity_key(void); +const ed25519_keypair_t *get_master_signing_keypair(void); +const struct tor_cert_st *get_master_signing_key_cert(void); + +const ed25519_keypair_t *get_current_auth_keypair(void); +const struct tor_cert_st *get_current_link_cert_cert(void); +const struct tor_cert_st *get_current_auth_key_cert(void); + +void get_master_rsa_crosscert(const uint8_t **cert_out, + size_t *size_out); + +struct tor_cert_st *make_ntor_onion_key_crosscert( + const curve25519_keypair_t *onion_key, + const ed25519_public_key_t *master_id_key, + time_t now, time_t lifetime, + int *sign_out); +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); + +int check_tap_onion_key_crosscert(const uint8_t *crosscert, + int crosscert_len, + const crypto_pk_t *onion_pkey, + const ed25519_public_key_t *master_id_pkey, + const uint8_t *rsa_id_digest); + +int load_ed_keys(const or_options_t *options, time_t now); +int should_make_new_ed_keys(const or_options_t *options, const time_t now); + +int generate_ed_link_cert(const or_options_t *options, time_t now); + +void routerkeys_free_all(void); + +#endif + diff --git a/src/or/routerlist.c b/src/or/routerlist.c index fd096799de..7eba13a544 100644 --- a/src/or/routerlist.c +++ b/src/or/routerlist.c @@ -13,6 +13,7 @@ #define ROUTERLIST_PRIVATE #include "or.h" +#include "crypto_ed25519.h" #include "circuitstats.h" #include "config.h" #include "connection.h" @@ -38,6 +39,8 @@ #include "routerparse.h" #include "routerset.h" #include "sandbox.h" +#include "torcert.h" + // #define DEBUG_ROUTERLIST /****************************************************************************/ @@ -2660,6 +2663,7 @@ routerinfo_free(routerinfo_t *router) tor_free(router->onion_curve25519_pkey); if (router->identity_pkey) crypto_pk_free(router->identity_pkey); + tor_cert_free(router->signing_key_cert); if (router->declared_family) { SMARTLIST_FOREACH(router->declared_family, char *, s, tor_free(s)); smartlist_free(router->declared_family); @@ -2678,6 +2682,7 @@ extrainfo_free(extrainfo_t *extrainfo) { if (!extrainfo) return; + tor_cert_free(extrainfo->signing_key_cert); tor_free(extrainfo->cache_info.signed_descriptor_body); tor_free(extrainfo->pending_sig); @@ -3288,6 +3293,11 @@ router_add_to_routerlist(routerinfo_t *router, const char **msg, old_router = router_get_mutable_by_digest(id_digest); + /* Make sure that it isn't expired. */ + if (router->cert_expiration_time < approx_time()) { + return ROUTER_CERTS_EXPIRED; + } + /* Make sure that we haven't already got this exact descriptor. */ if (sdmap_get(routerlist->desc_digest_map, router->cache_info.signed_descriptor_digest)) { @@ -4894,7 +4904,7 @@ routerinfo_incompatible_with_extrainfo(const routerinfo_t *ri, signed_descriptor_t *sd, const char **msg) { - int digest_matches, r=1; + int digest_matches, digest256_matches, r=1; tor_assert(ri); tor_assert(ei); if (!sd) @@ -4907,6 +4917,11 @@ routerinfo_incompatible_with_extrainfo(const routerinfo_t *ri, digest_matches = tor_memeq(ei->cache_info.signed_descriptor_digest, sd->extra_info_digest, DIGEST_LEN); + /* Set digest256_matches to 1 if the digest is correct, or if no + * digest256 was in the ri. */ + digest256_matches = tor_memeq(ei->digest256, + ri->extra_info_digest256, DIGEST256_LEN); + digest256_matches |= tor_mem_is_zero(ri->extra_info_digest256, DIGEST256_LEN); /* The identity must match exactly to have been generated at the same time * by the same router. */ @@ -4917,6 +4932,11 @@ routerinfo_incompatible_with_extrainfo(const routerinfo_t *ri, goto err; /* different servers */ } + if (! tor_cert_opt_eq(ri->signing_key_cert, ei->signing_key_cert)) { + if (msg) *msg = "Extrainfo signing key cert didn't match routerinfo"; + goto err; /* different servers */ + } + if (ei->pending_sig) { char signed_digest[128]; if (crypto_pk_public_checksig(ri->identity_pkey, @@ -4943,6 +4963,11 @@ routerinfo_incompatible_with_extrainfo(const routerinfo_t *ri, goto err; } + if (!digest256_matches) { + if (msg) *msg = "Extrainfo digest did not match digest256 from routerdesc"; + goto err; /* Digest doesn't match declared value. */ + } + if (!digest_matches) { if (msg) *msg = "Extrainfo digest did not match value from routerdesc"; goto err; /* Digest doesn't match declared value. */ diff --git a/src/or/routerlist.h b/src/or/routerlist.h index 78c3fbb880..200533fe91 100644 --- a/src/or/routerlist.h +++ b/src/or/routerlist.h @@ -118,13 +118,15 @@ WRA_WAS_ADDED(was_router_added_t s) { * - not in the consensus * - neither in the consensus nor in any networkstatus document * - it was outdated. + * - its certificates were expired. */ static INLINE int WRA_WAS_OUTDATED(was_router_added_t s) { return (s == ROUTER_WAS_TOO_OLD || s == ROUTER_IS_ALREADY_KNOWN || s == ROUTER_NOT_IN_CONSENSUS || - s == ROUTER_NOT_IN_CONSENSUS_OR_NETWORKSTATUS); + s == ROUTER_NOT_IN_CONSENSUS_OR_NETWORKSTATUS || + s == ROUTER_CERTS_EXPIRED); } /** Return true iff the outcome code in s indicates that the descriptor * was flat-out rejected. */ @@ -138,7 +140,8 @@ static INLINE int WRA_NEVER_DOWNLOADABLE(was_router_added_t s) { return (s == ROUTER_AUTHDIR_REJECTS || s == ROUTER_BAD_EI || - s == ROUTER_WAS_TOO_OLD); + s == ROUTER_WAS_TOO_OLD || + s == ROUTER_CERTS_EXPIRED); } was_router_added_t router_add_to_routerlist(routerinfo_t *router, const char **msg, diff --git a/src/or/routerparse.c b/src/or/routerparse.c index f15aeeb0cf..c3dc241573 100644 --- a/src/or/routerparse.c +++ b/src/or/routerparse.c @@ -24,8 +24,11 @@ #include "microdesc.h" #include "networkstatus.h" #include "rephist.h" +#include "routerkeys.h" #include "routerparse.h" #include "entrynodes.h" +#include "torcert.h" + #undef log #include @@ -69,6 +72,7 @@ typedef enum { K_CLIENT_VERSIONS, K_SERVER_VERSIONS, K_OR_ADDRESS, + K_ID, K_P, K_P6, K_R, @@ -83,6 +87,10 @@ typedef enum { K_HIDDEN_SERVICE_DIR, K_ALLOW_SINGLE_HOP_EXITS, K_IPV6_POLICY, + K_ROUTER_SIG_ED25519, + K_IDENTITY_ED25519, + K_ONION_KEY_CROSSCERT, + K_NTOR_ONION_KEY_CROSSCERT, K_DIRREQ_END, K_DIRREQ_V2_IPS, @@ -293,6 +301,12 @@ static token_rule_t routerdesc_token_table[] = { T01("write-history", K_WRITE_HISTORY, ARGS, NO_OBJ ), T01("extra-info-digest", K_EXTRA_INFO_DIGEST, GE(1), NO_OBJ ), T01("hidden-service-dir", K_HIDDEN_SERVICE_DIR, NO_ARGS, NO_OBJ ), + T01("identity-ed25519", K_IDENTITY_ED25519, NO_ARGS, NEED_OBJ ), + T01("router-sig-ed25519", K_ROUTER_SIG_ED25519, GE(1), NO_OBJ ), + T01("onion-key-crosscert", K_ONION_KEY_CROSSCERT, NO_ARGS, NEED_OBJ ), + T01("ntor-onion-key-crosscert", K_NTOR_ONION_KEY_CROSSCERT, + EQ(1), NEED_OBJ ), + T01("allow-single-hop-exits",K_ALLOW_SINGLE_HOP_EXITS, NO_ARGS, NO_OBJ ), T01("family", K_FAMILY, ARGS, NO_OBJ ), @@ -310,6 +324,8 @@ static token_rule_t routerdesc_token_table[] = { static token_rule_t extrainfo_token_table[] = { T1_END( "router-signature", K_ROUTER_SIGNATURE, NO_ARGS, NEED_OBJ ), T1( "published", K_PUBLISHED, CONCAT_ARGS, NO_OBJ ), + T01("identity-ed25519", K_IDENTITY_ED25519, NO_ARGS, NEED_OBJ ), + T01("router-sig-ed25519", K_ROUTER_SIG_ED25519, GE(1), NO_OBJ ), T0N("opt", K_OPT, CONCAT_ARGS, OBJ_OK ), T01("read-history", K_READ_HISTORY, ARGS, NO_OBJ ), T01("write-history", K_WRITE_HISTORY, ARGS, NO_OBJ ), @@ -353,6 +369,7 @@ static token_rule_t rtrstatus_token_table[] = { T01("v", K_V, CONCAT_ARGS, NO_OBJ ), T01("w", K_W, ARGS, NO_OBJ ), T0N("m", K_M, CONCAT_ARGS, NO_OBJ ), + T0N("id", K_ID, GE(2), NO_OBJ ), T0N("opt", K_OPT, CONCAT_ARGS, OBJ_OK ), END_OF_TABLE }; @@ -490,6 +507,7 @@ static token_rule_t networkstatus_detached_signature_token_table[] = { static token_rule_t microdesc_token_table[] = { T1_START("onion-key", K_ONION_KEY, NO_ARGS, NEED_KEY_1024), T01("ntor-onion-key", K_ONION_KEY_NTOR, GE(1), NO_OBJ ), + T0N("id", K_ID, GE(2), NO_OBJ ), T0N("a", K_A, GE(1), NO_OBJ ), T01("family", K_FAMILY, ARGS, NO_OBJ ), T01("p", K_P, CONCAT_ARGS, NO_OBJ ), @@ -506,6 +524,10 @@ static addr_policy_t *router_parse_addr_policy(directory_token_t *tok, unsigned fmt_flags); static addr_policy_t *router_parse_addr_policy_private(directory_token_t *tok); +static int router_get_hash_impl_helper(const char *s, size_t s_len, + const char *start_str, + const char *end_str, char end_c, + const char **start_out, const char **end_out); static int router_get_hash_impl(const char *s, size_t s_len, char *digest, const char *start_str, const char *end_str, char end_char, @@ -637,7 +659,7 @@ router_get_extrainfo_hash(const char *s, size_t s_len, char *digest) char * router_get_dirobj_signature(const char *digest, size_t digest_len, - crypto_pk_t *private_key) + const crypto_pk_t *private_key) { char *signature; size_t i, keysize; @@ -858,8 +880,8 @@ check_signature_token(const char *digest, tor_free(signed_digest); return -1; } -// log_debug(LD_DIR,"Signed %s hash starts %s", doctype, -// hex_str(signed_digest,4)); + // log_debug(LD_DIR,"Signed %s hash starts %s", doctype, + // hex_str(signed_digest,4)); if (tor_memneq(digest, signed_digest, digest_len)) { log_warn(LD_DIR, "Error reading %s: signature does not match.", doctype); tor_free(signed_digest); @@ -1106,6 +1128,7 @@ router_parse_entry_from_string(const char *s, const char *end, size_t prepend_len = prepend_annotations ? strlen(prepend_annotations) : 0; int ok = 1; memarea_t *area = NULL; + tor_cert_t *ntor_cc_cert = NULL; /* Do not set this to '1' until we have parsed everything that we intend to * parse that's covered by the hash. */ int can_dl_again = 0; @@ -1178,9 +1201,11 @@ router_parse_entry_from_string(const char *s, const char *end, } tok = find_by_keyword(tokens, K_ROUTER); + const int router_token_pos = smartlist_pos(tokens, tok); tor_assert(tok->n_args >= 5); router = tor_malloc_zero(sizeof(routerinfo_t)); + router->cert_expiration_time = TIME_MAX; router->cache_info.routerlist_index = -1; router->cache_info.annotations_len = s-start_of_annotations + prepend_len; router->cache_info.signed_descriptor_len = end-s; @@ -1311,6 +1336,147 @@ router_parse_entry_from_string(const char *s, const char *end, log_warn(LD_DIR, "Couldn't calculate key digest"); goto err; } + { + directory_token_t *ed_sig_tok, *ed_cert_tok, *cc_tap_tok, *cc_ntor_tok; + ed_sig_tok = find_opt_by_keyword(tokens, K_ROUTER_SIG_ED25519); + ed_cert_tok = find_opt_by_keyword(tokens, K_IDENTITY_ED25519); + cc_tap_tok = find_opt_by_keyword(tokens, K_ONION_KEY_CROSSCERT); + cc_ntor_tok = find_opt_by_keyword(tokens, K_NTOR_ONION_KEY_CROSSCERT); + int n_ed_toks = !!ed_sig_tok + !!ed_cert_tok + + !!cc_tap_tok + !!cc_ntor_tok; + if ((n_ed_toks != 0 && n_ed_toks != 4) || + (n_ed_toks == 4 && !router->onion_curve25519_pkey)) { + log_warn(LD_DIR, "Router descriptor with only partial ed25519/" + "cross-certification support"); + goto err; + } + if (ed_sig_tok) { + tor_assert(ed_cert_tok && cc_tap_tok && cc_ntor_tok); + const int ed_cert_token_pos = smartlist_pos(tokens, ed_cert_tok); + if (ed_cert_token_pos == -1 || router_token_pos == -1 || + (ed_cert_token_pos != router_token_pos + 1 && + ed_cert_token_pos != router_token_pos - 1)) { + log_warn(LD_DIR, "Ed25519 certificate in wrong position"); + goto err; + } + if (ed_sig_tok != smartlist_get(tokens, smartlist_len(tokens)-2)) { + log_warn(LD_DIR, "Ed25519 signature in wrong position"); + goto err; + } + if (strcmp(ed_cert_tok->object_type, "ED25519 CERT")) { + log_warn(LD_DIR, "Wrong object type on identity-ed25519 in decriptor"); + goto err; + } + if (strcmp(cc_ntor_tok->object_type, "ED25519 CERT")) { + log_warn(LD_DIR, "Wrong object type on ntor-onion-key-crosscert " + "in decriptor"); + goto err; + } + if (strcmp(cc_tap_tok->object_type, "CROSSCERT")) { + log_warn(LD_DIR, "Wrong object type on onion-key-crosscert " + "in decriptor"); + goto err; + } + if (strcmp(cc_ntor_tok->args[0], "0") && + strcmp(cc_ntor_tok->args[0], "1")) { + log_warn(LD_DIR, "Bad sign bit on ntor-onion-key-crosscert"); + goto err; + } + int ntor_cc_sign_bit = !strcmp(cc_ntor_tok->args[0], "1"); + + uint8_t d256[DIGEST256_LEN]; + const char *signed_start, *signed_end; + tor_cert_t *cert = tor_cert_parse( + (const uint8_t*)ed_cert_tok->object_body, + ed_cert_tok->object_size); + if (! cert) { + log_warn(LD_DIR, "Couldn't parse ed25519 cert"); + goto err; + } + router->signing_key_cert = cert; /* makes sure it gets freed. */ + if (cert->cert_type != CERT_TYPE_ID_SIGNING || + ! cert->signing_key_included) { + log_warn(LD_DIR, "Invalid form for ed25519 cert"); + goto err; + } + + ntor_cc_cert = tor_cert_parse((const uint8_t*)cc_ntor_tok->object_body, + cc_ntor_tok->object_size); + if (!ntor_cc_cert) { + log_warn(LD_DIR, "Couldn't parse ntor-onion-key-crosscert cert"); + goto err; + } + if (ntor_cc_cert->cert_type != CERT_TYPE_ONION_ID || + ! ed25519_pubkey_eq(&ntor_cc_cert->signed_key, &cert->signing_key)) { + log_warn(LD_DIR, "Invalid contents for ntor-onion-key-crosscert cert"); + goto err; + } + + ed25519_public_key_t ntor_cc_pk; + if (ed25519_public_key_from_curve25519_public_key(&ntor_cc_pk, + router->onion_curve25519_pkey, + ntor_cc_sign_bit)<0) { + log_warn(LD_DIR, "Error converting onion key to ed25519"); + goto err; + } + + if (router_get_hash_impl_helper(s, end-s, "router ", + "\nrouter-sig-ed25519", + ' ', &signed_start, &signed_end) < 0) { + log_warn(LD_DIR, "Can't find ed25519-signed portion of descriptor"); + goto err; + } + crypto_digest_t *d = crypto_digest256_new(DIGEST_SHA256); + crypto_digest_add_bytes(d, ED_DESC_SIGNATURE_PREFIX, + strlen(ED_DESC_SIGNATURE_PREFIX)); + crypto_digest_add_bytes(d, signed_start, signed_end-signed_start); + crypto_digest_get_digest(d, (char*)d256, sizeof(d256)); + crypto_digest_free(d); + + ed25519_checkable_t check[3]; + int check_ok[3]; + if (tor_cert_get_checkable_sig(&check[0], cert, NULL) < 0) { + log_err(LD_BUG, "Couldn't create 'checkable' for cert."); + goto err; + } + if (tor_cert_get_checkable_sig(&check[1], + ntor_cc_cert, &ntor_cc_pk) < 0) { + log_err(LD_BUG, "Couldn't create 'checkable' for ntor_cc_cert."); + goto err; + } + + if (ed25519_signature_from_base64(&check[2].signature, + ed_sig_tok->args[0])<0) { + log_warn(LD_DIR, "Couldn't decode ed25519 signature"); + goto err; + } + check[2].pubkey = &cert->signed_key; + check[2].msg = d256; + check[2].len = DIGEST256_LEN; + + if (ed25519_checksig_batch(check_ok, check, 3) < 0) { + log_warn(LD_DIR, "Incorrect ed25519 signature(s)"); + goto err; + } + + if (check_tap_onion_key_crosscert( + (const uint8_t*)cc_tap_tok->object_body, + (int)cc_tap_tok->object_size, + router->onion_pkey, + &cert->signing_key, + (const uint8_t*)router->cache_info.identity_digest)<0) { + log_warn(LD_DIR, "Incorrect TAP cross-verification"); + goto err; + } + + /* We check this before adding it to the routerlist. */ + if (cert->valid_until < ntor_cc_cert->valid_until) + router->cert_expiration_time = cert->valid_until; + else + router->cert_expiration_time = ntor_cc_cert->valid_until; + } + } + if ((tok = find_opt_by_keyword(tokens, K_FINGERPRINT))) { /* If there's a fingerprint line, it must match the identity digest. */ char d[DIGEST_LEN]; @@ -1402,6 +1568,14 @@ router_parse_entry_from_string(const char *s, const char *end, } else { log_warn(LD_DIR, "Invalid extra info digest %s", escaped(tok->args[0])); } + + if (tok->n_args >= 2) { + if (digest256_from_base64(router->extra_info_digest256, tok->args[1]) + < 0) { + log_warn(LD_DIR, "Invalid extra info digest256 %s", + escaped(tok->args[1])); + } + } } if (find_opt_by_keyword(tokens, K_HIDDEN_SERVICE_DIR)) { @@ -1437,6 +1611,7 @@ router_parse_entry_from_string(const char *s, const char *end, routerinfo_free(router); router = NULL; done: + tor_cert_free(ntor_cc_cert); if (tokens) { SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t)); smartlist_free(tokens); @@ -1503,6 +1678,7 @@ extrainfo_parse_entry_from_string(const char *s, const char *end, goto err; } + /* XXXX Accept this in position 1 too, and ed identity in position 0. */ tok = smartlist_get(tokens,0); if (tok->tp != K_EXTRA_INFO) { log_warn(LD_DIR,"Entry does not start with \"extra-info\""); @@ -1515,6 +1691,7 @@ extrainfo_parse_entry_from_string(const char *s, const char *end, extrainfo->cache_info.signed_descriptor_body = tor_memdup_nulterm(s,end-s); extrainfo->cache_info.signed_descriptor_len = end-s; memcpy(extrainfo->cache_info.signed_descriptor_digest, digest, DIGEST_LEN); + crypto_digest256((char*)extrainfo->digest256, s, end-s, DIGEST_SHA256); tor_assert(tok->n_args >= 2); if (!is_legal_nickname(tok->args[0])) { @@ -1537,6 +1714,87 @@ extrainfo_parse_entry_from_string(const char *s, const char *end, goto err; } + { + directory_token_t *ed_sig_tok, *ed_cert_tok; + ed_sig_tok = find_opt_by_keyword(tokens, K_ROUTER_SIG_ED25519); + ed_cert_tok = find_opt_by_keyword(tokens, K_IDENTITY_ED25519); + int n_ed_toks = !!ed_sig_tok + !!ed_cert_tok; + if (n_ed_toks != 0 && n_ed_toks != 2) { + log_warn(LD_DIR, "Router descriptor with only partial ed25519/" + "cross-certification support"); + goto err; + } + if (ed_sig_tok) { + tor_assert(ed_cert_tok); + const int ed_cert_token_pos = smartlist_pos(tokens, ed_cert_tok); + if (ed_cert_token_pos != 1) { + /* Accept this in position 0 XXXX */ + log_warn(LD_DIR, "Ed25519 certificate in wrong position"); + goto err; + } + if (ed_sig_tok != smartlist_get(tokens, smartlist_len(tokens)-2)) { + log_warn(LD_DIR, "Ed25519 signature in wrong position"); + goto err; + } + if (strcmp(ed_cert_tok->object_type, "ED25519 CERT")) { + log_warn(LD_DIR, "Wrong object type on identity-ed25519 in decriptor"); + goto err; + } + + uint8_t d256[DIGEST256_LEN]; + const char *signed_start, *signed_end; + tor_cert_t *cert = tor_cert_parse( + (const uint8_t*)ed_cert_tok->object_body, + ed_cert_tok->object_size); + if (! cert) { + log_warn(LD_DIR, "Couldn't parse ed25519 cert"); + goto err; + } + extrainfo->signing_key_cert = cert; /* makes sure it gets freed. */ + if (cert->cert_type != CERT_TYPE_ID_SIGNING || + ! cert->signing_key_included) { + log_warn(LD_DIR, "Invalid form for ed25519 cert"); + goto err; + } + + if (router_get_hash_impl_helper(s, end-s, "extra-info ", + "\nrouter-sig-ed25519", + ' ', &signed_start, &signed_end) < 0) { + log_warn(LD_DIR, "Can't find ed25519-signed portion of extrainfo"); + goto err; + } + crypto_digest_t *d = crypto_digest256_new(DIGEST_SHA256); + crypto_digest_add_bytes(d, ED_DESC_SIGNATURE_PREFIX, + strlen(ED_DESC_SIGNATURE_PREFIX)); + crypto_digest_add_bytes(d, signed_start, signed_end-signed_start); + crypto_digest_get_digest(d, (char*)d256, sizeof(d256)); + crypto_digest_free(d); + + ed25519_checkable_t check[2]; + int check_ok[2]; + if (tor_cert_get_checkable_sig(&check[0], cert, NULL) < 0) { + log_err(LD_BUG, "Couldn't create 'checkable' for cert."); + goto err; + } + + if (ed25519_signature_from_base64(&check[1].signature, + ed_sig_tok->args[0])<0) { + log_warn(LD_DIR, "Couldn't decode ed25519 signature"); + goto err; + } + check[1].pubkey = &cert->signed_key; + check[1].msg = d256; + check[1].len = DIGEST256_LEN; + + if (ed25519_checksig_batch(check_ok, check, 2) < 0) { + log_warn(LD_DIR, "Incorrect ed25519 signature(s)"); + goto err; + } + /* We don't check the certificate expiration time: checking that it + * matches the cert in the router descriptor is adequate. */ + } + } + /* We've checked everything that's covered by the hash. */ can_dl_again = 1; @@ -2089,6 +2347,17 @@ routerstatus_parse_entry_from_string(memarea_t *area, line->microdesc_hash_line = tor_strdup(t->args[0]); vote_rs->microdesc = line; } + if (t->tp == K_ID) { + tor_assert(t->n_args >= 2); + if (!strcmp(t->args[0], "ed25519")) { + vote_rs->has_ed25519_listing = 1; + if (strcmp(t->args[1], "none") && + digest256_from_base64((char*)vote_rs->ed25519_id, t->args[1])<0) { + log_warn(LD_DIR, "Bogus ed25519 key in networkstatus vote"); + goto err; + } + } + } } SMARTLIST_FOREACH_END(t); } else if (flav == FLAV_MICRODESC) { tok = find_opt_by_keyword(tokens, K_M); @@ -2913,6 +3182,21 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out, goto err; } } + if (ns_type != NS_TYPE_CONSENSUS) { + digest256map_t *ed_id_map = digest256map_new(); + SMARTLIST_FOREACH_BEGIN(ns->routerstatus_list, vote_routerstatus_t *, vrs) { + if (! vrs->has_ed25519_listing || + tor_mem_is_zero((const char *)vrs->ed25519_id, DIGEST256_LEN)) + continue; + if (digest256map_get(ed_id_map, vrs->ed25519_id) != NULL) { + log_warn(LD_DIR, "Vote networkstatus ed25519 identities were not " + "unique"); + goto err; + } + digest256map_set(ed_id_map, vrs->ed25519_id, (void*)1); + } SMARTLIST_FOREACH_END(vrs); + digest256map_free(ed_id_map, NULL); + } /* Parse footer; check signature. */ footer_tokens = smartlist_new(); @@ -4210,6 +4494,26 @@ microdescs_parse_from_string(const char *s, const char *eos, tor_memdup(&k, sizeof(curve25519_public_key_t)); } + smartlist_t *id_lines = find_all_by_keyword(tokens, K_ID); + if (id_lines) { + SMARTLIST_FOREACH_BEGIN(id_lines, directory_token_t *, t) { + tor_assert(t->n_args >= 2); + if (!strcmp(t->args[0], "ed25519")) { + if (md->ed25519_identity_pkey) { + log_warn(LD_DIR, "Extra ed25519 key in microdesc"); + goto next; + } + ed25519_public_key_t k; + if (ed25519_public_from_base64(&k, t->args[1])<0) { + log_warn(LD_DIR, "Bogus ed25519 key in microdesc"); + goto next; + } + md->ed25519_identity_pkey = tor_memdup(&k, sizeof(k)); + } + } SMARTLIST_FOREACH_END(t); + smartlist_free(id_lines); + } + { smartlist_t *a_lines = find_all_by_keyword(tokens, K_A); if (a_lines) { diff --git a/src/or/routerparse.h b/src/or/routerparse.h index e294d95391..85e4b7d88e 100644 --- a/src/or/routerparse.h +++ b/src/or/routerparse.h @@ -19,7 +19,7 @@ int router_get_extrainfo_hash(const char *s, size_t s_len, char *digest); #define DIROBJ_MAX_SIG_LEN 256 char *router_get_dirobj_signature(const char *digest, size_t digest_len, - crypto_pk_t *private_key); + const crypto_pk_t *private_key); int router_append_dirobj_signature(char *buf, size_t buf_len, const char *digest, size_t digest_len, @@ -91,5 +91,7 @@ STATIC int routerstatus_parse_guardfraction(const char *guardfraction_str, routerstatus_t *rs); #endif +#define ED_DESC_SIGNATURE_PREFIX "Tor router descriptor signature v1" + #endif diff --git a/src/or/torcert.c b/src/or/torcert.c new file mode 100644 index 0000000000..e2ddffd7a9 --- /dev/null +++ b/src/or/torcert.c @@ -0,0 +1,280 @@ +/* Copyright (c) 2014, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "crypto.h" +#include "torcert.h" +#include "ed25519_cert.h" +#include "torlog.h" +#include "util.h" +#include "compat.h" +#include "link_handshake.h" + +/** Helper for tor_cert_create(): signs any 32 bytes, not just an ed25519 + * key. + */ +static tor_cert_t * +tor_cert_sign_impl(const ed25519_keypair_t *signing_key, + uint8_t cert_type, + uint8_t signed_key_type, + const uint8_t signed_key_info[32], + time_t now, time_t lifetime, + uint32_t flags) +{ + tor_cert_t *torcert = NULL; + + ed25519_cert_t *cert = ed25519_cert_new(); + cert->cert_type = cert_type; + cert->exp_field = (uint32_t) CEIL_DIV(now + lifetime, 3600); + cert->cert_key_type = signed_key_type; + memcpy(cert->certified_key, signed_key_info, 32); + + if (flags & CERT_FLAG_INCLUDE_SIGNING_KEY) { + ed25519_cert_extension_t *ext = ed25519_cert_extension_new(); + ext->ext_type = CERTEXT_SIGNED_WITH_KEY; + memcpy(ext->un_signing_key, signing_key->pubkey.pubkey, 32); + ed25519_cert_add_ext(cert, ext); + ++cert->n_extensions; + } + + const ssize_t alloc_len = ed25519_cert_encoded_len(cert); + tor_assert(alloc_len > 0); + uint8_t *encoded = tor_malloc(alloc_len); + const ssize_t real_len = ed25519_cert_encode(encoded, alloc_len, cert); + if (real_len < 0) + goto err; + tor_assert(real_len == alloc_len); + tor_assert(real_len > ED25519_SIG_LEN); + uint8_t *sig = encoded + (real_len - ED25519_SIG_LEN); + tor_assert(tor_mem_is_zero((char*)sig, ED25519_SIG_LEN)); + + ed25519_signature_t signature; + if (ed25519_sign(&signature, encoded, + real_len-ED25519_SIG_LEN, signing_key)<0) { + log_warn(LD_BUG, "Can't sign certificate"); + goto err; + } + memcpy(sig, signature.sig, ED25519_SIG_LEN); + + torcert = tor_cert_parse(encoded, real_len); + if (! torcert) { + log_warn(LD_BUG, "Generated a certificate we cannot parse"); + goto err; + } + + if (tor_cert_checksig(torcert, &signing_key->pubkey, now) < 0) { + log_warn(LD_BUG, "Generated a certificate whose signature we can't check"); + goto err; + } + + tor_free(encoded); + + return torcert; + + err: + tor_cert_free(torcert); + ed25519_cert_free(cert); + tor_free(encoded); + return NULL; +} + +/** + * Create and return a new new certificate of type cert_type to + * authenticate signed_key using the key signing_key. The + * certificate should remain valid for at least lifetime seconds after + * now. + * + * If CERT_FLAG_INCLUDE_SIGNING_KEY is set in flags, embed + * the public part of signing_key in the certificate. + */ +tor_cert_t * +tor_cert_create(const ed25519_keypair_t *signing_key, + uint8_t cert_type, + const ed25519_public_key_t *signed_key, + time_t now, time_t lifetime, + uint32_t flags) +{ + return tor_cert_sign_impl(signing_key, cert_type, + SIGNED_KEY_TYPE_ED25519, signed_key->pubkey, + now, lifetime, flags); +} + +/** Release all storage held for cert. */ +void +tor_cert_free(tor_cert_t *cert) +{ + if (! cert) + return; + + if (cert->encoded) + memwipe(cert->encoded, 0, cert->encoded_len); + tor_free(cert->encoded); + + memwipe(cert, 0, sizeof(tor_cert_t)); + tor_free(cert); +} + +/** Parse a certificate encoded with len bytes in encoded. */ +tor_cert_t * +tor_cert_parse(const uint8_t *encoded, const size_t len) +{ + tor_cert_t *cert = NULL; + ed25519_cert_t *parsed = NULL; + ssize_t got_len = ed25519_cert_parse(&parsed, encoded, len); + if (got_len < 0 || (size_t) got_len != len) + goto err; + + cert = tor_malloc_zero(sizeof(tor_cert_t)); + cert->encoded = tor_memdup(encoded, len); + cert->encoded_len = len; + + memcpy(cert->signed_key.pubkey, parsed->certified_key, 32); + cert->valid_until = parsed->exp_field * 3600; + cert->cert_type = parsed->cert_type; + + for (unsigned i = 0; i < ed25519_cert_getlen_ext(parsed); ++i) { + ed25519_cert_extension_t *ext = ed25519_cert_get_ext(parsed, i); + if (ext->ext_type == CERTEXT_SIGNED_WITH_KEY) { + if (cert->signing_key_included) + goto err; + + cert->signing_key_included = 1; + memcpy(cert->signing_key.pubkey, ext->un_signing_key, 32); + } else if (ext->ext_flags & CERTEXT_FLAG_AFFECTS_VALIDATION) { + /* Unrecognized extension with affects_validation set */ + goto err; + } + } + + return cert; + err: + ed25519_cert_free(parsed); + tor_cert_free(cert); + return NULL; +} + +/** Fill in checkable_out with the information needed to check + * the signature on cert with pubkey. */ +int +tor_cert_get_checkable_sig(ed25519_checkable_t *checkable_out, + const tor_cert_t *cert, + const ed25519_public_key_t *pubkey) +{ + if (! pubkey) { + if (cert->signing_key_included) + pubkey = &cert->signing_key; + else + return -1; + } + + checkable_out->msg = cert->encoded; + checkable_out->pubkey = pubkey; + tor_assert(cert->encoded_len > ED25519_SIG_LEN); + const size_t signed_len = cert->encoded_len - ED25519_SIG_LEN; + checkable_out->len = signed_len; + memcpy(checkable_out->signature.sig, + cert->encoded + signed_len, ED25519_SIG_LEN); + + return 0; +} + +/** Validates the signature on cert with pubkey relative to + * the current time now. Return 0 on success, -1 on failure. + * Sets flags in cert as appropriate. + */ +int +tor_cert_checksig(tor_cert_t *cert, + const ed25519_public_key_t *pubkey, time_t now) +{ + ed25519_checkable_t checkable; + int okay; + + if (now > cert->valid_until) { + cert->cert_expired = 1; + return -1; + } + + if (tor_cert_get_checkable_sig(&checkable, cert, pubkey) < 0) + return -1; + + if (ed25519_checksig_batch(&okay, &checkable, 1) < 0) { + cert->sig_bad = 1; + return -1; + } else { + cert->sig_ok = 1; + memcpy(cert->signing_key.pubkey, checkable.pubkey->pubkey, 32); + cert->cert_valid = 1; + return 0; + } +} + +/** Return a new copy of cert */ +tor_cert_t * +tor_cert_dup(const tor_cert_t *cert) +{ + tor_cert_t *newcert = tor_memdup(cert, sizeof(tor_cert_t)); + if (cert->encoded) + newcert->encoded = tor_memdup(cert->encoded, cert->encoded_len); + return newcert; +} + +/** Return true iff cert1 and cert2 are the same cert. */ +int +tor_cert_eq(const tor_cert_t *cert1, const tor_cert_t *cert2) +{ + tor_assert(cert1); + tor_assert(cert2); + return cert1->encoded_len == cert2->encoded_len && + tor_memeq(cert1->encoded, cert2->encoded, cert1->encoded_len); +} + +/** Return true iff cert1 and cert2 are the same cert, or if they are both + * NULL. */ +int +tor_cert_opt_eq(const tor_cert_t *cert1, const tor_cert_t *cert2) +{ + if (cert1 == NULL && cert2 == NULL) + return 1; + if (!cert1 || !cert2) + return 0; + return tor_cert_eq(cert1, cert2); +} + +/** Create new cross-certification object to certify ed_key as the + * master ed25519 identity key for the RSA identity key rsa_key. + * Allocates and stores the encoded certificate in *cert, and returns + * the number of bytes stored. Returns negative on error.*/ +ssize_t +tor_make_rsa_ed25519_crosscert(const ed25519_public_key_t *ed_key, + const crypto_pk_t *rsa_key, + time_t expires, + uint8_t **cert) +{ + uint8_t *res; + + rsa_ed_crosscert_t *cc = rsa_ed_crosscert_new(); + memcpy(cc->ed_key, ed_key->pubkey, ED25519_PUBKEY_LEN); + cc->expiration = (uint32_t) CEIL_DIV(expires, 3600); + cc->sig_len = crypto_pk_keysize(rsa_key); + rsa_ed_crosscert_setlen_sig(cc, crypto_pk_keysize(rsa_key)); + + ssize_t alloc_sz = rsa_ed_crosscert_encoded_len(cc); + tor_assert(alloc_sz > 0); + res = tor_malloc_zero(alloc_sz); + ssize_t sz = rsa_ed_crosscert_encode(res, alloc_sz, cc); + tor_assert(sz > 0 && sz <= alloc_sz); + + const int signed_part_len = 32 + 4; + int siglen = crypto_pk_private_sign(rsa_key, + (char*)rsa_ed_crosscert_getarray_sig(cc), + rsa_ed_crosscert_getlen_sig(cc), + (char*)res, signed_part_len); + tor_assert(siglen > 0 && siglen <= (int)crypto_pk_keysize(rsa_key)); + tor_assert(siglen <= UINT8_MAX); + cc->sig_len = siglen; + rsa_ed_crosscert_setlen_sig(cc, siglen); + + sz = rsa_ed_crosscert_encode(res, alloc_sz, cc); + rsa_ed_crosscert_free(cc); + *cert = res; + return sz; +} diff --git a/src/or/torcert.h b/src/or/torcert.h new file mode 100644 index 0000000000..b67dc525a2 --- /dev/null +++ b/src/or/torcert.h @@ -0,0 +1,76 @@ +/* Copyright (c) 2014, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#ifndef TORCERT_H_INCLUDED +#define TORCERT_H_INCLUDED + +#include "crypto_ed25519.h" + +#define SIGNED_KEY_TYPE_ED25519 0x01 + +#define CERT_TYPE_ID_SIGNING 0x04 +#define CERT_TYPE_SIGNING_LINK 0x05 +#define CERT_TYPE_SIGNING_AUTH 0x06 +#define CERT_TYPE_ONION_ID 0x0A + +#define CERT_FLAG_INCLUDE_SIGNING_KEY 0x1 + +/** An ed25519-signed certificate as used throughout the Tor protocol. + **/ +typedef struct tor_cert_st { + /** The key authenticated by this certificate */ + ed25519_public_key_t signed_key; + /** The key that signed this certificate. This value may be unset if the + * certificate has never been checked, and didn't include its own key. */ + ed25519_public_key_t signing_key; + /** A time after which this certificate will no longer be valid. */ + time_t valid_until; + + /** The encoded representation of this certificate */ + uint8_t *encoded; + /** The length of encoded */ + size_t encoded_len; + + /** One of CERT_TYPE_... */ + uint8_t cert_type; + /** True iff we received a signing key embedded in this certificate */ + unsigned signing_key_included : 1; + /** True iff we checked the signature and found it bad */ + unsigned sig_bad : 1; + /** True iff we checked the signature and found it correct */ + unsigned sig_ok : 1; + /** True iff we checked the signature and first found that the cert + * had expired */ + unsigned cert_expired : 1; + /** True iff we checked the signature and found the whole cert valid */ + unsigned cert_valid : 1; +} tor_cert_t; + +tor_cert_t *tor_cert_create(const ed25519_keypair_t *signing_key, + uint8_t cert_type, + const ed25519_public_key_t *signed_key, + time_t now, time_t lifetime, + uint32_t flags); + +tor_cert_t *tor_cert_parse(const uint8_t *cert, size_t certlen); + +void tor_cert_free(tor_cert_t *cert); + +int tor_cert_get_checkable_sig(ed25519_checkable_t *checkable_out, + const tor_cert_t *out, + const ed25519_public_key_t *pubkey); + +int tor_cert_checksig(tor_cert_t *cert, + const ed25519_public_key_t *pubkey, time_t now); + +tor_cert_t *tor_cert_dup(const tor_cert_t *cert); +int tor_cert_eq(const tor_cert_t *cert1, const tor_cert_t *cert2); +int tor_cert_opt_eq(const tor_cert_t *cert1, const tor_cert_t *cert2); + +ssize_t tor_make_rsa_ed25519_crosscert(const ed25519_public_key_t *ed_key, + const crypto_pk_t *rsa_key, + time_t expires, + uint8_t **cert); + +#endif + diff --git a/src/test/example_extrainfo.inc b/src/test/example_extrainfo.inc index 606279a765..e096afd6c4 100644 --- a/src/test/example_extrainfo.inc +++ b/src/test/example_extrainfo.inc @@ -190,3 +190,236 @@ static const char EX_EI_BAD_PUBLISHED_KEY[] = "BvG6303md3INygg+KP49RvWEJR/cU4RZ9QfHpORxH2OocMyRedw2rLex2E7jNNSi\n" "52yd1sHFYI8ZQ4aff+ZHUjJUGKRyqpbc8okVbq/Rl7vug0dd12eHAgMBAAE=\n" "-----END RSA PUBLIC KEY-----\n"; + +static const char EX_EI_GOOD_ED_EI[] = + "extra-info emma A692FE045C32B5E3A54B52882EF678A9DAC46A73\n" + "identity-ed25519\n" + "-----BEGIN ED25519 CERT-----\n" + "AQQABf55AYgHn/OKR8GHBlscN5VkO73wA9jSci8QgTM30615ZT44AQAgBAC08woT\n" + "MBZpKzRcaoEJhEG7+RmuYtnB2+nODk9IRIs8ZoyYPTZ6dLzI+MLMmtzUuo/Wmvw0\n" + "PflTyCb2RlWitOEhAErWH3Z9UmYGnzM/COId0Fe3ScSriyvRoFnJY1+GVAQ=\n" + "-----END ED25519 CERT-----\n" + "published 2014-10-05 20:07:00\n" + "router-sig-ed25519 a7K8nwfg+HrdlSGQwr9rnLBq0qozkyZZs6d6aiLEiXGdhV1r9KJncmlQ5SNoY/zMQlyQm8EV5rCyBiVliKQ1Bw\n" + "router-signature\n" + "-----BEGIN SIGNATURE-----\n" + "GvmCmIGgbC1DeawRyRuChy62VmBOG0EviryG/a2qSZiFy0iPPwqSp5ZyZDQEIEId\n" + "kkk1zPzK1+S3fmgOAXyXGH0r4YFkoLGnhMk07BoEwi6HEXzjJsabmcNkOHfaOWgs\n" + "/5nvnLfcmxL4c6FstZ7t9VQpE06y3GU0zwBeIy1qjp0=\n" + "-----END SIGNATURE-----\n" + "\n" + "\n" + ; +const char EX_EI_GOOD_ED_EI_FP[] = "A692FE045C32B5E3A54B52882EF678A9DAC46A73"; +static const char EX_EI_GOOD_ED_EI_KEY[] = + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBAM3jdYwjwGxDWYj/vyFkQT7RgeCNIn89Ei6D2+L/fdtFnqrMXOreFFHL\n" + "C7CK2v2uN3v+uXxfb5lADz3NcalxJrCfGTGtaBk7PwMZraTSh2luFKOvSRBQCmB1\n" + "yD5N0QqnIhBJoGr6NITpbWyiTKWvYLjl9PZd9af8e8jQCAa5P1j1AgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + ; + +static const char EX_EI_ED_MISSING_SIG[] = + "extra-info rachel 2A7521497B91A8437021515308A47491164EDBA1\n" + "identity-ed25519\n" + "-----BEGIN ED25519 CERT-----\n" + "AQQABf55AT2/T71LFYHiI1ppwNiuaewIu2Hq+GWWQ85O8gpWcUxeAQAgBAC2dgYu\n" + "moxhtuip7GVlthT9iomZKba1IllVa7uE1u2uO9BUYZQWXciFt7OnNzMH5mlffwxB\n" + "1dWCl+G5nbOsV5jYLbfhrF5afZotf+EQTfob4cCH79AV223LPcySbTHTtQ4=\n" + "-----END ED25519 CERT-----\n" + "published 2014-10-05 20:07:00\n" + "router-signature\n" + "-----BEGIN SIGNATURE-----\n" + "oypRD2IZQ5EttOE8dvofrW80nnBfijSkvYzBrM6H4KVeayRYvWfmi96dYO6ybMqm\n" + "Yp7Gs3ngqeeNdfHtkRPuQVUXUGYZgBTvYItuagnFlFgRqaHy0knwUIVOL35eqWYx\n" + "xSbQKA7fglxEDMFs/RK7FRP4dWc731ZMt5wzzfJHZ8E=\n" + "-----END SIGNATURE-----\n" + "\n" + "\n" + ; +const char EX_EI_ED_MISSING_SIG_FP[] = "2A7521497B91A8437021515308A47491164EDBA1"; +static const char EX_EI_ED_MISSING_SIG_KEY[] = + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBAOOB8ccxbtk2dB5FuKFhGndDcO6STNjB6KiG0b9X2QwKrOZMfmXSigto\n" + "mtC1JfPTxECayRjLSiP/9UD8iTVvlcnc8mMWBGM12Pa/KoCZRn7McHI3JJ7n9lfn\n" + "qw9+iZ9b/rBimzOb3W6k3uxzg9r8secdq4jJwTnwSjTObgxZtC8/AgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + ; + +static const char EX_EI_ED_MISSING_CERT[] = + "extra-info lynne E88E43E86015345A323D93D825C33E4AD1028F65\n" + "published 2014-10-05 20:07:00\n" + "router-sig-ed25519 H4gKIKm5K9Pfkriy7SlMUD6BdYVp6B5mXKzR/rTyYlpH0tEZ4Fx2hlHNfNNdWXJieXzKZQZo8e7SOVzvrAC3CQ\n" + "router-signature\n" + "-----BEGIN SIGNATURE-----\n" + "dIrbQjK5T9t5KM8CpsMF85hh2i060oPIxzYQMgE1q4j99dtb/n7SE8nhj1Sjij4D\n" + "7JvTjGdLHi3bFSxXaSmla0wxD9PUYFN7VsBQmwSaDrqrzJFb1SGwZuzW1IEZ7BBi\n" + "H0czsxEteg5hcNRwISj5WVthuWmau9v13MijtZGSK40=\n" + "-----END SIGNATURE-----\n" + "\n" + "\n" + "\n" + ; +const char EX_EI_ED_MISSING_CERT_FP[] = "E88E43E86015345A323D93D825C33E4AD1028F65"; +static const char EX_EI_ED_MISSING_CERT_KEY[] = + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBALjA/geb0TR9rp/UPvLhABQpB0XUDYuZAnLkrv+i7AAV7FemTDveEGnc\n" + "XdXNSusO1mHOquvr0YYKPhwauInxD56S8QOzLYiWWajGq8XHARQ33b4/9K2TUrAx\n" + "W9HTHV1U1zrPlCJtrkbjxsYoHpUg5ljzM7FGYGY5xuvyHu18SQvzAgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + ; +static const char EX_EI_ED_BAD_CERT1[] = + "extra-info marcie F78D8A655607D32281D02144817A4F1D26AE520F\n" + "identity-ed25519\n" + "-----BEGIN PLAGICAL SPELL-----\n" + "aaaa\n" + "-----END PLAGICAL SPELL\n" + "published 2014-10-05 20:07:00\n" + "router-sig-ed25519 KQJ+2AH7EkkjrD0RtDtUAIr+Vc7wndwILYnoUxFLSJiTP+5fMi54eFF/f1OgkG8gYyTh8phMij9WOxK/dsOpBg\n" + "router-signature\n" + "-----BEGIN SIGNATURE-----\n" + "XWD+P25AH6moi79j20Si3hqKGcJDws+FORL1MTu+GeJLV1mp5CR9N83UH4ffulcL\n" + "CpSSBDL/j74HqapzW7QvBx3FilaNT55GvcobZDFK4TKkCEyEmcuWKpEceBS7JTTV\n" + "SvwZeOObTjWPafELbsc/gI9Rh5Idwu7mZt3ZVntCGaQ=\n" + "-----END SIGNATURE-----\n" + "\n" + ; +const char EX_EI_ED_BAD_CERT1_FP[] = "F78D8A655607D32281D02144817A4F1D26AE520F"; +static const char EX_EI_ED_BAD_CERT1_KEY[] = + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBAMlR46JhxsCmWYtmIB/JjTV2TUYIhJLmHy+X7FfkK3ZVQvvl9/3GSXFL\n" + "3USfyf3j34XLh8An7pJBi9LAHkIXgnRbglCud7dXoexabmC+c2mSbw5RnuxDGEwz\n" + "krXUph/r2b+2UY1CgEt28nFigaHrIQbCmF4szFX/2GPYCLi5SrRNAgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + ; +static const char EX_EI_ED_BAD_CERT2[] = + "extra-info jaeger 7C2B42E783C4E0EB0CC3BDB37385D16737BACFBD\n" + "identity-ed25519\n" + "-----BEGIN ED25519 CERT-----\n" + "AQoABf55Acpw27GZBdwGCgawCj2F/DPadt8F/9DnEWywEew1Yi3qAOtLpCB8KXL7\n" + "4w5deFW2RBg8qTondNSUvAmwYLbLjNXMmgA3+nkoJOP3fcmQMHz1jm5xzgs2lCVP\n" + "t5txApaBIA4=\n" + "-----END ED25519 CERT-----\n" + "published 2014-10-05 20:07:00\n" + "router-sig-ed25519 DRQ4MLOGosBbW8M+17klNu8uWVkPxErmmEYoSo6OuH2Tzrcs6sUY+8Xi2qLoV1SbOugJ214Htl0I+6ceag+vBA\n" + "router-signature\n" + "-----BEGIN SIGNATURE-----\n" + "DfdA+DbuN9nVJNujuSY5wNCDLk7Hfzkrde/sK0hVmZRvivtpF/Fy/dVQHHGNFY5i\n" + "L1cESAgq9HLdbHU+hcc08XXxTIaGwvoklcJClcG3ENVBWkTXbJNT+ifr7chEagIi\n" + "cVrtU6RVmzldSbyir8V/Z4S/Cm67gYAgjM5gfoFUqDs=\n" + "-----END SIGNATURE-----\n" + ; +const char EX_EI_ED_BAD_CERT2_FP[] = "7C2B42E783C4E0EB0CC3BDB37385D16737BACFBD"; +static const char EX_EI_ED_BAD_CERT2_KEY[] = + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBALAM1F/0XJEsbxIQqb3+ObX/yGVnq9of8Q9sLsmxffD6hwVpCqnV3lTg\n" + "iC6+xZ/bSlTGLPi0k8QLCaTmYxgKwmlMPpbQZ4kpZUrsb9flKdChMN7w8hd48pY9\n" + "lu8QiAEgErsl5rCCJIHHjrxxM/Cnd0TnedRnj/Z2YqpNx/ggsmsRAgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + ; +static const char EX_EI_ED_BAD_SIG1[] = + "extra-info vary 5AC3A538FEEFC6F9FCC5FA0CE64704396C30D62A\n" + "identity-ed25519\n" + "-----BEGIN ED25519 CERT-----\n" + "AQQABf55AbPp++GrRb6WphSu+PkMaYsqY/beiLBmtiV3YP5i2JkKAQAgBABKXjg1\n" + "aiz2JfQpNOG308i2EojnUAZEk0C0x9g2BAAXGL63sv3eO/qrlytsG1x2hkcamxFn\n" + "LmfZBb/prqe1Vy4wABuhqWHAUtM29vXR6lpiCJeddt9Pa8XVy/tgWLX6TAw=\n" + "-----END ED25519 CERT-----\n" + "published 2014-10-05 20:07:00\n" + "router-sig-ed25519 a7K8nwfg+HrdlSGQwr9rnLBq0qozkyZZs6d6aiLEiXGdhV1r9KJncmlQ5SNoY/zMQlyQm8EV5rCyBiVliKQ1Bw\n" + "router-signature\n" + "-----BEGIN SIGNATURE-----\n" + "xhZX8Qmgft51NJ7eMd4vrESzf/VdxDrBz7hgn8K+5bLtZUksG0s6s7IyGRYWQtp4\n" + "/7oc9sYe3lcQiUN2K7DkeBDlL8Pcsl8aIlKuujWomCE3j0TIu+8XK6oJeo7eYic+\n" + "IA7EwVbdZsKsW5/eJVzbX2eO0a5zyJ5RIYotFNYNCSE=\n" + "-----END SIGNATURE-----\n" + "\n" + ; +const char EX_EI_ED_BAD_SIG1_FP[] = "5AC3A538FEEFC6F9FCC5FA0CE64704396C30D62A"; +static const char EX_EI_ED_BAD_SIG1_KEY[] = + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBAMvb6SuoIkPfBkJgQuo5aQDepAs1kEETZ9VXotMlhB0JJikrqBrAAz+7\n" + "rjIJ4JsBaeQuN0Z5ksXk2ebxtef7oMIUs37NfekLQHbNR0VsXkFXPEGmOAqpZjW0\n" + "P524eHqybWYZTckvZtUvKI3xYGD6kEEkz4qmV6dcExU1OiAYO9jrAgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + ; +static const char EX_EI_ED_BAD_SIG2[] = + "extra-info coward 7F1D4DD477E340C6D6B389FAC26EDC746113082F\n" + "identity-ed25519\n" + "-----BEGIN ED25519 CERT-----\n" + "AQQABf56AZkSDiFZ1QaiLJhcKdFDE5Kei/sPaPEIEoPMGP4BvOVXAQAgBAAlRLzx\n" + "U029tgIL9BRe47MVgcPJGy48db6ntzhjil7iOnWKT70z2LorUD5CZoLJs72TjB6r\n" + "8+HYNyFLEM6dvytWZf9NA5gLdhogbFcUk/R3gbNepmCF7XoZjbhPIp8zOwg=\n" + "-----END ED25519 CERT-----\n" + "published 2014-10-05 20:07:00\n" + "router-sig-ed25519 yfV+GySMIP1fw1oVa1C1de4XOWBqT4pUtEmSHq1h+WrLBNCh3/HZWvNC/denf2YVntuQrMLCJEv5ZaFKU+AIDQ\n" + "router-signature\n" + "-----BEGIN SIGNATURE-----\n" + "g+BWq69i9CP19va2cYMAXCQ6jK3IG0VmNYspjjUFgmFpJKGG6bHeOkuy1GXp47fG\n" + "LzZ3OPfJLptxU5AOQDUUYf25hu9uSl6gyknCzsszFs5n6ticuNejvcpzw6UfO1LP\n" + "5u+mGJlgpcMtmSraImDZrRipmZ3oRWvEULltlvzGQcQ=\n" + "-----END SIGNATURE-----\n" + "\n" + ; +const char EX_EI_ED_BAD_SIG2_FP[] = "7F1D4DD477E340C6D6B389FAC26EDC746113082F"; +static const char EX_EI_ED_BAD_SIG2_KEY[] = + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBALzOyfCEUZnvCyhlyMctPkdXg/XRE3Cr6QgyzdKf5kQbUiu2n0FgSHOX\n" + "iP5gfq8sO9eVeTPZtjE7/+KiR8aQJECy+eoye+lpsfm3tXpLxnpOIgL4DlURxlo/\n" + "rfCyv30SYBN9j62qgU9m6U2ydI0tH7/9Ep8yIY/QL8me8VAjLbf/AgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + ; + +static const char EX_EI_ED_MISPLACED_CERT[] = + "extra-info msselene 3B788BD0CE348BC5CED48313307C78175EB6D0F3\n" + "published 2014-10-05 20:07:00\n" + "identity-ed25519\n" + "-----BEGIN ED25519 CERT-----\n" + "AQQABf55AWBcqjzLESDuLNGsqQ/tHn32XueXwj2fDlgEy/kQNVf/AQAgBAAFOegg\n" + "XY1LR82xE9ohAYJxYpwJJw0YfXsBhGHqfakEoBtSgFJ3cQAUXZQX4lX6G8IxAlQB\n" + "7Rj7dPQuQRUmqD1yyKb/ScBgCa8esxlhNlATz47kRNR38A3TcoJ4c1Zv6AE=\n" + "-----END ED25519 CERT-----\n" + "router-sig-ed25519 Q52JKH9/iMsr1jIPlWHHxakSBvyqjT1gzL944vad4OhzCZuNuAYGWyWSGzTb1DVmBqqbAUq73TiZKAz77YLNCQ\n" + "router-signature\n" + "-----BEGIN SIGNATURE-----\n" + "YplvAIwExGf5/L8AoroVQXtGm+26EffrxKBArMKn0zS1NOOie1p0oF/+qJg+rNWU\n" + "6cv3Anf188EXGlkUOddavgVH8CQbvve2nHSfIAPxjgEX9QNXbM5CiaMwgpCewXnF\n" + "UoNBVo5tydeLHVns15MBg/JNIxUQMd6svMoPp2WqmaE=\n" + "-----END SIGNATURE-----\n" + "\n" + ; +const char EX_EI_ED_MISPLACED_CERT_FP[] = "3B788BD0CE348BC5CED48313307C78175EB6D0F3"; +static const char EX_EI_ED_MISPLACED_CERT_KEY[] = + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBALTwNqhTprg1oC6bEbDqwIYBoER6prqUXQFbwbFDn+ekXhZj8vltgGwp\n" + "aDGl9ceZWDKfi+reR6rZXjAJGctmv0VHkfe7maUX4FC/d2T8N8DvS+3IvJzFMpbT\n" + "O0fFrDTrCSnPikqFfQWnlP8yoF5vO7wo0jRRY432fLRXg9WqVzdrAgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + ; +static const char EX_EI_ED_MISPLACED_SIG[] = + "extra-info grazie 384E40A5DEED4AB1D8A74F1FCBDB18B7C24A8284\n" + "identity-ed25519\n" + "-----BEGIN ED25519 CERT-----\n" + "AQQABf55AcGuIBoa6TBqD8Gg5atcwp/+r9ThxIBkULmPv9OSGhv+AQAgBACXH13y\n" + "mUvdpcN6oRN1nX6mnH40LyfYR5um8xogJZk3oINse5cRNrfMgVWiBpDlJZAwlDDa\n" + "lx99hzuZBong+CiOcnEvLMsBaVJmNTm5mpdetYclZpl0g8QEXznXXeRBMgM=\n" + "-----END ED25519 CERT-----\n" + "router-sig-ed25519 TxuO86dQ3pUaIY2raQ3hoDBmh4TTPC0OVgY98T5cf6Y+sHyiELCkkKQ3lqqXCjqnbTLr1/4riH980JoWPpR+Dw\n" + "published 2014-10-05 20:07:00\n" + "router-signature\n" + "-----BEGIN SIGNATURE-----\n" + "kV2CtArl1VF1nUSyHL00mO3nEdNxlQU5N7/hZNTd+45lej5Veb+6vb4ujelsFERJ\n" + "YoxwIs6SuKAR4orQytCL0e+GgZsrg8zGTveEtMX/+u//OcCwQBYEevR5duBZjVw/\n" + "yzpEHwdIdB2PPyDBLkf1VKnP7uDj059tXiQRWl7LXgE=\n" + "-----END SIGNATURE-----\n" + "\n" + ; +const char EX_EI_ED_MISPLACED_SIG_FP[] = "384E40A5DEED4AB1D8A74F1FCBDB18B7C24A8284"; +static const char EX_EI_ED_MISPLACED_SIG_KEY[] = + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBAK0HgOCG/6433VCrwz/vhk3cKmyOfenCp0GZ4DIUwPWt4DeyP4nTbN6T\n" + "1HJ1H8+hXC9bMuI4m43IWrzgLycQ9UaskUn372ZjHP9InPqHMJU6GQ7vZUe9Tgza\n" + "qnBdRPoxnrZzUOzlvatGrePt0hDiOZaMtDAkeEojFp9Wp2ZN7+tZAgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + ; + diff --git a/src/test/failing_routerdescs.inc b/src/test/failing_routerdescs.inc index b49d59fd8a..e2b72c58a0 100644 --- a/src/test/failing_routerdescs.inc +++ b/src/test/failing_routerdescs.inc @@ -666,3 +666,904 @@ static const char EX_RI_ZERO_ORPORT[] = "wgFKhHI/49NHyWHX5IMQpeicg0T7Qa6qwnUvspH62p8=\n" "-----END SIGNATURE-----\n" ; + +static const char EX_RI_MINIMAL_ED[] = + "router fred 127.0.0.1 9001 0 9002\n" + "identity-ed25519\n" + "-----BEGIN ED25519 CERT-----\n" + "AQQABf5iAa+2yD5ryD5kXaWbpmzaTyuTjRfjMTFleDuFGkHe26wrAQAgBABFTAHm\n" + "hdZriC+6BRCCMYu48cYc9tUN1adfEROqSHZN3HHP4k/fYgncoxrS3OYDX1x8Ysm/\n" + "sqxAXBY4NhCMswWvuDYgtQpro9YaFohiorJkHjyLQXjUeZikCfDrlxyR8AM=\n" + "-----END ED25519 CERT-----\n" + "signing-key\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBAOsjlHgM/lPQgjJyfrq0y+cR+iipcAeS2HAU8CK9SATETOTZYrxoL5vH\n" + "1BNteT+JxAxpjva+j7r7XZV41xPDx7alVr8G3zQsjqkAt5NnleTfUREUbg0+OSMV\n" + "10gU+DgcZJTMehfGYJnuJsF4eQHio/ZTdJLaZML7qwq0iWg3sZfBAgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + "onion-key\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBAK9NjRY7GtAZnlxrAZlImChXmGzml0uk2KlCugvju+eIsjSA/zW3LuqW\n" + "wqp7Kh488Ak5nUFSlCaV9GjAexT134pynst8P0m/ofrejwlzl5DHd6sFbR33Fkzl\n" + "H48zic0QDY+8tKXI732dA4GveEwZDlxxy8sPcvUDaVyTsuZLHR4zAgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + "ntor-onion-key 71DgscFrk4i58O5GuTerI9g3JL0kz+6QaCstAllz9xw=\n" + "ntor-onion-key-crosscert 1\n" + "-----BEGIN ED25519 CERT-----\n" + "AQoABf5iAUVMAeaF1muIL7oFEIIxi7jxxhz21Q3Vp18RE6pIdk3cAH5ijeKqa+LM\n" + "T5Nb0I42Io4Z7BVjXG7sYVSxrospCOI4dqkl2ln3BKNuEFFT42xJwt+XGz3aMyK2\n" + "Cpp8w8I8nwU=\n" + "-----END ED25519 CERT-----\n" + "onion-key-crosscert\n" + "-----BEGIN CROSSCERT-----\n" + "lAZwD6YVic61NvJ0Iy62cSPuzJl5hJOFYNh9iSG/vn4/lVfnnCik+Gqi2v9pwItC\n" + "acwmutCSrMprmmFAW1dgzoU7GzUtdbxaGaOJdg8WwtO4JjFSzScTDB8R6sp0SCAI\n" + "PdbzAzJyiMqYcynyyCTiL77iwhUOBPzs2fXlivMtW2E=\n" + "-----END CROSSCERT-----\n" + "published 2014-10-05 12:00:00\n" + "bandwidth 1000 1000 1000\n" + "reject *:*\n" + "router-sig-ed25519 Oyo/eES+/wsgse1f+YSiJDGatBDaiB4fASf7vJ7GxFeD4OfLbB7OYa4hYNEo5NBssNt/PA55AQVSL8hvzBE3Cg\n" + "router-signature\n" + "-----BEGIN SIGNATURE-----\n" + "wdk26ZtS1H81IxcUThyirANLoszrnYYhOMP57YRAUDEzUr88X6yNDZ5S0tLl+FoT\n" + "9XlEVrpN7Z3k4N9WloWb0o/zVVidPMRVwt8YQakSgR8axzMQg6QhQ6zXTiYhiXa4\n" + "mawlwYFXsaVDSIIqYA2CudIyF3UBRZuTbw0CFZElMWc=\n" + "-----END SIGNATURE-----\n" + "\n" + ; + +static const char EX_RI_ED_MISSING_CROSSCERT[] = + "router fred 127.0.0.1 9001 0 9002\n" + "identity-ed25519\n" + "-----BEGIN ED25519 CERT-----\n" + "AQQABf54AfsyyHhGluzfESzL4LP8AhFEm83+GkFoHbe1KnssVngHAQAgBABNzJRw\n" + "BLXT3QMlic0QZ4eG612wkfSRS4yzONIbATKLHIgyzgGiGl4gaSX0JTeHeGfIlu7P\n" + "5SKocZVNxm1mp55PG+tgBqHObDRJRSgbOyUbUgfOtcbQGUeVgUlFKWZ9FAY=\n" + "-----END ED25519 CERT-----\n" + "signing-key\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBAMqT7K8cEzWIaPNXbNgvoZ5ejavoszI2OjW9XXetPD/S2f+N7TfQXHBW\n" + "bnjpgj87gmk59w0OXTMCv+XofZ0xOy2YR/jG5l1VJIvqgJhhFJ8oSEGVzy+97Ekn\n" + "Lb1FEYuVfVxSxnU2jhHW6KPtee/gvuyRI/TvZuwmYWxLRpikVn4pAgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + "onion-key\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBAM4nITNe8UykgsIuo5czSSSl3Okr1K+UVWTzDGLznDg77MkLy7mydmk9\n" + "vf51OB+ogQhozYKIh9uHvecOzY4EhSIuKhui4hNyQklD9juGoW7RVTSpGdYT1ymp\n" + "dDYS30JBPwCZ7KjdMtXiU8ch2WgbzYBuI+JfjwOhfcsuNC9QPfbfAgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + "ntor-onion-key lx8o212IYw5Ly2KbH2ua1+fr4YvDq5nKd7LHMdPzTGo=\n" + "ntor-onion-key-crosscert 1\n" + "-----BEGIN ED25519 CERT-----\n" + "AQoABf54AU3MlHAEtdPdAyWJzRBnh4brXbCR9JFLjLM40hsBMoscAJ8cHMIc71+p\n" + "Qa+lg5JiYb551mLgtPWLy12xdhog7SXiJl3NvnMgbMZXHDqkU2YZCidnVz+xqMdh\n" + "mjQFK4AtRwg=\n" + "-----END ED25519 CERT-----\n" + "published 2014-10-05 12:00:00\n" + "bandwidth 1000 1000 1000\n" + "reject *:*\n" + "router-sig-ed25519 4DSdPePrToNx3WQ+4GfFelB8IyHu5Z9vTbbLZ02vfYEsCF9QeaeHbYagY/yjdt+9e71jmfM+W5MfRQd8FJ1+Dg\n" + "router-signature\n" + "-----BEGIN SIGNATURE-----\n" + "cv1yL8HhQzQfjzkSosziu2kMecNUQGle4d103h6tVMoZS1ua1xiDpVKeuWPl9Z0+\n" + "wpFwRkOmK0HpNeOXCNHJwfJaWBGQXunB3WQ6Oi1BLilwLtWQixGTYG0hZ6xYLTnX\n" + "PdSQIbsohSgCzo9HLTAgTnkyBgklIO1PHJBJsaNOwfI=\n" + "-----END SIGNATURE-----\n" + "\n" + ; + +static const char EX_RI_ED_MISSING_CROSSCERT2[] = + "router fred 127.0.0.1 9001 0 9002\n" + "identity-ed25519\n" + "-----BEGIN ED25519 CERT-----\n" + "AQQABf54AXXgm0CUWQr+rxvgdIslqaFdBiwosT+9PaC8zOxYGIsZAQAgBAA6yeH7\n" + "3AfGIGuDpVihVUUo0QwguWDPwk2dBJan7B0qgPWF5Y4YL5XDh2nMatskUrtUGCr1\n" + "abLYlJPozmYd6QBSv6eyBfITS/oNOMyZpjDiIjcLQD08tVQ2Jho+WmN64wc=\n" + "-----END ED25519 CERT-----\n" + "signing-key\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBAMdyTK/VPZloLUaLsvj1+NOFs33/E9HmA0VgvZ1nNUrR+PxSR71QF7Tw\n" + "DKz+/p2rJE+MPfQ/Na3dH0vH4CDZ+FH2m4A8SB9emF8aKxdc/7KCjQNDQCNlEQYn\n" + "O9WvZJhbNPHUmX0z4OotI+Sk3qBzVHu0BGDsPYC9gwszIumDUILxAgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + "onion-key\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBAL8o6CJiLfW4vdRFvJ2nFt/H/ei0ov83rilOuwSmNORmL9lvnHY++HrD\n" + "dmEEvBv74xqWJxGbJ6OQ3VOwRpf2X/cb4gAvsQDqDmNwpJsrPYRQVXp/KY/8z7bJ\n" + "dM4CjcsuJHHmj3yc3iCzgqt/Xr6vR24X4bee12/bP7R8IETvWoiHAgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + "ntor-onion-key qpNEGrLMVn28Odonk/nDtZq1ljy0fBshwgoAm4X1yzQ=\n" + "onion-key-crosscert\n" + "-----BEGIN CROSSCERT-----\n" + "i4RKGIeaUrO6nzfdtb6j+ijYJh1Vgc9bsHMpW9cVCOjoJKFW9xljgl9xp6LytviN\n" + "ppKYCt9/JflbZUZjny34ESltPGrdquvHe8TtdQazjiZBWQok/kKnx2i+PioRF/xI\n" + "P8D0512kbJjXSuuq9tGl94RKPM/ySGjkTJPevN4TaJE=\n" + "-----END CROSSCERT-----\n" + "published 2014-10-05 12:00:00\n" + "bandwidth 1000 1000 1000\n" + "reject *:*\n" + "router-sig-ed25519 pMAOpepn5Q9MxcV9+Yiftu50oBzBsItQcBV9qdZCIt3lvSFqFY9+wJjaShvW3N9ICHkunrC0h/w5VEfx4SQdDA\n" + "router-signature\n" + "-----BEGIN SIGNATURE-----\n" + "Du5fJYDzvEeGqKTJwgaQsJJgz39K/J4qEM2TZ3Mh0XuDM1ZWDtjyzP03PaPQqbJ1\n" + "FsN5IStjOqN3O1IWuLzGaZGpGVuqcyYOxjs7REkGQn2LfqCjpzjaAdcsL0fI4ain\n" + "o/in8GQ6S/qhsx8enKlN0tffTmWmH9bmmVz0+yYmBSo=\n" + "-----END SIGNATURE-----\n" + "\n" + ; +static const char EX_RI_ED_MISSING_CROSSCERT_SIGN[] = + "router fred 127.0.0.1 9001 0 9002\n" + "identity-ed25519\n" + "-----BEGIN ED25519 CERT-----\n" + "AQQABf54AfoVFYuJnDNBWbjbTqfXACUtXWPipmqEYC++Ok/+4VoFAQAgBADH7JzI\n" + "fjSMV158AMiftgNY+KyHYIECuL9SnV3CSO+8+I7+r9n+A3DQQmGLULo/uZnkbteJ\n" + "+uy6uRG4kW0fnuBlKhseJQm9hjNGWzC8hmebp1M+bxwG41EGI7BZvnTrRgM=\n" + "-----END ED25519 CERT-----\n" + "signing-key\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBALEqlijoFIDX1y1i5zfei8DuDIsFtSw56PGgnMRGcybwD1PRQCheCUZM\n" + "erQgFCWjgLgvGJERBK/oILW1dFXp4MAR5RgnrPGTfWTinCj32obMLN1gIczpq6a9\n" + "P9uv6Cz0ApSxpA/AuvjyAZwQKbUXuMvIY4aTprAKSqqVohk6E+E1AgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + "onion-key\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBAMZbbBjGV7xPri4XNmejq4add93p+XsWlsfbM930bcC2JZiwg4g4cq6W\n" + "idl8VDmCXeaWg5y3kb82Ch/Q9vPG0QYQbXxUA3JxQKKbcEK3QsEvqQh8Nb7krILK\n" + "YnSGAnLG2Nc3PnKb7Wpb8M3rAysC5O99Gq1mSfm8ntj3zlIM7NSHAgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + "ntor-onion-key CYcpfIF4T9PJcfROfVJTUYl0zNd4Ia5u0L9eng/EBSo=\n" + "ntor-onion-key-crosscert\n" + "-----BEGIN ED25519 CERT-----\n" + "AQoABf54AcfsnMh+NIxXXnwAyJ+2A1j4rIdggQK4v1KdXcJI77z4AMRc2LxiKbyr\n" + "fqRVynHuB031C4TN/HAlNPBjVoRvQRgzpiyyoyCqMDxLZdM8KtzdLLeqZJOXtWod\n" + "UXbYG3L70go=\n" + "-----END ED25519 CERT-----\n" + "onion-key-crosscert\n" + "-----BEGIN CROSSCERT-----\n" + "BRwRAK2lWxWGS49k8gXFHLEQ/h4k8gOQxM0WgCaN4LjAOilLHFjsjXkmKgttVpHl\n" + "f0V9ebSf+HgkpQnDSD8ittnr/0QaohUbD4lzslW4e/tQYEiM46soSoFft85J6U3G\n" + "D3D63+GmaOfIaa4nv7CD0Rw/Jz0zTuyEuARsdJIr1IY=\n" + "-----END CROSSCERT-----\n" + "published 2014-10-05 12:00:00\n" + "bandwidth 1000 1000 1000\n" + "reject *:*\n" + "router-sig-ed25519 7XfV5r7FXbXPEvrxlecWmAJxat/6VT+/4tE5cHrQnvLM4zslysstWH6/AfIfcmUuDlQ0watmfg1MvVnjavcfDA\n" + "router-signature\n" + "-----BEGIN SIGNATURE-----\n" + "eigLL3S/oMGL2tJULt9bl3S0iY+YIxdKeGFCcKZci59zD786m+n+BpGM3yPpvrXr\n" + "bGvl4IBqCa1I+TqPP1rM9lIEcUWaBT7Zo5uMcL1o+zZl1ZWPWVVKP5hC5ehDueu8\n" + "/blzNhTEFAp23ftDK9PnFf+bXxqbgKkEoZsxnd3e9Ns=\n" + "-----END SIGNATURE-----\n" + "\n" + ; + +static const char EX_RI_ED_BAD_SIG1[] = + "router fred 127.0.0.1 9001 0 9002\n" + "identity-ed25519\n" + "-----BEGIN ED25519 CERT-----\n" + "AQQABf54AR8QC+SNBpPOTVY198IQBANNwZjy+SBqQNxfzjEmo204AQAgBABjz4FP\n" + "zW/G+fu7YirvANvvqJeb7S1YYJnf6IrPaPsPRzDqJcO3/sTzFC5OSb9iJmzQAWnn\n" + "ADPOl+nOJC58XJnJ7CUJdPtyoVdMvUiUT/Jtg4RuCN1iDaDYaTh2VavImAY=\n" + "-----END ED25519 CERT-----\n" + "signing-key\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBAKuLC0kzCBTV6+WPZcAOQPKjqbjvMIyaehIQS1o90dYM+Tosrhtk3bw8\n" + "QBLMaiWL3kfIWPZuWi2ai40dmqAXMrXH3yBgKRNZ6zZSbUUuJ1IknqmrQ2PKjC/p\n" + "sIW2awC6Tq+zrZ7vntDb02zY857vP59j8eolTDg1Vvn6l2ieL+WhAgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + "onion-key\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBAMnBQPOJBQLZ3NAa70n6lGZGvS3DYZFNOZ2QnHVeVvOSFIFsuvHtnUdX\n" + "svDafznYAuRFRVqJS2xtKKGu0cmy6ulEbBF+4uAEMwQY7dGRPMgVF1Z33U0CSd08\n" + "ChCJGPTE7tGGuoeSIGN3mfC4z2v9SP3McBdAiLHisPzaUjfRTcwRAgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + "ntor-onion-key W8fUvBpKBoePmqb70rdJUcRT0NhELDWH7/BSXJtkXS0=\n" + "ntor-onion-key-crosscert 1\n" + "-----BEGIN ED25519 CERT-----\n" + "AQoABf54AWPPgU/Nb8b5+7tiKu8A2++ol5vtLVhgmd/ois9o+w9HAAPwWqmL0HXa\n" + "bYKrKPWQYnpQHQ3Ty0MmCgj3ABF940JURnV161RlN8CRAOJaeQ0Z8wBRLFC1NqLT\n" + "+GVdtewGeQA=\n" + "-----END ED25519 CERT-----\n" + "onion-key-crosscert\n" + "-----BEGIN CROSSCERT-----\n" + "x0vT5Wv7Guc0/Vu2BqomWwenh8oda9+8K/7ILi5GQL/WC29Tj51i0EE7PVSnSMJ7\n" + "33I/V+N5neauqWnbg7TxYaLsPfr6SpPTpBL1Xt0OiwT1//PvPYZ1gCcF3ig3KcfI\n" + "mreQd5C5Vri6ukWkMtz/zNDaDpDanzaNXTdaUXmFHF4=\n" + "-----END CROSSCERT-----\n" + "published 2014-10-05 12:00:00\n" + "bandwidth 1000 1000 1000\n" + "reject *:*\n" + "router-sig-ed25519 4DSdPePrToNx3WQ+4GfFelB8IyHu5Z9vTbbLZ02vfYEsCF9QeaeHbYagY/yjdt+9e71jmfM+W5MfRQd8FJ1+Dg\n" + "router-signature\n" + "-----BEGIN SIGNATURE-----\n" + "Hci/Br1+NNymDZBmQy1QWMlCeLe8Z1vtZ2ZTj42jDhWg1OC/v72ptI072x4x5cmi\n" + "X3EONy8wQUvTNowkfG6/V/B768C7FYJYBId1GAFZZymXnON9zUYnE3z1J20eu6l6\n" + "QepmmdvRmteIHMQ7HLSrBuDuXZUDJD0yXm6g8bMT+Ek=\n" + "-----END SIGNATURE-----\n" + "\n" + "\n" + "\n" + ; +static const char EX_RI_ED_BAD_SIG2[] = + "router fred 127.0.0.1 9001 0 9002\n" + "identity-ed25519\n" + "-----BEGIN ED25519 CERT-----\n" + "AQQABf54AW8fyx54c7vQQA/AmShAitFP7XI1CLdifEVPSrFKwYq6AQAgBAChqjVA\n" + "/wKKJZ30BIQoXe5+QMiPR6meNxF1lBttQ2t5AhauZbH5XzRhZkdGo114wuyPNEM9\n" + "PrBwp5akTtari9doVy6gs3McqdoIbRdWevpaGj5g5oOEOtA9b5UNWQSwUAs=\n" + "-----END ED25519 CERT-----\n" + "signing-key\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBALp0Croi9zhpGxi9sUj54jr/flZdzxVVS+8VNldJG2c1soSx8kwlwotu\n" + "7mGGudJDAzDHGo5F5CCPEfQov2OmDehpefYUz/AaMLly6PrLRJlcUcpLogGf1+KU\n" + "1lLwE8kanXUkgvDhVQiFvNjy2Dxxuv3AHH4WdZZfbMbm8FJRGoHzAgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + "onion-key\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBAMoI9vQT4g2sV2dViGOWOzxckk367T9sMjVwcYfJCmnixGxjWeKScQFB\n" + "K9v1uK73cfZR8AxiUGK4/iOX/9en14mJOGF7fftAqypFLAt1TBvb07IgXljOBoHc\n" + "Paw4oZoJQzEoazt0Oa181LyNnNIoaZpHVZd1+a1Gs1gKoM4xDBv1AgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + "ntor-onion-key KjyvXYkMcpke5ZsUYf2gZAUNeEoz8NAwYoQvvbcDGiw=\n" + "ntor-onion-key-crosscert 0\n" + "-----BEGIN ED25519 CERT-----\n" + "AQoABf54AaGqNUD/AoolnfQEhChd7n5AyI9HqZ43EXWUG21Da3kCAI6MRHm7GpCF\n" + "/3zDGR/6jKe625uFZX9HpLt6FgAdGSJeMQ9W4Np9VkrFXAB3gvh7xxRzSgZ1rXgR\n" + "lUomgi7N1gc=\n" + "-----END ED25519 CERT-----\n" + "onion-key-crosscert\n" + "-----BEGIN CROSSCERT-----\n" + "xJXvCCpP4ExBuT3OTsdn2HJB0HidupmQq5zBh8fx/ox6+047ZBOM7+hVxxWapcMg\n" + "PMXbcLD4L/FCBpA/rjnFUE/9kztdq7FH/rOdi0nB6FZWhwDcsZuyfvbnDTxz5iHJ\n" + "87gd5nXA5PE649SRCxW5LX0OtSiPFPazu4KyyBgnTIM=\n" + "-----END CROSSCERT-----\n" + "published 2014-10-05 12:00:00\n" + "bandwidth 1000 1000 1000\n" + "reject *:*\n" + "router-sig-ed25519 4DSdPePrToNx3WQ+4GfFelB8IyHu5Z9vTbbLZ02vfYEsCF9QeaeHbYagY/yjdt+9e71jmfM+W5MfRQd8FJ1+Dgxx\n" + "router-signature\n" + "-----BEGIN SIGNATURE-----\n" + "tk4kBNYqB8utOmX30HrV8YfnwBXYODIiL3M/juRS6nPn0uvbW7pjoZ3ck/ahgW+6\n" + "FNQsgTJnEADCWS1r6v7PcvzQjtrOUUpNxGJxYw1r8yZkvmIxSQD6GMzuTxq7o1VA\n" + "/wZYDLonLhCWRdPjxnrl12+z92NdyISJCHMLRVqs2QY=\n" + "-----END SIGNATURE-----\n" + "\n" + "\n" + ; +static const char EX_RI_ED_BAD_SIG3[] = + "router fred 127.0.0.1 9001 0 9002\n" + "identity-ed25519\n" + "-----BEGIN ED25519 CERT-----\n" + "AQQABf54AYYiKZrFWZ/Cj5mZbfK11MZHYbwchllsUl4qPqY9gfi6AQAgBAB4irxT\n" + "86FYA0NbZssSTmfyG6Edcf0ge61OwB4QD35kHCrvuZk2HnmL+63Tj4QoFqIVnwVC\n" + "3wRGJGcmS7y+vS64GUXbuyTgqgpl/KuoHo5Aqe6IxJlVWYtU6W0M6FV9tAM=\n" + "-----END ED25519 CERT-----\n" + "signing-key\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBAMUEvXTVTl5xkQ2MTEsB4sXQ3MQkz8sQrU63rlqglpi1yUv24fotjzvE\n" + "oJpeKJBwwg5WBW/fW0bUDJF2cOHRHkj/R4Is3m+2PR1Kn3UbYfxNkFkTE11l099V\n" + "H6xlsi0TJOJKlgrcbSuB7se2QctZVhwsdsJvFRptC9Qd+klAPb7tAgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + "onion-key\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBAMooTeSUX7GPoyklSd1/6cF1u8e2LbjOLIpZrMon0Xt7c/aNwlrG9rVo\n" + "TSokHs3AQ2H2XIceySVRRWR4AdX9KApO4CX0gGTuVUmq6hFJWMnHdAs2mKL0kt1w\n" + "I+YWzjUqn4jIVa2nMbyHVQWzIysWwWiO4yduIjAYpBbWd9Biew4BAgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + "ntor-onion-key BN0I+pLmFkDQD5iRsdkcped4eZwGIuXnLiX2K0Zoi2I=\n" + "ntor-onion-key-crosscert 1\n" + "-----BEGIN ED25519 CERT-----\n" + "AQoABf54AXiKvFPzoVgDQ1tmyxJOZ/IboR1x/SB7rU7AHhAPfmQcAOrIvaG/xJqe\n" + "adM6mai+FlV8Dbt6QrXTcNHJU1m+CUDthA9TPTAYz9D8W0mTEQ6KEAKGfQrNLy2r\n" + "G1B+9wWSpA4=\n" + "-----END ED25519 CERT-----\n" + "onion-key-crosscert\n" + "-----BEGIN CROSSCERT-----\n" + "BpLBsl6Yo64QzczJn0TjdcXC1Jv9IhUG2m/Re3v0voCELOP+t5vkZXXLoVL23oKv\n" + "JheSkWiuAIEPsatb4afXZ8wZxPcQjwy3zTOBM7p9CG5fA+KYpqKTxAi+dhVYlcDo\n" + "M7S5nMV63FclkZIT70FFTHwWed1sAKwEO3/Ny24eppc=\n" + "-----END CROSSCERT-----\n" + "published 2014-10-05 12:00:00\n" + "bandwidth 1000 1000 1000\n" + "reject *:*\n" + "router-sig-ed25519 abcdvEzGFYMcJ/Ea7sbessW1qRJmnNNo2Khkkl0rEEgtLX0b4L4MMhK/ktS52Y6jX3PRQWK5PZc6gjV7Jaldh+g0Aw\n" + "router-signature\n" + "-----BEGIN SIGNATURE-----\n" + "Vyj7g3eQ3K4+tm49fJkAtsAYnYHcEiMnlucYCEPeKojzYStNfZwQO2SG5gsoBIif\n" + "urgQZ/heaF4uiGFg64UFw08doXqQkd5SHO3B4astslITvmq0jyaqzSXhdB5uUzvp\n" + "QCR0fqGLVS1acUiqGbRr4PiZ9G7OJkm230N3rGdet+0=\n" + "-----END SIGNATURE-----\n" + "\n" + ; +static const char EX_RI_ED_BAD_SIG4[] = + "router fred 127.0.0.1 9001 0 9002\n" + "identity-ed25519\n" + "-----BEGIN ED25519 CERT-----\n" + "AQQABf55AaEnncX/t0cbLm1xrtlUpkXghaA8fVuV7g1VF3YNfCaIAQAgBAC7Ki3S\n" + "zzH9Aezz5X4fbwHeF+BQEDfVasfyTxTI4fhRi7t3RxHzBJd60uEMXy2FchD8VO5d\n" + "j4Dl7R4btrohPVSVBQZuemBQSW6g3ufNl0txpFWu0R7vBPTFH6oyXYfY9gQ=\n" + "-----END ED25519 CERT-----\n" + "signing-key\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBALGKwzhOui2/jJPjU1ngW5IZRPcoDk7RAfGDO4xaef4VfAFHCV9CQO1c\n" + "/wQ09CcRdggTvUcv9hJTGJhSObUUooCkxw4/35f/A6/NoW1Gi0JqF9EsQWHpuAfr\n" + "n/ATlJQ9oGdTCNDq/BXSPWXhoI6UhUe0wiD4P4x4QwaYHcZh+lE5AgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + "onion-key\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBAOKrizVm2h5/jE/HqqLCBLWJZVVoGspasCtDDqHhSqsPzyjpqa52iMKi\n" + "q/deJ92le3J2NJRGKxPmPQqWxwhIjnMS5kUMoW182iLpO/G9qyPZ0dh6jXB0NBLF\n" + "ySfW6V2s3h4G4D2P+fqnsnzQnAX7YufkvgDau/qTWi2CqD0CjavDAgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + "ntor-onion-key A9h8jY9dPbhHTDbIc/NYWXmRP65wwSMrkY1MN8dV3BM=\n" + "ntor-onion-key-crosscert 1\n" + "-----BEGIN ED25519 CERT-----\n" + "AQoABf55AbsqLdLPMf0B7PPlfh9vAd4X4FAQN9Vqx/JPFMjh+FGLAN8xr/w3KFVi\n" + "yXoP/az6hIbJh0HYCwH8D1rPoQLcdpe8XVwFSrHGarZesdslIwc9dZa/D1dx3OGO\n" + "UhJOrdv51QY=\n" + "-----END ED25519 CERT-----\n" + "onion-key-crosscert\n" + "-----BEGIN CROSSCERT-----\n" + "bLmdO7ME5vq+c9y/Hd8EyBviMBTeo85sHZF/z6Pehc3Wg3i1BJ8DHSd1cK24Pg48\n" + "4WUrGTfonewuzJBDd3MLkKe6epXmvUgvuQN5wQszq1+u9ap/mRf6b3nEG0MHxMlO\n" + "FLx5MBsScuo+Q+pwXZa8vPuKTtEjqbVZivdKExJuIX0=\n" + "-----END CROSSCERT-----\n" + "published 2014-10-05 12:00:00\n" + "bandwidth 1000 1000 1000\n" + "reject *:*\n" + " router-sig-ed25519 4DSdPePrToNx3WQ+4GfFelB8IyHu5Z9vTbbLZ02vfYEsCF9QeaeHbYagY/yjdt+9e71jmfM+W5MfRQd8FJ1+Dgxx\n" + "router-signature\n" + "-----BEGIN SIGNATURE-----\n" + "LqNGEa10zwSPeomBXTfgvBnnWAdWyiR7KYZq9T++jK4ctR6hUaWngH8qSteUrkMx\n" + "gyWb6UMmlxdfOG0sdcU463HsqV7zObaKya8/WwQ9elj3FfsToswUCeOaLR/Rg7wC\n" + "zcUjI5VsneQoXT2WVZbZBLsLB3+7QfezVHRMB377GAY=\n" + "-----END SIGNATURE-----\n" + ; + +static const char EX_RI_ED_BAD_CROSSCERT1[] = + "router fred 127.0.0.1 9001 0 9002\n" + "identity-ed25519\n" + "-----BEGIN ED25519 CERT-----\n" + "AQQABf55AV1AfOvQWKlWsbzoBdJc5m72ShIJuA8eNV15basjhXYdAQAgBABy+KQK\n" + "3oLDGtqL5kwRmjAsls/+C6SAoAALll7U7wNSH7en5RVBal4RUzCf57ea/KG0c9V8\n" + "2DmZ3PdOt2aY/M2bWGmmH/tyyapOoV98dhDwFU7zcx/pMfRnJTDRSDwl8QE=\n" + "-----END ED25519 CERT-----\n" + "signing-key\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBAMP6xbqbj+x1mq5XImjeT0rUzqKZTgBd5zvK4Xcy9IifJuFC9+mMzrY4\n" + "WhYbdClxKUkDMkit9MVhek+P/w5TSHKl6AuqGaO09ID+hZpoUSdoBUYktynxfGsx\n" + "kIDu0XvgtAeSyJaVvoV1SKVChY0IBbzUqbHt4O2Q1BhzFCKEJTEzAgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + "onion-key\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBANwWlBh7e/eSLlhto5YUdj1iGYOq+yAmlosDItVfYrSPJuUfM2ocMBAn\n" + "udbRbWiADoqsbKn/gwwHCC/f1HX2FkRXxxnOlJKLo+NEi8tGmOlcQXSQol1pCpvK\n" + "sA9TxtYr+Ft4LRpxNrexF+pIBxqzwetqQrZbKYr0CFJi8q1qlMynAgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + "ntor-onion-key cs1AP+xF5cXTLuKeOeItdoDAzfALTJkwk9lB4mtC4QI=\n" + "ntor-onion-key-crosscert 3\n" + "-----BEGIN ED25519 CERT-----\n" + "AQoABf55AXL4pAregsMa2ovmTBGaMCyWz/4LpICgAAuWXtTvA1IfAKo6ANUq+hi+\n" + "xb3J4aYafnszlj87oi/DR+SDf29wzwNw8gmaqGzJ5GbfISfABuTUCzlilZyVnLxi\n" + "BHcCH6PWiAQ=\n" + "-----END ED25519 CERT-----\n" + "onion-key-crosscert\n" + "-----BEGIN CROSSCERT-----\n" + "qC9Kph/kGtONR2DxZDoIFFgnDFC+/7H07EgCiYQdIFIROc+gGK9qBOgeFEptrkXF\n" + "XdE35xxox5xSASQvp7hjFwxUtJRGOtf2O98regqeeaz6O9VPXHkLf51uqX3bVgq8\n" + "KvFAsFFS66GxhtbrVjpyRgIwHAYvse1WVESfLuZZTn0=\n" + "-----END CROSSCERT-----\n" + "published 2014-10-05 12:00:00\n" + "bandwidth 1000 1000 1000\n" + "reject *:*\n" + "router-sig-ed25519 3uW8Q1aetIQLOsqSco128ZUaHlhqdYiBvrxV7x75BGNS5RzIMTEwYDNtEX1LNPFJ5N0YOV0HEEOLhrJUV9QCBA\n" + "router-signature\n" + "-----BEGIN SIGNATURE-----\n" + "WuD7S/saTYBxKvItITbHRi8n+e6g/oVbosicfbRbafYPzPp4Prb+RK03UTafzXrV\n" + "QEQIzDNhfePcIMH8qX+qrogLMXFqiXx6TVQ0GqNvqirokk8ar3AgtRtewhChAuAj\n" + "8pmQTj2JpZn/iB3PCE2l/93O9LHZfp44hc8QOWKs6BE=\n" + "-----END SIGNATURE-----\n" + "\n" + "\n" + "\n" + ; +static const char EX_RI_ED_BAD_CROSSCERT4[] = + "router fred 127.0.0.1 9001 0 9002\n" + "identity-ed25519\n" + "-----BEGIN ED25519 CERT-----\n" + "AQQABf55AW5TTGF9jCMl7aALZzqypD9Bj8WYnAPIrKCoIJdgMbY0AQAgBAB7eCn8\n" + "rukx7t/egZUdqU7+FYqsnO4wdmOkLZkp0+gpF3jjk6N1Q0037NNVNZBjONB0Nm2F\n" + "CpB3nWSJliSSKr5tOYsuBPFy5VVGYeKPakpOoxanQ1UcqevMBAQy0zf9hwA=\n" + "-----END ED25519 CERT-----\n" + "signing-key\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBALeS5YbeDuKQ5iiuUvh3REoyJ47/YU9lslWmTrVBf9b66pMnYJv/awPu\n" + "m2HredUAJ3VzwQ38VJA39w3fQXUhQDnQ0OPpKzeAmIiuG+6WdW/mBSK7uKcezC23\n" + "LA1d6Afyl79LjZz/n+ENXqNMlJk4QPcPHuRnAvwBl3t8YVRPJmxhAgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + "onion-key\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBAPprokY7utWuO/0252dBB5MCxmVD/dROaIBDyFtpdH+YVv04rkOlDzYD\n" + "W4mgHVBMxEm/cspTgQmJ4exRHJPpcSe1RYHt1ONZdLYr6D7OOWf0y1IUrVSzF6K4\n" + "lqlmNuH1H4+TKGbkvixYc5GU/2ZmAy6gFEuphYnBbsN2Ywc38mnfAgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + "ntor-onion-key Cgo6xniGfEiuYoLSPUdE4Vb2D4zj2NQzC1lRjysRRXs=\n" + "ntor-onion-key-crosscert 1\n" + "-----BEGIN ED25519 CERT-----\n" + "AQoABf54AU3MlHAEtdPdAyWJzRBnh4brXbCR9JFLjLM40hsBMoscAJ8cHMIc71+p\n" + "Qa+lg5JiYb551mLgtPWLy12xdhog7SXiJl3NvnMgbMZXHDqkU2YZCidnVz+xqMdh\n" + "mjQFK4AtRwg=\n" + "-----END ED25519 CERT-----\n" + "onion-key-crosscert\n" + "-----BEGIN CROSSCERT-----\n" + "bi4M/AJLZF7/vSNmOj4uhrgKBQA/KfcZy5e58mhGL4owxd9vaWfl3aelvb9jf9zN\n" + "Q7FMv8f9aXzeVIoXIpRJxSKIJgBtG2wnMumIc80pqBvTyGInharszb6njfm0bg1u\n" + "PfJkbQYyf/dA5l5UwCrjFs06ImDmjFTAdsSWf6DfZ/k=\n" + "-----END CROSSCERT-----\n" + "published 2014-10-05 12:00:00\n" + "bandwidth 1000 1000 1000\n" + "reject *:*\n" + "router-sig-ed25519 4DSdPePrToNx3WQ+4GfFelB8IyHu5Z9vTbbLZ02vfYEsCF9QeaeHbYagY/yjdt+9e71jmfM+W5MfRQd8FJ1+Dgxx\n" + "router-signature\n" + "-----BEGIN SIGNATURE-----\n" + "io16v+e0pK3sbFzPGnkQrAjrRgIOJHrVZ1RXcxZ1+UNXagWM/MOLhQpkU/cw49Wd\n" + "4rQeZD3JQh16330eXbxc97AyDgp0b30He846SI0MfW/DnmGI8ZNeYfLbMv2bmbs9\n" + "QULzyIH8C+5mnMI1arcuiAua+Dpa34F79vgqPuvw5fU=\n" + "-----END SIGNATURE-----\n" + "\n" + "\n" + ; +static const char EX_RI_ED_BAD_CROSSCERT3[] = + "router fred 127.0.0.1 9001 0 9002\n" + "identity-ed25519\n" + "-----BEGIN ED25519 CERT-----\n" + "AQQABf55AVB+j+B2yPgGywvp7nvejyhMh9ejKmw7LCwufV83Zl9eAQAgBAConA3B\n" + "jJ3X2tES40jd94rRUFS2/s/Yv7E4LEQ9z0+jz8horNivzK3O/t7IGxJggi+b41/9\n" + "Uaqt+wqtVuKj0xJ9jwBlCXFt28G2P9s4ZyXYgGZqo7MlJlboybnOMvmoTQA=\n" + "-----END ED25519 CERT-----\n" + "signing-key\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBAPWuEWckT4aYAVNrZzLA8xVwfXp0wzfXeTWBztLS8VzssN6w/+cwXdeY\n" + "N1YNc2DiD3u8f+7kmuZIqL1EFQUwTvRwEzQXm2dqGM7qkm5ZGNMb5FKu+QwO2ImI\n" + "FLNiO5zO/LqP3cf/2L8/DuvruLenUrhRtecGFaHmhDYl+2brHIiPAgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + "onion-key\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBAMtHTfk0gDvp9+PtIG8Ks7rgCiJZ2aihSvr6WaKHYuIprgspFuga98cg\n" + "D//J80CrgH5Dw68YnkG+gU40IxP7YzhQ4glFlJGu3s2y7Qazcv5ww1XtHur+GDoA\n" + "cY0zCLhltNQFxIsoVUepY97XA6Y2ejYJjyqNXQcAmoPNoVhnTdkhAgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + "ntor-onion-key ibZf57LptdOK3WpVFXkYMatEEqPhuVWxsnkwF6638V4=\n" + "ntor-onion-key-crosscert 0\n" + "-----BEGIN ED25519 CERT-----\n" + "AQoABf55AaicDcGMndfa0RLjSN33itFQVLb+z9i/sTgsRD3PT6PPAEbkxCdI/bH/\n" + "B06DAjRuoDiv1HKsGuW+UN1iGEiWu2ieFzf3m0Z7BL9p2u2zIbHYkP50b3T3sebD\n" + "1AksemmMdA0=\n" + "-----END ED25519 CERT-----\n" + "onion-key-crosscert\n" + "-----BEGIN CROSSCERT-----\n" + "BpLBsl6Yo64QzczJn0TjdcXC1Jv9IhUG2m/Re3v0voCELOP+t5vkZXXLoVL23oKv\n" + "JheSkWiuAIEPsatb4afXZ8wZxPcQjwy3zTOBM7p9CG5fA+KYpqKTxAi+dhVYlcDo\n" + "M7S5nMV63FclkZIT70FFTHwWed1sAKwEO3/Ny24eppc=\n" + "-----END CROSSCERT-----\n" + "published 2014-10-05 12:00:00\n" + "bandwidth 1000 1000 1000\n" + "reject *:*\n" + "router-sig-ed25519 XS4zVi46Xl3xKhuozPCDlW0QRFD4qUhJmkefonQNsRlMVsrPkALnP2tfnfdfTc69hbNa22pOjJNf6Gm505EnAw\n" + "router-signature\n" + "-----BEGIN SIGNATURE-----\n" + "Q+R3OpO8VhfvFbXuE5qolhVbgosBHy2A5QS91TMzCbsxa8pBA6Li4QdPR37wvdLq\n" + "KayfmmNCMKU5qiZMyXqJZm4fdpxiSi50Z0tYlXM3b2OVfza3+pSOEBl89fN6G4Qc\n" + "pAmM14eEo1UzXrqZw76tMS2CwOYF5vR2xFGCYC0b5hM=\n" + "-----END SIGNATURE-----\n" + "\n" + "\n" + "\n" + ; +static const char EX_RI_ED_BAD_CROSSCERT5[] = + "router fred 127.0.0.1 9001 0 9002\n" + "identity-ed25519\n" + "-----BEGIN ED25519 CERT-----\n" + "AQQABf55AaCfOaispi7dJhK0c8HXJHIwoBkMgRpmmHu+3Zce/soMAQAgBAB5bAIo\n" + "5i4TSY/bV2KQAyziRwvgJm+nEiECClflPbP9Um+zOzOgxtDmNnR5UFQj+VWNG4uf\n" + "5lnaryN+PfUXZMTcs8AARof3fFz9tVPINHDrsGvKt8gpzgZEHkVioAXOFwg=\n" + "-----END ED25519 CERT-----\n" + "signing-key\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBAL3Fr/ovZ9SMGYrAM24taKBm/NpemZaXdD/JeBXFYm5Zs3szLwJC4Etm\n" + "zjNL6tVy+I21O1g3cs16TkflcidsjPXNx//PHAn7bqWMekjrt3SQdkHW2gDPgT2c\n" + "zYJ/hBR96JYG796jP3pkfJz6Iz5uT/ci3A/cdaVbzM1uZbMUgYGzAgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + "onion-key\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBAMHB+1dWa8BBrKE94vTqfbkSEuysG5LyyZF/WrqHq/3W+ocDLz795k8O\n" + "2Zvgr9im/Ib4hD7IyrtRexcuBdwujdG7cBALdCcWiUTGAMkl96HNETSX+lUVIpJ9\n" + "pMsc9O7+yz+/0Cl2RpILZCdE/7I96qHpZl3tzlRKSu15WeIm5U77AgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + "ntor-onion-key GXi0a2VLcRHQMMYys85zu3IPqOn5ZTsOixYyQvTGnQs=\n" + "ntor-onion-key-crosscert 1\n" + "-----BEGIN BUTTERED CRUMPET-----\n" + "AQoABf54AU3MlHAEtdPdAyWJzRBnh4brXbCR9JFLjLM40hsBMoscAJ8cHMIc71+p\n" + "Qa+lg5JiYb551mLgtPWLy12xdhog7SXiJl3NvnMgbMZXHDqkU2YZCidnVz+xqMdh\n" + "mjQFK4AtRwg=\n" + "-----END BUTTERED CRUMPET-----\n" + "onion-key-crosscert\n" + "-----BEGIN CROSSCERT-----\n" + "T9NHMBhuJo+TlfU3TztNgCc9fK1naNRwPOyoqr5R6lJvJ40jkHnIVOFuvuzvZ35O\n" + "QgPbyFcMjv6leV5xcW+/I9tWaBUFXiRGI27qjCFth4Gxq2B6B2dIcQliLXSvW9b+\n" + "CMTgDwVa4h2R2PMh18TRx1596ywE09YhCgBF3CwYsiM=\n" + "-----END CROSSCERT-----\n" + "published 2014-10-05 12:00:00\n" + "bandwidth 1000 1000 1000\n" + "reject *:*\n" + "router-sig-ed25519 sRpiP9kyW/DGOphp4V2VCtcKNA8i7zGuv2tnljNIPTB7r7KsTvdUk/Ha9ArRQEivO4nC2HHENtknDl3GtWIPCA\n" + "router-signature\n" + "-----BEGIN SIGNATURE-----\n" + "DtORw3+gO/yUUIp70xDaWSOgQZrJAAoZTNCB7q5WCoZOngeaCiC1Gtc+Fmdn7tER\n" + "uPqQC5H/Kh3Mi82PCj0JxvNivnNTNY1AZVaIX5YoioXVOkWF0B2pqMvFuDSdm2oJ\n" + "29PqSVcklquu19EjJRTopIHvYn3sFhQL4LarMsYY11c=\n" + "-----END SIGNATURE-----\n" + "\n" + "\n" + "\n" + ; +static const char EX_RI_ED_BAD_CROSSCERT6[] = + "router fred 127.0.0.1 9001 0 9002\n" + "identity-ed25519\n" + "-----BEGIN ED25519 CERT-----\n" + "AQQABf55ARMMCtQ8pObC5bq02AUE9Lx2bqsZBBkeOsDZVaEq6JavAQAgBABtV0xF\n" + "CsWXL/uFIBnoEsnXBeU1MvYRFrj1vR7QHdWXnxywXvBYUAC8lu/uyc8qqLp+aQSJ\n" + "5JzpDYlg3hp1fl5k97iv5F9WrR6s554YpmgYy9agFaxZ4LmRgz7n0UJ8mwM=\n" + "-----END ED25519 CERT-----\n" + "signing-key\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBAO5qd1TndKD2pEs1ZLWsHlvfO/E7cA0H7NKGLSioGpBf4P0rtkueX4ci\n" + "kJNa/4Fn/QsLECqEF2lUjkIc8YL+HMS6qteKvN8+nn16DfvnIhPDNZWTJjLl1bOI\n" + "sWSSiduhanoWQnhRtl3Rxg3opdNd9ApO0DLUNy4Qy18Ai6SgksfHAgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + "onion-key\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBAJkMYNpK7eJJyGwD/xG/iNg6gzzbIwrOSvmtoP7Rot42qtBiQ9A9kdsy\n" + "sazwkWkM93U1+1OaAADPYxeHoyHnuia95Cnc5y2lFSH3I7gnGGSPKSTwXtdyvDWZ\n" + "P1LbmQ4Bnh5leTCNZ/eFC4/GjNVzqHxjbb8a11dQhA8dOk8PrUq9AgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + "ntor-onion-key HdSQOqvLr4YnJE1XzzVIddgKgnjaHKJqnq0GqF4wXDg=\n" + "ntor-onion-key-crosscert 0\n" + "-----BEGIN ED25519 CERT-----\n" + "AQoABf55AW1XTEUKxZcv+4UgGegSydcF5TUy9hEWuPW9HtAd1ZefACVwif1deQry\n" + "K5GeemRa32sGzujVDDe75WRiPKFT3l/EtjTq3oeVq2xwbVJklnG3ASejKTr3YcHt\n" + "ov0jOl0jywc=\n" + "-----END ED25519 CERT-----\n" + "onion-key-crosscert\n" + "-----BEGIN NAUGHTY MARMOSET-----\n" + "BpLBsl6Yo64QzczJn0TjdcXC1Jv9IhUG2m/Re3v0voCELOP+t5vkZXXLoVL23oKv\n" + "JheSkWiuAIEPsatb4afXZ8wZxPcQjwy3zTOBM7p9CG5fA+KYpqKTxAi+dhVYlcDo\n" + "M7S5nMV63FclkZIT70FFTHwWed1sAKwEO3/Ny24eppc=\n" + "-----END NAUGHTY MARMOSET-----\n" + "published 2014-10-05 12:00:00\n" + "bandwidth 1000 1000 1000\n" + "reject *:*\n" + "router-sig-ed25519 lNY8TRX/FZdH5eFbsBkFHuRi8bPDsE5P+v7zExyD/IXnKS/ffYlP8qw1XIPdEDOIzGQ14+kyPX0SotaAqHRtBA\n" + "router-signature\n" + "-----BEGIN SIGNATURE-----\n" + "BHamS+epF77iozo5cBt+tbs22m9GhwY55DRXpEWAtvn67jsMnmn7qCOLONigK1RT\n" + "adZNezIydcCxXltgHTdKaZw4lcqv3s0KL8kI8frbBmm7PjXtWnrdXBYY+YK54MN/\n" + "t4N3162o9hzzKSwye0gPjgzpQ1xtEIkzWhBcmE9Vw5s=\n" + "-----END SIGNATURE-----\n" + "\n" + ; +static const char EX_RI_ED_BAD_CROSSCERT7[] = + "router fred 127.0.0.1 9001 0 9002\n" + "identity-ed25519\n" + "-----BEGIN ED25519 CERT-----\n" + "AQQABf55AfVmH2ReTyatl4VnS5YREtCM2dwikWuAPffq6M5bysZxAQAgBAAXoqE7\n" + "taqwLDXLZrZukpF1eBkCwYQK9uzctHTuMdqOHChguvkfX7V4H3O76Ayqvz+Z1ut1\n" + "KYRdgiArn3viRaBv3ZKT4Z75suMI3bjqGOSGLAKfOa0uLkOmKblHHhSUkwQ=\n" + "-----END ED25519 CERT-----\n" + "signing-key\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBAOLNugzUezzzw+N1SuQWzILJYkUJyQDoVXSZjT0dzBplHCjlrv0WZCUP\n" + "/pbonE7SlCChIovHcdiASaLj7MVaGgYDq3M1Vtgt5vhgGl10/+evBAD1QEt8AVfr\n" + "5+PH/sbZvOWucAhNUhOlqFKAn4vdRY39VEEXC5/Jz5fsk1E/DBu5AgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + "onion-key\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBAKxzg1hsYMS+0zAIrgYxSGO0GbKRrL/VhdlMEGu7ACaoqlGnmGQS3B4B\n" + "gLk8xDdx9N//8+YTx0hUIxP38w08lubPl1WXMq8s7wAiFd06Nklf65mHs0sXVtS1\n" + "EG3f97PQqmBpEJOwYBATNcA9e6F62P8SXNkpSjOzNaE0h9wHNKk7AgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + "ntor-onion-key msdr3O4W4bm/xdmZLzj35363ZSFex8yQxLWsV3wRCAQ=\n" + "ntor-onion-key-crosscert 1\n" + "-----BEGIN ED25519 CERT-----\n" + "VQoABx54AU3MlHAEtgPdAyWJzRBnh4brXbCR9JFLjLM40hsBMoscAJ8cHMIc71+p\n" + "Qa+lg5JiYb551mLgtPWLy12xdhog7SXiJl3NvnMgbMZXHDqkU2YZCidnVz+xqMdh\n" + "mjQFK4AtRwg=\n" + "-----END ED25519 CERT-----\n" + "onion-key-crosscert\n" + "-----BEGIN CROSSCERT-----\n" + "RJJRiU0vjVtRi3bVZru3aTvV5l56X/WOOp/ii316yPAS3aAMpOm1+piFVR5MNqcB\n" + "ZGyrA2Kx0hawdL2buU47iZ12GOCi4f1Es4V4N0TQgJICsKX38DsRdct9c1qMcqpp\n" + "1aENSRuaw0szTIr9OgR7/8stqR5c3iF1H5fOhmTi6xM=\n" + "-----END CROSSCERT-----\n" + "published 2014-10-05 12:00:00\n" + "bandwidth 1000 1000 1000\n" + "reject *:*\n" + "router-sig-ed25519 4DSdPePrToNx3WQ+4GfFelB8IyHu5Z9vTbbLZ02vfYEsCF9QeaeHbYagY/yjdt+9e71jmfM+W5MfRQd8FJ1+Dgxx\n" + "router-signature\n" + "-----BEGIN SIGNATURE-----\n" + "F3ZqvsyL6RRhPEnNFFIZY4WJM7LK082rseWzRkGNXjwoEwOWUK8enQ4Wjit+wozW\n" + "4HVIY1F+vP7gm6IiOEAFgEpB4C8FGuyoFw2q0ONA2tqTcvBJDDnqbx08FO7v2Dij\n" + "d3ucfc5gf7YNaoFCMMuyAzC56eyNk4U+6cSKy6wnJds=\n" + "-----END SIGNATURE-----\n" + ; + +static const char EX_RI_ED_MISPLACED1[] = + "router fred 127.0.0.1 9001 0 9002\n" + "signing-key\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBAKT6OIN6TsDB+xcp1uLeE0K3aiHGqa7hdxMBGpvcD0UFSyzpVv1A/fJa\n" + "tClDCwTpfTGbyK2L7AO75Ci0c7jf6Pq+V7L6R7o12g6WBTMrgsceC4YqXSKpXNhi\n" + "oudJyPfVzBfKcJUSynv89FUQOyul/WRRqWTfv0xUsJ3yjuOESfCNAgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + "identity-ed25519\n" + "-----BEGIN ED25519 CERT-----\n" + "AQQABf55AbBV9NVz0Hdl0Uiv87LiXaTAoeSXE+bheNG4Dju1GzQHAQAgBAD16h+T\n" + "ygzSgPN4Qat5ITthvm+lvMwMVGbVNWMxNy9i33NGhgp8kqMp2iPAY+LhX8It2b+X\n" + "8H9cBmYLO5G7AlMPj7GsuWdCdP/M/ldMvFfznlqeE3pCpRas6W48CFJ+9Ao=\n" + "-----END ED25519 CERT-----\n" + "onion-key\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBANMO/MepK3uCkKTLRCwIWc/8URVza2gEmDx6mDTJIB/Mw8U8VRDuu4iJ\n" + "v+LL3D8/HGLvT9a8OXbl5525Zszt8XueF3uePBF0Qp0fjGBL8GFqmrmFe6plurPJ\n" + "TfrS/m3q+KhXAUowmghciVGDY0kMiDG9X/t/zKLMKWVDYRZk+fupAgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + "ntor-onion-key I8yDO62Flx5O/QsFvgb2ArIRqwJLWetHMeZdxngRl2A=\n" + "ntor-onion-key-crosscert 1\n" + "-----BEGIN ED25519 CERT-----\n" + "AQoABf55AfXqH5PKDNKA83hBq3khO2G+b6W8zAxUZtU1YzE3L2LfAGC1uXxN2KwW\n" + "w4PqRidM1UPZ5jVOHceZYNQcTzzzArfBpr9OraOO2up4TGte8GVqjJNxrZc1gfjn\n" + "CwPW5WxpFg0=\n" + "-----END ED25519 CERT-----\n" + "onion-key-crosscert\n" + "-----BEGIN CROSSCERT-----\n" + "jLg3D3VO4i0sN8p2qtB6+5C3tai/K4M89mP7z2abQnUTbynOacPoNXIk4o64DjBJ\n" + "kaR42yfA7yQZ8Rj8abwgz0Zz6zbd+JjE+s/EklrEEtOl+jZAl3i+92FaHROJojXq\n" + "hw+ZEPOb9zgb1UQ7S1Fo+GoqA5bdGm/Wg1kSQielkNE=\n" + "-----END CROSSCERT-----\n" + "published 2014-10-05 12:00:00\n" + "bandwidth 1000 1000 1000\n" + "reject *:*\n" + "router-sig-ed25519 TRKvIl/wIIRD4Xcmd6HYmy7tD0KhVGgoStpWPtX0zmXGZ7+jugItrY0frDu9n82syiruuA45ZOs1Rfi4CbOSCg\n" + "router-signature\n" + "-----BEGIN SIGNATURE-----\n" + "NYpRfurB1YhFmDAdRc2Sd77S7By2V/0kgEHpJhtySb7efiQsyOA4ZBr1zEFPAXdp\n" + "TviKzyS9kN2fnz3hORoqFul33BDZbiLMNLtt5tzp62TYtmIg9IZdjjczbJUgbVLt\n" + "KCJL0vM7fdbXkZX61GIBbMYwzwIiHvVxG7F/AS5RbtE=\n" + "-----END SIGNATURE-----\n" + "\n" + ; +static const char EX_RI_ED_MISPLACED2[] = + "router fred 127.0.0.1 9001 0 9002\n" + "identity-ed25519\n" + "-----BEGIN ED25519 CERT-----\n" + "AQQABf55AfJo9FIePrxeDNnWT6SWkoz0/L27018XjUNWEHfaR06MAQAgBAAMgolK\n" + "nLg3ZnVv0skzHCfmX+ZR9Ttwj7FNXfhXCsyr860S79OW5LD0/m1GcS9JflWhP+FO\n" + "ng5cRb+aqNc8Ul+/4sQudZRx8w4U3d5rOuMGCqhQXnktH9AFzQHFq0jpAAU=\n" + "-----END ED25519 CERT-----\n" + "signing-key\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBAPeK/znKLRvSUmCIUiZOgfhiRFt7XGN//C2GFuey4xkKiIr9LWMuVe9m\n" + "Wx39Ea2UGEtNGCEVvZdJMDVRl7heFTfJTN4L1YeyWx6iNRWlpAmgQOKII7slHwlq\n" + "seEULOLOXc9AsU/v9ba9G54DFbHfe2k44ZOwEmaQZW5VF/I0YMMdAgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + "onion-key\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBAKFRzlrqPPxEW0nboAJ1qzKFb/vFtvRW0xNVb8RtbOY/NY5FV1hS8yfH\n" + "igtugkrOBmWah7cmJhiON2j+TKeBxEoXwJMZeyV+HLbr7nY/mFhad4BQ3Frkl8d6\n" + "1kQMhOJswMdwnnVHPNGUob4YAX0SpFA6MpBVj92zmMBeaihqUS9VAgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + "ntor-onion-key br8svioLcJCAQxoo3KvlT288p8rb4lQIZNLlplkIKkw=\n" + "ntor-onion-key-crosscert 0\n" + "-----BEGIN ED25519 CERT-----\n" + "AQoABf55AQyCiUqcuDdmdW/SyTMcJ+Zf5lH1O3CPsU1d+FcKzKvzAG9XqwmRm0uJ\n" + "E49NoHcWr9IzdIwSGo+PJSkVpk95a5p2s065BetCWxEEBJQniajQf2hZ36zmV9rq\n" + "a6puqkEAKAM=\n" + "-----END ED25519 CERT-----\n" + "onion-key-crosscert\n" + "-----BEGIN CROSSCERT-----\n" + "d6QGIVAJL5JjHUyV+aicLIdBYyxHwviKpPcp7uldRF8vfDGFpu0qFgJ5KT+3t36w\n" + "QY1r75bvUMG/ZzGKDg95dcK0X2AK6GFlcrYyCoQEVOsuPc1QEUeK9P2s7viNQE4V\n" + "tRwG/CvJhPfcnxErzVGfXIeYRL1r/hPNFDZSeSxPPM0=\n" + "-----END CROSSCERT-----\n" + "published 2014-10-05 12:00:00\n" + "bandwidth 1000 1000 1000\n" + "router-sig-ed25519 ts9pFk8PnDWtXgQad09XC/ZCbruSx1U1pNOMWF9fyoNG0CodxdDH9Vglg+BOS7Nd9fmsINfPWKCVdVuSSM7zCA\n" + "reject *:*\n" + "router-signature\n" + "-----BEGIN SIGNATURE-----\n" + "YMl6mpQm7UCsPQhZKMm0aZ7fzGevWzRbQO+de20HTn7fVqMWQf2hBDJe9QTN/uDK\n" + "/VKYT8SnIBexbrSMy1N5q8kNFKxxUtwA9GRtz620Vvc4m+lz/tnT9qucIKCDL5iJ\n" + "eRpnls0JoAMIHKl99zdUioYubmOZuqUaRAdT8ulWy+Y=\n" + "-----END SIGNATURE-----\n" + "\n" + ; +static const char EX_RI_ED_BAD_CERT1[] = + "router fred 127.0.0.1 9001 0 9002\n" + "identity-ed25519\n" + "-----BEGIN ED25519 CERT-----\n" + "AQoABf55AYf+rX8a5rzdTBGPvLdQIP8XcElDDQnJIruGqfDTj+tjAP+3XOL2UTmn\n" + "Hu39PbLZV+m9DIj/DvG38M0hP4MmHUjP/iZG5PaCX6/aMe+nQSNuTl0IDGpIo1l8\n" + "dZToQTFSzAQ=\n" + "-----END ED25519 CERT-----\n" + "signing-key\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBAM4o2DrTwn3wrvUMm41S/hFL5ZtRHGRDh26o8htn14AKMC65vpygKFY7\n" + "fUQVClAiJthAs5fD/8sE5XDtQrLnFv5OegQx8kSPuwyS/+5pI1bdxRJvKMOUl2Tc\n" + "fAUhzeNBmPvW3lMi9Fksw5sCSAKQ5VH/+DlYvBGZIO49pTnOAty1AgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + "onion-key\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBAMzIsJeEWWjN3Lp6qrzaJGn8uhJPJyjy2Wt3sp7z7iD/yBWW6Q7Jku3e\n" + "C5QfKmSmNi2pNjS0SqPjqZZNsbcxpq/bEOcZdysZG1lqi/QgxUevk57RWjh3EFsG\n" + "TwK3ougKWB5Q6/3m32dNsnnnDqzVapgZo7Zd3V/aCo0BVtL5VXZbAgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + "ntor-onion-key W28nwT/5FJ818M78y/5sNOkxhQ7ENBhjVhGG2j6KvFY=\n" + "ntor-onion-key-crosscert 0\n" + "-----BEGIN ED25519 CERT-----\n" + "AQoABf55AYf+rX8a5rzdTBGPvLdQIP8XcElDDQnJIruGqfDTj+tjAP+3XOL2UTmn\n" + "Hu39PbLZV+m9DIj/DvG38M0hP4MmHUjP/iZG5PaCX6/aMe+nQSNuTl0IDGpIo1l8\n" + "dZToQTFSzAQ=\n" + "-----END ED25519 CERT-----\n" + "onion-key-crosscert\n" + "-----BEGIN CROSSCERT-----\n" + "FWnEjvFob0ObgqohMT7miwGsAuioCT7Urz6tyWaGWph/TP9hbFWj4MPK5mt998mn\n" + "xA8zHSF5n/edu7wVX+rtnPrYPBmg+qN8+Pq6XMg64CwtWu+sqigsi6vtz/TfAIDL\n" + "mypENmSY32sWPvy/CA8dAZ2ASh57EH9a+WcFModpXkM=\n" + "-----END CROSSCERT-----\n" + "published 2014-10-05 12:00:00\n" + "bandwidth 1000 1000 1000\n" + "reject *:*\n" + "router-sig-ed25519 88YqJdGJS4O6XiUCNrc9xbOHxujvcN/TkCoRuQQeKfZGHM+4IhI6AcXFlPIfDYq0SAavMhVmzsDDw0ROl7vyCQ\n" + "router-signature\n" + "-----BEGIN SIGNATURE-----\n" + "cU4WDO3w9ZfVRbNUgxOQMbwS2xWXvaL+cZmIV6AAjAZVWkLEpif4g6uYu+jJUZOS\n" + "NUT7lNOMwTu4tE4b1YJpnD9T8iW0DlOXxlvRBMQYmKwhQuYk898BDGTSk+0AY0HJ\n" + "vv8wRVewDajNhW7tFY907IdHvPXG0u83GANxkYrRyUg=\n" + "-----END SIGNATURE-----\n" + "\n" + ; +static const char EX_RI_ED_BAD_CERT2[] = + "router fred 127.0.0.1 9001 0 9002\n" + "identity-ed25519\n" + "-----BEGIN WOBBLY RUTABAGA-----\n" + "helo\n" + "-----END WOBBLY RUTABAGA-----\n" + "signing-key\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBANZvqyqFeiekh8ApqIGK4ZtOqjaX87EzDestvAWwamVOXiPoUrzXgM3O\n" + "l8uuTnMA4TfnjLyyA2TnaMzJylOI1OMHuW/D9B/liWDstSxWNNIlKgLQ/Dh9xBS7\n" + "uQb2PYlI+iMkPKPyJQSTDdGHE7cdFPewUfhRtJU3F5ztm/3FLBFvAgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + "onion-key\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBANZl8U/Z8KCPS7EBDzt8i9kNETXS7vnp9gnw3BQNXfjiDtDg9eO7ChxY\n" + "NBwuOTXmRxfX3W9kvZ0op9Hno6hixIhHzDql+vZ+hN7yPanVVDglSUXcr31yBm5K\n" + "kA+ZnRvH3oVQ97E4rRzpi09dtI13Pzu7JS5jRMtH+JF1kQBoNC0dAgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + "ntor-onion-key lUrEL+TVXpjjHQ2BIKk34vblyDmoyMro1a6/9hJ4VRc=\n" + "ntor-onion-key-crosscert 0\n" + "-----BEGIN ED25519 CERT-----\n" + "AQoABf55Abm5E7FBdd3F8N1xuz/vdv03zh2lABrmGjzPQ3AFJtntALNeQTgjv0JL\n" + "jON4+SPNi0B2Bva3yKaSsdxiHQ1rIwQqIUVkzXmmX4jmsvJK/9gERAdD7GafTKZQ\n" + "BaZbNXBvmQw=\n" + "-----END ED25519 CERT-----\n" + "onion-key-crosscert\n" + "-----BEGIN CROSSCERT-----\n" + "OxkqFsw1vHUQ9iPYcKC/MHUBtbLPK6JY2i81ccAai2eW118UXcTbeCRccrXyqSkl\n" + "RLcooZyli1D6wg9x7O8+2+HXIbUa6WcTOD1Qi7Z9wKZfk4sDUy7QHKENMRfAXwX3\n" + "U/gqd4BflMPp4+XrYfPzz+6yQPWp0t9wXbFv5hZ9F3k=\n" + "-----END CROSSCERT-----\n" + "published 2014-10-05 12:00:00\n" + "bandwidth 1000 1000 1000\n" + "reject *:*\n" + "router-sig-ed25519 fW6Bt4R3xVk5KMDyOcYg8n5ANP0OrQq2PQFK2cW0lTAdi+eX+oT/BeWnkrn0uSWOC/t4omCmH4Rdl8M9xtpfBA\n" + "router-signature\n" + "-----BEGIN SIGNATURE-----\n" + "DHxiQXuLxZR0ylqwUGGePgN4KF4ItlOV/DuGmmszCO/Ut0p+5s4FP2v6Mm9M92Wj\n" + "75rS9xF/Ts0Kf49dvgc+c5VTvhX5I5SwGQkRk0RNJtNoP0t+qXBHaFV8BlAeaWF6\n" + "Lg3O+GUK325fQv9uDPCe37mFQV9jafAzsZUrO/ggb1U=\n" + "-----END SIGNATURE-----\n" + "\n" + ; +static const char EX_RI_ED_BAD_CERT3[] = + "router fred 127.0.0.1 9001 0 9002\n" + "identity-ed25519\n" + "-----BEGIN ED25519 CERT-----\n" + "BVVVnf55AW5TTGF9jCMl7aALZzqypD9Bj8WYnAPIrKCoIJdgMbY0AQAgBAB7eCn8\n" + "rukx7t/egZUdqU7+FYqsnO4wdmOkLZkp0+gpF3jjk6N1Q0037NNVNZBjONB0Nm2F\n" + "CpB3nWSJliSSKr5tOYsuBPFy5VVGYeKPakpOoxanQ1UcqevMBAQy0zf9hwA=\n" + "-----END ED25519 CERT-----\n" + "signing-key\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBAPgeQNbKwpnTU+qW/2djh66hptS9rcy1B4vdyWkDTdREao2ECuCv691Y\n" + "oIw3MpTWvpC1qHIKorunusR0FKgwXw3xQTikXbDq/1ptsekzoIA1R/hltQV3UuGH\n" + "zdzHuQXAMX7Fdll2gyya03c3Yq5s+xSDvGdkEeaIoctKjwxp4SdNAgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + "onion-key\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBAOzWuH4cPW9rIrfi8MrruMUg4IUVHz4BxfY4/szMIUvzeEAdHn4FYkWy\n" + "Vt7MDtUELZsmZeFNmkn72kLxnrdZ5XhxZBriq1Fzq11cSWRBF+SyE1MdcouY4GyG\n" + "drw6T8xb8ty19q0eO6C/gw27iqXPAp1clvkroLg6Nv9lGZvsedVDAgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + "ntor-onion-key /vYZ+9yLqG7yUnutoI57s96JBl36GTz0IDWE244rbzE=\n" + "ntor-onion-key-crosscert 0\n" + "-----BEGIN ED25519 CERT-----\n" + "AQoABf55AZ4zVBWP/fIYEgWmyj0WpO6CkXRJjtrWXtiT02k3IddiAMpYgMemGIpN\n" + "xj7TQRULsHHYvo4fLcKrSgndQbUUhfLTUuVhIzbnE2TBLMVOEkpxKU6mTuvTT/3h\n" + "MJugrwTWVg4=\n" + "-----END ED25519 CERT-----\n" + "onion-key-crosscert\n" + "-----BEGIN CROSSCERT-----\n" + "c/Vqu3wtsTsYMdnhTS9Tn1Pq6jDmH4uRD5WmbaCKKrkin2DjuYSMVpypndkdlZDE\n" + "He7uF7SUO3QG/UcRIXYOsg9MSLUmvn2kIwef8ykyqlRh95Csjo5DyattUhL2w4QF\n" + "tJkJBQAnXWaAVW1O8XimGCAvJ84cxbmZEcpN6WKjrXI=\n" + "-----END CROSSCERT-----\n" + "published 2014-10-05 12:00:00\n" + "bandwidth 1000 1000 1000\n" + "reject *:*\n" + "router-sig-ed25519 Ue7bkPpOoc8ca7cyQj/Vq3BP5X4vwLA5QmpLGw/WfRNVRPojJRxU3RVqWMi3JbsJFRTe6pH6ZHyXER33G5aAAA\n" + "router-signature\n" + "-----BEGIN SIGNATURE-----\n" + "ifKUtbxmqHVs8A0oT5n7Te0c6D/XqWQTc0RxX9OKGspzh6wNX26h0Xa2vpK1Q9Zu\n" + "sj61I7vbHuZN6rxiWs9IzJgb//XaNJasX1pd9tbGSXW+yYzc9G9kaa7vp3HcnhIP\n" + "XVWzzS8WmOiVNGcF65j6f7yGloTgN7cHMptgJG7pWes=\n" + "-----END SIGNATURE-----\n" + "\n" + ; +static const char EX_RI_BAD_EI_DIGEST2[] = + "router fred 127.0.0.1 9001 0 9002\n" + "identity-ed25519\n" + "-----BEGIN ED25519 CERT-----\n" + "AQQABf55ATrK8IVBWLO2yXKCqXLXJOTu89W2b+hREPO+tCrxjVqWAQAgBACG/vVx\n" + "NK8wKVZvf34d75ZObSR0ge1N2RrAIKNslNXBq/tcllIrNE4S0ZNcMpA+hxXoVFeo\n" + "jbxifYX7nTs5N3GrGPmkiuo82v2X6ZwoIXJGFnvWMxCjsYsUVDDxoT6h/w8=\n" + "-----END ED25519 CERT-----\n" + "extra-info-digest E5FAC29E766D63F96AD175069640E803F2723765 99oo\n" + "signing-key\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBAK9wHSdRalxkuAybrSCA3dlEC1ZGc7oHOzXRGLg+z6batuiCdQtus1Rk\n" + "LP821eZJtEMAE56aewCIHDcTiCxVa6DMqmxRjm5pfW4G5H5QCPYT6Fu0RoYck3Ef\n" + "vkgits5/fNYGPPVC7k8AdGax5dKj5oFVGq+JWolYFRv6tyR9AThvAgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + "onion-key\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBAKxjxTQ/T/MHpFbk7/zwA7l5b3IW3yVcyVe6eIGFoYun8FI0fbYRmR4M\n" + "G5Asu07gP9Bbgt3AFPuEqrjg4u+lIkgqTcCgKWJbAgm7fslwaDTXQ36A7I1M95PD\n" + "GJ10Dk5v4dVbrqwoF7MSrQPFtMO91RP11nGPSvDqXZJ4XpwqwdxpAgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + "ntor-onion-key LuVmHxpj4F5mPXGNi4MtxbIbLMav6frJRBsRgAvpdzo=\n" + "ntor-onion-key-crosscert 0\n" + "-----BEGIN ED25519 CERT-----\n" + "AQoABf55AYb+9XE0rzApVm9/fh3vlk5tJHSB7U3ZGsAgo2yU1cGrAKBcSzwi4lY/\n" + "salCELOLdeZzOjDNnBd6cKp2WJg7Yz5zFlbVbyNk0iwfGmucHk8vQZe5BS0Oq/Pz\n" + "B1u/BcJv8gk=\n" + "-----END ED25519 CERT-----\n" + "onion-key-crosscert\n" + "-----BEGIN CROSSCERT-----\n" + "QsAQVdDVHtasDbhrZG4ZxImdTTMY7fz3vouAiGyZx6/jCCB5v0gHwTn4xo6pgLEW\n" + "LQfMhQZIr76Ky67c0hAN2hihuDlfvhfVe9c2c5UOH1BOhq3llE3Hc3xGyEy3rw7r\n" + "5y38YGi759CvsP2/L8JfXMuBg89OcgJYFa27Q6e6MdQ=\n" + "-----END CROSSCERT-----\n" + "published 2014-10-05 12:00:00\n" + "bandwidth 1000 1000 1000\n" + "reject *:*\n" + "router-sig-ed25519 5zoQ0dufeeOJ/tE/BgcWgM8JpfW1ELSXLz4dI+K8YRH/gUtaPmYJgU2QfeUHD0oy1iwv4Qvl8Ferga7aBk1+DA\n" + "router-signature\n" + "-----BEGIN SIGNATURE-----\n" + "D6KRMwkb6JmVEnpZ825SD3LMB84UmVy0i94xk44OwhoWNKLXhaSTWJgf6AqnPG5o\n" + "QrCypSb44bYLn+VaDN5LVUl36jeZqCT4xd+4ZwIRdPOUj7vcVmyUDg3lXcAIk97Q\n" + "E5PrQY1mQuLSIjjKInAR2NRBumNJtRw31Y/DTB7tODU=\n" + "-----END SIGNATURE-----\n" + "\n" + ; diff --git a/src/test/include.am b/src/test/include.am index c672b3fedc..0ef556a9bf 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -35,6 +35,8 @@ src_test_AM_CPPFLAGS = -DSHARE_DATADIR="\"$(datadir)\"" \ -DLOCALSTATEDIR="\"$(localstatedir)\"" \ -DBINDIR="\"$(bindir)\"" \ -I"$(top_srcdir)/src/or" -I"$(top_srcdir)/src/ext" \ + -I"$(top_srcdir)/src/trunnel" \ + -I"$(top_srcdir)/src/ext/trunnel" \ -DTOR_UNIT_TESTS # -L flags need to go in LDFLAGS. -l flags need to go in LDADD. @@ -67,6 +69,8 @@ src_test_test_SOURCES = \ src/test/test_extorport.c \ src/test/test_hs.c \ src/test/test_introduce.c \ + src/test/test_keypin.c \ + src/test/test_link_handshake.c \ src/test/test_logging.c \ src/test/test_microdesc.c \ src/test/test_nodelist.c \ @@ -135,7 +139,7 @@ src_test_bench_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \ @TOR_LDFLAGS_libevent@ src_test_bench_LDADD = src/or/libtor.a src/common/libor.a \ src/common/libor-crypto.a $(LIBDONNA) \ - src/common/libor-event.a \ + src/common/libor-event.a src/trunnel/libor-trunnel.a \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \ @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ \ @TOR_SYSTEMD_LIBS@ diff --git a/src/test/test.c b/src/test/test.c index f30b8ae1af..7ad849f49e 100644 --- a/src/test/test.c +++ b/src/test/test.c @@ -1137,6 +1137,8 @@ extern struct testcase_t guardfraction_tests[]; extern struct testcase_t extorport_tests[]; extern struct testcase_t hs_tests[]; extern struct testcase_t introduce_tests[]; +extern struct testcase_t keypin_tests[]; +extern struct testcase_t link_handshake_tests[]; extern struct testcase_t logging_tests[]; extern struct testcase_t microdesc_tests[]; extern struct testcase_t nodelist_tests[]; @@ -1183,6 +1185,8 @@ struct testgroup_t testgroups[] = { { "extorport/", extorport_tests }, { "hs/", hs_tests }, { "introduce/", introduce_tests }, + { "keypin/", keypin_tests }, + { "link-handshake/", link_handshake_tests }, { "nodelist/", nodelist_tests }, { "oom/", oom_tests }, { "options/", options_tests }, diff --git a/src/test/test_containers.c b/src/test/test_containers.c index 79085a748e..3d150f5abf 100644 --- a/src/test/test_containers.c +++ b/src/test/test_containers.c @@ -495,6 +495,43 @@ test_container_smartlist_join(void *arg) tor_free(joined); } +static void +test_container_smartlist_pos(void *arg) +{ + (void) arg; + smartlist_t *sl = smartlist_new(); + + smartlist_add(sl, tor_strdup("This")); + smartlist_add(sl, tor_strdup("is")); + smartlist_add(sl, tor_strdup("a")); + smartlist_add(sl, tor_strdup("test")); + smartlist_add(sl, tor_strdup("for")); + smartlist_add(sl, tor_strdup("a")); + smartlist_add(sl, tor_strdup("function")); + + /* Test string_pos */ + tt_int_op(smartlist_string_pos(NULL, "Fred"), ==, -1); + tt_int_op(smartlist_string_pos(sl, "Fred"), ==, -1); + tt_int_op(smartlist_string_pos(sl, "This"), ==, 0); + tt_int_op(smartlist_string_pos(sl, "a"), ==, 2); + tt_int_op(smartlist_string_pos(sl, "function"), ==, 6); + + /* Test pos */ + tt_int_op(smartlist_pos(NULL, "Fred"), ==, -1); + tt_int_op(smartlist_pos(sl, "Fred"), ==, -1); + tt_int_op(smartlist_pos(sl, "This"), ==, -1); + tt_int_op(smartlist_pos(sl, "a"), ==, -1); + tt_int_op(smartlist_pos(sl, "function"), ==, -1); + tt_int_op(smartlist_pos(sl, smartlist_get(sl,0)), ==, 0); + tt_int_op(smartlist_pos(sl, smartlist_get(sl,2)), ==, 2); + tt_int_op(smartlist_pos(sl, smartlist_get(sl,5)), ==, 5); + tt_int_op(smartlist_pos(sl, smartlist_get(sl,6)), ==, 6); + + done: + SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp)); + smartlist_free(sl); +} + static void test_container_smartlist_ints_eq(void *arg) { @@ -1053,6 +1090,7 @@ struct testcase_t container_tests[] = { CONTAINER_LEGACY(smartlist_overlap), CONTAINER_LEGACY(smartlist_digests), CONTAINER_LEGACY(smartlist_join), + CONTAINER_LEGACY(smartlist_pos), CONTAINER(smartlist_ints_eq, 0), CONTAINER_LEGACY(bitarray), CONTAINER_LEGACY(digestset), diff --git a/src/test/test_crypto.c b/src/test/test_crypto.c index 7944864bd7..6cba850f30 100644 --- a/src/test/test_crypto.c +++ b/src/test/test_crypto.c @@ -698,9 +698,18 @@ test_crypto_formats(void *arg) for (idx = 0; idx < 10; ++idx) { i = base64_encode(data2, 1024, data1, idx, 0); tt_int_op(i, OP_GE, 0); + tt_int_op(i, OP_EQ, strlen(data2)); j = base64_decode(data3, 1024, data2, i); tt_int_op(j,OP_EQ, idx); tt_mem_op(data3,OP_EQ, data1, idx); + + i = base64_encode_nopad(data2, 1024, (uint8_t*)data1, idx); + tt_int_op(i, OP_GE, 0); + tt_int_op(i, OP_EQ, strlen(data2)); + tt_assert(! strchr(data2, '=')); + j = base64_decode_nopad((uint8_t*)data3, 1024, data2, i); + tt_int_op(j, OP_EQ, idx); + tt_mem_op(data3,OP_EQ, data1, idx); } strlcpy(data1, "Test string that contains 35 chars.", 1024); @@ -1264,6 +1273,8 @@ test_crypto_ed25519_simple(void *arg) tt_int_op(0, OP_EQ, ed25519_public_key_generate(&pub2, &sec1)); tt_mem_op(pub1.pubkey, OP_EQ, pub2.pubkey, sizeof(pub1.pubkey)); + tt_assert(ed25519_pubkey_eq(&pub1, &pub2)); + tt_assert(ed25519_pubkey_eq(&pub1, &pub1)); memcpy(&kp1.pubkey, &pub1, sizeof(pub1)); memcpy(&kp1.seckey, &sec1, sizeof(sec1)); @@ -1283,6 +1294,7 @@ test_crypto_ed25519_simple(void *arg) /* Wrong public key doesn't work. */ tt_int_op(0, OP_EQ, ed25519_public_key_generate(&pub2, &sec2)); tt_int_op(-1, OP_EQ, ed25519_checksig(&sig2, msg, msg_len, &pub2)); + tt_assert(! ed25519_pubkey_eq(&pub1, &pub2)); /* Wrong message doesn't work. */ tt_int_op(0, OP_EQ, ed25519_checksig(&sig2, msg, msg_len, &pub1)); @@ -1421,9 +1433,10 @@ test_crypto_ed25519_test_vectors(void *arg) static void test_crypto_ed25519_encode(void *arg) { - char buf[ED25519_BASE64_LEN+1]; + char buf[ED25519_SIG_BASE64_LEN+1]; ed25519_keypair_t kp; ed25519_public_key_t pk; + ed25519_signature_t sig1, sig2; char *mem_op_hex_tmp = NULL; (void) arg; @@ -1434,6 +1447,11 @@ test_crypto_ed25519_encode(void *arg) tt_int_op(0, OP_EQ, ed25519_public_from_base64(&pk, buf)); tt_mem_op(kp.pubkey.pubkey, OP_EQ, pk.pubkey, ED25519_PUBKEY_LEN); + tt_int_op(0, OP_EQ, ed25519_sign(&sig1, (const uint8_t*)"ABC", 3, &kp)); + tt_int_op(0, OP_EQ, ed25519_signature_to_base64(buf, &sig1)); + tt_int_op(0, OP_EQ, ed25519_signature_from_base64(&sig2, buf)); + tt_mem_op(sig1.sig, OP_EQ, sig2.sig, ED25519_SIG_LEN); + /* Test known value. */ tt_int_op(0, OP_EQ, ed25519_public_from_base64(&pk, "lVIuIctLjbGZGU5wKMNXxXlSE3cW4kaqkqm04u6pxvM")); diff --git a/src/test/test_dir.c b/src/test/test_dir.c index a949f5de73..3e9e955b2b 100644 --- a/src/test/test_dir.c +++ b/src/test/test_dir.c @@ -14,15 +14,18 @@ #define NETWORKSTATUS_PRIVATE #include "or.h" #include "config.h" +#include "crypto_ed25519.h" #include "directory.h" #include "dirserv.h" #include "dirvote.h" #include "hibernate.h" #include "networkstatus.h" #include "router.h" +#include "routerkeys.h" #include "routerlist.h" #include "routerparse.h" #include "test.h" +#include "torcert.h" static void test_dir_nicknames(void *arg) @@ -87,8 +90,11 @@ test_dir_formats(void *arg) routerinfo_t *rp1 = NULL, *rp2 = NULL; addr_policy_t *ex1, *ex2; routerlist_t *dir1 = NULL, *dir2 = NULL; + uint8_t *rsa_cc = NULL; + tor_cert_t *ntor_cc = NULL; or_options_t *options = get_options_mutable(); const addr_policy_t *p; + time_t now = time(NULL); (void)arg; pk1 = pk_generate(0); @@ -127,14 +133,33 @@ test_dir_formats(void *arg) ex2->prt_min = ex2->prt_max = 24; r2 = tor_malloc_zero(sizeof(routerinfo_t)); r2->addr = 0x0a030201u; /* 10.3.2.1 */ + ed25519_keypair_t kp1, kp2; + ed25519_secret_key_from_seed(&kp1.seckey, + (const uint8_t*)"YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY"); + ed25519_public_key_generate(&kp1.pubkey, &kp1.seckey); + ed25519_secret_key_from_seed(&kp2.seckey, + (const uint8_t*)"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"); + ed25519_public_key_generate(&kp2.pubkey, &kp2.seckey); + r2->signing_key_cert = tor_cert_create(&kp1, + CERT_TYPE_ID_SIGNING, + &kp2.pubkey, + now, 86400, + CERT_FLAG_INCLUDE_SIGNING_KEY); + char cert_buf[256]; + base64_encode(cert_buf, sizeof(cert_buf), + (const char*)r2->signing_key_cert->encoded, + r2->signing_key_cert->encoded_len, + BASE64_ENCODE_MULTILINE); r2->platform = tor_strdup(platform); r2->cache_info.published_on = 5; r2->or_port = 9005; r2->dir_port = 0; r2->onion_pkey = crypto_pk_dup_key(pk2); r2->onion_curve25519_pkey = tor_malloc_zero(sizeof(curve25519_public_key_t)); - curve25519_public_from_base64(r2->onion_curve25519_pkey, - "skyinAnvardNostarsNomoonNowindormistsorsnow"); + curve25519_keypair_t r2_onion_keypair; + curve25519_keypair_generate(&r2_onion_keypair, 0); + r2->onion_curve25519_pkey = tor_memdup(&r2_onion_keypair.pubkey, + sizeof(curve25519_public_key_t)); r2->identity_pkey = crypto_pk_dup_key(pk1); r2->bandwidthrate = r2->bandwidthburst = r2->bandwidthcapacity = 3000; r2->exit_policy = smartlist_new(); @@ -150,7 +175,7 @@ test_dir_formats(void *arg) /* XXXX025 router_dump_to_string should really take this from ri.*/ options->ContactInfo = tor_strdup("Magri White " ""); - buf = router_dump_router_to_string(r1, pk2); + buf = router_dump_router_to_string(r1, pk2, NULL, NULL, NULL); tor_free(options->ContactInfo); tt_assert(buf); @@ -183,7 +208,7 @@ test_dir_formats(void *arg) tt_str_op(buf,OP_EQ, buf2); tor_free(buf); - buf = router_dump_router_to_string(r1, pk2); + buf = router_dump_router_to_string(r1, pk2, NULL, NULL, NULL); tt_assert(buf); cp = buf; rp1 = router_parse_entry_from_string((const char*)cp,NULL,1,0,NULL,NULL); @@ -201,6 +226,10 @@ test_dir_formats(void *arg) strlcpy(buf2, "router Fred 10.3.2.1 9005 0 0\n" + "identity-ed25519\n" + "-----BEGIN ED25519 CERT-----\n", sizeof(buf2)); + strlcat(buf2, cert_buf, sizeof(buf2)); + strlcat(buf2, "-----END ED25519 CERT-----\n" "platform Tor "VERSION" on ", sizeof(buf2)); strlcat(buf2, get_uname(), sizeof(buf2)); strlcat(buf2, "\n" @@ -215,19 +244,52 @@ test_dir_formats(void *arg) strlcat(buf2, pk2_str, sizeof(buf2)); strlcat(buf2, "signing-key\n", sizeof(buf2)); strlcat(buf2, pk1_str, sizeof(buf2)); - strlcat(buf2, "hidden-service-dir\n", sizeof(buf2)); - strlcat(buf2, "ntor-onion-key " - "skyinAnvardNostarsNomoonNowindormistsorsnow=\n", sizeof(buf2)); - strlcat(buf2, "accept *:80\nreject 18.0.0.0/8:24\n", sizeof(buf2)); - strlcat(buf2, "router-signature\n", sizeof(buf2)); + int rsa_cc_len; + rsa_cc = make_tap_onion_key_crosscert(pk2, + &kp1.pubkey, + pk1, + &rsa_cc_len); + tt_assert(rsa_cc); + base64_encode(cert_buf, sizeof(cert_buf), (char*)rsa_cc, rsa_cc_len, + BASE64_ENCODE_MULTILINE); + strlcat(buf2, "onion-key-crosscert\n" + "-----BEGIN CROSSCERT-----\n", sizeof(buf2)); + strlcat(buf2, cert_buf, sizeof(buf2)); + strlcat(buf2, "-----END CROSSCERT-----\n", sizeof(buf2)); + int ntor_cc_sign; + ntor_cc = make_ntor_onion_key_crosscert(&r2_onion_keypair, + &kp1.pubkey, + r2->cache_info.published_on, + MIN_ONION_KEY_LIFETIME, + &ntor_cc_sign); + tt_assert(ntor_cc); + base64_encode(cert_buf, sizeof(cert_buf), + (char*)ntor_cc->encoded, ntor_cc->encoded_len, + BASE64_ENCODE_MULTILINE); + tor_snprintf(buf2+strlen(buf2), sizeof(buf2)-strlen(buf2), + "ntor-onion-key-crosscert %d\n" + "-----BEGIN ED25519 CERT-----\n" + "%s" + "-----END ED25519 CERT-----\n", ntor_cc_sign, cert_buf); - buf = router_dump_router_to_string(r2, pk1); + strlcat(buf2, "hidden-service-dir\n", sizeof(buf2)); + strlcat(buf2, "ntor-onion-key ", sizeof(buf2)); + base64_encode(cert_buf, sizeof(cert_buf), + (const char*)r2_onion_keypair.pubkey.public_key, 32, + BASE64_ENCODE_MULTILINE); + strlcat(buf2, cert_buf, sizeof(buf2)); + strlcat(buf2, "accept *:80\nreject 18.0.0.0/8:24\n", sizeof(buf2)); + strlcat(buf2, "router-sig-ed25519 ", sizeof(buf2)); + + buf = router_dump_router_to_string(r2, pk1, pk2, &r2_onion_keypair, &kp2); + tt_assert(buf); buf[strlen(buf2)] = '\0'; /* Don't compare the sig; it's never the same * twice */ - tt_str_op(buf,OP_EQ, buf2); + + tt_str_op(buf, OP_EQ, buf2); tor_free(buf); - buf = router_dump_router_to_string(r2, pk1); + buf = router_dump_router_to_string(r2, pk1, NULL, NULL, NULL); cp = buf; rp2 = router_parse_entry_from_string((const char*)cp,NULL,1,0,NULL,NULL); tt_assert(rp2); @@ -280,6 +342,7 @@ test_dir_formats(void *arg) if (rp2) routerinfo_free(rp2); + tor_free(rsa_cc); tor_free(buf); tor_free(pk1_str); tor_free(pk2_str); @@ -293,7 +356,7 @@ test_dir_formats(void *arg) #include "failing_routerdescs.inc" static void -test_dir_routerparse_bad(void *arg) +test_dir_routerinfo_parsing(void *arg) { (void) arg; @@ -318,6 +381,8 @@ test_dir_routerparse_bad(void *arg) CHECK_OK(EX_RI_MINIMAL); CHECK_OK(EX_RI_MAXIMAL); + CHECK_OK(EX_RI_MINIMAL_ED); + /* good annotations prepended */ routerinfo_free(ri); ri = router_parse_entry_from_string(EX_RI_MINIMAL, NULL, 0, 0, @@ -376,8 +441,28 @@ test_dir_routerparse_bad(void *arg) CHECK_FAIL(EX_RI_BAD_FAMILY, 0); CHECK_FAIL(EX_RI_ZERO_ORPORT, 0); + CHECK_FAIL(EX_RI_ED_MISSING_CROSSCERT, 0); + CHECK_FAIL(EX_RI_ED_MISSING_CROSSCERT2, 0); + CHECK_FAIL(EX_RI_ED_MISSING_CROSSCERT_SIGN, 0); + CHECK_FAIL(EX_RI_ED_BAD_SIG1, 0); + CHECK_FAIL(EX_RI_ED_BAD_SIG2, 0); + CHECK_FAIL(EX_RI_ED_BAD_SIG3, 0); + CHECK_FAIL(EX_RI_ED_BAD_SIG4, 0); + CHECK_FAIL(EX_RI_ED_BAD_CROSSCERT1, 0); + CHECK_FAIL(EX_RI_ED_BAD_CROSSCERT3, 0); + CHECK_FAIL(EX_RI_ED_BAD_CROSSCERT4, 0); + CHECK_FAIL(EX_RI_ED_BAD_CROSSCERT5, 0); + CHECK_FAIL(EX_RI_ED_BAD_CROSSCERT6, 0); + CHECK_FAIL(EX_RI_ED_BAD_CROSSCERT7, 0); + CHECK_FAIL(EX_RI_ED_MISPLACED1, 0); + CHECK_FAIL(EX_RI_ED_MISPLACED2, 0); + CHECK_FAIL(EX_RI_ED_BAD_CERT1, 0); + CHECK_FAIL(EX_RI_ED_BAD_CERT2, 0); + CHECK_FAIL(EX_RI_ED_BAD_CERT3, 0); + /* This is allowed; we just ignore it. */ CHECK_OK(EX_RI_BAD_EI_DIGEST); + CHECK_OK(EX_RI_BAD_EI_DIGEST2); #undef CHECK_FAIL #undef CHECK_OK @@ -433,20 +518,34 @@ test_dir_extrainfo_parsing(void *arg) tt_assert(ei->pending_sig); CHECK_OK(EX_EI_MAXIMAL); tt_assert(ei->pending_sig); + CHECK_OK(EX_EI_GOOD_ED_EI); + tt_assert(ei->pending_sig); map = (struct digest_ri_map_t *)digestmap_new(); ADD(EX_EI_MINIMAL); ADD(EX_EI_MAXIMAL); + ADD(EX_EI_GOOD_ED_EI); ADD(EX_EI_BAD_FP); ADD(EX_EI_BAD_NICKNAME); ADD(EX_EI_BAD_TOKENS); ADD(EX_EI_BAD_START); ADD(EX_EI_BAD_PUBLISHED); + ADD(EX_EI_ED_MISSING_SIG); + ADD(EX_EI_ED_MISSING_CERT); + ADD(EX_EI_ED_BAD_CERT1); + ADD(EX_EI_ED_BAD_CERT2); + ADD(EX_EI_ED_BAD_SIG1); + ADD(EX_EI_ED_BAD_SIG2); + ADD(EX_EI_ED_MISPLACED_CERT); + ADD(EX_EI_ED_MISPLACED_SIG); + CHECK_OK(EX_EI_MINIMAL); tt_assert(!ei->pending_sig); CHECK_OK(EX_EI_MAXIMAL); tt_assert(!ei->pending_sig); + CHECK_OK(EX_EI_GOOD_ED_EI); + tt_assert(!ei->pending_sig); CHECK_FAIL(EX_EI_BAD_SIG1,1); CHECK_FAIL(EX_EI_BAD_SIG2,1); @@ -457,6 +556,15 @@ test_dir_extrainfo_parsing(void *arg) CHECK_FAIL(EX_EI_BAD_START,0); CHECK_FAIL(EX_EI_BAD_PUBLISHED,0); + CHECK_FAIL(EX_EI_ED_MISSING_SIG,0); + CHECK_FAIL(EX_EI_ED_MISSING_CERT,0); + CHECK_FAIL(EX_EI_ED_BAD_CERT1,0); + CHECK_FAIL(EX_EI_ED_BAD_CERT2,0); + CHECK_FAIL(EX_EI_ED_BAD_SIG1,0); + CHECK_FAIL(EX_EI_ED_BAD_SIG2,0); + CHECK_FAIL(EX_EI_ED_MISPLACED_CERT,0); + CHECK_FAIL(EX_EI_ED_MISPLACED_SIG,0); + #undef CHECK_OK #undef CHECK_FAIL @@ -1394,6 +1502,7 @@ generate_ri_from_rs(const vote_routerstatus_t *vrs) static time_t published = 0; r = tor_malloc_zero(sizeof(routerinfo_t)); + r->cert_expiration_time = TIME_MAX; memcpy(r->cache_info.identity_digest, rs->identity_digest, DIGEST_LEN); memcpy(r->cache_info.signed_descriptor_digest, rs->descriptor_digest, DIGEST_LEN); @@ -3108,7 +3217,7 @@ test_dir_packages(void *arg) struct testcase_t dir_tests[] = { DIR_LEGACY(nicknames), DIR_LEGACY(formats), - DIR(routerparse_bad, 0), + DIR(routerinfo_parsing, 0), DIR(extrainfo_parsing, 0), DIR(parse_router_list, TT_FORK), DIR(load_routers, TT_FORK), diff --git a/src/test/test_keypin.c b/src/test/test_keypin.c new file mode 100644 index 0000000000..afd4ca201d --- /dev/null +++ b/src/test/test_keypin.c @@ -0,0 +1,255 @@ +/* Copyright (c) 2014, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "orconfig.h" +#define KEYPIN_PRIVATE +#include "or.h" +#include "keypin.h" +#include "util.h" + +#include "test.h" + +static void +test_keypin_parse_line(void *arg) +{ + (void)arg; + keypin_ent_t *ent = NULL; + + /* Good line */ + ent = keypin_parse_journal_line( + "aGVyZSBpcyBhIGdvb2Qgc2hhMSE " + "VGhpcyBlZDI1NTE5IHNjb2ZmcyBhdCB0aGUgc2hhMS4"); + tt_assert(ent); + tt_mem_op(ent->rsa_id, ==, "here is a good sha1!", 20); + tt_mem_op(ent->ed25519_key, ==, "This ed25519 scoffs at the sha1.", 32); + tor_free(ent); ent = NULL; + + /* Good line with extra stuff we will ignore. */ + ent = keypin_parse_journal_line( + "aGVyZSBpcyBhIGdvb2Qgc2hhMSE " + "VGhpcyBlZDI1NTE5IHNjb2ZmcyBhdCB0aGUgc2hhMS4helloworld"); + tt_assert(ent); + tt_mem_op(ent->rsa_id, ==, "here is a good sha1!", 20); + tt_mem_op(ent->ed25519_key, ==, "This ed25519 scoffs at the sha1.", 32); + tor_free(ent); ent = NULL; + + /* Bad line: no space in the middle. */ + ent = keypin_parse_journal_line( + "aGVyZSBpcyBhIGdvb2Qgc2hhMSE?" + "VGhpcyBlZDI1NTE5IHNjb2ZmcyBhdCB0aGUgc2hhMS4"); + tt_assert(! ent); + + /* Bad line: bad base64 in RSA ID */ + ent = keypin_parse_journal_line( + "aGVyZSBpcyBhIGdv!2Qgc2hhMSE " + "VGhpcyBlZDI1NTE5IHNjb2ZmcyBhdCB0aGUgc2hhMS4"); + tt_assert(! ent); + + /* Bad line: bad base64 in Ed25519 */ + ent = keypin_parse_journal_line( + "aGVyZSBpcyBhIGdvb2Qgc2hhMSE " + "VGhpcyBlZDI1NTE5IHNjb2ZmcyB!dCB0aGUgc2hhMS4"); + tt_assert(! ent); + + done: + tor_free(ent); +} + +static smartlist_t *mock_addent_got = NULL; +static void +mock_addent(keypin_ent_t *ent) +{ + smartlist_add(mock_addent_got, ent); + keypin_add_entry_to_map__real(ent); +} + +static void +test_keypin_parse_file(void *arg) +{ + (void)arg; + + mock_addent_got = smartlist_new(); + MOCK(keypin_add_entry_to_map, mock_addent); + + /* Simple, minimal, correct example. */ + const char data1[] = +"PT09PT09PT09PT09PT09PT09PT0 PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0\n" +"TG9yYXggaXBzdW0gZ3J1dnZ1bHU cyB0aG5lZWQgYW1ldCwgc25lcmdlbGx5IG9uY2UtbGU\n" +"ciBsZXJraW0sIHNlZCBkbyBiYXI YmFsb290IHRlbXBvciBnbHVwcGl0dXMgdXQgbGFib3I\n" +"ZSBldCB0cnVmZnVsYSBtYWduYSA YWxpcXVhLiBVdCBlbmltIGFkIGdyaWNrbGUtZ3Jhc3M\n" +"dmVuaWFtLCBxdWlzIG1pZmYtbXU ZmZlcmVkIGdhLXp1bXBjbyBsYWJvcmlzIG5pc2kgdXQ\n" +"Y3J1ZmZ1bHVzIGV4IGVhIHNjaGw b3BwaXR5IGNvbnNlcXVhdC4gRHVpcyBhdXRlIHNuYXI\n" +"Z2dsZSBpbiBzd29tZWVzd2FucyA aW4gdm9sdXB0YXRlIGF4ZS1oYWNrZXIgZXNzZSByaXA\n" +"cHVsdXMgY3J1bW1paSBldSBtb28 ZiBudWxsYSBzbnV2di5QTFVHSFBMT1ZFUlhZWlpZLi4\n"; + + tt_int_op(0, ==, keypin_load_journal_impl(data1, strlen(data1))); + tt_int_op(8, ==, smartlist_len(mock_addent_got)); + keypin_ent_t *ent = smartlist_get(mock_addent_got, 2); + tt_mem_op(ent->rsa_id, ==, "r lerkim, sed do bar", 20); + tt_mem_op(ent->ed25519_key, ==, "baloot tempor gluppitus ut labor", 32); + + /* More complex example: weird lines, bogus lines, + duplicate/conflicting lines */ + const char data2[] = + "PT09PT09PT09PT09PT09PT09PT0 PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0\n" + "# This is a comment.\n" + " \n" + "QXQgdGhlIGVuZCBvZiB0aGUgeWU YXIgS3VycmVta2FybWVycnVrIHNhaWQgdG8gaGltLCA\n" + "IllvdSBoYXZlIG1hZGUgYSBnb28 ZCBiZWdpbm5pbmcuIiBCdXQgbm8gbW9yZS4gV2l6YXI\n" + "\n" + "ZHMgc3BlYWsgdHJ1dGgsIGFuZCA aXQgd2FzIHRydWUgdGhhdCBhbGwgdGhlIG1hc3Rlcgo\n" + "@reserved for a future extension \n" + "eSBvZiBOYW1lcyB0aGF0IEdlZCA aGFkIHRvaWxlZCbyB3aW4gdGhhdCB5ZWFyIHdhcyA\n" + "eSBvZiBOYW1lcyB0aGF0IEdlZCA aGFkIHRvaWxlZCbyB3aW4gdGhhdCB5ZWFyIHdhcy" + "A line too long\n" + "dGhlIG1lcmUgc3RhcnQgb2Ygd2g YXQgaGUgbXVzdCBnbyBvb!BsZWFybmluZy4uLi4uLi4\n" + "ZHMgc3BlYWsgdaJ1dGgsIGFuZCA aXQgd2FzIHRydWUgdGhhdCBhbGwgdGhlIG1hc3Rlcgo\n" + "ZHMgc3BlYWsgdHJ1dGgsIGFuZCA aXQgd2FzIHRydaUgdGhhdCBhbGwgdGhlIG1hc3Rlcgo\n" + ; + + tt_int_op(0, ==, keypin_load_journal_impl(data2, strlen(data2))); + tt_int_op(11, ==, smartlist_len(mock_addent_got)); + ent = smartlist_get(mock_addent_got, 9); + tt_mem_op(ent->rsa_id, ==, "\"You have made a goo", 20); + tt_mem_op(ent->ed25519_key, ==, "d beginning.\" But no more. Wizar", 32); + + ent = smartlist_get(mock_addent_got, 10); + tt_mem_op(ent->rsa_id, ==, "ds speak truth, and ", 20); + tt_mem_op(ent->ed25519_key, ==, "it was true that all the master\n", 32); + + /* File truncated before NL */ + const char data3[] = + "Tm8gZHJhZ29uIGNhbiByZXNpc3Q IHRoZSBmYXNjaW5hdGlvbiBvZiByaWRkbGluZyB0YWw"; + tt_int_op(0, ==, keypin_load_journal_impl(data3, strlen(data3))); + tt_int_op(12, ==, smartlist_len(mock_addent_got)); + ent = smartlist_get(mock_addent_got, 11); + tt_mem_op(ent->rsa_id, ==, "No dragon can resist", 20); + tt_mem_op(ent->ed25519_key, ==, " the fascination of riddling tal", 32); + + done: + keypin_clear(); + smartlist_free(mock_addent_got); +} + +#define ADD(a,b) keypin_check_and_add((const uint8_t*)(a),(const uint8_t*)(b)) +#define LONE_RSA(a) keypin_check_lone_rsa((const uint8_t*)(a)) + +static void +test_keypin_add_entry(void *arg) +{ + (void)arg; + keypin_clear(); + + tt_int_op(KEYPIN_ADDED, ==, ADD("ambassadors-at-large", + "bread-and-butter thing-in-itself")); + tt_int_op(KEYPIN_ADDED, ==, ADD("gentleman-adventurer", + "cloak-and-dagger what's-his-face")); + + tt_int_op(KEYPIN_FOUND, ==, ADD("ambassadors-at-large", + "bread-and-butter thing-in-itself")); + tt_int_op(KEYPIN_FOUND, ==, ADD("ambassadors-at-large", + "bread-and-butter thing-in-itself")); + tt_int_op(KEYPIN_FOUND, ==, ADD("gentleman-adventurer", + "cloak-and-dagger what's-his-face")); + + tt_int_op(KEYPIN_ADDED, ==, ADD("Johnnies-come-lately", + "run-of-the-mill root-mean-square")); + + tt_int_op(KEYPIN_MISMATCH, ==, ADD("gentleman-adventurer", + "hypersentimental closefistedness")); + + tt_int_op(KEYPIN_MISMATCH, ==, ADD("disestablismentarian", + "cloak-and-dagger what's-his-face")); + + tt_int_op(KEYPIN_FOUND, ==, ADD("gentleman-adventurer", + "cloak-and-dagger what's-his-face")); + + tt_int_op(KEYPIN_NOT_FOUND, ==, LONE_RSA("Llanfairpwllgwyngyll")); + tt_int_op(KEYPIN_MISMATCH, ==, LONE_RSA("Johnnies-come-lately")); + + done: + keypin_clear(); +} + +static void +test_keypin_journal(void *arg) +{ + (void)arg; + char *contents = NULL; + const char *fname = get_fname("keypin-journal"); + + tt_int_op(0, ==, keypin_load_journal(fname)); /* ENOENT is okay */ + update_approx_time(1217709000); + tt_int_op(0, ==, keypin_open_journal(fname)); + + tt_int_op(KEYPIN_ADDED, ==, ADD("king-of-the-herrings", + "good-for-nothing attorney-at-law")); + tt_int_op(KEYPIN_ADDED, ==, ADD("yellowish-red-yellow", + "salt-and-pepper high-muck-a-muck")); + tt_int_op(KEYPIN_FOUND, ==, ADD("yellowish-red-yellow", + "salt-and-pepper high-muck-a-muck")); + keypin_close_journal(); + keypin_clear(); + + tt_int_op(0, ==, keypin_load_journal(fname)); + update_approx_time(1231041600); + tt_int_op(0, ==, keypin_open_journal(fname)); + tt_int_op(KEYPIN_FOUND, ==, ADD("yellowish-red-yellow", + "salt-and-pepper high-muck-a-muck")); + tt_int_op(KEYPIN_ADDED, ==, ADD("theatre-in-the-round", + "holier-than-thou jack-in-the-box")); + tt_int_op(KEYPIN_ADDED, ==, ADD("no-deposit-no-return", + "across-the-board will-o-the-wisp")); + tt_int_op(KEYPIN_MISMATCH, ==, ADD("intellectualizations", + "salt-and-pepper high-muck-a-muck")); + keypin_close_journal(); + keypin_clear(); + + tt_int_op(0, ==, keypin_load_journal(fname)); + update_approx_time(1412278354); + tt_int_op(0, ==, keypin_open_journal(fname)); + tt_int_op(KEYPIN_FOUND, ==, ADD("yellowish-red-yellow", + "salt-and-pepper high-muck-a-muck")); + tt_int_op(KEYPIN_MISMATCH, ==, ADD("intellectualizations", + "salt-and-pepper high-muck-a-muck")); + tt_int_op(KEYPIN_FOUND, ==, ADD("theatre-in-the-round", + "holier-than-thou jack-in-the-box")); + tt_int_op(KEYPIN_MISMATCH, ==, ADD("counterrevolutionary", + "holier-than-thou jack-in-the-box")); + tt_int_op(KEYPIN_MISMATCH, ==, ADD("no-deposit-no-return", + "floccinaucinihilipilificationism")); + keypin_close_journal(); + + contents = read_file_to_str(fname, RFTS_BIN, NULL); + tt_assert(contents); + tt_str_op(contents,==, + "\n" + "@opened-at 2008-08-02 20:30:00\n" + "a2luZy1vZi10aGUtaGVycmluZ3M Z29vZC1mb3Itbm90aGluZyBhdHRvcm5leS1hdC1sYXc\n" + "eWVsbG93aXNoLXJlZC15ZWxsb3c c2FsdC1hbmQtcGVwcGVyIGhpZ2gtbXVjay1hLW11Y2s\n" + "\n" + "@opened-at 2009-01-04 04:00:00\n" + "dGhlYXRyZS1pbi10aGUtcm91bmQ aG9saWVyLXRoYW4tdGhvdSBqYWNrLWluLXRoZS1ib3g\n" + "bm8tZGVwb3NpdC1uby1yZXR1cm4 YWNyb3NzLXRoZS1ib2FyZCB3aWxsLW8tdGhlLXdpc3A\n" + "\n" + "@opened-at 2014-10-02 19:32:34\n"); + + done: + tor_free(contents); + keypin_clear(); +} + +#undef ADD +#undef LONE_RSA + +#define TEST(name, flags) \ + { #name , test_keypin_ ## name, (flags), NULL, NULL } + +struct testcase_t keypin_tests[] = { + TEST( parse_line, 0 ), + TEST( parse_file, TT_FORK ), + TEST( add_entry, TT_FORK ), + TEST( journal, TT_FORK ), + END_OF_TESTCASES +}; + diff --git a/src/test/test_link_handshake.c b/src/test/test_link_handshake.c new file mode 100644 index 0000000000..bfdd6f3d1a --- /dev/null +++ b/src/test/test_link_handshake.c @@ -0,0 +1,914 @@ +/* Copyright (c) 2014, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "orconfig.h" + +#define CHANNELTLS_PRIVATE +#define CONNECTION_PRIVATE +#define TOR_CHANNEL_INTERNAL_ +#include "or.h" +#include "config.h" +#include "connection.h" +#include "connection_or.h" +#include "channeltls.h" +#include "link_handshake.h" +#include "scheduler.h" + +#include "test.h" + +var_cell_t *mock_got_var_cell = NULL; + +static void +mock_write_var_cell(const var_cell_t *vc, or_connection_t *conn) +{ + (void)conn; + + var_cell_t *newcell = var_cell_new(vc->payload_len); + memcpy(newcell, vc, sizeof(var_cell_t)); + memcpy(newcell->payload, vc->payload, vc->payload_len); + + mock_got_var_cell = newcell; +} +static int +mock_tls_cert_matches_key(const tor_tls_t *tls, const tor_x509_cert_t *cert) +{ + (void) tls; + (void) cert; // XXXX look at this. + return 1; +} + +static int mock_send_netinfo_called = 0; +static int +mock_send_netinfo(or_connection_t *conn) +{ + (void) conn; + ++mock_send_netinfo_called;// XXX check_this + return 0; +} + +static int mock_close_called = 0; +static void +mock_close_for_err(or_connection_t *orconn, int flush) +{ + (void)orconn; + (void)flush; + ++mock_close_called; +} + +static int mock_send_authenticate_called = 0; +static int +mock_send_authenticate(or_connection_t *conn, int type) +{ + (void) conn; + (void) type; + ++mock_send_authenticate_called;// XXX check_this + return 0; +} + +/* 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); + circuitmux_free(chan1->base_.cmux); + tor_free(chan1); + circuitmux_free(chan2->base_.cmux); + tor_free(chan2); + crypto_pk_free(key1); + crypto_pk_free(key2); +} + +typedef struct certs_data_s { + or_connection_t *c; + channel_tls_t *chan; + certs_cell_t *ccell; + var_cell_t *cell; + crypto_pk_t *key1, *key2; +} certs_data_t; + + +static int +recv_certs_cleanup(const struct testcase_t *test, void *obj) +{ + (void)test; + certs_data_t *d = obj; + UNMOCK(tor_tls_cert_matches_key); + UNMOCK(connection_or_send_netinfo); + UNMOCK(connection_or_close_for_error); + + if (d) { + tor_free(d->cell); + certs_cell_free(d->ccell); + connection_free_(TO_CONN(d->c)); + circuitmux_free(d->chan->base_.cmux); + tor_free(d->chan); + crypto_pk_free(d->key1); + crypto_pk_free(d->key2); + tor_free(d); + } + return 1; +} + +static void * +recv_certs_setup(const struct testcase_t *test) +{ + (void)test; + certs_data_t *d = tor_malloc_zero(sizeof(*d)); + certs_cell_cert_t *ccc1 = NULL; + certs_cell_cert_t *ccc2 = NULL; + ssize_t n; + + d->c = or_connection_new(CONN_TYPE_OR, AF_INET); + d->chan = tor_malloc_zero(sizeof(*d->chan)); + d->c->chan = d->chan; + d->c->base_.address = tor_strdup("HaveAnAddress"); + d->c->base_.state = OR_CONN_STATE_OR_HANDSHAKING_V3; + d->chan->conn = d->c; + tt_int_op(connection_init_or_handshake_state(d->c, 1), ==, 0); + d->c->link_proto = 4; + + d->key1 = pk_generate(2); + d->key2 = pk_generate(3); + + tt_int_op(tor_tls_context_init(TOR_TLS_CTX_IS_PUBLIC_SERVER, + d->key1, d->key2, 86400), ==, 0); + d->ccell = certs_cell_new(); + ccc1 = certs_cell_cert_new(); + certs_cell_add_certs(d->ccell, ccc1); + ccc2 = certs_cell_cert_new(); + certs_cell_add_certs(d->ccell, ccc2); + d->ccell->n_certs = 2; + ccc1->cert_type = 1; + ccc2->cert_type = 2; + + const tor_x509_cert_t *a,*b; + const uint8_t *enca, *encb; + size_t lena, lenb; + tor_tls_get_my_certs(1, &a, &b); + tor_x509_cert_get_der(a, &enca, &lena); + tor_x509_cert_get_der(b, &encb, &lenb); + certs_cell_cert_setlen_body(ccc1, lena); + ccc1->cert_len = lena; + certs_cell_cert_setlen_body(ccc2, lenb); + ccc2->cert_len = lenb; + + memcpy(certs_cell_cert_getarray_body(ccc1), enca, lena); + memcpy(certs_cell_cert_getarray_body(ccc2), encb, lenb); + + d->cell = var_cell_new(4096); + d->cell->command = CELL_CERTS; + + n = certs_cell_encode(d->cell->payload, 4096, d->ccell); + tt_int_op(n, >, 0); + d->cell->payload_len = n; + + MOCK(tor_tls_cert_matches_key, mock_tls_cert_matches_key); + MOCK(connection_or_send_netinfo, mock_send_netinfo); + MOCK(connection_or_close_for_error, mock_close_for_err); + + tt_int_op(0, ==, d->c->handshake_state->received_certs_cell); + tt_int_op(0, ==, mock_send_authenticate_called); + tt_int_op(0, ==, mock_send_netinfo_called); + + return d; + done: + recv_certs_cleanup(test, d); + return NULL; +} + +static struct testcase_setup_t setup_recv_certs = { + .setup_fn = recv_certs_setup, + .cleanup_fn = recv_certs_cleanup +}; + +static void +test_link_handshake_recv_certs_ok(void *arg) +{ + certs_data_t *d = arg; + channel_tls_process_certs_cell(d->cell, d->chan); + tt_int_op(0, ==, mock_close_called); + tt_int_op(d->c->handshake_state->authenticated, ==, 1); + tt_int_op(d->c->handshake_state->received_certs_cell, ==, 1); + tt_assert(d->c->handshake_state->id_cert != NULL); + tt_assert(d->c->handshake_state->auth_cert == NULL); + + done: + ; +} + +static void +test_link_handshake_recv_certs_ok_server(void *arg) +{ + certs_data_t *d = arg; + d->c->handshake_state->started_here = 0; + certs_cell_get_certs(d->ccell, 0)->cert_type = 3; + certs_cell_get_certs(d->ccell, 1)->cert_type = 2; + ssize_t n = certs_cell_encode(d->cell->payload, 2048, d->ccell); + tt_int_op(n, >, 0); + d->cell->payload_len = n; + channel_tls_process_certs_cell(d->cell, d->chan); + tt_int_op(0, ==, mock_close_called); + tt_int_op(d->c->handshake_state->authenticated, ==, 0); + tt_int_op(d->c->handshake_state->received_certs_cell, ==, 1); + tt_assert(d->c->handshake_state->id_cert != NULL); + tt_assert(d->c->handshake_state->auth_cert != NULL); + + done: + ; +} + +#define CERTS_FAIL(name, code) \ + static void \ + test_link_handshake_recv_certs_ ## name (void *arg) \ + { \ + certs_data_t *d = arg; \ + { code ; } \ + channel_tls_process_certs_cell(d->cell, d->chan); \ + tt_int_op(1, ==, mock_close_called); \ + tt_int_op(0, ==, mock_send_authenticate_called); \ + tt_int_op(0, ==, mock_send_netinfo_called); \ + done: \ + ; \ + } + +CERTS_FAIL(badstate, d->c->base_.state = OR_CONN_STATE_CONNECTING) +CERTS_FAIL(badproto, d->c->link_proto = 2) +CERTS_FAIL(duplicate, d->c->handshake_state->received_certs_cell = 1) +CERTS_FAIL(already_authenticated, + d->c->handshake_state->authenticated = 1) +CERTS_FAIL(empty, d->cell->payload_len = 0) +CERTS_FAIL(bad_circid, d->cell->circ_id = 1) +CERTS_FAIL(truncated_1, d->cell->payload[0] = 5) +CERTS_FAIL(truncated_2, { + d->cell->payload_len = 4; + memcpy(d->cell->payload, "\x01\x01\x00\x05", 4);}) +CERTS_FAIL(truncated_3, { + d->cell->payload_len = 7; + memcpy(d->cell->payload, "\x01\x01\x00\x05""abc", 7);}) +#define REENCODE() do { \ + ssize_t n = certs_cell_encode(d->cell->payload, 4096, d->ccell); \ + tt_int_op(n, >, 0); \ + d->cell->payload_len = n; \ + } while (0) + +CERTS_FAIL(not_x509, { + certs_cell_cert_setlen_body(certs_cell_get_certs(d->ccell, 0), 3); + certs_cell_get_certs(d->ccell, 0)->cert_len = 3; + REENCODE(); + }) +CERTS_FAIL(both_link, { + certs_cell_get_certs(d->ccell, 0)->cert_type = 1; + certs_cell_get_certs(d->ccell, 1)->cert_type = 1; + REENCODE(); + }) +CERTS_FAIL(both_id_rsa, { + certs_cell_get_certs(d->ccell, 0)->cert_type = 2; + certs_cell_get_certs(d->ccell, 1)->cert_type = 2; + REENCODE(); + }) +CERTS_FAIL(both_auth, { + certs_cell_get_certs(d->ccell, 0)->cert_type = 3; + certs_cell_get_certs(d->ccell, 1)->cert_type = 3; + REENCODE(); + }) +CERTS_FAIL(wrong_labels_1, { + certs_cell_get_certs(d->ccell, 0)->cert_type = 2; + certs_cell_get_certs(d->ccell, 1)->cert_type = 1; + REENCODE(); + }) +CERTS_FAIL(wrong_labels_2, { + const tor_x509_cert_t *a; + const tor_x509_cert_t *b; + const uint8_t *enca; + size_t lena; + tor_tls_get_my_certs(1, &a, &b); + tor_x509_cert_get_der(a, &enca, &lena); + certs_cell_cert_setlen_body(certs_cell_get_certs(d->ccell, 1), lena); + memcpy(certs_cell_cert_getarray_body(certs_cell_get_certs(d->ccell, 1)), + enca, lena); + certs_cell_get_certs(d->ccell, 1)->cert_len = lena; + REENCODE(); + }) +CERTS_FAIL(wrong_labels_3, { + certs_cell_get_certs(d->ccell, 0)->cert_type = 2; + certs_cell_get_certs(d->ccell, 1)->cert_type = 3; + REENCODE(); + }) +CERTS_FAIL(server_missing_certs, { + d->c->handshake_state->started_here = 0; + }) +CERTS_FAIL(server_wrong_labels_1, { + d->c->handshake_state->started_here = 0; + certs_cell_get_certs(d->ccell, 0)->cert_type = 2; + certs_cell_get_certs(d->ccell, 1)->cert_type = 3; + REENCODE(); + }) + +static void +test_link_handshake_send_authchallenge(void *arg) +{ + (void)arg; + + or_connection_t *c1 = or_connection_new(CONN_TYPE_OR, AF_INET); + var_cell_t *cell1=NULL, *cell2=NULL; + + MOCK(connection_or_write_var_cell_to_buf, mock_write_var_cell); + + tt_int_op(connection_init_or_handshake_state(c1, 0), ==, 0); + c1->base_.state = OR_CONN_STATE_OR_HANDSHAKING_V3; + tt_assert(! mock_got_var_cell); + tt_int_op(0, ==, connection_or_send_auth_challenge_cell(c1)); + cell1 = mock_got_var_cell; + tt_int_op(0, ==, connection_or_send_auth_challenge_cell(c1)); + cell2 = mock_got_var_cell; + tt_int_op(36, ==, cell1->payload_len); + tt_int_op(36, ==, cell2->payload_len); + tt_int_op(0, ==, cell1->circ_id); + tt_int_op(0, ==, cell2->circ_id); + tt_int_op(CELL_AUTH_CHALLENGE, ==, cell1->command); + tt_int_op(CELL_AUTH_CHALLENGE, ==, cell2->command); + + tt_mem_op("\x00\x01\x00\x01", ==, cell1->payload + 32, 4); + tt_mem_op("\x00\x01\x00\x01", ==, cell2->payload + 32, 4); + tt_mem_op(cell1->payload, !=, cell2->payload, 32); + + done: + UNMOCK(connection_or_write_var_cell_to_buf); + connection_free_(TO_CONN(c1)); + tor_free(cell1); + tor_free(cell2); +} + +typedef struct authchallenge_data_s { + or_connection_t *c; + channel_tls_t *chan; + var_cell_t *cell; +} authchallenge_data_t; + +static int +recv_authchallenge_cleanup(const struct testcase_t *test, void *obj) +{ + (void)test; + authchallenge_data_t *d = obj; + + UNMOCK(connection_or_send_netinfo); + UNMOCK(connection_or_close_for_error); + UNMOCK(connection_or_send_authenticate_cell); + + if (d) { + tor_free(d->cell); + connection_free_(TO_CONN(d->c)); + circuitmux_free(d->chan->base_.cmux); + tor_free(d->chan); + tor_free(d); + } + return 1; +} + +static void * +recv_authchallenge_setup(const struct testcase_t *test) +{ + (void)test; + authchallenge_data_t *d = tor_malloc_zero(sizeof(*d)); + d->c = or_connection_new(CONN_TYPE_OR, AF_INET); + d->chan = tor_malloc_zero(sizeof(*d->chan)); + d->c->chan = d->chan; + d->c->base_.address = tor_strdup("HaveAnAddress"); + d->c->base_.state = OR_CONN_STATE_OR_HANDSHAKING_V3; + d->chan->conn = d->c; + tt_int_op(connection_init_or_handshake_state(d->c, 1), ==, 0); + d->c->link_proto = 4; + d->c->handshake_state->received_certs_cell = 1; + d->cell = var_cell_new(128); + d->cell->payload_len = 38; + d->cell->payload[33] = 2; + d->cell->payload[35] = 7; + d->cell->payload[37] = 1; + d->cell->command = CELL_AUTH_CHALLENGE; + + get_options_mutable()->ORPort_set = 1; + + MOCK(connection_or_close_for_error, mock_close_for_err); + MOCK(connection_or_send_netinfo, mock_send_netinfo); + MOCK(connection_or_send_authenticate_cell, mock_send_authenticate); + + tt_int_op(0, ==, d->c->handshake_state->received_auth_challenge); + tt_int_op(0, ==, mock_send_authenticate_called); + tt_int_op(0, ==, mock_send_netinfo_called); + + return d; + done: + recv_authchallenge_cleanup(test, d); + return NULL; +} + +static struct testcase_setup_t setup_recv_authchallenge = { + .setup_fn = recv_authchallenge_setup, + .cleanup_fn = recv_authchallenge_cleanup +}; + +static void +test_link_handshake_recv_authchallenge_ok(void *arg) +{ + authchallenge_data_t *d = arg; + + channel_tls_process_auth_challenge_cell(d->cell, d->chan); + tt_int_op(0, ==, mock_close_called); + tt_int_op(1, ==, d->c->handshake_state->received_auth_challenge); + tt_int_op(1, ==, mock_send_authenticate_called); + tt_int_op(1, ==, mock_send_netinfo_called); + done: + ; +} + +static void +test_link_handshake_recv_authchallenge_ok_noserver(void *arg) +{ + authchallenge_data_t *d = arg; + get_options_mutable()->ORPort_set = 0; + + channel_tls_process_auth_challenge_cell(d->cell, d->chan); + tt_int_op(0, ==, mock_close_called); + tt_int_op(1, ==, d->c->handshake_state->received_auth_challenge); + tt_int_op(0, ==, mock_send_authenticate_called); + tt_int_op(0, ==, mock_send_netinfo_called); + done: + ; +} + +static void +test_link_handshake_recv_authchallenge_ok_unrecognized(void *arg) +{ + authchallenge_data_t *d = arg; + d->cell->payload[37] = 99; + + channel_tls_process_auth_challenge_cell(d->cell, d->chan); + tt_int_op(0, ==, mock_close_called); + tt_int_op(1, ==, d->c->handshake_state->received_auth_challenge); + tt_int_op(0, ==, mock_send_authenticate_called); + tt_int_op(1, ==, mock_send_netinfo_called); + done: + ; +} + +#define AUTHCHALLENGE_FAIL(name, code) \ + static void \ + test_link_handshake_recv_authchallenge_ ## name (void *arg) \ + { \ + authchallenge_data_t *d = arg; \ + { code ; } \ + channel_tls_process_auth_challenge_cell(d->cell, d->chan); \ + tt_int_op(1, ==, mock_close_called); \ + tt_int_op(0, ==, mock_send_authenticate_called); \ + tt_int_op(0, ==, mock_send_netinfo_called); \ + done: \ + ; \ + } + +AUTHCHALLENGE_FAIL(badstate, + d->c->base_.state = OR_CONN_STATE_CONNECTING) +AUTHCHALLENGE_FAIL(badproto, + d->c->link_proto = 2) +AUTHCHALLENGE_FAIL(as_server, + d->c->handshake_state->started_here = 0;) +AUTHCHALLENGE_FAIL(duplicate, + d->c->handshake_state->received_auth_challenge = 1) +AUTHCHALLENGE_FAIL(nocerts, + d->c->handshake_state->received_certs_cell = 0) +AUTHCHALLENGE_FAIL(tooshort, + d->cell->payload_len = 33) +AUTHCHALLENGE_FAIL(truncated, + d->cell->payload_len = 34) +AUTHCHALLENGE_FAIL(nonzero_circid, + d->cell->circ_id = 1337) + + +static tor_x509_cert_t *mock_peer_cert = NULL; +static tor_x509_cert_t * +mock_get_peer_cert(tor_tls_t *tls) +{ + (void)tls; + return mock_peer_cert; +} + +static int +mock_get_tlssecrets(tor_tls_t *tls, uint8_t *secrets_out) +{ + (void)tls; + memcpy(secrets_out, "int getRandomNumber(){return 4;}", 32); + return 0; +} + +static void +mock_set_circid_type(channel_t *chan, + crypto_pk_t *identity_rcvd, + int consider_identity) +{ + (void) chan; + (void) identity_rcvd; + (void) consider_identity; +} + +typedef struct authenticate_data_s { + or_connection_t *c1, *c2; + channel_tls_t *chan2; + var_cell_t *cell; + crypto_pk_t *key1, *key2; +} authenticate_data_t; + +static int +authenticate_data_cleanup(const struct testcase_t *test, void *arg) +{ + (void) test; + UNMOCK(connection_or_write_var_cell_to_buf); + UNMOCK(tor_tls_get_peer_cert); + UNMOCK(tor_tls_get_tlssecrets); + UNMOCK(connection_or_close_for_error); + UNMOCK(channel_set_circid_type); + authenticate_data_t *d = arg; + if (d) { + tor_free(d->cell); + connection_free_(TO_CONN(d->c1)); + connection_free_(TO_CONN(d->c2)); + circuitmux_free(d->chan2->base_.cmux); + tor_free(d->chan2); + crypto_pk_free(d->key1); + crypto_pk_free(d->key2); + tor_free(d); + } + mock_peer_cert = NULL; + + return 1; +} + +static void * +authenticate_data_setup(const struct testcase_t *test) +{ + authenticate_data_t *d = tor_malloc_zero(sizeof(*d)); + + scheduler_init(); + + MOCK(connection_or_write_var_cell_to_buf, mock_write_var_cell); + MOCK(tor_tls_get_peer_cert, mock_get_peer_cert); + MOCK(tor_tls_get_tlssecrets, mock_get_tlssecrets); + MOCK(connection_or_close_for_error, mock_close_for_err); + MOCK(channel_set_circid_type, mock_set_circid_type); + d->c1 = or_connection_new(CONN_TYPE_OR, AF_INET); + d->c2 = or_connection_new(CONN_TYPE_OR, AF_INET); + + d->key1 = pk_generate(2); + d->key2 = pk_generate(3); + tt_int_op(tor_tls_context_init(TOR_TLS_CTX_IS_PUBLIC_SERVER, + d->key1, d->key2, 86400), ==, 0); + + d->c1->base_.state = OR_CONN_STATE_OR_HANDSHAKING_V3; + d->c1->link_proto = 3; + tt_int_op(connection_init_or_handshake_state(d->c1, 1), ==, 0); + + d->c2->base_.state = OR_CONN_STATE_OR_HANDSHAKING_V3; + d->c2->link_proto = 3; + tt_int_op(connection_init_or_handshake_state(d->c2, 0), ==, 0); + var_cell_t *cell = var_cell_new(16); + cell->command = CELL_CERTS; + or_handshake_state_record_var_cell(d->c1, d->c1->handshake_state, cell, 1); + or_handshake_state_record_var_cell(d->c2, d->c2->handshake_state, cell, 0); + memset(cell->payload, 0xf0, 16); + or_handshake_state_record_var_cell(d->c1, d->c1->handshake_state, cell, 0); + or_handshake_state_record_var_cell(d->c2, d->c2->handshake_state, cell, 1); + tor_free(cell); + + d->chan2 = tor_malloc_zero(sizeof(*d->chan2)); + channel_tls_common_init(d->chan2); + d->c2->chan = d->chan2; + d->chan2->conn = d->c2; + d->c2->base_.address = tor_strdup("C2"); + d->c2->tls = tor_tls_new(-1, 1); + d->c2->handshake_state->received_certs_cell = 1; + + const tor_x509_cert_t *id_cert=NULL, *link_cert=NULL, *auth_cert=NULL; + tt_assert(! tor_tls_get_my_certs(1, &link_cert, &id_cert)); + + const uint8_t *der; + size_t sz; + tor_x509_cert_get_der(id_cert, &der, &sz); + d->c1->handshake_state->id_cert = tor_x509_cert_decode(der, sz); + d->c2->handshake_state->id_cert = tor_x509_cert_decode(der, sz); + + tor_x509_cert_get_der(link_cert, &der, &sz); + mock_peer_cert = tor_x509_cert_decode(der, sz); + tt_assert(mock_peer_cert); + tt_assert(! tor_tls_get_my_certs(0, &auth_cert, &id_cert)); + tor_x509_cert_get_der(auth_cert, &der, &sz); + d->c2->handshake_state->auth_cert = tor_x509_cert_decode(der, sz); + + /* Make an authenticate cell ... */ + tt_int_op(0, ==, connection_or_send_authenticate_cell(d->c1, + AUTHTYPE_RSA_SHA256_TLSSECRET)); + tt_assert(mock_got_var_cell); + d->cell = mock_got_var_cell; + mock_got_var_cell = NULL; + + return d; + done: + authenticate_data_cleanup(test, d); + return NULL; +} + +static struct testcase_setup_t setup_authenticate = { + .setup_fn = authenticate_data_setup, + .cleanup_fn = authenticate_data_cleanup +}; + +static void +test_link_handshake_auth_cell(void *arg) +{ + authenticate_data_t *d = arg; + auth1_t *auth1 = NULL; + crypto_pk_t *auth_pubkey = NULL; + + /* Is the cell well-formed on the outer layer? */ + tt_int_op(d->cell->command, ==, CELL_AUTHENTICATE); + tt_int_op(d->cell->payload[0], ==, 0); + tt_int_op(d->cell->payload[1], ==, 1); + tt_int_op(ntohs(get_uint16(d->cell->payload + 2)), ==, + d->cell->payload_len - 4); + + /* Check it out for plausibility... */ + auth_ctx_t ctx; + ctx.is_ed = 0; + tt_int_op(d->cell->payload_len-4, ==, auth1_parse(&auth1, + d->cell->payload+4, + d->cell->payload_len - 4, &ctx)); + tt_assert(auth1); + + tt_mem_op(auth1->type, ==, "AUTH0001", 8); + tt_mem_op(auth1->tlssecrets, ==, "int getRandomNumber(){return 4;}", 32); + tt_int_op(auth1_getlen_sig(auth1), >, 120); + + /* Is the signature okay? */ + uint8_t sig[128]; + uint8_t digest[32]; + + auth_pubkey = tor_tls_cert_get_key(d->c2->handshake_state->auth_cert); + int n = crypto_pk_public_checksig( + auth_pubkey, + (char*)sig, sizeof(sig), (char*)auth1_getarray_sig(auth1), + auth1_getlen_sig(auth1)); + tt_int_op(n, ==, 32); + const uint8_t *start = d->cell->payload+4, *end = auth1->end_of_signed; + crypto_digest256((char*)digest, + (const char*)start, end-start, DIGEST_SHA256); + tt_mem_op(sig, ==, digest, 32); + + /* Then feed it to c2. */ + tt_int_op(d->c2->handshake_state->authenticated, ==, 0); + channel_tls_process_authenticate_cell(d->cell, d->chan2); + tt_int_op(mock_close_called, ==, 0); + tt_int_op(d->c2->handshake_state->authenticated, ==, 1); + + done: + auth1_free(auth1); + crypto_pk_free(auth_pubkey); +} + +#define AUTHENTICATE_FAIL(name, code) \ + static void \ + test_link_handshake_auth_ ## name (void *arg) \ + { \ + authenticate_data_t *d = arg; \ + { code ; } \ + tt_int_op(d->c2->handshake_state->authenticated, ==, 0); \ + channel_tls_process_authenticate_cell(d->cell, d->chan2); \ + tt_int_op(mock_close_called, ==, 1); \ + tt_int_op(d->c2->handshake_state->authenticated, ==, 0); \ + done: \ + ; \ + } + +AUTHENTICATE_FAIL(badstate, + d->c2->base_.state = OR_CONN_STATE_CONNECTING) +AUTHENTICATE_FAIL(badproto, + d->c2->link_proto = 2) +AUTHENTICATE_FAIL(atclient, + d->c2->handshake_state->started_here = 1) +AUTHENTICATE_FAIL(duplicate, + d->c2->handshake_state->received_authenticate = 1) +static void +test_link_handshake_auth_already_authenticated(void *arg) +{ + authenticate_data_t *d = arg; + d->c2->handshake_state->authenticated = 1; + channel_tls_process_authenticate_cell(d->cell, d->chan2); + tt_int_op(mock_close_called, ==, 1); + tt_int_op(d->c2->handshake_state->authenticated, ==, 1); + done: + ; +} +AUTHENTICATE_FAIL(nocerts, + d->c2->handshake_state->received_certs_cell = 0) +AUTHENTICATE_FAIL(noidcert, + tor_x509_cert_free(d->c2->handshake_state->id_cert); + d->c2->handshake_state->id_cert = NULL) +AUTHENTICATE_FAIL(noauthcert, + tor_x509_cert_free(d->c2->handshake_state->auth_cert); + d->c2->handshake_state->auth_cert = NULL) +AUTHENTICATE_FAIL(tooshort, + d->cell->payload_len = 3) +AUTHENTICATE_FAIL(badtype, + d->cell->payload[0] = 0xff) +AUTHENTICATE_FAIL(truncated_1, + d->cell->payload[2]++) +AUTHENTICATE_FAIL(truncated_2, + d->cell->payload[3]++) +AUTHENTICATE_FAIL(tooshort_1, + tt_int_op(d->cell->payload_len, >=, 260); + d->cell->payload[2] -= 1; + d->cell->payload_len -= 256;) +AUTHENTICATE_FAIL(badcontent, + d->cell->payload[10] ^= 0xff) +AUTHENTICATE_FAIL(badsig_1, + d->cell->payload[d->cell->payload_len - 5] ^= 0xff) + +#define TEST(name, flags) \ + { #name , test_link_handshake_ ## name, (flags), NULL, NULL } + +#define TEST_RCV_AUTHCHALLENGE(name) \ + { "recv_authchallenge/" #name , \ + test_link_handshake_recv_authchallenge_ ## name, TT_FORK, \ + &setup_recv_authchallenge, NULL } + +#define TEST_RCV_CERTS(name) \ + { "recv_certs/" #name , \ + test_link_handshake_recv_certs_ ## name, TT_FORK, \ + &setup_recv_certs, NULL } + +#define TEST_AUTHENTICATE(name) \ + { "authenticate/" #name , test_link_handshake_auth_ ## name, TT_FORK, \ + &setup_authenticate, NULL } + +struct testcase_t link_handshake_tests[] = { + TEST(certs_ok, TT_FORK), + //TEST(certs_bad, TT_FORK), + TEST_RCV_CERTS(ok), + TEST_RCV_CERTS(ok_server), + TEST_RCV_CERTS(badstate), + TEST_RCV_CERTS(badproto), + TEST_RCV_CERTS(duplicate), + TEST_RCV_CERTS(already_authenticated), + TEST_RCV_CERTS(empty), + TEST_RCV_CERTS(bad_circid), + TEST_RCV_CERTS(truncated_1), + TEST_RCV_CERTS(truncated_2), + TEST_RCV_CERTS(truncated_3), + TEST_RCV_CERTS(not_x509), + TEST_RCV_CERTS(both_link), + TEST_RCV_CERTS(both_id_rsa), + TEST_RCV_CERTS(both_auth), + TEST_RCV_CERTS(wrong_labels_1), + TEST_RCV_CERTS(wrong_labels_2), + TEST_RCV_CERTS(wrong_labels_3), + TEST_RCV_CERTS(server_missing_certs), + TEST_RCV_CERTS(server_wrong_labels_1), + + TEST(send_authchallenge, TT_FORK), + TEST_RCV_AUTHCHALLENGE(ok), + TEST_RCV_AUTHCHALLENGE(ok_noserver), + TEST_RCV_AUTHCHALLENGE(ok_unrecognized), + TEST_RCV_AUTHCHALLENGE(badstate), + TEST_RCV_AUTHCHALLENGE(badproto), + TEST_RCV_AUTHCHALLENGE(as_server), + TEST_RCV_AUTHCHALLENGE(duplicate), + TEST_RCV_AUTHCHALLENGE(nocerts), + TEST_RCV_AUTHCHALLENGE(tooshort), + TEST_RCV_AUTHCHALLENGE(truncated), + TEST_RCV_AUTHCHALLENGE(nonzero_circid), + + TEST_AUTHENTICATE(cell), + TEST_AUTHENTICATE(badstate), + TEST_AUTHENTICATE(badproto), + TEST_AUTHENTICATE(atclient), + TEST_AUTHENTICATE(duplicate), + TEST_AUTHENTICATE(already_authenticated), + TEST_AUTHENTICATE(nocerts), + TEST_AUTHENTICATE(noidcert), + TEST_AUTHENTICATE(noauthcert), + TEST_AUTHENTICATE(tooshort), + TEST_AUTHENTICATE(badtype), + TEST_AUTHENTICATE(truncated_1), + TEST_AUTHENTICATE(truncated_2), + TEST_AUTHENTICATE(tooshort_1), + TEST_AUTHENTICATE(badcontent), + TEST_AUTHENTICATE(badsig_1), + //TEST_AUTHENTICATE(), + + END_OF_TESTCASES +}; diff --git a/src/test/test_microdesc.c b/src/test/test_microdesc.c index fb3df77edc..3c22e1809a 100644 --- a/src/test/test_microdesc.c +++ b/src/test/test_microdesc.c @@ -10,6 +10,7 @@ #include "networkstatus.h" #include "routerlist.h" #include "routerparse.h" +#include "torcert.h" #include "test.h" @@ -335,6 +336,59 @@ static const char test_ri[] = "t0xkIE39ss/EwmQr7iIgkdVH4oRIMsjYnFFJBG26nYY=\n" "-----END SIGNATURE-----\n"; +static const char test_ri2[] = + "router test001a 127.0.0.1 5001 0 7001\n" + "identity-ed25519\n" + "-----BEGIN ED25519 CERT-----\n" + "AQQABf/FAf5iDuKCZP2VxnAaQWdklilAh6kaEeFX4z8261Yx2T1/AQAgBADCp8vO\n" + "B8K1F9g2DzwuwvVCnPFLSK1qknVqPpNucHLH9DY7fuIYogBAdz4zHv1qC7RKaMNG\n" + "Jux/tMO2tzPcm62Ky5PjClMQplKUOnZNQ+RIpA3wYCIfUDy/cQnY7XWgNQ0=\n" + "-----END ED25519 CERT-----\n" + "platform Tor 0.2.6.0-alpha-dev on Darwin\n" + "protocols Link 1 2 Circuit 1\n" + "published 2014-10-08 12:58:04\n" + "fingerprint B7E2 7F10 4213 C36F 13E7 E982 9182 845E 4959 97A0\n" + "uptime 0\n" + "bandwidth 1073741824 1073741824 0\n" + "extra-info-digest 568F27331B6D8C73E7024F1EF5D097B90DFC7CDB\n" + "caches-extra-info\n" + "onion-key\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBAL2R8EfubUcahxha4u02P4VAR0llQIMwFAmrHPjzcK7apcQgDOf2ovOA\n" + "+YQnJFxlpBmCoCZC6ssCi+9G0mqo650lFuTMP5I90BdtjotfzESfTykHLiChyvhd\n" + "l0dlqclb2SU/GKem/fLRXH16aNi72CdSUu/1slKs/70ILi34QixRAgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + "signing-key\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBAN8+78KUVlgHXdMMkYJxcwh1Zv2y+Gb5eWUyltUaQRajhrT9ij2T5JZs\n" + "M0g85xTcuM3jNVVpV79+33hiTohdC6UZ+Bk4USQ7WBFzRbVFSXoVKLBJFkCOIexg\n" + "SMGNd5WEDtHWrXl58mizmPFu1eG6ZxHzt7RuLSol5cwBvawXPNkFAgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + "onion-key-crosscert\n" + "-----BEGIN CROSSCERT-----\n" + "ETFDzU49bvNfoZnKK1j6JeBP2gDirgj6bBCgWpUYs663OO9ypbZRO0JwWANssKl6\n" + "oaq9vKTsKGRsaNnqnz/JGMhehymakjjNtqg7crWwsahe8+7Pw9GKmW+YjFtcOkUf\n" + "KfOn2bmKBa1FoJb4yW3oXzHcdlLSRuCciKqPn+Hky5o=\n" + "-----END CROSSCERT-----\n" + "ntor-onion-key-crosscert 0\n" + "-----BEGIN ED25519 CERT-----\n" + "AQoABf2dAcKny84HwrUX2DYPPC7C9UKc8UtIrWqSdWo+k25wcsf0AFohutG+xI06\n" + "Ef21c5Zl1j8Hw6DzHDjYyJevXLFuOneaL3zcH2Ldn4sjrG3kc5UuVvRfTvV120UO\n" + "xk4f5s5LGwY=\n" + "-----END ED25519 CERT-----\n" + "hidden-service-dir\n" + "contact auth1@test.test\n" + "ntor-onion-key hbxdRnfVUJJY7+KcT4E3Rs7/zuClbN3hJrjSBiEGMgI=\n" + "reject *:*\n" + "router-sig-ed25519 5aQXyTif7PExIuL2di37UvktmJECKnils2OWz2vDi" + "hFxi+5TTAAPxYkS5clhc/Pjvw34itfjGmTKFic/8httAQ\n" + "router-signature\n" + "-----BEGIN SIGNATURE-----\n" + "BaUB+aFPQbb3BwtdzKsKqV3+6cRlSqJF5bI3UTmwRoJk+Z5Pz+W5NWokNI0xArHM\n" + "T4T5FZCCP9350jXsUCIvzyIyktU6aVRCGFt76rFlo1OETpN8GWkMnQU0w18cxvgS\n" + "cf34GXHv61XReJF3AlzNHFpbrPOYmowmhrTULKyMqow=\n" + "-----END SIGNATURE-----\n"; + static const char test_md_8[] = "onion-key\n" "-----BEGIN RSA PUBLIC KEY-----\n" @@ -365,6 +419,26 @@ static const char test_md_18[] = "p reject 25,119,135-139,445,563,1214,4661-4666,6346-6429,6699,6881-6999\n" "id rsa1024 Cd47okjCHD83YGzThGBDptXs9Z4\n"; +static const char test_md2_18[] = + "onion-key\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBAL2R8EfubUcahxha4u02P4VAR0llQIMwFAmrHPjzcK7apcQgDOf2ovOA\n" + "+YQnJFxlpBmCoCZC6ssCi+9G0mqo650lFuTMP5I90BdtjotfzESfTykHLiChyvhd\n" + "l0dlqclb2SU/GKem/fLRXH16aNi72CdSUu/1slKs/70ILi34QixRAgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + "ntor-onion-key hbxdRnfVUJJY7+KcT4E3Rs7/zuClbN3hJrjSBiEGMgI=\n" + "id rsa1024 t+J/EEITw28T5+mCkYKEXklZl6A\n"; + +static const char test_md2_21[] = + "onion-key\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBAL2R8EfubUcahxha4u02P4VAR0llQIMwFAmrHPjzcK7apcQgDOf2ovOA\n" + "+YQnJFxlpBmCoCZC6ssCi+9G0mqo650lFuTMP5I90BdtjotfzESfTykHLiChyvhd\n" + "l0dlqclb2SU/GKem/fLRXH16aNi72CdSUu/1slKs/70ILi34QixRAgMBAAE=\n" + "-----END RSA PUBLIC KEY-----\n" + "ntor-onion-key hbxdRnfVUJJY7+KcT4E3Rs7/zuClbN3hJrjSBiEGMgI=\n" + "id ed25519 wqfLzgfCtRfYNg88LsL1QpzxS0itapJ1aj6TbnByx/Q\n"; + static void test_md_generate(void *arg) { @@ -391,6 +465,25 @@ test_md_generate(void *arg) md = dirvote_create_microdescriptor(ri, 18); tt_str_op(md->body, OP_EQ, test_md_18); + microdesc_free(md); + md = NULL; + md = dirvote_create_microdescriptor(ri, 21); + tt_str_op(md->body, ==, test_md_18); + + ri = router_parse_entry_from_string(test_ri2, NULL, 0, 0, NULL, NULL); + + microdesc_free(md); + md = NULL; + md = dirvote_create_microdescriptor(ri, 18); + tt_str_op(md->body, ==, test_md2_18); + + microdesc_free(md); + md = NULL; + md = dirvote_create_microdescriptor(ri, 21); + tt_str_op(md->body, ==, test_md2_21); + tt_assert(ed25519_pubkey_eq(md->ed25519_identity_pkey, + &ri->signing_key_cert->signing_key)); + done: microdesc_free(md); routerinfo_free(ri); diff --git a/src/test/test_routerkeys.c b/src/test/test_routerkeys.c index 60b6bb5a72..26f9701f49 100644 --- a/src/test/test_routerkeys.c +++ b/src/test/test_routerkeys.c @@ -8,11 +8,17 @@ #include "or.h" #include "config.h" #include "router.h" +#include "routerkeys.h" #include "util.h" #include "crypto.h" - +#include "torcert.h" #include "test.h" +#ifdef _WIN32 +/* For mkdir() */ +#include +#endif + static void test_routerkeys_write_fingerprint(void *arg) { @@ -75,11 +81,543 @@ test_routerkeys_write_fingerprint(void *arg) tor_free(cp2); } +static void +test_routerkeys_ed_certs(void *args) +{ + (void)args; + ed25519_keypair_t kp1, kp2; + tor_cert_t *cert[2] = {NULL, NULL}; + tor_cert_t *parsed_cert[2] = {NULL, NULL}; + time_t now = 1412094534; + uint8_t *junk = NULL; + char *base64 = NULL; + + tt_int_op(0,==,ed25519_keypair_generate(&kp1, 0)); + tt_int_op(0,==,ed25519_keypair_generate(&kp2, 0)); + + for (int i = 0; i <= 1; ++i) { + uint32_t flags = i ? CERT_FLAG_INCLUDE_SIGNING_KEY : 0; + + cert[i] = tor_cert_create(&kp1, 5, &kp2.pubkey, now, 10000, flags); + tt_assert(cert[i]); + + tt_assert(cert[i]->sig_bad == 0); + tt_assert(cert[i]->sig_ok == 1); + tt_assert(cert[i]->cert_expired == 0); + tt_assert(cert[i]->cert_valid == 1); + tt_int_op(cert[i]->cert_type, ==, 5); + tt_mem_op(cert[i]->signed_key.pubkey, ==, &kp2.pubkey.pubkey, 32); + tt_mem_op(cert[i]->signing_key.pubkey, ==, &kp1.pubkey.pubkey, 32); + tt_int_op(cert[i]->signing_key_included, ==, i); + + tt_assert(cert[i]->encoded); + tt_int_op(cert[i]->encoded_len, ==, 104 + 36 * i); + tt_int_op(cert[i]->encoded[0], ==, 1); + tt_int_op(cert[i]->encoded[1], ==, 5); + + parsed_cert[i] = tor_cert_parse(cert[i]->encoded, cert[i]->encoded_len); + tt_assert(parsed_cert[i]); + tt_int_op(cert[i]->encoded_len, ==, parsed_cert[i]->encoded_len); + tt_mem_op(cert[i]->encoded, ==, parsed_cert[i]->encoded, + cert[i]->encoded_len); + tt_assert(parsed_cert[i]->sig_bad == 0); + tt_assert(parsed_cert[i]->sig_ok == 0); + tt_assert(parsed_cert[i]->cert_expired == 0); + tt_assert(parsed_cert[i]->cert_valid == 0); + + /* Expired */ + tt_int_op(tor_cert_checksig(parsed_cert[i], &kp1.pubkey, now + 30000), + <, 0); + tt_assert(parsed_cert[i]->cert_expired == 1); + parsed_cert[i]->cert_expired = 0; + + /* Wrong key */ + tt_int_op(tor_cert_checksig(parsed_cert[i], &kp2.pubkey, now), <, 0); + tt_assert(parsed_cert[i]->sig_bad== 1); + parsed_cert[i]->sig_bad = 0; + + /* Missing key */ + int ok = tor_cert_checksig(parsed_cert[i], NULL, now); + tt_int_op(ok < 0, ==, i == 0); + tt_assert(parsed_cert[i]->sig_bad == 0); + tt_assert(parsed_cert[i]->sig_ok == (i != 0)); + tt_assert(parsed_cert[i]->cert_valid == (i != 0)); + parsed_cert[i]->sig_bad = 0; + parsed_cert[i]->sig_ok = 0; + parsed_cert[i]->cert_valid = 0; + + /* Right key */ + tt_int_op(tor_cert_checksig(parsed_cert[i], &kp1.pubkey, now), ==, 0); + tt_assert(parsed_cert[i]->sig_bad == 0); + tt_assert(parsed_cert[i]->sig_ok == 1); + tt_assert(parsed_cert[i]->cert_expired == 0); + tt_assert(parsed_cert[i]->cert_valid == 1); + } + + /* Now try some junky certs. */ + /* - Truncated */ + tt_ptr_op(NULL, ==,tor_cert_parse(cert[0]->encoded, cert[0]->encoded_len-1)); + + /* - First byte modified */ + cert[0]->encoded[0] = 99; + tt_ptr_op(NULL, ==,tor_cert_parse(cert[0]->encoded, cert[0]->encoded_len)); + cert[0]->encoded[0] = 1; + + /* - Extra byte at the end*/ + junk = tor_malloc_zero(cert[0]->encoded_len + 1); + memcpy(junk, cert[0]->encoded, cert[0]->encoded_len); + tt_ptr_op(NULL, ==, tor_cert_parse(junk, cert[0]->encoded_len+1)); + + /* - Multiple signing key instances */ + tor_free(junk); + junk = tor_malloc_zero(104 + 36 * 2); + junk[0] = 1; /* version */ + junk[1] = 5; /* cert type */ + junk[6] = 1; /* key type */ + junk[39] = 2; /* n_extensions */ + junk[41] = 32; /* extlen */ + junk[42] = 4; /* exttype */ + junk[77] = 32; /* extlen */ + junk[78] = 4; /* exttype */ + tt_ptr_op(NULL, ==, tor_cert_parse(junk, 104 + 36 * 2)); + + done: + tor_cert_free(cert[0]); + tor_cert_free(cert[1]); + tor_cert_free(parsed_cert[0]); + tor_cert_free(parsed_cert[1]); + tor_free(junk); + tor_free(base64); +} + +static void +test_routerkeys_ed_key_create(void *arg) +{ + (void)arg; + tor_cert_t *cert = NULL; + ed25519_keypair_t *kp1 = NULL, *kp2 = NULL; + time_t now = time(NULL); + + /* This is a simple alias for 'make a new keypair' */ + kp1 = ed_key_new(NULL, 0, 0, 0, 0, &cert); + tt_assert(kp1); + + /* Create a new certificate signed by kp1. */ + kp2 = ed_key_new(kp1, INIT_ED_KEY_NEEDCERT, now, 3600, 4, &cert); + tt_assert(kp2); + tt_assert(cert); + tt_mem_op(&cert->signed_key, ==, &kp2->pubkey, sizeof(ed25519_public_key_t)); + tt_assert(! cert->signing_key_included); + + tt_int_op(cert->valid_until, >=, now); + tt_int_op(cert->valid_until, <=, now+7200); + + /* Create a new key-including certificate signed by kp1 */ + ed25519_keypair_free(kp2); + tor_cert_free(cert); + cert = NULL; kp2 = NULL; + kp2 = ed_key_new(kp1, (INIT_ED_KEY_NEEDCERT| + INIT_ED_KEY_INCLUDE_SIGNING_KEY_IN_CERT), + now, 3600, 4, &cert); + tt_assert(kp2); + tt_assert(cert); + tt_assert(cert->signing_key_included); + tt_mem_op(&cert->signed_key, ==, &kp2->pubkey, sizeof(ed25519_public_key_t)); + tt_mem_op(&cert->signing_key, ==, &kp1->pubkey,sizeof(ed25519_public_key_t)); + + done: + ed25519_keypair_free(kp1); + ed25519_keypair_free(kp2); + tor_cert_free(cert); +} + +static void +test_routerkeys_ed_key_init_basic(void *arg) +{ + (void) arg; + + tor_cert_t *cert = NULL, *cert2 = NULL; + ed25519_keypair_t *kp1 = NULL, *kp2 = NULL, *kp3 = NULL; + time_t now = time(NULL); + char *fname1 = tor_strdup(get_fname("test_ed_key_1")); + char *fname2 = tor_strdup(get_fname("test_ed_key_2")); + struct stat st; + + unlink(fname1); + unlink(fname2); + + /* Fail to load a key that isn't there. */ + kp1 = ed_key_init_from_file(fname1, 0, LOG_INFO, NULL, now, 0, 7, &cert); + tt_assert(kp1 == NULL); + tt_assert(cert == NULL); + + /* Create the key if requested to do so. */ + kp1 = ed_key_init_from_file(fname1, INIT_ED_KEY_CREATE, LOG_INFO, + NULL, now, 0, 7, &cert); + tt_assert(kp1 != NULL); + tt_assert(cert == NULL); + tt_int_op(stat(get_fname("test_ed_key_1_cert"), &st), <, 0); + tt_int_op(stat(get_fname("test_ed_key_1_secret_key"), &st), ==, 0); + + /* Fail to load if we say we need a cert */ + kp2 = ed_key_init_from_file(fname1, INIT_ED_KEY_NEEDCERT, LOG_INFO, + NULL, now, 0, 7, &cert); + tt_assert(kp2 == NULL); + + /* Fail to load if we say the wrong key type */ + kp2 = ed_key_init_from_file(fname1, 0, LOG_INFO, + NULL, now, 0, 6, &cert); + tt_assert(kp2 == NULL); + + /* Load successfully if we're not picky, whether we say "create" or not. */ + kp2 = ed_key_init_from_file(fname1, INIT_ED_KEY_CREATE, LOG_INFO, + NULL, now, 0, 7, &cert); + tt_assert(kp2 != NULL); + tt_assert(cert == NULL); + tt_mem_op(kp1, ==, kp2, sizeof(*kp1)); + ed25519_keypair_free(kp2); kp2 = NULL; + + kp2 = ed_key_init_from_file(fname1, 0, LOG_INFO, + NULL, now, 0, 7, &cert); + tt_assert(kp2 != NULL); + tt_assert(cert == NULL); + tt_mem_op(kp1, ==, kp2, sizeof(*kp1)); + ed25519_keypair_free(kp2); kp2 = NULL; + + /* Now create a key with a cert. */ + kp2 = ed_key_init_from_file(fname2, (INIT_ED_KEY_CREATE| + INIT_ED_KEY_NEEDCERT), + LOG_INFO, kp1, now, 7200, 7, &cert); + tt_assert(kp2 != NULL); + tt_assert(cert != NULL); + tt_mem_op(kp1, !=, kp2, sizeof(*kp1)); + tt_int_op(stat(get_fname("test_ed_key_2_cert"), &st), ==, 0); + tt_int_op(stat(get_fname("test_ed_key_2_secret_key"), &st), ==, 0); + + tt_assert(cert->cert_valid == 1); + tt_mem_op(&cert->signed_key, ==, &kp2->pubkey, 32); + + /* Now verify we can load the cert... */ + kp3 = ed_key_init_from_file(fname2, (INIT_ED_KEY_CREATE| + INIT_ED_KEY_NEEDCERT), + LOG_INFO, kp1, now, 7200, 7, &cert2); + tt_mem_op(kp2, ==, kp3, sizeof(*kp2)); + tt_mem_op(cert2->encoded, ==, cert->encoded, cert->encoded_len); + ed25519_keypair_free(kp3); kp3 = NULL; + tor_cert_free(cert2); cert2 = NULL; + + /* ... even without create... */ + kp3 = ed_key_init_from_file(fname2, INIT_ED_KEY_NEEDCERT, + LOG_INFO, kp1, now, 7200, 7, &cert2); + tt_mem_op(kp2, ==, kp3, sizeof(*kp2)); + tt_mem_op(cert2->encoded, ==, cert->encoded, cert->encoded_len); + ed25519_keypair_free(kp3); kp3 = NULL; + tor_cert_free(cert2); cert2 = NULL; + + /* ... but that we don't crash or anything if we say we don't want it. */ + kp3 = ed_key_init_from_file(fname2, INIT_ED_KEY_NEEDCERT, + LOG_INFO, kp1, now, 7200, 7, NULL); + tt_mem_op(kp2, ==, kp3, sizeof(*kp2)); + ed25519_keypair_free(kp3); kp3 = NULL; + + /* Fail if we're told the wrong signing key */ + kp3 = ed_key_init_from_file(fname2, INIT_ED_KEY_NEEDCERT, + LOG_INFO, kp2, now, 7200, 7, &cert2); + tt_assert(kp3 == NULL); + tt_assert(cert2 == NULL); + + done: + ed25519_keypair_free(kp1); + ed25519_keypair_free(kp2); + ed25519_keypair_free(kp3); + tor_cert_free(cert); + tor_cert_free(cert2); + tor_free(fname1); + tor_free(fname2); +} + +static void +test_routerkeys_ed_key_init_split(void *arg) +{ + (void) arg; + + tor_cert_t *cert = NULL; + ed25519_keypair_t *kp1 = NULL, *kp2 = NULL; + time_t now = time(NULL); + char *fname1 = tor_strdup(get_fname("test_ed_key_3")); + char *fname2 = tor_strdup(get_fname("test_ed_key_4")); + struct stat st; + const uint32_t flags = INIT_ED_KEY_SPLIT|INIT_ED_KEY_MISSING_SECRET_OK; + + unlink(fname1); + unlink(fname2); + + /* Can't load key that isn't there. */ + kp1 = ed_key_init_from_file(fname1, flags, LOG_INFO, NULL, now, 0, 7, &cert); + tt_assert(kp1 == NULL); + tt_assert(cert == NULL); + + /* Create a split key */ + kp1 = ed_key_init_from_file(fname1, flags|INIT_ED_KEY_CREATE, + LOG_INFO, NULL, now, 0, 7, &cert); + tt_assert(kp1 != NULL); + tt_assert(cert == NULL); + tt_int_op(stat(get_fname("test_ed_key_3_cert"), &st), <, 0); + tt_int_op(stat(get_fname("test_ed_key_3_secret_key"), &st), ==, 0); + tt_int_op(stat(get_fname("test_ed_key_3_public_key"), &st), ==, 0); + + /* Load it. */ + kp2 = ed_key_init_from_file(fname1, flags|INIT_ED_KEY_CREATE, + LOG_INFO, NULL, now, 0, 7, &cert); + tt_assert(kp2 != NULL); + tt_assert(cert == NULL); + tt_mem_op(kp1, ==, kp2, sizeof(*kp2)); + ed25519_keypair_free(kp2); kp2 = NULL; + + /* Okay, try killing the secret key and loading it. */ + unlink(get_fname("test_ed_key_3_secret_key")); + kp2 = ed_key_init_from_file(fname1, flags, + LOG_INFO, NULL, now, 0, 7, &cert); + tt_assert(kp2 != NULL); + tt_assert(cert == NULL); + tt_mem_op(&kp1->pubkey, ==, &kp2->pubkey, sizeof(kp2->pubkey)); + tt_assert(tor_mem_is_zero((char*)kp2->seckey.seckey, + sizeof(kp2->seckey.seckey))); + ed25519_keypair_free(kp2); kp2 = NULL; + + /* Even when we're told to "create", don't create if there's a public key */ + kp2 = ed_key_init_from_file(fname1, flags|INIT_ED_KEY_CREATE, + LOG_INFO, NULL, now, 0, 7, &cert); + tt_assert(kp2 != NULL); + tt_assert(cert == NULL); + tt_mem_op(&kp1->pubkey, ==, &kp2->pubkey, sizeof(kp2->pubkey)); + tt_assert(tor_mem_is_zero((char*)kp2->seckey.seckey, + sizeof(kp2->seckey.seckey))); + ed25519_keypair_free(kp2); kp2 = NULL; + + /* Make sure we fail on a tag mismatch, though */ + kp2 = ed_key_init_from_file(fname1, flags, + LOG_INFO, NULL, now, 0, 99, &cert); + tt_assert(kp2 == NULL); + + done: + ed25519_keypair_free(kp1); + ed25519_keypair_free(kp2); + tor_cert_free(cert); + tor_free(fname1); + tor_free(fname2); +} + +static void +test_routerkeys_ed_keys_init_all(void *arg) +{ + (void)arg; + char *dir = tor_strdup(get_fname("test_ed_keys_init_all")); + or_options_t *options = tor_malloc_zero(sizeof(or_options_t)); + time_t now = time(NULL); + ed25519_public_key_t id; + ed25519_keypair_t sign, auth; + tor_cert_t *link_cert = NULL; + + get_options_mutable()->ORPort_set = 1; + + crypto_pk_t *rsa = pk_generate(0); + + set_server_identity_key(rsa); + set_client_identity_key(rsa); + + router_initialize_tls_context(); + + options->SigningKeyLifetime = 30*86400; + options->TestingAuthKeyLifetime = 2*86400; + options->TestingLinkCertLifetime = 2*86400; + options->TestingSigningKeySlop = 2*86400; + options->TestingAuthKeySlop = 2*3600; + options->TestingLinkKeySlop = 2*3600; + +#ifdef _WIN32 + mkdir(dir); + mkdir(get_fname("test_ed_keys_init_all/keys")); +#else + mkdir(dir, 0700); + mkdir(get_fname("test_ed_keys_init_all/keys"), 0700); +#endif + + options->DataDirectory = dir; + + tt_int_op(0, ==, load_ed_keys(options, now)); + tt_int_op(0, ==, generate_ed_link_cert(options, now)); + tt_assert(get_master_identity_key()); + tt_assert(get_master_identity_key()); + tt_assert(get_master_signing_keypair()); + tt_assert(get_current_auth_keypair()); + tt_assert(get_master_signing_key_cert()); + tt_assert(get_current_link_cert_cert()); + tt_assert(get_current_auth_key_cert()); + memcpy(&id, get_master_identity_key(), sizeof(id)); + memcpy(&sign, get_master_signing_keypair(), sizeof(sign)); + memcpy(&auth, get_current_auth_keypair(), sizeof(auth)); + link_cert = tor_cert_dup(get_current_link_cert_cert()); + + /* Call load_ed_keys again, but nothing has changed. */ + tt_int_op(0, ==, load_ed_keys(options, now)); + tt_int_op(0, ==, generate_ed_link_cert(options, now)); + tt_mem_op(&id, ==, get_master_identity_key(), sizeof(id)); + tt_mem_op(&sign, ==, get_master_signing_keypair(), sizeof(sign)); + tt_mem_op(&auth, ==, get_current_auth_keypair(), sizeof(auth)); + tt_assert(tor_cert_eq(link_cert, get_current_link_cert_cert())); + + /* Force a reload: we make new link/auth keys. */ + routerkeys_free_all(); + tt_int_op(0, ==, load_ed_keys(options, now)); + tt_int_op(0, ==, generate_ed_link_cert(options, now)); + tt_mem_op(&id, ==, get_master_identity_key(), sizeof(id)); + tt_mem_op(&sign, ==, get_master_signing_keypair(), sizeof(sign)); + tt_assert(tor_cert_eq(link_cert, get_current_link_cert_cert())); + tt_mem_op(&auth, !=, get_current_auth_keypair(), sizeof(auth)); + tt_assert(get_master_signing_key_cert()); + tt_assert(get_current_link_cert_cert()); + tt_assert(get_current_auth_key_cert()); + tor_cert_free(link_cert); + link_cert = tor_cert_dup(get_current_link_cert_cert()); + memcpy(&auth, get_current_auth_keypair(), sizeof(auth)); + + /* Force a link/auth-key regeneration by advancing time. */ + tt_int_op(0, ==, load_ed_keys(options, now+3*86400)); + tt_int_op(0, ==, generate_ed_link_cert(options, now+3*86400)); + tt_mem_op(&id, ==, get_master_identity_key(), sizeof(id)); + tt_mem_op(&sign, ==, get_master_signing_keypair(), sizeof(sign)); + tt_assert(! tor_cert_eq(link_cert, get_current_link_cert_cert())); + tt_mem_op(&auth, !=, get_current_auth_keypair(), sizeof(auth)); + tt_assert(get_master_signing_key_cert()); + tt_assert(get_current_link_cert_cert()); + tt_assert(get_current_auth_key_cert()); + tor_cert_free(link_cert); + link_cert = tor_cert_dup(get_current_link_cert_cert()); + memcpy(&auth, get_current_auth_keypair(), sizeof(auth)); + + /* Force a signing-key regeneration by advancing time. */ + tt_int_op(0, ==, load_ed_keys(options, now+100*86400)); + tt_int_op(0, ==, generate_ed_link_cert(options, now+100*86400)); + tt_mem_op(&id, ==, get_master_identity_key(), sizeof(id)); + tt_mem_op(&sign, !=, get_master_signing_keypair(), sizeof(sign)); + tt_assert(! tor_cert_eq(link_cert, get_current_link_cert_cert())); + tt_mem_op(&auth, !=, get_current_auth_keypair(), sizeof(auth)); + tt_assert(get_master_signing_key_cert()); + tt_assert(get_current_link_cert_cert()); + tt_assert(get_current_auth_key_cert()); + memcpy(&sign, get_master_signing_keypair(), sizeof(sign)); + tor_cert_free(link_cert); + link_cert = tor_cert_dup(get_current_link_cert_cert()); + memcpy(&auth, get_current_auth_keypair(), sizeof(auth)); + + /* Demonstrate that we can start up with no secret identity key */ + routerkeys_free_all(); + unlink(get_fname("test_ed_keys_init_all/keys/" + "ed25519_master_id_secret_key")); + tt_int_op(0, ==, load_ed_keys(options, now)); + tt_int_op(0, ==, generate_ed_link_cert(options, now)); + tt_mem_op(&id, ==, get_master_identity_key(), sizeof(id)); + tt_mem_op(&sign, ==, get_master_signing_keypair(), sizeof(sign)); + tt_assert(! tor_cert_eq(link_cert, get_current_link_cert_cert())); + tt_mem_op(&auth, !=, get_current_auth_keypair(), sizeof(auth)); + tt_assert(get_master_signing_key_cert()); + tt_assert(get_current_link_cert_cert()); + tt_assert(get_current_auth_key_cert()); + + /* But we're in trouble if we have no id key and our signing key has + expired. */ + log_global_min_severity_ = LOG_ERR; /* Suppress warnings. + * XXX (better way to do this)? */ + routerkeys_free_all(); + tt_int_op(-1, ==, load_ed_keys(options, now+200*86400)); + + done: + tor_free(dir); + tor_free(options); + routerkeys_free_all(); +} + +static void +test_routerkeys_cross_certify_ntor(void *args) +{ + (void) args; + + tor_cert_t *cert = NULL; + curve25519_keypair_t onion_keys; + ed25519_public_key_t master_key; + ed25519_public_key_t onion_check_key; + time_t now = time(NULL); + int sign; + + tt_int_op(0, ==, ed25519_public_from_base64(&master_key, + "IamwritingthesetestsOnARainyAfternoonin2014")); + tt_int_op(0, ==, curve25519_keypair_generate(&onion_keys, 0)); + cert = make_ntor_onion_key_crosscert(&onion_keys, + &master_key, + now, 10000, + &sign); + tt_assert(cert); + tt_assert(sign == 0 || sign == 1); + tt_int_op(cert->cert_type, ==, CERT_TYPE_ONION_ID); + tt_int_op(1, ==, ed25519_pubkey_eq(&cert->signed_key, &master_key)); + tt_int_op(0, ==, ed25519_public_key_from_curve25519_public_key( + &onion_check_key, &onion_keys.pubkey, sign)); + tt_int_op(0, ==, tor_cert_checksig(cert, &onion_check_key, now)); + + done: + tor_cert_free(cert); +} + +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, ==, 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,>,0); + tt_int_op(n,==,52); + + crypto_pk_get_digest(id_key, digest); + tt_mem_op(buf,==,digest,20); + tt_mem_op(buf+20,==,master_key.pubkey,32); + + tt_int_op(0, ==, check_tap_onion_key_crosscert(cc, cc_len, + onion_key, &master_key, (uint8_t*)digest)); + + done: + tor_free(cc); +} + #define TEST(name, flags) \ { #name , test_routerkeys_ ## name, (flags), NULL, NULL } struct testcase_t routerkeys_tests[] = { TEST(write_fingerprint, TT_FORK), + TEST(ed_certs, TT_FORK), + TEST(ed_key_create, TT_FORK), + TEST(ed_key_init_basic, TT_FORK), + TEST(ed_key_init_split, TT_FORK), + TEST(ed_keys_init_all, TT_FORK), + TEST(cross_certify_ntor, 0), + TEST(cross_certify_tap, 0), END_OF_TESTCASES }; diff --git a/src/trunnel/ed25519_cert.c b/src/trunnel/ed25519_cert.c new file mode 100644 index 0000000000..2a84e4b163 --- /dev/null +++ b/src/trunnel/ed25519_cert.c @@ -0,0 +1,887 @@ +/* ed25519_cert.c -- generated by Trunnel v1.2. + * https://gitweb.torproject.org/trunnel.git + * You probably shouldn't edit this file. + */ +#include +#include "trunnel-impl.h" + +#include "ed25519_cert.h" + +#define TRUNNEL_SET_ERROR_CODE(obj) \ + do { \ + (obj)->trunnel_error_code_ = 1; \ + } while (0) + +#if defined(__COVERITY__) || defined(__clang_analyzer__) +/* If we're runnning a static analysis tool, we don't want it to complain + * that some of our remaining-bytes checks are dead-code. */ +int edcert_deadcode_dummy__ = 0; +#define OR_DEADCODE_DUMMY || edcert_deadcode_dummy__ +#else +#define OR_DEADCODE_DUMMY +#endif + +#define CHECK_REMAINING(nbytes, label) \ + do { \ + if (remaining < (nbytes) OR_DEADCODE_DUMMY) { \ + goto label; \ + } \ + } while (0) + +ed25519_cert_extension_t * +ed25519_cert_extension_new(void) +{ + ed25519_cert_extension_t *val = trunnel_calloc(1, sizeof(ed25519_cert_extension_t)); + if (NULL == val) + return NULL; + return val; +} + +/** Release all storage held inside 'obj', but do not free 'obj'. + */ +static void +ed25519_cert_extension_clear(ed25519_cert_extension_t *obj) +{ + (void) obj; + TRUNNEL_DYNARRAY_WIPE(&obj->un_unparsed); + TRUNNEL_DYNARRAY_CLEAR(&obj->un_unparsed); +} + +void +ed25519_cert_extension_free(ed25519_cert_extension_t *obj) +{ + if (obj == NULL) + return; + ed25519_cert_extension_clear(obj); + trunnel_memwipe(obj, sizeof(ed25519_cert_extension_t)); + trunnel_free_(obj); +} + +uint16_t +ed25519_cert_extension_get_ext_length(ed25519_cert_extension_t *inp) +{ + return inp->ext_length; +} +int +ed25519_cert_extension_set_ext_length(ed25519_cert_extension_t *inp, uint16_t val) +{ + inp->ext_length = val; + return 0; +} +uint8_t +ed25519_cert_extension_get_ext_type(ed25519_cert_extension_t *inp) +{ + return inp->ext_type; +} +int +ed25519_cert_extension_set_ext_type(ed25519_cert_extension_t *inp, uint8_t val) +{ + inp->ext_type = val; + return 0; +} +uint8_t +ed25519_cert_extension_get_ext_flags(ed25519_cert_extension_t *inp) +{ + return inp->ext_flags; +} +int +ed25519_cert_extension_set_ext_flags(ed25519_cert_extension_t *inp, uint8_t val) +{ + inp->ext_flags = val; + return 0; +} +size_t +ed25519_cert_extension_getlen_un_signing_key(const ed25519_cert_extension_t *inp) +{ + (void)inp; return 32; +} + +uint8_t +ed25519_cert_extension_get_un_signing_key(const ed25519_cert_extension_t *inp, size_t idx) +{ + trunnel_assert(idx < 32); + return inp->un_signing_key[idx]; +} + +int +ed25519_cert_extension_set_un_signing_key(ed25519_cert_extension_t *inp, size_t idx, uint8_t elt) +{ + trunnel_assert(idx < 32); + inp->un_signing_key[idx] = elt; + return 0; +} + +uint8_t * +ed25519_cert_extension_getarray_un_signing_key(ed25519_cert_extension_t *inp) +{ + return inp->un_signing_key; +} +size_t +ed25519_cert_extension_getlen_un_unparsed(const ed25519_cert_extension_t *inp) +{ + return TRUNNEL_DYNARRAY_LEN(&inp->un_unparsed); +} + +uint8_t +ed25519_cert_extension_get_un_unparsed(ed25519_cert_extension_t *inp, size_t idx) +{ + return TRUNNEL_DYNARRAY_GET(&inp->un_unparsed, idx); +} + +int +ed25519_cert_extension_set_un_unparsed(ed25519_cert_extension_t *inp, size_t idx, uint8_t elt) +{ + TRUNNEL_DYNARRAY_SET(&inp->un_unparsed, idx, elt); + return 0; +} +int +ed25519_cert_extension_add_un_unparsed(ed25519_cert_extension_t *inp, uint8_t elt) +{ + TRUNNEL_DYNARRAY_ADD(uint8_t, &inp->un_unparsed, elt, {}); + return 0; + trunnel_alloc_failed: + TRUNNEL_SET_ERROR_CODE(inp); + return -1; +} + +uint8_t * +ed25519_cert_extension_getarray_un_unparsed(ed25519_cert_extension_t *inp) +{ + return inp->un_unparsed.elts_; +} +int +ed25519_cert_extension_setlen_un_unparsed(ed25519_cert_extension_t *inp, size_t newlen) +{ + uint8_t *newptr; + newptr = trunnel_dynarray_setlen(&inp->un_unparsed.allocated_, + &inp->un_unparsed.n_, inp->un_unparsed.elts_, newlen, + sizeof(inp->un_unparsed.elts_[0]), (trunnel_free_fn_t) NULL, + &inp->trunnel_error_code_); + if (newptr == NULL) + goto trunnel_alloc_failed; + inp->un_unparsed.elts_ = newptr; + return 0; + trunnel_alloc_failed: + TRUNNEL_SET_ERROR_CODE(inp); + return -1; +} +const char * +ed25519_cert_extension_check(const ed25519_cert_extension_t *obj) +{ + if (obj == NULL) + return "Object was NULL"; + if (obj->trunnel_error_code_) + return "A set function failed on this object"; + switch (obj->ext_type) { + + case CERTEXT_SIGNED_WITH_KEY: + break; + + default: + break; + } + return NULL; +} + +ssize_t +ed25519_cert_extension_encoded_len(const ed25519_cert_extension_t *obj) +{ + ssize_t result = 0; + + if (NULL != ed25519_cert_extension_check(obj)) + return -1; + + + /* Length of u16 ext_length */ + result += 2; + + /* Length of u8 ext_type */ + result += 1; + + /* Length of u8 ext_flags */ + result += 1; + switch (obj->ext_type) { + + case CERTEXT_SIGNED_WITH_KEY: + + /* Length of u8 un_signing_key[32] */ + result += 32; + break; + + default: + + /* Length of u8 un_unparsed[] */ + result += TRUNNEL_DYNARRAY_LEN(&obj->un_unparsed); + break; + } + return result; +} +int +ed25519_cert_extension_clear_errors(ed25519_cert_extension_t *obj) +{ + int r = obj->trunnel_error_code_; + obj->trunnel_error_code_ = 0; + return r; +} +ssize_t +ed25519_cert_extension_encode(uint8_t *output, const size_t avail, const ed25519_cert_extension_t *obj) +{ + ssize_t result = 0; + size_t written = 0; + uint8_t *ptr = output; + const char *msg; +#ifdef TRUNNEL_CHECK_ENCODED_LEN + const ssize_t encoded_len = ed25519_cert_extension_encoded_len(obj); +#endif + + uint8_t *backptr_ext_length = NULL; + + if (NULL != (msg = ed25519_cert_extension_check(obj))) + goto check_failed; + +#ifdef TRUNNEL_CHECK_ENCODED_LEN + trunnel_assert(encoded_len >= 0); +#endif + + /* Encode u16 ext_length */ + backptr_ext_length = ptr; + trunnel_assert(written <= avail); + if (avail - written < 2) + goto truncated; + trunnel_set_uint16(ptr, trunnel_htons(obj->ext_length)); + written += 2; ptr += 2; + + /* Encode u8 ext_type */ + trunnel_assert(written <= avail); + if (avail - written < 1) + goto truncated; + trunnel_set_uint8(ptr, (obj->ext_type)); + written += 1; ptr += 1; + + /* Encode u8 ext_flags */ + trunnel_assert(written <= avail); + if (avail - written < 1) + goto truncated; + trunnel_set_uint8(ptr, (obj->ext_flags)); + written += 1; ptr += 1; + { + size_t written_before_union = written; + + /* Encode union un[ext_type] */ + trunnel_assert(written <= avail); + switch (obj->ext_type) { + + case CERTEXT_SIGNED_WITH_KEY: + + /* Encode u8 un_signing_key[32] */ + trunnel_assert(written <= avail); + if (avail - written < 32) + goto truncated; + memcpy(ptr, obj->un_signing_key, 32); + written += 32; ptr += 32; + break; + + default: + + /* Encode u8 un_unparsed[] */ + { + size_t elt_len = TRUNNEL_DYNARRAY_LEN(&obj->un_unparsed); + trunnel_assert(written <= avail); + if (avail - written < elt_len) + goto truncated; + memcpy(ptr, obj->un_unparsed.elts_, elt_len); + written += elt_len; ptr += elt_len; + } + break; + } + /* Write the length field back to ext_length */ + trunnel_assert(written >= written_before_union); +#if UINT16_MAX < SIZE_MAX + if (written - written_before_union > UINT16_MAX) + goto check_failed; +#endif + trunnel_set_uint16(backptr_ext_length, trunnel_htons(written - written_before_union)); + } + + + trunnel_assert(ptr == output + written); +#ifdef TRUNNEL_CHECK_ENCODED_LEN + { + trunnel_assert(encoded_len >= 0); + trunnel_assert((size_t)encoded_len == written); + } + +#endif + + return written; + + truncated: + result = -2; + goto fail; + check_failed: + (void)msg; + result = -1; + goto fail; + fail: + trunnel_assert(result < 0); + return result; +} + +/** As ed25519_cert_extension_parse(), but do not allocate the output + * object. + */ +static ssize_t +ed25519_cert_extension_parse_into(ed25519_cert_extension_t *obj, const uint8_t *input, const size_t len_in) +{ + const uint8_t *ptr = input; + size_t remaining = len_in; + ssize_t result = 0; + (void)result; + + /* Parse u16 ext_length */ + CHECK_REMAINING(2, truncated); + obj->ext_length = trunnel_ntohs(trunnel_get_uint16(ptr)); + remaining -= 2; ptr += 2; + + /* Parse u8 ext_type */ + CHECK_REMAINING(1, truncated); + obj->ext_type = (trunnel_get_uint8(ptr)); + remaining -= 1; ptr += 1; + + /* Parse u8 ext_flags */ + CHECK_REMAINING(1, truncated); + obj->ext_flags = (trunnel_get_uint8(ptr)); + remaining -= 1; ptr += 1; + { + size_t remaining_after; + CHECK_REMAINING(obj->ext_length, truncated); + remaining_after = remaining - obj->ext_length; + remaining = obj->ext_length; + + /* Parse union un[ext_type] */ + switch (obj->ext_type) { + + case CERTEXT_SIGNED_WITH_KEY: + + /* Parse u8 un_signing_key[32] */ + CHECK_REMAINING(32, fail); + memcpy(obj->un_signing_key, ptr, 32); + remaining -= 32; ptr += 32; + break; + + default: + + /* Parse u8 un_unparsed[] */ + TRUNNEL_DYNARRAY_EXPAND(uint8_t, &obj->un_unparsed, remaining, {}); + obj->un_unparsed.n_ = remaining; + memcpy(obj->un_unparsed.elts_, ptr, remaining); + ptr += remaining; remaining -= remaining; + break; + } + if (remaining != 0) + goto fail; + remaining = remaining_after; + } + trunnel_assert(ptr + remaining == input + len_in); + return len_in - remaining; + + truncated: + return -2; + trunnel_alloc_failed: + return -1; + fail: + result = -1; + return result; +} + +ssize_t +ed25519_cert_extension_parse(ed25519_cert_extension_t **output, const uint8_t *input, const size_t len_in) +{ + ssize_t result; + *output = ed25519_cert_extension_new(); + if (NULL == *output) + return -1; + result = ed25519_cert_extension_parse_into(*output, input, len_in); + if (result < 0) { + ed25519_cert_extension_free(*output); + *output = NULL; + } + return result; +} +ed25519_cert_t * +ed25519_cert_new(void) +{ + ed25519_cert_t *val = trunnel_calloc(1, sizeof(ed25519_cert_t)); + if (NULL == val) + return NULL; + val->version = 1; + return val; +} + +/** Release all storage held inside 'obj', but do not free 'obj'. + */ +static void +ed25519_cert_clear(ed25519_cert_t *obj) +{ + (void) obj; + { + + unsigned idx; + for (idx = 0; idx < TRUNNEL_DYNARRAY_LEN(&obj->ext); ++idx) { + ed25519_cert_extension_free(TRUNNEL_DYNARRAY_GET(&obj->ext, idx)); + } + } + TRUNNEL_DYNARRAY_WIPE(&obj->ext); + TRUNNEL_DYNARRAY_CLEAR(&obj->ext); +} + +void +ed25519_cert_free(ed25519_cert_t *obj) +{ + if (obj == NULL) + return; + ed25519_cert_clear(obj); + trunnel_memwipe(obj, sizeof(ed25519_cert_t)); + trunnel_free_(obj); +} + +uint8_t +ed25519_cert_get_version(ed25519_cert_t *inp) +{ + return inp->version; +} +int +ed25519_cert_set_version(ed25519_cert_t *inp, uint8_t val) +{ + if (! ((val == 1))) { + TRUNNEL_SET_ERROR_CODE(inp); + return -1; + } + inp->version = val; + return 0; +} +uint8_t +ed25519_cert_get_cert_type(ed25519_cert_t *inp) +{ + return inp->cert_type; +} +int +ed25519_cert_set_cert_type(ed25519_cert_t *inp, uint8_t val) +{ + inp->cert_type = val; + return 0; +} +uint32_t +ed25519_cert_get_exp_field(ed25519_cert_t *inp) +{ + return inp->exp_field; +} +int +ed25519_cert_set_exp_field(ed25519_cert_t *inp, uint32_t val) +{ + inp->exp_field = val; + return 0; +} +uint8_t +ed25519_cert_get_cert_key_type(ed25519_cert_t *inp) +{ + return inp->cert_key_type; +} +int +ed25519_cert_set_cert_key_type(ed25519_cert_t *inp, uint8_t val) +{ + inp->cert_key_type = val; + return 0; +} +size_t +ed25519_cert_getlen_certified_key(const ed25519_cert_t *inp) +{ + (void)inp; return 32; +} + +uint8_t +ed25519_cert_get_certified_key(const ed25519_cert_t *inp, size_t idx) +{ + trunnel_assert(idx < 32); + return inp->certified_key[idx]; +} + +int +ed25519_cert_set_certified_key(ed25519_cert_t *inp, size_t idx, uint8_t elt) +{ + trunnel_assert(idx < 32); + inp->certified_key[idx] = elt; + return 0; +} + +uint8_t * +ed25519_cert_getarray_certified_key(ed25519_cert_t *inp) +{ + return inp->certified_key; +} +uint8_t +ed25519_cert_get_n_extensions(ed25519_cert_t *inp) +{ + return inp->n_extensions; +} +int +ed25519_cert_set_n_extensions(ed25519_cert_t *inp, uint8_t val) +{ + inp->n_extensions = val; + return 0; +} +size_t +ed25519_cert_getlen_ext(const ed25519_cert_t *inp) +{ + return TRUNNEL_DYNARRAY_LEN(&inp->ext); +} + +struct ed25519_cert_extension_st * +ed25519_cert_get_ext(ed25519_cert_t *inp, size_t idx) +{ + return TRUNNEL_DYNARRAY_GET(&inp->ext, idx); +} + +int +ed25519_cert_set_ext(ed25519_cert_t *inp, size_t idx, struct ed25519_cert_extension_st * elt) +{ + ed25519_cert_extension_t *oldval = TRUNNEL_DYNARRAY_GET(&inp->ext, idx); + if (oldval && oldval != elt) + ed25519_cert_extension_free(oldval); + return ed25519_cert_set0_ext(inp, idx, elt); +} +int +ed25519_cert_set0_ext(ed25519_cert_t *inp, size_t idx, struct ed25519_cert_extension_st * elt) +{ + TRUNNEL_DYNARRAY_SET(&inp->ext, idx, elt); + return 0; +} +int +ed25519_cert_add_ext(ed25519_cert_t *inp, struct ed25519_cert_extension_st * elt) +{ +#if SIZE_MAX >= UINT8_MAX + if (inp->ext.n_ == UINT8_MAX) + goto trunnel_alloc_failed; +#endif + TRUNNEL_DYNARRAY_ADD(struct ed25519_cert_extension_st *, &inp->ext, elt, {}); + return 0; + trunnel_alloc_failed: + TRUNNEL_SET_ERROR_CODE(inp); + return -1; +} + +struct ed25519_cert_extension_st * * +ed25519_cert_getarray_ext(ed25519_cert_t *inp) +{ + return inp->ext.elts_; +} +int +ed25519_cert_setlen_ext(ed25519_cert_t *inp, size_t newlen) +{ + struct ed25519_cert_extension_st * *newptr; +#if UINT8_MAX < SIZE_MAX + if (newlen > UINT8_MAX) + goto trunnel_alloc_failed; +#endif + newptr = trunnel_dynarray_setlen(&inp->ext.allocated_, + &inp->ext.n_, inp->ext.elts_, newlen, + sizeof(inp->ext.elts_[0]), (trunnel_free_fn_t) ed25519_cert_extension_free, + &inp->trunnel_error_code_); + if (newptr == NULL) + goto trunnel_alloc_failed; + inp->ext.elts_ = newptr; + return 0; + trunnel_alloc_failed: + TRUNNEL_SET_ERROR_CODE(inp); + return -1; +} +size_t +ed25519_cert_getlen_signature(const ed25519_cert_t *inp) +{ + (void)inp; return 64; +} + +uint8_t +ed25519_cert_get_signature(const ed25519_cert_t *inp, size_t idx) +{ + trunnel_assert(idx < 64); + return inp->signature[idx]; +} + +int +ed25519_cert_set_signature(ed25519_cert_t *inp, size_t idx, uint8_t elt) +{ + trunnel_assert(idx < 64); + inp->signature[idx] = elt; + return 0; +} + +uint8_t * +ed25519_cert_getarray_signature(ed25519_cert_t *inp) +{ + return inp->signature; +} +const char * +ed25519_cert_check(const ed25519_cert_t *obj) +{ + if (obj == NULL) + return "Object was NULL"; + if (obj->trunnel_error_code_) + return "A set function failed on this object"; + if (! (obj->version == 1)) + return "Integer out of bounds"; + { + const char *msg; + + unsigned idx; + for (idx = 0; idx < TRUNNEL_DYNARRAY_LEN(&obj->ext); ++idx) { + if (NULL != (msg = ed25519_cert_extension_check(TRUNNEL_DYNARRAY_GET(&obj->ext, idx)))) + return msg; + } + } + if (TRUNNEL_DYNARRAY_LEN(&obj->ext) != obj->n_extensions) + return "Length mismatch for ext"; + return NULL; +} + +ssize_t +ed25519_cert_encoded_len(const ed25519_cert_t *obj) +{ + ssize_t result = 0; + + if (NULL != ed25519_cert_check(obj)) + return -1; + + + /* Length of u8 version IN [1] */ + result += 1; + + /* Length of u8 cert_type */ + result += 1; + + /* Length of u32 exp_field */ + result += 4; + + /* Length of u8 cert_key_type */ + result += 1; + + /* Length of u8 certified_key[32] */ + result += 32; + + /* Length of u8 n_extensions */ + result += 1; + + /* Length of struct ed25519_cert_extension ext[n_extensions] */ + { + + unsigned idx; + for (idx = 0; idx < TRUNNEL_DYNARRAY_LEN(&obj->ext); ++idx) { + result += ed25519_cert_extension_encoded_len(TRUNNEL_DYNARRAY_GET(&obj->ext, idx)); + } + } + + /* Length of u8 signature[64] */ + result += 64; + return result; +} +int +ed25519_cert_clear_errors(ed25519_cert_t *obj) +{ + int r = obj->trunnel_error_code_; + obj->trunnel_error_code_ = 0; + return r; +} +ssize_t +ed25519_cert_encode(uint8_t *output, const size_t avail, const ed25519_cert_t *obj) +{ + ssize_t result = 0; + size_t written = 0; + uint8_t *ptr = output; + const char *msg; +#ifdef TRUNNEL_CHECK_ENCODED_LEN + const ssize_t encoded_len = ed25519_cert_encoded_len(obj); +#endif + + if (NULL != (msg = ed25519_cert_check(obj))) + goto check_failed; + +#ifdef TRUNNEL_CHECK_ENCODED_LEN + trunnel_assert(encoded_len >= 0); +#endif + + /* Encode u8 version IN [1] */ + trunnel_assert(written <= avail); + if (avail - written < 1) + goto truncated; + trunnel_set_uint8(ptr, (obj->version)); + written += 1; ptr += 1; + + /* Encode u8 cert_type */ + trunnel_assert(written <= avail); + if (avail - written < 1) + goto truncated; + trunnel_set_uint8(ptr, (obj->cert_type)); + written += 1; ptr += 1; + + /* Encode u32 exp_field */ + trunnel_assert(written <= avail); + if (avail - written < 4) + goto truncated; + trunnel_set_uint32(ptr, trunnel_htonl(obj->exp_field)); + written += 4; ptr += 4; + + /* Encode u8 cert_key_type */ + trunnel_assert(written <= avail); + if (avail - written < 1) + goto truncated; + trunnel_set_uint8(ptr, (obj->cert_key_type)); + written += 1; ptr += 1; + + /* Encode u8 certified_key[32] */ + trunnel_assert(written <= avail); + if (avail - written < 32) + goto truncated; + memcpy(ptr, obj->certified_key, 32); + written += 32; ptr += 32; + + /* Encode u8 n_extensions */ + trunnel_assert(written <= avail); + if (avail - written < 1) + goto truncated; + trunnel_set_uint8(ptr, (obj->n_extensions)); + written += 1; ptr += 1; + + /* Encode struct ed25519_cert_extension ext[n_extensions] */ + { + + unsigned idx; + for (idx = 0; idx < TRUNNEL_DYNARRAY_LEN(&obj->ext); ++idx) { + trunnel_assert(written <= avail); + result = ed25519_cert_extension_encode(ptr, avail - written, TRUNNEL_DYNARRAY_GET(&obj->ext, idx)); + if (result < 0) + goto fail; /* XXXXXXX !*/ + written += result; ptr += result; + } + } + + /* Encode u8 signature[64] */ + trunnel_assert(written <= avail); + if (avail - written < 64) + goto truncated; + memcpy(ptr, obj->signature, 64); + written += 64; ptr += 64; + + + trunnel_assert(ptr == output + written); +#ifdef TRUNNEL_CHECK_ENCODED_LEN + { + trunnel_assert(encoded_len >= 0); + trunnel_assert((size_t)encoded_len == written); + } + +#endif + + return written; + + truncated: + result = -2; + goto fail; + check_failed: + (void)msg; + result = -1; + goto fail; + fail: + trunnel_assert(result < 0); + return result; +} + +/** As ed25519_cert_parse(), but do not allocate the output object. + */ +static ssize_t +ed25519_cert_parse_into(ed25519_cert_t *obj, const uint8_t *input, const size_t len_in) +{ + const uint8_t *ptr = input; + size_t remaining = len_in; + ssize_t result = 0; + (void)result; + + /* Parse u8 version IN [1] */ + CHECK_REMAINING(1, truncated); + obj->version = (trunnel_get_uint8(ptr)); + remaining -= 1; ptr += 1; + if (! (obj->version == 1)) + goto fail; + + /* Parse u8 cert_type */ + CHECK_REMAINING(1, truncated); + obj->cert_type = (trunnel_get_uint8(ptr)); + remaining -= 1; ptr += 1; + + /* Parse u32 exp_field */ + CHECK_REMAINING(4, truncated); + obj->exp_field = trunnel_ntohl(trunnel_get_uint32(ptr)); + remaining -= 4; ptr += 4; + + /* Parse u8 cert_key_type */ + CHECK_REMAINING(1, truncated); + obj->cert_key_type = (trunnel_get_uint8(ptr)); + remaining -= 1; ptr += 1; + + /* Parse u8 certified_key[32] */ + CHECK_REMAINING(32, truncated); + memcpy(obj->certified_key, ptr, 32); + remaining -= 32; ptr += 32; + + /* Parse u8 n_extensions */ + CHECK_REMAINING(1, truncated); + obj->n_extensions = (trunnel_get_uint8(ptr)); + remaining -= 1; ptr += 1; + + /* Parse struct ed25519_cert_extension ext[n_extensions] */ + TRUNNEL_DYNARRAY_EXPAND(ed25519_cert_extension_t *, &obj->ext, obj->n_extensions, {}); + { + ed25519_cert_extension_t * elt; + unsigned idx; + for (idx = 0; idx < obj->n_extensions; ++idx) { + result = ed25519_cert_extension_parse(&elt, ptr, remaining); + if (result < 0) + goto relay_fail; + trunnel_assert((size_t)result <= remaining); + remaining -= result; ptr += result; + TRUNNEL_DYNARRAY_ADD(ed25519_cert_extension_t *, &obj->ext, elt, {ed25519_cert_extension_free(elt);}); + } + } + + /* Parse u8 signature[64] */ + CHECK_REMAINING(64, truncated); + memcpy(obj->signature, ptr, 64); + remaining -= 64; ptr += 64; + trunnel_assert(ptr + remaining == input + len_in); + return len_in - remaining; + + truncated: + return -2; + relay_fail: + if (result >= 0) result = -1; + return result; + trunnel_alloc_failed: + return -1; + fail: + result = -1; + return result; +} + +ssize_t +ed25519_cert_parse(ed25519_cert_t **output, const uint8_t *input, const size_t len_in) +{ + ssize_t result; + *output = ed25519_cert_new(); + if (NULL == *output) + return -1; + result = ed25519_cert_parse_into(*output, input, len_in); + if (result < 0) { + ed25519_cert_free(*output); + *output = NULL; + } + return result; +} diff --git a/src/trunnel/ed25519_cert.h b/src/trunnel/ed25519_cert.h new file mode 100644 index 0000000000..3ddf95ef9f --- /dev/null +++ b/src/trunnel/ed25519_cert.h @@ -0,0 +1,288 @@ +/* ed25519_cert.h -- generated by by Trunnel v1.2. + * https://gitweb.torproject.org/trunnel.git + * You probably shouldn't edit this file. + */ +#ifndef TRUNNEL_ED25519_CERT_H +#define TRUNNEL_ED25519_CERT_H + +#include +#include "trunnel.h" + +#define CERTEXT_SIGNED_WITH_KEY 4 +#define CERTEXT_FLAG_AFFECTS_VALIDATION 1 +#if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_ED25519_CERT_EXTENSION) +struct ed25519_cert_extension_st { + uint16_t ext_length; + uint8_t ext_type; + uint8_t ext_flags; + uint8_t un_signing_key[32]; + TRUNNEL_DYNARRAY_HEAD(, uint8_t) un_unparsed; + uint8_t trunnel_error_code_; +}; +#endif +typedef struct ed25519_cert_extension_st ed25519_cert_extension_t; +#if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_ED25519_CERT) +struct ed25519_cert_st { + uint8_t version; + uint8_t cert_type; + uint32_t exp_field; + uint8_t cert_key_type; + uint8_t certified_key[32]; + uint8_t n_extensions; + TRUNNEL_DYNARRAY_HEAD(, struct ed25519_cert_extension_st *) ext; + uint8_t signature[64]; + uint8_t trunnel_error_code_; +}; +#endif +typedef struct ed25519_cert_st ed25519_cert_t; +/** Return a newly allocated ed25519_cert_extension with all elements + * set to zero. + */ +ed25519_cert_extension_t *ed25519_cert_extension_new(void); +/** Release all storage held by the ed25519_cert_extension in + * 'victim'. (Do nothing if 'victim' is NULL.) + */ +void ed25519_cert_extension_free(ed25519_cert_extension_t *victim); +/** Try to parse a ed25519_cert_extension from the buffer in 'input', + * using up to 'len_in' bytes from the input buffer. On success, + * return the number of bytes consumed and set *output to the newly + * allocated ed25519_cert_extension_t. On failure, return -2 if the + * input appears truncated, and -1 if the input is otherwise invalid. + */ +ssize_t ed25519_cert_extension_parse(ed25519_cert_extension_t **output, const uint8_t *input, const size_t len_in); +/** Return the number of bytes we expect to need to encode the + * ed25519_cert_extension in 'obj'. On failure, return a negative + * value. Note that this value may be an overestimate, and can even be + * an underestimate for certain unencodeable objects. + */ +ssize_t ed25519_cert_extension_encoded_len(const ed25519_cert_extension_t *obj); +/** Try to encode the ed25519_cert_extension from 'input' into the + * buffer at 'output', using up to 'avail' bytes of the output buffer. + * On success, return the number of bytes used. On failure, return -2 + * if the buffer was not long enough, and -1 if the input was invalid. + */ +ssize_t ed25519_cert_extension_encode(uint8_t *output, const size_t avail, const ed25519_cert_extension_t *input); +/** Check whether the internal state of the ed25519_cert_extension in + * 'obj' is consistent. Return NULL if it is, and a short message if + * it is not. + */ +const char *ed25519_cert_extension_check(const ed25519_cert_extension_t *obj); +/** Clear any errors that were set on the object 'obj' by its setter + * functions. Return true iff errors were cleared. + */ +int ed25519_cert_extension_clear_errors(ed25519_cert_extension_t *obj); +/** Return the value of the ext_length field of the + * ed25519_cert_extension_t in 'inp' + */ +uint16_t ed25519_cert_extension_get_ext_length(ed25519_cert_extension_t *inp); +/** Set the value of the ext_length field of the + * ed25519_cert_extension_t in 'inp' to 'val'. Return 0 on success; + * return -1 and set the error code on 'inp' on failure. + */ +int ed25519_cert_extension_set_ext_length(ed25519_cert_extension_t *inp, uint16_t val); +/** Return the value of the ext_type field of the + * ed25519_cert_extension_t in 'inp' + */ +uint8_t ed25519_cert_extension_get_ext_type(ed25519_cert_extension_t *inp); +/** Set the value of the ext_type field of the + * ed25519_cert_extension_t in 'inp' to 'val'. Return 0 on success; + * return -1 and set the error code on 'inp' on failure. + */ +int ed25519_cert_extension_set_ext_type(ed25519_cert_extension_t *inp, uint8_t val); +/** Return the value of the ext_flags field of the + * ed25519_cert_extension_t in 'inp' + */ +uint8_t ed25519_cert_extension_get_ext_flags(ed25519_cert_extension_t *inp); +/** Set the value of the ext_flags field of the + * ed25519_cert_extension_t in 'inp' to 'val'. Return 0 on success; + * return -1 and set the error code on 'inp' on failure. + */ +int ed25519_cert_extension_set_ext_flags(ed25519_cert_extension_t *inp, uint8_t val); +/** Return the (constant) length of the array holding the + * un_signing_key field of the ed25519_cert_extension_t in 'inp'. + */ +size_t ed25519_cert_extension_getlen_un_signing_key(const ed25519_cert_extension_t *inp); +/** Return the element at position 'idx' of the fixed array field + * un_signing_key of the ed25519_cert_extension_t in 'inp'. + */ +uint8_t ed25519_cert_extension_get_un_signing_key(const ed25519_cert_extension_t *inp, size_t idx); +/** Change the element at position 'idx' of the fixed array field + * un_signing_key of the ed25519_cert_extension_t in 'inp', so that it + * will hold the value 'elt'. + */ +int ed25519_cert_extension_set_un_signing_key(ed25519_cert_extension_t *inp, size_t idx, uint8_t elt); +/** Return a pointer to the 32-element array field un_signing_key of + * 'inp'. + */ +uint8_t * ed25519_cert_extension_getarray_un_signing_key(ed25519_cert_extension_t *inp); +/** Return the length of the dynamic array holding the un_unparsed + * field of the ed25519_cert_extension_t in 'inp'. + */ +size_t ed25519_cert_extension_getlen_un_unparsed(const ed25519_cert_extension_t *inp); +/** Return the element at position 'idx' of the dynamic array field + * un_unparsed of the ed25519_cert_extension_t in 'inp'. + */ +uint8_t ed25519_cert_extension_get_un_unparsed(ed25519_cert_extension_t *inp, size_t idx); +/** Change the element at position 'idx' of the dynamic array field + * un_unparsed of the ed25519_cert_extension_t in 'inp', so that it + * will hold the value 'elt'. + */ +int ed25519_cert_extension_set_un_unparsed(ed25519_cert_extension_t *inp, size_t idx, uint8_t elt); +/** Append a new element 'elt' to the dynamic array field un_unparsed + * of the ed25519_cert_extension_t in 'inp'. + */ +int ed25519_cert_extension_add_un_unparsed(ed25519_cert_extension_t *inp, uint8_t elt); +/** Return a pointer to the variable-length array field un_unparsed of + * 'inp'. + */ +uint8_t * ed25519_cert_extension_getarray_un_unparsed(ed25519_cert_extension_t *inp); +/** Change the length of the variable-length array field un_unparsed + * of 'inp' to 'newlen'.Fill extra elements with 0. Return 0 on + * success; return -1 and set the error code on 'inp' on failure. + */ +int ed25519_cert_extension_setlen_un_unparsed(ed25519_cert_extension_t *inp, size_t newlen); +/** Return a newly allocated ed25519_cert with all elements set to + * zero. + */ +ed25519_cert_t *ed25519_cert_new(void); +/** Release all storage held by the ed25519_cert in 'victim'. (Do + * nothing if 'victim' is NULL.) + */ +void ed25519_cert_free(ed25519_cert_t *victim); +/** Try to parse a ed25519_cert from the buffer in 'input', using up + * to 'len_in' bytes from the input buffer. On success, return the + * number of bytes consumed and set *output to the newly allocated + * ed25519_cert_t. On failure, return -2 if the input appears + * truncated, and -1 if the input is otherwise invalid. + */ +ssize_t ed25519_cert_parse(ed25519_cert_t **output, const uint8_t *input, const size_t len_in); +/** Return the number of bytes we expect to need to encode the + * ed25519_cert in 'obj'. On failure, return a negative value. Note + * that this value may be an overestimate, and can even be an + * underestimate for certain unencodeable objects. + */ +ssize_t ed25519_cert_encoded_len(const ed25519_cert_t *obj); +/** Try to encode the ed25519_cert from 'input' into the buffer at + * 'output', using up to 'avail' bytes of the output buffer. On + * success, return the number of bytes used. On failure, return -2 if + * the buffer was not long enough, and -1 if the input was invalid. + */ +ssize_t ed25519_cert_encode(uint8_t *output, const size_t avail, const ed25519_cert_t *input); +/** Check whether the internal state of the ed25519_cert in 'obj' is + * consistent. Return NULL if it is, and a short message if it is not. + */ +const char *ed25519_cert_check(const ed25519_cert_t *obj); +/** Clear any errors that were set on the object 'obj' by its setter + * functions. Return true iff errors were cleared. + */ +int ed25519_cert_clear_errors(ed25519_cert_t *obj); +/** Return the value of the version field of the ed25519_cert_t in + * 'inp' + */ +uint8_t ed25519_cert_get_version(ed25519_cert_t *inp); +/** Set the value of the version field of the ed25519_cert_t in 'inp' + * to 'val'. Return 0 on success; return -1 and set the error code on + * 'inp' on failure. + */ +int ed25519_cert_set_version(ed25519_cert_t *inp, uint8_t val); +/** Return the value of the cert_type field of the ed25519_cert_t in + * 'inp' + */ +uint8_t ed25519_cert_get_cert_type(ed25519_cert_t *inp); +/** Set the value of the cert_type field of the ed25519_cert_t in + * 'inp' to 'val'. Return 0 on success; return -1 and set the error + * code on 'inp' on failure. + */ +int ed25519_cert_set_cert_type(ed25519_cert_t *inp, uint8_t val); +/** Return the value of the exp_field field of the ed25519_cert_t in + * 'inp' + */ +uint32_t ed25519_cert_get_exp_field(ed25519_cert_t *inp); +/** Set the value of the exp_field field of the ed25519_cert_t in + * 'inp' to 'val'. Return 0 on success; return -1 and set the error + * code on 'inp' on failure. + */ +int ed25519_cert_set_exp_field(ed25519_cert_t *inp, uint32_t val); +/** Return the value of the cert_key_type field of the ed25519_cert_t + * in 'inp' + */ +uint8_t ed25519_cert_get_cert_key_type(ed25519_cert_t *inp); +/** Set the value of the cert_key_type field of the ed25519_cert_t in + * 'inp' to 'val'. Return 0 on success; return -1 and set the error + * code on 'inp' on failure. + */ +int ed25519_cert_set_cert_key_type(ed25519_cert_t *inp, uint8_t val); +/** Return the (constant) length of the array holding the + * certified_key field of the ed25519_cert_t in 'inp'. + */ +size_t ed25519_cert_getlen_certified_key(const ed25519_cert_t *inp); +/** Return the element at position 'idx' of the fixed array field + * certified_key of the ed25519_cert_t in 'inp'. + */ +uint8_t ed25519_cert_get_certified_key(const ed25519_cert_t *inp, size_t idx); +/** Change the element at position 'idx' of the fixed array field + * certified_key of the ed25519_cert_t in 'inp', so that it will hold + * the value 'elt'. + */ +int ed25519_cert_set_certified_key(ed25519_cert_t *inp, size_t idx, uint8_t elt); +/** Return a pointer to the 32-element array field certified_key of + * 'inp'. + */ +uint8_t * ed25519_cert_getarray_certified_key(ed25519_cert_t *inp); +/** Return the value of the n_extensions field of the ed25519_cert_t + * in 'inp' + */ +uint8_t ed25519_cert_get_n_extensions(ed25519_cert_t *inp); +/** Set the value of the n_extensions field of the ed25519_cert_t in + * 'inp' to 'val'. Return 0 on success; return -1 and set the error + * code on 'inp' on failure. + */ +int ed25519_cert_set_n_extensions(ed25519_cert_t *inp, uint8_t val); +/** Return the length of the dynamic array holding the ext field of + * the ed25519_cert_t in 'inp'. + */ +size_t ed25519_cert_getlen_ext(const ed25519_cert_t *inp); +/** Return the element at position 'idx' of the dynamic array field + * ext of the ed25519_cert_t in 'inp'. + */ +struct ed25519_cert_extension_st * ed25519_cert_get_ext(ed25519_cert_t *inp, size_t idx); +/** Change the element at position 'idx' of the dynamic array field + * ext of the ed25519_cert_t in 'inp', so that it will hold the value + * 'elt'. Free the previous value, if any. + */ +int ed25519_cert_set_ext(ed25519_cert_t *inp, size_t idx, struct ed25519_cert_extension_st * elt); +/** As ed25519_cert_set_ext, but does not free the previous value. + */ +int ed25519_cert_set0_ext(ed25519_cert_t *inp, size_t idx, struct ed25519_cert_extension_st * elt); +/** Append a new element 'elt' to the dynamic array field ext of the + * ed25519_cert_t in 'inp'. + */ +int ed25519_cert_add_ext(ed25519_cert_t *inp, struct ed25519_cert_extension_st * elt); +/** Return a pointer to the variable-length array field ext of 'inp'. + */ +struct ed25519_cert_extension_st * * ed25519_cert_getarray_ext(ed25519_cert_t *inp); +/** Change the length of the variable-length array field ext of 'inp' + * to 'newlen'.Fill extra elements with NULL; free removed elements. + * Return 0 on success; return -1 and set the error code on 'inp' on + * failure. + */ +int ed25519_cert_setlen_ext(ed25519_cert_t *inp, size_t newlen); +/** Return the (constant) length of the array holding the signature + * field of the ed25519_cert_t in 'inp'. + */ +size_t ed25519_cert_getlen_signature(const ed25519_cert_t *inp); +/** Return the element at position 'idx' of the fixed array field + * signature of the ed25519_cert_t in 'inp'. + */ +uint8_t ed25519_cert_get_signature(const ed25519_cert_t *inp, size_t idx); +/** Change the element at position 'idx' of the fixed array field + * signature of the ed25519_cert_t in 'inp', so that it will hold the + * value 'elt'. + */ +int ed25519_cert_set_signature(ed25519_cert_t *inp, size_t idx, uint8_t elt); +/** Return a pointer to the 64-element array field signature of 'inp'. + */ +uint8_t * ed25519_cert_getarray_signature(ed25519_cert_t *inp); + + +#endif diff --git a/src/trunnel/ed25519_cert.trunnel b/src/trunnel/ed25519_cert.trunnel new file mode 100644 index 0000000000..c46f1b6c6b --- /dev/null +++ b/src/trunnel/ed25519_cert.trunnel @@ -0,0 +1,76 @@ + +struct ed25519_cert { + u8 version IN [1]; + u8 cert_type; + u32 exp_field; + u8 cert_key_type; + u8 certified_key[32]; + u8 n_extensions; + struct ed25519_cert_extension ext[n_extensions]; + u8 signature[64]; +} + +const CERTEXT_SIGNED_WITH_KEY = 4; +const CERTEXT_FLAG_AFFECTS_VALIDATION = 1; + +struct ed25519_cert_extension { + u16 ext_length; + u8 ext_type; + u8 ext_flags; + union un[ext_type] with length ext_length { + CERTEXT_SIGNED_WITH_KEY : u8 signing_key[32]; + default: u8 unparsed[]; + }; +} + +/* +struct cert_revocation { + u8 prefix[8]; + u8 version IN [1]; + u8 keytype; + u8 identity_key[32]; + u8 revoked_key[32]; + u64 published; + u8 n_extensions; + struct cert_extension ext[n_extensions]; + u8 signature[64]; +} + +struct crosscert_ed_rsa { + u8 ed_key[32]; + u32 expiration_date; + u8 signature[128]; +} + +struct auth02_cell { + u8 type[8]; + u8 cid[32]; + u8 sid[32]; + u8 cid_ed[32]; + u8 sid_ed[32]; + u8 slog[32]; + u8 clog[32]; + u8 scert[32]; + u8 tlssecrets[32]; + u8 rand[24]; + u8 sig[64]; +} + +const LS_IPV4 = 0x00; +const LS_IPV6 = 0x01; +const LS_LEGACY_ID = 0x02; +const LS_ED25519_ID = 0x03; + +// amended from tor.trunnel +struct link_specifier { + u8 ls_type; + u8 ls_len; + union un[ls_type] with length ls_len { + LS_IPV4: u32 ipv4_addr; u16 ipv4_port; + LS_IPV6: u8 ipv6_addr[16]; u16 ipv6_port; + LS_LEGACY_ID: u8 legacy_id[20]; + LS_ED25519_ID: u8 ed25519_id[32]; + default: u8 unrecognized[]; + }; +} +*/ \ No newline at end of file diff --git a/src/trunnel/include.am b/src/trunnel/include.am index 54e3db287e..9bf37fe58b 100644 --- a/src/trunnel/include.am +++ b/src/trunnel/include.am @@ -9,15 +9,24 @@ endif AM_CPPFLAGS += -I$(srcdir)/src/ext/trunnel -I$(srcdir)/src/trunnel +TRUNNELINPUTS = \ + src/trunnel/ed25519_cert.trunnel \ + src/trunnel/link_handshake.trunnel \ + src/trunnel/pwbox.trunnel + TRUNNELSOURCES = \ - src/ext/trunnel/trunnel.c \ - src/trunnel/pwbox.c + src/ext/trunnel/trunnel.c \ + src/trunnel/ed25519_cert.c \ + src/trunnel/link_handshake.c \ + src/trunnel/pwbox.c TRUNNELHEADERS = \ - src/ext/trunnel/trunnel.h \ - src/ext/trunnel/trunnel-impl.h \ - src/trunnel/trunnel-local.h \ - src/trunnel/pwbox.h + src/ext/trunnel/trunnel.h \ + src/ext/trunnel/trunnel-impl.h \ + src/trunnel/trunnel-local.h \ + src/trunnel/ed25519_cert.h \ + src/trunnel/link_handshake.h \ + src/trunnel/pwbox.h src_trunnel_libor_trunnel_a_SOURCES = $(TRUNNELSOURCES) src_trunnel_libor_trunnel_a_CPPFLAGS = -DTRUNNEL_LOCAL_H $(AM_CPPFLAGS) diff --git a/src/trunnel/link_handshake.c b/src/trunnel/link_handshake.c new file mode 100644 index 0000000000..9630d1340d --- /dev/null +++ b/src/trunnel/link_handshake.c @@ -0,0 +1,1885 @@ +/* link_handshake.c -- generated by Trunnel v1.4-pre. + * https://gitweb.torproject.org/trunnel.git + * You probably shouldn't edit this file. + */ +#include +#include "trunnel-impl.h" + +#include "link_handshake.h" + +#define TRUNNEL_SET_ERROR_CODE(obj) \ + do { \ + (obj)->trunnel_error_code_ = 1; \ + } while (0) + +#if defined(__COVERITY__) || defined(__clang_analyzer__) +/* If we're runnning a static analysis tool, we don't want it to complain + * that some of our remaining-bytes checks are dead-code. */ +int linkhandshake_deadcode_dummy__ = 0; +#define OR_DEADCODE_DUMMY || linkhandshake_deadcode_dummy__ +#else +#define OR_DEADCODE_DUMMY +#endif + +#define CHECK_REMAINING(nbytes, label) \ + do { \ + if (remaining < (nbytes) OR_DEADCODE_DUMMY) { \ + goto label; \ + } \ + } while (0) + +auth_challenge_cell_t * +auth_challenge_cell_new(void) +{ + auth_challenge_cell_t *val = trunnel_calloc(1, sizeof(auth_challenge_cell_t)); + if (NULL == val) + return NULL; + return val; +} + +/** Release all storage held inside 'obj', but do not free 'obj'. + */ +static void +auth_challenge_cell_clear(auth_challenge_cell_t *obj) +{ + (void) obj; + TRUNNEL_DYNARRAY_WIPE(&obj->methods); + TRUNNEL_DYNARRAY_CLEAR(&obj->methods); +} + +void +auth_challenge_cell_free(auth_challenge_cell_t *obj) +{ + if (obj == NULL) + return; + auth_challenge_cell_clear(obj); + trunnel_memwipe(obj, sizeof(auth_challenge_cell_t)); + trunnel_free_(obj); +} + +size_t +auth_challenge_cell_getlen_challenge(const auth_challenge_cell_t *inp) +{ + (void)inp; return 32; +} + +uint8_t +auth_challenge_cell_get_challenge(const auth_challenge_cell_t *inp, size_t idx) +{ + trunnel_assert(idx < 32); + return inp->challenge[idx]; +} + +int +auth_challenge_cell_set_challenge(auth_challenge_cell_t *inp, size_t idx, uint8_t elt) +{ + trunnel_assert(idx < 32); + inp->challenge[idx] = elt; + return 0; +} + +uint8_t * +auth_challenge_cell_getarray_challenge(auth_challenge_cell_t *inp) +{ + return inp->challenge; +} +uint16_t +auth_challenge_cell_get_n_methods(auth_challenge_cell_t *inp) +{ + return inp->n_methods; +} +int +auth_challenge_cell_set_n_methods(auth_challenge_cell_t *inp, uint16_t val) +{ + inp->n_methods = val; + return 0; +} +size_t +auth_challenge_cell_getlen_methods(const auth_challenge_cell_t *inp) +{ + return TRUNNEL_DYNARRAY_LEN(&inp->methods); +} + +uint16_t +auth_challenge_cell_get_methods(auth_challenge_cell_t *inp, size_t idx) +{ + return TRUNNEL_DYNARRAY_GET(&inp->methods, idx); +} + +int +auth_challenge_cell_set_methods(auth_challenge_cell_t *inp, size_t idx, uint16_t elt) +{ + TRUNNEL_DYNARRAY_SET(&inp->methods, idx, elt); + return 0; +} +int +auth_challenge_cell_add_methods(auth_challenge_cell_t *inp, uint16_t elt) +{ +#if SIZE_MAX >= UINT16_MAX + if (inp->methods.n_ == UINT16_MAX) + goto trunnel_alloc_failed; +#endif + TRUNNEL_DYNARRAY_ADD(uint16_t, &inp->methods, elt, {}); + return 0; + trunnel_alloc_failed: + TRUNNEL_SET_ERROR_CODE(inp); + return -1; +} + +uint16_t * +auth_challenge_cell_getarray_methods(auth_challenge_cell_t *inp) +{ + return inp->methods.elts_; +} +int +auth_challenge_cell_setlen_methods(auth_challenge_cell_t *inp, size_t newlen) +{ + uint16_t *newptr; +#if UINT16_MAX < SIZE_MAX + if (newlen > UINT16_MAX) + goto trunnel_alloc_failed; +#endif + newptr = trunnel_dynarray_setlen(&inp->methods.allocated_, + &inp->methods.n_, inp->methods.elts_, newlen, + sizeof(inp->methods.elts_[0]), (trunnel_free_fn_t) NULL, + &inp->trunnel_error_code_); + if (newptr == NULL) + goto trunnel_alloc_failed; + inp->methods.elts_ = newptr; + return 0; + trunnel_alloc_failed: + TRUNNEL_SET_ERROR_CODE(inp); + return -1; +} +const char * +auth_challenge_cell_check(const auth_challenge_cell_t *obj) +{ + if (obj == NULL) + return "Object was NULL"; + if (obj->trunnel_error_code_) + return "A set function failed on this object"; + if (TRUNNEL_DYNARRAY_LEN(&obj->methods) != obj->n_methods) + return "Length mismatch for methods"; + return NULL; +} + +ssize_t +auth_challenge_cell_encoded_len(const auth_challenge_cell_t *obj) +{ + ssize_t result = 0; + + if (NULL != auth_challenge_cell_check(obj)) + return -1; + + + /* Length of u8 challenge[32] */ + result += 32; + + /* Length of u16 n_methods */ + result += 2; + + /* Length of u16 methods[n_methods] */ + result += 2 * TRUNNEL_DYNARRAY_LEN(&obj->methods); + return result; +} +int +auth_challenge_cell_clear_errors(auth_challenge_cell_t *obj) +{ + int r = obj->trunnel_error_code_; + obj->trunnel_error_code_ = 0; + return r; +} +ssize_t +auth_challenge_cell_encode(uint8_t *output, const size_t avail, const auth_challenge_cell_t *obj) +{ + ssize_t result = 0; + size_t written = 0; + uint8_t *ptr = output; + const char *msg; +#ifdef TRUNNEL_CHECK_ENCODED_LEN + const ssize_t encoded_len = auth_challenge_cell_encoded_len(obj); +#endif + + if (NULL != (msg = auth_challenge_cell_check(obj))) + goto check_failed; + +#ifdef TRUNNEL_CHECK_ENCODED_LEN + trunnel_assert(encoded_len >= 0); +#endif + + /* Encode u8 challenge[32] */ + trunnel_assert(written <= avail); + if (avail - written < 32) + goto truncated; + memcpy(ptr, obj->challenge, 32); + written += 32; ptr += 32; + + /* Encode u16 n_methods */ + trunnel_assert(written <= avail); + if (avail - written < 2) + goto truncated; + trunnel_set_uint16(ptr, trunnel_htons(obj->n_methods)); + written += 2; ptr += 2; + + /* Encode u16 methods[n_methods] */ + { + + unsigned idx; + for (idx = 0; idx < TRUNNEL_DYNARRAY_LEN(&obj->methods); ++idx) { + trunnel_assert(written <= avail); + if (avail - written < 2) + goto truncated; + trunnel_set_uint16(ptr, trunnel_htons(TRUNNEL_DYNARRAY_GET(&obj->methods, idx))); + written += 2; ptr += 2; + } + } + + + trunnel_assert(ptr == output + written); +#ifdef TRUNNEL_CHECK_ENCODED_LEN + { + trunnel_assert(encoded_len >= 0); + trunnel_assert((size_t)encoded_len == written); + } + +#endif + + return written; + + truncated: + result = -2; + goto fail; + check_failed: + (void)msg; + result = -1; + goto fail; + fail: + trunnel_assert(result < 0); + return result; +} + +/** As auth_challenge_cell_parse(), but do not allocate the output + * object. + */ +static ssize_t +auth_challenge_cell_parse_into(auth_challenge_cell_t *obj, const uint8_t *input, const size_t len_in) +{ + const uint8_t *ptr = input; + size_t remaining = len_in; + ssize_t result = 0; + (void)result; + + /* Parse u8 challenge[32] */ + CHECK_REMAINING(32, truncated); + memcpy(obj->challenge, ptr, 32); + remaining -= 32; ptr += 32; + + /* Parse u16 n_methods */ + CHECK_REMAINING(2, truncated); + obj->n_methods = trunnel_ntohs(trunnel_get_uint16(ptr)); + remaining -= 2; ptr += 2; + + /* Parse u16 methods[n_methods] */ + TRUNNEL_DYNARRAY_EXPAND(uint16_t, &obj->methods, obj->n_methods, {}); + { + uint16_t elt; + unsigned idx; + for (idx = 0; idx < obj->n_methods; ++idx) { + CHECK_REMAINING(2, truncated); + elt = trunnel_ntohs(trunnel_get_uint16(ptr)); + remaining -= 2; ptr += 2; + TRUNNEL_DYNARRAY_ADD(uint16_t, &obj->methods, elt, {}); + } + } + trunnel_assert(ptr + remaining == input + len_in); + return len_in - remaining; + + truncated: + return -2; + trunnel_alloc_failed: + return -1; +} + +ssize_t +auth_challenge_cell_parse(auth_challenge_cell_t **output, const uint8_t *input, const size_t len_in) +{ + ssize_t result; + *output = auth_challenge_cell_new(); + if (NULL == *output) + return -1; + result = auth_challenge_cell_parse_into(*output, input, len_in); + if (result < 0) { + auth_challenge_cell_free(*output); + *output = NULL; + } + return result; +} +auth_ctx_t * +auth_ctx_new(void) +{ + auth_ctx_t *val = trunnel_calloc(1, sizeof(auth_ctx_t)); + if (NULL == val) + return NULL; + return val; +} + +/** Release all storage held inside 'obj', but do not free 'obj'. + */ +static void +auth_ctx_clear(auth_ctx_t *obj) +{ + (void) obj; +} + +void +auth_ctx_free(auth_ctx_t *obj) +{ + if (obj == NULL) + return; + auth_ctx_clear(obj); + trunnel_memwipe(obj, sizeof(auth_ctx_t)); + trunnel_free_(obj); +} + +uint8_t +auth_ctx_get_is_ed(auth_ctx_t *inp) +{ + return inp->is_ed; +} +int +auth_ctx_set_is_ed(auth_ctx_t *inp, uint8_t val) +{ + inp->is_ed = val; + return 0; +} +certs_cell_cert_t * +certs_cell_cert_new(void) +{ + certs_cell_cert_t *val = trunnel_calloc(1, sizeof(certs_cell_cert_t)); + if (NULL == val) + return NULL; + return val; +} + +/** Release all storage held inside 'obj', but do not free 'obj'. + */ +static void +certs_cell_cert_clear(certs_cell_cert_t *obj) +{ + (void) obj; + TRUNNEL_DYNARRAY_WIPE(&obj->body); + TRUNNEL_DYNARRAY_CLEAR(&obj->body); +} + +void +certs_cell_cert_free(certs_cell_cert_t *obj) +{ + if (obj == NULL) + return; + certs_cell_cert_clear(obj); + trunnel_memwipe(obj, sizeof(certs_cell_cert_t)); + trunnel_free_(obj); +} + +uint8_t +certs_cell_cert_get_cert_type(certs_cell_cert_t *inp) +{ + return inp->cert_type; +} +int +certs_cell_cert_set_cert_type(certs_cell_cert_t *inp, uint8_t val) +{ + inp->cert_type = val; + return 0; +} +uint16_t +certs_cell_cert_get_cert_len(certs_cell_cert_t *inp) +{ + return inp->cert_len; +} +int +certs_cell_cert_set_cert_len(certs_cell_cert_t *inp, uint16_t val) +{ + inp->cert_len = val; + return 0; +} +size_t +certs_cell_cert_getlen_body(const certs_cell_cert_t *inp) +{ + return TRUNNEL_DYNARRAY_LEN(&inp->body); +} + +uint8_t +certs_cell_cert_get_body(certs_cell_cert_t *inp, size_t idx) +{ + return TRUNNEL_DYNARRAY_GET(&inp->body, idx); +} + +int +certs_cell_cert_set_body(certs_cell_cert_t *inp, size_t idx, uint8_t elt) +{ + TRUNNEL_DYNARRAY_SET(&inp->body, idx, elt); + return 0; +} +int +certs_cell_cert_add_body(certs_cell_cert_t *inp, uint8_t elt) +{ +#if SIZE_MAX >= UINT16_MAX + if (inp->body.n_ == UINT16_MAX) + goto trunnel_alloc_failed; +#endif + TRUNNEL_DYNARRAY_ADD(uint8_t, &inp->body, elt, {}); + return 0; + trunnel_alloc_failed: + TRUNNEL_SET_ERROR_CODE(inp); + return -1; +} + +uint8_t * +certs_cell_cert_getarray_body(certs_cell_cert_t *inp) +{ + return inp->body.elts_; +} +int +certs_cell_cert_setlen_body(certs_cell_cert_t *inp, size_t newlen) +{ + uint8_t *newptr; +#if UINT16_MAX < SIZE_MAX + if (newlen > UINT16_MAX) + goto trunnel_alloc_failed; +#endif + newptr = trunnel_dynarray_setlen(&inp->body.allocated_, + &inp->body.n_, inp->body.elts_, newlen, + sizeof(inp->body.elts_[0]), (trunnel_free_fn_t) NULL, + &inp->trunnel_error_code_); + if (newptr == NULL) + goto trunnel_alloc_failed; + inp->body.elts_ = newptr; + return 0; + trunnel_alloc_failed: + TRUNNEL_SET_ERROR_CODE(inp); + return -1; +} +const char * +certs_cell_cert_check(const certs_cell_cert_t *obj) +{ + if (obj == NULL) + return "Object was NULL"; + if (obj->trunnel_error_code_) + return "A set function failed on this object"; + if (TRUNNEL_DYNARRAY_LEN(&obj->body) != obj->cert_len) + return "Length mismatch for body"; + return NULL; +} + +ssize_t +certs_cell_cert_encoded_len(const certs_cell_cert_t *obj) +{ + ssize_t result = 0; + + if (NULL != certs_cell_cert_check(obj)) + return -1; + + + /* Length of u8 cert_type */ + result += 1; + + /* Length of u16 cert_len */ + result += 2; + + /* Length of u8 body[cert_len] */ + result += TRUNNEL_DYNARRAY_LEN(&obj->body); + return result; +} +int +certs_cell_cert_clear_errors(certs_cell_cert_t *obj) +{ + int r = obj->trunnel_error_code_; + obj->trunnel_error_code_ = 0; + return r; +} +ssize_t +certs_cell_cert_encode(uint8_t *output, const size_t avail, const certs_cell_cert_t *obj) +{ + ssize_t result = 0; + size_t written = 0; + uint8_t *ptr = output; + const char *msg; +#ifdef TRUNNEL_CHECK_ENCODED_LEN + const ssize_t encoded_len = certs_cell_cert_encoded_len(obj); +#endif + + if (NULL != (msg = certs_cell_cert_check(obj))) + goto check_failed; + +#ifdef TRUNNEL_CHECK_ENCODED_LEN + trunnel_assert(encoded_len >= 0); +#endif + + /* Encode u8 cert_type */ + trunnel_assert(written <= avail); + if (avail - written < 1) + goto truncated; + trunnel_set_uint8(ptr, (obj->cert_type)); + written += 1; ptr += 1; + + /* Encode u16 cert_len */ + trunnel_assert(written <= avail); + if (avail - written < 2) + goto truncated; + trunnel_set_uint16(ptr, trunnel_htons(obj->cert_len)); + written += 2; ptr += 2; + + /* Encode u8 body[cert_len] */ + { + size_t elt_len = TRUNNEL_DYNARRAY_LEN(&obj->body); + trunnel_assert(obj->cert_len == elt_len); + trunnel_assert(written <= avail); + if (avail - written < elt_len) + goto truncated; + memcpy(ptr, obj->body.elts_, elt_len); + written += elt_len; ptr += elt_len; + } + + + trunnel_assert(ptr == output + written); +#ifdef TRUNNEL_CHECK_ENCODED_LEN + { + trunnel_assert(encoded_len >= 0); + trunnel_assert((size_t)encoded_len == written); + } + +#endif + + return written; + + truncated: + result = -2; + goto fail; + check_failed: + (void)msg; + result = -1; + goto fail; + fail: + trunnel_assert(result < 0); + return result; +} + +/** As certs_cell_cert_parse(), but do not allocate the output object. + */ +static ssize_t +certs_cell_cert_parse_into(certs_cell_cert_t *obj, const uint8_t *input, const size_t len_in) +{ + const uint8_t *ptr = input; + size_t remaining = len_in; + ssize_t result = 0; + (void)result; + + /* Parse u8 cert_type */ + CHECK_REMAINING(1, truncated); + obj->cert_type = (trunnel_get_uint8(ptr)); + remaining -= 1; ptr += 1; + + /* Parse u16 cert_len */ + CHECK_REMAINING(2, truncated); + obj->cert_len = trunnel_ntohs(trunnel_get_uint16(ptr)); + remaining -= 2; ptr += 2; + + /* Parse u8 body[cert_len] */ + CHECK_REMAINING(obj->cert_len, truncated); + TRUNNEL_DYNARRAY_EXPAND(uint8_t, &obj->body, obj->cert_len, {}); + obj->body.n_ = obj->cert_len; + memcpy(obj->body.elts_, ptr, obj->cert_len); + ptr += obj->cert_len; remaining -= obj->cert_len; + trunnel_assert(ptr + remaining == input + len_in); + return len_in - remaining; + + truncated: + return -2; + trunnel_alloc_failed: + return -1; +} + +ssize_t +certs_cell_cert_parse(certs_cell_cert_t **output, const uint8_t *input, const size_t len_in) +{ + ssize_t result; + *output = certs_cell_cert_new(); + if (NULL == *output) + return -1; + result = certs_cell_cert_parse_into(*output, input, len_in); + if (result < 0) { + certs_cell_cert_free(*output); + *output = NULL; + } + return result; +} +rsa_ed_crosscert_t * +rsa_ed_crosscert_new(void) +{ + rsa_ed_crosscert_t *val = trunnel_calloc(1, sizeof(rsa_ed_crosscert_t)); + if (NULL == val) + return NULL; + return val; +} + +/** Release all storage held inside 'obj', but do not free 'obj'. + */ +static void +rsa_ed_crosscert_clear(rsa_ed_crosscert_t *obj) +{ + (void) obj; + TRUNNEL_DYNARRAY_WIPE(&obj->sig); + TRUNNEL_DYNARRAY_CLEAR(&obj->sig); +} + +void +rsa_ed_crosscert_free(rsa_ed_crosscert_t *obj) +{ + if (obj == NULL) + return; + rsa_ed_crosscert_clear(obj); + trunnel_memwipe(obj, sizeof(rsa_ed_crosscert_t)); + trunnel_free_(obj); +} + +size_t +rsa_ed_crosscert_getlen_ed_key(const rsa_ed_crosscert_t *inp) +{ + (void)inp; return 32; +} + +uint8_t +rsa_ed_crosscert_get_ed_key(const rsa_ed_crosscert_t *inp, size_t idx) +{ + trunnel_assert(idx < 32); + return inp->ed_key[idx]; +} + +int +rsa_ed_crosscert_set_ed_key(rsa_ed_crosscert_t *inp, size_t idx, uint8_t elt) +{ + trunnel_assert(idx < 32); + inp->ed_key[idx] = elt; + return 0; +} + +uint8_t * +rsa_ed_crosscert_getarray_ed_key(rsa_ed_crosscert_t *inp) +{ + return inp->ed_key; +} +uint32_t +rsa_ed_crosscert_get_expiration(rsa_ed_crosscert_t *inp) +{ + return inp->expiration; +} +int +rsa_ed_crosscert_set_expiration(rsa_ed_crosscert_t *inp, uint32_t val) +{ + inp->expiration = val; + return 0; +} +const uint8_t * +rsa_ed_crosscert_get_end_of_signed(const rsa_ed_crosscert_t *inp) +{ + return inp->end_of_signed; +} +uint8_t +rsa_ed_crosscert_get_sig_len(rsa_ed_crosscert_t *inp) +{ + return inp->sig_len; +} +int +rsa_ed_crosscert_set_sig_len(rsa_ed_crosscert_t *inp, uint8_t val) +{ + inp->sig_len = val; + return 0; +} +size_t +rsa_ed_crosscert_getlen_sig(const rsa_ed_crosscert_t *inp) +{ + return TRUNNEL_DYNARRAY_LEN(&inp->sig); +} + +uint8_t +rsa_ed_crosscert_get_sig(rsa_ed_crosscert_t *inp, size_t idx) +{ + return TRUNNEL_DYNARRAY_GET(&inp->sig, idx); +} + +int +rsa_ed_crosscert_set_sig(rsa_ed_crosscert_t *inp, size_t idx, uint8_t elt) +{ + TRUNNEL_DYNARRAY_SET(&inp->sig, idx, elt); + return 0; +} +int +rsa_ed_crosscert_add_sig(rsa_ed_crosscert_t *inp, uint8_t elt) +{ +#if SIZE_MAX >= UINT8_MAX + if (inp->sig.n_ == UINT8_MAX) + goto trunnel_alloc_failed; +#endif + TRUNNEL_DYNARRAY_ADD(uint8_t, &inp->sig, elt, {}); + return 0; + trunnel_alloc_failed: + TRUNNEL_SET_ERROR_CODE(inp); + return -1; +} + +uint8_t * +rsa_ed_crosscert_getarray_sig(rsa_ed_crosscert_t *inp) +{ + return inp->sig.elts_; +} +int +rsa_ed_crosscert_setlen_sig(rsa_ed_crosscert_t *inp, size_t newlen) +{ + uint8_t *newptr; +#if UINT8_MAX < SIZE_MAX + if (newlen > UINT8_MAX) + goto trunnel_alloc_failed; +#endif + newptr = trunnel_dynarray_setlen(&inp->sig.allocated_, + &inp->sig.n_, inp->sig.elts_, newlen, + sizeof(inp->sig.elts_[0]), (trunnel_free_fn_t) NULL, + &inp->trunnel_error_code_); + if (newptr == NULL) + goto trunnel_alloc_failed; + inp->sig.elts_ = newptr; + return 0; + trunnel_alloc_failed: + TRUNNEL_SET_ERROR_CODE(inp); + return -1; +} +const char * +rsa_ed_crosscert_check(const rsa_ed_crosscert_t *obj) +{ + if (obj == NULL) + return "Object was NULL"; + if (obj->trunnel_error_code_) + return "A set function failed on this object"; + if (TRUNNEL_DYNARRAY_LEN(&obj->sig) != obj->sig_len) + return "Length mismatch for sig"; + return NULL; +} + +ssize_t +rsa_ed_crosscert_encoded_len(const rsa_ed_crosscert_t *obj) +{ + ssize_t result = 0; + + if (NULL != rsa_ed_crosscert_check(obj)) + return -1; + + + /* Length of u8 ed_key[32] */ + result += 32; + + /* Length of u32 expiration */ + result += 4; + + /* Length of u8 sig_len */ + result += 1; + + /* Length of u8 sig[sig_len] */ + result += TRUNNEL_DYNARRAY_LEN(&obj->sig); + return result; +} +int +rsa_ed_crosscert_clear_errors(rsa_ed_crosscert_t *obj) +{ + int r = obj->trunnel_error_code_; + obj->trunnel_error_code_ = 0; + return r; +} +ssize_t +rsa_ed_crosscert_encode(uint8_t *output, const size_t avail, const rsa_ed_crosscert_t *obj) +{ + ssize_t result = 0; + size_t written = 0; + uint8_t *ptr = output; + const char *msg; +#ifdef TRUNNEL_CHECK_ENCODED_LEN + const ssize_t encoded_len = rsa_ed_crosscert_encoded_len(obj); +#endif + + if (NULL != (msg = rsa_ed_crosscert_check(obj))) + goto check_failed; + +#ifdef TRUNNEL_CHECK_ENCODED_LEN + trunnel_assert(encoded_len >= 0); +#endif + + /* Encode u8 ed_key[32] */ + trunnel_assert(written <= avail); + if (avail - written < 32) + goto truncated; + memcpy(ptr, obj->ed_key, 32); + written += 32; ptr += 32; + + /* Encode u32 expiration */ + trunnel_assert(written <= avail); + if (avail - written < 4) + goto truncated; + trunnel_set_uint32(ptr, trunnel_htonl(obj->expiration)); + written += 4; ptr += 4; + + /* Encode u8 sig_len */ + trunnel_assert(written <= avail); + if (avail - written < 1) + goto truncated; + trunnel_set_uint8(ptr, (obj->sig_len)); + written += 1; ptr += 1; + + /* Encode u8 sig[sig_len] */ + { + size_t elt_len = TRUNNEL_DYNARRAY_LEN(&obj->sig); + trunnel_assert(obj->sig_len == elt_len); + trunnel_assert(written <= avail); + if (avail - written < elt_len) + goto truncated; + memcpy(ptr, obj->sig.elts_, elt_len); + written += elt_len; ptr += elt_len; + } + + + trunnel_assert(ptr == output + written); +#ifdef TRUNNEL_CHECK_ENCODED_LEN + { + trunnel_assert(encoded_len >= 0); + trunnel_assert((size_t)encoded_len == written); + } + +#endif + + return written; + + truncated: + result = -2; + goto fail; + check_failed: + (void)msg; + result = -1; + goto fail; + fail: + trunnel_assert(result < 0); + return result; +} + +/** As rsa_ed_crosscert_parse(), but do not allocate the output + * object. + */ +static ssize_t +rsa_ed_crosscert_parse_into(rsa_ed_crosscert_t *obj, const uint8_t *input, const size_t len_in) +{ + const uint8_t *ptr = input; + size_t remaining = len_in; + ssize_t result = 0; + (void)result; + + /* Parse u8 ed_key[32] */ + CHECK_REMAINING(32, truncated); + memcpy(obj->ed_key, ptr, 32); + remaining -= 32; ptr += 32; + + /* Parse u32 expiration */ + CHECK_REMAINING(4, truncated); + obj->expiration = trunnel_ntohl(trunnel_get_uint32(ptr)); + remaining -= 4; ptr += 4; + obj->end_of_signed = ptr; + + /* Parse u8 sig_len */ + CHECK_REMAINING(1, truncated); + obj->sig_len = (trunnel_get_uint8(ptr)); + remaining -= 1; ptr += 1; + + /* Parse u8 sig[sig_len] */ + CHECK_REMAINING(obj->sig_len, truncated); + TRUNNEL_DYNARRAY_EXPAND(uint8_t, &obj->sig, obj->sig_len, {}); + obj->sig.n_ = obj->sig_len; + memcpy(obj->sig.elts_, ptr, obj->sig_len); + ptr += obj->sig_len; remaining -= obj->sig_len; + trunnel_assert(ptr + remaining == input + len_in); + return len_in - remaining; + + truncated: + return -2; + trunnel_alloc_failed: + return -1; +} + +ssize_t +rsa_ed_crosscert_parse(rsa_ed_crosscert_t **output, const uint8_t *input, const size_t len_in) +{ + ssize_t result; + *output = rsa_ed_crosscert_new(); + if (NULL == *output) + return -1; + result = rsa_ed_crosscert_parse_into(*output, input, len_in); + if (result < 0) { + rsa_ed_crosscert_free(*output); + *output = NULL; + } + return result; +} +auth1_t * +auth1_new(void) +{ + auth1_t *val = trunnel_calloc(1, sizeof(auth1_t)); + if (NULL == val) + return NULL; + return val; +} + +/** Release all storage held inside 'obj', but do not free 'obj'. + */ +static void +auth1_clear(auth1_t *obj) +{ + (void) obj; + TRUNNEL_DYNARRAY_WIPE(&obj->sig); + TRUNNEL_DYNARRAY_CLEAR(&obj->sig); +} + +void +auth1_free(auth1_t *obj) +{ + if (obj == NULL) + return; + auth1_clear(obj); + trunnel_memwipe(obj, sizeof(auth1_t)); + trunnel_free_(obj); +} + +size_t +auth1_getlen_type(const auth1_t *inp) +{ + (void)inp; return 8; +} + +uint8_t +auth1_get_type(const auth1_t *inp, size_t idx) +{ + trunnel_assert(idx < 8); + return inp->type[idx]; +} + +int +auth1_set_type(auth1_t *inp, size_t idx, uint8_t elt) +{ + trunnel_assert(idx < 8); + inp->type[idx] = elt; + return 0; +} + +uint8_t * +auth1_getarray_type(auth1_t *inp) +{ + return inp->type; +} +size_t +auth1_getlen_cid(const auth1_t *inp) +{ + (void)inp; return 32; +} + +uint8_t +auth1_get_cid(const auth1_t *inp, size_t idx) +{ + trunnel_assert(idx < 32); + return inp->cid[idx]; +} + +int +auth1_set_cid(auth1_t *inp, size_t idx, uint8_t elt) +{ + trunnel_assert(idx < 32); + inp->cid[idx] = elt; + return 0; +} + +uint8_t * +auth1_getarray_cid(auth1_t *inp) +{ + return inp->cid; +} +size_t +auth1_getlen_sid(const auth1_t *inp) +{ + (void)inp; return 32; +} + +uint8_t +auth1_get_sid(const auth1_t *inp, size_t idx) +{ + trunnel_assert(idx < 32); + return inp->sid[idx]; +} + +int +auth1_set_sid(auth1_t *inp, size_t idx, uint8_t elt) +{ + trunnel_assert(idx < 32); + inp->sid[idx] = elt; + return 0; +} + +uint8_t * +auth1_getarray_sid(auth1_t *inp) +{ + return inp->sid; +} +size_t +auth1_getlen_u1_cid_ed(const auth1_t *inp) +{ + (void)inp; return 32; +} + +uint8_t +auth1_get_u1_cid_ed(const auth1_t *inp, size_t idx) +{ + trunnel_assert(idx < 32); + return inp->u1_cid_ed[idx]; +} + +int +auth1_set_u1_cid_ed(auth1_t *inp, size_t idx, uint8_t elt) +{ + trunnel_assert(idx < 32); + inp->u1_cid_ed[idx] = elt; + return 0; +} + +uint8_t * +auth1_getarray_u1_cid_ed(auth1_t *inp) +{ + return inp->u1_cid_ed; +} +size_t +auth1_getlen_u1_sid_ed(const auth1_t *inp) +{ + (void)inp; return 32; +} + +uint8_t +auth1_get_u1_sid_ed(const auth1_t *inp, size_t idx) +{ + trunnel_assert(idx < 32); + return inp->u1_sid_ed[idx]; +} + +int +auth1_set_u1_sid_ed(auth1_t *inp, size_t idx, uint8_t elt) +{ + trunnel_assert(idx < 32); + inp->u1_sid_ed[idx] = elt; + return 0; +} + +uint8_t * +auth1_getarray_u1_sid_ed(auth1_t *inp) +{ + return inp->u1_sid_ed; +} +size_t +auth1_getlen_slog(const auth1_t *inp) +{ + (void)inp; return 32; +} + +uint8_t +auth1_get_slog(const auth1_t *inp, size_t idx) +{ + trunnel_assert(idx < 32); + return inp->slog[idx]; +} + +int +auth1_set_slog(auth1_t *inp, size_t idx, uint8_t elt) +{ + trunnel_assert(idx < 32); + inp->slog[idx] = elt; + return 0; +} + +uint8_t * +auth1_getarray_slog(auth1_t *inp) +{ + return inp->slog; +} +size_t +auth1_getlen_clog(const auth1_t *inp) +{ + (void)inp; return 32; +} + +uint8_t +auth1_get_clog(const auth1_t *inp, size_t idx) +{ + trunnel_assert(idx < 32); + return inp->clog[idx]; +} + +int +auth1_set_clog(auth1_t *inp, size_t idx, uint8_t elt) +{ + trunnel_assert(idx < 32); + inp->clog[idx] = elt; + return 0; +} + +uint8_t * +auth1_getarray_clog(auth1_t *inp) +{ + return inp->clog; +} +size_t +auth1_getlen_scert(const auth1_t *inp) +{ + (void)inp; return 32; +} + +uint8_t +auth1_get_scert(const auth1_t *inp, size_t idx) +{ + trunnel_assert(idx < 32); + return inp->scert[idx]; +} + +int +auth1_set_scert(auth1_t *inp, size_t idx, uint8_t elt) +{ + trunnel_assert(idx < 32); + inp->scert[idx] = elt; + return 0; +} + +uint8_t * +auth1_getarray_scert(auth1_t *inp) +{ + return inp->scert; +} +size_t +auth1_getlen_tlssecrets(const auth1_t *inp) +{ + (void)inp; return 32; +} + +uint8_t +auth1_get_tlssecrets(const auth1_t *inp, size_t idx) +{ + trunnel_assert(idx < 32); + return inp->tlssecrets[idx]; +} + +int +auth1_set_tlssecrets(auth1_t *inp, size_t idx, uint8_t elt) +{ + trunnel_assert(idx < 32); + inp->tlssecrets[idx] = elt; + return 0; +} + +uint8_t * +auth1_getarray_tlssecrets(auth1_t *inp) +{ + return inp->tlssecrets; +} +const uint8_t * +auth1_get_end_of_fixed_part(const auth1_t *inp) +{ + return inp->end_of_fixed_part; +} +size_t +auth1_getlen_rand(const auth1_t *inp) +{ + (void)inp; return 24; +} + +uint8_t +auth1_get_rand(const auth1_t *inp, size_t idx) +{ + trunnel_assert(idx < 24); + return inp->rand[idx]; +} + +int +auth1_set_rand(auth1_t *inp, size_t idx, uint8_t elt) +{ + trunnel_assert(idx < 24); + inp->rand[idx] = elt; + return 0; +} + +uint8_t * +auth1_getarray_rand(auth1_t *inp) +{ + return inp->rand; +} +const uint8_t * +auth1_get_end_of_signed(const auth1_t *inp) +{ + return inp->end_of_signed; +} +size_t +auth1_getlen_sig(const auth1_t *inp) +{ + return TRUNNEL_DYNARRAY_LEN(&inp->sig); +} + +uint8_t +auth1_get_sig(auth1_t *inp, size_t idx) +{ + return TRUNNEL_DYNARRAY_GET(&inp->sig, idx); +} + +int +auth1_set_sig(auth1_t *inp, size_t idx, uint8_t elt) +{ + TRUNNEL_DYNARRAY_SET(&inp->sig, idx, elt); + return 0; +} +int +auth1_add_sig(auth1_t *inp, uint8_t elt) +{ + TRUNNEL_DYNARRAY_ADD(uint8_t, &inp->sig, elt, {}); + return 0; + trunnel_alloc_failed: + TRUNNEL_SET_ERROR_CODE(inp); + return -1; +} + +uint8_t * +auth1_getarray_sig(auth1_t *inp) +{ + return inp->sig.elts_; +} +int +auth1_setlen_sig(auth1_t *inp, size_t newlen) +{ + uint8_t *newptr; + newptr = trunnel_dynarray_setlen(&inp->sig.allocated_, + &inp->sig.n_, inp->sig.elts_, newlen, + sizeof(inp->sig.elts_[0]), (trunnel_free_fn_t) NULL, + &inp->trunnel_error_code_); + if (newptr == NULL) + goto trunnel_alloc_failed; + inp->sig.elts_ = newptr; + return 0; + trunnel_alloc_failed: + TRUNNEL_SET_ERROR_CODE(inp); + return -1; +} +const char * +auth1_check(const auth1_t *obj, const auth_ctx_t *auth_ctx_ctx) +{ + if (obj == NULL) + return "Object was NULL"; + if (obj->trunnel_error_code_) + return "A set function failed on this object"; + if (auth_ctx_ctx == NULL) + return "Context was NULL"; + switch (auth_ctx_ctx->is_ed) { + + case 0: + break; + + case 1: + break; + + default: + return "Bad tag for union"; + break; + } + return NULL; +} + +ssize_t +auth1_encoded_len(const auth1_t *obj, const auth_ctx_t *auth_ctx_ctx) +{ + ssize_t result = 0; + + if (NULL != auth1_check(obj, auth_ctx_ctx)) + return -1; + + + /* Length of u8 type[8] */ + result += 8; + + /* Length of u8 cid[32] */ + result += 32; + + /* Length of u8 sid[32] */ + result += 32; + switch (auth_ctx_ctx->is_ed) { + + case 0: + break; + + case 1: + + /* Length of u8 u1_cid_ed[32] */ + result += 32; + + /* Length of u8 u1_sid_ed[32] */ + result += 32; + break; + + default: + trunnel_assert(0); + break; + } + + /* Length of u8 slog[32] */ + result += 32; + + /* Length of u8 clog[32] */ + result += 32; + + /* Length of u8 scert[32] */ + result += 32; + + /* Length of u8 tlssecrets[32] */ + result += 32; + + /* Length of u8 rand[24] */ + result += 24; + + /* Length of u8 sig[] */ + result += TRUNNEL_DYNARRAY_LEN(&obj->sig); + return result; +} +int +auth1_clear_errors(auth1_t *obj) +{ + int r = obj->trunnel_error_code_; + obj->trunnel_error_code_ = 0; + return r; +} +ssize_t +auth1_encode(uint8_t *output, const size_t avail, const auth1_t *obj, const auth_ctx_t *auth_ctx_ctx) +{ + ssize_t result = 0; + size_t written = 0; + uint8_t *ptr = output; + const char *msg; +#ifdef TRUNNEL_CHECK_ENCODED_LEN + const ssize_t encoded_len = auth1_encoded_len(obj, auth_ctx_ctx); +#endif + + if (NULL != (msg = auth1_check(obj, auth_ctx_ctx))) + goto check_failed; + +#ifdef TRUNNEL_CHECK_ENCODED_LEN + trunnel_assert(encoded_len >= 0); +#endif + + /* Encode u8 type[8] */ + trunnel_assert(written <= avail); + if (avail - written < 8) + goto truncated; + memcpy(ptr, obj->type, 8); + written += 8; ptr += 8; + + /* Encode u8 cid[32] */ + trunnel_assert(written <= avail); + if (avail - written < 32) + goto truncated; + memcpy(ptr, obj->cid, 32); + written += 32; ptr += 32; + + /* Encode u8 sid[32] */ + trunnel_assert(written <= avail); + if (avail - written < 32) + goto truncated; + memcpy(ptr, obj->sid, 32); + written += 32; ptr += 32; + + /* Encode union u1[auth_ctx.is_ed] */ + trunnel_assert(written <= avail); + switch (auth_ctx_ctx->is_ed) { + + case 0: + break; + + case 1: + + /* Encode u8 u1_cid_ed[32] */ + trunnel_assert(written <= avail); + if (avail - written < 32) + goto truncated; + memcpy(ptr, obj->u1_cid_ed, 32); + written += 32; ptr += 32; + + /* Encode u8 u1_sid_ed[32] */ + trunnel_assert(written <= avail); + if (avail - written < 32) + goto truncated; + memcpy(ptr, obj->u1_sid_ed, 32); + written += 32; ptr += 32; + break; + + default: + trunnel_assert(0); + break; + } + + /* Encode u8 slog[32] */ + trunnel_assert(written <= avail); + if (avail - written < 32) + goto truncated; + memcpy(ptr, obj->slog, 32); + written += 32; ptr += 32; + + /* Encode u8 clog[32] */ + trunnel_assert(written <= avail); + if (avail - written < 32) + goto truncated; + memcpy(ptr, obj->clog, 32); + written += 32; ptr += 32; + + /* Encode u8 scert[32] */ + trunnel_assert(written <= avail); + if (avail - written < 32) + goto truncated; + memcpy(ptr, obj->scert, 32); + written += 32; ptr += 32; + + /* Encode u8 tlssecrets[32] */ + trunnel_assert(written <= avail); + if (avail - written < 32) + goto truncated; + memcpy(ptr, obj->tlssecrets, 32); + written += 32; ptr += 32; + + /* Encode u8 rand[24] */ + trunnel_assert(written <= avail); + if (avail - written < 24) + goto truncated; + memcpy(ptr, obj->rand, 24); + written += 24; ptr += 24; + + /* Encode u8 sig[] */ + { + size_t elt_len = TRUNNEL_DYNARRAY_LEN(&obj->sig); + trunnel_assert(written <= avail); + if (avail - written < elt_len) + goto truncated; + memcpy(ptr, obj->sig.elts_, elt_len); + written += elt_len; ptr += elt_len; + } + + + trunnel_assert(ptr == output + written); +#ifdef TRUNNEL_CHECK_ENCODED_LEN + { + trunnel_assert(encoded_len >= 0); + trunnel_assert((size_t)encoded_len == written); + } + +#endif + + return written; + + truncated: + result = -2; + goto fail; + check_failed: + (void)msg; + result = -1; + goto fail; + fail: + trunnel_assert(result < 0); + return result; +} + +/** As auth1_parse(), but do not allocate the output object. + */ +static ssize_t +auth1_parse_into(auth1_t *obj, const uint8_t *input, const size_t len_in, const auth_ctx_t *auth_ctx_ctx) +{ + const uint8_t *ptr = input; + size_t remaining = len_in; + ssize_t result = 0; + (void)result; + if (auth_ctx_ctx == NULL) + return -1; + + /* Parse u8 type[8] */ + CHECK_REMAINING(8, truncated); + memcpy(obj->type, ptr, 8); + remaining -= 8; ptr += 8; + + /* Parse u8 cid[32] */ + CHECK_REMAINING(32, truncated); + memcpy(obj->cid, ptr, 32); + remaining -= 32; ptr += 32; + + /* Parse u8 sid[32] */ + CHECK_REMAINING(32, truncated); + memcpy(obj->sid, ptr, 32); + remaining -= 32; ptr += 32; + + /* Parse union u1[auth_ctx.is_ed] */ + switch (auth_ctx_ctx->is_ed) { + + case 0: + break; + + case 1: + + /* Parse u8 u1_cid_ed[32] */ + CHECK_REMAINING(32, truncated); + memcpy(obj->u1_cid_ed, ptr, 32); + remaining -= 32; ptr += 32; + + /* Parse u8 u1_sid_ed[32] */ + CHECK_REMAINING(32, truncated); + memcpy(obj->u1_sid_ed, ptr, 32); + remaining -= 32; ptr += 32; + break; + + default: + goto fail; + break; + } + + /* Parse u8 slog[32] */ + CHECK_REMAINING(32, truncated); + memcpy(obj->slog, ptr, 32); + remaining -= 32; ptr += 32; + + /* Parse u8 clog[32] */ + CHECK_REMAINING(32, truncated); + memcpy(obj->clog, ptr, 32); + remaining -= 32; ptr += 32; + + /* Parse u8 scert[32] */ + CHECK_REMAINING(32, truncated); + memcpy(obj->scert, ptr, 32); + remaining -= 32; ptr += 32; + + /* Parse u8 tlssecrets[32] */ + CHECK_REMAINING(32, truncated); + memcpy(obj->tlssecrets, ptr, 32); + remaining -= 32; ptr += 32; + obj->end_of_fixed_part = ptr; + + /* Parse u8 rand[24] */ + CHECK_REMAINING(24, truncated); + memcpy(obj->rand, ptr, 24); + remaining -= 24; ptr += 24; + obj->end_of_signed = ptr; + + /* Parse u8 sig[] */ + TRUNNEL_DYNARRAY_EXPAND(uint8_t, &obj->sig, remaining, {}); + obj->sig.n_ = remaining; + memcpy(obj->sig.elts_, ptr, remaining); + ptr += remaining; remaining -= remaining; + trunnel_assert(ptr + remaining == input + len_in); + return len_in - remaining; + + truncated: + return -2; + trunnel_alloc_failed: + return -1; + fail: + result = -1; + return result; +} + +ssize_t +auth1_parse(auth1_t **output, const uint8_t *input, const size_t len_in, const auth_ctx_t *auth_ctx_ctx) +{ + ssize_t result; + *output = auth1_new(); + if (NULL == *output) + return -1; + result = auth1_parse_into(*output, input, len_in, auth_ctx_ctx); + if (result < 0) { + auth1_free(*output); + *output = NULL; + } + return result; +} +certs_cell_t * +certs_cell_new(void) +{ + certs_cell_t *val = trunnel_calloc(1, sizeof(certs_cell_t)); + if (NULL == val) + return NULL; + return val; +} + +/** Release all storage held inside 'obj', but do not free 'obj'. + */ +static void +certs_cell_clear(certs_cell_t *obj) +{ + (void) obj; + { + + unsigned idx; + for (idx = 0; idx < TRUNNEL_DYNARRAY_LEN(&obj->certs); ++idx) { + certs_cell_cert_free(TRUNNEL_DYNARRAY_GET(&obj->certs, idx)); + } + } + TRUNNEL_DYNARRAY_WIPE(&obj->certs); + TRUNNEL_DYNARRAY_CLEAR(&obj->certs); +} + +void +certs_cell_free(certs_cell_t *obj) +{ + if (obj == NULL) + return; + certs_cell_clear(obj); + trunnel_memwipe(obj, sizeof(certs_cell_t)); + trunnel_free_(obj); +} + +uint8_t +certs_cell_get_n_certs(certs_cell_t *inp) +{ + return inp->n_certs; +} +int +certs_cell_set_n_certs(certs_cell_t *inp, uint8_t val) +{ + inp->n_certs = val; + return 0; +} +size_t +certs_cell_getlen_certs(const certs_cell_t *inp) +{ + return TRUNNEL_DYNARRAY_LEN(&inp->certs); +} + +struct certs_cell_cert_st * +certs_cell_get_certs(certs_cell_t *inp, size_t idx) +{ + return TRUNNEL_DYNARRAY_GET(&inp->certs, idx); +} + +int +certs_cell_set_certs(certs_cell_t *inp, size_t idx, struct certs_cell_cert_st * elt) +{ + certs_cell_cert_t *oldval = TRUNNEL_DYNARRAY_GET(&inp->certs, idx); + if (oldval && oldval != elt) + certs_cell_cert_free(oldval); + return certs_cell_set0_certs(inp, idx, elt); +} +int +certs_cell_set0_certs(certs_cell_t *inp, size_t idx, struct certs_cell_cert_st * elt) +{ + TRUNNEL_DYNARRAY_SET(&inp->certs, idx, elt); + return 0; +} +int +certs_cell_add_certs(certs_cell_t *inp, struct certs_cell_cert_st * elt) +{ +#if SIZE_MAX >= UINT8_MAX + if (inp->certs.n_ == UINT8_MAX) + goto trunnel_alloc_failed; +#endif + TRUNNEL_DYNARRAY_ADD(struct certs_cell_cert_st *, &inp->certs, elt, {}); + return 0; + trunnel_alloc_failed: + TRUNNEL_SET_ERROR_CODE(inp); + return -1; +} + +struct certs_cell_cert_st * * +certs_cell_getarray_certs(certs_cell_t *inp) +{ + return inp->certs.elts_; +} +int +certs_cell_setlen_certs(certs_cell_t *inp, size_t newlen) +{ + struct certs_cell_cert_st * *newptr; +#if UINT8_MAX < SIZE_MAX + if (newlen > UINT8_MAX) + goto trunnel_alloc_failed; +#endif + newptr = trunnel_dynarray_setlen(&inp->certs.allocated_, + &inp->certs.n_, inp->certs.elts_, newlen, + sizeof(inp->certs.elts_[0]), (trunnel_free_fn_t) certs_cell_cert_free, + &inp->trunnel_error_code_); + if (newptr == NULL) + goto trunnel_alloc_failed; + inp->certs.elts_ = newptr; + return 0; + trunnel_alloc_failed: + TRUNNEL_SET_ERROR_CODE(inp); + return -1; +} +const char * +certs_cell_check(const certs_cell_t *obj) +{ + if (obj == NULL) + return "Object was NULL"; + if (obj->trunnel_error_code_) + return "A set function failed on this object"; + { + const char *msg; + + unsigned idx; + for (idx = 0; idx < TRUNNEL_DYNARRAY_LEN(&obj->certs); ++idx) { + if (NULL != (msg = certs_cell_cert_check(TRUNNEL_DYNARRAY_GET(&obj->certs, idx)))) + return msg; + } + } + if (TRUNNEL_DYNARRAY_LEN(&obj->certs) != obj->n_certs) + return "Length mismatch for certs"; + return NULL; +} + +ssize_t +certs_cell_encoded_len(const certs_cell_t *obj) +{ + ssize_t result = 0; + + if (NULL != certs_cell_check(obj)) + return -1; + + + /* Length of u8 n_certs */ + result += 1; + + /* Length of struct certs_cell_cert certs[n_certs] */ + { + + unsigned idx; + for (idx = 0; idx < TRUNNEL_DYNARRAY_LEN(&obj->certs); ++idx) { + result += certs_cell_cert_encoded_len(TRUNNEL_DYNARRAY_GET(&obj->certs, idx)); + } + } + return result; +} +int +certs_cell_clear_errors(certs_cell_t *obj) +{ + int r = obj->trunnel_error_code_; + obj->trunnel_error_code_ = 0; + return r; +} +ssize_t +certs_cell_encode(uint8_t *output, const size_t avail, const certs_cell_t *obj) +{ + ssize_t result = 0; + size_t written = 0; + uint8_t *ptr = output; + const char *msg; +#ifdef TRUNNEL_CHECK_ENCODED_LEN + const ssize_t encoded_len = certs_cell_encoded_len(obj); +#endif + + if (NULL != (msg = certs_cell_check(obj))) + goto check_failed; + +#ifdef TRUNNEL_CHECK_ENCODED_LEN + trunnel_assert(encoded_len >= 0); +#endif + + /* Encode u8 n_certs */ + trunnel_assert(written <= avail); + if (avail - written < 1) + goto truncated; + trunnel_set_uint8(ptr, (obj->n_certs)); + written += 1; ptr += 1; + + /* Encode struct certs_cell_cert certs[n_certs] */ + { + + unsigned idx; + for (idx = 0; idx < TRUNNEL_DYNARRAY_LEN(&obj->certs); ++idx) { + trunnel_assert(written <= avail); + result = certs_cell_cert_encode(ptr, avail - written, TRUNNEL_DYNARRAY_GET(&obj->certs, idx)); + if (result < 0) + goto fail; /* XXXXXXX !*/ + written += result; ptr += result; + } + } + + + trunnel_assert(ptr == output + written); +#ifdef TRUNNEL_CHECK_ENCODED_LEN + { + trunnel_assert(encoded_len >= 0); + trunnel_assert((size_t)encoded_len == written); + } + +#endif + + return written; + + truncated: + result = -2; + goto fail; + check_failed: + (void)msg; + result = -1; + goto fail; + fail: + trunnel_assert(result < 0); + return result; +} + +/** As certs_cell_parse(), but do not allocate the output object. + */ +static ssize_t +certs_cell_parse_into(certs_cell_t *obj, const uint8_t *input, const size_t len_in) +{ + const uint8_t *ptr = input; + size_t remaining = len_in; + ssize_t result = 0; + (void)result; + + /* Parse u8 n_certs */ + CHECK_REMAINING(1, truncated); + obj->n_certs = (trunnel_get_uint8(ptr)); + remaining -= 1; ptr += 1; + + /* Parse struct certs_cell_cert certs[n_certs] */ + TRUNNEL_DYNARRAY_EXPAND(certs_cell_cert_t *, &obj->certs, obj->n_certs, {}); + { + certs_cell_cert_t * elt; + unsigned idx; + for (idx = 0; idx < obj->n_certs; ++idx) { + result = certs_cell_cert_parse(&elt, ptr, remaining); + if (result < 0) + goto relay_fail; + trunnel_assert((size_t)result <= remaining); + remaining -= result; ptr += result; + TRUNNEL_DYNARRAY_ADD(certs_cell_cert_t *, &obj->certs, elt, {certs_cell_cert_free(elt);}); + } + } + trunnel_assert(ptr + remaining == input + len_in); + return len_in - remaining; + + truncated: + return -2; + relay_fail: + if (result >= 0) result = -1; + return result; + trunnel_alloc_failed: + return -1; +} + +ssize_t +certs_cell_parse(certs_cell_t **output, const uint8_t *input, const size_t len_in) +{ + ssize_t result; + *output = certs_cell_new(); + if (NULL == *output) + return -1; + result = certs_cell_parse_into(*output, input, len_in); + if (result < 0) { + certs_cell_free(*output); + *output = NULL; + } + return result; +} diff --git a/src/trunnel/link_handshake.h b/src/trunnel/link_handshake.h new file mode 100644 index 0000000000..109fe8d065 --- /dev/null +++ b/src/trunnel/link_handshake.h @@ -0,0 +1,654 @@ +/* link_handshake.h -- generated by by Trunnel v1.4-pre. + * https://gitweb.torproject.org/trunnel.git + * You probably shouldn't edit this file. + */ +#ifndef TRUNNEL_LINK_HANDSHAKE_H +#define TRUNNEL_LINK_HANDSHAKE_H + +#include +#include "trunnel.h" + +#define CERTTYPE_RSA1024_ID_LINK 1 +#define CERTTYPE_RSA1024_ID_ID 2 +#define CERTTYPE_RSA1024_ID_AUTH 3 +#define CERTTYPE_ED_ID_SIGN 4 +#define CERTTYPE_ED_SIGN_LINK 5 +#define CERTTYPE_ED_SIGN_AUTH 6 +#define CERTTYPE_RSA1024_ID_EDID 7 +#if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_AUTH_CHALLENGE_CELL) +struct auth_challenge_cell_st { + uint8_t challenge[32]; + uint16_t n_methods; + TRUNNEL_DYNARRAY_HEAD(, uint16_t) methods; + uint8_t trunnel_error_code_; +}; +#endif +typedef struct auth_challenge_cell_st auth_challenge_cell_t; +#if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_AUTH_CTX) +struct auth_ctx_st { + uint8_t is_ed; + uint8_t trunnel_error_code_; +}; +#endif +typedef struct auth_ctx_st auth_ctx_t; +#if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_CERTS_CELL_CERT) +struct certs_cell_cert_st { + uint8_t cert_type; + uint16_t cert_len; + TRUNNEL_DYNARRAY_HEAD(, uint8_t) body; + uint8_t trunnel_error_code_; +}; +#endif +typedef struct certs_cell_cert_st certs_cell_cert_t; +#if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_RSA_ED_CROSSCERT) +struct rsa_ed_crosscert_st { + uint8_t ed_key[32]; + uint32_t expiration; + const uint8_t *end_of_signed; + uint8_t sig_len; + TRUNNEL_DYNARRAY_HEAD(, uint8_t) sig; + uint8_t trunnel_error_code_; +}; +#endif +typedef struct rsa_ed_crosscert_st rsa_ed_crosscert_t; +#if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_AUTH1) +struct auth1_st { + uint8_t type[8]; + uint8_t cid[32]; + uint8_t sid[32]; + uint8_t u1_cid_ed[32]; + uint8_t u1_sid_ed[32]; + uint8_t slog[32]; + uint8_t clog[32]; + uint8_t scert[32]; + uint8_t tlssecrets[32]; + const uint8_t *end_of_fixed_part; + uint8_t rand[24]; + const uint8_t *end_of_signed; + TRUNNEL_DYNARRAY_HEAD(, uint8_t) sig; + uint8_t trunnel_error_code_; +}; +#endif +typedef struct auth1_st auth1_t; +#if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_CERTS_CELL) +struct certs_cell_st { + uint8_t n_certs; + TRUNNEL_DYNARRAY_HEAD(, struct certs_cell_cert_st *) certs; + uint8_t trunnel_error_code_; +}; +#endif +typedef struct certs_cell_st certs_cell_t; +/** Return a newly allocated auth_challenge_cell with all elements set + * to zero. + */ +auth_challenge_cell_t *auth_challenge_cell_new(void); +/** Release all storage held by the auth_challenge_cell in 'victim'. + * (Do nothing if 'victim' is NULL.) + */ +void auth_challenge_cell_free(auth_challenge_cell_t *victim); +/** Try to parse a auth_challenge_cell from the buffer in 'input', + * using up to 'len_in' bytes from the input buffer. On success, + * return the number of bytes consumed and set *output to the newly + * allocated auth_challenge_cell_t. On failure, return -2 if the input + * appears truncated, and -1 if the input is otherwise invalid. + */ +ssize_t auth_challenge_cell_parse(auth_challenge_cell_t **output, const uint8_t *input, const size_t len_in); +/** Return the number of bytes we expect to need to encode the + * auth_challenge_cell in 'obj'. On failure, return a negative value. + * Note that this value may be an overestimate, and can even be an + * underestimate for certain unencodeable objects. + */ +ssize_t auth_challenge_cell_encoded_len(const auth_challenge_cell_t *obj); +/** Try to encode the auth_challenge_cell from 'input' into the buffer + * at 'output', using up to 'avail' bytes of the output buffer. On + * success, return the number of bytes used. On failure, return -2 if + * the buffer was not long enough, and -1 if the input was invalid. + */ +ssize_t auth_challenge_cell_encode(uint8_t *output, const size_t avail, const auth_challenge_cell_t *input); +/** Check whether the internal state of the auth_challenge_cell in + * 'obj' is consistent. Return NULL if it is, and a short message if + * it is not. + */ +const char *auth_challenge_cell_check(const auth_challenge_cell_t *obj); +/** Clear any errors that were set on the object 'obj' by its setter + * functions. Return true iff errors were cleared. + */ +int auth_challenge_cell_clear_errors(auth_challenge_cell_t *obj); +/** Return the (constant) length of the array holding the challenge + * field of the auth_challenge_cell_t in 'inp'. + */ +size_t auth_challenge_cell_getlen_challenge(const auth_challenge_cell_t *inp); +/** Return the element at position 'idx' of the fixed array field + * challenge of the auth_challenge_cell_t in 'inp'. + */ +uint8_t auth_challenge_cell_get_challenge(const auth_challenge_cell_t *inp, size_t idx); +/** Change the element at position 'idx' of the fixed array field + * challenge of the auth_challenge_cell_t in 'inp', so that it will + * hold the value 'elt'. + */ +int auth_challenge_cell_set_challenge(auth_challenge_cell_t *inp, size_t idx, uint8_t elt); +/** Return a pointer to the 32-element array field challenge of 'inp'. + */ +uint8_t * auth_challenge_cell_getarray_challenge(auth_challenge_cell_t *inp); +/** Return the value of the n_methods field of the + * auth_challenge_cell_t in 'inp' + */ +uint16_t auth_challenge_cell_get_n_methods(auth_challenge_cell_t *inp); +/** Set the value of the n_methods field of the auth_challenge_cell_t + * in 'inp' to 'val'. Return 0 on success; return -1 and set the error + * code on 'inp' on failure. + */ +int auth_challenge_cell_set_n_methods(auth_challenge_cell_t *inp, uint16_t val); +/** Return the length of the dynamic array holding the methods field + * of the auth_challenge_cell_t in 'inp'. + */ +size_t auth_challenge_cell_getlen_methods(const auth_challenge_cell_t *inp); +/** Return the element at position 'idx' of the dynamic array field + * methods of the auth_challenge_cell_t in 'inp'. + */ +uint16_t auth_challenge_cell_get_methods(auth_challenge_cell_t *inp, size_t idx); +/** Change the element at position 'idx' of the dynamic array field + * methods of the auth_challenge_cell_t in 'inp', so that it will hold + * the value 'elt'. + */ +int auth_challenge_cell_set_methods(auth_challenge_cell_t *inp, size_t idx, uint16_t elt); +/** Append a new element 'elt' to the dynamic array field methods of + * the auth_challenge_cell_t in 'inp'. + */ +int auth_challenge_cell_add_methods(auth_challenge_cell_t *inp, uint16_t elt); +/** Return a pointer to the variable-length array field methods of + * 'inp'. + */ +uint16_t * auth_challenge_cell_getarray_methods(auth_challenge_cell_t *inp); +/** Change the length of the variable-length array field methods of + * 'inp' to 'newlen'.Fill extra elements with 0. Return 0 on success; + * return -1 and set the error code on 'inp' on failure. + */ +int auth_challenge_cell_setlen_methods(auth_challenge_cell_t *inp, size_t newlen); +/** Return a newly allocated auth_ctx with all elements set to zero. + */ +auth_ctx_t *auth_ctx_new(void); +/** Release all storage held by the auth_ctx in 'victim'. (Do nothing + * if 'victim' is NULL.) + */ +void auth_ctx_free(auth_ctx_t *victim); +/** Return the value of the is_ed field of the auth_ctx_t in 'inp' + */ +uint8_t auth_ctx_get_is_ed(auth_ctx_t *inp); +/** Set the value of the is_ed field of the auth_ctx_t in 'inp' to + * 'val'. Return 0 on success; return -1 and set the error code on + * 'inp' on failure. + */ +int auth_ctx_set_is_ed(auth_ctx_t *inp, uint8_t val); +/** Return a newly allocated certs_cell_cert with all elements set to + * zero. + */ +certs_cell_cert_t *certs_cell_cert_new(void); +/** Release all storage held by the certs_cell_cert in 'victim'. (Do + * nothing if 'victim' is NULL.) + */ +void certs_cell_cert_free(certs_cell_cert_t *victim); +/** Try to parse a certs_cell_cert from the buffer in 'input', using + * up to 'len_in' bytes from the input buffer. On success, return the + * number of bytes consumed and set *output to the newly allocated + * certs_cell_cert_t. On failure, return -2 if the input appears + * truncated, and -1 if the input is otherwise invalid. + */ +ssize_t certs_cell_cert_parse(certs_cell_cert_t **output, const uint8_t *input, const size_t len_in); +/** Return the number of bytes we expect to need to encode the + * certs_cell_cert in 'obj'. On failure, return a negative value. Note + * that this value may be an overestimate, and can even be an + * underestimate for certain unencodeable objects. + */ +ssize_t certs_cell_cert_encoded_len(const certs_cell_cert_t *obj); +/** Try to encode the certs_cell_cert from 'input' into the buffer at + * 'output', using up to 'avail' bytes of the output buffer. On + * success, return the number of bytes used. On failure, return -2 if + * the buffer was not long enough, and -1 if the input was invalid. + */ +ssize_t certs_cell_cert_encode(uint8_t *output, const size_t avail, const certs_cell_cert_t *input); +/** Check whether the internal state of the certs_cell_cert in 'obj' + * is consistent. Return NULL if it is, and a short message if it is + * not. + */ +const char *certs_cell_cert_check(const certs_cell_cert_t *obj); +/** Clear any errors that were set on the object 'obj' by its setter + * functions. Return true iff errors were cleared. + */ +int certs_cell_cert_clear_errors(certs_cell_cert_t *obj); +/** Return the value of the cert_type field of the certs_cell_cert_t + * in 'inp' + */ +uint8_t certs_cell_cert_get_cert_type(certs_cell_cert_t *inp); +/** Set the value of the cert_type field of the certs_cell_cert_t in + * 'inp' to 'val'. Return 0 on success; return -1 and set the error + * code on 'inp' on failure. + */ +int certs_cell_cert_set_cert_type(certs_cell_cert_t *inp, uint8_t val); +/** Return the value of the cert_len field of the certs_cell_cert_t in + * 'inp' + */ +uint16_t certs_cell_cert_get_cert_len(certs_cell_cert_t *inp); +/** Set the value of the cert_len field of the certs_cell_cert_t in + * 'inp' to 'val'. Return 0 on success; return -1 and set the error + * code on 'inp' on failure. + */ +int certs_cell_cert_set_cert_len(certs_cell_cert_t *inp, uint16_t val); +/** Return the length of the dynamic array holding the body field of + * the certs_cell_cert_t in 'inp'. + */ +size_t certs_cell_cert_getlen_body(const certs_cell_cert_t *inp); +/** Return the element at position 'idx' of the dynamic array field + * body of the certs_cell_cert_t in 'inp'. + */ +uint8_t certs_cell_cert_get_body(certs_cell_cert_t *inp, size_t idx); +/** Change the element at position 'idx' of the dynamic array field + * body of the certs_cell_cert_t in 'inp', so that it will hold the + * value 'elt'. + */ +int certs_cell_cert_set_body(certs_cell_cert_t *inp, size_t idx, uint8_t elt); +/** Append a new element 'elt' to the dynamic array field body of the + * certs_cell_cert_t in 'inp'. + */ +int certs_cell_cert_add_body(certs_cell_cert_t *inp, uint8_t elt); +/** Return a pointer to the variable-length array field body of 'inp'. + */ +uint8_t * certs_cell_cert_getarray_body(certs_cell_cert_t *inp); +/** Change the length of the variable-length array field body of 'inp' + * to 'newlen'.Fill extra elements with 0. Return 0 on success; return + * -1 and set the error code on 'inp' on failure. + */ +int certs_cell_cert_setlen_body(certs_cell_cert_t *inp, size_t newlen); +/** Return a newly allocated rsa_ed_crosscert with all elements set to + * zero. + */ +rsa_ed_crosscert_t *rsa_ed_crosscert_new(void); +/** Release all storage held by the rsa_ed_crosscert in 'victim'. (Do + * nothing if 'victim' is NULL.) + */ +void rsa_ed_crosscert_free(rsa_ed_crosscert_t *victim); +/** Try to parse a rsa_ed_crosscert from the buffer in 'input', using + * up to 'len_in' bytes from the input buffer. On success, return the + * number of bytes consumed and set *output to the newly allocated + * rsa_ed_crosscert_t. On failure, return -2 if the input appears + * truncated, and -1 if the input is otherwise invalid. + */ +ssize_t rsa_ed_crosscert_parse(rsa_ed_crosscert_t **output, const uint8_t *input, const size_t len_in); +/** Return the number of bytes we expect to need to encode the + * rsa_ed_crosscert in 'obj'. On failure, return a negative value. + * Note that this value may be an overestimate, and can even be an + * underestimate for certain unencodeable objects. + */ +ssize_t rsa_ed_crosscert_encoded_len(const rsa_ed_crosscert_t *obj); +/** Try to encode the rsa_ed_crosscert from 'input' into the buffer at + * 'output', using up to 'avail' bytes of the output buffer. On + * success, return the number of bytes used. On failure, return -2 if + * the buffer was not long enough, and -1 if the input was invalid. + */ +ssize_t rsa_ed_crosscert_encode(uint8_t *output, const size_t avail, const rsa_ed_crosscert_t *input); +/** Check whether the internal state of the rsa_ed_crosscert in 'obj' + * is consistent. Return NULL if it is, and a short message if it is + * not. + */ +const char *rsa_ed_crosscert_check(const rsa_ed_crosscert_t *obj); +/** Clear any errors that were set on the object 'obj' by its setter + * functions. Return true iff errors were cleared. + */ +int rsa_ed_crosscert_clear_errors(rsa_ed_crosscert_t *obj); +/** Return the (constant) length of the array holding the ed_key field + * of the rsa_ed_crosscert_t in 'inp'. + */ +size_t rsa_ed_crosscert_getlen_ed_key(const rsa_ed_crosscert_t *inp); +/** Return the element at position 'idx' of the fixed array field + * ed_key of the rsa_ed_crosscert_t in 'inp'. + */ +uint8_t rsa_ed_crosscert_get_ed_key(const rsa_ed_crosscert_t *inp, size_t idx); +/** Change the element at position 'idx' of the fixed array field + * ed_key of the rsa_ed_crosscert_t in 'inp', so that it will hold the + * value 'elt'. + */ +int rsa_ed_crosscert_set_ed_key(rsa_ed_crosscert_t *inp, size_t idx, uint8_t elt); +/** Return a pointer to the 32-element array field ed_key of 'inp'. + */ +uint8_t * rsa_ed_crosscert_getarray_ed_key(rsa_ed_crosscert_t *inp); +/** Return the value of the expiration field of the rsa_ed_crosscert_t + * in 'inp' + */ +uint32_t rsa_ed_crosscert_get_expiration(rsa_ed_crosscert_t *inp); +/** Set the value of the expiration field of the rsa_ed_crosscert_t in + * 'inp' to 'val'. Return 0 on success; return -1 and set the error + * code on 'inp' on failure. + */ +int rsa_ed_crosscert_set_expiration(rsa_ed_crosscert_t *inp, uint32_t val); +/** Return the position for end_of_signed when we parsed this object + */ +const uint8_t * rsa_ed_crosscert_get_end_of_signed(const rsa_ed_crosscert_t *inp); +/** Return the value of the sig_len field of the rsa_ed_crosscert_t in + * 'inp' + */ +uint8_t rsa_ed_crosscert_get_sig_len(rsa_ed_crosscert_t *inp); +/** Set the value of the sig_len field of the rsa_ed_crosscert_t in + * 'inp' to 'val'. Return 0 on success; return -1 and set the error + * code on 'inp' on failure. + */ +int rsa_ed_crosscert_set_sig_len(rsa_ed_crosscert_t *inp, uint8_t val); +/** Return the length of the dynamic array holding the sig field of + * the rsa_ed_crosscert_t in 'inp'. + */ +size_t rsa_ed_crosscert_getlen_sig(const rsa_ed_crosscert_t *inp); +/** Return the element at position 'idx' of the dynamic array field + * sig of the rsa_ed_crosscert_t in 'inp'. + */ +uint8_t rsa_ed_crosscert_get_sig(rsa_ed_crosscert_t *inp, size_t idx); +/** Change the element at position 'idx' of the dynamic array field + * sig of the rsa_ed_crosscert_t in 'inp', so that it will hold the + * value 'elt'. + */ +int rsa_ed_crosscert_set_sig(rsa_ed_crosscert_t *inp, size_t idx, uint8_t elt); +/** Append a new element 'elt' to the dynamic array field sig of the + * rsa_ed_crosscert_t in 'inp'. + */ +int rsa_ed_crosscert_add_sig(rsa_ed_crosscert_t *inp, uint8_t elt); +/** Return a pointer to the variable-length array field sig of 'inp'. + */ +uint8_t * rsa_ed_crosscert_getarray_sig(rsa_ed_crosscert_t *inp); +/** Change the length of the variable-length array field sig of 'inp' + * to 'newlen'.Fill extra elements with 0. Return 0 on success; return + * -1 and set the error code on 'inp' on failure. + */ +int rsa_ed_crosscert_setlen_sig(rsa_ed_crosscert_t *inp, size_t newlen); +/** Return a newly allocated auth1 with all elements set to zero. + */ +auth1_t *auth1_new(void); +/** Release all storage held by the auth1 in 'victim'. (Do nothing if + * 'victim' is NULL.) + */ +void auth1_free(auth1_t *victim); +/** Try to parse a auth1 from the buffer in 'input', using up to + * 'len_in' bytes from the input buffer. On success, return the number + * of bytes consumed and set *output to the newly allocated auth1_t. + * On failure, return -2 if the input appears truncated, and -1 if the + * input is otherwise invalid. + */ +ssize_t auth1_parse(auth1_t **output, const uint8_t *input, const size_t len_in, const auth_ctx_t *auth_ctx_ctx); +/** Return the number of bytes we expect to need to encode the auth1 + * in 'obj'. On failure, return a negative value. Note that this value + * may be an overestimate, and can even be an underestimate for + * certain unencodeable objects. + */ +ssize_t auth1_encoded_len(const auth1_t *obj, const auth_ctx_t *auth_ctx_ctx); +/** Try to encode the auth1 from 'input' into the buffer at 'output', + * using up to 'avail' bytes of the output buffer. On success, return + * the number of bytes used. On failure, return -2 if the buffer was + * not long enough, and -1 if the input was invalid. + */ +ssize_t auth1_encode(uint8_t *output, const size_t avail, const auth1_t *input, const auth_ctx_t *auth_ctx_ctx); +/** Check whether the internal state of the auth1 in 'obj' is + * consistent. Return NULL if it is, and a short message if it is not. + */ +const char *auth1_check(const auth1_t *obj, const auth_ctx_t *auth_ctx_ctx); +/** Clear any errors that were set on the object 'obj' by its setter + * functions. Return true iff errors were cleared. + */ +int auth1_clear_errors(auth1_t *obj); +/** Return the (constant) length of the array holding the type field + * of the auth1_t in 'inp'. + */ +size_t auth1_getlen_type(const auth1_t *inp); +/** Return the element at position 'idx' of the fixed array field type + * of the auth1_t in 'inp'. + */ +uint8_t auth1_get_type(const auth1_t *inp, size_t idx); +/** Change the element at position 'idx' of the fixed array field type + * of the auth1_t in 'inp', so that it will hold the value 'elt'. + */ +int auth1_set_type(auth1_t *inp, size_t idx, uint8_t elt); +/** Return a pointer to the 8-element array field type of 'inp'. + */ +uint8_t * auth1_getarray_type(auth1_t *inp); +/** Return the (constant) length of the array holding the cid field of + * the auth1_t in 'inp'. + */ +size_t auth1_getlen_cid(const auth1_t *inp); +/** Return the element at position 'idx' of the fixed array field cid + * of the auth1_t in 'inp'. + */ +uint8_t auth1_get_cid(const auth1_t *inp, size_t idx); +/** Change the element at position 'idx' of the fixed array field cid + * of the auth1_t in 'inp', so that it will hold the value 'elt'. + */ +int auth1_set_cid(auth1_t *inp, size_t idx, uint8_t elt); +/** Return a pointer to the 32-element array field cid of 'inp'. + */ +uint8_t * auth1_getarray_cid(auth1_t *inp); +/** Return the (constant) length of the array holding the sid field of + * the auth1_t in 'inp'. + */ +size_t auth1_getlen_sid(const auth1_t *inp); +/** Return the element at position 'idx' of the fixed array field sid + * of the auth1_t in 'inp'. + */ +uint8_t auth1_get_sid(const auth1_t *inp, size_t idx); +/** Change the element at position 'idx' of the fixed array field sid + * of the auth1_t in 'inp', so that it will hold the value 'elt'. + */ +int auth1_set_sid(auth1_t *inp, size_t idx, uint8_t elt); +/** Return a pointer to the 32-element array field sid of 'inp'. + */ +uint8_t * auth1_getarray_sid(auth1_t *inp); +/** Return the (constant) length of the array holding the u1_cid_ed + * field of the auth1_t in 'inp'. + */ +size_t auth1_getlen_u1_cid_ed(const auth1_t *inp); +/** Return the element at position 'idx' of the fixed array field + * u1_cid_ed of the auth1_t in 'inp'. + */ +uint8_t auth1_get_u1_cid_ed(const auth1_t *inp, size_t idx); +/** Change the element at position 'idx' of the fixed array field + * u1_cid_ed of the auth1_t in 'inp', so that it will hold the value + * 'elt'. + */ +int auth1_set_u1_cid_ed(auth1_t *inp, size_t idx, uint8_t elt); +/** Return a pointer to the 32-element array field u1_cid_ed of 'inp'. + */ +uint8_t * auth1_getarray_u1_cid_ed(auth1_t *inp); +/** Return the (constant) length of the array holding the u1_sid_ed + * field of the auth1_t in 'inp'. + */ +size_t auth1_getlen_u1_sid_ed(const auth1_t *inp); +/** Return the element at position 'idx' of the fixed array field + * u1_sid_ed of the auth1_t in 'inp'. + */ +uint8_t auth1_get_u1_sid_ed(const auth1_t *inp, size_t idx); +/** Change the element at position 'idx' of the fixed array field + * u1_sid_ed of the auth1_t in 'inp', so that it will hold the value + * 'elt'. + */ +int auth1_set_u1_sid_ed(auth1_t *inp, size_t idx, uint8_t elt); +/** Return a pointer to the 32-element array field u1_sid_ed of 'inp'. + */ +uint8_t * auth1_getarray_u1_sid_ed(auth1_t *inp); +/** Return the (constant) length of the array holding the slog field + * of the auth1_t in 'inp'. + */ +size_t auth1_getlen_slog(const auth1_t *inp); +/** Return the element at position 'idx' of the fixed array field slog + * of the auth1_t in 'inp'. + */ +uint8_t auth1_get_slog(const auth1_t *inp, size_t idx); +/** Change the element at position 'idx' of the fixed array field slog + * of the auth1_t in 'inp', so that it will hold the value 'elt'. + */ +int auth1_set_slog(auth1_t *inp, size_t idx, uint8_t elt); +/** Return a pointer to the 32-element array field slog of 'inp'. + */ +uint8_t * auth1_getarray_slog(auth1_t *inp); +/** Return the (constant) length of the array holding the clog field + * of the auth1_t in 'inp'. + */ +size_t auth1_getlen_clog(const auth1_t *inp); +/** Return the element at position 'idx' of the fixed array field clog + * of the auth1_t in 'inp'. + */ +uint8_t auth1_get_clog(const auth1_t *inp, size_t idx); +/** Change the element at position 'idx' of the fixed array field clog + * of the auth1_t in 'inp', so that it will hold the value 'elt'. + */ +int auth1_set_clog(auth1_t *inp, size_t idx, uint8_t elt); +/** Return a pointer to the 32-element array field clog of 'inp'. + */ +uint8_t * auth1_getarray_clog(auth1_t *inp); +/** Return the (constant) length of the array holding the scert field + * of the auth1_t in 'inp'. + */ +size_t auth1_getlen_scert(const auth1_t *inp); +/** Return the element at position 'idx' of the fixed array field + * scert of the auth1_t in 'inp'. + */ +uint8_t auth1_get_scert(const auth1_t *inp, size_t idx); +/** Change the element at position 'idx' of the fixed array field + * scert of the auth1_t in 'inp', so that it will hold the value + * 'elt'. + */ +int auth1_set_scert(auth1_t *inp, size_t idx, uint8_t elt); +/** Return a pointer to the 32-element array field scert of 'inp'. + */ +uint8_t * auth1_getarray_scert(auth1_t *inp); +/** Return the (constant) length of the array holding the tlssecrets + * field of the auth1_t in 'inp'. + */ +size_t auth1_getlen_tlssecrets(const auth1_t *inp); +/** Return the element at position 'idx' of the fixed array field + * tlssecrets of the auth1_t in 'inp'. + */ +uint8_t auth1_get_tlssecrets(const auth1_t *inp, size_t idx); +/** Change the element at position 'idx' of the fixed array field + * tlssecrets of the auth1_t in 'inp', so that it will hold the value + * 'elt'. + */ +int auth1_set_tlssecrets(auth1_t *inp, size_t idx, uint8_t elt); +/** Return a pointer to the 32-element array field tlssecrets of + * 'inp'. + */ +uint8_t * auth1_getarray_tlssecrets(auth1_t *inp); +/** Return the position for end_of_fixed_part when we parsed this + * object + */ +const uint8_t * auth1_get_end_of_fixed_part(const auth1_t *inp); +/** Return the (constant) length of the array holding the rand field + * of the auth1_t in 'inp'. + */ +size_t auth1_getlen_rand(const auth1_t *inp); +/** Return the element at position 'idx' of the fixed array field rand + * of the auth1_t in 'inp'. + */ +uint8_t auth1_get_rand(const auth1_t *inp, size_t idx); +/** Change the element at position 'idx' of the fixed array field rand + * of the auth1_t in 'inp', so that it will hold the value 'elt'. + */ +int auth1_set_rand(auth1_t *inp, size_t idx, uint8_t elt); +/** Return a pointer to the 24-element array field rand of 'inp'. + */ +uint8_t * auth1_getarray_rand(auth1_t *inp); +/** Return the position for end_of_signed when we parsed this object + */ +const uint8_t * auth1_get_end_of_signed(const auth1_t *inp); +/** Return the length of the dynamic array holding the sig field of + * the auth1_t in 'inp'. + */ +size_t auth1_getlen_sig(const auth1_t *inp); +/** Return the element at position 'idx' of the dynamic array field + * sig of the auth1_t in 'inp'. + */ +uint8_t auth1_get_sig(auth1_t *inp, size_t idx); +/** Change the element at position 'idx' of the dynamic array field + * sig of the auth1_t in 'inp', so that it will hold the value 'elt'. + */ +int auth1_set_sig(auth1_t *inp, size_t idx, uint8_t elt); +/** Append a new element 'elt' to the dynamic array field sig of the + * auth1_t in 'inp'. + */ +int auth1_add_sig(auth1_t *inp, uint8_t elt); +/** Return a pointer to the variable-length array field sig of 'inp'. + */ +uint8_t * auth1_getarray_sig(auth1_t *inp); +/** Change the length of the variable-length array field sig of 'inp' + * to 'newlen'.Fill extra elements with 0. Return 0 on success; return + * -1 and set the error code on 'inp' on failure. + */ +int auth1_setlen_sig(auth1_t *inp, size_t newlen); +/** Return a newly allocated certs_cell with all elements set to zero. + */ +certs_cell_t *certs_cell_new(void); +/** Release all storage held by the certs_cell in 'victim'. (Do + * nothing if 'victim' is NULL.) + */ +void certs_cell_free(certs_cell_t *victim); +/** Try to parse a certs_cell from the buffer in 'input', using up to + * 'len_in' bytes from the input buffer. On success, return the number + * of bytes consumed and set *output to the newly allocated + * certs_cell_t. On failure, return -2 if the input appears truncated, + * and -1 if the input is otherwise invalid. + */ +ssize_t certs_cell_parse(certs_cell_t **output, const uint8_t *input, const size_t len_in); +/** Return the number of bytes we expect to need to encode the + * certs_cell in 'obj'. On failure, return a negative value. Note that + * this value may be an overestimate, and can even be an underestimate + * for certain unencodeable objects. + */ +ssize_t certs_cell_encoded_len(const certs_cell_t *obj); +/** Try to encode the certs_cell from 'input' into the buffer at + * 'output', using up to 'avail' bytes of the output buffer. On + * success, return the number of bytes used. On failure, return -2 if + * the buffer was not long enough, and -1 if the input was invalid. + */ +ssize_t certs_cell_encode(uint8_t *output, const size_t avail, const certs_cell_t *input); +/** Check whether the internal state of the certs_cell in 'obj' is + * consistent. Return NULL if it is, and a short message if it is not. + */ +const char *certs_cell_check(const certs_cell_t *obj); +/** Clear any errors that were set on the object 'obj' by its setter + * functions. Return true iff errors were cleared. + */ +int certs_cell_clear_errors(certs_cell_t *obj); +/** Return the value of the n_certs field of the certs_cell_t in 'inp' + */ +uint8_t certs_cell_get_n_certs(certs_cell_t *inp); +/** Set the value of the n_certs field of the certs_cell_t in 'inp' to + * 'val'. Return 0 on success; return -1 and set the error code on + * 'inp' on failure. + */ +int certs_cell_set_n_certs(certs_cell_t *inp, uint8_t val); +/** Return the length of the dynamic array holding the certs field of + * the certs_cell_t in 'inp'. + */ +size_t certs_cell_getlen_certs(const certs_cell_t *inp); +/** Return the element at position 'idx' of the dynamic array field + * certs of the certs_cell_t in 'inp'. + */ +struct certs_cell_cert_st * certs_cell_get_certs(certs_cell_t *inp, size_t idx); +/** Change the element at position 'idx' of the dynamic array field + * certs of the certs_cell_t in 'inp', so that it will hold the value + * 'elt'. Free the previous value, if any. + */ +int certs_cell_set_certs(certs_cell_t *inp, size_t idx, struct certs_cell_cert_st * elt); +/** As certs_cell_set_certs, but does not free the previous value. + */ +int certs_cell_set0_certs(certs_cell_t *inp, size_t idx, struct certs_cell_cert_st * elt); +/** Append a new element 'elt' to the dynamic array field certs of the + * certs_cell_t in 'inp'. + */ +int certs_cell_add_certs(certs_cell_t *inp, struct certs_cell_cert_st * elt); +/** Return a pointer to the variable-length array field certs of + * 'inp'. + */ +struct certs_cell_cert_st * * certs_cell_getarray_certs(certs_cell_t *inp); +/** Change the length of the variable-length array field certs of + * 'inp' to 'newlen'.Fill extra elements with NULL; free removed + * elements. Return 0 on success; return -1 and set the error code on + * 'inp' on failure. + */ +int certs_cell_setlen_certs(certs_cell_t *inp, size_t newlen); + + +#endif diff --git a/src/trunnel/link_handshake.trunnel b/src/trunnel/link_handshake.trunnel new file mode 100644 index 0000000000..b858e17c60 --- /dev/null +++ b/src/trunnel/link_handshake.trunnel @@ -0,0 +1,57 @@ + +struct certs_cell { + u8 n_certs; + struct certs_cell_cert certs[n_certs]; +} + +const CERTTYPE_RSA1024_ID_LINK = 1; +const CERTTYPE_RSA1024_ID_ID = 2; +const CERTTYPE_RSA1024_ID_AUTH = 3; +const CERTTYPE_ED_ID_SIGN = 4; +const CERTTYPE_ED_SIGN_LINK = 5; +const CERTTYPE_ED_SIGN_AUTH = 6; +const CERTTYPE_RSA1024_ID_EDID = 7; + +struct certs_cell_cert { + u8 cert_type; + u16 cert_len; + u8 body[cert_len]; +} + +struct rsa_ed_crosscert { + u8 ed_key[32]; + u32 expiration; + @ptr end_of_signed; + u8 sig_len; + u8 sig[sig_len]; // mismatches spec. +} + +struct auth_challenge_cell { + u8 challenge[32]; + u16 n_methods; + u16 methods[n_methods]; +} + +context auth_ctx { + u8 is_ed; +} + +struct auth1 with context auth_ctx { + u8 type[8]; + u8 cid[32]; + u8 sid[32]; + union u1[auth_ctx.is_ed] { + 0 : ; + 1 : u8 cid_ed[32]; + u8 sid_ed[32]; + default: fail; + }; + u8 slog[32]; + u8 clog[32]; + u8 scert[32]; + u8 tlssecrets[32]; + @ptr end_of_fixed_part; + u8 rand[24]; + @ptr end_of_signed; + u8 sig[]; +}