Created
January 31, 2017 23:51
-
-
Save rhelmer/c27dffd9bbd3a5e1499ccc9f3fb2a26e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| let {classes: Cc, interfaces: Ci, utils: Cu} = Components; | |
| Cu.import("resource:///modules/experiments/Experiments.jsm"); | |
| Cu.import("resource://gre/modules/Preferences.jsm"); | |
| Cu.import("resource://gre/modules/Services.jsm"); | |
| Cu.import("resource://gre/modules/TelemetryController.jsm"); | |
| var gStarted = false; | |
| const kSELF_ID = "tls-compat-beta51@experiments.mozilla.org"; | |
| const kVERSION_MAX_PREF = "security.tls.version.max"; | |
| // These should be different hosts so that we don't bias any performance test | |
| // toward 1.2. | |
| const kURLs = [ | |
| // This must be first because we use it as a test for TLS 1.3 working | |
| "https://enabled.tls13.com/", | |
| "https://disabled.tls13.com/" | |
| ]; | |
| // These variables are unreliable for some reason. | |
| function read(obj, field) { | |
| try { | |
| return obj[field]; | |
| } catch (e) { | |
| Cu.reportError(e); | |
| } | |
| return undefined; | |
| } | |
| // This might help us work out if there was a MitM | |
| function recordSecInfo(channel, result) { | |
| let secInfo = channel.securityInfo; | |
| if (secInfo instanceof Ci.nsITransportSecurityInfo) { | |
| secInfo.QueryInterface(Ci.nsITransportSecurityInfo); | |
| const isSecure = Ci.nsIWebProgressListener.STATE_IS_SECURE; | |
| result.secure = !!(read(secInfo, 'securityState') & isSecure); | |
| result.prError = read(secInfo, 'errorCode'); | |
| } | |
| if (secInfo instanceof Ci.nsISSLStatusProvider) { | |
| let sslStatus = secInfo.QueryInterface(Ci.nsISSLStatusProvider) | |
| .SSLStatus.QueryInterface(Ci.nsISSLStatus); | |
| let cert = read(sslStatus, 'serverCert'); | |
| result.certfp = read(cert, 'sha256Fingerprint'); // A hex string | |
| result.version = read(sslStatus, 'protocolVersion'); | |
| } | |
| } | |
| function makeRequest(index, url, body) { | |
| return new Promise(resolve => { | |
| let t0 = Date.now(); | |
| let req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"] | |
| .createInstance(Ci.nsIXMLHttpRequest); | |
| req.open( | |
| body ? "POST" : "GET", url, true); | |
| req.setRequestHeader("Content-Type", "application/json"); | |
| var result = { | |
| "index" : index, | |
| "url" : url, | |
| "start_time" : t0 | |
| }; | |
| req.timeout = 10000; // 10s is low intentionally | |
| req.addEventListener("error", e => { | |
| let channel = e.target.channel; | |
| let nsireq = channel.QueryInterface(Ci.nsIRequest); | |
| result.error= nsireq ? nsireq.status : NS_ERROR_NOT_AVAILABLE; | |
| recordSecInfo(channel, result); | |
| result.elapsed = Date.now() - t0; | |
| resolve(result); | |
| }); | |
| req.addEventListener("load", e => { | |
| result.status = e.target.status; | |
| recordSecInfo(e.target.channel, result); | |
| result.elapsed = Date.now() - t0; | |
| resolve(result); | |
| }); | |
| if (body) { | |
| req.send(JSON.stringify(body)); | |
| } else { | |
| req.send(); | |
| } | |
| }); | |
| } | |
| function report(result) { | |
| console.log("Result"); | |
| console.log(result); | |
| return TelemetryController.submitExternalPing( | |
| "tls-13-study-v1", | |
| { | |
| results: result | |
| }, {}); | |
| } | |
| function disable() { | |
| Experiments.instance().disableExperiment("FROM_API"); | |
| } | |
| // Inefficient shuffle algorithm, but n <= 10 | |
| function shuffleArray(orig) { | |
| var inarr = []; | |
| for (i in orig) { | |
| inarr.push(orig[i]); | |
| } | |
| var out = []; | |
| while(inarr.length > 0) { | |
| x = Math.floor(Math.random() * inarr.length); | |
| out.push(inarr.splice(x,1)[0]) | |
| } | |
| return out; | |
| } | |
| function ensureExperimentBranch() { | |
| return new Promise(resolve => { | |
| let experiments = Experiments.instance(); | |
| // If we have already determined the branch, return the | |
| // response. | |
| let branch = experiments.getActiveExperimentBranch(); | |
| if (branch) { | |
| console.log("TLS 1.3 experiment branch already selected: " + branch); | |
| resolve(branch); | |
| return; | |
| } | |
| // OK, we need to run an experiment to determine the | |
| // branch. | |
| // Start by making it disabled. | |
| branch = "disabled"; | |
| let prefs = new Preferences({ defaultBranch: true }); | |
| prefs.set(kVERSION_MAX_PREF, 4); | |
| let todo = []; | |
| let shuffled = shuffleArray(kURLs); | |
| for (var i in shuffled) { | |
| todo.push(makeRequest(i, shuffled[i], null )); | |
| } | |
| Promise.all(todo) | |
| .then(result => { | |
| for (r in result) { | |
| // If we successfully do a TLS 1.3 fetch at all, mark it enabled. | |
| if ((result[r].url == kURLs[0]) && result[r].status === 200) { | |
| console.log("TLS 1.3 succeeded"); | |
| branch = "enabled"; | |
| } | |
| } | |
| report(result); | |
| }) | |
| .catch(e => Cu.reportError(e)) | |
| .then(_ => { | |
| prefs.set(kVERSION_MAX_PREF, 3); | |
| }) | |
| .then(_ => { | |
| let id = experiments.getActiveExperimentID(); | |
| console.log("TLS 1.3 experiment branch result was: " + branch); | |
| return experiments.setExperimentBranch(id, branch); | |
| }). | |
| then(_ => resolve(branch)); | |
| }); | |
| } | |
| // This is a simple experiment: | |
| // - Install | |
| // - Enable TLS 1.3. | |
| // - Connect to a bunch of servers and record the results | |
| // (see README.md for details on report format) | |
| // - If the TLS 1.3 connection succeeded, leave TLS 1.3 on. | |
| function startup() { | |
| // Don't do anything if the user has already messed with this | |
| // setting. | |
| let userprefs = new Preferences(); | |
| if (userprefs.isSet(kVERSION_MAX_PREF)) { | |
| console.log("User has changed TLS max version. Skipping"); | |
| experiments.setExperimentBranch(id, "skipped"); | |
| disable(); | |
| return; | |
| } | |
| // Seems startup() function is launched twice after install, we're | |
| // unsure why so far. We only want it to run once. | |
| if (gStarted) { | |
| return; | |
| } | |
| gStarted = true; | |
| ensureExperimentBranch().then(branch => { | |
| let prefs = new Preferences({defaultBranch: true}); | |
| switch (branch) { | |
| case "enabled": | |
| prefs.set(kVERSION_MAX_PREF, 4); | |
| return; | |
| case "disabled": | |
| // Do nothing. | |
| return; | |
| default: | |
| throw new Error("Unexpected experiment branch: " + branch); | |
| } | |
| }, e => { | |
| Cu.reportError("Got error during bootstrap startup: " + e); | |
| }); | |
| } | |
| function shutdown() { | |
| gStarted = false; | |
| } | |
| function install() {} | |
| function uninstall() {} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment