[tor-commits] [tor/master] NSS support for x509 certs

nickm at torproject.org nickm at torproject.org
Wed Sep 5 00:47:14 UTC 2018


commit c567b8fcb4e4851d6db19946cce8c4d5e75535f5
Author: Nick Mathewson <nickm at torproject.org>
Date:   Mon Aug 13 14:54:35 2018 -0400

    NSS support for x509 certs
    
    7 unit tests are failing at this point, but they're all TLS-related.
---
 src/lib/crypt_ops/crypto_nss_mgt.c |   9 +
 src/lib/crypt_ops/crypto_rsa.h     |  10 +
 src/lib/crypt_ops/crypto_rsa_nss.c |  27 +++
 src/lib/tls/tortls.c               | 157 ++++++++++++++++
 src/lib/tls/tortls_internal.h      |   4 +
 src/lib/tls/tortls_nss.c           |  61 +++----
 src/lib/tls/tortls_openssl.c       | 157 ++--------------
 src/lib/tls/x509.h                 |   6 -
 src/lib/tls/x509_internal.h        |  17 ++
 src/lib/tls/x509_nss.c             | 362 ++++++++++++++++++++++++++++++++++---
 src/lib/tls/x509_openssl.c         |   3 +-
 src/test/test_link_handshake.c     |  24 ++-
 12 files changed, 620 insertions(+), 217 deletions(-)

diff --git a/src/lib/crypt_ops/crypto_nss_mgt.c b/src/lib/crypt_ops/crypto_nss_mgt.c
index 85b18e00c..187f556bd 100644
--- a/src/lib/crypt_ops/crypto_nss_mgt.c
+++ b/src/lib/crypt_ops/crypto_nss_mgt.c
@@ -69,6 +69,15 @@ crypto_nss_early_init(void)
     crypto_nss_log_errors(LOG_ERR, "setting cipher policy");
     tor_assert_unreached();
   }
+
+  /* We need to override the default here, or NSS will reject all the
+   * legacy Tor certificates. */
+  SECStatus rv = NSS_OptionSet(NSS_RSA_MIN_KEY_SIZE, 1024);
+  if (rv != SECSuccess) {
+    log_err(LD_CRYPTO, "Unable to set NSS min RSA key size");
+    crypto_nss_log_errors(LOG_ERR, "setting cipher option.");
+    tor_assert_unreached();
+  }
 }
 
 void
diff --git a/src/lib/crypt_ops/crypto_rsa.h b/src/lib/crypt_ops/crypto_rsa.h
index afc6c4201..aaf32ec1b 100644
--- a/src/lib/crypt_ops/crypto_rsa.h
+++ b/src/lib/crypt_ops/crypto_rsa.h
@@ -121,6 +121,16 @@ MOCK_DECL(struct evp_pkey_st *, crypto_pk_get_openssl_evp_pkey_,(
                                  crypto_pk_t *env,int private));
 #endif
 
+#ifdef ENABLE_NSS
+struct SECKEYPublicKeyStr;
+struct SECKEYPrivateKeyStr;
+crypto_pk_t *crypto_pk_new_from_nss_pubkey(struct SECKEYPublicKeyStr *pub);
+const struct SECKEYPublicKeyStr *crypto_pk_get_nss_pubkey(
+                                           const crypto_pk_t *key);
+const struct SECKEYPrivateKeyStr *crypto_pk_get_nss_privkey(
+                                           const crypto_pk_t *key);
+#endif
+
 void crypto_pk_assign_public(crypto_pk_t *dest, const crypto_pk_t *src);
 void crypto_pk_assign_private(crypto_pk_t *dest, const crypto_pk_t *src);
 
diff --git a/src/lib/crypt_ops/crypto_rsa_nss.c b/src/lib/crypt_ops/crypto_rsa_nss.c
index 0411687b9..517faa5c7 100644
--- a/src/lib/crypt_ops/crypto_rsa_nss.c
+++ b/src/lib/crypt_ops/crypto_rsa_nss.c
@@ -47,6 +47,33 @@ crypto_pk_key_is_private(const crypto_pk_t *key)
   return key && key->seckey;
 }
 
+/** used by tortls.c: wrap a SecKEYPublicKey in a crypto_pk_t. Take ownership
+ * of the RSA object. */
+crypto_pk_t *
+crypto_pk_new_from_nss_pubkey(struct SECKEYPublicKeyStr *pub)
+{
+  crypto_pk_t *result = tor_malloc_zero(sizeof(crypto_pk_t));
+  result->pubkey = pub;
+  return result;
+}
+
+/** Return the SECKEYPublicKey for the provided crypto_pk_t. */
+const SECKEYPublicKey *
+crypto_pk_get_nss_pubkey(const crypto_pk_t *key)
+{
+  tor_assert(key);
+  return key->pubkey;
+}
+
+/** Return the SECKEYPrivateKey for the provided crypto_pk_t, or NULL if it
+ * does not exist. */
+const SECKEYPrivateKey *
+crypto_pk_get_nss_privkey(const crypto_pk_t *key)
+{
+  tor_assert(key);
+  return key->seckey;
+}
+
 #ifdef ENABLE_OPENSSL
 /** used by tortls.c: wrap an RSA* in a crypto_pk_t. Take ownership of the
  * RSA object. */
diff --git a/src/lib/tls/tortls.c b/src/lib/tls/tortls.c
index 395f0148e..0b14b69f4 100644
--- a/src/lib/tls/tortls.c
+++ b/src/lib/tls/tortls.c
@@ -5,11 +5,14 @@
 
 #define TORTLS_PRIVATE
 #include "lib/tls/x509.h"
+#include "lib/tls/x509_internal.h"
 #include "lib/tls/tortls.h"
 #include "lib/tls/tortls_st.h"
 #include "lib/tls/tortls_internal.h"
 #include "lib/log/util_bug.h"
 #include "lib/intmath/cmp.h"
+#include "lib/crypt_ops/crypto_rsa.h"
+#include "lib/crypt_ops/crypto_rand.h"
 
 /** Global TLS contexts. We keep them here because nobody else needs
  * to touch them.
@@ -28,6 +31,39 @@ tor_tls_context_get(int is_server)
   return is_server ? server_tls_context : client_tls_context;
 }
 
+/** Set *<b>link_cert_out</b> and *<b>id_cert_out</b> to the link certificate
+ * and ID certificate that we're currently using for our V3 in-protocol
+ * handshake's certificate chain.  If <b>server</b> is true, provide the certs
+ * that we use in server mode (auth, ID); otherwise, provide the certs that we
+ * use in client mode. (link, ID) */
+int
+tor_tls_get_my_certs(int server,
+                     const tor_x509_cert_t **link_cert_out,
+                     const tor_x509_cert_t **id_cert_out)
+{
+  tor_tls_context_t *ctx = tor_tls_context_get(server);
+  if (! ctx)
+    return -1;
+  if (link_cert_out)
+    *link_cert_out = server ? ctx->my_link_cert : ctx->my_auth_cert;
+  if (id_cert_out)
+    *id_cert_out = ctx->my_id_cert;
+  return 0;
+}
+
+/**
+ * Return the authentication key that we use to authenticate ourselves as a
+ * client in the V3 in-protocol handshake.
+ */
+crypto_pk_t *
+tor_tls_get_my_client_auth_key(void)
+{
+  tor_tls_context_t *context = tor_tls_context_get(0);
+  if (! context)
+    return NULL;
+  return context->auth_key;
+}
+
 /** Increase the reference count of <b>ctx</b>. */
 void
 tor_tls_context_incref(tor_tls_context_t *ctx)
@@ -158,6 +194,127 @@ tor_tls_context_init(unsigned flags,
   return MIN(rv1, rv2);
 }
 
+/** Create a new global TLS context.
+ *
+ * You can call this function multiple times.  Each time you call it,
+ * it generates new certificates; all new connections will use
+ * the new SSL context.
+ */
+int
+tor_tls_context_init_one(tor_tls_context_t **ppcontext,
+                         crypto_pk_t *identity,
+                         unsigned int key_lifetime,
+                         unsigned int flags,
+                         int is_client)
+{
+  tor_tls_context_t *new_ctx = tor_tls_context_new(identity,
+                                                   key_lifetime,
+                                                   flags,
+                                                   is_client);
+  tor_tls_context_t *old_ctx = *ppcontext;
+
+  if (new_ctx != NULL) {
+    *ppcontext = new_ctx;
+
+    /* Free the old context if one existed. */
+    if (old_ctx != NULL) {
+      /* This is safe even if there are open connections: we reference-
+       * count tor_tls_context_t objects. */
+      tor_tls_context_decref(old_ctx);
+    }
+  }
+
+  return ((new_ctx != NULL) ? 0 : -1);
+}
+
+/** Size of the RSA key to use for our TLS link keys */
+#define RSA_LINK_KEY_BITS 2048
+
+/** How long do identity certificates live? (sec) */
+#define IDENTITY_CERT_LIFETIME  (365*24*60*60)
+
+/**
+ * Initialize the certificates and keys for a TLS context <b>result</b>
+ *
+ * Other arguments as for tor_tls_context_new().
+ */
+int
+tor_tls_context_init_certificates(tor_tls_context_t *result,
+                                  crypto_pk_t *identity,
+                                  unsigned key_lifetime,
+                                  unsigned flags)
+{
+  (void)flags;
+  int rv = -1;
+  char *nickname = NULL, *nn2 = NULL;
+  crypto_pk_t *rsa = NULL, *rsa_auth = NULL;
+  tor_x509_cert_impl_t *cert = NULL, *idcert = NULL, *authcert = NULL;
+
+  nickname = crypto_random_hostname(8, 20, "www.", ".net");
+
+#ifdef DISABLE_V3_LINKPROTO_SERVERSIDE
+  nn2 = crypto_random_hostname(8, 20, "www.", ".net");
+#else
+  nn2 = crypto_random_hostname(8, 20, "www.", ".com");
+#endif
+
+  /* Generate short-term RSA key for use with TLS. */
+  if (!(rsa = crypto_pk_new()))
+    goto error;
+  if (crypto_pk_generate_key_with_bits(rsa, RSA_LINK_KEY_BITS)<0)
+    goto error;
+
+  /* Generate short-term RSA key for use in the in-protocol ("v3")
+   * authentication handshake. */
+  if (!(rsa_auth = crypto_pk_new()))
+    goto error;
+  if (crypto_pk_generate_key(rsa_auth)<0)
+    goto error;
+
+  /* Create a link certificate signed by identity key. */
+  cert = tor_tls_create_certificate(rsa, identity, nickname, nn2,
+                                    key_lifetime);
+  /* Create self-signed certificate for identity key. */
+  idcert = tor_tls_create_certificate(identity, identity, nn2, nn2,
+                                      IDENTITY_CERT_LIFETIME);
+  /* Create an authentication certificate signed by identity key. */
+  authcert = tor_tls_create_certificate(rsa_auth, identity, nickname, nn2,
+                                        key_lifetime);
+  if (!cert || !idcert || !authcert) {
+    log_warn(LD_CRYPTO, "Error creating certificate");
+    goto error;
+  }
+
+  result->my_link_cert = tor_x509_cert_new(cert);
+  cert = NULL;
+  result->my_id_cert = tor_x509_cert_new(idcert);
+  idcert = NULL;
+  result->my_auth_cert = tor_x509_cert_new(authcert);
+  authcert = NULL;
+  if (!result->my_link_cert || !result->my_id_cert || !result->my_auth_cert)
+    goto error;
+  result->link_key = rsa;
+  rsa = NULL;
+  result->auth_key = rsa_auth;
+  rsa_auth = NULL;
+
+  rv = 0;
+ error:
+
+  tor_free(nickname);
+  tor_free(nn2);
+
+  if (cert)
+    tor_x509_cert_impl_free_(cert);
+  if (idcert)
+    tor_x509_cert_impl_free_(idcert);
+  if (authcert)
+    tor_x509_cert_impl_free_(authcert);
+  crypto_pk_free(rsa);
+  crypto_pk_free(rsa_auth);
+
+  return rv;
+}
 /** Make future log messages about <b>tls</b> display the address
  * <b>address</b>.
  */
diff --git a/src/lib/tls/tortls_internal.h b/src/lib/tls/tortls_internal.h
index b997ee3e4..2920e9658 100644
--- a/src/lib/tls/tortls_internal.h
+++ b/src/lib/tls/tortls_internal.h
@@ -27,6 +27,10 @@ int tor_tls_context_init_one(tor_tls_context_t **ppcontext,
                              unsigned int key_lifetime,
                              unsigned int flags,
                              int is_client);
+int tor_tls_context_init_certificates(tor_tls_context_t *result,
+                                      crypto_pk_t *identity,
+                                      unsigned key_lifetime,
+                                      unsigned flags);
 
 #ifdef ENABLE_OPENSSL
 void tor_tls_context_impl_free(struct ssl_ctx_st *);
diff --git a/src/lib/tls/tortls_nss.c b/src/lib/tls/tortls_nss.c
index 3ab5c753d..35dbc27d9 100644
--- a/src/lib/tls/tortls_nss.c
+++ b/src/lib/tls/tortls_nss.c
@@ -23,7 +23,9 @@
 #include "lib/crypt_ops/crypto_dh.h"
 #include "lib/crypt_ops/crypto_util.h"
 #include "lib/tls/x509.h"
+#include "lib/tls/x509_internal.h"
 #include "lib/tls/tortls.h"
+#include "lib/tls/tortls_st.h"
 #include "lib/tls/tortls_internal.h"
 #include "lib/log/util_bug.h"
 
@@ -64,27 +66,27 @@ tor_tls_context_new(crypto_pk_t *identity,
                     unsigned int key_lifetime, unsigned flags, int is_client)
 {
   tor_assert(identity);
-  tor_assert(key_lifetime);
-  (void)flags;
-  (void)is_client;
-  // XXXX
-  return NULL;
-}
-int
-tor_tls_context_init_one(tor_tls_context_t **ppcontext,
-                         crypto_pk_t *identity,
-                         unsigned int key_lifetime,
-                         unsigned int flags,
-                         int is_client)
-{
-  tor_assert(ppcontext);
-  tor_assert(identity);
-  tor_assert(key_lifetime);
-  (void)flags;
-  (void)is_client;
-  // XXXX
-  return -1;
+
+  tor_tls_context_t *ctx = tor_malloc_zero(sizeof(tor_tls_context_t));
+  ctx->refcnt = 1;
+
+  if (! is_client) {
+    if (tor_tls_context_init_certificates(ctx, identity,
+                                          key_lifetime, flags) < 0) {
+      goto err;
+    }
+  }
+
+  // XXXX write the main body.
+
+  goto done;
+ err:
+  tor_tls_context_decref(ctx);
+  ctx = NULL;
+ done:
+  return ctx;
 }
+
 void
 tor_tls_context_impl_free(struct ssl_ctx_st *ctx)
 {
@@ -361,25 +363,6 @@ tor_tls_log_one_error(tor_tls_t *tls, unsigned long err,
   // XXXX
 }
 
-int
-tor_tls_get_my_certs(int server,
-                     const struct tor_x509_cert_t **link_cert_out,
-                     const struct tor_x509_cert_t **id_cert_out)
-{
-  tor_assert(link_cert_out);
-  tor_assert(id_cert_out);
-  (void)server;
-  // XXXX
-  return -1;
-}
-
-crypto_pk_t *
-tor_tls_get_my_client_auth_key(void)
-{
-  // XXXX
-  return NULL;
-}
-
 const char *
 tor_tls_get_ciphersuite_name(tor_tls_t *tls)
 {
diff --git a/src/lib/tls/tortls_openssl.c b/src/lib/tls/tortls_openssl.c
index 5f5431235..fb6328bcd 100644
--- a/src/lib/tls/tortls_openssl.c
+++ b/src/lib/tls/tortls_openssl.c
@@ -18,6 +18,7 @@
 
 #define TORTLS_PRIVATE
 #define TORTLS_OPENSSL_PRIVATE
+#define TOR_X509_PRIVATE
 
 #ifdef _WIN32 /*wrkard for dtls1.h >= 0.9.8m of "#include <winsock.h>"*/
   #include <winsock2.h>
@@ -75,9 +76,6 @@ ENABLE_GCC_WARNING(redundant-decls)
 #define LEGAL_NICKNAME_CHARACTERS \
   "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
 
-/** How long do identity certificates live? (sec) */
-#define IDENTITY_CERT_LIFETIME  (365*24*60*60)
-
 #define ADDR(tls) (((tls) && (tls)->address) ? tls->address : "peer")
 
 #if OPENSSL_VERSION_NUMBER <  OPENSSL_V(1,0,0,'f')
@@ -489,39 +487,6 @@ static const char CLIENT_CIPHER_LIST[] =
 #undef CIPHER
 #undef XCIPHER
 
-/** Set *<b>link_cert_out</b> and *<b>id_cert_out</b> to the link certificate
- * and ID certificate that we're currently using for our V3 in-protocol
- * handshake's certificate chain.  If <b>server</b> is true, provide the certs
- * that we use in server mode (auth, ID); otherwise, provide the certs that we
- * use in client mode. (link, ID) */
-int
-tor_tls_get_my_certs(int server,
-                     const tor_x509_cert_t **link_cert_out,
-                     const tor_x509_cert_t **id_cert_out)
-{
-  tor_tls_context_t *ctx = tor_tls_context_get(server);
-  if (! ctx)
-    return -1;
-  if (link_cert_out)
-    *link_cert_out = server ? ctx->my_link_cert : ctx->my_auth_cert;
-  if (id_cert_out)
-    *id_cert_out = ctx->my_id_cert;
-  return 0;
-}
-
-/**
- * Return the authentication key that we use to authenticate ourselves as a
- * client in the V3 in-protocol handshake.
- */
-crypto_pk_t *
-tor_tls_get_my_client_auth_key(void)
-{
-  tor_tls_context_t *context = tor_tls_context_get(0);
-  if (! context)
-    return NULL;
-  return context->auth_key;
-}
-
 /** Return true iff the other side of <b>tls</b> has authenticated to us, and
  * the key certified in <b>cert</b> is the same as the key they used to do it.
  */
@@ -548,39 +513,6 @@ tor_tls_cert_matches_key,(const tor_tls_t *tls, const tor_x509_cert_t *cert))
   return result;
 }
 
-/** Create a new global TLS context.
- *
- * You can call this function multiple times.  Each time you call it,
- * it generates new certificates; all new connections will use
- * the new SSL context.
- */
-int
-tor_tls_context_init_one(tor_tls_context_t **ppcontext,
-                         crypto_pk_t *identity,
-                         unsigned int key_lifetime,
-                         unsigned int flags,
-                         int is_client)
-{
-  tor_tls_context_t *new_ctx = tor_tls_context_new(identity,
-                                                   key_lifetime,
-                                                   flags,
-                                                   is_client);
-  tor_tls_context_t *old_ctx = *ppcontext;
-
-  if (new_ctx != NULL) {
-    *ppcontext = new_ctx;
-
-    /* Free the old context if one existed. */
-    if (old_ctx != NULL) {
-      /* This is safe even if there are open connections: we reference-
-       * count tor_tls_context_t objects. */
-      tor_tls_context_decref(old_ctx);
-    }
-  }
-
-  return ((new_ctx != NULL) ? 0 : -1);
-}
-
 void
 tor_tls_context_impl_free(struct ssl_ctx_st *ctx)
 {
@@ -592,8 +524,6 @@ tor_tls_context_impl_free(struct ssl_ctx_st *ctx)
 /** The group we should use for ecdhe when none was selected. */
 #define  NID_tor_default_ecdhe_group NID_X9_62_prime256v1
 
-#define RSA_LINK_KEY_BITS 2048
-
 /** Create a new TLS context for use with Tor TLS handshakes.
  * <b>identity</b> should be set to the identity key used to sign the
  * certificate.
@@ -602,57 +532,19 @@ tor_tls_context_t *
 tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime,
                     unsigned flags, int is_client)
 {
-  crypto_pk_t *rsa = NULL, *rsa_auth = NULL;
   EVP_PKEY *pkey = NULL;
   tor_tls_context_t *result = NULL;
-  X509 *cert = NULL, *idcert = NULL, *authcert = NULL;
-  char *nickname = NULL, *nn2 = NULL;
 
   tor_tls_init();
-  nickname = crypto_random_hostname(8, 20, "www.", ".net");
-#ifdef DISABLE_V3_LINKPROTO_SERVERSIDE
-  nn2 = crypto_random_hostname(8, 20, "www.", ".net");
-#else
-  nn2 = crypto_random_hostname(8, 20, "www.", ".com");
-#endif
-
-  /* Generate short-term RSA key for use with TLS. */
-  if (!(rsa = crypto_pk_new()))
-    goto error;
-  if (crypto_pk_generate_key_with_bits(rsa, RSA_LINK_KEY_BITS)<0)
-    goto error;
-  if (!is_client) {
-    /* Generate short-term RSA key for use in the in-protocol ("v3")
-     * authentication handshake. */
-    if (!(rsa_auth = crypto_pk_new()))
-      goto error;
-    if (crypto_pk_generate_key(rsa_auth)<0)
-      goto error;
-    /* Create a link certificate signed by identity key. */
-    cert = tor_tls_create_certificate(rsa, identity, nickname, nn2,
-                                      key_lifetime);
-    /* Create self-signed certificate for identity key. */
-    idcert = tor_tls_create_certificate(identity, identity, nn2, nn2,
-                                        IDENTITY_CERT_LIFETIME);
-    /* Create an authentication certificate signed by identity key. */
-    authcert = tor_tls_create_certificate(rsa_auth, identity, nickname, nn2,
-                                          key_lifetime);
-    if (!cert || !idcert || !authcert) {
-      log_warn(LD_CRYPTO, "Error creating certificate");
-      goto error;
-    }
-  }
 
   result = tor_malloc_zero(sizeof(tor_tls_context_t));
   result->refcnt = 1;
-  if (!is_client) {
-    result->my_link_cert = tor_x509_cert_new(X509_dup(cert));
-    result->my_id_cert = tor_x509_cert_new(X509_dup(idcert));
-    result->my_auth_cert = tor_x509_cert_new(X509_dup(authcert));
-    if (!result->my_link_cert || !result->my_id_cert || !result->my_auth_cert)
+
+  if (! is_client) {
+    if (tor_tls_context_init_certificates(result, identity, key_lifetime,
+                                          flags) < 0) {
       goto error;
-    result->link_key = crypto_pk_dup_key(rsa);
-    result->auth_key = crypto_pk_dup_key(rsa_auth);
+    }
   }
 
 #if 0
@@ -727,22 +619,21 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime,
   SSL_CTX_set_mode(result->ctx, SSL_MODE_RELEASE_BUFFERS);
 #endif
   if (! is_client) {
-    if (cert && !SSL_CTX_use_certificate(result->ctx,cert))
+    if (result->my_link_cert &&
+        !SSL_CTX_use_certificate(result->ctx,
+                                 result->my_link_cert->cert)) {
       goto error;
-    X509_free(cert); /* We just added a reference to cert. */
-    cert=NULL;
-    if (idcert) {
+    }
+    if (result->my_id_cert) {
       X509_STORE *s = SSL_CTX_get_cert_store(result->ctx);
       tor_assert(s);
-      X509_STORE_add_cert(s, idcert);
-      X509_free(idcert); /* The context now owns the reference to idcert */
-      idcert = NULL;
+      X509_STORE_add_cert(s, X509_dup(result->my_id_cert->cert));
     }
   }
   SSL_CTX_set_session_cache_mode(result->ctx, SSL_SESS_CACHE_OFF);
   if (!is_client) {
-    tor_assert(rsa);
-    if (!(pkey = crypto_pk_get_openssl_evp_pkey_(rsa,1)))
+    tor_assert(result->link_key);
+    if (!(pkey = crypto_pk_get_openssl_evp_pkey_(result->link_key,1)))
       goto error;
     if (!SSL_CTX_use_PrivateKey(result->ctx, pkey))
       goto error;
@@ -751,6 +642,7 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime,
     if (!SSL_CTX_check_private_key(result->ctx))
       goto error;
   }
+
   {
     DH *dh = crypto_dh_new_openssl_tls();
     tor_assert(dh);
@@ -777,33 +669,14 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime,
   /* let us realloc bufs that we're writing from */
   SSL_CTX_set_mode(result->ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
 
-  if (rsa)
-    crypto_pk_free(rsa);
-  if (rsa_auth)
-    crypto_pk_free(rsa_auth);
-  X509_free(authcert);
-  tor_free(nickname);
-  tor_free(nn2);
   return result;
 
  error:
   tls_log_errors(NULL, LOG_WARN, LD_NET, "creating TLS context");
-  tor_free(nickname);
-  tor_free(nn2);
   if (pkey)
     EVP_PKEY_free(pkey);
-  if (rsa)
-    crypto_pk_free(rsa);
-  if (rsa_auth)
-    crypto_pk_free(rsa_auth);
   if (result)
     tor_tls_context_decref(result);
-  if (cert)
-    X509_free(cert);
-  if (idcert)
-    X509_free(idcert);
-  if (authcert)
-    X509_free(authcert);
   return NULL;
 }
 
diff --git a/src/lib/tls/x509.h b/src/lib/tls/x509.h
index 8316df75a..e7440a192 100644
--- a/src/lib/tls/x509.h
+++ b/src/lib/tls/x509.h
@@ -72,10 +72,4 @@ int tor_tls_cert_is_valid(int severity,
                           time_t now,
                           int check_rsa_1024);
 
-int tor_x509_check_cert_lifetime_internal(int severity,
-                                          const tor_x509_cert_impl_t *cert,
-                                          time_t now,
-                                          int past_tolerance,
-                                          int future_tolerance);
-
 #endif
diff --git a/src/lib/tls/x509_internal.h b/src/lib/tls/x509_internal.h
index 86f5a0de5..4b49f1dec 100644
--- a/src/lib/tls/x509_internal.h
+++ b/src/lib/tls/x509_internal.h
@@ -14,6 +14,17 @@
 #include "lib/crypt_ops/crypto_rsa.h"
 #include "lib/testsupport/testsupport.h"
 
+/**
+ * How skewed do we allow our clock to be with respect to certificates that
+ * seem to be expired? (seconds)
+ */
+#define TOR_X509_PAST_SLOP (2*24*60*60)
+/**
+ * How skewed do we allow our clock to be with respect to certificates that
+ * seem to come from the future? (seconds)
+ */
+#define  TOR_X509_FUTURE_SLOP (30*24*60*60)
+
 MOCK_DECL(tor_x509_cert_impl_t *, tor_tls_create_certificate,
                                                    (crypto_pk_t *rsa,
                                                     crypto_pk_t *rsa_sign,
@@ -25,6 +36,12 @@ MOCK_DECL(tor_x509_cert_t *, tor_x509_cert_new,
 const tor_x509_cert_impl_t *tor_x509_cert_get_impl(
                                            const tor_x509_cert_t *cert);
 
+int tor_x509_check_cert_lifetime_internal(int severity,
+                                          const tor_x509_cert_impl_t *cert,
+                                          time_t now,
+                                          int past_tolerance,
+                                          int future_tolerance);
+
 void tor_x509_cert_impl_free_(tor_x509_cert_impl_t *cert);
 #ifdef ENABLE_OPENSSL
 int tor_x509_cert_set_cached_der_encoding(tor_x509_cert_t *cert);
diff --git a/src/lib/tls/x509_nss.c b/src/lib/tls/x509_nss.c
index ac9e6658d..35b3d2542 100644
--- a/src/lib/tls/x509_nss.c
+++ b/src/lib/tls/x509_nss.c
@@ -15,10 +15,144 @@
 #include "lib/tls/tortls.h"
 #include "lib/crypt_ops/crypto_rand.h"
 #include "lib/crypt_ops/crypto_util.h"
+#include "lib/crypt_ops/crypto_nss_mgt.h"
 #include "lib/log/util_bug.h"
+#include "lib/encoding/time_fmt.h"
+#include "lib/string/printf.h"
 
 #include <pk11pub.h>
+#include <cryptohi.h>
 #include <cert.h>
+#include <keyhi.h>
+#include <time.h>
+
+/* Units of PRTime per second.
+ *
+ * (PRTime is based in microseconds since the Unix
+ * epoch.) */
+#define PRTIME_PER_SEC (1000*1000)
+
+static tor_x509_cert_impl_t *tor_x509_cert_decode_internal(
+                      const uint8_t *certificate, int certificate_len);
+
+static tor_x509_cert_impl_t *
+tor_tls_create_certificate_internal(crypto_pk_t *rsa,
+                                    crypto_pk_t *rsa_sign,
+                                    CERTName *subject_dn,
+                                    CERTName *issuer_dn,
+                                    time_t start_time,
+                                    time_t end_time)
+{
+  if (! crypto_pk_key_is_private(rsa_sign)) {
+    return NULL;
+  }
+
+  const SECKEYPublicKey *subject_key = crypto_pk_get_nss_pubkey(rsa);
+  const SECKEYPrivateKey *signing_key = crypto_pk_get_nss_privkey(rsa_sign);
+  SECStatus s;
+
+  CERTSubjectPublicKeyInfo *subject_spki = NULL;
+  CERTCertificateRequest *request = NULL;
+  CERTValidity *validity = NULL;
+  CERTCertificate *cert = NULL;
+  SECItem der = { .data = NULL, .len = 0 };
+  SECItem signed_der = { .data = NULL, .len = 0 };
+
+  CERTCertificate *result_cert = NULL;
+
+  validity = CERT_CreateValidity(((PRTime)start_time) * PRTIME_PER_SEC,
+                                 ((PRTime)end_time) * PRTIME_PER_SEC);
+  if (! validity) {
+    crypto_nss_log_errors(LOG_WARN, "creating a validity object");
+    goto err;
+  }
+
+  unsigned long serial_number;
+  crypto_rand((char*)&serial_number, sizeof(serial_number));
+
+  subject_spki = SECKEY_CreateSubjectPublicKeyInfo(subject_key);
+  if (!subject_spki)
+    goto err;
+
+  /* Make a CSR ... */
+  // XXX do we need to set any attributes?
+  request = CERT_CreateCertificateRequest(subject_dn,
+                                          subject_spki,
+                                          NULL /* attributes */);
+  if (!request)
+    goto err;
+
+  /* Put it into a certificate ... */
+  cert = CERT_CreateCertificate(serial_number,
+                                issuer_dn,
+                                validity,
+                                request);
+  if (!cert)
+    goto err;
+
+  /* version 3 cert */
+  *cert->version.data = 2; /* 2 means version 3. */
+  cert->version.len = 1;
+
+  // XXX do we need to set anything else on the cert?
+
+  /* Sign it. */
+  KeyType privkey_type = SECKEY_GetPrivateKeyType(signing_key);
+  SECOidTag oid_tag = SEC_GetSignatureAlgorithmOidTag(privkey_type,
+                                                      SEC_OID_SHA256);
+  if (oid_tag == SEC_OID_UNKNOWN)
+    goto err;
+  s = SECOID_SetAlgorithmID(cert->arena, &cert->signature, oid_tag, NULL);
+  if (s != SECSuccess)
+    goto err;
+
+  void *tmp;
+  tmp = SEC_ASN1EncodeItem(cert->arena, &der, cert,
+                           SEC_ASN1_GET(CERT_CertificateTemplate));
+  if (!tmp)
+    goto err;
+
+  s = SEC_DerSignDataWithAlgorithmID(cert->arena,
+                                     &signed_der,
+                                     der.data, der.len,
+                                     (SECKEYPrivateKey *)signing_key,//const
+                                     &cert->signature);
+
+  if (s != SECSuccess)
+    goto err;
+
+  /* Re-parse it, to make sure all the certificates we actually use
+   * appear via being decoded. */
+  result_cert = tor_x509_cert_decode_internal(signed_der.data, signed_der.len);
+
+#if 1
+  {
+    // Can we check the cert we just signed?
+    tor_assert(result_cert);
+    SECKEYPublicKey *issuer_pk = (SECKEYPublicKey *)
+      crypto_pk_get_nss_pubkey(rsa_sign);
+    SECStatus cert_ok = CERT_VerifySignedDataWithPublicKey(
+                               &result_cert->signatureWrap, issuer_pk, NULL);
+    tor_assert(cert_ok == SECSuccess);
+  }
+#endif
+
+ err:
+  if (subject_spki)
+    SECKEY_DestroySubjectPublicKeyInfo(subject_spki);
+  if (request)
+    CERT_DestroyCertificateRequest(request);
+  if (validity)
+    CERT_DestroyValidity(validity);
+
+  // unnecessary, since these are allocated in the cert's arena.
+  //SECITEM_FreeItem(&der, PR_FALSE);
+  //SECITEM_FreeItem(&signed_der, PR_FALSE);
+  if (cert)
+    CERT_DestroyCertificate(cert);
+
+  return result_cert;
+}
 
 MOCK_IMPL(tor_x509_cert_impl_t *,
 tor_tls_create_certificate,(crypto_pk_t *rsa,
@@ -31,9 +165,39 @@ tor_tls_create_certificate,(crypto_pk_t *rsa,
   tor_assert(rsa_sign);
   tor_assert(cname);
   tor_assert(cname_sign);
-  (void) cert_lifetime;
-  // XXXX
-  return NULL;
+
+  char *cname_rfc_1485 = NULL, *cname_sign_rfc_1485 = NULL;
+  CERTName *subject_dn = NULL, *issuer_dn = NULL;
+  time_t start_time;
+  time_t end_time;
+  CERTCertificate *result = NULL;
+
+  tor_asprintf(&cname_rfc_1485, "CN=%s", cname);
+  tor_asprintf(&cname_sign_rfc_1485, "CN=%s", cname_sign);
+
+  subject_dn = CERT_AsciiToName(cname_rfc_1485);
+  issuer_dn = CERT_AsciiToName(cname_sign_rfc_1485);
+  if (!subject_dn || !issuer_dn)
+    goto err;
+
+  tor_tls_pick_certificate_lifetime(time(NULL), cert_lifetime,
+                                    &start_time, &end_time);
+
+  result = tor_tls_create_certificate_internal(rsa,
+                                               rsa_sign,
+                                               subject_dn,
+                                               issuer_dn,
+                                               start_time,
+                                               end_time);
+ err:
+  tor_free(cname_rfc_1485);
+  tor_free(cname_sign_rfc_1485);
+  if (subject_dn)
+    CERT_DestroyName(subject_dn);
+  if (issuer_dn)
+    CERT_DestroyName(issuer_dn);
+
+  return result;
 }
 
 /** Set *<b>encoded_out</b> and *<b>size_out</b> to <b>cert</b>'s encoded DER
@@ -63,26 +227,63 @@ tor_x509_cert_t *
 tor_x509_cert_dup(const tor_x509_cert_t *cert)
 {
   tor_assert(cert);
-  // XXXX
-  return NULL;
+  return tor_x509_cert_new(CERT_DupCertificate(cert->cert));
+}
+
+/**
+ * As tor_x509_cert_decode, but return the NSS certificate type
+*/
+static tor_x509_cert_impl_t *
+tor_x509_cert_decode_internal(const uint8_t *certificate,
+                              int certificate_len)
+{
+  tor_assert(certificate);
+  if (certificate_len > INT_MAX)
+    return NULL;
+
+  SECItem der = { .type = siBuffer,
+                  .data = (unsigned char *)certificate,
+                  .len = certificate_len };
+  CERTCertDBHandle *certdb = CERT_GetDefaultCertDB();
+  tor_assert(certdb);
+  return CERT_NewTempCertificate(certdb,
+                                 &der,
+                                 NULL /* nickname */,
+                                 PR_FALSE, /* isPerm */
+                                 PR_TRUE /* CopyDER */);
 }
 
 tor_x509_cert_t *
 tor_x509_cert_decode(const uint8_t *certificate,
                      size_t certificate_len)
 {
-  tor_assert(certificate);
-  (void) certificate_len;
-  // XXXX
-  return NULL;
+  CERTCertificate *cert = tor_x509_cert_decode_internal(certificate,
+                                                        (int)certificate_len);
+  if (! cert) {
+    crypto_nss_log_errors(LOG_INFO, "decoding certificate");
+    return NULL;
+  }
+
+  tor_x509_cert_t *newcert = tor_x509_cert_new(cert);
+
+  return newcert;
 }
 
 crypto_pk_t *
 tor_tls_cert_get_key(tor_x509_cert_t *cert)
 {
   tor_assert(cert);
-  // XXXXX
-  return NULL;
+  CERTSubjectPublicKeyInfo *spki = &cert->cert->subjectPublicKeyInfo;
+  SECKEYPublicKey *pub = SECKEY_ExtractPublicKey(spki); // we own this pointer
+  if (pub == NULL)
+    return NULL;
+
+  if (SECKEY_GetPublicKeyType(pub) != rsaKey) {
+    SECKEY_DestroyPublicKey(pub);
+    return NULL;
+  }
+
+  return crypto_pk_new_from_nss_pubkey(pub);
 }
 
 int
@@ -92,14 +293,80 @@ tor_tls_cert_is_valid(int severity,
                       time_t now,
                       int check_rsa_1024)
 {
+  int result = 0;
+
   tor_assert(cert);
   tor_assert(signing_cert);
-  (void)severity;
-  (void)now;
-  (void)check_rsa_1024;
-  // XXXXX
 
-  return 0;
+  SECKEYPublicKey *pk = CERT_ExtractPublicKey(signing_cert->cert);
+  if (pk == NULL) {
+    log_fn(severity, LD_CRYPTO,
+           "Invalid certificate: could not extract issuer key");
+    goto fail;
+  }
+
+  SECStatus s = CERT_VerifySignedDataWithPublicKey(&cert->cert->signatureWrap,
+                                                   pk, NULL);
+  if (s != SECSuccess) {
+    log_fn(severity, LD_CRYPTO,
+           "Invalid certificate: could not validate signature.");
+    goto fail;
+  }
+
+  if (tor_x509_check_cert_lifetime_internal(severity,
+                                            cert->cert,
+                                            now,
+                                            TOR_X509_PAST_SLOP,
+                                            TOR_X509_FUTURE_SLOP) < 0)
+    goto fail;
+
+  if (check_rsa_1024) {
+    /* We require that this is a 1024-bit RSA key, for legacy reasons .:p */
+    if (SECKEY_GetPublicKeyType(pk) != rsaKey ||
+        SECKEY_PublicKeyStrengthInBits(pk) != 1024) {
+      log_fn(severity, LD_CRYPTO, "Invalid certificate: Key is not RSA1024.");
+      goto fail;
+    }
+  } else {
+    /* We require that this key is at least minimally strong. */
+    unsigned min_bits = (SECKEY_GetPublicKeyType(pk) == ecKey) ? 128: 1024;
+    if (SECKEY_PublicKeyStrengthInBits(pk) < min_bits) {
+      log_fn(severity, LD_CRYPTO, "Invalid certificate: Key is too weak.");
+      goto fail;
+    }
+  }
+
+  /* The certificate is valid. */
+  result = 1;
+
+ fail:
+  if (pk)
+    SECKEY_DestroyPublicKey(pk);
+  return result;
+}
+
+static void
+log_cert_lifetime(int severity,
+                  const char *status,
+                  time_t now,
+                  PRTime notBefore,
+                  PRTime notAfter)
+{
+  log_fn(severity, LD_GENERAL,
+         "Certificate %s. Either their clock is set wrong, or your clock "
+         "is incorrect.", status);
+
+  char nowbuf[ISO_TIME_LEN+1];
+  char nbbuf[ISO_TIME_LEN+1];
+  char nabuf[ISO_TIME_LEN+1];
+
+  format_iso_time(nowbuf, now);
+  format_iso_time(nbbuf, notBefore / PRTIME_PER_SEC);
+  format_iso_time(nabuf, notAfter / PRTIME_PER_SEC);
+
+  log_fn(severity, LD_GENERAL,
+         "(The certificate is valid from %s until %s. Your time is %s.)",
+         nbbuf, nabuf, nowbuf);
 }
 
 int
@@ -110,12 +377,33 @@ tor_x509_check_cert_lifetime_internal(int severity,
                                       int future_tolerance)
 {
   tor_assert(cert);
-  (void)severity;
-  (void)now;
-  (void)past_tolerance;
-  (void)future_tolerance;
-  // XXXX
-  return -1;
+
+  PRTime notBefore=0, notAfter=0;
+  int64_t t;
+  SECStatus r = CERT_GetCertTimes(cert, &notBefore, &notAfter);
+  if (r != SECSuccess) {
+    log_fn(severity, LD_CRYPTO,
+           "Couldn't get validity times from certificate");
+    return -1;
+  }
+
+  t = ((int64_t)now) + future_tolerance;
+  t *= PRTIME_PER_SEC;
+  if (notBefore > t) {
+    log_cert_lifetime(severity, "not yet valid", now,
+                      notBefore, notAfter);
+    return -1;
+  }
+
+  t = ((int64_t)now) - past_tolerance;
+  t *= PRTIME_PER_SEC;
+  if (notAfter < t) {
+    log_cert_lifetime(severity, "already expired", now,
+                      notBefore, notAfter);
+    return -1;
+  }
+
+  return 0;
 }
 
 #ifdef TOR_UNIT_TESTS
@@ -126,9 +414,33 @@ tor_x509_cert_replace_expiration(const tor_x509_cert_t *inp,
 {
   tor_assert(inp);
   tor_assert(signing_key);
-  (void)new_expiration_time;
 
-  // XXXX
-  return NULL;
+  PRTime notBefore=0, notAfter=0;
+  SECStatus r = CERT_GetCertTimes(inp->cert, &notBefore, &notAfter);
+  if (r != SECSuccess)
+    return NULL;
+
+  time_t start_time = notBefore / PRTIME_PER_SEC;
+  if (new_expiration_time < start_time) {
+    /* This prevents an NSS error. */
+    start_time = new_expiration_time - 10;
+  }
+
+  crypto_pk_t *subject_key = tor_tls_cert_get_key((tor_x509_cert_t *)inp);
+  if (!subject_key)
+    return NULL;
+
+  CERTCertificate *newcert;
+
+  newcert = tor_tls_create_certificate_internal(subject_key,
+                                                signing_key,
+                                                &inp->cert->subject,
+                                                &inp->cert->issuer,
+                                                start_time,
+                                                new_expiration_time);
+
+  crypto_pk_free(subject_key);
+
+  return newcert ? tor_x509_cert_new(newcert) : NULL;
 }
 #endif
diff --git a/src/lib/tls/x509_openssl.c b/src/lib/tls/x509_openssl.c
index 43bd6b4d4..28a30b66e 100644
--- a/src/lib/tls/x509_openssl.c
+++ b/src/lib/tls/x509_openssl.c
@@ -319,7 +319,8 @@ tor_tls_cert_is_valid(int severity,
   /* okay, the signature checked out right.  Now let's check the check the
    * lifetime. */
   if (tor_x509_check_cert_lifetime_internal(severity, cert->cert, now,
-                                   48*60*60, 30*24*60*60) < 0)
+                                            TOR_X509_PAST_SLOP,
+                                            TOR_X509_FUTURE_SLOP) < 0)
     goto bad;
 
   cert_key = X509_get_pubkey(cert->cert);
diff --git a/src/test/test_link_handshake.c b/src/test/test_link_handshake.c
index 677276599..df3fa67eb 100644
--- a/src/test/test_link_handshake.c
+++ b/src/test/test_link_handshake.c
@@ -795,11 +795,26 @@ CERTS_FAIL(bad_rsa_id_cert, /*ed25519*/
   {
     require_failure_message = "legacy RSA ID certificate was not valid";
     certs_cell_cert_t *cert = certs_cell_get_certs(d->ccell, 1);
-    uint8_t *body = certs_cell_cert_getarray_body(cert);
-    ssize_t body_len = certs_cell_cert_getlen_body(cert);
-    /* Frob a byte in the signature */
-    body[body_len - 13] ^= 7;
+    uint8_t *body;
+    /* Frob a byte in the signature, after making a new cert. (NSS won't let
+     * us just frob the old cert, since it will see that the issuer & serial
+     * number are the same, which will make it fail at an earlier stage than
+     * signature verification.) */
+    const tor_x509_cert_t *idc;
+    tor_x509_cert_t *newc;
+    tor_tls_get_my_certs(1, NULL, &idc);
+    time_t new_end = time(NULL) + 86400 * 10;
+    newc = tor_x509_cert_replace_expiration(idc, new_end, d->key2);
+    const uint8_t *encoded;
+    size_t encoded_len;
+    tor_x509_cert_get_der(newc, &encoded, &encoded_len);
+    certs_cell_cert_setlen_body(cert, encoded_len);
+    certs_cell_cert_set_cert_len(cert, encoded_len);
+    body = certs_cell_cert_getarray_body(cert);
+    memcpy(body, encoded, encoded_len);
+    body[encoded_len - 13] ^= 7;
     REENCODE();
+    tor_x509_cert_free(newc);
   })
 CERTS_FAIL(expired_rsa_id, /* both */
   {
@@ -815,6 +830,7 @@ CERTS_FAIL(expired_rsa_id, /* both */
     size_t encoded_len;
     tor_x509_cert_get_der(newc, &encoded, &encoded_len);
     certs_cell_cert_setlen_body(cert, encoded_len);
+    certs_cell_cert_set_cert_len(cert, encoded_len);
     memcpy(certs_cell_cert_getarray_body(cert), encoded, encoded_len);
     REENCODE();
     tor_x509_cert_free(newc);





More information about the tor-commits mailing list