summaryrefslogtreecommitdiffstats
path: root/security/manager/ssl/tests/unit/test_weak_crypto.js
diff options
context:
space:
mode:
Diffstat (limited to 'security/manager/ssl/tests/unit/test_weak_crypto.js')
-rw-r--r--security/manager/ssl/tests/unit/test_weak_crypto.js273
1 files changed, 273 insertions, 0 deletions
diff --git a/security/manager/ssl/tests/unit/test_weak_crypto.js b/security/manager/ssl/tests/unit/test_weak_crypto.js
new file mode 100644
index 000000000..effedf8e3
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_weak_crypto.js
@@ -0,0 +1,273 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests the weak crypto override
+
+const TLS_RSA_WITH_RC4_128_MD5 = 0x0004;
+const TLS_RSA_WITH_RC4_128_SHA = 0x0005;
+const TLS_ECDHE_ECDSA_WITH_RC4_128_SHA = 0xC007;
+const TLS_ECDHE_RSA_WITH_RC4_128_SHA = 0xC011;
+
+// Need profile dir to store the key / cert
+do_get_profile();
+// Ensure PSM is initialized
+Cc["@mozilla.org/psm;1"].getService(Ci.nsISupports);
+
+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 weakCryptoOverride = Cc["@mozilla.org/security/weakcryptooverride;1"]
+ .getService(Ci.nsIWeakCryptoOverride);
+const socketTransportService =
+ Cc["@mozilla.org/network/socket-transport-service;1"]
+ .getService(Ci.nsISocketTransportService);
+
+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, rc4only) {
+ 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");
+
+ equal(status.tlsVersionUsed, Ci.nsITLSClientStatus.TLS_VERSION_1_2,
+ "Using TLS 1.2");
+ let expectedCipher = rc4only ? "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA"
+ : "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, rc4only ? 160 : 128, "Using MAC of expected length");
+
+ input.asyncWait({
+ onInputStreamReady: function(streamReadyInput) {
+ NetUtil.asyncCopy(streamReadyInput, output);
+ }
+ }, 0, 0, Services.tm.currentThread);
+ },
+ onStopListening: function() {}
+ };
+
+ tlsServer.setSessionCache(false);
+ tlsServer.setSessionTickets(false);
+ tlsServer.setRequestClientCertificate(Ci.nsITLSServerSocket.REQUEST_NEVER);
+ if (rc4only) {
+ let cipherSuites = [
+ TLS_ECDHE_RSA_WITH_RC4_128_SHA,
+ TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
+ TLS_RSA_WITH_RC4_128_SHA,
+ TLS_RSA_WITH_RC4_128_MD5
+ ];
+ tlsServer.setCipherSuites(cipherSuites, cipherSuites.length);
+ }
+
+ 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, expectedResult, options = {}) {
+ let transport =
+ socketTransportService.createTransport(["ssl"], 1, "127.0.0.1", port, null);
+ if (options.isPrivate) {
+ transport.connectionFlags |= Ci.nsISocketTransport.NO_PERMANENT_STORAGE;
+ }
+ let input;
+ let output;
+
+ let deferred = Promise.defer();
+
+ let handler = {
+
+ onTransportStatus: function(unused, status) {
+ if (status === Ci.nsISocketTransport.STATUS_CONNECTED_TO) {
+ output.asyncWait(handler, 0, 0, Services.tm.currentThread);
+ }
+ },
+
+ onInputStreamReady: function(streamReadyInput) {
+ try {
+ let data =
+ NetUtil.readInputStreamToString(streamReadyInput,
+ streamReadyInput.available());
+ equal(Cr.NS_OK, expectedResult, "Connection should succeed");
+ equal(data, "HELLO", "Echoed data received");
+ } catch (e) {
+ if (!((e.result == Cr.NS_ERROR_NET_RESET) && options.allowReset) &&
+ (e.result != expectedResult)) {
+ deferred.reject(e);
+ }
+ }
+ streamReadyInput.close();
+ output.close();
+ deferred.resolve();
+ },
+
+ onOutputStreamReady: function(streamReadyOutput) {
+ try {
+ try {
+ streamReadyOutput.write("HELLO", 5);
+ } catch (e) {
+ if (e.result == Cr.NS_BASE_STREAM_WOULD_BLOCK) {
+ streamReadyOutput.asyncWait(handler, 0, 0, Services.tm.currentThread);
+ return;
+ }
+ if (e.result != Cr.NS_OK) {
+ ok((e.result === expectedResult) ||
+ (options.allowReset && (e.result === Cr.NS_ERROR_NET_RESET)),
+ "Actual and expected connection result should match");
+ streamReadyOutput.close();
+ deferred.resolve();
+ return;
+ }
+ }
+ do_print("Output to server written");
+ input = transport.openInputStream(0, 0, 0);
+ input.asyncWait(handler, 0, 0, Services.tm.currentThread);
+ } catch (e) {
+ deferred.reject(e);
+ }
+ }
+
+ };
+
+ transport.setEventSink(handler, Services.tm.currentThread);
+ output = transport.openOutputStream(Ci.nsITransport.OPEN_UNBUFFERED, 0, 0);
+ output.QueryInterface(Ci.nsIAsyncOutputStream);
+
+ return deferred.promise;
+}
+
+function run_test() {
+ Services.prefs.setBoolPref("security.tls.unrestricted_rc4_fallback", false);
+ run_next_test();
+}
+
+// for sanity check
+add_task(function* () {
+ let cert = yield getCert();
+ ok(!!cert, "Got self-signed cert");
+ let port = startServer(cert, false);
+ storeCertOverride(port, cert);
+ yield startClient(port, Cr.NS_OK);
+ yield startClient(port, Cr.NS_OK, {isPrivate: true});
+});
+
+add_task(function* () {
+ let cert = yield getCert();
+ ok(!!cert, "Got self-signed cert");
+ let port = startServer(cert, true);
+ storeCertOverride(port, cert);
+ yield startClient(port, getXPCOMStatusFromNSS(SSL_ERROR_NO_CYPHER_OVERLAP));
+ yield startClient(port, getXPCOMStatusFromNSS(SSL_ERROR_NO_CYPHER_OVERLAP),
+ {isPrivate: true});
+
+ weakCryptoOverride.addWeakCryptoOverride("127.0.0.1", true);
+ // private browsing should not affect the permanent storage.
+ equal(Services.prefs.getCharPref("security.tls.insecure_fallback_hosts"),
+ "");
+ yield startClient(port, getXPCOMStatusFromNSS(SSL_ERROR_NO_CYPHER_OVERLAP));
+ // The auto-retry on connection reset is implemented in our HTTP layer.
+ // So we will see the crafted NS_ERROR_NET_RESET when we use
+ // nsISocketTransport directly.
+ yield startClient(port, getXPCOMStatusFromNSS(SSL_ERROR_NO_CYPHER_OVERLAP),
+ {isPrivate: true, allowReset: true});
+ // retry manually to simulate the HTTP layer
+ yield startClient(port, Cr.NS_OK, {isPrivate: true});
+
+ weakCryptoOverride.removeWeakCryptoOverride("127.0.0.1", port, true);
+ equal(Services.prefs.getCharPref("security.tls.insecure_fallback_hosts"),
+ "");
+ yield startClient(port, getXPCOMStatusFromNSS(SSL_ERROR_NO_CYPHER_OVERLAP));
+ yield startClient(port, getXPCOMStatusFromNSS(SSL_ERROR_NO_CYPHER_OVERLAP),
+ {isPrivate: true});
+
+ weakCryptoOverride.addWeakCryptoOverride("127.0.0.1", false, true);
+ // temporary override should not change the pref.
+ equal(Services.prefs.getCharPref("security.tls.insecure_fallback_hosts"),
+ "");
+ yield startClient(port, getXPCOMStatusFromNSS(SSL_ERROR_NO_CYPHER_OVERLAP),
+ {allowReset: true});
+ yield startClient(port, Cr.NS_OK);
+ yield startClient(port, getXPCOMStatusFromNSS(SSL_ERROR_NO_CYPHER_OVERLAP),
+ {isPrivate: true});
+
+ weakCryptoOverride.removeWeakCryptoOverride("127.0.0.1", port, false);
+ equal(Services.prefs.getCharPref("security.tls.insecure_fallback_hosts"),
+ "");
+ yield startClient(port, getXPCOMStatusFromNSS(SSL_ERROR_NO_CYPHER_OVERLAP));
+ yield startClient(port, getXPCOMStatusFromNSS(SSL_ERROR_NO_CYPHER_OVERLAP),
+ {isPrivate: true});
+
+ weakCryptoOverride.addWeakCryptoOverride("127.0.0.1", false);
+ // permanent override should change the pref.
+ equal(Services.prefs.getCharPref("security.tls.insecure_fallback_hosts"),
+ "127.0.0.1");
+ yield startClient(port, getXPCOMStatusFromNSS(SSL_ERROR_NO_CYPHER_OVERLAP),
+ {allowReset: true});
+ yield startClient(port, Cr.NS_OK);
+ yield startClient(port, getXPCOMStatusFromNSS(SSL_ERROR_NO_CYPHER_OVERLAP),
+ {isPrivate: true});
+
+ weakCryptoOverride.removeWeakCryptoOverride("127.0.0.1", port, false);
+ equal(Services.prefs.getCharPref("security.tls.insecure_fallback_hosts"),
+ "");
+ yield startClient(port, getXPCOMStatusFromNSS(SSL_ERROR_NO_CYPHER_OVERLAP));
+ yield startClient(port, getXPCOMStatusFromNSS(SSL_ERROR_NO_CYPHER_OVERLAP),
+ {isPrivate: true});
+
+ // add a host to the pref to prepare the next test
+ weakCryptoOverride.addWeakCryptoOverride("127.0.0.1", false);
+ yield startClient(port, getXPCOMStatusFromNSS(SSL_ERROR_NO_CYPHER_OVERLAP),
+ {allowReset: true});
+ yield startClient(port, Cr.NS_OK);
+ equal(Services.prefs.getCharPref("security.tls.insecure_fallback_hosts"),
+ "127.0.0.1");
+});
+
+add_task(function* () {
+ let cert = yield getCert();
+ ok(!!cert, "Got self-signed cert");
+ let port = startServer(cert, false);
+ storeCertOverride(port, cert);
+ yield startClient(port, Cr.NS_OK);
+ // Successful strong cipher will remove the host from the pref.
+ equal(Services.prefs.getCharPref("security.tls.insecure_fallback_hosts"),
+ "");
+});