diff options
Diffstat (limited to 'netwerk/test/unit/test_tls_server.js')
-rw-r--r-- | netwerk/test/unit/test_tls_server.js | 237 |
1 files changed, 237 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 000000000..d805359c7 --- /dev/null +++ b/netwerk/test/unit/test_tls_server.js @@ -0,0 +1,237 @@ +/* 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 { Services } = Cu.import("resource://gre/modules/Services.jsm", {}); +const { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm", {}); +const { Promise: promise } = + Cu.import("resource://gre/modules/Promise.jsm", {}); +const certService = Cc["@mozilla.org/security/local-cert-service;1"] + .getService(Ci.nsILocalCertService); +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 = Cc["@mozilla.org/preferences-service;1"] + .getService(Ci.nsIPrefBranch); + +function run_test() { + run_next_test(); +} + +function getCert() { + let deferred = promise.defer(); + certService.getOrCreateCert("tls-test", { + handleCert: function(c, rv) { + if (rv) { + deferred.reject(rv); + return; + } + deferred.resolve(c); + } + }); + return deferred.promise; +} + +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: function(socket, transport) { + do_print("Accept TLS client connection"); + let connectionInfo = transport.securityInfo + .QueryInterface(Ci.nsITLSServerConnectionInfo); + connectionInfo.setSecurityObserver(listener); + input = transport.openInputStream(0, 0, 0); + output = transport.openOutputStream(0, 0, 0); + }, + onHandshakeDone: function(socket, status) { + do_print("TLS handshake done"); + if (expectingPeerCert) { + ok(!!status.peerCert, "Has peer cert"); + ok(status.peerCert.equals(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_ECDSA_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: function(input) { + NetUtil.asyncCopy(input, output); + } + }, 0, 0, Services.tm.currentThread); + }, + onStopListening: function() {} + }; + + tlsServer.setSessionCache(false); + tlsServer.setSessionTickets(false); + tlsServer.setRequestClientCertificate(clientCertificateConfig); + + tlsServer.asyncListen(listener); + + return tlsServer.port; +} + +function storeCertOverride(port, cert) { + let overrideBits = Ci.nsICertOverrideService.ERROR_UNTRUSTED | + Ci.nsICertOverrideService.ERROR_MISMATCH; + certOverrideService.rememberValidityOverride("127.0.0.1", port, cert, + overrideBits, true); +} + +function startClient(port, cert, expectingBadCertAlert) { + let SSL_ERROR_BASE = Ci.nsINSSErrorsService.NSS_SSL_ERROR_BASE; + let SSL_ERROR_BAD_CERT_ALERT = SSL_ERROR_BASE + 17; + let transport = + socketTransportService.createTransport(["ssl"], 1, "127.0.0.1", port, null); + let input; + let output; + + let inputDeferred = promise.defer(); + let outputDeferred = promise.defer(); + + let handler = { + + onTransportStatus: function(transport, status) { + if (status === Ci.nsISocketTransport.STATUS_CONNECTED_TO) { + output.asyncWait(handler, 0, 0, Services.tm.currentThread); + } + }, + + onInputStreamReady: function(input) { + try { + let data = NetUtil.readInputStreamToString(input, input.available()); + equal(data, "HELLO", "Echoed data received"); + input.close(); + output.close(); + ok(!expectingBadCertAlert, "No bad cert alert expected"); + inputDeferred.resolve(); + } catch (e) { + let errorCode = -1 * (e.result & 0xFFFF); + if (expectingBadCertAlert && errorCode == SSL_ERROR_BAD_CERT_ALERT) { + inputDeferred.resolve(); + } else { + inputDeferred.reject(e); + } + } + }, + + onOutputStreamReady: function(output) { + try { + // Set the client certificate as appropriate. + if (cert) { + let clientSecInfo = transport.securityInfo; + let tlsControl = clientSecInfo.QueryInterface(Ci.nsISSLSocketControl); + tlsControl.clientCert = cert; + } + + output.write("HELLO", 5); + do_print("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) { + do_print("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. +do_load_manifest("client_cert_chooser.manifest"); + +const tests = [{ + expectingPeerCert: true, + clientCertificateConfig: Ci.nsITLSServerSocket.REQUIRE_ALWAYS, + sendClientCert: true, + expectingBadCertAlert: false +}, { + expectingPeerCert: true, + clientCertificateConfig: Ci.nsITLSServerSocket.REQUIRE_ALWAYS, + sendClientCert: false, + expectingBadCertAlert: true +}, { + expectingPeerCert: true, + clientCertificateConfig: Ci.nsITLSServerSocket.REQUEST_ALWAYS, + sendClientCert: true, + expectingBadCertAlert: false +}, { + expectingPeerCert: false, + clientCertificateConfig: Ci.nsITLSServerSocket.REQUEST_ALWAYS, + sendClientCert: false, + expectingBadCertAlert: false +}, { + expectingPeerCert: false, + clientCertificateConfig: Ci.nsITLSServerSocket.REQUEST_NEVER, + sendClientCert: true, + expectingBadCertAlert: false +}, { + expectingPeerCert: false, + clientCertificateConfig: Ci.nsITLSServerSocket.REQUEST_NEVER, + sendClientCert: false, + expectingBadCertAlert: 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(function*() { + let cert = yield getCert(); + 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 port = startServer(cert, + t.expectingPeerCert, + t.clientCertificateConfig, + v.version, + v.versionStr); + storeCertOverride(port, cert); + yield startClient(port, t.sendClientCert ? cert : null, t.expectingBadCertAlert); + } + } +}); + +do_register_cleanup(function() { + prefs.clearUserPref("security.tls.version.max"); +}); |