|  | @@ -12,6 +12,7 @@
 | 
	
		
			
				|  |  |  #include "orconfig.h"
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  #define TORTLS_PRIVATE
 | 
	
		
			
				|  |  | +#define TOR_X509_PRIVATE
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  #ifdef _WIN32 /*wrkard for dtls1.h >= 0.9.8m of "#include <winsock.h>"*/
 | 
	
		
			
				|  |  |    #include <winsock2.h>
 | 
	
	
		
			
				|  | @@ -22,6 +23,9 @@
 | 
	
		
			
				|  |  |  #include "lib/crypt_ops/crypto_rand.h"
 | 
	
		
			
				|  |  |  #include "lib/crypt_ops/crypto_dh.h"
 | 
	
		
			
				|  |  |  #include "lib/crypt_ops/crypto_util.h"
 | 
	
		
			
				|  |  | +#include "lib/crypt_ops/crypto_nss_mgt.h"
 | 
	
		
			
				|  |  | +#include "lib/string/printf.h"
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  #include "lib/tls/x509.h"
 | 
	
		
			
				|  |  |  #include "lib/tls/x509_internal.h"
 | 
	
		
			
				|  |  |  #include "lib/tls/tortls.h"
 | 
	
	
		
			
				|  | @@ -29,26 +33,16 @@
 | 
	
		
			
				|  |  |  #include "lib/tls/tortls_internal.h"
 | 
	
		
			
				|  |  |  #include "lib/log/util_bug.h"
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -int
 | 
	
		
			
				|  |  | -tor_errno_to_tls_error(int e)
 | 
	
		
			
				|  |  | -{
 | 
	
		
			
				|  |  | -  (void)e;
 | 
	
		
			
				|  |  | -  // XXXX
 | 
	
		
			
				|  |  | -  return -1;
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -int
 | 
	
		
			
				|  |  | -tor_tls_get_error(tor_tls_t *tls, int r, int extra,
 | 
	
		
			
				|  |  | -                  const char *doing, int severity, int domain)
 | 
	
		
			
				|  |  | -{
 | 
	
		
			
				|  |  | -  (void)tls;
 | 
	
		
			
				|  |  | -  (void)r;
 | 
	
		
			
				|  |  | -  (void)extra;
 | 
	
		
			
				|  |  | -  (void)doing;
 | 
	
		
			
				|  |  | -  (void)severity;
 | 
	
		
			
				|  |  | -  (void)domain;
 | 
	
		
			
				|  |  | -  // XXXX
 | 
	
		
			
				|  |  | -  return -1;
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | +#include <prio.h>
 | 
	
		
			
				|  |  | +// For access to raw sockets.
 | 
	
		
			
				|  |  | +#include <private/pprio.h>
 | 
	
		
			
				|  |  | +#include <ssl.h>
 | 
	
		
			
				|  |  | +#include <sslt.h>
 | 
	
		
			
				|  |  | +#include <sslproto.h>
 | 
	
		
			
				|  |  | +#include <certt.h>
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static SECStatus always_accept_cert_cb(void *, PRFileDesc *, PRBool, PRBool);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  MOCK_IMPL(void,
 | 
	
		
			
				|  |  |  try_to_extract_certs_from_tls,(int severity, tor_tls_t *tls,
 | 
	
		
			
				|  |  |                                 tor_x509_cert_impl_t **cert_out,
 | 
	
	
		
			
				|  | @@ -57,14 +51,109 @@ try_to_extract_certs_from_tls,(int severity, tor_tls_t *tls,
 | 
	
		
			
				|  |  |    tor_assert(tls);
 | 
	
		
			
				|  |  |    tor_assert(cert_out);
 | 
	
		
			
				|  |  |    tor_assert(id_cert_out);
 | 
	
		
			
				|  |  | -  (void)severity;
 | 
	
		
			
				|  |  | -  // XXXX
 | 
	
		
			
				|  |  | +  (void) severity;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  *cert_out = *id_cert_out = NULL;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  CERTCertificate *peer = SSL_PeerCertificate(tls->ssl);
 | 
	
		
			
				|  |  | +  if (!peer)
 | 
	
		
			
				|  |  | +    return;
 | 
	
		
			
				|  |  | +  *cert_out = peer; /* Now owns pointer. */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  CERTCertList *chain = SSL_PeerCertificateChain(tls->ssl);
 | 
	
		
			
				|  |  | +  CERTCertListNode *c = CERT_LIST_HEAD(chain);
 | 
	
		
			
				|  |  | +  for (; !CERT_LIST_END(c, chain); c = CERT_LIST_NEXT(c)) {
 | 
	
		
			
				|  |  | +    if (CERT_CompareCerts(c->cert, peer) == PR_FALSE) {
 | 
	
		
			
				|  |  | +      *id_cert_out = CERT_DupCertificate(c->cert);
 | 
	
		
			
				|  |  | +      break;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  CERT_DestroyCertList(chain);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static bool
 | 
	
		
			
				|  |  | +we_like_ssl_cipher(SSLCipherAlgorithm ca)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +  switch (ca) {
 | 
	
		
			
				|  |  | +    case ssl_calg_null: return false;
 | 
	
		
			
				|  |  | +    case ssl_calg_rc4: return false;
 | 
	
		
			
				|  |  | +    case ssl_calg_rc2: return false;
 | 
	
		
			
				|  |  | +    case ssl_calg_des: return false;
 | 
	
		
			
				|  |  | +    case ssl_calg_3des: return false; /* ???? */
 | 
	
		
			
				|  |  | +    case ssl_calg_idea: return false;
 | 
	
		
			
				|  |  | +    case ssl_calg_fortezza: return false;
 | 
	
		
			
				|  |  | +    case ssl_calg_camellia: return false;
 | 
	
		
			
				|  |  | +    case ssl_calg_seed: return false;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    case ssl_calg_aes: return true;
 | 
	
		
			
				|  |  | +    case ssl_calg_aes_gcm: return true;
 | 
	
		
			
				|  |  | +    case ssl_calg_chacha20: return true;
 | 
	
		
			
				|  |  | +    default: return true;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +static bool
 | 
	
		
			
				|  |  | +we_like_ssl_kea(SSLKEAType kt)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +  switch (kt) {
 | 
	
		
			
				|  |  | +    case ssl_kea_null: return false;
 | 
	
		
			
				|  |  | +    case ssl_kea_rsa: return false; /* ??? */
 | 
	
		
			
				|  |  | +    case ssl_kea_fortezza: return false;
 | 
	
		
			
				|  |  | +    case ssl_kea_ecdh_psk: return false;
 | 
	
		
			
				|  |  | +    case ssl_kea_dh_psk: return false;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    case ssl_kea_dh: return true;
 | 
	
		
			
				|  |  | +    case ssl_kea_ecdh: return true;
 | 
	
		
			
				|  |  | +    case ssl_kea_tls13_any: return true;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    case ssl_kea_size: return true; /* prevent a warning. */
 | 
	
		
			
				|  |  | +    default: return true;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static bool
 | 
	
		
			
				|  |  | +we_like_mac_algorithm(SSLMACAlgorithm ma)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +  switch (ma) {
 | 
	
		
			
				|  |  | +    case ssl_mac_null: return false;
 | 
	
		
			
				|  |  | +    case ssl_mac_md5: return false;
 | 
	
		
			
				|  |  | +    case ssl_hmac_md5: return false;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    case ssl_mac_sha: return true;
 | 
	
		
			
				|  |  | +    case ssl_hmac_sha: return true;
 | 
	
		
			
				|  |  | +    case ssl_hmac_sha256: return true;
 | 
	
		
			
				|  |  | +    case ssl_mac_aead: return true;
 | 
	
		
			
				|  |  | +    case ssl_hmac_sha384: return true;
 | 
	
		
			
				|  |  | +    default: return true;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static bool
 | 
	
		
			
				|  |  | +we_like_auth_type(SSLAuthType at)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +  switch (at) {
 | 
	
		
			
				|  |  | +    case ssl_auth_null: return false;
 | 
	
		
			
				|  |  | +    case ssl_auth_rsa_decrypt: return false;
 | 
	
		
			
				|  |  | +    case ssl_auth_dsa: return false;
 | 
	
		
			
				|  |  | +    case ssl_auth_kea: return false;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    case ssl_auth_ecdsa: return true;
 | 
	
		
			
				|  |  | +    case ssl_auth_ecdh_rsa: return true;
 | 
	
		
			
				|  |  | +    case ssl_auth_ecdh_ecdsa: return true;
 | 
	
		
			
				|  |  | +    case ssl_auth_rsa_sign: return true;
 | 
	
		
			
				|  |  | +    case ssl_auth_rsa_pss: return true;
 | 
	
		
			
				|  |  | +    case ssl_auth_psk: return true;
 | 
	
		
			
				|  |  | +    case ssl_auth_tls13_any: return true;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    case ssl_auth_size: return true; /* prevent a warning. */
 | 
	
		
			
				|  |  | +    default: return true;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  tor_tls_context_t *
 | 
	
		
			
				|  |  |  tor_tls_context_new(crypto_pk_t *identity,
 | 
	
		
			
				|  |  |                      unsigned int key_lifetime, unsigned flags, int is_client)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  | +  SECStatus s;
 | 
	
		
			
				|  |  |    tor_assert(identity);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    tor_tls_context_t *ctx = tor_malloc_zero(sizeof(tor_tls_context_t));
 | 
	
	
		
			
				|  | @@ -77,7 +166,128 @@ tor_tls_context_new(crypto_pk_t *identity,
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  // XXXX write the main body.
 | 
	
		
			
				|  |  | +  {
 | 
	
		
			
				|  |  | +    /* Create the "model" PRFileDesc that we will use to base others on. */
 | 
	
		
			
				|  |  | +    PRFileDesc *tcp = PR_NewTCPSocket();
 | 
	
		
			
				|  |  | +    if (!tcp)
 | 
	
		
			
				|  |  | +      goto err;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    ctx->ctx = SSL_ImportFD(NULL, tcp);
 | 
	
		
			
				|  |  | +    if (!ctx->ctx) {
 | 
	
		
			
				|  |  | +      PR_Close(tcp);
 | 
	
		
			
				|  |  | +      goto err;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // Configure the certificate.
 | 
	
		
			
				|  |  | +  if (!is_client) {
 | 
	
		
			
				|  |  | +    s = SSL_ConfigServerCert(ctx->ctx,
 | 
	
		
			
				|  |  | +                             ctx->my_link_cert->cert,
 | 
	
		
			
				|  |  | +                             (SECKEYPrivateKey *)
 | 
	
		
			
				|  |  | +                               crypto_pk_get_nss_privkey(ctx->link_key),
 | 
	
		
			
				|  |  | +                             NULL, /* ExtraServerCertData */
 | 
	
		
			
				|  |  | +                             0 /* DataLen */);
 | 
	
		
			
				|  |  | +    if (s != SECSuccess)
 | 
	
		
			
				|  |  | +      goto err;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // We need a certificate from the other side.
 | 
	
		
			
				|  |  | +  if (is_client) {
 | 
	
		
			
				|  |  | +    // XXXX does this do anything?
 | 
	
		
			
				|  |  | +    s = SSL_OptionSet(ctx->ctx, SSL_REQUIRE_CERTIFICATE, PR_TRUE);
 | 
	
		
			
				|  |  | +    if (s != SECSuccess)
 | 
	
		
			
				|  |  | +      goto err;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // Always accept other side's cert; we'll check it ourselves in goofy
 | 
	
		
			
				|  |  | +  // tor ways.
 | 
	
		
			
				|  |  | +  s = SSL_AuthCertificateHook(ctx->ctx, always_accept_cert_cb, NULL);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // We allow simultaneous read and write.
 | 
	
		
			
				|  |  | +  s = SSL_OptionSet(ctx->ctx, SSL_ENABLE_FDX, PR_TRUE);
 | 
	
		
			
				|  |  | +  if (s != SECSuccess)
 | 
	
		
			
				|  |  | +    goto err;
 | 
	
		
			
				|  |  | +  // XXXX SSL_ROLLBACK_DETECTION??
 | 
	
		
			
				|  |  | +  // XXXX SSL_ENABLE_ALPN??
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // Force client-mode or server_mode.
 | 
	
		
			
				|  |  | +  s = SSL_OptionSet(ctx->ctx,
 | 
	
		
			
				|  |  | +                is_client ? SSL_HANDSHAKE_AS_CLIENT : SSL_HANDSHAKE_AS_SERVER,
 | 
	
		
			
				|  |  | +                PR_TRUE);
 | 
	
		
			
				|  |  | +  if (s != SECSuccess)
 | 
	
		
			
				|  |  | +    goto err;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // Disable everything before TLS 1.0; support everything else.
 | 
	
		
			
				|  |  | +  {
 | 
	
		
			
				|  |  | +    SSLVersionRange vrange;
 | 
	
		
			
				|  |  | +    memset(&vrange, 0, sizeof(vrange));
 | 
	
		
			
				|  |  | +    s = SSL_VersionRangeGetSupported(ssl_variant_stream, &vrange);
 | 
	
		
			
				|  |  | +    if (s != SECSuccess)
 | 
	
		
			
				|  |  | +      goto err;
 | 
	
		
			
				|  |  | +    if (vrange.min < SSL_LIBRARY_VERSION_TLS_1_0)
 | 
	
		
			
				|  |  | +      vrange.min = SSL_LIBRARY_VERSION_TLS_1_0;
 | 
	
		
			
				|  |  | +    s = SSL_VersionRangeSet(ctx->ctx, &vrange);
 | 
	
		
			
				|  |  | +    if (s != SECSuccess)
 | 
	
		
			
				|  |  | +      goto err;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // Only support strong ciphers.
 | 
	
		
			
				|  |  | +  {
 | 
	
		
			
				|  |  | +    const PRUint16 *ciphers = SSL_GetImplementedCiphers();
 | 
	
		
			
				|  |  | +    const PRUint16 n_ciphers = SSL_GetNumImplementedCiphers();
 | 
	
		
			
				|  |  | +    PRUint16 i;
 | 
	
		
			
				|  |  | +    for (i = 0; i < n_ciphers; ++i) {
 | 
	
		
			
				|  |  | +      SSLCipherSuiteInfo info;
 | 
	
		
			
				|  |  | +      memset(&info, 0, sizeof(info));
 | 
	
		
			
				|  |  | +      s = SSL_GetCipherSuiteInfo(ciphers[i], &info, sizeof(info));
 | 
	
		
			
				|  |  | +      if (s != SECSuccess)
 | 
	
		
			
				|  |  | +        goto err;
 | 
	
		
			
				|  |  | +      if (BUG(info.cipherSuite != ciphers[i]))
 | 
	
		
			
				|  |  | +        goto err;
 | 
	
		
			
				|  |  | +      int disable = info.effectiveKeyBits < 128 ||
 | 
	
		
			
				|  |  | +        info.macBits < 128 ||
 | 
	
		
			
				|  |  | +        !we_like_ssl_cipher(info.symCipher) ||
 | 
	
		
			
				|  |  | +        !we_like_ssl_kea(info.keaType) ||
 | 
	
		
			
				|  |  | +        !we_like_mac_algorithm(info.macAlgorithm) ||
 | 
	
		
			
				|  |  | +        !we_like_auth_type(info.authType)/* Requires NSS 3.24 */;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +      s = SSL_CipherPrefSet(ctx->ctx, ciphers[i],
 | 
	
		
			
				|  |  | +                            disable ? PR_FALSE : PR_TRUE);
 | 
	
		
			
				|  |  | +      if (s != SECSuccess)
 | 
	
		
			
				|  |  | +        goto err;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // Only use DH and ECDH keys once.
 | 
	
		
			
				|  |  | +  s = SSL_OptionSet(ctx->ctx, SSL_REUSE_SERVER_ECDHE_KEY, PR_FALSE);
 | 
	
		
			
				|  |  | +  if (s != SECSuccess)
 | 
	
		
			
				|  |  | +    goto err;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // don't cache sessions.
 | 
	
		
			
				|  |  | +  s = SSL_OptionSet(ctx->ctx, SSL_NO_CACHE, PR_TRUE);
 | 
	
		
			
				|  |  | +  if (s != SECSuccess)
 | 
	
		
			
				|  |  | +    goto err;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // Enable DH.
 | 
	
		
			
				|  |  | +  s = SSL_OptionSet(ctx->ctx, SSL_ENABLE_SERVER_DHE, PR_TRUE);
 | 
	
		
			
				|  |  | +  if (s != SECSuccess)
 | 
	
		
			
				|  |  | +    goto err;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // Set DH and ECDH groups.
 | 
	
		
			
				|  |  | +  SSLNamedGroup groups[] = {
 | 
	
		
			
				|  |  | +      ssl_grp_ec_curve25519,
 | 
	
		
			
				|  |  | +      ssl_grp_ec_secp256r1,
 | 
	
		
			
				|  |  | +      ssl_grp_ec_secp224r1,
 | 
	
		
			
				|  |  | +      ssl_grp_ffdhe_2048,
 | 
	
		
			
				|  |  | +  };
 | 
	
		
			
				|  |  | +  s = SSL_NamedGroupConfig(ctx->ctx, groups, ARRAY_LENGTH(groups));
 | 
	
		
			
				|  |  | +  if (s != SECSuccess)
 | 
	
		
			
				|  |  | +    goto err;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // These features are off by default, so we don't need to disable them:
 | 
	
		
			
				|  |  | +  //   Session tickets
 | 
	
		
			
				|  |  | +  //   Renegotiation
 | 
	
		
			
				|  |  | +  //   Compression
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    goto done;
 | 
	
		
			
				|  |  |   err:
 | 
	
	
		
			
				|  | @@ -88,11 +298,9 @@ tor_tls_context_new(crypto_pk_t *identity,
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  void
 | 
	
		
			
				|  |  | -tor_tls_context_impl_free(struct ssl_ctx_st *ctx)
 | 
	
		
			
				|  |  | +tor_tls_context_impl_free(tor_tls_context_impl_t *ctx)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  | -  (void)ctx;
 | 
	
		
			
				|  |  | -  // XXXX
 | 
	
		
			
				|  |  | -  // XXXX openssl type.
 | 
	
		
			
				|  |  | +  PR_Close(ctx);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  void
 | 
	
	
		
			
				|  | @@ -101,33 +309,82 @@ tor_tls_get_state_description(tor_tls_t *tls, char *buf, size_t sz)
 | 
	
		
			
				|  |  |    (void)tls;
 | 
	
		
			
				|  |  |    (void)buf;
 | 
	
		
			
				|  |  |    (void)sz;
 | 
	
		
			
				|  |  | -  // XXXX
 | 
	
		
			
				|  |  | +  // AFAICT, NSS doesn't expose its internal state.
 | 
	
		
			
				|  |  | +  buf[0]=0;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  void
 | 
	
		
			
				|  |  |  tor_tls_init(void)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  | -  // XXXX
 | 
	
		
			
				|  |  | +  /* We don't have any global setup to do yet, but that will change */
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  void
 | 
	
		
			
				|  |  |  tls_log_errors(tor_tls_t *tls, int severity, int domain,
 | 
	
		
			
				|  |  |                 const char *doing)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  | +  /* XXXX This implementation isn't right for NSS -- it logs the last error
 | 
	
		
			
				|  |  | +     whether anything actually failed or not. */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |    (void)tls;
 | 
	
		
			
				|  |  | -  (void)severity;
 | 
	
		
			
				|  |  | -  (void)domain;
 | 
	
		
			
				|  |  | -  (void)doing;
 | 
	
		
			
				|  |  | -  // XXXX
 | 
	
		
			
				|  |  | +  PRErrorCode code = PORT_GetError();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  const char *string = PORT_ErrorToString(code);
 | 
	
		
			
				|  |  | +  const char *name = PORT_ErrorToName(code);
 | 
	
		
			
				|  |  | +  char buf[16];
 | 
	
		
			
				|  |  | +  if (!string)
 | 
	
		
			
				|  |  | +    string = "<unrecognized>";
 | 
	
		
			
				|  |  | +  if (!name) {
 | 
	
		
			
				|  |  | +    tor_snprintf(buf, sizeof(buf), "%d", code);
 | 
	
		
			
				|  |  | +    name = buf;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  if (doing) {
 | 
	
		
			
				|  |  | +    log_fn(severity, domain, "TLS error %s while %s: %s", name, doing, string);
 | 
	
		
			
				|  |  | +  } else {
 | 
	
		
			
				|  |  | +    log_fn(severity, domain, "TLS error %s: %s", name, string);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  tor_tls_t *
 | 
	
		
			
				|  |  | -tor_tls_new(int sock, int is_server)
 | 
	
		
			
				|  |  | +tor_tls_new(tor_socket_t sock, int is_server)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |    (void)sock;
 | 
	
		
			
				|  |  | -  (void)is_server;
 | 
	
		
			
				|  |  | -  // XXXX
 | 
	
		
			
				|  |  | -  return NULL;
 | 
	
		
			
				|  |  | +  tor_tls_context_t *ctx = tor_tls_context_get(is_server);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  PRFileDesc *tcp = PR_ImportTCPSocket(sock);
 | 
	
		
			
				|  |  | +  if (!tcp)
 | 
	
		
			
				|  |  | +    return NULL;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  PRFileDesc *ssl = SSL_ImportFD(ctx->ctx, tcp);
 | 
	
		
			
				|  |  | +  if (!ssl) {
 | 
	
		
			
				|  |  | +    PR_Close(tcp);
 | 
	
		
			
				|  |  | +    return NULL;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  tor_tls_t *tls = tor_malloc_zero(sizeof(tor_tls_t));
 | 
	
		
			
				|  |  | +  tls->magic = TOR_TLS_MAGIC;
 | 
	
		
			
				|  |  | +  tls->context = ctx;
 | 
	
		
			
				|  |  | +  tor_tls_context_incref(ctx);
 | 
	
		
			
				|  |  | +  tls->ssl = ssl;
 | 
	
		
			
				|  |  | +  tls->socket = sock;
 | 
	
		
			
				|  |  | +  tls->state = TOR_TLS_ST_HANDSHAKE;
 | 
	
		
			
				|  |  | +  tls->isServer = !!is_server;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  if (!is_server) {
 | 
	
		
			
				|  |  | +    /* Set a random SNI */
 | 
	
		
			
				|  |  | +    char *fake_hostname = crypto_random_hostname(4,25, "www.",".com");
 | 
	
		
			
				|  |  | +    SSL_SetURL(tls->ssl, fake_hostname);
 | 
	
		
			
				|  |  | +    tor_free(fake_hostname);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  SECStatus s = SSL_ResetHandshake(ssl, is_server ? PR_TRUE : PR_FALSE);
 | 
	
		
			
				|  |  | +  if (s != SECSuccess) {
 | 
	
		
			
				|  |  | +    crypto_nss_log_errors(LOG_WARN, "resetting handshake state");
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  return tls;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  void
 | 
	
		
			
				|  |  |  tor_tls_set_renegotiate_callback(tor_tls_t *tls,
 | 
	
		
			
				|  |  |                                   void (*cb)(tor_tls_t *, void *arg),
 | 
	
	
		
			
				|  | @@ -136,131 +393,175 @@ tor_tls_set_renegotiate_callback(tor_tls_t *tls,
 | 
	
		
			
				|  |  |    tor_assert(tls);
 | 
	
		
			
				|  |  |    (void)cb;
 | 
	
		
			
				|  |  |    (void)arg;
 | 
	
		
			
				|  |  | -  // XXXX;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /* We don't support renegotiation-based TLS with NSS. */
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  void
 | 
	
		
			
				|  |  | -tor_tls_free_(tor_tls_t *tls)
 | 
	
		
			
				|  |  | +tor_tls_impl_free_(tor_tls_impl_t *tls)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  | -  (void)tls;
 | 
	
		
			
				|  |  | -  // XXXX
 | 
	
		
			
				|  |  | +  // XXXX This will close the underlying fd, which our OpenSSL version does
 | 
	
		
			
				|  |  | +  // not do!
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  PR_Close(tls);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  int
 | 
	
		
			
				|  |  |  tor_tls_peer_has_cert(tor_tls_t *tls)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  | -  (void)tls;
 | 
	
		
			
				|  |  | -  // XXXX
 | 
	
		
			
				|  |  | -  return -1;
 | 
	
		
			
				|  |  | +  CERTCertificate *cert = SSL_PeerCertificate(tls->ssl);
 | 
	
		
			
				|  |  | +  int result = (cert != NULL);
 | 
	
		
			
				|  |  | +  CERT_DestroyCertificate(cert);
 | 
	
		
			
				|  |  | +  return result;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  MOCK_IMPL(tor_x509_cert_t *,
 | 
	
		
			
				|  |  |  tor_tls_get_peer_cert,(tor_tls_t *tls))
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  | -  tor_assert(tls);
 | 
	
		
			
				|  |  | -  // XXXX
 | 
	
		
			
				|  |  | -  return NULL;
 | 
	
		
			
				|  |  | +  CERTCertificate *cert = SSL_PeerCertificate(tls->ssl);
 | 
	
		
			
				|  |  | +  if (cert)
 | 
	
		
			
				|  |  | +    return tor_x509_cert_new(cert);
 | 
	
		
			
				|  |  | +  else
 | 
	
		
			
				|  |  | +    return NULL;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  MOCK_IMPL(tor_x509_cert_t *,
 | 
	
		
			
				|  |  |  tor_tls_get_own_cert,(tor_tls_t *tls))
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |    tor_assert(tls);
 | 
	
		
			
				|  |  | -  // XXXX
 | 
	
		
			
				|  |  | -  return NULL;
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -int
 | 
	
		
			
				|  |  | -tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_t **identity)
 | 
	
		
			
				|  |  | -{
 | 
	
		
			
				|  |  | -  tor_assert(tls);
 | 
	
		
			
				|  |  | -  tor_assert(identity);
 | 
	
		
			
				|  |  | -  (void)severity;
 | 
	
		
			
				|  |  | -  // XXXX
 | 
	
		
			
				|  |  | -  return -1;
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -int
 | 
	
		
			
				|  |  | -tor_tls_check_lifetime(int severity,
 | 
	
		
			
				|  |  | -                       tor_tls_t *tls, time_t now,
 | 
	
		
			
				|  |  | -                       int past_tolerance,
 | 
	
		
			
				|  |  | -                       int future_tolerance)
 | 
	
		
			
				|  |  | -{
 | 
	
		
			
				|  |  | -  tor_assert(tls);
 | 
	
		
			
				|  |  | -  (void)severity;
 | 
	
		
			
				|  |  | -  (void)now;
 | 
	
		
			
				|  |  | -  (void)past_tolerance;
 | 
	
		
			
				|  |  | -  (void)future_tolerance;
 | 
	
		
			
				|  |  | -  // XXXX
 | 
	
		
			
				|  |  | -  return -1;
 | 
	
		
			
				|  |  | +  CERTCertificate *cert = SSL_LocalCertificate(tls->ssl);
 | 
	
		
			
				|  |  | +  if (cert)
 | 
	
		
			
				|  |  | +    return tor_x509_cert_new(cert);
 | 
	
		
			
				|  |  | +  else
 | 
	
		
			
				|  |  | +    return NULL;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  MOCK_IMPL(int,
 | 
	
		
			
				|  |  |  tor_tls_read, (tor_tls_t *tls, char *cp, size_t len))
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |    tor_assert(tls);
 | 
	
		
			
				|  |  |    tor_assert(cp);
 | 
	
		
			
				|  |  | -  (void)len;
 | 
	
		
			
				|  |  | -  // XXXX
 | 
	
		
			
				|  |  | -  return -1;
 | 
	
		
			
				|  |  | +  tor_assert(len < INT_MAX);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  PRInt32 rv = PR_Read(tls->ssl, cp, (int)len);
 | 
	
		
			
				|  |  | +  // log_debug(LD_NET, "PR_Read(%zu) returned %d", n, (int)rv);
 | 
	
		
			
				|  |  | +  if (rv > 0) {
 | 
	
		
			
				|  |  | +    tls->n_read_since_last_check += rv;
 | 
	
		
			
				|  |  | +    return rv;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  if (rv == 0)
 | 
	
		
			
				|  |  | +    return TOR_TLS_CLOSE;
 | 
	
		
			
				|  |  | +  PRErrorCode err = PORT_GetError();
 | 
	
		
			
				|  |  | +  if (err == PR_WOULD_BLOCK_ERROR) {
 | 
	
		
			
				|  |  | +    return TOR_TLS_WANTREAD; // XXXX ????
 | 
	
		
			
				|  |  | +  } else {
 | 
	
		
			
				|  |  | +    crypto_nss_log_errors(LOG_NOTICE, "reading"); // XXXX
 | 
	
		
			
				|  |  | +    return TOR_TLS_ERROR_MISC; // ????
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  int
 | 
	
		
			
				|  |  |  tor_tls_write(tor_tls_t *tls, const char *cp, size_t n)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |    tor_assert(tls);
 | 
	
		
			
				|  |  | -  tor_assert(cp);
 | 
	
		
			
				|  |  | -  (void)n;
 | 
	
		
			
				|  |  | -  // XXXX
 | 
	
		
			
				|  |  | -  return -1;
 | 
	
		
			
				|  |  | +  tor_assert(cp || n == 0);
 | 
	
		
			
				|  |  | +  tor_assert(n < INT_MAX);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  PRInt32 rv = PR_Write(tls->ssl, cp, (int)n);
 | 
	
		
			
				|  |  | +  // log_debug(LD_NET, "PR_Write(%zu) returned %d", n, (int)rv);
 | 
	
		
			
				|  |  | +  if (rv > 0) {
 | 
	
		
			
				|  |  | +    tls->n_written_since_last_check += rv;
 | 
	
		
			
				|  |  | +    return rv;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  if (rv == 0)
 | 
	
		
			
				|  |  | +    return TOR_TLS_ERROR_MISC;
 | 
	
		
			
				|  |  | +  PRErrorCode err = PORT_GetError();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  if (err == PR_WOULD_BLOCK_ERROR) {
 | 
	
		
			
				|  |  | +    return TOR_TLS_WANTWRITE; // XXXX ????
 | 
	
		
			
				|  |  | +  } else {
 | 
	
		
			
				|  |  | +    crypto_nss_log_errors(LOG_NOTICE, "writing"); // XXXX
 | 
	
		
			
				|  |  | +    return TOR_TLS_ERROR_MISC; // ????
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  int
 | 
	
		
			
				|  |  |  tor_tls_handshake(tor_tls_t *tls)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |    tor_assert(tls);
 | 
	
		
			
				|  |  | -  // XXXX
 | 
	
		
			
				|  |  | -  return -1;
 | 
	
		
			
				|  |  | +  tor_assert(tls->state == TOR_TLS_ST_HANDSHAKE);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  SECStatus s = SSL_ForceHandshake(tls->ssl);
 | 
	
		
			
				|  |  | +  if (s == SECSuccess) {
 | 
	
		
			
				|  |  | +    tls->state = TOR_TLS_ST_OPEN;
 | 
	
		
			
				|  |  | +    log_debug(LD_NET, "SSL handshake is supposedly complete.");
 | 
	
		
			
				|  |  | +    return tor_tls_finish_handshake(tls);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  if (PORT_GetError() == PR_WOULD_BLOCK_ERROR)
 | 
	
		
			
				|  |  | +    return TOR_TLS_WANTREAD; /* XXXX What about wantwrite? */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  return TOR_TLS_ERROR_MISC; // XXXX
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  int
 | 
	
		
			
				|  |  |  tor_tls_finish_handshake(tor_tls_t *tls)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |    tor_assert(tls);
 | 
	
		
			
				|  |  | -  // XXXX
 | 
	
		
			
				|  |  | -  return -1;
 | 
	
		
			
				|  |  | +  // We don't need to do any of the weird handshake nonsense stuff on NSS,
 | 
	
		
			
				|  |  | +  // since we only support recent handshakes.
 | 
	
		
			
				|  |  | +  return TOR_TLS_DONE;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  void
 | 
	
		
			
				|  |  |  tor_tls_unblock_renegotiation(tor_tls_t *tls)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |    tor_assert(tls);
 | 
	
		
			
				|  |  | -  // XXXX
 | 
	
		
			
				|  |  | +  /* We don't support renegotiation with NSS. */
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  void
 | 
	
		
			
				|  |  |  tor_tls_block_renegotiation(tor_tls_t *tls)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |    tor_assert(tls);
 | 
	
		
			
				|  |  | -  // XXXX
 | 
	
		
			
				|  |  | +  /* We don't support renegotiation with NSS. */
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  void
 | 
	
		
			
				|  |  |  tor_tls_assert_renegotiation_unblocked(tor_tls_t *tls)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |    tor_assert(tls);
 | 
	
		
			
				|  |  | -  // XXXX
 | 
	
		
			
				|  |  | +  /* We don't support renegotiation with NSS. */
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  int
 | 
	
		
			
				|  |  |  tor_tls_shutdown(tor_tls_t *tls)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |    tor_assert(tls);
 | 
	
		
			
				|  |  | -  // XXXX
 | 
	
		
			
				|  |  | +  /* XXXX This is not actually used, so I'm not implementing it.  We can
 | 
	
		
			
				|  |  | +   * XXXX remove this function entirely someday. */
 | 
	
		
			
				|  |  |    return -1;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  int
 | 
	
		
			
				|  |  |  tor_tls_get_pending_bytes(tor_tls_t *tls)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |    tor_assert(tls);
 | 
	
		
			
				|  |  | -  // XXXX
 | 
	
		
			
				|  |  | -  return -1;
 | 
	
		
			
				|  |  | +  int n = SSL_DataPending(tls->ssl);
 | 
	
		
			
				|  |  | +  if (n < 0) {
 | 
	
		
			
				|  |  | +    crypto_nss_log_errors(LOG_WARN, "Looking up pending bytes");
 | 
	
		
			
				|  |  | +    return 0;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  return (int)n;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  size_t
 | 
	
		
			
				|  |  |  tor_tls_get_forced_write_size(tor_tls_t *tls)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |    tor_assert(tls);
 | 
	
		
			
				|  |  | -  // XXXX
 | 
	
		
			
				|  |  | +  /* NSS doesn't have the same "forced write" restriction as openssl. */
 | 
	
		
			
				|  |  |    return 0;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  void
 | 
	
		
			
				|  |  |  tor_tls_get_n_raw_bytes(tor_tls_t *tls,
 | 
	
		
			
				|  |  |                          size_t *n_read, size_t *n_written)
 | 
	
	
		
			
				|  | @@ -268,7 +569,13 @@ tor_tls_get_n_raw_bytes(tor_tls_t *tls,
 | 
	
		
			
				|  |  |    tor_assert(tls);
 | 
	
		
			
				|  |  |    tor_assert(n_read);
 | 
	
		
			
				|  |  |    tor_assert(n_written);
 | 
	
		
			
				|  |  | -  // XXXX
 | 
	
		
			
				|  |  | +  /* XXXX We don't curently have a way to measure this information correctly
 | 
	
		
			
				|  |  | +   * in NSS; we could do that with a PRIO layer, but it'll take a little
 | 
	
		
			
				|  |  | +   * coding.  For now, we just track the number of bytes sent _in_ the TLS
 | 
	
		
			
				|  |  | +   * stream.  Doing this will make our rate-limiting slightly inaccurate. */
 | 
	
		
			
				|  |  | +  *n_read = tls->n_read_since_last_check;
 | 
	
		
			
				|  |  | +  *n_written = tls->n_written_since_last_check;
 | 
	
		
			
				|  |  | +  tls->n_read_since_last_check = tls->n_written_since_last_check = 0;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  int
 | 
	
	
		
			
				|  | @@ -281,54 +588,70 @@ tor_tls_get_buffer_sizes(tor_tls_t *tls,
 | 
	
		
			
				|  |  |    tor_assert(rbuf_bytes);
 | 
	
		
			
				|  |  |    tor_assert(wbuf_capacity);
 | 
	
		
			
				|  |  |    tor_assert(wbuf_bytes);
 | 
	
		
			
				|  |  | -  // XXXX
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /* This is an acceptable way to say "we can't measure this." */
 | 
	
		
			
				|  |  |    return -1;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  MOCK_IMPL(double,
 | 
	
		
			
				|  |  |  tls_get_write_overhead_ratio, (void))
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  | -  // XXXX
 | 
	
		
			
				|  |  | -  return 0.0;
 | 
	
		
			
				|  |  | +  /* XXX We don't currently have a way to measure this in NSS; we could do that
 | 
	
		
			
				|  |  | +   * XXX with a PRIO layer, but it'll take a little coding. */
 | 
	
		
			
				|  |  | +  return 0.95;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  int
 | 
	
		
			
				|  |  |  tor_tls_used_v1_handshake(tor_tls_t *tls)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |    tor_assert(tls);
 | 
	
		
			
				|  |  | -  // XXXX
 | 
	
		
			
				|  |  | -  return -1;
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -int
 | 
	
		
			
				|  |  | -tor_tls_get_num_server_handshakes(tor_tls_t *tls)
 | 
	
		
			
				|  |  | -{
 | 
	
		
			
				|  |  | -  tor_assert(tls);
 | 
	
		
			
				|  |  | -  // XXXX
 | 
	
		
			
				|  |  | -  return -1;
 | 
	
		
			
				|  |  | +  /* We don't support or allow the V1 handshake with NSS.
 | 
	
		
			
				|  |  | +   */
 | 
	
		
			
				|  |  | +  return 0;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  int
 | 
	
		
			
				|  |  |  tor_tls_server_got_renegotiate(tor_tls_t *tls)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |    tor_assert(tls);
 | 
	
		
			
				|  |  | -  // XXXX
 | 
	
		
			
				|  |  | -  return -1;
 | 
	
		
			
				|  |  | +  return 0; /* We don't support renegotiation with NSS */
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  MOCK_IMPL(int,
 | 
	
		
			
				|  |  |  tor_tls_cert_matches_key,(const tor_tls_t *tls,
 | 
	
		
			
				|  |  |                            const struct tor_x509_cert_t *cert))
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |    tor_assert(tls);
 | 
	
		
			
				|  |  |    tor_assert(cert);
 | 
	
		
			
				|  |  | -  // XXXX
 | 
	
		
			
				|  |  | -  return 0;
 | 
	
		
			
				|  |  | +  int rv = 0;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  CERTCertificate *peercert = SSL_PeerCertificate(tls->ssl);
 | 
	
		
			
				|  |  | +  if (!peercert)
 | 
	
		
			
				|  |  | +    goto done;
 | 
	
		
			
				|  |  | +  CERTSubjectPublicKeyInfo *peer_info = &peercert->subjectPublicKeyInfo;
 | 
	
		
			
				|  |  | +  CERTSubjectPublicKeyInfo *cert_info = &cert->cert->subjectPublicKeyInfo;
 | 
	
		
			
				|  |  | +  rv = SECOID_CompareAlgorithmID(&peer_info->algorithm,
 | 
	
		
			
				|  |  | +                                 &cert_info->algorithm) == 0 &&
 | 
	
		
			
				|  |  | +       SECITEM_ItemsAreEqual(&peer_info->subjectPublicKey,
 | 
	
		
			
				|  |  | +                             &cert_info->subjectPublicKey);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | + done:
 | 
	
		
			
				|  |  | +  if (peercert)
 | 
	
		
			
				|  |  | +    CERT_DestroyCertificate(peercert);
 | 
	
		
			
				|  |  | +  return rv;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  MOCK_IMPL(int,
 | 
	
		
			
				|  |  |  tor_tls_get_tlssecrets,(tor_tls_t *tls, uint8_t *secrets_out))
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |    tor_assert(tls);
 | 
	
		
			
				|  |  |    tor_assert(secrets_out);
 | 
	
		
			
				|  |  | -  // XXXX
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /* There's no way to get this information out of NSS. */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |    return -1;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  MOCK_IMPL(int,
 | 
	
		
			
				|  |  |  tor_tls_export_key_material,(tor_tls_t *tls, uint8_t *secrets_out,
 | 
	
		
			
				|  |  |                               const uint8_t *context,
 | 
	
	
		
			
				|  | @@ -339,42 +662,72 @@ tor_tls_export_key_material,(tor_tls_t *tls, uint8_t *secrets_out,
 | 
	
		
			
				|  |  |    tor_assert(secrets_out);
 | 
	
		
			
				|  |  |    tor_assert(context);
 | 
	
		
			
				|  |  |    tor_assert(label);
 | 
	
		
			
				|  |  | -  (void)context_len;
 | 
	
		
			
				|  |  | -  // XXXX
 | 
	
		
			
				|  |  | -  return -1;
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | +  tor_assert(strlen(label) <= UINT_MAX);
 | 
	
		
			
				|  |  | +  tor_assert(context_len <= UINT_MAX);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -void
 | 
	
		
			
				|  |  | -check_no_tls_errors_(const char *fname, int line)
 | 
	
		
			
				|  |  | -{
 | 
	
		
			
				|  |  | -  (void)fname;
 | 
	
		
			
				|  |  | -  (void)line;
 | 
	
		
			
				|  |  | -  // XXXX
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -void
 | 
	
		
			
				|  |  | -tor_tls_log_one_error(tor_tls_t *tls, unsigned long err,
 | 
	
		
			
				|  |  | -                      int severity, int domain, const char *doing)
 | 
	
		
			
				|  |  | -{
 | 
	
		
			
				|  |  | -  tor_assert(tls);
 | 
	
		
			
				|  |  | -  (void)err;
 | 
	
		
			
				|  |  | -  (void)severity;
 | 
	
		
			
				|  |  | -  (void)domain;
 | 
	
		
			
				|  |  | -  (void)doing;
 | 
	
		
			
				|  |  | -  // XXXX
 | 
	
		
			
				|  |  | +  SECStatus s;
 | 
	
		
			
				|  |  | +  s = SSL_ExportKeyingMaterial(tls->ssl,
 | 
	
		
			
				|  |  | +                               label, (unsigned)strlen(label),
 | 
	
		
			
				|  |  | +                               PR_TRUE, context, (unsigned)context_len,
 | 
	
		
			
				|  |  | +                               secrets_out, DIGEST256_LEN);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  return (s == SECSuccess) ? 0 : -1;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  const char *
 | 
	
		
			
				|  |  |  tor_tls_get_ciphersuite_name(tor_tls_t *tls)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |    tor_assert(tls);
 | 
	
		
			
				|  |  | -  // XXXX
 | 
	
		
			
				|  |  | -  return NULL;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  SSLChannelInfo channel_info;
 | 
	
		
			
				|  |  | +  SSLCipherSuiteInfo cipher_info;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  memset(&channel_info, 0, sizeof(channel_info));
 | 
	
		
			
				|  |  | +  memset(&cipher_info, 0, sizeof(cipher_info));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  SECStatus s = SSL_GetChannelInfo(tls->ssl,
 | 
	
		
			
				|  |  | +                                   &channel_info, sizeof(channel_info));
 | 
	
		
			
				|  |  | +  if (s != SECSuccess)
 | 
	
		
			
				|  |  | +    return NULL;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  s = SSL_GetCipherSuiteInfo(channel_info.cipherSuite,
 | 
	
		
			
				|  |  | +                             &cipher_info, sizeof(cipher_info));
 | 
	
		
			
				|  |  | +  if (s != SECSuccess)
 | 
	
		
			
				|  |  | +    return NULL;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  return cipher_info.cipherSuiteName;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +/** The group we should use for ecdhe when none was selected. */
 | 
	
		
			
				|  |  | +#define SEC_OID_TOR_DEFAULT_ECDHE_GROUP SEC_OID_ANSIX962_EC_PRIME256V1
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  int
 | 
	
		
			
				|  |  |  evaluate_ecgroup_for_tls(const char *ecgroup)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  | -  (void)ecgroup;
 | 
	
		
			
				|  |  | -  // XXXX
 | 
	
		
			
				|  |  | -  return -1;
 | 
	
		
			
				|  |  | +  SECOidTag tag;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  if (!ecgroup)
 | 
	
		
			
				|  |  | +    tag = SEC_OID_TOR_DEFAULT_ECDHE_GROUP;
 | 
	
		
			
				|  |  | +  else if (!strcasecmp(ecgroup, "P256"))
 | 
	
		
			
				|  |  | +    tag = SEC_OID_ANSIX962_EC_PRIME256V1;
 | 
	
		
			
				|  |  | +  else if (!strcasecmp(ecgroup, "P224"))
 | 
	
		
			
				|  |  | +    tag = SEC_OID_SECG_EC_SECP224R1;
 | 
	
		
			
				|  |  | +  else
 | 
	
		
			
				|  |  | +    return 0;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /* I don't think we need any additional tests here for NSS */
 | 
	
		
			
				|  |  | +  (void) tag;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  return 1;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static SECStatus
 | 
	
		
			
				|  |  | +always_accept_cert_cb(void *arg, PRFileDesc *ssl, PRBool checkSig,
 | 
	
		
			
				|  |  | +                      PRBool isServer)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +  (void)arg;
 | 
	
		
			
				|  |  | +  (void)ssl;
 | 
	
		
			
				|  |  | +  (void)checkSig;
 | 
	
		
			
				|  |  | +  (void)isServer;
 | 
	
		
			
				|  |  | +  return SECSuccess;
 | 
	
		
			
				|  |  |  }
 |