diff --git a/src/lib/crypt_ops/crypto_nss_mgt.c b/src/lib/crypt_ops/crypto_nss_mgt.c
index 85b18e00cd..187f556bd2 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 afc6c4201c..aaf32ec1b0 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 0411687b9a..517faa5c7b 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 395f0148e2..0b14b69f44 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 *link_cert_out and *id_cert_out to the link certificate
+ * and ID certificate that we're currently using for our V3 in-protocol
+ * handshake's certificate chain. If server 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 ctx. */
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 result
+ *
+ * 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 tls display the address
* address.
*/
diff --git a/src/lib/tls/tortls_internal.h b/src/lib/tls/tortls_internal.h
index b997ee3e4a..2920e96585 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 3ab5c753d0..35dbc27d9c 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 5f54312355..fb6328bcd0 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 "*/
#include
@@ -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 *link_cert_out and *id_cert_out to the link certificate
- * and ID certificate that we're currently using for our V3 in-protocol
- * handshake's certificate chain. If server 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 tls has authenticated to us, and
* the key certified in cert 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.
* identity 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 8316df75a3..e7440a192f 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 86f5a0de59..4b49f1dec2 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 ac9e6658d8..35b3d2542b 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
+#include
#include
+#include
+#include
+
+/* 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 *encoded_out and *size_out to cert'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, ¬Before, ¬After);
+ 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, ¬Before, ¬After);
+ 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 43bd6b4d48..28a30b66e1 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 6772765993..df3fa67eb1 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);