summaryrefslogtreecommitdiffstats
path: root/netwerk/test/unit/test_tls_server.js
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--netwerk/test/unit/test_tls_server.js343
1 files changed, 343 insertions, 0 deletions
diff --git a/netwerk/test/unit/test_tls_server.js b/netwerk/test/unit/test_tls_server.js
new file mode 100644
index 0000000000..37648eee0e
--- /dev/null
+++ b/netwerk/test/unit/test_tls_server.js
@@ -0,0 +1,343 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Need profile dir to store the key / cert
+do_get_profile();
+// Ensure PSM is initialized
+Cc["@mozilla.org/psm;1"].getService(Ci.nsISupports);
+
+const { MockRegistrar } = ChromeUtils.importESModule(
+ "resource://testing-common/MockRegistrar.sys.mjs"
+);
+const { PromiseUtils } = ChromeUtils.importESModule(
+ "resource://gre/modules/PromiseUtils.sys.mjs"
+);
+const certOverrideService = Cc[
+ "@mozilla.org/security/certoverride;1"
+].getService(Ci.nsICertOverrideService);
+const socketTransportService = Cc[
+ "@mozilla.org/network/socket-transport-service;1"
+].getService(Ci.nsISocketTransportService);
+
+const prefs = Services.prefs;
+
+function areCertsEqual(certA, certB) {
+ let derA = certA.getRawDER();
+ let derB = certB.getRawDER();
+ if (derA.length != derB.length) {
+ return false;
+ }
+ for (let i = 0; i < derA.length; i++) {
+ if (derA[i] != derB[i]) {
+ return false;
+ }
+ }
+ return true;
+}
+
+function startServer(
+ cert,
+ expectingPeerCert,
+ clientCertificateConfig,
+ expectedVersion,
+ expectedVersionStr
+) {
+ let tlsServer = Cc["@mozilla.org/network/tls-server-socket;1"].createInstance(
+ Ci.nsITLSServerSocket
+ );
+ tlsServer.init(-1, true, -1);
+ tlsServer.serverCert = cert;
+
+ let input, output;
+
+ let listener = {
+ onSocketAccepted(socket, transport) {
+ info("Accept TLS client connection");
+ let connectionInfo = transport.securityCallbacks.getInterface(
+ Ci.nsITLSServerConnectionInfo
+ );
+ connectionInfo.setSecurityObserver(listener);
+ input = transport.openInputStream(0, 0, 0);
+ output = transport.openOutputStream(0, 0, 0);
+ },
+ onHandshakeDone(socket, status) {
+ info("TLS handshake done");
+ if (expectingPeerCert) {
+ ok(!!status.peerCert, "Has peer cert");
+ ok(
+ areCertsEqual(status.peerCert, cert),
+ "Peer cert matches expected cert"
+ );
+ } else {
+ ok(!status.peerCert, "No peer cert (as expected)");
+ }
+
+ equal(
+ status.tlsVersionUsed,
+ expectedVersion,
+ "Using " + expectedVersionStr
+ );
+ let expectedCipher;
+ if (expectedVersion >= 772) {
+ expectedCipher = "TLS_AES_128_GCM_SHA256";
+ } else {
+ expectedCipher = "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256";
+ }
+ equal(status.cipherName, expectedCipher, "Using expected cipher");
+ equal(status.keyLength, 128, "Using 128-bit key");
+ equal(status.macLength, 128, "Using 128-bit MAC");
+
+ input.asyncWait(
+ {
+ onInputStreamReady(input) {
+ NetUtil.asyncCopy(input, output);
+ },
+ },
+ 0,
+ 0,
+ Services.tm.currentThread
+ );
+ },
+ onStopListening() {
+ info("onStopListening");
+ input.close();
+ output.close();
+ },
+ };
+
+ tlsServer.setSessionTickets(false);
+ tlsServer.setRequestClientCertificate(clientCertificateConfig);
+
+ tlsServer.asyncListen(listener);
+
+ return tlsServer;
+}
+
+function storeCertOverride(port, cert) {
+ certOverrideService.rememberValidityOverride(
+ "127.0.0.1",
+ port,
+ {},
+ cert,
+ true
+ );
+}
+
+function startClient(port, sendClientCert, expectingAlert, tlsVersion) {
+ gClientAuthDialogs.selectCertificate = sendClientCert;
+ let SSL_ERROR_BASE = Ci.nsINSSErrorsService.NSS_SSL_ERROR_BASE;
+ let SSL_ERROR_BAD_CERT_ALERT = SSL_ERROR_BASE + 17;
+ let SSL_ERROR_RX_CERTIFICATE_REQUIRED_ALERT = SSL_ERROR_BASE + 181;
+ let transport = socketTransportService.createTransport(
+ ["ssl"],
+ "127.0.0.1",
+ port,
+ null,
+ null
+ );
+ let input;
+ let output;
+
+ let inputDeferred = PromiseUtils.defer();
+ let outputDeferred = PromiseUtils.defer();
+
+ let handler = {
+ onTransportStatus(transport, status) {
+ if (status === Ci.nsISocketTransport.STATUS_CONNECTED_TO) {
+ output.asyncWait(handler, 0, 0, Services.tm.currentThread);
+ }
+ },
+
+ onInputStreamReady(input) {
+ try {
+ let data = NetUtil.readInputStreamToString(input, input.available());
+ equal(data, "HELLO", "Echoed data received");
+ input.close();
+ output.close();
+ ok(!expectingAlert, "No cert alert expected");
+ inputDeferred.resolve();
+ } catch (e) {
+ let errorCode = -1 * (e.result & 0xffff);
+ if (expectingAlert) {
+ if (
+ tlsVersion == Ci.nsITLSClientStatus.TLS_VERSION_1_2 &&
+ errorCode == SSL_ERROR_BAD_CERT_ALERT
+ ) {
+ info("Got bad cert alert as expected for tls 1.2");
+ input.close();
+ output.close();
+ inputDeferred.resolve();
+ return;
+ }
+ if (
+ tlsVersion == Ci.nsITLSClientStatus.TLS_VERSION_1_3 &&
+ errorCode == SSL_ERROR_RX_CERTIFICATE_REQUIRED_ALERT
+ ) {
+ info("Got cert required alert as expected for tls 1.3");
+ input.close();
+ output.close();
+ inputDeferred.resolve();
+ return;
+ }
+ }
+ inputDeferred.reject(e);
+ }
+ },
+
+ onOutputStreamReady(output) {
+ try {
+ output.write("HELLO", 5);
+ info("Output to server written");
+ outputDeferred.resolve();
+ input = transport.openInputStream(0, 0, 0);
+ input.asyncWait(handler, 0, 0, Services.tm.currentThread);
+ } catch (e) {
+ let errorCode = -1 * (e.result & 0xffff);
+ if (errorCode == SSL_ERROR_BAD_CERT_ALERT) {
+ info("Server doesn't like client cert");
+ }
+ outputDeferred.reject(e);
+ }
+ },
+ };
+
+ transport.setEventSink(handler, Services.tm.currentThread);
+ output = transport.openOutputStream(0, 0, 0);
+
+ return Promise.all([inputDeferred.promise, outputDeferred.promise]);
+}
+
+// Replace the UI dialog that prompts the user to pick a client certificate.
+const gClientAuthDialogs = {
+ _selectCertificate: false,
+
+ set selectCertificate(value) {
+ this._selectCertificate = value;
+ },
+
+ chooseCertificate(
+ hostname,
+ port,
+ organization,
+ issuerOrg,
+ certList,
+ selectedIndex,
+ rememberClientAuthCertificate
+ ) {
+ rememberClientAuthCertificate.value = false;
+ if (this._selectCertificate) {
+ selectedIndex.value = 0;
+ return true;
+ }
+ return false;
+ },
+
+ QueryInterface: ChromeUtils.generateQI([Ci.nsIClientAuthDialogs]),
+};
+
+const ClientAuthDialogsContractID = "@mozilla.org/nsClientAuthDialogs;1";
+// On all platforms but Android, this component already exists, so this replaces
+// it. On Android, the component does not exist, so this registers it.
+if (AppConstants.platform != "android") {
+ MockRegistrar.register(ClientAuthDialogsContractID, gClientAuthDialogs);
+} else {
+ const factory = {
+ createInstance(iid) {
+ return gClientAuthDialogs.QueryInterface(iid);
+ },
+
+ QueryInterface: ChromeUtils.generateQI(["nsIFactory"]),
+ };
+ const Cm = Components.manager;
+ const registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
+ const cid = Services.uuid.generateUUID();
+ registrar.registerFactory(
+ cid,
+ "A Mock for " + ClientAuthDialogsContractID,
+ ClientAuthDialogsContractID,
+ factory
+ );
+}
+
+const tests = [
+ {
+ expectingPeerCert: true,
+ clientCertificateConfig: Ci.nsITLSServerSocket.REQUIRE_ALWAYS,
+ sendClientCert: true,
+ expectingAlert: false,
+ },
+ {
+ expectingPeerCert: true,
+ clientCertificateConfig: Ci.nsITLSServerSocket.REQUIRE_ALWAYS,
+ sendClientCert: false,
+ expectingAlert: true,
+ },
+ {
+ expectingPeerCert: true,
+ clientCertificateConfig: Ci.nsITLSServerSocket.REQUEST_ALWAYS,
+ sendClientCert: true,
+ expectingAlert: false,
+ },
+ {
+ expectingPeerCert: false,
+ clientCertificateConfig: Ci.nsITLSServerSocket.REQUEST_ALWAYS,
+ sendClientCert: false,
+ expectingAlert: false,
+ },
+ {
+ expectingPeerCert: false,
+ clientCertificateConfig: Ci.nsITLSServerSocket.REQUEST_NEVER,
+ sendClientCert: true,
+ expectingAlert: false,
+ },
+ {
+ expectingPeerCert: false,
+ clientCertificateConfig: Ci.nsITLSServerSocket.REQUEST_NEVER,
+ sendClientCert: false,
+ expectingAlert: false,
+ },
+];
+
+const versions = [
+ {
+ prefValue: 3,
+ version: Ci.nsITLSClientStatus.TLS_VERSION_1_2,
+ versionStr: "TLS 1.2",
+ },
+ {
+ prefValue: 4,
+ version: Ci.nsITLSClientStatus.TLS_VERSION_1_3,
+ versionStr: "TLS 1.3",
+ },
+];
+
+add_task(async function() {
+ let cert = getTestServerCertificate();
+ ok(!!cert, "Got self-signed cert");
+ for (let v of versions) {
+ prefs.setIntPref("security.tls.version.max", v.prefValue);
+ for (let t of tests) {
+ let server = startServer(
+ cert,
+ t.expectingPeerCert,
+ t.clientCertificateConfig,
+ v.version,
+ v.versionStr
+ );
+ storeCertOverride(server.port, cert);
+ await startClient(
+ server.port,
+ t.sendClientCert,
+ t.expectingAlert,
+ v.version
+ );
+ server.close();
+ }
+ }
+});
+
+registerCleanupFunction(function() {
+ prefs.clearUserPref("security.tls.version.max");
+});