[tor-commits] [tor/master] Verify ed25519 link handshake certificates

nickm at torproject.org nickm at torproject.org
Thu Nov 3 13:18:59 UTC 2016


commit b4a5c779014b35d60f4a2ddcec31e7075ad52995
Author: Nick Mathewson <nickm at torproject.org>
Date:   Fri Jun 19 09:09:49 2015 -0400

    Verify ed25519 link handshake certificates
    
    This code stores the ed certs as appropriate, and tries to check
    them. The Ed25519 result is not yet used, and (because of its
    behavior) this will break RSA authenticate cells.  That will get
    fixed as we go, however.
    
    This should implement 19157, but it needs tests, and it needs
    to get wired in.
---
 src/or/channeltls.c    |  90 ++++++++++++++++++-----------
 src/or/connection_or.c |  10 +++-
 src/or/or.h            |  27 +++++++--
 src/or/torcert.c       | 150 +++++++++++++++++++++++++++++++++++++++++++++++--
 src/or/torcert.h       |  10 ++++
 5 files changed, 240 insertions(+), 47 deletions(-)

diff --git a/src/or/channeltls.c b/src/or/channeltls.c
index d1fae92..6de3bd4 100644
--- a/src/or/channeltls.c
+++ b/src/or/channeltls.c
@@ -1788,8 +1788,9 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan)
    * of ed/x509 */
   tor_x509_cert_t *x509_certs[MAX_CERT_TYPE_WANTED + 1];
   tor_cert_t *ed_certs[MAX_CERT_TYPE_WANTED + 1];
+  uint8_t *rsa_ed_cc_cert = NULL;
+  size_t rsa_ed_cc_cert_len = 0;
 
-  rsa_ed_crosscert_t *rsa_crosscert = NULL;
   int n_certs, i;
   certs_cell_t *cc = NULL;
 
@@ -1879,38 +1880,45 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan)
         }
         break;
       }
+
      case CERT_ENCODING_RSA_CROSSCERT: {
-        rsa_ed_crosscert_t *cc_cert = NULL;
-        ssize_t n = rsa_ed_crosscert_parse(&cc_cert, cert_body, cert_len);
-        if (n != cert_len) {
-          log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
-                 "Received unparseable RS1024-Ed25519 crosscert "
-                 " in CERTS cell from %s:%d",
-                 safe_str(chan->conn->base_.address),
-                 chan->conn->base_.port);
+        if (rsa_ed_cc_cert) {
+          ERR("Duplicate RSA->Ed25519 crosscert");
         } else {
-          if (rsa_crosscert) {
-            rsa_ed_crosscert_free(cc_cert);
-            ERR("Duplicate RSA->Ed25519 crosscert");
-          } else {
-            rsa_crosscert = cc_cert;
-          }
+          rsa_ed_cc_cert = tor_memdup(cert_body, cert_len);
+          rsa_ed_cc_cert_len = cert_len;
         }
         break;
       }
     }
   }
 
-  tor_x509_cert_t *id_cert = x509_certs[OR_CERT_TYPE_ID_1024];
-  tor_x509_cert_t *auth_cert = x509_certs[OR_CERT_TYPE_AUTH_1024];
-  tor_x509_cert_t *link_cert = x509_certs[OR_CERT_TYPE_TLS_LINK];
-
+  /* Move the certificates we (might) want into the handshake_state->certs
+   * structure. */
+  tor_x509_cert_t *id_cert = x509_certs[CERTTYPE_RSA1024_ID_ID];
+  tor_x509_cert_t *auth_cert = x509_certs[CERTTYPE_RSA1024_ID_AUTH];
+  tor_x509_cert_t *link_cert = x509_certs[CERTTYPE_RSA1024_ID_LINK];
   chan->conn->handshake_state->certs->auth_cert = auth_cert;
   chan->conn->handshake_state->certs->link_cert = link_cert;
   chan->conn->handshake_state->certs->id_cert = id_cert;
-  x509_certs[OR_CERT_TYPE_ID_1024] =
-    x509_certs[OR_CERT_TYPE_AUTH_1024] =
-    x509_certs[OR_CERT_TYPE_TLS_LINK] = NULL;
+  x509_certs[CERTTYPE_RSA1024_ID_ID] =
+    x509_certs[CERTTYPE_RSA1024_ID_AUTH] =
+    x509_certs[CERTTYPE_RSA1024_ID_LINK] = NULL;
+
+  tor_cert_t *ed_id_sign = ed_certs[CERTTYPE_ED_ID_SIGN];
+  tor_cert_t *ed_sign_link = ed_certs[CERTTYPE_ED_SIGN_LINK];
+  tor_cert_t *ed_sign_auth = ed_certs[CERTTYPE_ED_SIGN_AUTH];
+  chan->conn->handshake_state->certs->ed_id_sign = ed_id_sign;
+  chan->conn->handshake_state->certs->ed_sign_link = ed_sign_link;
+  chan->conn->handshake_state->certs->ed_sign_auth = ed_sign_auth;
+  ed_certs[CERTTYPE_ED_ID_SIGN] =
+    ed_certs[CERTTYPE_ED_SIGN_LINK] =
+    ed_certs[CERTTYPE_ED_SIGN_AUTH] = NULL;
+
+  chan->conn->handshake_state->certs->ed_rsa_crosscert = rsa_ed_cc_cert;
+  chan->conn->handshake_state->certs->ed_rsa_crosscert_len =
+    rsa_ed_cc_cert_len;
+  rsa_ed_cc_cert = NULL;
 
   int severity;
   /* Note that this warns more loudly about time and validity if we were
@@ -1922,11 +1930,17 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan)
   else
     severity = LOG_PROTOCOL_WARN;
 
-  if (! or_handshake_certs_rsa_ok(severity,
-                                  chan->conn->handshake_state->certs,
-                                  chan->conn->tls,
-                                  time(NULL)))
-    ERR("Invalid RSA certificates!");
+  const ed25519_public_key_t *checked_ed_id = NULL;
+  const common_digests_t *checked_rsa_id = NULL;
+  or_handshake_certs_check_both(severity,
+                                chan->conn->handshake_state->certs,
+                                chan->conn->tls,
+                                time(NULL),
+                                &checked_ed_id,
+                                &checked_rsa_id);
+
+  if (!checked_rsa_id)
+    ERR("Invalid certificate chain!");
 
   if (chan->conn->handshake_state->started_here) {
     /* No more information is needed. */
@@ -1934,8 +1948,7 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan)
     chan->conn->handshake_state->authenticated = 1;
     chan->conn->handshake_state->authenticated_rsa = 1;
     {
-      const common_digests_t *id_digests =
-        tor_x509_cert_get_id_digests(id_cert);
+      const common_digests_t *id_digests = checked_rsa_id;
       crypto_pk_t *identity_rcvd;
       if (!id_digests)
         ERR("Couldn't compute digests for key in ID cert");
@@ -1950,14 +1963,22 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan)
       crypto_pk_free(identity_rcvd);
     }
 
+    if (checked_ed_id) {
+      chan->conn->handshake_state->authenticated_ed25519 = 1;
+      memcpy(&chan->conn->handshake_state->authenticated_ed25519_peer_id,
+             checked_ed_id, sizeof(ed25519_public_key_t));
+    }
+
     if (connection_or_client_learned_peer_id(chan->conn,
                   chan->conn->handshake_state->authenticated_rsa_peer_id,
-                  NULL) < 0)
+                  checked_ed_id) < 0)
       ERR("Problem setting or checking peer id");
 
     log_info(LD_OR,
-             "Got some good certificates from %s:%d: Authenticated it.",
-             safe_str(chan->conn->base_.address), chan->conn->base_.port);
+             "Got some good certificates from %s:%d: Authenticated it with "
+             "RSA%s",
+             safe_str(chan->conn->base_.address), chan->conn->base_.port,
+             checked_ed_id ? " and Ed25519" : "");
 
     if (!public_server_mode(get_options())) {
       /* If we initiated the connection and we are not a public server, we
@@ -1968,8 +1989,9 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan)
   } else {
     /* We can't call it authenticated till we see an AUTHENTICATE cell. */
     log_info(LD_OR,
-             "Got some good certificates from %s:%d: "
+             "Got some good RSA%s certificates from %s:%d. "
              "Waiting for AUTHENTICATE.",
+             checked_ed_id ? " and Ed25519" : "",
              safe_str(chan->conn->base_.address),
              chan->conn->base_.port);
     /* XXXX check more stuff? */
@@ -1992,7 +2014,7 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan)
   for (unsigned u = 0; u < ARRAY_LENGTH(ed_certs); ++u) {
     tor_cert_free(ed_certs[u]);
   }
-  rsa_ed_crosscert_free(rsa_crosscert);
+  tor_free(rsa_ed_cc_cert);
   certs_cell_free(cc);
 #undef ERR
 }
diff --git a/src/or/connection_or.c b/src/or/connection_or.c
index 9048fde..b922e97 100644
--- a/src/or/connection_or.c
+++ b/src/or/connection_or.c
@@ -2391,10 +2391,12 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn,
 
   if (is_ed) {
     const ed25519_public_key_t *my_ed_id, *their_ed_id;
-    if (!conn->handshake_state->certs->ed_id_sign_cert)
+    if (!conn->handshake_state->certs->ed_id_sign) {
+      log_warn(LD_OR, "Ed authenticate without Ed ID cert from peer.");
       goto err;
+    }
     my_ed_id = get_master_identity_key();
-    their_ed_id = &conn->handshake_state->certs->ed_id_sign_cert->signing_key;
+    their_ed_id = &conn->handshake_state->certs->ed_id_sign->signing_key;
 
     const uint8_t *cid_ed = (server ? their_ed_id : my_ed_id)->pubkey;
     const uint8_t *sid_ed = (server ? my_ed_id : their_ed_id)->pubkey;
@@ -2500,8 +2502,10 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn,
 
   if (ed_signing_key && is_ed) {
     ed25519_signature_t sig;
-    if (ed25519_sign(&sig, out, len, ed_signing_key) < 0)
+    if (ed25519_sign(&sig, out, len, ed_signing_key) < 0) {
+      log_warn(LD_OR, "Unable to sign ed25519 cert");
       goto err;
+    }
     auth1_setlen_sig(auth, ED25519_SIG_LEN);
     memcpy(auth1_getarray_sig(auth), sig.sig, ED25519_SIG_LEN);
 
diff --git a/src/or/or.h b/src/or/or.h
index 65d4057..e6162c5 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -1386,17 +1386,32 @@ typedef struct listener_connection_t {
  * signs. */
 #define V3_AUTH_BODY_LEN (V3_AUTH_FIXED_PART_LEN + 8 + 16)
 
+/** Structure to hold all the certificates we've received on an OR connection
+ */
 typedef struct or_handshake_certs_t {
-  /** DOCDOC */
+  /** True iff we originated this connection. */
   int started_here;
-  /** The cert for the key that's supposed to sign the AUTHENTICATE cell */
+  /** The cert for the 'auth' RSA key that's supposed to sign the AUTHENTICATE
+   * cell. Signed with the RSA identity key. */
   tor_x509_cert_t *auth_cert;
-  /** DOCDOC */
+  /** The cert for the 'link' RSA key that was used to negotiate the TLS
+   *  connection.  Signed with the RSA identity key. */
   tor_x509_cert_t *link_cert;
-  /** A self-signed identity certificate */
+  /** A self-signed identity certificate: the RSA identity key signed
+   * with itself.  */
   tor_x509_cert_t *id_cert;
-  /** DOCDOC */
-  struct tor_cert_st *ed_id_sign_cert;
+  /** The Ed25519 signing key, signed with the Ed25519 identity key. */
+  struct tor_cert_st *ed_id_sign;
+  /** A digest of the X509 link certificate for the TLS connection, signed
+   * with the Ed25519 siging key. */
+  struct tor_cert_st *ed_sign_link;
+  /** The Ed25519 authentication key (that's supposed to sign an AUTHENTICATE
+   * cell) , signed with the Ed25519 siging key. */
+  struct tor_cert_st *ed_sign_auth;
+  /** The Ed25519 identity key, crosssigned with the RSA identity key. */
+  uint8_t *ed_rsa_crosscert;
+  /** The length of <b>ed_rsa_crosscert</b> in bytes */
+  size_t ed_rsa_crosscert_len;
 } or_handshake_certs_t;
 
 /** Stores flags and information related to the portion of a v2/v3 Tor OR
diff --git a/src/or/torcert.c b/src/or/torcert.c
index ef7775e..cff1ed1 100644
--- a/src/or/torcert.c
+++ b/src/or/torcert.c
@@ -420,7 +420,7 @@ or_handshake_certs_new(void)
   return tor_malloc_zero(sizeof(or_handshake_certs_t));
 }
 
-/** DODCDOC */
+/** Release all storage held in <b>certs</b> */
 void
 or_handshake_certs_free(or_handshake_certs_t *certs)
 {
@@ -428,8 +428,14 @@ or_handshake_certs_free(or_handshake_certs_t *certs)
     return;
 
   tor_x509_cert_free(certs->auth_cert);
+  tor_x509_cert_free(certs->link_cert);
   tor_x509_cert_free(certs->id_cert);
 
+  tor_cert_free(certs->ed_id_sign);
+  tor_cert_free(certs->ed_sign_link);
+  tor_cert_free(certs->ed_sign_auth);
+  tor_free(certs->ed_rsa_crosscert);
+
   memwipe(certs, 0xBD, sizeof(*certs));
   tor_free(certs);
 }
@@ -477,9 +483,145 @@ or_handshake_certs_rsa_ok(int severity,
   return 1;
 }
 
+/** Check all the ed25519 certificates in <b>certs</b> against each other, and
+ * against the peer certificate in <b>tls</b> if appropriate.  On success,
+ * return 0; on failure, return a negative value and warn at level
+ * <b>severity</b> */
 int
-or_handshake_certs_ed25519_ok(or_handshake_certs_t *certs)
+or_handshake_certs_ed25519_ok(int severity,
+                              or_handshake_certs_t *certs,
+                              tor_tls_t *tls,
+                              time_t now)
 {
-  (void) certs;
-  return 0;
+  ed25519_checkable_t check[10];
+  unsigned n_checkable = 0;
+  time_t expiration = TIME_MAX;
+
+#define ADDCERT(cert, pk)                                               \
+  do {                                                                  \
+    tor_assert(n_checkable < ARRAY_LENGTH(check));                      \
+    if (tor_cert_get_checkable_sig(&check[n_checkable++], cert, pk,     \
+                                   &expiration) < 0)                    \
+      ERR("Could not get checkable cert.");                             \
+  } while (0)
+
+  if (! certs->ed_id_sign || !certs->ed_id_sign->signing_key_included)
+    ERR("No signing key");
+  ADDCERT(certs->ed_id_sign, NULL);
+
+  if (certs->started_here) {
+    if (! certs->ed_sign_link)
+      ERR("No link key");
+    {
+      /* check for a match with the TLS cert. */
+      tor_x509_cert_t *peer_cert = tor_tls_get_peer_cert(tls);
+      /* XXXX Does 'cert' match spec in this case? I hope so; if not, fix
+       * spec */
+      if (!peer_cert)
+        ERR("No x509 peer cert");
+      const common_digests_t *peer_cert_digests =
+        tor_x509_cert_get_cert_digests(peer_cert);
+      int okay = tor_memeq(peer_cert_digests->d[DIGEST_SHA256],
+                           certs->ed_sign_link->signed_key.pubkey,
+                           DIGEST256_LEN);
+      tor_x509_cert_free(peer_cert);
+      if (!okay)
+        ERR("link certificate does not match TLS certificate");
+    }
+
+    ADDCERT(certs->ed_sign_link, &certs->ed_id_sign->signed_key);
+
+  } else {
+    if (! certs->ed_sign_auth)
+      ERR("No link authentiction key");
+    ADDCERT(certs->ed_sign_auth, &certs->ed_id_sign->signed_key);
+  }
+
+  if (expiration < now) {
+    ERR("At least one certificate expired.");
+  }
+
+  /* Okay, we've gotten ready to check all the Ed25519 certificates.
+   * Now, we are going to check the RSA certificate's cross-certification
+   * with the ED certificates.
+   *
+   * FFFF In the future, we might want to make this optional.
+   */
+
+  tor_x509_cert_t *rsa_id_cert = certs->id_cert;
+  if (!rsa_id_cert) {
+    ERR("Missing legacy RSA ID certificate");
+  }
+  if (! tor_tls_cert_is_valid(severity, rsa_id_cert, rsa_id_cert, now, 1)) {
+    ERR("The legacy RSA ID certificate was not valid");
+  }
+  crypto_pk_t *rsa_id_key = tor_tls_cert_get_key(rsa_id_cert);
+
+  if (rsa_ed25519_crosscert_check(certs->ed_rsa_crosscert,
+                                  certs->ed_rsa_crosscert_len,
+                                  rsa_id_key,
+                                  &certs->ed_id_sign->signing_key,
+                                  now) < 0) {
+    crypto_pk_free(rsa_id_key);
+    ERR("Invalid/missing RSA crosscert");
+  }
+  crypto_pk_free(rsa_id_key);
+  rsa_id_key = NULL;
+
+  /* FFFF We could save a little time in the client case by queueing
+   * this batch to check it later, along with the signature from the
+   * AUTHENTICATE cell. That will change our data flow a bit, though,
+   * so I say "postpone". */
+
+  if (ed25519_checksig_batch(NULL, check, n_checkable) < 0) {
+    ERR("At least one Ed25519 certificate was badly signed");
+  }
+
+  return 1;
+}
+
+
+/**
+ * Check the Ed certificates and/or the RSA certificates, as appropriate.  If
+ * we obtained an Ed25519 identity, set *ed_id_out. If we obtained an RSA
+ * identity, set *rs_id_out. Otherwise, set them both to NULL.
+ */
+void
+or_handshake_certs_check_both(int severity,
+                              or_handshake_certs_t *certs,
+                              tor_tls_t *tls,
+                              time_t now,
+                              const ed25519_public_key_t **ed_id_out,
+                              const common_digests_t **rsa_id_out)
+{
+  tor_assert(ed_id_out);
+  tor_assert(rsa_id_out);
+
+  *ed_id_out = NULL;
+  *rsa_id_out = NULL;
+
+  if (certs->ed_id_sign) {
+    if (or_handshake_certs_ed25519_ok(severity, certs, tls, now)) {
+      tor_assert(certs->ed_id_sign);
+      tor_assert(certs->id_cert);
+
+      *ed_id_out = &certs->ed_id_sign->signing_key;
+      *rsa_id_out = tor_x509_cert_get_id_digests(certs->id_cert);
+
+      /* If we reached this point, we did not look at any of the
+       * subsidiary RSA certificates, so we'd better just remove them.
+       */
+      tor_x509_cert_free(certs->link_cert);
+      tor_x509_cert_free(certs->auth_cert);
+      certs->link_cert = certs->auth_cert = NULL;
+    }
+    /* We do _not_ fall through here.  If you provided us Ed25519
+     * certificates, we expect to verify them! */
+  } else {
+    /* No ed25519 keys given in the CERTS cell */
+    if (or_handshake_certs_rsa_ok(severity, certs, tls, now)) {
+      *rsa_id_out = tor_x509_cert_get_id_digests(certs->id_cert);
+    }
+  }
+
 }
diff --git a/src/or/torcert.h b/src/or/torcert.h
index 143a2aa..f7ca0ff 100644
--- a/src/or/torcert.h
+++ b/src/or/torcert.h
@@ -84,6 +84,16 @@ int or_handshake_certs_rsa_ok(int severity,
                               or_handshake_certs_t *certs,
                               tor_tls_t *tls,
                               time_t now);
+int or_handshake_certs_ed25519_ok(int severity,
+                                  or_handshake_certs_t *certs,
+                                  tor_tls_t *tls,
+                                  time_t now);
+void or_handshake_certs_check_both(int severity,
+                              or_handshake_certs_t *certs,
+                              tor_tls_t *tls,
+                              time_t now,
+                              const ed25519_public_key_t **ed_id_out,
+                              const common_digests_t **rsa_id_out);
 
 #endif
 





More information about the tor-commits mailing list