aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/crypto_ed25519.c10
-rw-r--r--src/common/crypto_ed25519.h5
-rw-r--r--src/or/config.h4
-rw-r--r--src/or/include.am13
-rw-r--r--src/or/main.c2
-rw-r--r--src/or/or.h1
-rw-r--r--src/or/router.c5
-rw-r--r--src/or/routerkeys.c453
-rw-r--r--src/or/routerkeys.h44
-rw-r--r--src/or/torcert.c208
-rw-r--r--src/or/torcert.h66
-rw-r--r--src/test/include.am2
-rw-r--r--src/test/test_routerkeys.c444
-rw-r--r--src/trunnel/ed25519_cert.c887
-rw-r--r--src/trunnel/ed25519_cert.h288
-rw-r--r--src/trunnel/ed25519_cert.trunnel76
-rw-r--r--src/trunnel/include.am18
17 files changed, 2513 insertions, 13 deletions
diff --git a/src/common/crypto_ed25519.c b/src/common/crypto_ed25519.c
index f2e6945ac8..7e8b00abef 100644
--- a/src/common/crypto_ed25519.c
+++ b/src/common/crypto_ed25519.c
@@ -351,3 +351,13 @@ ed25519_pubkey_read_from_file(ed25519_public_key_t *pubkey_out,
return 0;
}
+void
+ed25519_keypair_free(ed25519_keypair_t *kp)
+{
+ if (! kp)
+ return;
+
+ memwipe(kp, 0, sizeof(*kp));
+ tor_free(kp);
+}
+
diff --git a/src/common/crypto_ed25519.h b/src/common/crypto_ed25519.h
index 7efa74bff5..8e06191fc5 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. */
@@ -109,5 +110,7 @@ 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);
+
#endif
diff --git a/src/or/config.h b/src/or/config.h
index b064f05321..e97248540c 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/include.am b/src/or/include.am
index b44e1099dc..a85b0dde05 100644
--- a/src/or/include.am
+++ b/src/or/include.am
@@ -71,12 +71,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) \
@@ -87,7 +89,7 @@ src_or_libtor_testing_a_SOURCES = $(LIBTOR_A_SOURCES)
#libtor_a_LIBADD = ../common/libor.a ../common/libor-crypto.a \
# ../common/libor-event.a
-
+#src_or_libtor_a_LIBADD = src/trunnel/libor-trunnel.a
src_or_tor_SOURCES = src/or/tor_main.c
AM_CPPFLAGS += -I$(srcdir)/src/or -Isrc/or
@@ -109,7 +111,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 +122,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 = ./src/or/tor-cov
@@ -180,12 +182,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/main.c b/src/or/main.c
index bc89458e87..8b82a31d7a 100644
--- a/src/or/main.c
+++ b/src/or/main.c
@@ -51,6 +51,7 @@
#include "rendservice.h"
#include "rephist.h"
#include "router.h"
+#include "routerkeys.h"
#include "routerlist.h"
#include "routerparse.h"
#include "scheduler.h"
@@ -2648,6 +2649,7 @@ tor_free_all(int postfork)
config_free_all();
or_state_free_all();
router_free_all();
+ routerkeys_free_all();
policies_free_all();
}
if (!postfork) {
diff --git a/src/or/or.h b/src/or/or.h
index 19694a081f..2044e844c3 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.
diff --git a/src/or/router.c b/src/or/router.c
index 2ddaa895fc..135561381f 100644
--- a/src/or/router.c
+++ b/src/or/router.c
@@ -26,6 +26,7 @@
#include "relay.h"
#include "rephist.h"
#include "router.h"
+#include "routerkeys.h"
#include "routerlist.h"
#include "routerparse.h"
#include "statefile.h"
@@ -861,6 +862,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);
diff --git a/src/or/routerkeys.c b/src/or/routerkeys.c
new file mode 100644
index 0000000000..fb61c310bd
--- /dev/null
+++ b/src/or/routerkeys.c
@@ -0,0 +1,453 @@
+/* Copyright (c) 2014, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "or.h"
+#include "config.h"
+#include "routerkeys.h"
+#include "torcert.h"
+
+/**
+ * Read an ed25519 key and associated certificates from files beginning with
+ * <b>fname</b>, with certificate type <b>cert_type</b>. On failure, return
+ * NULL; on success return the keypair.
+ *
+ * If INIT_ED_KEY_CREATE is set in <b>flags</b>, 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 <b>flags</b>, load/create a certificate
+ * too and store it in *<b>cert_out</b>. Fail if the cert can't be
+ * found/created. To create a certificate, <b>signing_key</b> must be set to
+ * the key that should sign it; <b>now</b> to the current time, and
+ * <b>lifetime</b> to the lifetime of the key.
+ *
+ * If INIT_ED_KEY_REPLACE is set in <b>flags</b>, then create and save new key
+ * whether we can read the old one or not.
+ *
+ * If INIT_ED_KEY_EXTRA_STRONG is set in <b>flags</b>, set the extra_strong
+ * flag when creating the secret key.
+ *
+ * If INIT_ED_KEY_INCLUDE_SIGNING_KEY_IN_CERT is set in <b>flags</b>, and
+ * we create a new certificate, create it with the signing key embedded.
+ *
+ * If INIT_ED_KEY_SPLIT is set in <b>flags</b>, 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 <b>flags</b>, and we find a
+ * public key file but no secret key file, return successfully anyway.
+ */
+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 &&
+ 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 && (flags & INIT_ED_KEY_MISSING_SECRET_OK)) {
+ 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 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_link_key = NULL;
+static ed25519_keypair_t *current_auth_key = NULL;
+static tor_cert_t *signing_key_cert = NULL;
+static tor_cert_t *link_key_cert = NULL;
+static tor_cert_t *auth_key_cert = NULL;
+
+/**
+ * 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 *link = NULL;
+ ed25519_keypair_t *auth = NULL;
+ const ed25519_keypair_t *use_signing = NULL;
+ tor_cert_t *sign_cert = NULL;
+ tor_cert_t *link_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 */
+
+ /* XXXX use options. */
+ (void) options;
+
+ id = ed_key_init_from_file(
+ options_get_datadir_fname2(options, "keys", "ed25519_master_id"),
+ (INIT_ED_KEY_CREATE|INIT_ED_KEY_SPLIT|
+ INIT_ED_KEY_MISSING_SECRET_OK|
+ INIT_ED_KEY_EXTRA_STRONG),
+ LOG_WARN, NULL, 0, 0, 0, NULL);
+ if (!id)
+ FAIL("Missing identity key");
+
+ if (!master_signing_key || EXPIRES_SOON(signing_key_cert, 86400/*???*/)) {
+ uint32_t flags = (INIT_ED_KEY_CREATE|
+ INIT_ED_KEY_EXTRA_STRONG|
+ INIT_ED_KEY_NEEDCERT|
+ INIT_ED_KEY_INCLUDE_SIGNING_KEY_IN_CERT);
+ const ed25519_keypair_t *sign_with_id = id;
+ if (master_signing_key) {
+ flags |= INIT_ED_KEY_REPLACE; /* it's expired, so force-replace it. */
+ }
+ if (tor_mem_is_zero((char*)id->seckey.seckey, sizeof(id->seckey))) {
+ sign_with_id = NULL;
+ flags &= ~INIT_ED_KEY_CREATE;
+ }
+ sign = ed_key_init_from_file(
+ options_get_datadir_fname2(options, "keys", "ed25519_signing"),
+ flags, LOG_WARN,
+ sign_with_id, now, 30*86400/*XXX option*/,
+ CERT_TYPE_ID_SIGNING, &sign_cert);
+ if (!sign)
+ FAIL("Missing signing key");
+ use_signing = sign;
+ } else {
+ use_signing = master_signing_key;
+ }
+
+ /* 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 (!current_link_key || EXPIRES_SOON(link_key_cert, 7200/*???*/)) {
+ link = ed_key_new(use_signing, INIT_ED_KEY_NEEDCERT,
+ now, 2*86400/*XXX option??*/,
+ CERT_TYPE_SIGNING_LINK, &link_cert);
+
+ if (!link)
+ FAIL("Can't create link key");
+ }
+
+ if (!current_auth_key || EXPIRES_SOON(auth_key_cert, 7200)/*???*/) {
+ auth = ed_key_new(use_signing, INIT_ED_KEY_NEEDCERT,
+ now, 2*86400/*XXX option??*/,
+ 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 (link) {
+ SET_KEY(current_link_key, link);
+ SET_CERT(link_key_cert, link_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(link);
+ ed25519_keypair_free(auth);
+ tor_cert_free(sign_cert);
+ tor_cert_free(link_cert);
+ tor_cert_free(auth_cert);
+ return -1;
+#undef FAIL
+#undef SET_KEY
+#undef SET_CERT
+#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_link_keypair(void)
+{
+ return current_link_key;
+}
+
+const ed25519_keypair_t *
+get_current_auth_keypair(void)
+{
+ return current_auth_key;
+}
+
+const tor_cert_t *
+get_current_link_key_cert(void)
+{
+ return link_key_cert;
+}
+
+const tor_cert_t *
+get_current_auth_key_cert(void)
+{
+ return auth_key_cert;
+}
+
+void
+routerkeys_free_all(void)
+{
+ ed25519_keypair_free(master_identity_key);
+ ed25519_keypair_free(master_signing_key);
+ ed25519_keypair_free(current_link_key);
+ ed25519_keypair_free(current_auth_key);
+ tor_cert_free(signing_key_cert);
+ tor_cert_free(link_key_cert);
+ tor_cert_free(auth_key_cert);
+
+ master_identity_key = master_signing_key = NULL;
+ current_link_key = current_auth_key = NULL;
+ signing_key_cert = link_key_cert = auth_key_cert = NULL;
+}
+
diff --git a/src/or/routerkeys.h b/src/or/routerkeys.h
new file mode 100644
index 0000000000..eb21401d56
--- /dev/null
+++ b/src/or/routerkeys.h
@@ -0,0 +1,44 @@
+/* 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)
+
+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_link_keypair(void);
+const ed25519_keypair_t *get_current_auth_keypair(void);
+const struct tor_cert_st *get_current_link_key_cert(void);
+const struct tor_cert_st *get_current_auth_key_cert(void);
+
+int load_ed_keys(const or_options_t *options, time_t now);
+void routerkeys_free_all(void);
+
+#endif
+
diff --git a/src/or/torcert.c b/src/or/torcert.c
new file mode 100644
index 0000000000..478ec4d5ac
--- /dev/null
+++ b/src/or/torcert.c
@@ -0,0 +1,208 @@
+/* Copyright (c) 2014, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "torcert.h"
+#include "crypto.h"
+#include "ed25519_cert.h"
+#include "torlog.h"
+#include "util.h"
+#include "compat.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 <b>cert_type</b> to
+ * authenticate <b>signed_key</b> using the key <b>signing_key</b>. The
+ * certificate should remain valid for at least <b>lifetime</b> seconds after
+ * <b>now</b>.
+ *
+ * If CERT_FLAG_INCLUDE_SIGNING_KEY is set in <b>flags</b>, embed
+ * the public part of <b>signing_key</b> 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 <b>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 <b>len</b> bytes in <b>encoded</b>. */
+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 <b>checkable_out</b> with the information needed to check
+ * the signature on <b>cert</b> with <b>pubkey</b>. */
+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 <b>cert</b> with <b>pubkey</b> relative to
+ * the current time <b>now</b>. Return 0 on success, -1 on failure.
+ * Sets flags in <b>cert</b> 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;
+ }
+}
+
diff --git a/src/or/torcert.h b/src/or/torcert.h
new file mode 100644
index 0000000000..7e9c3f5b2d
--- /dev/null
+++ b/src/or/torcert.h
@@ -0,0 +1,66 @@
+/* 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_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 <b>encoded</b> */
+ 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);
+
+#endif
+
diff --git a/src/test/include.am b/src/test/include.am
index d20d2f66b9..44f63a7230 100644
--- a/src/test/include.am
+++ b/src/test/include.am
@@ -104,7 +104,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_routerkeys.c b/src/test/test_routerkeys.c
index 60b6bb5a72..63284b798b 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 <direct.h>
+#endif
+
static void
test_routerkeys_write_fingerprint(void *arg)
{
@@ -75,11 +81,447 @@ 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;
+
+ 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);
+}
+
+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, link, auth;
+ // tor_cert_t *cert_is, *cert_sl, *cert_auth;
+
+#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_assert(get_master_identity_key());
+ tt_assert(get_master_identity_key());
+ tt_assert(get_master_signing_keypair());
+ tt_assert(get_current_link_keypair());
+ tt_assert(get_current_auth_keypair());
+ tt_assert(get_master_signing_key_cert());
+ tt_assert(get_current_link_key_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(&link, get_current_link_keypair(), sizeof(link));
+ memcpy(&auth, get_current_auth_keypair(), sizeof(auth));
+
+ /* Call load_ed_keys again, but nothing has changed. */
+ tt_int_op(0, ==, load_ed_keys(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(&link, ==, get_current_link_keypair(), sizeof(link));
+ tt_mem_op(&auth, ==, get_current_auth_keypair(), sizeof(auth));
+
+ /* Force a reload: we make new link/auth keys. */
+ routerkeys_free_all();
+ tt_int_op(0, ==, load_ed_keys(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(&link, !=, get_current_link_keypair(), sizeof(link));
+ tt_mem_op(&auth, !=, get_current_auth_keypair(), sizeof(auth));
+ tt_assert(get_master_signing_key_cert());
+ tt_assert(get_current_link_key_cert());
+ tt_assert(get_current_auth_key_cert());
+ memcpy(&link, get_current_link_keypair(), sizeof(link));
+ 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_mem_op(&id, ==, get_master_identity_key(), sizeof(id));
+ tt_mem_op(&sign, ==, get_master_signing_keypair(), sizeof(sign));
+ tt_mem_op(&link, !=, get_current_link_keypair(), sizeof(link));
+ tt_mem_op(&auth, !=, get_current_auth_keypair(), sizeof(auth));
+ tt_assert(get_master_signing_key_cert());
+ tt_assert(get_current_link_key_cert());
+ tt_assert(get_current_auth_key_cert());
+ memcpy(&link, get_current_link_keypair(), sizeof(link));
+ 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_mem_op(&id, ==, get_master_identity_key(), sizeof(id));
+ tt_mem_op(&sign, !=, get_master_signing_keypair(), sizeof(sign));
+ tt_mem_op(&link, !=, get_current_link_keypair(), sizeof(link));
+ tt_mem_op(&auth, !=, get_current_auth_keypair(), sizeof(auth));
+ tt_assert(get_master_signing_key_cert());
+ tt_assert(get_current_link_key_cert());
+ tt_assert(get_current_auth_key_cert());
+ memcpy(&sign, get_master_signing_keypair(), sizeof(sign));
+ memcpy(&link, get_current_link_keypair(), sizeof(link));
+ 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_mem_op(&id, ==, get_master_identity_key(), sizeof(id));
+ tt_mem_op(&sign, ==, get_master_signing_keypair(), sizeof(sign));
+ tt_mem_op(&link, !=, get_current_link_keypair(), sizeof(link));
+ tt_mem_op(&auth, !=, get_current_auth_keypair(), sizeof(auth));
+ tt_assert(get_master_signing_key_cert());
+ tt_assert(get_current_link_key_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();
+}
+
#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),
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 <stdlib.h>
+#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 <stdint.h>
+#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 c7ac1679d0..6e7851aafe 100644
--- a/src/trunnel/include.am
+++ b/src/trunnel/include.am
@@ -9,15 +9,21 @@ endif
AM_CPPFLAGS += -I$(srcdir)/src/ext/trunnel -I$(srcdir)/src/trunnel
+TRUNNELINPUTS = \
+ src/trunnel/ed25519_cert.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/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/pwbox.h
src_trunnel_libor_trunnel_a_SOURCES = $(TRUNNELSOURCES)
src_trunnel_libor_trunnel_a_CPPFLAGS = -DTRUNNEL_LOCAL_H $(AM_CPPFLAGS)