From c567b8fcb4e4851d6db19946cce8c4d5e75535f5 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Mon, 13 Aug 2018 14:54:35 -0400 Subject: [PATCH] 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 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);