diff --git a/README b/README
index f9dd6c1..67c02ed 100644
--- a/README
+++ b/README
@@ -1,23 +1,11 @@
-To create a firefox addon:
-
-1. Create a zip archive which contains the following:
-content (a directory)
-bootstrap.js
-chrome.manifest
-icon.png
-install.rdf
-README
-
-e.g. in Linux you can "Select All" then right-click and choose Compress.
-
-2. Rename the zip extension to .xpi
-
-3. Drag and drop the .xpi into Firefox
+To create a Firefox addon:
+Go to about:debugging#addons -> Load Temporary Add-on -> point to install.rdf
To create a Chrome extension:
1. go to chrome://extensions and Enable "Developer mode"
-2. Click "Load unpacked extension" and point to the parent folder
-(inside of which this README file is)
+2. Click "Load unpacked extension"
+
+3. Navigate inside "webextension" folder and click Open.
diff --git a/bootstrap.js b/bootstrap.js
index 7a71bfe..31357ca 100644
--- a/bootstrap.js
+++ b/bootstrap.js
@@ -1,234 +1,155 @@
//from https://raw.githubusercontent.com/dgutov/bmreplace/67ad019be480fc6b5d458dc886a2fb5364e92171/bootstrap.js
var bootstrapjs_exception;
-var thisaddon;
-var jsloaded = false;
-try {
+var api = null;
+var window = null;
+var setTimeout;
+var clearTimeout;
+var clearInterval;
+var setInterval;
+var connections = {}; //uid:{buffer:, socketId:} dictionary
+console.log = function(){};
-const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/AddonManager.jsm");
-
-var self = this;
-var busy = false;
-
-function include(addon, path) {
- Services.scriptloader.loadSubScript("chrome://pagesigner/content/"+path, self);
+//js native ArrayBuffer to Array of numbers
+function ab2ba(ab) {
+ var view = new DataView(ab);
+ var int_array = [];
+ for (var i = 0; i < view.byteLength; i++) {
+ int_array.push(view.getUint8(i));
+ }
+ return int_array;
}
-function $(node, childId) {
- if (node.getElementById) {
- return node.getElementById(childId);
- } else {
- return node.querySelector("#" + childId);
+
+function ba2ab(ba) {
+ var ab = new ArrayBuffer(ba.length);
+ var dv = new DataView(ab);
+ for (var i = 0; i < ba.length; i++) {
+ dv.setUint8(i, ba[i]);
+ }
+ return ab;
+}
+
+
+function Socket(name, port) {
+ this.name = name;
+ this.port = port;
+ this.sckt = null;
+ this.buffer = [];
+ this.uid = Math.random().toString(36).slice(-10);
+ this.recv_timeout = 20 * 1000;
+}
+
+Socket.prototype.constructor = Socket;
+
+Socket.prototype.connect = function() {
+ var that = this;
+ return new Promise(function(resolve, reject) {
+ that.sckt = new window.TCPSocket(that.name, that.port, {
+ binaryType: "arraybuffer"
+ });
+ that.sckt.ondata = function(event) {
+ var int_array = ab2ba(event.data)
+ console.log('ondata got bytes:', int_array.length);
+ that.buffer = [].concat(that.buffer, int_array);
+ };
+ //dont wait for connect for too long
+ var timer = setTimeout(function() {
+ reject('connect: socket timed out');
+ }, 1000 * 20)
+
+ that.sckt.onopen = function() {
+ clearInterval(timer);
+ console.log('onopen');
+ resolve('ready');
+ };
+ that.sckt.onerror = function(event) {
+ clearInterval(timer);
+ console.log('onerror', event);
+ reject(event.data);
+ };
+ });
+};
+Socket.prototype.send = function(data_in) {
+ var ab = ba2ab(data_in);
+ console.log('socket putting data on the wire');
+ this.sckt.send(ab, 0, ab.byteLength);
+};
+Socket.prototype.close = function() {
+ this.sckt.close();
+};
+
+var first_listener_connect = true;
+
+function listener(request, sender, sendResponse) {
+ console.log('got data in listener in bootstrap');
+ if (first_listener_connect) {
+ first_listener_connect = false;
+ var {
+ classes: Cc,
+ interfaces: Ci,
+ utils: Cu
+ } = Components;
+ var win = Cc['@mozilla.org/appshell/window-mediator;1'].getService(Ci.nsIWindowMediator).getMostRecentWindow('navigator:browser');
+ window = win.window;
+ setTimeout = window.setTimeout;
+ setInterval = window.setInterval;
+ clearTimeout = window.clearTimeout;
+ clearInterval = window.clearInterval;
+ }
+ if (request.command === 'connect') {
+ connections[request.uid] = {
+ socket: {}
+ };
+ var socket = new Socket(request.args.name, request.args.port);
+ connections[request.uid].socket = socket;
+ socket.connect().then(function() {
+ console.log('retval of connect');
+ sendResponse({
+ 'retval': 'success'
+ });
+ });
+ return true; //for async sendResponse
+ } else if (request.command === 'send') {
+ connections[request.uid].socket.send(request.args.data);
+ } else if (request.command === 'close') {
+ connections[request.uid].socket.close();
+ } else if (request.command === 'recv') {
+ //only send back when there is actual data to send
+ //request will become a dead Object after some time, so we copy it's uid while it is not yet dead
+ var uid = request.uid;
+ var timer = setInterval(function() {
+ if (connections[uid].socket.buffer.length > 0) {
+ clearInterval(timer);
+ var buffer = connections[uid].socket.buffer;
+ connections[request.uid].socket.buffer = [];
+ console.log('sending back', buffer);
+ sendResponse({
+ 'data': buffer
+ });
+ }
+ }, 100);
+ setTimeout(function() {
+ clearInterval(timer);
+ }, 60 * 1000);
+ return true; //for async sendResponse
}
}
-function loadNormalIcon(){
- eachWindow(unloadFromWindow);
- busy = false;
- eachWindow(loadIntoWindow);
+function startup({
+ webExtension
+}) {
+ console.log('getting webextension');
+ webExtension.startup().then(apis => {
+ api = apis;
+ api.browser.runtime.onMessage.addListener(listener);
+ });
+ console.log('got webextension');
}
-function loadBusyIcon(){
- eachWindow(unloadFromWindow);
- busy = true;
- eachWindow(loadIntoWindow);
-}
+function shutdown(data, reason) {}
+function install(data, reason) {}
-function loadIntoWindow(window) {
- if (!window) return;
-
- let doc = window.document;
- let toolbox = $(doc, "navigator-toolbox");
-
- if (toolbox) { // navigator window
- // add to palette
- var button = doc.createElement("toolbarbutton");
- button.setAttribute("id", BUTTON_ID);
- button.setAttribute("label", "PageSigner");
- button.setAttribute("type", "menu");
- button.setAttribute("class", "toolbarbutton-1 chromeclass-toolbar-additional");
- button.setAttribute("tooltiptext", "PageSigner menu");
-
- if (busy === true){
- button.style.listStyleImage = "url(" + "chrome://pagesigner/content/icon_spin.gif" + ")";
- toolbox.palette.appendChild(button);
- let mpu = doc.createElement("menupopup");
- let mi1 = doc.createElement("menuitem");
- mi1.setAttribute("label", 'Please wait. Signing webpage...');
- mpu.appendChild(mi1);
- button.appendChild(mpu);
- }
- else {
- button.style.listStyleImage = "url(" + "chrome://pagesigner/content/icon16.png" + ")";
- button.addEventListener("command", main.action, false);
- toolbox.palette.appendChild(button);
-
- let sep1 = doc.createElement("menuseparator");
- let sep2 = doc.createElement("menuseparator");
- let sep3 = doc.createElement("menuseparator");
- let mpu = doc.createElement("menupopup");
- mpu.setAttribute("id","tlsnmpu");
- let mi1 = doc.createElement("menuitem");
- mi1.setAttribute("label", 'Notarize this page');
- mi1.setAttribute("class","menuitem-with-favicon menuitem-iconic bookmark-item");
- mi1.setAttribute("image", 'chrome://pagesigner/content/icon16.png');
- mi1.addEventListener("command",main.notarize, false)
- mpu.appendChild(mi1);
- mpu.appendChild(sep1);
-
- let mi3 = doc.createElement("menuitem");
- mi3.setAttribute("label", 'Manage files');
- mi3.setAttribute("class","menuitem-with-favicon menuitem-iconic bookmark-item");
- mi3.setAttribute("image", 'chrome://pagesigner/content/manage.png');
- mi3.addEventListener("command",main.manage, false)
- mpu.appendChild(mi3);
- mpu.appendChild(sep3);
-
- let mi2 = doc.createElement("menuitem");
- mi2.setAttribute("label", 'Import .pgsg file');
- mi2.setAttribute("class","menuitem-with-favicon menuitem-iconic bookmark-item");
- mi2.setAttribute("image", 'chrome://pagesigner/content/verify.png');
- mi2.addEventListener("command",main.verify, false)
- mpu.appendChild(mi2);
- mpu.appendChild(sep2);
-
- let mi4 = doc.createElement("menuitem");
- mi4.setAttribute("label", 'About');
- mi4.setAttribute("class","menuitem-with-favicon menuitem-iconic bookmark-item");
- mi4.setAttribute("image",'chrome://pagesigner/content/icon16.png');
- mi4.addEventListener("command",main.about, false)
- mpu.appendChild(mi4);
- button.appendChild(mpu);
- }
-
-
- // move to saved toolbar position
- let {toolbarId, nextItemId} = main.getPrefs(),
- toolbar = toolbarId && $(doc, toolbarId);
- if (toolbar) {
- let nextItem = $(doc, nextItemId);
- toolbar.insertItem(BUTTON_ID, nextItem &&
- nextItem.parentNode.id == toolbarId &&
- nextItem);
- }
- window.addEventListener("aftercustomization", afterCustomize, false);
- }
-}
-
-function afterCustomize(e) {
- let toolbox = e.target;
- let button = $(toolbox.parentNode, BUTTON_ID);
- let toolbarId, nextItemId;
- if (button) {
- let parent = button.parentNode,
- nextItem = button.nextSibling;
- if (parent) {
- toolbarId = parent.id;
- nextItemId = nextItem && nextItem.id;
- }
- }
- main.setPrefs(toolbarId, nextItemId);
-}
-
-function unloadFromWindow(window) {
- if (!window) return;
- let doc = window.document;
- let button = $(doc, BUTTON_ID) ||
- $($(doc, "navigator-toolbox").palette, BUTTON_ID);
- button && button.parentNode.removeChild(button);
- window.removeEventListener("aftercustomization", afterCustomize, false);
-}
-
-function eachWindow(callback) {
- let enumerator = Services.wm.getEnumerator("navigator:browser");
- while (enumerator.hasMoreElements()) {
- if (!jsloaded){
- loadjs();
- }
- let win = enumerator.getNext();
- if (win.document.readyState === "complete") {
- callback(win);
- } else {
- runOnLoad(win, callback);
- }
- }
-}
-
-function runOnLoad (window, callback) {
- window.addEventListener("load", function() {
- if (!jsloaded){
- loadjs();
- }
- window.removeEventListener("load", arguments.callee, false);
- callback(window);
- }, false);
-}
-
-function windowWatcher (subject, topic) {
- if (topic === "domwindowopened") {
- runOnLoad(subject, loadIntoWindow);
- }
-}
-
-function startup(data, reason) AddonManager.getAddonByID(data.id, function(addon) {
- thisaddon = addon;
- // existing windows
- eachWindow(loadIntoWindow);
- // new windows
- Services.ww.registerNotification(windowWatcher);
-});
-
-//we want to load js files only after browser started, so we wait for a window object
-//to be exposed first and then loadjs get triggered
-function loadjs(){
- jsloaded = true;
- var addon = thisaddon;
- include(addon, "socket.js");
- include(addon, "firefox/button.js");
- include(addon, "tlsn_utils.js");
- include(addon, "oracles.js");
- include(addon, "CryptoJS/components/core.js");
- include(addon, "CryptoJS/components/md5.js");
- include(addon, "CryptoJS/components/evpkdf.js");
- include(addon, "CryptoJS/components/enc-base64.js");
- include(addon, "CryptoJS/components/sha1.js");
- include(addon, "CryptoJS/components/sha256.js");
- include(addon, "CryptoJS/components/hmac.js");
- include(addon, "CryptoJS/components/cipher-core.js");
- include(addon, "CryptoJS/components/aes.js");
- include(addon, "CryptoJS/components/pad-nopadding.js");
- include(addon, "firefox/firefox_specific.js");
- include(addon, "main.js");
- include(addon, "jsbn.js");
- include(addon, "jsbn2.js");
- include(addon, "pako.js");
- include(addon, "tlsn.js");
- include(addon, "notification_bar.js");
- include(addon, "testing/testing.js");
- include(addon, "testing/manager_test.js");
- include(addon, "verifychain/buffer.js2");
- include(addon, "verifychain/asn1.js2");
- include(addon, "verifychain/jsrsasign-latest-all-min.js2");
- include(addon, "verifychain/rootcertslist.js");
- include(addon, "verifychain/rootcerts.js");
- include(addon, "verifychain/verifychain.js");
- include(addon, "testdriver.js");
-}
-
-
-function shutdown(data, reason) {
- gBrowser.removeProgressListener(myListener);
- Services.obs.removeObserver(httpRequestBlocker, "http-on-modify-request");
- Services.ww.unregisterNotification(windowWatcher);
- eachWindow(unloadFromWindow);
-}
-function install(data,reason) {}
-function uninstall(data,reason) {}
-
-} catch (e){
- bootstrapjs_exception = e;
-}
+function uninstall(data, reason) {}
diff --git a/chrome.manifest b/chrome.manifest
deleted file mode 100644
index 39a31a6..0000000
--- a/chrome.manifest
+++ /dev/null
@@ -1,2 +0,0 @@
-content pagesigner content/
-
diff --git a/content/chrome/about.html b/content/chrome/about.html
deleted file mode 100644
index 6cd10bf..0000000
--- a/content/chrome/about.html
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
- About PageSigner.
-
-
-
-
- PageSigner
-
-
-
- Using TLSNotary technology.
-
-
- For help and information, go to
-
- the PageSigner FAQ
-
-
-
- Email the developers:
-
- tlsnotarygroup@gmail.com
-
-
- Donations welcome:
- 367SYUMqo1Fi4tQsycnmCtB6Ces1Z7EZLH
-
-
-
-
-
-
diff --git a/content/chrome/about.js b/content/chrome/about.js
deleted file mode 100644
index c758ff4..0000000
--- a/content/chrome/about.js
+++ /dev/null
@@ -1,21 +0,0 @@
-
-var link1 = document.getElementById("link1");
-link1.addEventListener("click", function(evt){
- chrome.runtime.sendMessage({'destination':'extension',
- 'message':'openLink1'});
- window.close();
-});
-
-var link2 = document.getElementById("link2");
-link2.addEventListener("click", function(evt){
- chrome.runtime.sendMessage({'destination':'extension',
- 'message':'openLink2'});
- window.close();
-});
-
-var link3 = document.getElementById("link3");
-link3.addEventListener("click", function(evt){
- chrome.runtime.sendMessage({'destination':'extension',
- 'message':'openLink3'});
- window.close();
-});
diff --git a/content/chrome/chrome_specific.js b/content/chrome/chrome_specific.js
deleted file mode 100644
index 0cbf59f..0000000
--- a/content/chrome/chrome_specific.js
+++ /dev/null
@@ -1,702 +0,0 @@
-var testing = false;
-var tabs = {};
-var appId = "oclohfdjoojomkfddjclanpogcnjhemd"; //id of the helper app
-var is_chrome = true;
-var fsRootPath; //path to local storage root, e.g. filesystem:chrome-extension://abcdabcd/persistent
-var manager_path; //manager.html which was copied into Downloads/ dir
-var notarization_in_progress = false;
-
-function getPref(pref, type){
- return new Promise(function(resolve, reject) {
- chrome.storage.local.get(pref, function(obj){
- if (Object.keys(obj).length === 0){
- resolve('undefined');
- return;
- }
- else {
- resolve(obj[pref]);
- }
- });
- });
-}
-
-
-function setPref(pref, type, value){
- return new Promise(function(resolve, reject) {
- var obj = {};
- obj[pref] = value;
- chrome.storage.local.set(obj, function(){
- resolve();
- });
- });
-}
-
-
-function import_reliable_sites(){
- import_resource('pubkeys.txt')
- .then(function(text_ba){
- parse_reliable_sites(ba2str(text_ba));
- });
-}
-
-//we can import chrome:// and file:// URL
-function import_resource(filename, isFileURI){
- if (typeof(isFileURI) === 'undefined'){
- isFileURI = false;
- }
- return new Promise(function(resolve, reject) {
- var xhr = new XMLHttpRequest();
- xhr.responseType = "arraybuffer";
- xhr.onreadystatechange = function(){
- if (xhr.readyState != 4)
- return;
-
- if (xhr.response) {
- resolve(ab2ba(xhr.response));
- }
- };
- var path = isFileURI ? filename : chrome.extension.getURL('content/'+toFilePath(filename));
- xhr.open('get', path, true);
- xhr.send();
- });
-}
-
-
-//converts an array of file names into a string with correct slashes
-function toFilePath(pathArray){
- if (typeof(pathArray) === 'string') return pathArray;
- var expanded = '';
- for(var i=0; i < pathArray.length; i++){
- expanded += pathArray[i];
- //not trailing slash for last element
- if (i < (pathArray.length-1) ){
- expanded += '/';
- }
- }
- return expanded;
-}
-
-
-function startListening(){
- console.log('tab listener started')
- chrome.webRequest.onSendHeaders.addListener(
- function(details) {
- console.log('in tab listener', details);
- if (details.type === "main_frame"){
- tabs[details.tabId] = details;
- }
- },
- {urls: [""]},
- ["requestHeaders"]);
-}
-
-
-function getHeaders(){
- return new Promise(function(resolve, reject) {
- chrome.tabs.query({active: true}, function(t){
- if (! t[0].url.startsWith('https://')){
- reject('You can only notarize pages which start with https://');
- return;
- }
- if (!tabs.hasOwnProperty(t[0].id)) {
- reject('Please refresh the page and then try to notarize it again');
- return;
- }
- var tab = tabs[t[0].id];
- var x = tab.url.split('/');
- var host = x[2].split(':')[0];
- x.splice(0,3);
- var resource_url = x.join('/');
- var headers = tab.method + " /" + resource_url + " HTTP/1.1" + "\r\n";
- headers += "Host: " + host + "\r\n";
- for (var i = 0; i < tab.requestHeaders.length; i++){
- var h = tab.requestHeaders[i];
- headers += h.name + ": " + h.value + "\r\n";
- }
- if (tab.method == "GET"){
- headers += "\r\n";
- }
- var port = 443;
- if (tab.url.split(':').length === 3){
- //the port is explicitely provided in URL
- port = parseInt(tab.url.split(':')[2].split('/')[0]);
- }
- resolve({'headers':headers, 'server':host, 'port':port});
- });
- });
-}
-
-
-function loadBusyIcon(){
- chrome.browserAction.setIcon({path:"content/icon_spin.gif"});
- notarization_in_progress = true;
-}
-
-
-function loadNormalIcon(){
- chrome.browserAction.setIcon({path:"icon.png"});
- notarization_in_progress = false;
-}
-
-
-function browser_specific_init(){
- window.webkitRequestFileSystem(window.PERSISTENT, 50*1024*1024, function(fs){
- fsRootPath = fs.root.toURL();
- });
- getPref('valid_hashes')
- .then(function(hashes){
- if (hashes !== 'undefined'){
- valid_hashes = hashes;
- }
- });
- chrome.runtime.getPlatformInfo(function(p){
- if(p.os === "win"){
- os_win = true;
- }
- });
- //put icon into downloads dir. This is the icon for injected notification
- //put manager files also there so we could inject code to get the manager's DOM when testing
- //(Chrome forbids injecting into chrome-extension://* URIs but allows into file://* URIs)
- chrome.downloads.setShelfEnabled(false);
- var files_to_copy = ['icon16.png', 'manager.css', 'manager.html', 'manager.js2', 'sweetalert.css', 'sweetalert.min.js2', 'check.png', 'cross.png'];
- var copied_so_far = 0;
- for (var i=0; i < files_to_copy.length; i++){
- chrome.downloads.download(
- {url:chrome.extension.getURL('content/' + files_to_copy[i]),
- conflictAction:'overwrite',
- filename:'pagesigner.tmp.dir/' + files_to_copy[i]},
- function(downloadID){
-
- var erase_when_download_completed = function(id){
- chrome.downloads.search({id:id}, function(item){
- if (item[0].state !== 'complete'){
- setTimeout(function(){
- erase_when_download_completed(id)
- }, 100);
- }
- else {
- if (item[0].filename.endsWith('manager.html')){
- var path = item[0].filename;
- if (os_win){
- path = '/' + encodeURI(path.replace(/\\/g, '/'));
- }
- manager_path = 'file://' + path;
- }
- //dont litter the Downloads menu
- chrome.downloads.erase({id:downloadID});
- copied_so_far++;
- if (copied_so_far === files_to_copy.length){
- chrome.downloads.setShelfEnabled(true);
- }
- }
- });
-
- }
- erase_when_download_completed(downloadID);
- });
- }
-
- chrome.runtime.onMessage.addListener(function(data){
- if (data.destination !== 'extension') return;
- console.log('ext got msg', data);
- if (data.message === 'rename'){
- renamePGSG(data.args.dir, data.args.newname);
- }
- else if (data.message === 'delete'){
- deletePGSG(data.args.dir);
- }
- else if (data.message === 'import'){
- verify_tlsn_and_show_data(data.args.data, true);
- }
- else if (data.message === 'export'){
- if (testing){
- chrome.downloads.download({url:fsRootPath+data.args.dir+'/pgsg.pgsg',
- 'saveAs':false, filename:'pagesigner.tmp.dir/' + data.args.file+'.pgsg'});
- }
- else {
- chrome.downloads.download({url:fsRootPath+data.args.dir+'/pgsg.pgsg',
- 'saveAs':true, filename:data.args.file+'.pgsg'});
- }
- }
- else if (data.message === 'notarize'){
- startNotarizing();
- }
- else if (data.message === 'manage'){
- openManager();
- }
- else if (data.message === 'refresh'){
- populateTable();
- }
- else if (data.message === 'openLink1'){
- chrome.tabs.create({url:'https://www.tlsnotary.org'});
- }
- else if (data.message === 'openLink2'){
- chrome.tabs.create({url:'https://www.tlsnotary.org/pagesigner/faq'});
- }
- else if (data.message === 'openLink3'){
- chrome.tabs.create({url:'bitcoin:35q65MQPVSi9TYMKxNYmpSyhWj7FkzTjzQ'});
- }
- else if (data.message === 'viewdata'){
- openTabs(fsRootPath+data.args.dir);
- }
- else if (data.message === 'viewraw'){
- chrome.tabs.create({url:fsRootPath+data.args.dir+'/raw.txt'});
- }
- else if (data.message === 'openInstallLink'){
- chrome.tabs.create({url:'https://chrome.google.com/webstore/detail/pagesigner-helper-app/oclohfdjoojomkfddjclanpogcnjhemd'});
- }
- else if (data.message === 'openChromeExtensions'){
- chrome.tabs.query({url:'chrome://extensions/*'}, function(tabs){
- if (tabs.length === 0){
- chrome.tabs.create({url:'chrome://extensions'});
- return;
- }
- chrome.tabs.update(tabs[0].id, {active:true});
- });
- }
- else if (data.message === 'popup active'){
- if (notarization_in_progress){
- chrome.runtime.sendMessage({'destination':'popup',
- 'message':'notarization_in_progress'});
- return;
- }
- chrome.extension.isAllowedFileSchemeAccess(function(bool){
- if (bool === false){
- chrome.runtime.sendMessage({'destination':'popup',
- 'message':'file_access_disabled'});
- return;
- }
- chrome.management.get(appId, function(info){
- if (! info){
- chrome.runtime.sendMessage({'destination':'popup',
- 'message':'app_not_installed'});
- return;
- }
- if (info.enabled === false){
- chrome.runtime.sendMessage({'destination':'popup',
- 'message':'app_disabled'});
- return;
- }
- chrome.runtime.sendMessage({'destination':'popup',
- 'message':'show_menu'});
- });
- });
- }
- });
-
- init();
-}
-
-
-
-
-
-function makeSessionDir(server, is_imported){
- return new Promise(function(resolve, reject) {
-
- if (typeof(is_imported) === "undefined"){
- is_imported = false;
- }
- var time = getTime();
- var imported_str = "";
- if (is_imported){
- imported_str = "-IMPORTED";
- }
- var server_sanitized = server;
- if (server.search(/\*/) > -1){
- var parts = server.split('.');
- server_sanitized = parts[parts.length-2]+'.'+parts[parts.length-1];
- }
- var name = time+'-'+server_sanitized+imported_str;
- window.webkitRequestFileSystem(window.PERSISTENT, 5*1024*1024, function(fs){
- fs.root.getDirectory(name, {create: true}, function (dir){
- resolve(dir.toURL());
- });
- });
- });
-}
-
-
-//we remove the file and re-create it because if we simply update the file
-//the dir's modification time won't change
-function removeFile(dirName, fileName){
- return new Promise(function(resolve, reject) {
- window.webkitRequestFileSystem(window.PERSISTENT, 5*1024*1024, function(fs){
- fs.root.getDirectory(dirName, {}, function (dir){
- dir.getFile(fileName, {}, function (f){
- f.remove(function(){
- resolve();
- });
- });
- });
- });
- });
-}
-
-
-function writeFile(dirName, fileName, data, is_update){
- if (data.length === 0) return;
- if(typeof(is_update) === "undefined"){
- is_update = false;
- }
- var remove_promise = Promise.resolve();
- if (is_update) remove_promise = removeFile(dirName, fileName);
- return remove_promise
- .then(function(){
- return new Promise(function(resolve, reject) {
- window.webkitRequestFileSystem(window.PERSISTENT, 5*1024*1024, function(fs){
- fs.root.getDirectory(dirName, {}, function (dir){
- dir.getFile(fileName, {create:true, exclusive:true}, function (f){
- f.createWriter(function(fw) {
- fw.onwriteend = function() {
- resolve();
- };
- fw.write(new Blob([ba2ua(data)]));
- });
- });
- });
- });
- });
- });
-}
-
-
-function openTabs(sdir){
- //Because Chrome tabs crash when opening html from filesystem:chrome-extension:// URI
- //we first download the file to /Downloads and then open
- var uid = Math.random().toString(36).slice(-10);
- chrome.downloads.setShelfEnabled(false);
- setTimeout(function(){chrome.downloads.setShelfEnabled(true);}, 1000);
- var dirname = sdir.split('/').pop();
- var commonName;
- getFileContent(dirname, "metaDomainName")
- .then(function(data_ba){
- commonName = ba2str(data_ba);
- return getFileContent(dirname, "metaDataFilename");
- })
- .then(function(data){
- var name = ba2str(data);
- chrome.downloads.download({url:sdir + '/' + name, filename:'pagesigner.tmp.dir/'+uid+name},
- function(id){
- chrome.downloads.onChanged.addListener(function downloadCompleted(delta){
- if (delta.id != id) return;
- if (typeof(delta.state) === "undefined") return;
- if (delta.state.current !== 'complete') return;
- chrome.downloads.onChanged.removeListener(downloadCompleted);
- onComplete(id); //download completed
- });
-
- var onComplete = function(id){
- chrome.downloads.search({id:id}, function(items){
- var item = items[0];
- var path = 'file://' + item.filename;
- if (os_win) path = 'file:///'+fixWinPath(item.filename);
- chrome.tabs.query({url: 'chrome-extension://*/content/chrome/file_picker.html'},
- function(t){
- //we want to find the file import tab and reuse it
- if (t.length === 1){
- chrome.tabs.update(t[0].id, {url:path}, function(t){
- chrome.tabs.onUpdated.addListener(function tabUpdated(tabId, info, tab){
- if (tabId != t.id) return;
- //dont wait for tab to load, reload immediately
- chrome.tabs.onUpdated.removeListener(tabUpdated);
- block_and_reload(t.id, path);
- })
- });
- }
- //otherwise we are not importing - just open a new tab
- else {
- chrome.tabs.create({url:path}, function(t){
- block_and_reload(t.id, path);
- });
- }
- });
- });
- };
-
- var block_and_reload = function(id, path){
- //Blocking listener means it will process each request serially, not in parallel.
- //There is a Chrome bug that when the listener is not blocking
- //and a flood of requests happen, some of those requests don't end up in the
- //listener and thus are allowed to go through.
- chrome.webRequest.onBeforeRequest.addListener(function(x){
- if (x.url.startsWith('file://')) return; //dont block the actual file we are opening
- console.log('blocking', x.url);
- return {cancel:true};
- }, {tabId:id, urls: [""]}, ["blocking"]);
- chrome.tabs.reload(id, {bypassCache:true}, function(){
- //this callback triggers too early sometimes. Wait to make sure page reloaded
- setTimeout(function(){
- chrome.tabs.executeScript(id, {file: 'content/notification_bar.js'}, function(a){
- chrome.tabs.executeScript(id, {code:
- 'viewTabDocument = document;' +
- 'install_bar();' +
- 'document.getElementById("domainName").textContent="' + commonName.toString() + '";' +
- 'document["pagesigner-session-dir"]="' + dirname.toString() + '";'});
- });
- }, 500);
- });
- };
- });
- });
-}
-
-
-function Socket(name, port){
- this.name = name;
- this.port = port;
- this.uid = Math.random().toString(36).slice(-10);
- this.buffer = [];
- this.recv_timeout = 20*1000;
-}
-//inherit the base class
-Socket.prototype = Object.create(AbstractSocket.prototype);
-Socket.prototype.constructor = Socket;
-
-Socket.prototype.connect = function(){
- var that = this;
- return new Promise(function(resolve, reject) {
- chrome.runtime.sendMessage(appId,
- {'command':'connect',
- 'args':{'name':that.name, 'port':that.port},
- 'uid':that.uid},
- function(response){
- console.log('in connect response', response);
- clearInterval(timer);
- if (response.retval === 'success'){
- //endless data fetching loop for the lifetime of this Socket
- var fetch = function(){
- chrome.runtime.sendMessage(appId, {'command':'recv', 'uid':that.uid}, function(response){
- console.log('fetched some data', response.data.length, that.uid);
- that.buffer = [].concat(that.buffer, response.data);
- setTimeout(function(){fetch()}, 100);
- });
- };
- fetch();
- resolve('ready');
- }
- reject(response.retval);
- });
- //dont wait for connect for too long
- var timer = setTimeout(function(){
- reject('connect: socket timed out');
- }, 1000*20);
- });
-};
-Socket.prototype.send = function(data_in){
- chrome.runtime.sendMessage(appId,
- {'command':'send',
- 'args':{'data':data_in},
- 'uid':this.uid});
-};
-Socket.prototype.close = function(){
- console.log('closing socket', this.uid);
- chrome.runtime.sendMessage(appId,
- {'command':'close', 'uid':this.uid});
-};
-
-
-function get_xhr(){
- return new XMLHttpRequest();
-}
-
-function openManager(){
- //re-focus tab if manager already open
- chrome.tabs.query({}, function(tabs){
- for(var i=0; i < tabs.length; i++){
- if (tabs[i].url === manager_path){
- chrome.tabs.update(tabs[i].id, {active:true});
- return;
- }
- }
- chrome.tabs.create({url:manager_path});
- });
-}
-
-
-function deletePGSG(dir){
- window.webkitRequestFileSystem(window.PERSISTENT, 50*1024*1024, function(fs){
- fs.root.getDirectory(dir, {}, function(dirEntry){
- dirEntry.removeRecursively(function() {
- console.log('Directory removed.');
- populateTable();
- });
- });
- });
-}
-
-
-function renamePGSG(dir, newname){
- writeFile(dir, 'meta', str2ba(newname), true)
- .then(function(){
- populateTable();
- });
-}
-
-
-function sendMessage(data){
- //get the manager tab and inject the data into it
- chrome.tabs.query({}, function(tabs){
- for(var i=0; i < tabs.length; i++){
- if (tabs[i].url === manager_path){
- var jsonstring = JSON.stringify(data);
- chrome.tabs.executeScript(tabs[i].id, {code:
- 'var idiv = document.getElementById("extension2manager");' +
- //seems like chrome unstringifies jsonstring, so we stringify it again
- 'var json = JSON.stringify(' + jsonstring + ');' +
- 'console.log("json is", json);' +
- 'idiv.textContent = json;'+
- 'idiv.click();'
- });
- }
- }
- });
-}
-
-
-function updateCache(hash){
- if (!(hash.toString() in valid_hashes)){
- valid_hashes.push(hash.toString());
- chrome.storage.local.set({'valid_hashes':valid_hashes});
- }
-}
-
-
-function getModTime(obj){
- return new Promise(function(resolve, reject) {
- obj.getMetadata(function(m){
- var t = m.modificationTime.getTime();
- resolve(t);
- });
- });
-}
-
-
-function isDirectory(obj){
- return obj.isDirectory;
-}
-
-function getName(obj){
- return obj.name;
-}
-
-
-function getFullPath(obj){
- return obj.toURL();
-}
-
-
-function getDirEntry(dirName){
- return new Promise(function(resolve, reject) {
- return new Promise(function(resolve, reject) {
- window.webkitRequestFileSystem(window.PERSISTENT, 50*1024*1024, function(fs){
- resolve(fs.root);
- })
- })
- .then(function(rootDirEntry){
- rootDirEntry.getDirectory(dirName, {}, function(dirEntry){
- resolve(dirEntry);
- }, function(what){
- reject(what);
- });
- });
- });
-}
-
-
-
-
-
-
-function getDirContents(dirName){
- return new Promise(function(resolve, reject) {
- return new Promise(function(resolve, reject) {
- window.webkitRequestFileSystem(window.PERSISTENT, 50*1024*1024, function(fs){
- resolve(fs.root);
- })
- })
- .then(function(rootDirEntry){
- return new Promise(function(resolve, reject) {
- rootDirEntry.getDirectory(dirName, {}, function(dirEntry){
- return resolve(dirEntry);
- });
- });
- })
- .then(function(dirEntry){
- var dirReader = dirEntry.createReader();
- var entries = [];
-
- var readEntries = function() {
- dirReader.readEntries (function(results) {
- if (results.length) {
- //extend entries array
- entries.push.apply(entries, results);
- readEntries();
- return;
- }
- //else finished reading
- resolve(entries);
- });
- };
- readEntries();
- });
- });
-}
-
-
-function getFileContent(dirname, filename){
- return new Promise(function(resolve, reject) {
- var handleError = function(e){
- reject(e);
- };
-
- window.webkitRequestFileSystem(window.PERSISTENT, 5*1024*1024, function(fs){
- fs.root.getDirectory(dirname, {}, function (dirEntry){
- dirEntry.getFile(filename, {}, function(fileEntry){
- fileEntry.file(function(file){
- var reader = new FileReader();
- reader.onloadend = function(e) {
- resolve(ab2ba(this.result));
- };
- reader.readAsArrayBuffer(file);
- });
- }, handleError);
- }, handleError);
- });
- });
-}
-
-
-function sendAlert(alertData){
- chrome.tabs.query({active: true}, function(tabs) {
- if (!tabs[0].url.startsWith("http")){
- //we cannot inject out alert into not http & https URLs, use the ugly alert
- alert(alertData.title + ' ' + alertData.text);
- return;
- }
- chrome.tabs.executeScript(tabs[0].id, {file:"content/sweetalert.min.js2"}, function(){
- chrome.tabs.insertCSS(tabs[0].id, {file:"content/sweetalert.css"}, function(){
- chrome.tabs.executeScript(tabs[0].id, {code:"swal("+ JSON.stringify(alertData) +")"});
- });
- });
-
-
- //chrome.tabs.sendMessage(tabs[0].id, {destination:'sweetalert', args:alertData})
- });
-}
-
-
-//Used only for testing - empty the filesystem
-function emptyRootDir(){
- window.webkitRequestFileSystem(window.PERSISTENT, 5*1024*1024, function(fs){
- var r = fs.root.createReader();
- r.readEntries(function(results){
- for(var i=0; i < results.length; i++){
- if (!results[i].isDirectory) continue;
- results[i].removeRecursively(function(){});
- }
- });
- });
-}
-
-
diff --git a/content/chrome/file_picker.html b/content/chrome/file_picker.html
deleted file mode 100644
index a52e7bc..0000000
--- a/content/chrome/file_picker.html
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
diff --git a/content/chrome/file_picker.js b/content/chrome/file_picker.js
deleted file mode 100644
index fc82d29..0000000
--- a/content/chrome/file_picker.js
+++ /dev/null
@@ -1,22 +0,0 @@
-//Because file picker doesn't work from popup.html we open a new tab just for this purpose
-
-var fileChooser = document.getElementById("import");
-
-fileChooser.addEventListener('change', function (evt) {
- var f = evt.target.files[0];
- if(f) {
- var reader = new FileReader();
- reader.onload = function(e) {
- var contents = e.target.result;
- var view = new DataView(contents);
- var int_array = [];
- for(var i=0; i < view.byteLength; i++){
- int_array.push(view.getUint8(i));
- }
- chrome.runtime.sendMessage({'destination':'extension',
- 'message':'import',
- 'args':{'data':int_array}});
- }
- reader.readAsArrayBuffer(f);
- }
-});
diff --git a/content/chrome/inject_into_manager.js b/content/chrome/inject_into_manager.js
deleted file mode 100644
index d91a7c0..0000000
--- a/content/chrome/inject_into_manager.js
+++ /dev/null
@@ -1,9 +0,0 @@
-console.log("injecting some code");
-document.addEventListener("hello", function(evt) {
- var data = evt.detail;
- console.log("got hello with", data);
- chrome.runtime.sendMessage(data);
-});
-
-//this element is accessible by content scripts and by page javascript
-document.getElementById('content_script_injected_into_page').textContent = 'true';
diff --git a/content/chrome/popup.html b/content/chrome/popup.html
deleted file mode 100644
index f76effa..0000000
--- a/content/chrome/popup.html
+++ /dev/null
@@ -1,74 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-Click here to install the helper app needed for PageSigner to work
-
-Click here and then enable PageSigner helper app
-
-
-
-Notarization in progress. Please wait...
-
-
-
-
diff --git a/content/chrome/popup.js b/content/chrome/popup.js
deleted file mode 100644
index 5739775..0000000
--- a/content/chrome/popup.js
+++ /dev/null
@@ -1,89 +0,0 @@
-chrome.runtime.sendMessage({'destination':'extension',
- 'message':'popup active'});
-
-chrome.runtime.onMessage.addListener(function(data){
- if (data.destination !== 'popup') return;
- if (data.message === 'file_access_disabled'){
- document.getElementById("enable_file_access").removeAttribute('hidden');
- document.body.style.width = '100%';
- }
- else if (data.message === 'app_not_installed'){
- document.getElementById("app_not_installed").removeAttribute('hidden');
- }
- else if (data.message === 'app_disabled'){
- document.getElementById("app_disabled").removeAttribute('hidden');
- }
- else if (data.message === 'show_menu'){
- document.getElementById("menu").removeAttribute('hidden');
- }
- else if (data.message === 'notarization_in_progress'){
- document.getElementById("notarization_in_progress").removeAttribute('hidden');
- }
- else {
- console.log('popup received unexpected message ' + data.message);
- }
-});
-
-
-document.getElementById("notarize").addEventListener("click",
- function(){
- window.close();
- chrome.runtime.sendMessage({'destination':'extension',
- 'message':'notarize'});
- });
-
-document.getElementById("manage").addEventListener("click",
- function(){
- window.close();
- chrome.runtime.sendMessage({'destination':'extension',
- 'message':'manage'});
- });
-
-document.getElementById("import").addEventListener("click",
- function(){
- window.close();
- var url = chrome.extension.getURL('content/chrome/file_picker.html');
- chrome.tabs.create({url:url}, function(t){
- console.log('tabid is', t.id);
- });
- });
-
-document.getElementById("about").addEventListener("click",
- function(){
- chrome.windows.create({url:'content/chrome/about.html',
- type:'detached_panel',
- width:300,
- height:300,
- left:500})
- window.close();
- });
-
-var app_not_installed = document.getElementById("app_not_installed");
-app_not_installed.addEventListener("click", function(evt){
- chrome.runtime.sendMessage({'destination':'extension',
- 'message':'openInstallLink'});
- window.close();
-});
-
-var app_disabled = document.getElementById("app_disabled");
-app_disabled.addEventListener("click", function(evt){
- chrome.runtime.sendMessage({'destination':'extension',
- 'message':'openChromeExtensions'});
- window.close();
-});
-
-var enable_file_access = document.getElementById("enable_file_access");
-enable_file_access.addEventListener("click", function(evt){
- chrome.runtime.sendMessage({'destination':'extension',
- 'message':'openChromeExtensions'});
- window.close();
-});
-
-
-//if this file is opened in a tab during testing, it will have a hash appended to the URL
-setTimeout(function(){
- var hash = window.location.hash;
- if (hash === "#manage"){
- document.getElementById('manage').click();
- }
-}, 100);
diff --git a/content/enable_file_access.png b/content/enable_file_access.png
deleted file mode 100644
index 42476c8..0000000
Binary files a/content/enable_file_access.png and /dev/null differ
diff --git a/content/firefox/about.xul b/content/firefox/about.xul
deleted file mode 100644
index acd4697..0000000
--- a/content/firefox/about.xul
+++ /dev/null
@@ -1,41 +0,0 @@
-
-
-
-
-
-
diff --git a/content/firefox/button.js b/content/firefox/button.js
deleted file mode 100644
index 2a8fcb4..0000000
--- a/content/firefox/button.js
+++ /dev/null
@@ -1,78 +0,0 @@
-//from https://raw.githubusercontent.com/dgutov/bmreplace/67ad019be480fc6b5d458dc886a2fb5364e92171/content/main.js
-
-"use strict";
-
-var prompts = Services.prompt;
-var prefs = Services.prefs;
-var testing_import_path; //used only in testing
-
-var NS_XUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
- PREFS_BRANCH = Services.prefs.getBranch("extensions.pagesigner.button-position."),
- PREF_TB = "nav-bar",
- PREF_NEXT = "next-item",
- BUTTON_ID = "pagesigner-button";
-
-var main = {
- notarize: function() {
- startNotarizing();
- },
- verify: function() {
- function importPath(path){
- OS.File.read(path)
- .then(function(imported_data){
- var data_ba = ua2ba(imported_data);
- verify_tlsn_and_show_data(data_ba, true);
- populateTable(); //TODO, why again? v_t_a_s_d already does that
- });
- };
-
- if (testing){
- importPath(testing_import_path);
- return;
- }
-
- const nsIFilePicker = Components.interfaces.nsIFilePicker;
- var fp = Components.classes["@mozilla.org/filepicker;1"]
- .createInstance(nsIFilePicker);
- fp.init(window, "Select the .pgsg file you want to import and verify", nsIFilePicker.modeOpen);
- var rv = fp.show();
- if (rv == nsIFilePicker.returnOK || rv == nsIFilePicker.returnReplace) {
- importPath(fp.file.path);
- }
- },
- manage: function() {
- openManager();
- },
-
- about: function() {
- showAboutInfo();
- },
-
- /*
- * @return {toolbarId, nextItemId}
- */
- getPrefs: function() {
- try {
- var tb = PREFS_BRANCH.getCharPref(PREF_TB);
- var next = PREFS_BRANCH.getCharPref(PREF_NEXT);
- if (tb === "" || next === ""){
- throw ('use default');
- }
- return {
- toolbarId: tb,
- nextItemId: next
- };
- } catch(e) {
- return { // default position
- toolbarId: "nav-bar",
- nextItemId: "bookmarks-menu-button-container"
- };
- }
- },
-
- setPrefs: function(toolbarId, nextItemId) {
- PREFS_BRANCH.setCharPref(PREF_TB, toolbarId || "");
- PREFS_BRANCH.setCharPref(PREF_NEXT, nextItemId || "");
- }
-};
-
diff --git a/content/firefox/firefox_specific.js b/content/firefox/firefox_specific.js
deleted file mode 100644
index 74ea335..0000000
--- a/content/firefox/firefox_specific.js
+++ /dev/null
@@ -1,837 +0,0 @@
-var {classes: Cc, interfaces: Ci, utils: Cu} = Components;
-Cu.import("resource://gre/modules/PopupNotifications.jsm");
-Cu.import('resource://gre/modules/Services.jsm');
-Cu.import("resource://gre/modules/osfile.jsm");
-var dict_of_status = {};
-var dict_of_httpchannels = {};
-var win = Cc['@mozilla.org/appshell/window-mediator;1']
- .getService(Ci.nsIWindowMediator).getMostRecentWindow('navigator:browser');
-var gBrowser = win.gBrowser;
-var block_urls = []; //an array of urls (filesystem paths) for which all http requests must be blocked
-//navigator must be exposed for jsbn.js
-var navigator = win.navigator;
-var setTimeout = win.setTimeout;
-var clearTimeout = win.clearTimeout;
-var setInterval = win.setInterval;
-var clearInterval = win.clearInterval;
-var alert = win.alert;
-var btoa = win.btoa;
-var atob = win.atob;
-var JSON = win.JSON;
-var is_chrome = false;
-var fsRootPath; //path to pagesigner folder in FF profile dir
-var manager_path; //manager.html which was copied into profile's pagesigner dir
-
-function getPref(prefname, type){
- return new Promise(function(resolve, reject) {
- var branch = Services.prefs.getBranch("extensions.pagesigner.");
- if (branch.prefHasUserValue(prefname)){
- if (type === 'bool'){
- resolve(branch.getBoolPref(prefname));
- }
- else if (type === 'string'){
- resolve(branch.getCharPref(prefname));
- }
- }
- resolve('not found');
- });
-}
-
-
-function setPref(prefname, type, value){
- return new Promise(function(resolve, reject) {
- var branch = Services.prefs.getBranch("extensions.pagesigner.");
- if (type === 'bool'){
- branch.setBoolPref(prefname, value);
- }
- else if (type === 'string'){
- branch.setCharPref(prefname, value);
- }
- resolve();
- });
-}
-
-
-function import_reliable_sites(){
- import_resource('pubkeys.txt')
- .then(function(ba) {
- parse_reliable_sites(ba2str(ba));
- });
-}
-
-
-//converts an array of file names into a string with correct slashes
-function toFilePath(pathArray){
- if (typeof(pathArray) === 'string') return pathArray;
- var expanded = '';
- for(var i=0; i < pathArray.length; i++){
- expanded = OS.Path.join(pathArray, dirName[i]);
- }
- return expanded;
-}
-
-
-//reads from addon content folder but also can read an arbitrary file://
-function import_resource(filename, isFileURI){
- if (typeof(isFileURI) === 'undefined'){
- isFileURI = false;
- }
- return new Promise(function(resolve, reject) {
- var path = 'content';
- if (typeof(filename) === 'string'){
- path += '/'+filename;
- }
- else {
- for (var i=0; i < filename.length; i++){
- path += '/'+filename[i];
- }
- }
-
- path = isFileURI ? filename : thisaddon.getResourceURI(path).spec;
- var xhr = get_xhr();
- xhr.responseType = "arraybuffer";
- xhr.onreadystatechange = function(){
- if (xhr.readyState != 4)
- return;
-
- if (xhr.response) {
- resolve(ab2ba(xhr.response));
- }
- };
- xhr.open('get', path, true);
- xhr.send();
-
- /*
- OS.File.read(OS.Path.fromFileURI(thisaddon.getResourceURI(path).spec))
- .then(function onSuccess(ba) {
- //returns Uint8Array which is compatible with our internal byte array
- resolve(ba);
- });
- */
- });
-}
-
-
-function startListening(){
-//from now on, we will check the security status of all loaded tabs
-//and store the security status in a lookup table indexed by the url.
- gBrowser.addProgressListener(myListener);
- Services.obs.addObserver(httpRequestBlocker, "http-on-modify-request", false);
-}
-
-
-function getHeaders(){
- return new Promise(function(resolve, reject) {
-
- var audited_browser = gBrowser.selectedBrowser;
- var tab_url_full = audited_browser.contentWindow.location.href;
-
- //remove hashes - they are not URLs but are used for internal page mark-up
- var sanitized_url = tab_url_full.split("#")[0];
-
- if (!sanitized_url.startsWith("https://")){
- reject('"ERROR You can only notarize pages which start with https://');
- return;
- }
- //XXX this check is not needed anymore
- if (dict_of_status[sanitized_url] != "secure"){
- reject("Please refresh the page and then try to notarize it again");
- return;
- }
-
- //passed tests, secure, grab headers, update status bar and start audit:
- var x = sanitized_url.split('/');
- x.splice(0,3);
- var resource_url = x.join('/');
-
- var httpChannel = dict_of_httpchannels[sanitized_url];
- var headers = httpChannel.requestMethod + " /" + resource_url + " HTTP/1.1" + "\r\n";
- httpChannel.visitRequestHeaders(function(header,value){
- headers += header +": " + value + "\r\n";});
- if (httpChannel.requestMethod == "GET"){
- headers += "\r\n";
- }
- if (httpChannel.requestMethod == "POST"){
- //for POST, extra "\r\n" is already included in uploaddata (see below) to separate http header from http body
- var uploadChannel = httpChannel.QueryInterface(Ci.nsIUploadChannel);
- var uploadChannelStream = uploadChannel.uploadStream;
- uploadChannelStream.QueryInterface(Ci.nsISeekableStream);
- uploadChannelStream.seek(0,0);
- var stream = Cc['@mozilla.org/scriptableinputstream;1'].createInstance(Ci.nsIScriptableInputStream);
- stream.init(uploadChannelStream);
- var uploaddata = stream.read(stream.available());
- stream.close();
- //FF's uploaddata contains Content-Type and Content-Length headers + '\r\n\r\n' + http body
- headers += uploaddata;
- }
- var host = headers.split('\r\n')[1].split(':')[1].replace(/ /g,'');
- var port = 443;
- if (tab_url_full.split(':').length === 3){
- //the port is explicitely provided in URL
- port = parseInt(tab_url_full.split(':')[2].split('/')[0]);
- }
- resolve({'headers':headers, 'server':host, 'port':port});
- });
-}
-
-
-function browser_specific_init(){
- //sometimes gBrowser is not available
- if (gBrowser === null || typeof(gBrowser) === "undefined"){
- gBrowser = win.gBrowser;
- setTimeout(browser_specific_init, 100);
- return;
- }
-
- var os = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS;
- if (os === "WINNT") os_win = true;
-
- getPref('valid_hashes', 'string')
- .then(function(vh){
- if (vh != 'not found'){
- valid_hashes = JSON.parse(vh);
- }
- });
-
- fsRootPath = OS.Path.join(OS.Constants.Path.profileDir, "pagesigner");
- manager_path = OS.Path.toFileURI(OS.Path.join(fsRootPath, "manager.html"));
-
- //copy the manager file to local filesystem for security + takes care of some odd behaviour
- //when trying to add eventListener to chrome:// resources
- var html = OS.Path.fromFileURI(thisaddon.getResourceURI('content/manager.html').spec);
- var js = OS.Path.fromFileURI(thisaddon.getResourceURI('content/manager.js2').spec);
- var css = OS.Path.fromFileURI(thisaddon.getResourceURI('content/manager.css').spec);
- var check = OS.Path.fromFileURI(thisaddon.getResourceURI('content/check.png').spec);
- var cross = OS.Path.fromFileURI(thisaddon.getResourceURI('content/cross.png').spec);
- var swalcss = OS.Path.fromFileURI(thisaddon.getResourceURI('content/sweetalert.css').spec);
- var swaljs = OS.Path.fromFileURI(thisaddon.getResourceURI('content/sweetalert.min.js2').spec);
- var icon = OS.Path.fromFileURI(thisaddon.getResourceURI('content/icon16.png').spec);
-
- var dest_html = OS.Path.join(fsRootPath, "manager.html");
- var dest_js = OS.Path.join(fsRootPath, "manager.js2");
- var dest_css = OS.Path.join(fsRootPath, "manager.css");
- var dest_check = OS.Path.join(fsRootPath, "check.png");
- var dest_cross = OS.Path.join(fsRootPath, "cross.png");
- var dest_swalcss = OS.Path.join(fsRootPath, "sweetalert.css");
- var dest_swaljs = OS.Path.join(fsRootPath, "sweetalert.min.js2");
- var dest_icon = OS.Path.join(fsRootPath, "icon16.png");
-
- OS.File.makeDir(fsRootPath, {ignoreExisting:true})
- .then(function(){
- OS.File.copy(html, dest_html);
- })
- .then(function(){
- return OS.File.copy(js, dest_js);
- })
- .then(function(){
- OS.File.copy(css, dest_css);
- })
- .then(function(){
- OS.File.copy(check, dest_check);
- })
- .then(function(){
- OS.File.copy(cross, dest_cross);
- })
- .then(function(){
- OS.File.copy(swalcss, dest_swalcss);
- })
- .then(function(){
- OS.File.copy(swaljs, dest_swaljs);
- })
- .then(function(){
- OS.File.copy(icon, dest_icon);
- });
-
- init();
-}
-
-var idiv, listener, managerDocument; //these must be global otherwise we'll get no events
-function openManager(is_loading){
- if (typeof(is_loading) === 'undefined'){
- is_loading = false;
- }
- var t;
- var was_manager_open = false;
- var tabs = gBrowser.tabs;
- for(var i=0; i < tabs.length; i++){
- var url = gBrowser.getBrowserForTab(tabs[i]).contentWindow.location.href;
- if (url == manager_path){
- t = tabs[i];
- if (! is_loading){
- //on Win7 i was getting 'load' event even when I clicked another tab
- //we want to select the tab only if manager was called from the menu
- gBrowser.selectedTab = t;
- }
- was_manager_open = true;
- }
- }
- if (was_manager_open && (gBrowser.getBrowserForTab(t).contentWindow.document === managerDocument)){
- console.log('ignoring the same managerDocument in tab');
- return;
- }
-
- var promise;
- if (was_manager_open && !is_loading){
- //this may be a dangling manager from previous browser session
- //if so, then reload it
- if (gBrowser.getBrowserForTab(t).contentWindow.document !== managerDocument){
- console.log('detected a dangling manager tab');
- promise = Promise.resolve();
- }
- else {
- console.log('focusing existing manager');
- return;
- }
- }
- else if (was_manager_open && is_loading){
-
- console.log('reloading existing manager');
- promise = Promise.resolve();
- }
- else if (!was_manager_open){
- promise = new Promise(function(resolve, reject) {
- console.log('opening a new manager');
- t = gBrowser.addTab(manager_path);
- gBrowser.selectedTab = t;
- function check_uri(){
- if (gBrowser.getBrowserForTab(t).contentWindow.location.href !== manager_path){
- console.log('data tab href not ready, waiting');
- setTimeout(function(){check_uri();}, 100);
- }
- else {
- resolve();
- }
- };
- check_uri();
- });
- }
-
- promise
- .then(function(){
- //DOM may not be available immediately
- managerDocument = gBrowser.getBrowserForTab(t).contentWindow.document;
- return new Promise(function(resolve, reject) {
- function wait_for_DOM(){
- listener = managerDocument.getElementById('manager2extension');
- idiv = managerDocument.getElementById('extension2manager');
- if (listener && idiv){
- resolve();
- }
- else {
- console.log('manager DOM not ready yet, waiting');
- setTimeout(function(){wait_for_DOM()}, 100);
- }
- }
- wait_for_DOM();
- });
- })
- .then(function(){
- function onEvent(){
- console.log('in click event');
- if (listener.textContent === '') return;//spurious click
- var data = JSON.parse(listener.textContent);
- listener.textContent = '';
- if (data.destination !== 'extension') return;
- if (data.message === 'refresh'){
- populateTable();
- }
- else if (data.message === 'export'){
- var path = OS.Path.join(fsRootPath, data.args.dir, 'pgsg.pgsg');
- console.log('saving full path', path);
- savePGSGFile(path, data.args.file);
- }
- else if (data.message === 'delete'){
- OS.File.removeDir(OS.Path.join(fsRootPath, data.args.dir))
- .then(function(){
- populateTable();
- });
- }
- else if (data.message === 'rename'){
- //to update dir's modtime, we remove the file and recreate it
- writeFile(data.args.dir, "meta", str2ba(data.args.newname), true)
- .then(function(){
- populateTable();
- });
- }
- else if (data.message === 'viewdata'){
- var dir = OS.Path.join(fsRootPath, data.args.dir);
- openTabs(dir);
- }
- else if (data.message === 'viewraw'){
- var path = OS.Path.join(fsRootPath, data.args.dir, 'raw.txt');
- gBrowser.selectedTab = gBrowser.addTab(path);
- }
- };
- listener.addEventListener('click', onEvent);
- onEvent(); //maybe the page asked for refresh before listener installed
-
- //add tab event listener which will trigger when user reloads the tab ie with F5
- function onLoadEvent(e){
- console.log('in tab load event handler');
- openManager(true);
- };
- if (!was_manager_open){
- //installed only once on first tab load
- t.addEventListener('load', onLoadEvent);
- }
- });
-}
-
-
-function getDirContents(dirName){
- return new Promise(function(resolve, reject) {
- var subdirs = [];
- if (dirName === '/') dirName = '';
- var path = OS.Path.join(fsRootPath, dirName);
- var iterator = new OS.File.DirectoryIterator(path);
- var p = iterator.forEach(
- function (entry) {
- //entry is not path
- subdirs.push(entry);
- });
- p.then(function(){
- iterator.close();
- resolve(subdirs);
- })
- .catch(function(e){
- console.log('error', e);
- });
- });
-}
-
-
-function getDirEntry(dirName){
- return new Promise(function(resolve, reject) {
- var path = OS.Path.join(fsRootPath, dirName);
- console.log('point1', path);
- OS.File.stat(path)
- .then(function(stat){
- resolve(stat);
- })
- .catch(function(what){
- reject(what);
- });
- });
-}
-
-
-function getName(obj){
- var delimiter = os_win ? '\\' : '/';
- return obj.path.split(delimiter).pop();
-}
-
-
-function getFullPath(obj){
- return obj.path;
-}
-
-
-function getFileContent(dirname, filename){
- return new Promise(function(resolve, reject) {
- var path = OS.Path.join(fsRootPath, dirname, filename);
- OS.File.read(path)
- .then(function (data){
- resolve(ua2ba(data));
- }).
- catch(function(e){
- console.log('resolving with error', e);
- //must resolve even on error
- resolve(e);
- });
- });
-}
-
-function isDirectory(obj){
- return obj.isDir;
-}
-
-
-function getModTime(dirEntry){
- return new Promise(function(resolve, reject) {
- var promise = OS.File.stat(dirEntry.path);
- promise
- .then(function(info){
- resolve(info.lastModificationDate.getTime());
- });
- });
-}
-
-
-function sendMessage(data){
- if (typeof(idiv) === "undefined") return;
- if (!idiv) return;
- try{ //idiv may be a dead object. The only way to find out is by accessing its property
- var json = JSON.stringify(data)
- idiv.textContent = json;
- idiv.click();
- }
- catch (e){
- return;
- }
-}
-
-
-function savePGSGFile(existing_path, name){
- var copyFile = function(src, dst){
- OS.File.copy(src, dst)
- .then(function(){
- console.log("File write OK");
- },
- function (e){
- console.log("Caught error writing file: "+e);
- });
- };
-
- if (testing){
- var dldir = Cc["@mozilla.org/file/directory_service;1"].
- getService(Ci.nsIProperties).get("DfltDwnld", Ci.nsIFile).path;
- var dst = OS.Path.join(dldir, 'pagesigner.tmp.dir', name + '.pgsg');
- copyFile(existing_path, dst);
- return;
- }
-
- var nsIFilePicker = Ci.nsIFilePicker;
- var fp = Cc["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
- fp.init(window, "Save PageSigner file As", nsIFilePicker.modeSave);
- //don't set the display directory; leave as default
- fp.defaultExtension = "pgsg";
- fp.defaultString = name + ".pgsg";
- var rv = fp.show();
- if (rv == nsIFilePicker.returnOK || rv == nsIFilePicker.returnReplace) {
- copyFile(existing_path, fp.file.path);
- }
-}
-
-
-
-
-function showAboutInfo(){
- window.openDialog("chrome://pagesigner/content/firefox/about.xul","","chrome, dialog, modal", gBrowser).focus();
-}
-
-
-function dumpSecurityInfo(channel,urldata) {
- // Do we have a valid channel argument?
- if (! channel instanceof Ci.nsIChannel) {
- console.log("No channel available\n");
- return;
- }
- var secInfo = channel.securityInfo;
- // Print general connection security state
- if (secInfo instanceof Ci.nsITransportSecurityInfo) {
- secInfo.QueryInterface(Ci.nsITransportSecurityInfo);
- // Check security state flags
- latest_tab_sec_state = "uninitialised";
- if ((secInfo.securityState & Ci.nsIWebProgressListener.STATE_IS_SECURE) == Ci.nsIWebProgressListener.STATE_IS_SECURE)
- latest_tab_sec_state = "secure";
- else if ((secInfo.securityState & Ci.nsIWebProgressListener.STATE_IS_INSECURE) == Ci.nsIWebProgressListener.STATE_IS_INSECURE)
- latest_tab_sec_state = "insecure";
- else if ((secInfo.securityState & Ci.nsIWebProgressListener.STATE_IS_BROKEN) == Ci.nsIWebProgressListener.STATE_IS_BROKEN)
- latest_tab_sec_state = "unknown";
-
- //remove hashes - they are not URLs but are used for internal page mark-up
- sanitized_url = urldata.split("#")[0];
- dict_of_status[sanitized_url] = latest_tab_sec_state;
- dict_of_httpchannels[sanitized_url] = channel.QueryInterface(Ci.nsIHttpChannel);
- }
- else {
- console.log("\tNo security info available for this channel\n");
- }
-}
-
-
-//blocks http request coming from block_tab
-var httpRequestBlocker = {
- observe: function (httpChannel, aTopic, aData) {
- try{
- if (aTopic !== "http-on-modify-request") return;
- if (!(httpChannel instanceof Ci.nsIHttpChannel)) return;
- var notificationCallbacks;
- if (httpChannel.notificationCallbacks) {
- notificationCallbacks = httpChannel.notificationCallbacks;
- }
- else if (httpChannel.loadGroup && httpChannel.loadGroup.notificationCallbacks) {
- notificationCallbacks = httpChannel.loadGroup.notificationCallbacks;
- }
- else {
- return;
- }
- var path = notificationCallbacks.getInterface(Components.interfaces.nsIDOMWindow).top.location.pathname;
- } catch (e){
- return; //xhr dont have any interface
- }
- if (block_urls.indexOf(path) > -1){
- httpChannel.cancel(Components.results.NS_BINDING_ABORTED);
- return;
- }
- }
-};
-
-
-var myListener =
-{
- QueryInterface: function(aIID)
- {
- if (aIID.equals(Ci.nsIWebProgressListener) ||
- aIID.equals(Ci.nsISupportsWeakReference) ||
- aIID.equals(Ci.nsISupports))
- return this;
- throw Components.results.NS_NOINTERFACE;
- },
- onStateChange: function(aWebProgress, aRequest, aFlag, aStatus) { },
- onLocationChange: function(aProgress, aRequest, aURI) { },
- onProgressChange: function(aWebProgress, aRequest, curSelf, maxSelf, curTot, maxTot) { },
- onStatusChange: function(aWebProgress, aRequest, aStatus, aMessage) { },
- onSecurityChange: function(aWebProgress, aRequest, aState)
- {
- // check if the state is secure or not
- if(aState & Ci.nsIWebProgressListener.STATE_IS_SECURE)
- {
- // this is a secure page, check if aRequest is a channel,
- // since only channels have security information
- if (aRequest instanceof Ci.nsIChannel)
- {
- dumpSecurityInfo(aRequest,gBrowser.selectedBrowser.contentWindow.location.href);
- }
- }
- }
-};
-
-
-function install_notification(t, commonName, raw_path){
- t.addEventListener("load", function load(){
- console.log('in load event');
- var box = gBrowser.getNotificationBox();
- var priority = box.PRIORITY_INFO_HIGH;
- var message = 'PageSigner successfully verified that the webpage below was received from '+commonName;
- var icon = 'chrome://pagesigner/content/icon16.png';
- var buttons = [{
- label: 'View raw HTML with HTTP headers',
- accessKey: '',
- callback: function(){
- var nt = gBrowser.addTab(raw_path);
- gBrowser.selectedTab = nt;
- //throwing an error prevents notification from closing
- throw new Error('prevent notification close');
- }
- }];
- setTimeout(function(){
- //without timeout, notifbar fails to show
- box.appendNotification(message, 'pgsg-notif', icon, priority, buttons);
- }, 1000);
- t.removeEventListener("load", load, false);
- }, false);
-}
-
-
-function makeSessionDir(server, is_imported){
- return new Promise(function(resolve, reject) {
- if (typeof(is_imported) === "undefined"){
- is_imported = false;
- }
- var localDir = OS.Path.join(OS.Constants.Path.profileDir, "pagesigner");
- var time = getTime();
- var imported_str = "";
- if (is_imported){
- imported_str = "-IMPORTED";
- }
- var newdir = OS.Path.join(localDir, time+'-'+server+imported_str);
- OS.File.makeDir(newdir)
- .then(function(){
- resolve(newdir);
- });
- });
-}
-
-
-function writePgsg(pgsg, session_dir, commonName){
- //* is illegal in a Windows filename; remove
- var name = commonName.replace(/\*\./g,"");
- //var f = OS.File.open(OS.Path.join(session_dir, 'pgsg.pgsg'), {create:true});
- return OS.File.writeAtomic(OS.Path.join(session_dir, 'pgsg.pgsg'), ba2ua(pgsg))
- .then(function(){
- return OS.File.writeAtomic(OS.Path.join(session_dir, 'meta'), ba2ua(str2ba(name)));
- });
-}
-
-
-function writeFile(dirName, fileName, data, is_update){
- if(typeof(is_update) === "undefined"){
- is_update = false;
- }
- return new Promise(function(resolve, reject) {
- var path = OS.Path.join(fsRootPath, dirName, fileName);
- var promise = is_update ? OS.File.remove(path) : Promise.resolve();
- promise
- .then(function(){
- return OS.File.open(path, {create:true});
- })
- .then(function(f){
- return f.close();
- })
- .then(function(){
- return OS.File.writeAtomic(path, ba2ua(data));
- })
- .then(function(){
- resolve();
- })
- .catch(function(e){
- console.log('caught error in writeFile', e);
- alert('error in writeFile');
- })
- });
-}
-
-
-function openTabs(sdir){
- var raw_path = OS.Path.join(sdir, 'raw.txt');
- try{
- OS.File.stat(raw_path);
- }
- catch(e){
- //file hasnt been written yet, sleep a while
- setTimeout(function(){
- openTabs(sdir);
- }, 1000);
- return;
- }
-
- var commonName;
- var dataFileURI;
- var t;
- getFileContent(sdir, "metaDomainName")
- .then(function(data_ba){
- commonName = ba2str(data_ba);
- return getFileContent(sdir, "metaDataFilename")
- })
- .then(function(data){
- var name = ba2str(data);
- var data_path = OS.Path.join(fsRootPath, sdir, name);
- dataFileURI = OS.Path.toFileURI(data_path);
- block_urls.push(data_path);
- t = gBrowser.addTab(data_path);
- gBrowser.selectedTab = t;
- //resolve when URI is ours
- console.log('opening data tab');
- return new Promise(function(resolve, reject) {
- function check_uri(){
- if (gBrowser.getBrowserForTab(t).contentWindow.location.href !== dataFileURI){
- console.log('data tab href not ready, waiting');
- setTimeout(function(){check_uri();}, 100);
- }
- else {
- resolve();
- }
- };
- check_uri();
- });
- })
- .then(function(){
- //reload the tab and check URI
- console.log('reloading data tab');
- //set a token on old document object
- gBrowser.getBrowserForTab(t).contentWindow.document['pagesigner-before-reload'] = true;
- gBrowser.getBrowserForTab(t).reloadWithFlags(Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE);
- //after reload FF marks previous document as [dead Object],
- return new Promise(function(resolve, reject) {
- function check_new_document(){
- var doc = gBrowser.getBrowserForTab(t).contentWindow.document;
- if (doc.hasOwnProperty('pagesigner-before-reload')){
- console.log('data tab href not ready, waiting');
- setTimeout(function(){check_new_document();}, 100);
- }
- else {
- resolve();
- }
- };
- check_new_document();
- });
- })
- .then(function(){
- viewTabDocument = gBrowser.getBrowserForTab(t).contentWindow.document;
- //even though .document is immediately available, its .body property may not be
- return new Promise(function(resolve, reject) {
- function wait_for_body(){
- if (viewTabDocument.body === null){
- console.log('body not available, waiting');
- setTimeout(function(){wait_for_body()}, 100);
- }
- else {
- console.log('viewTabDocument is ', viewTabDocument);
- console.log('body is ', viewTabDocument.body);
- install_bar();
- viewTabDocument.getElementById("domainName").textContent = commonName;
- viewTabDocument['pagesigner-session-dir'] = sdir;
- console.log('injected stuff into viewTabDocument');
- }
- };
- wait_for_body();
- });
- });
-}
-
-
-function Socket(name, port){
- this.name = name;
- this.port = port;
- this.sckt = null;
- this.buffer = [];
- this.recv_timeout = 20*1000;
-}
-//inherit the base class
-Socket.prototype = Object.create(AbstractSocket.prototype);
-Socket.prototype.constructor = Socket;
-
-Socket.prototype.connect = function(){
- var that = this;
- return new Promise(function(resolve, reject) {
- that.sckt = new window.TCPSocket(that.name, that.port, {binaryType:"arraybuffer"});
- that.sckt.ondata = function(event){
- var int_array = ab2ba(event.data)
- console.log('ondata got bytes:', int_array.length);
- that.buffer = [].concat(that.buffer, int_array);
- };
- //dont wait for connect for too long
- var timer = setTimeout(function(){
- reject('connect: socket timed out');
- }, 1000*20)
-
- that.sckt.onopen = function() {
- clearInterval(timer);
- console.log('onopen');
- resolve('ready');
- };
- that.sckt.onerror = function(event) {
- clearInterval(timer);
- console.log('onerror', event.data);
- reject(event.data);
- };
- });
-};
-Socket.prototype.send = function(data_in){
- var ab = ba2ab(data_in);
- this.sckt.send(ab, 0, ab.byteLength);
-};
-Socket.prototype.close = function(){
- this.sckt.close();
-};
-
-
-function get_xhr(){
- return Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance();
-}
-
-
-
-function updateCache(hash){
- if (valid_hashes.indexOf(hash.toString()) < 0){
- valid_hashes.push(hash.toString());
- setPref('valid_hashes', 'string', JSON.stringify(valid_hashes));
- }
-}
-
-
-function sendAlert(alertData){
- var p = Cc["@mozilla.org/embedcomp/prompt-service;1"].getService(Ci.nsIPromptService);
- p.alert(null, alertData.title, alertData.text);
-}
diff --git a/content/firefox/settings.xul b/content/firefox/settings.xul
deleted file mode 100644
index 7fabd8a..0000000
--- a/content/firefox/settings.xul
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/content/icon16.png b/content/icon16.png
deleted file mode 100644
index 65305be..0000000
Binary files a/content/icon16.png and /dev/null differ
diff --git a/content/main.js b/content/main.js
deleted file mode 100644
index 50dba75..0000000
--- a/content/main.js
+++ /dev/null
@@ -1,690 +0,0 @@
-var script_exception;
-try {
-var random_uid; //we get a new uid for each notarized page
-var reliable_sites = []; //read from content/pubkeys.txt
-var previous_session_start_time; // used to make sure user doesnt exceed rate limiting
-var chosen_notary;
-var tdict = {};
-var valid_hashes = [];
-var os_win = false; //is OS windows? used to fix / in paths
-var browser_init_finished = false; //signal to test script when it can start
-
-
-function init(){
- getPref('verbose', 'bool')
- .then(function(value){
- if (value !== true && !is_chrome){
- console.log = function(){};
- }
- return getPref('fallback', 'bool');
- })
- .then(function(value){
- if (value === true){
- //TODO this should be configurable, e.g. choice from list
- //or set in prefs
- chosen_notary = pagesigner_servers[1];
- oracles_intact = true;
- }
- else {
- chosen_notary = oracles[Math.random()*(oracles.length) << 0];
- var oracle_hash = ba2hex(sha256(JSON.stringify(chosen_notary)));
- var was_oracle_verified = false;
- getPref('verifiedOracles.'+oracle_hash, 'bool')
- .then(function(value){
- if (value === true){
- oracles_intact = true;
- }
- else {
- //async check oracles and if the check fails, sets a global var
- //which prevents notarization session from running
- console.log('oracle not verified');
- check_oracle(chosen_notary)
- .then(function success(){
- return setPref('verifiedOracles.'+oracle_hash, 'bool', true);
- })
- .then(function (){
- oracles_intact = true;
- })
- .catch(function(err){
- console.log('caught error', err);
- //query for a new oracle
- //TODO fetch backup oracles list
- });
- }
- });
- }
- import_reliable_sites();
- startListening();
- browser_init_finished = true;
- });
-}
-
-
-function parse_reliable_sites(text){
- var lines = text.split('\n');
- var name = "";
- var expires = "";
- var modulus = [];
- var i = -1;
- var x;
- var mod_str;
- var line;
- while (true){
- i += 1;
- if (i >= lines.length){
- break;
- }
- x = lines[i];
- if (x.startsWith('#')){
- continue;
- }
- else if (x.startsWith('Name=')){
- name = x.slice('Name='.length);
- }
- else if (x.startsWith('Expires=')){
- expires = x.slice('Expires='.length);
- }
- else if (x.startsWith('Modulus=')){
- mod_str = '';
- while (true){
- i += 1;
- if (i >= lines.length){
- break;
- }
- line = lines[i];
- if (line === ''){
- break;
- }
- mod_str += line;
- }
- modulus = [];
- var bytes = mod_str.split(' ');
- for (var j=0; j < bytes.length; j++){
- if (bytes[j] === ''){
- continue;
- }
- modulus.push( hex2ba(bytes[j])[0] );
- }
- //Don't use pubkeys which expire less than 3 months from now
- var ex = expires.split('/');
- var extime = new Date(parseInt(ex[2]), parseInt(ex[0])-1, parseInt(ex[1]) ).getTime();
- var now = new Date().getTime();
- if ( (extime - now) < 1000*60*60*24*90){
- continue;
- }
- reliable_sites.push( {'name':name, 'port':443, 'expires':expires, 'modulus':modulus} );
- }
- }
-}
-
-
-//callback is used in testing to signal when this page's n10n finished
-function startNotarizing(callback){
- if (! oracles_intact){
- sendAlert({title:'PageSigner error', text:'Cannot notarize because something is wrong with PageSigner server. Please try again later'});
- return;
- }
- var modulus;
- var certsha256;
- var headers, server, port, chain;
- getHeaders()
- .then(function(obj){
- headers = obj.headers;
- server = obj.server;
- port = obj.port;
- loadBusyIcon();
- return get_certificate(server, port);
- })
- .then(function(certchain){
- chain = certchain;
- if (! verifyCert(chain)){
- sendAlert({title:"PageSigner error", text:"This website cannot be audited by PageSigner because it presented an untrusted certificate"});
- return;
- }
- modulus = getModulus(chain[0]);
- certsha256 = sha256(chain[0]);
- random_uid = Math.random().toString(36).slice(-10);
- previous_session_start_time = new Date().getTime();
- //loop prepare_pms 10 times until succeeds
- return new Promise(function(resolve, reject) {
- var tries = 0;
- var loop = function(resolve, reject){
- tries += 1;
- prepare_pms(modulus).then(function(args){
- resolve(args);
- }).catch(function(error){
- console.log('caught error', error);
- if (error.startsWith('Timed out')){
- reject(error);
- return;
- }
- if (error != 'PMS trial failed'){
- reject('in prepare_pms: caught error ' + error);
- return;
- }
- if (tries == 10){
- reject('Could not prepare PMS after 10 tries');
- return;
- }
- //else PMS trial failed
- loop(resolve, reject);
- });
- };
- loop(resolve, reject);
- });
- })
- .then(function(args){
- return start_audit(modulus, certsha256, server, port, headers, args[0], args[1], args[2]);
- })
- .then(function(args2){
- return save_session_and_open_data(args2, server);
- })
- .then(function(){
- //testing only
- if (testing){
- callback();
- }
- loadNormalIcon();
- })
- .catch(function(err){
- //TODO need to get a decent stack trace
- loadNormalIcon();
- console.log('There was an error: ' + err);
- if (err === "Server sent alert 2,40"){
- sendAlert({title:'PageSigner error', text:'Pagesigner is not compatible with this website because the website does not use RSA ciphersuites'});
- }
- else if (err.startsWith('Timed out waiting for notary server to respond') &&
- ((new Date().getTime() - previous_session_start_time) < 60*1000) ){
- sendAlert ({title:'PageSigner error', text:'You are signing pages way too fast. Please retry in 60 seconds'});
- }
- else {
- sendAlert({title:'PageSigner error', text:err});
- }
- });
-}
-
-
-
-function save_session_and_open_data(args, server){
- assert (args.length === 18, "wrong args length");
- var cipher_suite = args[0];
- var client_random = args[1];
- var server_random = args[2];
- var pms1 = args[3];
- var pms2 = args[4];
- var server_certchain = args[5];
- var tlsver = args[6];
- var initial_tlsver = args[7];
- var fullresp_length = args[8];
- var fullresp = args[9];
- var IV_after_finished_length = args[10];
- var IV_after_finished = args[11];
- var notary_modulus_length = args[12];
- var signature = args[13];
- var commit_hash = args[14];
- var notary_modulus = args[15];
- var data_with_headers = args[16];
- var time = args[17];
-
- var server_chain_serialized = []; //3-byte length prefix followed by cert
- for (var i=0; i < server_certchain.length; i++){
- var cert = server_certchain[i];
- server_chain_serialized = [].concat(
- server_chain_serialized,
- bi2ba(cert.length, {'fixed':3}),
- cert);
- }
-
- var pgsg = [].concat(
- str2ba('tlsnotary notarization file\n\n'),
- [0x00, 0x02],
- bi2ba(cipher_suite, {'fixed':2}),
- client_random,
- server_random,
- pms1,
- pms2,
- bi2ba(server_chain_serialized.length, {'fixed':3}),
- server_chain_serialized,
- tlsver,
- initial_tlsver,
- bi2ba(fullresp_length, {'fixed':8}),
- fullresp,
- bi2ba(IV_after_finished_length, {'fixed':2}),
- IV_after_finished,
- bi2ba(notary_modulus_length, {'fixed':2}),
- signature,
- commit_hash,
- notary_modulus,
- time);
-
- var commonName = getCommonName(server_certchain[0]);
- var sdir;
- return makeSessionDir(server).
- then(function(dir){
- sdir = dir;
- return writeDatafile(data_with_headers, sdir);
- })
- .then(function(){
- return writePgsg(pgsg, sdir, commonName);
- })
- .then(function(){
- return openTabs(sdir);
- })
- .then(function(){
- updateCache(sha256(pgsg));
- populateTable(); //refresh manager
- });
-}
-
-
-//data_with_headers is a string
-function writeDatafile(data_with_headers, session_dir){
- return new Promise(function(resolve, reject) {
- var rv = data_with_headers.split('\r\n\r\n');
- var headers = rv[0];
- var data = rv.splice(1).join('\r\n\r\n');
- var dirname = session_dir.split('/').pop();
- var header_lines = headers.split('\r\n');
- var type = 'html';
- for(var i=0; i < header_lines.length; i++){
- if (header_lines[i].search(/content-type:\s*/i) > -1){
- if (header_lines[i].search("html") > -1){
- type = 'html';
- break;
- }
- else if (header_lines[i].search("xml") > -1){
- type = 'xml';
- break;
- }
- else if (header_lines[i].search("json") > -1){
- type = 'json';
- break;
- }
- else if (header_lines[i].search("pdf") > -1){
- type = 'pdf';
- break;
- }
- else if (header_lines[i].search("zip") > -1){
- type = 'zip';
- break;
- }
- }
- }
- if (type === "html"){
- //html needs utf-8 byte order mark
- data = [].concat([0xef, 0xbb, 0xbf], str2ba(data));
- }
- else {
- data = str2ba(data);
- }
- writeFile(dirname, 'metaDataFilename', str2ba('data.'+type))
- .then(function(){
- return writeFile(dirname, 'data.'+type, data);
- })
- .then(function(){
- return writeFile(dirname, 'raw.txt', str2ba(data_with_headers));
- })
- .then(function(){
- resolve();
- });
- });
-}
-
-
-
-function writePgsg(pgsg, session_dir, commonName){
- return new Promise(function(resolve, reject) {
- var dirname = session_dir.split('/').pop();
- var name = commonName.replace(/\*\./g,"");
- writeFile(dirname, 'pgsg.pgsg', pgsg)
- .then(function(){
- return writeFile(dirname, 'meta', str2ba(name));
- })
- .then(function(){
- return writeFile(dirname, 'metaDomainName', str2ba(commonName));
- })
- .then(function(){
- resolve();
- });
- });
-}
-
-
-
-//imported_data is an array of numbers
-function verify_tlsn(data, from_past){
- var offset = 0;
- if (ba2str(data.slice(offset, offset+=29)) !== "tlsnotary notarization file\n\n"){
- throw('wrong header');
- }
- if(data.slice(offset, offset+=2).toString() !== [0x00, 0x02].toString()){
- throw('wrong version');
- }
- var cs = ba2int(data.slice(offset, offset+=2));
- var cr = data.slice(offset, offset+=32);
- var sr = data.slice(offset, offset+=32);
- var pms1 = data.slice(offset, offset+=24);
- var pms2 = data.slice(offset, offset+=24);
- var chain_serialized_len = ba2int(data.slice(offset, offset+=3));
- var chain_serialized = data.slice(offset, offset+=chain_serialized_len);
- var tlsver = data.slice(offset, offset+=2);
- var tlsver_initial = data.slice(offset, offset+=2);
- var response_len = ba2int(data.slice(offset, offset+=8));
- var response = data.slice(offset, offset+=response_len);
- var IV_len = ba2int(data.slice(offset, offset+=2));
- var IV = data.slice(offset, offset+=IV_len);
- var sig_len = ba2int(data.slice(offset, offset+=2));
- var sig = data.slice(offset, offset+=sig_len);
- var commit_hash = data.slice(offset, offset+=32);
- var notary_pubkey = data.slice(offset, offset+=sig_len);
- var time = data.slice(offset, offset+=4);
- assert (data.length === offset, 'invalid .pgsg length');
-
- offset = 0;
- var chain = []; //For now we only use the 1st cert in the chain
- while(offset < chain_serialized.length){
- var len = ba2int(chain_serialized.slice(offset, offset+=3));
- var cert = chain_serialized.slice(offset, offset+=len);
- chain.push(cert);
- }
-
- var commonName = getCommonName(chain[0]);
- //verify cert
- if (!verifyCert(chain)){
- throw ('certificate verification failed');
- }
- var modulus = getModulus(chain[0]);
- //verify commit hash
- if (sha256(response).toString() !== commit_hash.toString()){
- throw ('commit hash mismatch');
- }
- //verify sig
- var signed_data = sha256([].concat(commit_hash, pms2, modulus, time));
- var signing_key;
- if (from_past){signing_key = notary_pubkey;}
- else {signing_key = chosen_notary.modulus;}
- if (!verify_commithash_signature(signed_data, sig, signing_key)){
- throw ('notary signature verification failed');
- }
-
- //decrypt html and check MAC
- var s = new TLSNClientSession();
- s.__init__();
- s.unexpected_server_app_data_count = response.slice(0,1);
- s.chosen_cipher_suite = cs;
- s.client_random = cr;
- s.server_random = sr;
- s.auditee_secret = pms1.slice(2, 2+s.n_auditee_entropy);
- s.initial_tlsver = tlsver_initial;
- s.tlsver = tlsver;
- s.server_modulus = modulus;
- s.set_auditee_secret();
- s.auditor_secret = pms2.slice(0, s.n_auditor_entropy);
- s.set_auditor_secret();
- s.set_master_secret_half(); //#without arguments sets the whole MS
- s.do_key_expansion(); //#also resets encryption connection state
- s.store_server_app_data_records(response.slice(1));
- s.IV_after_finished = IV;
- s.server_connection_state.seq_no += 1;
- s.server_connection_state.IV = s.IV_after_finished;
- html_with_headers = decrypt_html(s);
- return [html_with_headers,commonName, data, notary_pubkey];
-}
-
-
-//imported_data is an array of numbers
-function verify_tlsn_and_show_data(imported_data, create){
- try{
- var a = verify_tlsn(imported_data, create);
- }
- catch (e){
- sendAlert({title:'PageSigner failed to import file', text:'The error was: ' + e});
- return;
- }
- if (create){
- var data_with_headers = a[0];
- var commonName = a[1];
- var imported_data = a[2];
- var session_dir;
- makeSessionDir(commonName, true)
- .then(function(sdir){
- session_dir = sdir;
- return writeDatafile(data_with_headers, session_dir);
- })
- .then(function(){
- return writePgsg(imported_data, session_dir, commonName);
- })
- .then(function(){
- return openTabs(session_dir);
- })
- .then(function(){
- updateCache(sha256(imported_data));
- populateTable(); //refresh manager
- })
- .catch( function(error){
- console.log("got error in vtsh: "+error);
- });
- }
-}
-
-
-function populateTable(){
- var prev_tdict = tdict;
- tdict = {};
- var entries = [];
- var promises = [];
- getDirContents('/')
- .then(function(results){
- return new Promise(function(resolve, reject) {
-
- var returnPromise = function(dir){
- return new Promise(function(resolve, reject) {
- getModTime(dir)
- .then(function(t){
- var name = getName(dir);
- if (t === prev_tdict[name].modtime){
- tdict[name] = prev_tdict[name];
- }
- else {
- entries.push(name);
- }
- resolve();
- });
- });
- };
-
- for (var i=0; i < results.length; i++){
- if (!isDirectory(results[i])) continue;
- var name = getName(results[i]);
- if (!(name in prev_tdict)){
- entries.push(name);
- continue;
- }
- promises.push(returnPromise(results[i]));
- }
- resolve(Promise.all(promises));
- });
- })
- .then(function(){
- //boiled down the entries to new dirs only
- process_entries(entries);
- });
-}
-
-
-function process_entries(pgsg_subdirs){
-
- var returnPromise = function(dir){
- return new Promise(function(resolve, reject){
- getDirEntry(dir)
- .then(function(dirEntry){
- console.log('about to process_subdir');
- resolve(process_subdir(dirEntry));
- }).
- catch(function(e){
- console.log('what:', e);
- });
- });
- };
-
- var promises = [];
- for (var i=0; i < pgsg_subdirs.length; i++){
- var subdir = pgsg_subdirs[i];
- promises.push(returnPromise(subdir));
- }
- Promise.all(promises)
- .then(function(){
- //console.log('about to sendTable', tdict);
- sendTable();
- })
- .catch(function(e){
- console.log('promise rejection', e);
- });
-}
-
-
-function process_subdir(dirEntry){
- return new Promise(function(resolve, reject) {
- var imported = false;
- var displayName;
- var pgsg;
- var file_hash;
- var dirname = getName(dirEntry);
- var dirpath = getFullPath(dirEntry);
- if (dirname.match("-IMPORTED$")=="-IMPORTED"){
- imported = true;
- }
- getFileContent(dirname, 'meta')
- .then(function(name){
- displayName = ba2str(name);
- return getFileContent(dirname, 'pgsg.pgsg');
- })
- .then(function(raw){
- pgsg = raw;
- file_hash = sha256(pgsg);
- return getModTime(dirEntry);
- })
- .then(function(modtime){
- tdict[dirname] =
- {'name':displayName,
- 'imported':imported,
- 'dirURL':dirpath,
- 'hash':file_hash,
- 'pgsg':pgsg,
- 'modtime':modtime};
- resolve('ok');
- })
- .catch(function(e){
- //we must resolve even on error because of Promise.all()
- resolve(e);
- });
- });
-}
-
-
-//Also check validity of pgsg before sending
-function sendTable(){
- var rows = [];
- for (var key in tdict){
- var row = tdict[key];
- var is_valid = false;
- if (valid_hashes.indexOf(row.hash.toString()) > -1){
- is_valid = true;
- }
- else { //e.g. for some reason the cache was flushed
- try{
- verify_tlsn(row.pgsg, true);
- //if it doesnt throw - the check passed
- is_valid = true;
- updateCache(row.hash);
- }
- catch(e){
- is_valid = false;
- }
- }
- if (os_win) row.dirURL = fixWinPath(row.dirURL);
- rows.push({'name': row.name,
- 'imported':row.imported,
- 'valid':is_valid,
- 'verifier':'tlsnotarygroup1',
- 'dir':row.dirURL});
- }
- sendMessage(rows);
-}
-
-
-//invert all slashes and replace spaces with %20
-function fixWinPath(path){
- return path.replace(/\\/g,"/").replace(/ /g, "%20");
-}
-
-
-function getModulus(cert){
- var c = Certificate.decode(new Buffer(cert), 'der');
- var pk = c.tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.data;
- var pkba = ua2ba(pk);
- //expected modulus length 256, 384, 512
- var modlen = 256;
- if (pkba.length > 384) modlen = 384;
- if (pkba.length > 512) modlen = 512;
- var modulus = pkba.slice(pkba.length - modlen - 5, pkba.length -5);
- return modulus;
-}
-
-
-function getCommonName(cert){
- var c = Certificate.decode(new Buffer(cert), 'der');
- var fields = c.tbsCertificate.subject.value;
- for (var i=0; i < fields.length; i++){
- if (fields[i][0].type.toString() !== [2,5,4,3].toString()) continue;
- //first 2 bytes are DER-like metadata
- return ba2str(fields[i][0].value.slice(2));
- }
- return 'unknown';
-}
-
-
-function permutator(inputArr) {
- var results = [];
-
- function permute(arr, memo) {
- var cur, memo = memo || [];
-
- for (var i = 0; i < arr.length; i++) {
- cur = arr.splice(i, 1);
- if (arr.length === 0) {
- results.push(memo.concat(cur));
- }
- permute(arr.slice(), memo.concat(cur));
- arr.splice(i, 0, cur[0]);
- }
-
- return results;
- }
-
- return permute(inputArr);
-}
-
-
-function verifyCert(chain){
- var chainperms = permutator(chain);
- for (var i=0; i < chainperms.length; i++){
- if (verifyCertChain(chainperms[i])){
- return true;
- }
- }
- return false;
-}
-
-
-
-
-
-//This must be at the bottom, otherwise we'd have to define each function
-//before it gets used.
-browser_specific_init();
-
-
-} catch (e){
- script_exception = e;
-}
diff --git a/content/manager.css b/content/manager.css
deleted file mode 100644
index 91b0d1e..0000000
--- a/content/manager.css
+++ /dev/null
@@ -1,158 +0,0 @@
-a:link {
- text-decoration: none;
- color:#0000ff
-}
-
-a:visited {
- text-decoration: none;
- color:#0000ff;
-}
-
-a:hover {
- text-decoration: underline;
- color: #aa00aa;
-}
-
-a:active {
- text-decoration: underline;
-}
-
-h1 {
- text-shadow: 4px 4px 4px #CCCCCC;
-}
-
-/* Table 1 Style */
-table.table1{
- font-family: "Trebuchet MS", sans-serif;
- font-size: 16px;
- font-weight: bold;
- line-height: 1.4em;
- font-style: normal;
- border-collapse:separate;
-}
-.table1 thead th{
- padding:15px;
- color:#fff;
- text-shadow:1px 1px 1px #568F23;
- border:1px solid #93CE37;
- border-bottom:3px solid #9ED929;
- background-color:#9DD929;
- background:-webkit-gradient(
- linear,
- left bottom,
- left top,
- color-stop(0.02, rgb(123,192,67)),
- color-stop(0.51, rgb(139,198,66)),
- color-stop(0.87, rgb(158,217,41))
- );
- background: -moz-linear-gradient(
- center bottom,
- rgb(123,192,67) 2%,
- rgb(139,198,66) 51%,
- rgb(158,217,41) 87%
- );
- -webkit-border-top-left-radius:5px;
- -webkit-border-top-right-radius:5px;
- -moz-border-radius:5px 5px 0px 0px;
- border-top-left-radius:5px;
- border-top-right-radius:5px;
-}
-.table1 thead th:empty{
- background:transparent;
- border:none;
-}
-.table1 tbody th{
- color:#fff;
- text-shadow:1px 1px 1px #568F23;
- background-color:#9DD929;
- border:1px solid #93CE37;
- border-right:3px solid #9ED929;
- padding:0px 10px;
- background:-webkit-gradient(
- linear,
- left bottom,
- right top,
- color-stop(0.02, rgb(158,217,41)),
- color-stop(0.51, rgb(139,198,66)),
- color-stop(0.87, rgb(123,192,67))
- );
- background: -moz-linear-gradient(
- left bottom,
- rgb(158,217,41) 2%,
- rgb(139,198,66) 51%,
- rgb(123,192,67) 87%
- );
- -moz-border-radius:5px 0px 0px 5px;
- -webkit-border-top-left-radius:5px;
- -webkit-border-bottom-left-radius:5px;
- border-top-left-radius:5px;
- border-bottom-left-radius:5px;
-}
-.table1 tfoot td{
- color: #9CD009;
- font-size:32px;
- text-align:center;
- padding:10px 0px;
- text-shadow:1px 1px 1px #444;
-}
-.table1 tfoot th{
- color:#666;
-}
-.table1 tbody td{
- padding:0px;
- text-align:center;
- background-color:#DEF3CA;
- border: 2px solid #E7EFE0;
- -moz-border-radius:2px;
- -webkit-border-radius:2px;
- border-radius:2px;
- color:#666;
- text-shadow:1px 1px 1px #fff;
- height: 10px !important;
-}
-
-.table1 tbody tr{
- height: 10px;
-}
-
-.table1 tbody span.check::before{
- content : url(chrome://pagesigner/content/check.png)
-}
-
-
-
-button{
- margin: 0px;
- padding: 0px;
- font-family:Lucida Sans MS, Tahoma;
- font-size: 12px;
- color: #000;
- white-space:nowrap;
- width:95%;
- overflow:visible;
- height:auto;
-}
-
-
-
-
-
-
-
-button em{
- /*vertical-align:middle;
- margin:0 2px;
- display:inline-block;
- width:24px;
- height:auto;*/
- background-image: url(chrome://pagesigner/content/refresh.png);
-}
-
-button em.leftImage{
- background-position: left center;
-}
-
-button em.rightImage{
- background-position: -64px -16px;
-}
-
diff --git a/content/manager.html b/content/manager.html
deleted file mode 100644
index 921f5b0..0000000
--- a/content/manager.html
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
-
- Manage your PageSigner notarization files.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Your stored pagesigner notarization (.pgsg) files. Refresh the page (F5) to update.
-
-
-
-
- | File |
- Creation date , server name |
- Mine/imported |
- Verified |
- Verifier |
- View Data |
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/content/manager.js2 b/content/manager.js2
deleted file mode 100644
index 145b5fe..0000000
--- a/content/manager.js2
+++ /dev/null
@@ -1,236 +0,0 @@
-var is_chrome = false;
-if (navigator.userAgent.search('Chrome') > -1){
- var is_chrome = true;
-}
-var idiv;
-
-function onload(){
- //These divs are used only in testing. We cant use a variable because Chrome
- //content scripts cant access vars, only DOM elements
- var div = document.createElement('div');
- div.id = 'content_script_injected_into_page';
- div.textContent = 'false';
- div.style.display = 'none';
- document.body.appendChild(div);
-
- var div2 = document.createElement('div');
- div2.id = 'table_populated';
- div2.textContent = 'false';
- div2.style.display = 'none';
- document.body.appendChild(div2);
- //----------END OF TESTING DIVS----------
-
- idiv = document.getElementById('extension2manager');
- idiv.addEventListener('click', function(e){
- console.log('changed to', idiv.textContent);
- if (idiv.textContent === '') return; //spurious clicks can happen
- var data = JSON.parse(idiv.textContent);
- process_data(data);
- });
- sendMessage({'destination':'extension', 'message':'refresh'});
-}
-
-document.addEventListener('load', onload);
-window.addEventListener('load', onload);
-
-
-function process_data(rows){
- tb = document.getElementsByTagName('tbody')[0];
- var initial_row_length = tb.rows.length;
- for (var j=0; j < initial_row_length; j++){
- tb.deleteRow(0);
- }
- //create sort order based on names (more intuitive than time,
- //but should really be user-configurable sort of course...
- rows.sort(function (a,b){
- var as = a.name, bs = b.name;
- return as == bs ? 0: (as > bs ? 1 : -1);
- });
-
- for (var i=0; i < rows.length; i++){
- var r = rows[i];
- addRow({'name':r.name,
- 'imported':r.imported,
- 'valid':r.valid,
- 'verifier':r.verifier,
- 'dir':r.dir});
- }
- document.getElementById('table_populated').textContent = 'true';
- table_populated = true;
-}
-
-
-
-function addRow(args){
- var dir = args.dir.split('/').pop();
- var tb, row, td, a, img, text;
- tb = document.getElementById('myTableBody');
- row = tb.insertRow(tb.rows.length);
- td = document.createElement("td");
- td.appendChild(document.createTextNode(args.name));
- row.appendChild(td);
-
- td = document.createElement("td");
- a = document.createElement("a");
- a.title = 'Give the file a more memorable name';
- a.style = 'float: right';
- a.onclick = function (event){ doRename(event.target, args.name, dir);};
- a.text = 'Rename';
- td.appendChild(a);
- row.appendChild(td);
-
- td = document.createElement("td");
- a = document.createElement("a");
- a.title = 'Save the file so you can transfer it to others';
- a.style = 'float: right';
- a.onclick = function (event){
- console.log('export clicked');
- swal({
- title:'You MUST LOG OUT before exporting',
- text:'Before exporting you MUST LOG OUT of any sessions associated with the data you are about to export. Please LOG OUT NOW if you have any active sessions running and press OK to proceed',
- type: "warning"},
- function(){
- sendMessage({'destination':'extension','message':'export',
- 'args':{'dir': dir, 'file': args.name}});
- });
-
- };
- a.text = 'Export';
- td.appendChild(a);
- row.appendChild(td);
-
- td = document.createElement("td");
- a = document.createElement("a");
- a.title = 'permanently remove this set of files from disk';
- a.style = 'float: right';
- a.onclick = function (event){
- swal({
- title:'Removing notarization data',
- text: "This will remove all notarized data of "+args.name+". Are you sure?",
- type: "warning"
- },
- function(){
- sendMessage({'destination':'extension', 'message':'delete',
- 'args':{'dir':dir}});
- });
- };
- a.text = 'Delete';
- td.appendChild(a);
- row.appendChild(td);
-
- td = document.createElement("td");
- //make sure the -IMPORTED suffix isnt present
- td.textContent = dir.slice(0,19) + ' , ' + dir.slice(20).split('-')[0];
- row.appendChild(td);
-
- td = document.createElement("td");
- var implabel = "mine";
- if (args.imported) implabel = "imported";
- td.textContent = implabel;
- row.appendChild(td);
-
- td = document.createElement("td");
- img = document.createElement("img");
- img.height = 30;
- img.width = 30;
- var label;
- if (args.valid){
- img.src = 'check.png';
- label = 'valid';
- }
- else {
- img.src = 'cross.png';
- label = 'invalid';
- }
- text = document.createElement("text");
- text.textContent = label;
- td.appendChild(img);
- td.appendChild(text);
- row.appendChild(td);
-
- td = document.createElement("td");
- td.textContent = args.verifier;
- row.appendChild(td);
-
- td = document.createElement("td");
- a = document.createElement("a");
- a.onclick = function (event){
- sendMessage({'destination':'extension', 'message':'viewdata',
- 'args':{'dir':dir}});
- };
- a.text = "view";
- td.appendChild(a);
- text = document.createElement("text");
- text.textContent = ' , ';
- td.appendChild(text);
- a = document.createElement("a");
- a.onclick = function (event){
- sendMessage({'destination':'extension', 'message':'viewraw',
- 'args':{'dir':dir}});
- };
- a.text = "raw";
- td.appendChild(a);
-
- row.appendChild(td);
-}
-
-
-function doRename(t, oldname, dir){
- var isValid=(function(){
- var rg1=/^[^\\/:\*\?"<>\|]+$/; // forbidden characters \ / : * ? " < > |
- var rg2=/^\./; // cannot start with dot (.)
- var rg3=/^(nul|prn|con|lpt[0-9]|com[0-9])(\.|$)/i; // forbidden file names
- return function isValid(fname){
- return rg1.test(fname)&&!rg2.test(fname)&&!rg3.test(fname);
- };
- })();
- swal({
- title: "Enter a new name for the notarization file",
- type: "input",
- inputPlaceholder: "Write something"
- },
- function(new_name){
- if(!(isValid(new_name))){
- console.log('detected invalid name', new_name);
- //swal glitch - need a timeout
- setTimeout(function(){
- swal({title:"Invalid filename", text:'Please only use alphanumerical characters', type:'warning'});
- }, 200);
- }
- else if (new_name === null) return; //escape pressed
- else {
- sendMessage({'destination':'extension',
- 'message':'rename',
- 'args':{'dir':dir, 'newname':new_name}
- });
- }
- });
-}
-
-
-
-function sendMessage(msg){
- console.log('in sendMessage', msg);
- if (is_chrome){
- var evt = document.createEvent("CustomEvent");
- evt.initCustomEvent("hello", true, true, msg);
- var dispatch_when_injected = function(){
- if (document.getElementById('content_script_injected_into_page').textContent !== 'true'){
- console.log('chrome has not yet injected, retrying');
- setTimeout(function(){dispatch_when_injected();}, 100);
- }
- else {
- document.dispatchEvent(evt);
- }
- }
- dispatch_when_injected();
- }
- else {
- var json = JSON.stringify(msg);
- var buf = document.getElementById('manager2extension');
- buf.textContent = json;
- buf.click();
- }
-}
-
-
diff --git a/content/notification_bar.js b/content/notification_bar.js
deleted file mode 100644
index 579c05a..0000000
--- a/content/notification_bar.js
+++ /dev/null
@@ -1,68 +0,0 @@
-var viewTabDocument;
-
-function install_bar(){
-//when running from Firefox, the var gBrowser is available
-var using_chrome = typeof(gBrowser) === 'undefined';
-var using_firefox = typeof(gBrowser) !== 'undefined';
-var document = viewTabDocument;
-var table = document.createElement("table");
-table.style.position = "fixed";
-table.style.top = "0px";
-table.style.left = "100px";
-table.style.background = "rgba(242, 241, 240, 0.9)";
-table.style.width = "80%";
-table.style.height = "32px";
-table.style.visibility = 'hidden';
-table.style.opacity = '0';
-table.style.webkitTransition = 'visibility 0s 2s, opacity 2s linear';
-table.style.transition = 'visibility 0s 2s, opacity 2s linear';
-var row = document.createElement("tr");
-var cell1 = document.createElement("td");
-var cell2 = document.createElement("td");
-var cell3 = document.createElement("td");
-cell3.style.align = "right";
-var img = document.createElement("img");
-img.src = using_chrome ? "icon16.png" : "../icon16.png";
-var text = document.createElement("text");
-text.textContent = "PageSigner verified that this page was received from ";
-var domain = document.createElement("text");
-domain.id = "domainName";
-var sdir = document.createElement("text");
-sdir.id = "sdir";
-var button = document.createElement("button");
-button.id = "viewRaw";
-button.textContent = "View raw data with HTTP headers";
-button.style.MozBorderRadius = "4px";
-button.style.WebkitBorderRadius = "4px";
-button.style.borderRadius = "4px";
-button.onclick = function(){
- var dir = document['pagesigner-session-dir'];
- if (using_chrome){
- chrome.runtime.sendMessage({destination:'extension', message:'viewraw', args:{dir:dir}});
- }
- else if (using_firefox){
- var path = OS.Path.join(fsRootPath, dir, 'raw.txt');
- gBrowser.selectedTab = gBrowser.addTab(path);
- }
-};
-cell3.appendChild(button)
-cell2.appendChild(text);
-cell2.appendChild(domain);
-cell1.appendChild(img);
-row.appendChild(cell1);
-row.appendChild(cell2);
-row.appendChild(cell3);
-table.appendChild(row);
-table.appendChild(sdir);
-document.body.appendChild(table);
-document['pagesigner-session-dir'] = sdir;
-
-setTimeout(function(){
- //make a transition to visible
- table.style.visibility = 'visible';
- table.style.opacity = '1';
- table.style.webkitTransition = 'opacity 2s linear';
- table.style.transition = 'opacity 2s linear';
-}, 0);
-
-}
diff --git a/content/oracles.js b/content/oracles.js
deleted file mode 100644
index 8dd9543..0000000
--- a/content/oracles.js
+++ /dev/null
@@ -1,272 +0,0 @@
-var snapshotID = 'snap-2c1fab9b';
-var imageID = 'ami-15192302';
-var oracles_intact = false; //must be explicitely set to true
-
-var oracle =
-{'name':'tlsnotarygroup4',
- 'IP':'54.152.4.116',
- 'port':'10011',
-'DI':'https://ec2.us-east-1.amazonaws.com/?AWSAccessKeyId=AKIAIHZGACNJKBHFWOTQ&Action=DescribeInstances&Expires=2019-01-01&InstanceId=i-4b3aff5c&SignatureMethod=HmacSHA256&SignatureVersion=2&Version=2014-10-01&Signature=ByJUrXXgB%2BmJwc2Irk%2BxfZQh1yR3tYiqcA6Hp2gKciE%3D',
-'DV':'https://ec2.us-east-1.amazonaws.com/?AWSAccessKeyId=AKIAIHZGACNJKBHFWOTQ&Action=DescribeVolumes&Expires=2019-01-01&SignatureMethod=HmacSHA256&SignatureVersion=2&Version=2014-10-01&VolumeId=vol-006fce93&Signature=DXq7nf5BrpjUF7Rj%2FMJgk%2Bbs959FrVcJvfquT%2BeNS%2BM%3D',
-'GCO':'https://ec2.us-east-1.amazonaws.com/?AWSAccessKeyId=AKIAIHZGACNJKBHFWOTQ&Action=GetConsoleOutput&Expires=2019-01-01&InstanceId=i-4b3aff5c&SignatureMethod=HmacSHA256&SignatureVersion=2&Version=2014-10-01&Signature=ZUPfAD0jruIIupYSUrXUW7SR9vDhiNIyuVLvO7kgLLM%3D',
-'GU':'https://iam.amazonaws.com/?AWSAccessKeyId=AKIAIHZGACNJKBHFWOTQ&Action=GetUser&Expires=2019-01-01&SignatureMethod=HmacSHA256&SignatureVersion=2&Version=2010-05-08&Signature=C%2B2l1fpHxTt4p0JnROsu%2FMLlOnAJBjQ%2FS%2B8p%2FumAcH0%3D',
-'DIA':'https://ec2.us-east-1.amazonaws.com/?AWSAccessKeyId=AKIAIHZGACNJKBHFWOTQ&Action=DescribeInstanceAttribute&Attribute=userData&Expires=2019-01-01&InstanceId=i-4b3aff5c&SignatureMethod=HmacSHA256&SignatureVersion=2&Version=2014-10-01&Signature=FBc9vervde7ofgVYS3MR1kIjrW8yyAJQ8wDtURAJkwM%3D',
- 'instanceId': 'i-4b3aff5c',
- 'modulus':[160,219,242,71,45,207,8,59,79,223,247,65,118,79,92,119,51,107,26,66,49,174,16,126,182,43,221,31,56,45,138,214,69,246,225,36,162,66,241,197,137,45,96,224,13,213,205,59,163,225,202,179,175,99,112,135,37,149,17,87,168,15,93,245,138,106,137,39,236,125,88,170,131,191,243,226,163,209,235,135,152,55,101,152,168,71,152,48,157,184,96,196,19,187,171,238,168,208,59,101,32,119,124,132,16,43,162,173,242,160,81,39,173,128,196,136,86,121,80,10,12,233,53,185,147,114,124,68,216,23,186,156,117,53,21,52,200,223,222,52,201,180,208,17,165,33,212,48,55,111,235,30,189,200,248,218,90,191,253,172,93,146,140,248,150,70,93,221,161,172,179,156,58,230,161,111,95,45,90,27,102,206,136,222,127,191,203,43,156,198,50,21,232,229,41,110,195,37,206,62,126,249,50,1,45,157,87,13,172,255,161,110,34,151,53,233,96,201,139,149,220,67,182,190,23,135,40,93,221,214,41,159,219,183,119,132,86,205,216,161,97,0,28,124,91,1,125,209,106,47,220,75,108,224,143,139,150,188,23,23,15,203,42,231,76,253,239,195,6,111,246,30,31,156,115,190,52,52,37,213,102,0,150,110,7,150,120,61,190,135,244,228,107,87,87,223,24,212,178,205,198,61,140,16,44,6,224,168,214,53,201,247,121,138,240,72,7,73,149,181,133,147,124,221,222,46,121,176,200,162,48,33,59,241,254,30,247,7,165,91,166,113,133,119,234,229,129,162,64,164,205,172,79,182,147,63,226,133,82,201,26,251,17,227,251,0,25,238,38,70,85,229,92,103,180,87,60,159,148,113,135,33,169,101,184,138,239,71,40,187,1,133,134,49,160,236,165,160,250,77,140,213,234,172,225,231,174,21,29,220,60,221,177,21,26,245,163,155,187,28,66,50,159,184,97,107,14,86,26,145,171,88,137,238,212,36,79,123,183,190,202,177,201,132,121,178,127,149,13,184,243,47,132,120,153,28,41,169,72,251,152,86,153,212,63,247,29,52,173,26,252,249,63,146,188,53,97,244,90,123,71,47,195,142,91,123,213,151,166,229,208,154,127,208,243,253,168,154,171,110,253,153,129,176,27,155,195,103,49,211,182,55]
-}
-
-
-//there can be potentially multiple oracles to choose from
-var oracles = [];
-oracles.push(oracle);
-//all servers trusted to perform notary (including non-oracles)
-//TODO: configurable
-var pagesigner_servers = [oracle];
-
-//assuming both events happened on the same day, get the time
-//difference between them in seconds
-//the time string looks like "2015-04-15T19:00:59.000Z"
-function getSecondsDelta (later, sooner){
- assert (later.length == 24);
- if (later.slice(0,11) !== sooner.slice(0, 11)){
- return 999999; //not on the same day
- }
- var laterTime = later.slice(11,19).split(':');
- var soonerTime = sooner.slice(11,19).split(':');
- var laterSecs = parseInt(laterTime[0])*3600+parseInt(laterTime[1])*60+parseInt(laterTime[2]);
- var soonerSecs = parseInt(soonerTime[0])*3600+parseInt(soonerTime[1])*60+parseInt(soonerTime[2]);
- return laterSecs - soonerSecs;
-}
-
-
-
-function modulus_from_pubkey(pem_pubkey){
- var b64_str = '';
- var lines = pem_pubkey.split('\n');
- //omit header and footer lines
- for (var i=1; i < (lines.length-1); i++){
- b64_str += lines[i];
- }
- var der = b64decode(b64_str);
- //last 5 bytes are 2 DER bytes and 3 bytes exponent, our pubkey is the preceding 512 bytes
- var pubkey = der.slice(der.length - 517, der.length -5);
- return pubkey;
-}
-
-
-function checkDescribeInstances(xmlDoc, instanceId, IP){
- try{
- var rs = xmlDoc.getElementsByTagName('reservationSet');
- assert(rs.length === 1);
- var rs_items = rs[0].children;
- assert(rs_items.length === 1);
- var ownerId = rs_items[0].getElementsByTagName('ownerId')[0].textContent;
- var isets = rs_items[0].getElementsByTagName('instancesSet');
- assert(isets.length === 1);
- var instances = isets[0].children;
- assert(instances.length === 1);
- var parent = instances[0];
- assert(parent.getElementsByTagName('instanceId')[0].textContent === instanceId);
- assert(parent.getElementsByTagName('imageId')[0].textContent === imageID);
- assert(parent.getElementsByTagName('instanceState')[0].getElementsByTagName('name')[0].textContent === 'running');
- var launchTime = parent.getElementsByTagName('launchTime')[0].textContent;
- assert(parent.getElementsByTagName('ipAddress')[0].textContent === IP);
- assert(parent.getElementsByTagName('rootDeviceType')[0].textContent === 'ebs');
- assert(parent.getElementsByTagName('rootDeviceName')[0].textContent === '/dev/xvda');
- var devices = parent.getElementsByTagName('blockDeviceMapping')[0].getElementsByTagName('item');
- assert(devices.length === 1);
- assert(devices[0].getElementsByTagName('deviceName')[0].textContent === '/dev/xvda');
- assert(devices[0].getElementsByTagName('ebs')[0].getElementsByTagName('status')[0].textContent === 'attached');
- var volAttachTime = devices[0].getElementsByTagName('ebs')[0].getElementsByTagName('attachTime')[0].textContent;
- var volumeId = devices[0].getElementsByTagName('ebs')[0].getElementsByTagName('volumeId')[0].textContent;
- //get seconds from "2015-04-15T19:00:59.000Z"
- assert(getSecondsDelta(volAttachTime, launchTime) <= 3);
- assert(parent.getElementsByTagName('virtualizationType')[0].textContent === 'hvm');
- }catch(e){
- return false;
- }
- return {'ownerId':ownerId, 'volumeId':volumeId, 'volAttachTime':volAttachTime, 'launchTime':launchTime};
-}
-
-
-function checkDescribeVolumes(xmlDoc, instanceId, volumeId, volAttachTime){
- try{
- var volumes = xmlDoc.getElementsByTagName('volumeSet')[0].children;
- assert(volumes.length === 1);
- var volume = volumes[0];
- assert(volume.getElementsByTagName('volumeId')[0].textContent === volumeId);
- assert(volume.getElementsByTagName('snapshotId')[0].textContent === snapshotID);
- assert(volume.getElementsByTagName('status')[0].textContent === 'in-use');
- var volCreateTime = volume.getElementsByTagName('createTime')[0].textContent;
- var attVolumes = volume.getElementsByTagName('attachmentSet')[0].getElementsByTagName('item');
- assert(attVolumes.length === 1);
- var attVolume = attVolumes[0];
- assert(attVolume.getElementsByTagName('volumeId')[0].textContent === volumeId);
- assert(attVolume.getElementsByTagName('instanceId')[0].textContent === instanceId);
- assert(attVolume.getElementsByTagName('device')[0].textContent === '/dev/xvda');
- assert(attVolume.getElementsByTagName('status')[0].textContent === 'attached');
- var attTime = attVolume.getElementsByTagName('attachTime')[0].textContent;
- assert(volAttachTime === attTime);
- //Crucial: volume was created from snapshot and attached at the same instant
- //this guarantees that there was no time window to modify it
- assert(getSecondsDelta(attTime, volCreateTime) === 0);
- }catch(e){
- return false;
- }
- return true;
-}
-
-
-function checkGetConsoleOutput(xmlDoc, instanceId, launchTime){
- try{
- assert(xmlDoc.getElementsByTagName('instanceId')[0].textContent === instanceId);
- var timestamp = xmlDoc.getElementsByTagName('timestamp')[0].textContent;
- //prevent funny business: last consoleLog entry no later than 5 minutes after instance starts
- assert(getSecondsDelta(timestamp, launchTime) <= 300);
- var b64data = xmlDoc.getElementsByTagName('output')[0].textContent;
- var logstr = ba2str(b64decode(b64data));
- var sigmark = 'PageSigner public key for verification';
- var pkstartmark = '-----BEGIN PUBLIC KEY-----';
- var pkendmark = '-----END PUBLIC KEY-----';
-
- var mark_start = logstr.search(sigmark);
- assert(mark_start !== -1);
- var pubkey_start = mark_start + logstr.slice(mark_start).search(pkstartmark);
- var pubkey_end = pubkey_start+ logstr.slice(pubkey_start).search(pkendmark) + pkendmark.length;
- var pk = logstr.slice(pubkey_start, pubkey_end);
- assert(pk.length > 0);
- return pk;
- }catch(e){
- return false;
- }
-}
-
-// "userData" allows to pass an arbitrary script to the instance at launch. It MUST be empty.
-// This is a sanity check because the instance is stripped of the code which parses userData.
-function checkDescribeInstanceAttribute(xmlDoc, instanceId){
- try{
- assert(xmlDoc.getElementsByTagName('instanceId')[0].textContent === instanceId);
- assert(xmlDoc.getElementsByTagName('userData')[0].textContent === "");
- }catch(e){
- return false;
- }
- return true;
-}
-
-
-function checkGetUser(xmlDoc, ownerId){
- try{
- assert(xmlDoc.getElementsByTagName('UserId')[0].textContent === ownerId);
- assert(xmlDoc.getElementsByTagName('Arn')[0].textContent.slice(-(ownerId.length + ':root'.length)) === ownerId+':root');
- }catch(e){
- return false;
- }
- return true;
-}
-
-
-function check_oracle(o){
- return new Promise(function(resolve, reject) {
- var xhr = get_xhr();
- xhr.open('GET', o.DI, true);
- xhr.onload = function(){
- var xmlDoc = xhr.responseXML;
- var result = checkDescribeInstances(xmlDoc, o.instanceId, o.IP);
- if (!result){
- reject('checkDescribeInstances');
- }
- else {
- resolve(result);
- }
- };
- xhr.send();
- })
- .then(function(args){
- return new Promise(function(resolve, reject) {
- var xhr = get_xhr();
- xhr.open('GET', o.DV, true);
- xhr.onload = function(){
- var xmlDoc = xhr.responseXML;
- var result = checkDescribeVolumes(xmlDoc, o.instanceId, args.volumeId, args.volAttachTime);
- if (!result){
- reject('checkDescribeVolumes');
- }
- else {
- resolve({'ownerId':args.ownerId, 'launchTime':args.launchTime});
- }
- };
- xhr.send();
- });
- })
- .then(function(args){
- return new Promise(function(resolve, reject) {
- var xhr = get_xhr();
- xhr.open('GET', o.GU, true);
- xhr.onload = function(){
- var xmlDoc = xhr.responseXML;
- var result = checkGetUser(xmlDoc, args.ownerId);
- if (!result){
- reject('checkGetUser');
- }
- else {
- resolve(args.launchTime);
- }
- };
- xhr.send();
- });
- })
- .then(function(launchTime){
- return new Promise(function(resolve, reject) {
- var xhr = get_xhr();
- xhr.open('GET', o.GCO, true);
- xhr.onload = function(){
- var xmlDoc = xhr.responseXML;
- var result = checkGetConsoleOutput(xmlDoc, o.instanceId, launchTime);
- if (!result){
- reject('checkGetConsoleOutput');
- }
- else {
- if (modulus_from_pubkey(result).toString() !== o.modulus.toString()){
- reject('modulus_from_pubkey');
- }
- resolve();
- }
- };
- xhr.send();
- });
- })
- .then(function(){
- return new Promise(function(resolve, reject) {
- var xhr = get_xhr();
- xhr.open('GET', o.DIA, true);
- xhr.onload = function(){
- var xmlDoc = xhr.responseXML;
- var result = checkDescribeInstanceAttribute(xmlDoc, o.instanceId);
- if (!result){
- reject('checkDescribeInstanceAttribute');
- }
- else {
- resolve();
- }
- };
- xhr.send();
- });
- })
- .then(function(){
- var mark = 'AWSAccessKeyId=';
- var start;
- var id;
- var ids = [];
- //"AWSAccessKeyId" should be the same to prove that the queries are made on behalf of AWS user "root".
- //The attacker can be a user with limited privileges for whom the API would report only partial information.
- for (var url in [o.DI, o.DV, o.GU, o.GCO, o.DIA]){
- start = url.search(mark)+mark.length;
- id = url.slice(start, start + url.slice(start).search('&'));
- ids.push(id);
- }
- assert(new Set(ids).size === 1);
- console.log('oracle verification successfully finished');
- });
-}
-
-
diff --git a/content/refresh.png b/content/refresh.png
deleted file mode 100644
index f87824b..0000000
Binary files a/content/refresh.png and /dev/null differ
diff --git a/content/socket.js b/content/socket.js
deleted file mode 100644
index 784a039..0000000
--- a/content/socket.js
+++ /dev/null
@@ -1,107 +0,0 @@
-//The only way to determine if the server is done sending data is to check that the receiving
-//buffer has nothing but complete TLS records i.e. that there is no incomplete TLS records
-//However it was observed that in cases when getting e.g. zip files, some servers first send HTTP header as one
-//TLS record followed by the body as another record(s)
-//That's why after receiving a complete TLS record we wait to get some more data
-//This extra waiting must not be done for the handshake messages to avoid adding latency and having the handshake
-//dropped by the server
-function AbstractSocket(){};
-AbstractSocket.prototype.recv = function(is_handshake){
- if (typeof(is_handshake) === 'undefined'){
- is_handshake = false;
- }
- var that = this;
- return new Promise(function(resolve, reject) {
- var startTime = new Date().getTime();
- var complete_records = [];
- var buf = [];
- var resolved = false;
-
- var timer = setTimeout(function(){
- reject('recv: socket timed out');
- resolved = true;
- }, that.recv_timeout);
-
- var check = function(){
- //console.log('check()ing for more data', uid);
- if (resolved) {
- console.log('returning because resolved');
- return;
- }
- if (that.buffer.length === 0) {
- setTimeout(function(){check()}, 100);
- return;
- }
- console.log('new data in check', that.buffer.length);
- //else got new data
- buf = [].concat(buf, that.buffer);
- that.buffer = [];
- var rv = check_complete_records(buf);
- complete_records = [].concat(complete_records, rv.comprecs);
- if (! rv.is_complete){
- console.log("check_complete_records failed", that.uid);
- buf = rv.incomprecs;
- setTimeout(function(){check()}, 100);
- return;
- }
- else {
- function finished_receiving(){
- clearTimeout(timer);
- console.log('recv promise resolving', that.uid);
- resolved = true;
- resolve(complete_records);
- };
-
- console.log("got complete records", that.uid);
- if (is_handshake){
- finished_receiving();
- return;
- }
- else {
- console.log("in recv waiting for an extra second", that.uid);
- buf = [];
- //give the server another second to send more data
- setTimeout(function(){
- if (that.buffer.length === 0){
- finished_receiving();
- return;
- }
- else {
- console.log('more data received after waiting for a second', that.uid);
- check();
- }
- },1000);
- }
- }
- };
- check();
- });
-};
-
-
-function check_complete_records(d){
- /*'''Given a response d from a server,
- we want to know if its contents represents
- a complete set of records, however many.'''
- */
- var complete_records = [];
- var incomplete_records = [];
-
- while(d){
- if (d.length < 5){
- return {'is_complete':false, 'comprecs':complete_records, 'incomprecs':d};
- }
- var l = ba2int(d.slice(3,5));
- if (d.length < (l+5)){
- return {'is_complete':false, 'comprecs':complete_records, 'incomprecs':d};
- }
- else if(d.length === (l+5)){
- return {'is_complete':true, 'comprecs':[].concat(complete_records, d)};
- }
- else {
- complete_records = [].concat(complete_records, d.slice(0, l+5));
- d = d.slice(l+5);
- continue;
- }
- }
-}
\ No newline at end of file
diff --git a/content/sweetalert.css b/content/sweetalert.css
deleted file mode 100644
index bc588bc..0000000
--- a/content/sweetalert.css
+++ /dev/null
@@ -1,757 +0,0 @@
-body.stop-scrolling {
- height: 100%;
- overflow: hidden; }
-
-.sweet-overlay {
- background-color: black;
- /* IE8 */
- -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=40)";
- /* IE8 */
- background-color: rgba(0, 0, 0, 0.4);
- position: fixed;
- left: 0;
- right: 0;
- top: 0;
- bottom: 0;
- display: none;
- z-index: 10000; }
-
-.sweet-alert {
- background-color: white;
- font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
- width: 478px;
- padding: 17px;
- border-radius: 5px;
- text-align: center;
- position: fixed;
- left: 50%;
- top: 50%;
- margin-left: -256px;
- margin-top: -200px;
- overflow: hidden;
- display: none;
- z-index: 99999; }
- @media all and (max-width: 540px) {
- .sweet-alert {
- width: auto;
- margin-left: 0;
- margin-right: 0;
- left: 15px;
- right: 15px; } }
- .sweet-alert h2 {
- color: #575757;
- font-size: 30px;
- text-align: center;
- font-weight: 600;
- text-transform: none;
- position: relative;
- margin: 25px 0;
- padding: 0;
- line-height: 40px;
- display: block; }
- .sweet-alert p {
- color: #797979;
- font-size: 16px;
- text-align: center;
- font-weight: 300;
- position: relative;
- text-align: inherit;
- float: none;
- margin: 0;
- padding: 0;
- line-height: normal; }
- .sweet-alert fieldset {
- border: none;
- position: relative; }
- .sweet-alert .sa-error-container {
- background-color: #f1f1f1;
- margin-left: -17px;
- margin-right: -17px;
- overflow: hidden;
- padding: 0 10px;
- max-height: 0;
- webkit-transition: padding 0.15s, max-height 0.15s;
- transition: padding 0.15s, max-height 0.15s; }
- .sweet-alert .sa-error-container.show {
- padding: 10px 0;
- max-height: 100px;
- webkit-transition: padding 0.2s, max-height 0.2s;
- transition: padding 0.25s, max-height 0.25s; }
- .sweet-alert .sa-error-container .icon {
- display: inline-block;
- width: 24px;
- height: 24px;
- border-radius: 50%;
- background-color: #ea7d7d;
- color: white;
- line-height: 24px;
- text-align: center;
- margin-right: 3px; }
- .sweet-alert .sa-error-container p {
- display: inline-block; }
- .sweet-alert .sa-input-error {
- position: absolute;
- top: 29px;
- right: 26px;
- width: 20px;
- height: 20px;
- opacity: 0;
- -webkit-transform: scale(0.5);
- transform: scale(0.5);
- -webkit-transform-origin: 50% 50%;
- transform-origin: 50% 50%;
- -webkit-transition: all 0.1s;
- transition: all 0.1s; }
- .sweet-alert .sa-input-error::before, .sweet-alert .sa-input-error::after {
- content: "";
- width: 20px;
- height: 6px;
- background-color: #f06e57;
- border-radius: 3px;
- position: absolute;
- top: 50%;
- margin-top: -4px;
- left: 50%;
- margin-left: -9px; }
- .sweet-alert .sa-input-error::before {
- -webkit-transform: rotate(-45deg);
- transform: rotate(-45deg); }
- .sweet-alert .sa-input-error::after {
- -webkit-transform: rotate(45deg);
- transform: rotate(45deg); }
- .sweet-alert .sa-input-error.show {
- opacity: 1;
- -webkit-transform: scale(1);
- transform: scale(1); }
- .sweet-alert input {
- width: 100%;
- box-sizing: border-box;
- border-radius: 3px;
- border: 1px solid #d7d7d7;
- height: 43px;
- margin-top: 10px;
- margin-bottom: 17px;
- font-size: 18px;
- box-shadow: inset 0px 1px 1px rgba(0, 0, 0, 0.06);
- padding: 0 12px;
- display: none;
- -webkit-transition: all 0.3s;
- transition: all 0.3s; }
- .sweet-alert input:focus {
- outline: none;
- box-shadow: 0px 0px 3px #c4e6f5;
- border: 1px solid #b4dbed; }
- .sweet-alert input:focus::-moz-placeholder {
- transition: opacity 0.3s 0.03s ease;
- opacity: 0.5; }
- .sweet-alert input:focus:-ms-input-placeholder {
- transition: opacity 0.3s 0.03s ease;
- opacity: 0.5; }
- .sweet-alert input:focus::-webkit-input-placeholder {
- transition: opacity 0.3s 0.03s ease;
- opacity: 0.5; }
- .sweet-alert input::-moz-placeholder {
- color: #bdbdbd; }
- .sweet-alert input:-ms-input-placeholder {
- color: #bdbdbd; }
- .sweet-alert input::-webkit-input-placeholder {
- color: #bdbdbd; }
- .sweet-alert.show-input input {
- display: block; }
- .sweet-alert button {
- background-color: #AEDEF4;
- color: white;
- border: none;
- box-shadow: none;
- font-size: 17px;
- font-weight: 500;
- -webkit-border-radius: 4px;
- border-radius: 5px;
- padding: 10px 32px;
- margin: 26px 5px 0 5px;
- cursor: pointer; }
- .sweet-alert button:focus {
- outline: none;
- box-shadow: 0 0 2px rgba(128, 179, 235, 0.5), inset 0 0 0 1px rgba(0, 0, 0, 0.05); }
- .sweet-alert button:hover {
- background-color: #a1d9f2; }
- .sweet-alert button:active {
- background-color: #81ccee; }
- .sweet-alert button.cancel {
- background-color: #D0D0D0; }
- .sweet-alert button.cancel:hover {
- background-color: #c8c8c8; }
- .sweet-alert button.cancel:active {
- background-color: #b6b6b6; }
- .sweet-alert button.cancel:focus {
- box-shadow: rgba(197, 205, 211, 0.8) 0px 0px 2px, rgba(0, 0, 0, 0.0470588) 0px 0px 0px 1px inset !important; }
- .sweet-alert button::-moz-focus-inner {
- border: 0; }
- .sweet-alert[data-has-cancel-button=false] button {
- box-shadow: none !important; }
- .sweet-alert[data-has-confirm-button=false][data-has-cancel-button=false] {
- padding-bottom: 40px; }
- .sweet-alert .sa-icon {
- width: 80px;
- height: 80px;
- border: 4px solid gray;
- -webkit-border-radius: 40px;
- border-radius: 40px;
- border-radius: 50%;
- margin: 20px auto;
- padding: 0;
- position: relative;
- box-sizing: content-box; }
- .sweet-alert .sa-icon.sa-error {
- border-color: #F27474; }
- .sweet-alert .sa-icon.sa-error .sa-x-mark {
- position: relative;
- display: block; }
- .sweet-alert .sa-icon.sa-error .sa-line {
- position: absolute;
- height: 5px;
- width: 47px;
- background-color: #F27474;
- display: block;
- top: 37px;
- border-radius: 2px; }
- .sweet-alert .sa-icon.sa-error .sa-line.sa-left {
- -webkit-transform: rotate(45deg);
- transform: rotate(45deg);
- left: 17px; }
- .sweet-alert .sa-icon.sa-error .sa-line.sa-right {
- -webkit-transform: rotate(-45deg);
- transform: rotate(-45deg);
- right: 16px; }
- .sweet-alert .sa-icon.sa-warning {
- border-color: #F8BB86; }
- .sweet-alert .sa-icon.sa-warning .sa-body {
- position: absolute;
- width: 5px;
- height: 47px;
- left: 50%;
- top: 10px;
- -webkit-border-radius: 2px;
- border-radius: 2px;
- margin-left: -2px;
- background-color: #F8BB86; }
- .sweet-alert .sa-icon.sa-warning .sa-dot {
- position: absolute;
- width: 7px;
- height: 7px;
- -webkit-border-radius: 50%;
- border-radius: 50%;
- margin-left: -3px;
- left: 50%;
- bottom: 10px;
- background-color: #F8BB86; }
- .sweet-alert .sa-icon.sa-info {
- border-color: #C9DAE1; }
- .sweet-alert .sa-icon.sa-info::before {
- content: "";
- position: absolute;
- width: 5px;
- height: 29px;
- left: 50%;
- bottom: 17px;
- border-radius: 2px;
- margin-left: -2px;
- background-color: #C9DAE1; }
- .sweet-alert .sa-icon.sa-info::after {
- content: "";
- position: absolute;
- width: 7px;
- height: 7px;
- border-radius: 50%;
- margin-left: -3px;
- top: 19px;
- background-color: #C9DAE1; }
- .sweet-alert .sa-icon.sa-success {
- border-color: #A5DC86; }
- .sweet-alert .sa-icon.sa-success::before, .sweet-alert .sa-icon.sa-success::after {
- content: '';
- -webkit-border-radius: 40px;
- border-radius: 40px;
- border-radius: 50%;
- position: absolute;
- width: 60px;
- height: 120px;
- background: white;
- -webkit-transform: rotate(45deg);
- transform: rotate(45deg); }
- .sweet-alert .sa-icon.sa-success::before {
- -webkit-border-radius: 120px 0 0 120px;
- border-radius: 120px 0 0 120px;
- top: -7px;
- left: -33px;
- -webkit-transform: rotate(-45deg);
- transform: rotate(-45deg);
- -webkit-transform-origin: 60px 60px;
- transform-origin: 60px 60px; }
- .sweet-alert .sa-icon.sa-success::after {
- -webkit-border-radius: 0 120px 120px 0;
- border-radius: 0 120px 120px 0;
- top: -11px;
- left: 30px;
- -webkit-transform: rotate(-45deg);
- transform: rotate(-45deg);
- -webkit-transform-origin: 0px 60px;
- transform-origin: 0px 60px; }
- .sweet-alert .sa-icon.sa-success .sa-placeholder {
- width: 80px;
- height: 80px;
- border: 4px solid rgba(165, 220, 134, 0.2);
- -webkit-border-radius: 40px;
- border-radius: 40px;
- border-radius: 50%;
- box-sizing: content-box;
- position: absolute;
- left: -4px;
- top: -4px;
- z-index: 2; }
- .sweet-alert .sa-icon.sa-success .sa-fix {
- width: 5px;
- height: 90px;
- background-color: white;
- position: absolute;
- left: 28px;
- top: 8px;
- z-index: 1;
- -webkit-transform: rotate(-45deg);
- transform: rotate(-45deg); }
- .sweet-alert .sa-icon.sa-success .sa-line {
- height: 5px;
- background-color: #A5DC86;
- display: block;
- border-radius: 2px;
- position: absolute;
- z-index: 2; }
- .sweet-alert .sa-icon.sa-success .sa-line.sa-tip {
- width: 25px;
- left: 14px;
- top: 46px;
- -webkit-transform: rotate(45deg);
- transform: rotate(45deg); }
- .sweet-alert .sa-icon.sa-success .sa-line.sa-long {
- width: 47px;
- right: 8px;
- top: 38px;
- -webkit-transform: rotate(-45deg);
- transform: rotate(-45deg); }
- .sweet-alert .sa-icon.sa-custom {
- background-size: contain;
- border-radius: 0;
- border: none;
- background-position: center center;
- background-repeat: no-repeat; }
-
-/*
- * Animations
- */
-@-webkit-keyframes showSweetAlert {
- 0% {
- transform: scale(0.7);
- -webkit-transform: scale(0.7); }
-
- 45% {
- transform: scale(1.05);
- -webkit-transform: scale(1.05); }
-
- 80% {
- transform: scale(0.95);
- -webkit-transform: scale(0.95); }
-
- 100% {
- transform: scale(1);
- -webkit-transform: scale(1); } }
-
-@keyframes showSweetAlert {
- 0% {
- transform: scale(0.7);
- -webkit-transform: scale(0.7); }
-
- 45% {
- transform: scale(1.05);
- -webkit-transform: scale(1.05); }
-
- 80% {
- transform: scale(0.95);
- -webkit-transform: scale(0.95); }
-
- 100% {
- transform: scale(1);
- -webkit-transform: scale(1); } }
-
-@-webkit-keyframes hideSweetAlert {
- 0% {
- transform: scale(1);
- -webkit-transform: scale(1); }
-
- 100% {
- transform: scale(0.5);
- -webkit-transform: scale(0.5); } }
-
-@keyframes hideSweetAlert {
- 0% {
- transform: scale(1);
- -webkit-transform: scale(1); }
-
- 100% {
- transform: scale(0.5);
- -webkit-transform: scale(0.5); } }
-
-@-webkit-keyframes slideFromTop {
- 0% {
- top: 0%; }
-
- 100% {
- top: 50%; } }
-
-@keyframes slideFromTop {
- 0% {
- top: 0%; }
-
- 100% {
- top: 50%; } }
-
-@-webkit-keyframes slideToTop {
- 0% {
- top: 50%; }
-
- 100% {
- top: 0%; } }
-
-@keyframes slideToTop {
- 0% {
- top: 50%; }
-
- 100% {
- top: 0%; } }
-
-@-webkit-keyframes slideFromBottom {
- 0% {
- top: 70%; }
-
- 100% {
- top: 50%; } }
-
-@keyframes slideFromBottom {
- 0% {
- top: 70%; }
-
- 100% {
- top: 50%; } }
-
-@-webkit-keyframes slideToBottom {
- 0% {
- top: 50%; }
-
- 100% {
- top: 70%; } }
-
-@keyframes slideToBottom {
- 0% {
- top: 50%; }
-
- 100% {
- top: 70%; } }
-
-.showSweetAlert[data-animation=pop] {
- -webkit-animation: showSweetAlert 0.3s;
- animation: showSweetAlert 0.3s; }
-.showSweetAlert[data-animation=none] {
- -webkit-animation: none;
- animation: none; }
-.showSweetAlert[data-animation=slide-from-top] {
- -webkit-animation: slideFromTop 0.3s;
- animation: slideFromTop 0.3s; }
-.showSweetAlert[data-animation=slide-from-bottom] {
- -webkit-animation: slideFromBottom 0.3s;
- animation: slideFromBottom 0.3s; }
-
-.hideSweetAlert[data-animation=pop] {
- -webkit-animation: hideSweetAlert 0.2s;
- animation: hideSweetAlert 0.2s; }
-.hideSweetAlert[data-animation=none] {
- -webkit-animation: none;
- animation: none; }
-.hideSweetAlert[data-animation=slide-from-top] {
- -webkit-animation: slideToTop 0.4s;
- animation: slideToTop 0.4s; }
-.hideSweetAlert[data-animation=slide-from-bottom] {
- -webkit-animation: slideToBottom 0.3s;
- animation: slideToBottom 0.3s; }
-
-@-webkit-keyframes animateSuccessTip {
- 0% {
- width: 0;
- left: 1px;
- top: 19px; }
-
- 54% {
- width: 0;
- left: 1px;
- top: 19px; }
-
- 70% {
- width: 50px;
- left: -8px;
- top: 37px; }
-
- 84% {
- width: 17px;
- left: 21px;
- top: 48px; }
-
- 100% {
- width: 25px;
- left: 14px;
- top: 45px; } }
-
-@keyframes animateSuccessTip {
- 0% {
- width: 0;
- left: 1px;
- top: 19px; }
-
- 54% {
- width: 0;
- left: 1px;
- top: 19px; }
-
- 70% {
- width: 50px;
- left: -8px;
- top: 37px; }
-
- 84% {
- width: 17px;
- left: 21px;
- top: 48px; }
-
- 100% {
- width: 25px;
- left: 14px;
- top: 45px; } }
-
-@-webkit-keyframes animateSuccessLong {
- 0% {
- width: 0;
- right: 46px;
- top: 54px; }
-
- 65% {
- width: 0;
- right: 46px;
- top: 54px; }
-
- 84% {
- width: 55px;
- right: 0px;
- top: 35px; }
-
- 100% {
- width: 47px;
- right: 8px;
- top: 38px; } }
-
-@keyframes animateSuccessLong {
- 0% {
- width: 0;
- right: 46px;
- top: 54px; }
-
- 65% {
- width: 0;
- right: 46px;
- top: 54px; }
-
- 84% {
- width: 55px;
- right: 0px;
- top: 35px; }
-
- 100% {
- width: 47px;
- right: 8px;
- top: 38px; } }
-
-@-webkit-keyframes rotatePlaceholder {
- 0% {
- transform: rotate(-45deg);
- -webkit-transform: rotate(-45deg); }
-
- 5% {
- transform: rotate(-45deg);
- -webkit-transform: rotate(-45deg); }
-
- 12% {
- transform: rotate(-405deg);
- -webkit-transform: rotate(-405deg); }
-
- 100% {
- transform: rotate(-405deg);
- -webkit-transform: rotate(-405deg); } }
-
-@keyframes rotatePlaceholder {
- 0% {
- transform: rotate(-45deg);
- -webkit-transform: rotate(-45deg); }
-
- 5% {
- transform: rotate(-45deg);
- -webkit-transform: rotate(-45deg); }
-
- 12% {
- transform: rotate(-405deg);
- -webkit-transform: rotate(-405deg); }
-
- 100% {
- transform: rotate(-405deg);
- -webkit-transform: rotate(-405deg); } }
-
-.animateSuccessTip {
- -webkit-animation: animateSuccessTip 0.75s;
- animation: animateSuccessTip 0.75s; }
-
-.animateSuccessLong {
- -webkit-animation: animateSuccessLong 0.75s;
- animation: animateSuccessLong 0.75s; }
-
-.sa-icon.sa-success.animate::after {
- -webkit-animation: rotatePlaceholder 4.25s ease-in;
- animation: rotatePlaceholder 4.25s ease-in; }
-
-@-webkit-keyframes animateErrorIcon {
- 0% {
- transform: rotateX(100deg);
- -webkit-transform: rotateX(100deg);
- opacity: 0; }
-
- 100% {
- transform: rotateX(0deg);
- -webkit-transform: rotateX(0deg);
- opacity: 1; } }
-
-@keyframes animateErrorIcon {
- 0% {
- transform: rotateX(100deg);
- -webkit-transform: rotateX(100deg);
- opacity: 0; }
-
- 100% {
- transform: rotateX(0deg);
- -webkit-transform: rotateX(0deg);
- opacity: 1; } }
-
-.animateErrorIcon {
- -webkit-animation: animateErrorIcon 0.5s;
- animation: animateErrorIcon 0.5s; }
-
-@-webkit-keyframes animateXMark {
- 0% {
- transform: scale(0.4);
- -webkit-transform: scale(0.4);
- margin-top: 26px;
- opacity: 0; }
-
- 50% {
- transform: scale(0.4);
- -webkit-transform: scale(0.4);
- margin-top: 26px;
- opacity: 0; }
-
- 80% {
- transform: scale(1.15);
- -webkit-transform: scale(1.15);
- margin-top: -6px; }
-
- 100% {
- transform: scale(1);
- -webkit-transform: scale(1);
- margin-top: 0;
- opacity: 1; } }
-
-@keyframes animateXMark {
- 0% {
- transform: scale(0.4);
- -webkit-transform: scale(0.4);
- margin-top: 26px;
- opacity: 0; }
-
- 50% {
- transform: scale(0.4);
- -webkit-transform: scale(0.4);
- margin-top: 26px;
- opacity: 0; }
-
- 80% {
- transform: scale(1.15);
- -webkit-transform: scale(1.15);
- margin-top: -6px; }
-
- 100% {
- transform: scale(1);
- -webkit-transform: scale(1);
- margin-top: 0;
- opacity: 1; } }
-
-.animateXMark {
- -webkit-animation: animateXMark 0.5s;
- animation: animateXMark 0.5s; }
-
-@-webkit-keyframes pulseWarning {
- 0% {
- border-color: #F8D486; }
-
- 100% {
- border-color: #F8BB86; } }
-
-@keyframes pulseWarning {
- 0% {
- border-color: #F8D486; }
-
- 100% {
- border-color: #F8BB86; } }
-
-.pulseWarning {
- -webkit-animation: pulseWarning 0.75s infinite alternate;
- animation: pulseWarning 0.75s infinite alternate; }
-
-@-webkit-keyframes pulseWarningIns {
- 0% {
- background-color: #F8D486; }
-
- 100% {
- background-color: #F8BB86; } }
-
-@keyframes pulseWarningIns {
- 0% {
- background-color: #F8D486; }
-
- 100% {
- background-color: #F8BB86; } }
-
-.pulseWarningIns {
- -webkit-animation: pulseWarningIns 0.75s infinite alternate;
- animation: pulseWarningIns 0.75s infinite alternate; }
-
-/* Internet Explorer 9 has some special quirks that are fixed here */
-/* The icons are not animated. */
-/* This file is automatically merged into sweet-alert.min.js through Gulp */
-/* Error icon */
-.sweet-alert .sa-icon.sa-error .sa-line.sa-left {
- -ms-transform: rotate(45deg) \9; }
-
-.sweet-alert .sa-icon.sa-error .sa-line.sa-right {
- -ms-transform: rotate(-45deg) \9; }
-
-/* Success icon */
-.sweet-alert .sa-icon.sa-success {
- border-color: transparent\9; }
-
-.sweet-alert .sa-icon.sa-success .sa-line.sa-tip {
- -ms-transform: rotate(45deg) \9; }
-
-.sweet-alert .sa-icon.sa-success .sa-line.sa-long {
- -ms-transform: rotate(-45deg) \9; }
diff --git a/content/sweetalert_listener.js b/content/sweetalert_listener.js
deleted file mode 100644
index e9857b8..0000000
--- a/content/sweetalert_listener.js
+++ /dev/null
@@ -1,4 +0,0 @@
-chrome.runtime.onMessage.addListener(function(data){
- if (data.destination !== 'sweetalert') return;
- swal(data.args);
- });
diff --git a/content/testdriver.js b/content/testdriver.js
deleted file mode 100644
index 3bec630..0000000
--- a/content/testdriver.js
+++ /dev/null
@@ -1,62 +0,0 @@
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-var testing = false;
-
-var prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefService).getBranch("");
-prefs.setBoolPref("browser.tabs.warnOnCloseOtherTabs", false);
-
-var wslist = [];
-
-function startTesting(){
- //var wslist_path = thisaddon.getResourceURI("content/websitelist.txt").path;
- return OS.File.read('/tmp/websitelist.txt', { encoding: "utf-8" }).then(
- function onSuccess(text) {
- wslist = text.split('\n'); // text is a string
- openNextLink();
- }
- );
-}
-
-function openNextLink(){
- while(true){
- var wslist_index = (Math.random() * wslist.length) << 0;
- if (! (wslist[wslist_index].startsWith('#') ||
- wslist[wslist_index] === '')){
- break;
- }
- }
- var url = wslist[wslist_index];
- console.log('test: opening new tab', url, wslist.length, wslist_index);
- //main.js needs some time to open tlsn tab first
- setTimeout(function(){
- var auditeeBrowser = gBrowser.addTab(url);
- gBrowser.addProgressListener(tlsnLoadListener);
- gBrowser.removeAllTabsBut(auditeeBrowser);
- }, 20*1000); //dont violate rate-limiting of 15 connections per minute.
- return;
- //remove tabs ~ every 5th run. We dont want tabs immediately closed so we could examine html while the test is running
- if ((Math.random()*5 << 0) === 4){
- gBrowser.removeAllTabsBut(auditeeBrowser);
- }
-}
-
-
-//wait for the page to become secure before we press AUDIT
-var tlsnLoadListener = {
- QueryInterface: XPCOMUtils.generateQI(["nsIWebProgressListener",
- "nsISupportsWeakReference"]),
-
- onStateChange: function(aWebProgress, aRequest, aFlag, aStatus) {},
- onLocationChange: function(aProgress, aRequest, aURI) {},
- onProgressChange: function(aWebProgress, aRequest, curSelf, maxSelf, curTot, maxTot) {},
- onStatusChange: function(aWebProgress, aRequest, aStatus, aMessage) {},
- onSecurityChange: function(aWebProgress, aRequest, aState)
- {
- // check if the state is secure or not
- if(aState & Ci.nsIWebProgressListener.STATE_IS_SECURE)
- {
- gBrowser.removeProgressListener(this);
- //begin recording as soon as the page turns into https
- startNotarizing(openNextLink);
- }
- }
-}
diff --git a/content/testing/manager_test.js b/content/testing/manager_test.js
deleted file mode 100644
index f12018f..0000000
--- a/content/testing/manager_test.js
+++ /dev/null
@@ -1 +0,0 @@
-//Do not remove this empty file
diff --git a/content/testing/testing.js b/content/testing/testing.js
deleted file mode 100644
index 411f0d0..0000000
--- a/content/testing/testing.js
+++ /dev/null
@@ -1,2 +0,0 @@
-//This is an empty file. Browsers expect to find this file in content/testing/testing.js
-//When running a testsuite, this file will be replaced
diff --git a/content/tlsn.js b/content/tlsn.js
deleted file mode 100644
index 16db333..0000000
--- a/content/tlsn.js
+++ /dev/null
@@ -1,2013 +0,0 @@
-var is_chrome = navigator.userAgent.toLowerCase().indexOf('chrome') > -1;
-if (!is_chrome){
- if (typeof(win) !== "undefined"){
- var window = win;
- }
-}
-
-var global_tlsver = [0x03, 0x02];
-
-
-//#constants
-var md5_hash_len = 16;
-var sha1_hash_len = 20;
-var aes_block_size = 16;
-var tls_ver_1_0 = [3,1];
-var tls_ver_1_1 = [3,2];
-var tls_versions = [tls_ver_1_0,tls_ver_1_1];
-//#record types
-var appd = 0x17; //#Application Data
-var hs = 0x16; //#Handshake
-var chcis = 0x14; //#Change Cipher Spec
-var alrt = 0x15; //#Alert
-var tls_record_types = [appd,hs,chcis,alrt];
-//#handshake types
-var h_ch = 0x01; //#Client Hello
-var h_sh = 0x02; //#Server Hello
-var h_cert = 0x0b; //#Certificate
-var h_shd = 0x0e; //#Server Hello Done
-var h_cke = 0x10; //#Client Key Exchange
-var h_fin = 0x14; //#Finished
-var tls_handshake_types = [h_ch,h_sh,h_cert,h_shd,h_cke,h_fin];
-
-
-/*
-The amount of key material for each ciphersuite:
-AES256-CBC-SHA: mac key 20*2, encryption key 32*2, IV 16*2 == 136bytes
-AES128-CBC-SHA: mac key 20*2, encryption key 16*2, IV 16*2 == 104bytes
-RC4128_SHA: mac key 20*2, encryption key 16*2 == 72bytes
-RC4128_MD5: mac key 16*2, encryption key 16*2 == 64 bytes
-*/
-
-var tlsn_cipher_suites = [ {47:['AES128',20,20,16,16,16,16]},
- {53:['AES256',20,20,32,32,16,16]},
- {5:['RC4SHA',20,20,16,16,0,0]},
- {4:['RC4MD5',16,16,16,16,0,0]} ];
-//#preprocessing: add the total number of bytes in the expanded keys format
-//#for each cipher suite, for ease of reference
-for(var i=0; i < tlsn_cipher_suites.length; i++){
- var key = Object.keys(tlsn_cipher_suites[i])[0];
- var sum = 0;
- var values = tlsn_cipher_suites[i][key];
- for (var j=1; j -1, "Invalid handshake type");
- constructed_obj = new hs_type_map[plaintext[0]]();
- constructed_obj.__init__({'serialized':plaintext});
- }
- else if (t === appd){
- constructed_obj = new TLSAppData();
- constructed_obj.__init__(plaintext);
- }
- else if (t === alrt){
- constructed_obj = new TLSAlert();
- constructed_obj.__init__({'serialized':plaintext});
- }
- else if (t === chcis){
- constructed_obj = new TLSChangeCipherSpec();
- constructed_obj.__init__({'serialized':plaintext});
- }
- else{
- throw("Invalid record type");
- }
- hlpos.push(constructed_obj);
- plaintext = constructed_obj.discarded;
- }
- if (conn){
- //#Note this assumes that only ONE encrypted message
- hlpos[0].encrypted = d;
- if (ignore_mac){
- hlpos[0].recorded_mac = mac;
- }
- }
- return hlpos;
-}
-
-
-function TLSRecord(){}
-TLSRecord.prototype.__init__ = function(ct, fragment, tlsver){
- this.content_type = ct;
- this.content_version = tlsver;
- if (fragment){
- this.fragment = fragment;
- this._length = this.fragment.length;
- this.serialize();
- }
-};
-TLSRecord.prototype.serialize = function(){
- var check_contents = this.content_type && this.content_version && this._length && this.fragment;
- assert(check_contents, "Cannot serialize record, data incomplete");
- assert(this.fragment.length == this._length, "Incorrect record length");
- this.serialized = [].concat(this.content_type, this.content_version, bi2ba(this._length, {'fixed':2}), this.fragment);
-};
-
-
-
-function TLSHandshake(){}
-TLSHandshake.prototype.__init__ = function(serialized, handshake_type){
- if (typeof(serialized)==='undefined') serialized = null;
- if (typeof(handshake_type)==='undefined') handshake_type = null;
- this.handshake_type = handshake_type;
- if (serialized){
- this.serialized = serialized;
- assert(this.handshake_type == this.serialized[0], "Mismatched handshake type");
- assert([h_ch,h_sh,h_shd,h_cert,h_cke,h_fin].indexOf(this.handshake_type) > -1,
- 'Unrecognized or unimplemented handshake type');
- this.handshake_record_length = ba2int(this.serialized.slice(1,4));
- if (this.serialized.slice(4).length < this.handshake_record_length){
- throw ('Invalid handshake message length');
- }
- this.discarded = this.serialized.slice(4+this.handshake_record_length);
- /*if (this.discarded){
- log ('Info: got a discarded data when constructing',
- 'a handshake message of type: ', this.handshake_type.toString(),
- ' and discarded length was: ', this.discarded.length);
- }
- * */
- //#Note that we do *not* strip handshake headers for the serialized form;
- //#this is a complete, valid handshake message.
- this.serialized = this.serialized.slice(0,4+this.handshake_record_length);
- }
-};
-TLSHandshake.prototype.serialize = function(){
- if (typeof(this.serialized) === "undefined"){
- this.serialized = [];
- }
- var len = bi2ba(this.serialized.length, {'fixed':3});
- this.serialized = [].concat(this.handshake_type, len, this.serialized);
-};
-
-
-
-//inheritance
-TLSClientHello.prototype = new TLSHandshake();
-TLSClientHello.prototype.constructor=TLSClientHello;
-TLSClientHello.prototype.parent = TLSHandshake.prototype;
-function TLSClientHello(){}
-TLSClientHello.prototype.__init__ = function(args){
- var serialized = args.serialized;
- var client_random = args.client_random;
- var cipher_suites = args.cipher_suites;
- var tlsver = args.tlsver;
- var i;
- this.typename = "TLSClientHello";
- //TODO default args here
- if (serialized){
- print ('Not implemented instantiation of client hello',
- 'with serialization; this is a client-only',
- ' TLS implementation');
- }
- else {
- if (client_random){
- this.client_random = client_random;
- }
- else {
- var cr = [];
- for(i=0; i<32; i++){cr.push(2);}
- //this.client_random = cr;
- this.client_random = getRandom(32, window);
-
- }
- this.tlsver = tlsver;
- this.serialized = [].concat(this.tlsver, this.client_random, 0);
- this.cipher_suites = cipher_suites;
- this.serialized = [].concat(this.serialized, 0x00, 2*this.cipher_suites.length);
- for (i=0; i -1,
- 'Server chosen cipher suite not in TLS Notary allowed list, it was: '+this.cipher_suite.toString());
- assert(remainder.slice(2).toString() === [0x00].toString(), 'Received invalid server hello compression method');
- //#At end of serialized instantiation, we have defined server
- //#random and cipher suite
- }
- else {
- print ("Not implemented instantiation of server hello without serialization; this is a client-only TLS implementation");
- }
-};
-
-
-
-TLSCertificate.prototype = new TLSHandshake();
-TLSCertificate.prototype.constructor=TLSCertificate;
-TLSServerHello.prototype.parent = TLSHandshake.prototype;
-function TLSCertificate(){}
-TLSCertificate.prototype.__init__ = function(args){
- var serialized = args.serialized;
- if (serialized){
- TLSHandshake.prototype.__init__.call(this, serialized, h_cert);
- /*#This handshake message has format: hs_cert(1), hs_msg_len(3),
- #certs_list_msg_len(3), [cert1_msg_len(3), cert1, cert_msg_len(3), cert2...]
- #so the first cert data starts at byte position 10 */
- this.cert_len = ba2int(this.serialized.slice(7,10));
- this.asn1cert = this.serialized.slice(10,10+this.cert_len);
- var listlen = ba2int(this.serialized.slice(4,7))
- assert(this.serialized.length === listlen+7);
- var offset = 7;
- var certlen;
- var cert;
- var chain = [];
- while (offset < this.serialized.length-1){
- certlen = ba2int(this.serialized.slice(offset, offset+3));
- offset += 3;
- cert = this.serialized.slice(offset, offset+certlen);
- offset += certlen;
- chain.push(cert);
- }
- this.chain = chain;
- this.typename = "TLSCertificate";
- }
-
- else {
- print ('Not implemented instantiation of certificate without serialization; this is a client-only TLS implementation');
- }
-};
-
-
-
-TLSServerHelloDone.prototype = new TLSHandshake();
-TLSServerHelloDone.prototype.constructor=TLSServerHelloDone;
-TLSServerHelloDone.prototype.parent = TLSHandshake.prototype;
-function TLSServerHelloDone(){}
-TLSServerHelloDone.prototype.__init__ = function(args){
- var serialized = args.serialized;
- this.typename = "TLSServerHelloDone";
- if (serialized){
- //call parent method in the context of this instance
- TLSHandshake.prototype.__init__.call(this, serialized, h_shd);
- }
- else {
- print ('Not implemented instantiation of server hello done without serialization; this is a client-only TLS implementation');
- }
-};
-
-
-TLSClientKeyExchange.prototype = new TLSHandshake();
-TLSClientKeyExchange.prototype.constructor=TLSClientKeyExchange;
-TLSClientKeyExchange.prototype.parent = TLSHandshake.prototype;
-function TLSClientKeyExchange(){}
-TLSClientKeyExchange.prototype.__init__ = function(args){
- var serialized = null;
- var encryptedPMS = null;
- if(typeof(args) !== "undefined"){
- if(typeof(args.serialized) !== "undefined"){
- serialized = args.serialized;
- }
- if(typeof(args.encryptedPMS) !== "undefined"){
- encryptedPMS = args.encryptedPMS;
- }
-
- }
- this.typename = "TLSClientKeyExchange";
- if (serialized){
- print ('Not implemented instantiation of client key exchange with serialization; this is a client-only TLS implementation');
- }
- else {
- if (encryptedPMS){
- this.encryptedPMS = encryptedPMS;
- }
- //#Note that the encpms is preceded by its 2-byte length
- this.serialized = [].concat(bi2ba(this.encryptedPMS.length, {'fixed':2}), this.encryptedPMS);
- TLSHandshake.prototype.__init__.call(this, null, h_cke);
- TLSHandshake.prototype.serialize.call(this);
- }
-};
-
-
-function TLSChangeCipherSpec(){}
-TLSChangeCipherSpec.prototype.__init__ = function(args){
- var serialized = null;
- if (typeof(args) !== "undefined"){
- if (typeof(args.serialized) !== "undefined"){
- serialized = args.serialized;
- }
- }
- if (serialized){
- this.serialized = serialized;
- assert(this.serialized[0] == 0x01, 'Invalid change cipher spec received');
- this.discarded = this.serialized.slice(1);
- this.serialized = this.serialized[0];
- }
- else {
- this.serialized = [0x01];
- }
- this.typename = "TLSChangeCipherSpec";
-};
-
-
-
-TLSFinished.prototype = new TLSHandshake();
-TLSFinished.prototype.constructor=TLSFinished;
-TLSFinished.prototype.parent = TLSHandshake.prototype;
-function TLSFinished(){}
-TLSFinished.prototype.__init__ = function(args){
- var serialized = null;
- var verify_data = null;
- this.typename = "TLSFinished";
- if (typeof(args) !== "undefined"){
- if (typeof(args.serialized) !== "undefined"){
- serialized = args.serialized;
- }
- if (typeof(args.verify_data) !== "undefined"){
- verify_data = args.verify_data;
- }
- }
- if (serialized){
- TLSHandshake.prototype.__init__.call(this, serialized, h_fin);
- this.validity = null;
- this.verify_data = this.serialized.slice(4);
- }
- else{
- this.serialized = verify_data;
- TLSHandshake.prototype.__init__.call(this, null, h_fin);
- TLSHandshake.prototype.serialize.call(this);
- }
-};
-TLSFinished.prototype.decrypt_verify_data = function(conn){
- this.encrypted = this.verify_data; //#the encrypted form is kept for later processing
- var rv = conn.dtvm(this.verify_data, hs);
- this.validity = rv[0];
- this.verify_data = rv[1];
-};
-
-
-
-function TLSAppData(){}
-TLSAppData.prototype.__init__ = function(serialized, args){
- var encrypted = false;
- if (typeof(args) !== "undefined"){
- if (typeof(args.encrypted) !== "undefined"){
- encrypted = args.encrypted;
- }
- }
- /*#App Data is 'transparent' to the Record protocol layer
- #(I borrow this slighly, ahem, opaque language from the
- #RFC Section 10). This means that there is no notion of
- #'length of an app data message'. Nor is there any meaning
- #to the concept of 'serialization' in this context, since
- #there is no structure. However the terminology is kept
- #the same as other record types, for consistency.*/
- this.serialized = serialized;
- this.discarded='';
- this.typename = "TLSAppData";
-};
-TLSAppData.prototype.decrypt_app_data = function(conn){
- this.serialized = conn.dtvm(this.serialized, appd);
-};
-
-
-
-function TLSAlert(){}
-TLSAlert.prototype.__init__ = function(args){
- var serialized = args.serialized;
- if (serialized){
- this.serialized = serialized;
- this.discarded='';
- this.typename = "TLSAlert";
- }
- else{
- throw ("Alert creation not implemented");
- }
-};
-
-
-
-
-function TLSConnectionState(){
- /*Note that this implementation of connection
- state uses the pre-computed expanded keys rather
- than generating the secrets within it. A corollary
- of this is that there is no need for this encapsulation
- for the unencrypted portion of the TLS connection, and
- so this object is only initiated once TLSNotary key
- expansion is performed (after negotiation with auditor).
- Mac failures should be treated as fatal in TLS, but
- for specific cases in TLSNotary, the mac check is delayed,
- hence mac failure is returned as False rather than raising
- an exception.*/
-}
-TLSConnectionState.prototype.__init__ = function(cipher_suite, expanded_keys, is_client, tlsver){
- /*Provide the cipher suite as defined in the global
- cipher suite list.
- Currently only AES-CBC and RC4 cipher suites are
- supported.
- The format of expanded_keys must be as required
- by the specified cipher suite.
- If mac failures occur they will be flagged but
- decrypted result is still made available.*/
- this.tlsver = tlsver; //either TLS1.0 or 1.1
- var version_found = false;
- for (var i=0; i -1){
- return this.rc4_me(cleartext,rec_type);
- }
- else {
- return this.aes_cbc_mpe(cleartext,rec_type);
- }
-};
-TLSConnectionState.prototype.dtvm = function(cleartext, rec_type, return_mac){
- //'''Decrypt then verify mac'''
- if (typeof(return_mac) === "undefined"){
- return_mac = false;
- }
- var retval;
- if ([4,5].indexOf(this.cipher_suite) > -1){
- retval = this.rc4_dm(cleartext,rec_type, return_mac);
- return retval;
- }
- else {
- retval = this.aes_cbc_dum(cleartext, rec_type, return_mac);
- return retval;
- }
-};
-TLSConnectionState.prototype.verify_mac = function(cleartext, rec_type, args){
- var return_mac = false;
- if(typeof(args) !== "undefined"){
- if(typeof(args.return_mac) !== "undefined"){
- return_mac = args.return_mac;
- }
- }
- var len_wo_mac = cleartext.length-this.hash_len; //length without mac
- var received_mac = cleartext.slice(len_wo_mac);
- var check_mac = this.build_record_mac(cleartext.slice(0, len_wo_mac), rec_type);
- this.seq_no += 1;
- var validity = false;
- if (return_mac){
- validity = (received_mac.toString() === check_mac.toString());
- return [validity, cleartext.slice(0, len_wo_mac), received_mac];
- }
- else {
- validity = (received_mac.toString() === check_mac.toString());
- return [validity, cleartext.slice(0, len_wo_mac)];
- }
-};
-TLSConnectionState.prototype.rc4_me = function(cleartext, rec_type){
- //#mac
- cleartext = [].concat(cleartext, this.build_record_mac(cleartext,rec_type));
- //#encrypt
- //#note: for RC4, the 'IV' is None at the start,
- //#which tells the RC4 to initialize state
- var rv = rc4_crypt(cleartext, this.enc_key, this.IV);
- var ciphertext = rv[0];
- this.IV = rv[1];
- this.seq_no += 1;
- return ciphertext;
-};
-TLSConnectionState.prototype.rc4_dm = function(ciphertext, rec_type, args){
- var return_mac = args.return_mac;
- //#decrypt
- var rv = rc4_crypt(ciphertext, this.enc_key, this.IV);
- var plaintext = rv[0];
- this.IV = rv[1];
- //#mac check
- return this.verify_mac(plaintext, rec_type, return_mac);
-};
-TLSConnectionState.prototype.aes_cbc_mpe = function(cleartext, rec_type){
- //#mac
- cleartext = [].concat(cleartext, this.build_record_mac(cleartext,rec_type));
- //#pad
- var padded_cleartext = [].concat(cleartext, get_cbc_padding(cleartext.length));
- var ciphertext = aes_encrypt(padded_cleartext, this.enc_key, this.IV);
- if (this.tlsver.toString() === tls_ver_1_0.toString()){
- this.IV = ciphertext.slice(ciphertext.length-aes_block_size);
- }
- else if (this.tlsver.toString() === tls_ver_1_1.toString()){
- //#the per-record IV is now sent as the start of the fragment
- ciphertext = [].concat(this.IV, ciphertext);
- this.IV = getRandom(aes_block_size, window); //#use a new, random IV for each record
- }
- this.seq_no += 1;
- return ciphertext;
-};
-TLSConnectionState.prototype.aes_cbc_dum = function(ciphertext, rec_type, return_mac){
- if(typeof(return_mac) === "undefined"){
- return_mac = false;
- }
- //#decrypt
- if (this.tlsver.toString() === tls_ver_1_1.toString()){
- this.IV = ciphertext.slice(0, aes_block_size);
- ciphertext = ciphertext.slice(aes_block_size);
- }
- //#else self.IV already stores the correct IV
- var decrypted = aes_decrypt(ciphertext, this.enc_key, this.IV);
- if (this.tlsver.toString() === tls_ver_1_0.toString()){
- this.IV = ciphertext.slice(ciphertext.length-aes_block_size);
- }
- //#unpad
- var plaintext = cbc_unpad(decrypted);
- //#mac check
- var retval = this.verify_mac(plaintext, rec_type, {'return_mac':return_mac});
- return retval;
-};
-
-
-
-
-
-function tls_sender(sckt, msg, rec_type, tlsver, conn){
- /*'''Wrap a message in a TLS Record before sending
- If conn argument provided, encrypt the payload
- before sending'''*/
- if (typeof(conn) !== "undefined" && conn !== null){
- msg = conn.mte(msg,rec_type);
- }
- var rec = new TLSRecord();
- rec.__init__(rec_type, msg, tlsver);
- return sckt.send(rec.serialized);
-}
-
-function recv_socket(sckt, is_handshake){
- return sckt.recv(sckt, is_handshake);
-}
-
-
-
-function TLSNClientSession(){}
-TLSNClientSession.prototype.__init__ = function(args){
- if (typeof(args)!=='undefined'){
- var server = args.server;
- var port = args.port;
- var ccs = args.ccs;
- var tlsver = args.tlsver;
- if (typeof(server)==='undefined') server = null;
- if (typeof(port)==='undefined') port = 443;
- if (typeof(ccs)==='undefined') ccs = null;
- if (typeof(tlsver)==='undefined') tlsver = null;
- }
-
- this.server_name = server;
- this.ssl_port = port;
- this.sckt = null;
- this.initial_tlsver = tlsver;
- //#current TLS version may be downgraded
- this.tlsver = tlsver;
- this.n_auditee_entropy = 12;
- this.n_auditor_entropy = 9;
- this.auditor_secret = null;
- this.auditee_secret = null;
- this.auditor_padding_secret = null;
- this.auditee_padding_secret = null;
- this.pms1 = null; //#auditee's
- this.pms2 = null; //#auditor's
- this.enc_first_half_pms = null;
- this.enc_second_half_pms = null;
- this.enc_pms = null;
- //#client hello, server hello, certificate, server hello done,
- //#client key exchange, change cipher spec, finished
- this.handshake_messages = [null, null, null, null, null, null, null];
- this.handshake_hash_sha = null;
- this.handshake_hash_md5 = null;
- this.p_auditor = null;
- this.p_auditee = null;
- this.master_secret_half_auditor = null;
- this.master_secret_half_auditee = null;
- this.p_master_secret_auditor = null;
- this.p_master_secret_auditee = null;
- this.server_mac_key = null;
- this.client_mac_key = null;
- this.server_enc_key = null;
- this.client_enc_key = null;
- this.serverIV = null;
- this.clientIV = null;
- this.server_certificate = null;
- this.server_modulus = null;
- this.server_exponent = 65537;
- this.server_mod_length = null;
-
- //#array of ciphertexts from each SSL record
- this.server_response_app_data=[];
-
- //#unexpected app data is defined as that received after
- //#server finished, but before client request. This will
- //#be decrypted, but not included in plaintext result.
- this.unexpected_server_app_data_count = 0;
- this.unexpected_server_app_data_raw = [];
-
- /*#the HMAC required to construct the verify data
- #for the server Finished record
- self.verify_hmac_for_server_finished = None
-
- #for certain testing cases we want to limit the
- #choice of cipher suite to 1, otherwise we use
- #the globally defined standard 4: */
- if (ccs){
- this.offered_cipher_suites = [];
- var cs = {};
- cs[ccs] = get_cs(ccs);
- this.offered_cipher_suites.push(cs);
- }
- else {
- this.offered_cipher_suites = tlsn_cipher_suites;
- }
- this.chosen_cipher_suite = ccs;
-};
-
-TLSNClientSession.prototype.dump = function(){
-//XXX implement this
-};
-
-TLSNClientSession.prototype.send_client_hello = function(){
- var offered_cs_keys = [];
- for (var i=0; i < this.offered_cipher_suites.length; i++){
- var cs = Object.keys(this.offered_cipher_suites[i])[0];
- //cast cs to int, otherwise we'll have strings
- offered_cs_keys.push(parseInt(cs));
- }
- this.client_hello = new TLSClientHello();
- this.client_hello.__init__({'cipher_suites':offered_cs_keys, 'tlsver':this.tlsver});
- this.handshake_messages[0]= this.client_hello.serialized;
-
- tls_sender(this.sckt, this.handshake_messages[0], hs, this.tlsver, null);
-}
-
-
-TLSNClientSession.prototype.get_server_hello = function(){
- /* #the handshake messages: server hello, certificate, server hello done
- #may be packed in arbitrary groupings into the TLS records, since
- #they are all the same record type (Handshake) */
-
- var handshake_objects = [];
- var sckt = this.sckt;
- return new Promise(function(resolve, reject) {
- var loop = function(resolve, reject){
- console.log('get_server_hello next iteration');
- sckt.recv(true)
- .then(function(rspns){
- console.log('returned from sckt.recv with length', rspns.length);
- var rv = tls_record_decoder(rspns);
- var records = rv[0];
- var remaining = rv[1];
- assert(remaining.length === 0, "Server sent spurious non-TLS response");
- if (records.length === 1){
- if(records[0].content_type === alrt){
- reject('Server sent alert ' + records[0].fragment.toString());
- return;
- }
- }
- for(var i=0; i < records.length; i++){
- var decoded = tls_record_fragment_decoder(hs, records[i].fragment);
- handshake_objects = [].concat(handshake_objects, decoded);
- }
- if (handshake_objects.length < 3){
- console.log('get_server_hello handshake_objects.length < 3');
- loop(resolve, reject);
- return;
- }
- //else
- resolve(handshake_objects);
- })
- .catch(function(e){
- reject(e);
- });
- };
- loop(resolve, reject);
- });
-}
-
-
-TLSNClientSession.prototype.process_server_hello = function(handshake_objects){
-
- var handshake_types=[];
- for (i=0; i= 0 && handshake_types.indexOf(h_cert) >= 0 &&
- handshake_types.indexOf(h_shd) >= 0,
- "Server failed to send server hello, certificate, server hello done");
- this.server_hello = handshake_objects[0];
- this.server_certificate = handshake_objects[1];
- this.server_hello_done = handshake_objects[2];
-
- this.handshake_messages[1] = handshake_objects[0].serialized;
- this.handshake_messages[2] = handshake_objects[1].serialized;
- this.handshake_messages[3] = handshake_objects[2].serialized;
-
- this.client_random = this.client_hello.client_random;
- this.server_random = this.server_hello.server_random;
- this.chosen_cipher_suite = this.server_hello.cipher_suite;
-
- if (this.server_hello.tlsver.toString() !== this.tlsver.toString()){
- if ((this.server_hello.tlsver.toString() === [0x03,0x01].toString()) &&
- (this.tlsver.toString() === [0x03,0x02].toString())){
- /*#server requested downgrade
- #note that this can only happen *before* a TLSConnectionState object is
- #initialised, so the tlsversion used in that object will be synchronised.
- #TODO: error checking to make sure this is the case.*/
- this.tlsver = [0x03,0x01];
- }
- else{
- throw("Failed to negotiate valid TLS version with server");
- }
- }
- //#for 'full' sessions, we can immediately precompute everything except
- //#for finished, including the handshake hashes used to calc the Finished
- if (this.enc_pms){
- this.client_key_exchange = new TLSClientKeyExchange();
- this.client_key_exchange.__init__({'serialized':null, 'encryptedPMS':this.enc_pms});
- this.change_cipher_spec = new TLSChangeCipherSpec();
- this.change_cipher_spec.__init__();
- this.handshake_messages[4] = this.client_key_exchange.serialized;
- this.handshake_messages[5] = this.change_cipher_spec.serialized;
- this.set_handshake_hashes();
- }
-};
-
-TLSNClientSession.prototype.get_verify_data_for_finished = function(args){
- var sha_verify = null;
- var md5_verify = null;
- var half = 1;
- var provided_p_value = null;
- var is_for_client = true;
- if(typeof(args.sha_verify) !== "undefined"){
- sha_verify = args.sha_verify;
- }
- if(typeof(args.md5_verify) !== "undefined"){
- md5_verify = args.md5_verify;
- }
- if(typeof(args.half) !== "undefined"){
- half = args.half;
- }
- if(typeof(args.provided_p_value) !== "undefined"){
- provided_p_value = args.provided_p_value;
- }
- if(typeof(args.is_for_client) !== "undefined"){
- is_for_client = args.is_for_client;
- }
-
- if (! (sha_verify && md5_verify)){
- sha_verify = this.handshake_hash_sha;
- md5_verify = this.handshake_hash_md5;
- }
-
- if (!provided_p_value){
- //#we calculate the verify data from the raw handshake messages
- if (this.handshake_messages.slice(0,6).indexOf(null) > -1){
- print('Here are the handshake messages: ' + this.handshake_messages.slice(0,6).toString());
- throw('Handshake data was not complete, could not calculate verify data');
- }
- var label;
- if (is_for_client){
- label = str2ba('client finished');
- }
- else {
- label = str2ba('server finished');
- }
- var seed = [].concat(md5_verify, sha_verify);
- var ms = [].concat(this.master_secret_half_auditor, this.master_secret_half_auditee);
- //#we don't store the verify data locally, just return it
- return tls_10_prf([].concat(label,seed), {'req_bytes':12,'full_secret':ms})[2];
- }
- //#we calculate based on provided hmac by the other party
- var verify_hmac = this.get_verify_hmac({'sha_verify':sha_verify, 'md5_verify':md5_verify,
- 'half':half, 'is_for_client':is_for_client});
- return xor(provided_p_value.slice(0,12), verify_hmac);
-};
-
-TLSNClientSession.prototype.set_handshake_hashes = function(args){
- var server = false;
- if (typeof(args) !== "undefined"){
- if (typeof(args.server) !== "undefined"){
- server = args.server;
- }
- }
- /* '''An obscure but important detail: the hashes used
- for the server Finished use the *unencrypted* client finished;
- in the current model this is automatic since the TLSFinished objects
- store the verify data unencrypted.'''*/
- var handshake_data = [];
- for(var i=0; i<5; i++){
- handshake_data = handshake_data.concat(this.handshake_messages[i]);
- }
- if (server){
- handshake_data = [].concat(handshake_data, this.handshake_messages[6]);// #client finished
- }
- var handshake_hash_sha = sha1(handshake_data);
- var handshake_hash_md5 = md5(handshake_data);
- if (! server){
- this.handshake_hash_sha = handshake_hash_sha;
- this.handshake_hash_md5 = handshake_hash_md5;
- }
- return [handshake_hash_sha, handshake_hash_md5];
-};
-
-TLSNClientSession.prototype.send_client_finished = function(provided_p_value){
- /* '''Creates the client finished handshake message without
- access to the master secret, but on the P-hash data provided
- by the auditor. Then receives the server ccs and finished.'''*/
- var verify_data = this.get_verify_data_for_finished({'provided_p_value':provided_p_value, 'half':2});
- this.client_finished = new TLSFinished();
- this.client_finished.__init__({'serialized':null, 'verify_data':verify_data});
- this.handshake_messages[6] = this.client_finished.serialized;
- //#Note that the three messages cannot be packed into one record;
- //#change cipher spec is *not* a handshake message
- tls_sender(this.sckt, this.handshake_messages[4], hs, this.tlsver);
- tls_sender(this.sckt, this.handshake_messages[5], chcis, this.tlsver);
- tls_sender(this.sckt, this.handshake_messages[6], hs, this.tlsver, this.client_connection_state);
-}
-
-
-TLSNClientSession.prototype.get_server_finished = function(){
- //keep recv'ing more data until we get enough records
- var that = this;
- var records = [];
- var sckt = this.sckt;
- return new Promise(function(resolve, reject) {
- var loop = function(){
- console.log('get_server_finished next iteration');
- sckt.recv(true).then(function(rspns){
- console.log('returned from sckt.recv');
- var rv = tls_record_decoder(rspns);
- var x = rv[0];
- var remaining = rv[1];
- assert(remaining.length === 0, "Server sent spurious non-TLS response");
- records = [].concat(records, x);
- if (records.length < 2){
- console.log('get_server_finished records.length < 2');
- loop();
- return;
- }
- //else
- resolve(records);
- })
- .catch(function(e){
- reject(e);
- });
- };
- loop();
- });
-}
-
-
-
-TLSNClientSession.prototype.process_server_finished = function(records){
- var i,x;
- /* #this strange-looking 'filtering' approach is based on observation
- #in practice of CCS being repeated (and possible also Finished, although I don't remember)*/
- var sccs = null;
- for(i=0; i 2){
- //#we received extra records; are they app data? if not we have bigger problems..
- for(i=0; i < records.length; i++){
- x = records[i];
- if ([chcis,hs].indexOf(x.content_type) > -1){
- continue;
- }
- if (x.content_type !== appd){
- //#this is too much; if it's an Alert or something, we give up.
- throw("Received unexpected TLS record before client request.");
- }
- //#store any app data records, in sequence, prior to processing all app data.
- this.server_response_app_data = [].concat(this.server_response_app_data, tls_record_fragment_decoder(appd,x.fragment));
- //#We have to store the raw form of these unexpected app data records, since they will
- //#be needed by auditor.
- this.unexpected_server_app_data_raw = [].concat(this.unexpected_server_app_data_raw, x.serialized);// #the full record serialization (otw bytes)
- this.unexpected_server_app_data_count += 1; //#note: each appd record contains ONE appd message
- }
- }
-};
-
-TLSNClientSession.prototype.complete_handshake = function(rsapms2){
- /* '''Called from prepare_pms(). For auditee only,
- who passes the second half of the encrypted
- PMS product (see TLSNotary.pdf under documentation).'''*/
- this.set_auditee_secret();
- this.set_master_secret_half(); //#default values means full MS created
- this.do_key_expansion();
- this.enc_second_half_pms = rsapms2;
- this.set_enc_first_half_pms();
- this.set_encrypted_pms();
- this.client_key_exchange = new TLSClientKeyExchange();
- this.client_key_exchange.__init__({'encryptedPMS':this.enc_pms});
- this.handshake_messages[4] = this.client_key_exchange.serialized;
- this.change_cipher_spec = new TLSChangeCipherSpec();
- this.change_cipher_spec.__init__();
- this.handshake_messages[5] = this.change_cipher_spec.serialized;
- this.set_handshake_hashes();
-
- var client_verify_data = this.get_verify_data_for_finished({'sha_verify':this.handshake_hash_sha,
- 'md5_verify':this.handshake_hash_md5, 'half':1});
-
- this.client_finished = new TLSFinished();
- this.client_finished.__init__({'verify_data':client_verify_data});
- this.handshake_messages[6] = this.client_finished.serialized;
- //#Note that the three messages cannot be packed into one record;
- //#change cipher spec is *not* a handshake message
- tls_sender(this.sckt, this.handshake_messages[4], hs, this.tlsver)
- tls_sender(this.sckt, this.handshake_messages[5], chcis, this.tlsver);
- tls_sender(this.sckt, this.handshake_messages[6], hs, this.tlsver, this.client_connection_state);
- return this.sckt.recv(true);
-}
-
-
-
-TLSNClientSession.prototype.set_encrypted_pms = function(){
- assert(this.enc_first_half_pms && this.enc_second_half_pms && this.server_modulus,
- 'failed to set enc_pms, first half was: ' + this.enc_first_half_pms.toString() +
- ' second half was: ' + this.enc_second_half_pms.toString() + ' modulus was: ' +
- this.server_modulus.toString());
- var bigint_pms1 = new BigInteger(ba2hex(this.enc_first_half_pms), 16);
- var bigint_pms2 = new BigInteger(ba2hex(this.enc_second_half_pms), 16);
- var bigint_mod = new BigInteger(ba2hex(this.server_modulus), 16);
- var bigint_pms = bigint_pms1.multiply(bigint_pms2).mod(bigint_mod);
- var resulthex = bigint_pms.toString(16);
- this.enc_pms = hex2ba(resulthex);
- return this.enc_pms;
-};
-
-TLSNClientSession.prototype.set_enc_first_half_pms = function(){
- assert(this.server_modulus && !this.enc_first_half_pms);
- var ones_length = 23;
- var trailing_zeroes = [];
- var i;
- for (i=0; i < (24-2-this.n_auditee_entropy); ++i){
- trailing_zeroes.push(0);
- }
- this.pms1 = [].concat(this.initial_tlsver, this.auditee_secret, trailing_zeroes);
- var ones = [];
- for (i=0; i -1, "Must provide half argument as 1 or 2");
- //#otherwise the p value must be enough to provide one half of MS
- assert(provided_p_value.length === 24, "Wrong length of P-hash value for half MS setting.");
- if (half === 1){
- this.master_secret_half_auditor = xor(this.p_auditor.slice(0,24), provided_p_value);
- return this.master_secret_half_auditor;
- }
- else{
- this.master_secret_half_auditee = xor(this.p_auditee.slice(24), provided_p_value);
- return this.master_secret_half_auditee;
- }
-};
-
-
-//------------------Not in use by the auditee. Can be removed
-TLSNClientSession.prototype.get_p_value_ms = function(ctrprty){
- /* '''Provide a list of keys that you want to 'garbageize' so as to hide
- that key from the counterparty, in the array 'garbage', each number is
- an index to that key in the cipher_suites dict
- '''*/
- var garbage = args.garbage;
- assert(this.server_random && this.client_random && this.chosen_cipher_suite,
- "server random, client random or cipher suite not set.");
- var label = str2ba('key expansion');
- var seed = [].concat(this.server_random, this.client_random);
- var chosen_cs = get_cs(this.chosen_cipher_suite);
- var expkeys_len = chosn_cs[chosen_cs.length-1];
- var i;
- if (ctrprty == 'auditor'){
- this.p_master_secret_auditor = tls_10_prf([].concat(label, seed),
- {'req_bytes':expkeys_len, 'first_half':this.master_secret_half_auditor})[0];
- }
- else{
- this.p_master_secret_auditee = tls_10_prf([].concat(label, seed),
- {'req_bytes':expkeys_len, 'second_half':this.master_secret_half_auditee})[1];
- }
-
- var tmp;
- if (ctrprty=='auditor'){
- tmp = this.p_master_secret_auditor;
- }
- else {
- tmp = this.p_master_secret_auditee;
- }
- for(var j=0; j < garbage.length; j++){
- var k = garbage[j];
- var start = 0;
- if (k==1){
- start = 0;
- }
- else{
- for(i=1; i 0,
- "Could not process the server response, no ciphertext found.");
- var plaintexts = [];
- var bad_record_mac = 0;
-
- for(var i=0; ithis.unexpected_server_app_data_count-1){
- plaintexts = [].concat(plaintexts, plaintext);
- }
- }
- return [plaintexts, bad_record_mac];
-};
-
-
-
-function get_cbc_padding(data_length){
- var req_padding = aes_block_size - data_length % aes_block_size;
- var padding = [];
- for (var i=0; i= 0; --j) {
- byteArray.push((word >> 8 * j) & 0xFF);
- }
- }
- return byteArray;
-}
-
-//CryptoJS doesnt accept bytearray input but it does accept a hexstring
-function ba2hex(bytearray){
- try{
- var hexstring = '';
- for(var i=0; i>8;
- bytes = [].concat(onebyte, bytes);
- } while ( x !== 0 );
- var padding = [];
- if (fixed){
- for(var i=0; i < fixed-bytes.length; i++){
- padding = [].concat(padding, 0x00);
- }
- }
- return [].concat(padding,bytes);
-}
-
-
-//converts string to bytearray
-function str2ba(str){
- if (typeof(str) !== "string"){
- throw("Only type string is allowed in str2ba");
- }
- ba = [];
- for(var i=0; i -1){
- //#TODO manually resend the request with compression disabled
- throw('Please set gzip_disabled = 1 in tlsnotary.ini and rerun the audit');
- }
- if (http_header.search(/content-encoding:\s.*gzip/i) === -1){
- console.log('nothing to gunzip');
- return http_data; //#nothing to gunzip
- }
- var http_body = http_data.slice(http_header.length);
- var ungzipped = http_header;
- if (!http_body){
- //HTTP 304 Not Modified has no body
- return ungzipped;
- }
- var inflated = pako.inflate(http_body);
- ungzipped += ba2str(inflated);
- return ungzipped;
-}
-
-function getTime(){
- var today = new Date();
- var time = today.getFullYear()+'-'+("00"+(today.getMonth()+1)).slice(-2)+'-'+("00"+today.getDate()).slice(-2)+'-'+ ("00"+today.getHours()).slice(-2)+'-'+("00"+today.getMinutes()).slice(-2)+'-'+("00"+today.getSeconds()).slice(-2);
- return time;
-}
diff --git a/content/websitelist b/content/websitelist
deleted file mode 100644
index a9b3b85..0000000
--- a/content/websitelist
+++ /dev/null
@@ -1,43 +0,0 @@
-https://google.com
-https://youtube.com
-https://wikipedia.org
-https://login.live.com
-https://reddit.com
-https://www.yandex.ru
-https://imgur.com
-https://paypal.com
-https://apple.com
-https://microsoft.com
-https://stackoverflow.com
-https://gmail.com
-https://netflix.com
-https://www.pinterest.com
-https://www.pnc.com/en/personal-banking.html
-https://online.citibank.com/US/JPS/portal/NoCookie.do
-https://www.wellsfargo.com
-https://www.security.us.hsbc.com
-https://www.chase.com
-https://www.bankofamerica.com
-https://www.bing.com
-https://www.mozilla.org
-https://www.vk.com
-https://www.facebook.com
-https://www.twitter.com
-https://www.wordpress.org
-https://www.linkedin.com
-https://www.yahoo.com
-https://www.flickr.com
-https://www.vimeo.com
-https://www.bawagpsk.com
-https://banking.dkb.de
-https://www.deutsche-bank.es/pbct/loginDNIe.doLoginConnection.db
-https://www.debian.org
-https://www.okpay.com/en/index.html
-https://www.delicious.com
-https://www.gov.uk
-https://www.cloudflare.com
-https://www.drupal.org
-https://www.imgur.com
-https://www.github.com
-https://www.umich.edu
-https://www.spotify.com
diff --git a/install.rdf b/install.rdf
index 050b3f4..ce4be19 100644
--- a/install.rdf
+++ b/install.rdf
@@ -5,18 +5,19 @@
pagesigner@tlsnotary
- 1.0.7.2
+ 1.1.0
2
true
true
+ true
{ec8030f7-c20a-464f-9b0e-13a3a9e97384}
- 43.0
- 45.0
+ 53.0
+ *
diff --git a/manifest.json b/manifest.json
index 3388d0d..7dace76 100644
--- a/manifest.json
+++ b/manifest.json
@@ -3,73 +3,63 @@
"name": "PageSigner",
"description": "PageSigner - a cryptographically secure webpage screenshot tool",
- "version": "1.0.7.2",
+ "version": "1.1.0",
"author": "TLSNotary Group",
- "permissions": [
+ "permissions": [
"background",
"*://*/*",
- "file:///*pagesigner.tmp.dir*",
"webRequest",
"webRequestBlocking",
"activeTab",
"tabs",
"storage",
"unlimitedStorage",
- "management",
- "downloads",
- "downloads.shelf"
+ "management"
],
-
+
"icons": {
- "64": "icon.png"
+ "64": "icon.png"
},
-
+
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",
-
+
"browser_action": {
- "default_icon": "icon.png",
- "default_popup": "content/chrome/popup.html"
+ "default_icon": "webextension/content/icon.png",
+ "default_popup": "webextension/content/popup.html"
},
- "content_scripts":[
- {
- "matches":["file:///*pagesigner.tmp.dir/manager.html"],
- "js":["content/chrome/inject_into_manager.js"]
- }
- ],
- "background": {
+ "background": {
"scripts": [
- "content/socket.js",
- "content/chrome/chrome_specific.js",
- "content/tlsn_utils.js",
- "content/oracles.js",
- "content/CryptoJS/components/core.js",
- "content/CryptoJS/components/md5.js",
- "content/CryptoJS/components/evpkdf.js",
- "content/CryptoJS/components/enc-base64.js",
- "content/CryptoJS/components/sha1.js",
- "content/CryptoJS/components/sha256.js",
- "content/CryptoJS/components/hmac.js",
- "content/CryptoJS/components/cipher-core.js",
- "content/CryptoJS/components/aes.js",
- "content/CryptoJS/components/pad-nopadding.js",
- "content/jsbn.js",
- "content/jsbn2.js",
- "content/pako.js",
- "content/tlsn.js",
- "content/main.js",
- "content/testing/testing.js",
- "content/verifychain/buffer.js2",
- "content/verifychain/asn1.js2",
- "content/verifychain/jsrsasign-latest-all-min.js2",
- "content/verifychain/rootcertslist.js",
- "content/verifychain/rootcerts.js",
- "content/verifychain/verifychain.js"
+ "webextension/content/socket.js",
+ "webextension/content/tlsn_utils.js",
+ "webextension/content/oracles.js",
+ "webextension/content/CryptoJS/components/core.js",
+ "webextension/content/CryptoJS/components/md5.js",
+ "webextension/content/CryptoJS/components/evpkdf.js",
+ "webextension/content/CryptoJS/components/enc-base64.js",
+ "webextension/content/CryptoJS/components/sha1.js",
+ "webextension/content/CryptoJS/components/sha256.js",
+ "webextension/content/CryptoJS/components/hmac.js",
+ "webextension/content/CryptoJS/components/cipher-core.js",
+ "webextension/content/CryptoJS/components/aes.js",
+ "webextension/content/CryptoJS/components/pad-nopadding.js",
+ "webextension/content/jsbn.js",
+ "webextension/content/jsbn2.js",
+ "webextension/content/pako.js",
+ "webextension/content/tlsn.js",
+ "webextension/content/main.js",
+ "webextension/content/testing/testing.js",
+ "webextension/content/verifychain/buffer.js",
+ "webextension/content/verifychain/asn1.js",
+ "webextension/content/verifychain/jsrsasign-latest-all-min.js",
+ "webextension/content/verifychain/rootcertslist.js",
+ "webextension/content/verifychain/rootcerts.js",
+ "webextension/content/verifychain/verifychain.js"
]
}
-
-
+
+
}
diff --git a/content/CryptoJS/components/aes.js b/webextension/content/CryptoJS/components/aes.js
similarity index 100%
rename from content/CryptoJS/components/aes.js
rename to webextension/content/CryptoJS/components/aes.js
diff --git a/content/CryptoJS/components/cipher-core.js b/webextension/content/CryptoJS/components/cipher-core.js
similarity index 100%
rename from content/CryptoJS/components/cipher-core.js
rename to webextension/content/CryptoJS/components/cipher-core.js
diff --git a/content/CryptoJS/components/core.js b/webextension/content/CryptoJS/components/core.js
similarity index 100%
rename from content/CryptoJS/components/core.js
rename to webextension/content/CryptoJS/components/core.js
diff --git a/content/CryptoJS/components/enc-base64.js b/webextension/content/CryptoJS/components/enc-base64.js
similarity index 100%
rename from content/CryptoJS/components/enc-base64.js
rename to webextension/content/CryptoJS/components/enc-base64.js
diff --git a/content/CryptoJS/components/evpkdf.js b/webextension/content/CryptoJS/components/evpkdf.js
similarity index 100%
rename from content/CryptoJS/components/evpkdf.js
rename to webextension/content/CryptoJS/components/evpkdf.js
diff --git a/content/CryptoJS/components/hmac.js b/webextension/content/CryptoJS/components/hmac.js
similarity index 100%
rename from content/CryptoJS/components/hmac.js
rename to webextension/content/CryptoJS/components/hmac.js
diff --git a/content/CryptoJS/components/md5.js b/webextension/content/CryptoJS/components/md5.js
similarity index 100%
rename from content/CryptoJS/components/md5.js
rename to webextension/content/CryptoJS/components/md5.js
diff --git a/content/CryptoJS/components/pad-nopadding.js b/webextension/content/CryptoJS/components/pad-nopadding.js
similarity index 100%
rename from content/CryptoJS/components/pad-nopadding.js
rename to webextension/content/CryptoJS/components/pad-nopadding.js
diff --git a/content/CryptoJS/components/sha1.js b/webextension/content/CryptoJS/components/sha1.js
similarity index 100%
rename from content/CryptoJS/components/sha1.js
rename to webextension/content/CryptoJS/components/sha1.js
diff --git a/content/CryptoJS/components/sha256.js b/webextension/content/CryptoJS/components/sha256.js
similarity index 100%
rename from content/CryptoJS/components/sha256.js
rename to webextension/content/CryptoJS/components/sha256.js
diff --git a/webextension/content/about.js b/webextension/content/about.js
new file mode 100644
index 0000000..1cfadd3
--- /dev/null
+++ b/webextension/content/about.js
@@ -0,0 +1,8 @@
+var link1 = document.getElementById("link1");
+link1.addEventListener("click", function(evt) {
+ sendMessage({
+ 'destination': 'extension',
+ 'message': 'openLink1'
+ });
+ window.close();
+});
diff --git a/webextension/content/arrow16.png b/webextension/content/arrow16.png
new file mode 100644
index 0000000..9e2cbdf
Binary files /dev/null and b/webextension/content/arrow16.png differ
diff --git a/webextension/content/arrow24.png b/webextension/content/arrow24.png
new file mode 100644
index 0000000..2537eb9
Binary files /dev/null and b/webextension/content/arrow24.png differ
diff --git a/content/check.png b/webextension/content/check.png
similarity index 100%
rename from content/check.png
rename to webextension/content/check.png
diff --git a/content/cross.png b/webextension/content/cross.png
similarity index 100%
rename from content/cross.png
rename to webextension/content/cross.png
diff --git a/webextension/content/file_picker.html b/webextension/content/file_picker.html
new file mode 100644
index 0000000..699004d
--- /dev/null
+++ b/webextension/content/file_picker.html
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/webextension/content/file_picker.js b/webextension/content/file_picker.js
new file mode 100644
index 0000000..4a75a08
--- /dev/null
+++ b/webextension/content/file_picker.js
@@ -0,0 +1,52 @@
+//Because file picker doesn't work from popup.html we open a new tab just for this purpose
+var is_chrome = window.navigator.userAgent.match('Chrome') ? true : false;
+
+chrome.storage.local.get('testing', function(obj) {
+ if (obj.testing == true) {
+ var script = document.createElement('script');
+ script.src = 'testing/file_picker_test.js';
+ document.body.appendChild(script);
+ }
+});
+
+var fileChooser = document.getElementById("import");
+
+function onload(e) {
+ var contents = e.target.result;
+ var view = new DataView(contents);
+ var int_array = [];
+ for (var i = 0; i < view.byteLength; i++) {
+ int_array.push(view.getUint8(i));
+ }
+ console.log('will send array now');
+ if (!is_chrome) {
+ var port = chrome.runtime.connect({
+ name: "filepicker-to-extension"
+ });
+ port.postMessage({
+ 'destination': 'extension',
+ 'message': 'import',
+ 'args': {
+ 'data': int_array
+ }
+ });
+ } else {
+ chrome.runtime.sendMessage({
+ 'destination': 'extension',
+ 'message': 'import',
+ 'args': {
+ 'data': int_array
+ }
+ });
+ }
+ window.close();
+}
+
+fileChooser.addEventListener('change', function(evt) {
+ var f = evt.target.files[0];
+ if (f) {
+ var reader = new FileReader();
+ reader.onload = onload;
+ reader.readAsArrayBuffer(f);
+ }
+});
diff --git a/webextension/content/icon.png b/webextension/content/icon.png
new file mode 100644
index 0000000..c6ce4fd
Binary files /dev/null and b/webextension/content/icon.png differ
diff --git a/webextension/content/icon_error.png b/webextension/content/icon_error.png
new file mode 100644
index 0000000..6e723a6
Binary files /dev/null and b/webextension/content/icon_error.png differ
diff --git a/content/icon_spin.gif b/webextension/content/icon_spin.gif
similarity index 100%
rename from content/icon_spin.gif
rename to webextension/content/icon_spin.gif
diff --git a/webextension/content/import.png b/webextension/content/import.png
new file mode 100644
index 0000000..ba8ee02
Binary files /dev/null and b/webextension/content/import.png differ
diff --git a/content/jsbn.js b/webextension/content/jsbn.js
similarity index 100%
rename from content/jsbn.js
rename to webextension/content/jsbn.js
diff --git a/content/jsbn2.js b/webextension/content/jsbn2.js
similarity index 100%
rename from content/jsbn2.js
rename to webextension/content/jsbn2.js
diff --git a/webextension/content/main.js b/webextension/content/main.js
new file mode 100644
index 0000000..0433510
--- /dev/null
+++ b/webextension/content/main.js
@@ -0,0 +1,1374 @@
+var random_uid; //we get a new uid for each notarized page
+var reliable_sites = []; //read from content/pubkeys.txt
+var previous_session_start_time; // used to make sure user doesnt exceed rate limiting
+var chosen_notary;
+var tdict = {};
+var valid_hashes = [];
+var browser_init_finished = false; //signal to test script when it can start
+var mustVerifyCert = true; //set to false during debugging to be able to work with self-signed certs
+var portPopup;
+var portManager = null;
+var notarization_in_progress = false;
+var waiting_for_click = false;
+var clickTimeout = null;
+var requestBody = null; //will contain POST request's body
+var is_chrome = window.navigator.userAgent.match('Chrome') ? true : false;
+var appId = null; //Chrome uses to send message to external Chrome app. Firefox uses it's own id
+var popupError = null; //set to non-null when there is some error message that must be shown
+//via the popup
+var testing = false;
+
+function getPref(pref) {
+ return new Promise(function(resolve, reject) {
+ chrome.storage.local.get(pref, function(obj) {
+ if (Object.keys(obj).length === 0) {
+ resolve('undefined');
+ return;
+ } else {
+ resolve(obj[pref]);
+ }
+ });
+ });
+}
+
+function setPref(pref, value) {
+ return new Promise(function(resolve, reject) {
+ var obj = {};
+ obj[pref] = value;
+ chrome.storage.local.set(obj, function() {
+ resolve();
+ });
+ });
+}
+
+function sendToPopup(data) {
+ if (is_chrome) {
+ chrome.runtime.sendMessage(data);
+ } else {
+ console.log('will postMessage ', data);
+ portPopup.postMessage(data);
+ }
+}
+
+
+function openManager() {
+ var prefix = is_chrome ? 'webextension/' : '';
+ var url = chrome.extension.getURL(prefix + 'content/manager.html');
+ //re-focus tab if manager already open
+ chrome.tabs.query({}, function(tabs) {
+ for (var i = 0; i < tabs.length; i++) {
+ if (tabs[i].url === url) {
+ chrome.tabs.update(tabs[i].id, {
+ active: true
+ });
+ return;
+ }
+ }
+ chrome.tabs.create({
+ url: url
+ });
+ });
+}
+
+
+
+function notarizeAfterClickSelected() {
+ var prefix = is_chrome ? 'webextension/' : '';
+ var url = chrome.extension.getURL(prefix + 'content/arrow24.png');
+ chrome.browserAction.setIcon({
+ path: url
+ });
+ waiting_for_click = true;
+ clickTimeout = setTimeout(function() {
+ chrome.webRequest.onBeforeRequest.removeListener(onBeforeRequestListener);
+ loadNormalIcon();
+ waiting_for_click = false;
+ sendAlert({
+ title: 'PageSigner error.',
+ text: 'You haven\'t clicked any https:// links in 30 seconds. Please try again. If this error persists it may mean that the website you are trying to notarize is not compatible with PageSigner.'
+ });
+ }, 30 * 1000);
+
+ //get current tab ID and install listener only for that tab
+
+ chrome.tabs.query({
+ active: true
+ }, function(t) {
+ //Note that onBeforeRequest triggers first and only then onBeforeSendHeaders
+ chrome.webRequest.onBeforeRequest.addListener(
+ onBeforeRequestListener, {
+ urls: [""],
+ tabId: t[0].id,
+ types: ["main_frame", "xmlhttprequest"]
+ }, ["requestBody", "blocking"]);
+ });
+}
+
+
+function onBeforeRequestListener(details) {
+ if (waiting_for_click) {
+ clearTimeout(clickTimeout);
+ waiting_for_click = false;
+ }
+ console.log('in onBeforeRequestListener got details', details);
+ if (details.method == 'POST') {
+ //POST payload is only available from onBeforeRequest
+ requestBody = details.requestBody;
+ }
+
+ var url = details.url;
+
+ //kludge: FF wont trigger onBeforeSendHeaders for 127.0.0.1 url
+ //which we use during testing
+ if (testing && !is_chrome) url = '';
+
+ chrome.webRequest.onBeforeSendHeaders.addListener(
+ onBeforeSendHeadersListener, {
+ urls: [url],
+ tabId: details.tabId,
+ types: [details.type]
+ }, ["requestHeaders", "blocking"]);
+
+
+ chrome.webRequest.onBeforeRequest.removeListener(onBeforeRequestListener);
+ return {
+ 'cancel': false
+ };
+}
+
+
+function onBeforeSendHeadersListener(details) {
+ console.log('in onBeforeSendHeadersListener got details', details);
+ details['requestBody'] = requestBody;
+ var rv = getHeaders(details);
+ //we must return fast hence the async invocation
+ setTimeout(function() {
+ startNotarizing(rv.headers, rv.server, rv.port);
+ }, 0);
+ var jsRunner = {
+ 'code': 'window.stop()'
+ };
+ chrome.tabs.executeScript(details.tabId, jsRunner);
+ chrome.webRequest.onBeforeSendHeaders.removeListener(onBeforeSendHeadersListener);
+ return {
+ 'cancel': true
+ };
+}
+
+
+function notarizeNowSelected() {
+
+ chrome.tabs.query({
+ active: true
+ }, function(t) {
+ if (!t[0].url.startsWith('https://')) {
+ sendAlert({
+ 'title': 'PageSigner error',
+ 'text': 'You can only notarize pages which start with https://'
+ });
+ return;
+ }
+
+ //Note that onBeforeRequest triggers first and only then onBeforeSendHeaders
+ chrome.webRequest.onBeforeRequest.addListener(
+ onBeforeRequestListener, {
+ urls: [""],
+ tabId: t[0].id,
+ types: ["main_frame"]
+ }, ["requestBody", "blocking"]);
+
+ //reload current tab in order to trigger the HTTP request
+ chrome.tabs.reload(t[0].id);
+ });
+}
+
+
+function getHeaders(obj) {
+ var x = obj.url.split('/');
+ var host = x[2].split(':')[0];
+ x.splice(0, 3);
+ var resource_url = x.join('/');
+ var headers = obj.method + " /" + resource_url + " HTTP/1.1" + "\r\n";
+ headers += "Host: " + host + "\r\n";
+ for (var i = 0; i < obj.requestHeaders.length; i++) {
+ var h = obj.requestHeaders[i];
+ headers += h.name + ": " + h.value + "\r\n";
+ }
+ if (obj.method == "GET") {
+ headers += "\r\n";
+ } else if (obj.method == 'POST') {
+ var formData = obj.requestBody.formData;
+ var keys = Object.keys(formData);
+ var content = '';
+ for (var i = 0; i < keys.length; i++) {
+ content += keys[i] + '=' + formData[keys[i]];
+ if (i + 1 < keys.length) {
+ content += '&';
+ }
+ }
+ //Chrome doesn't expose Content-Length which chokes nginx
+ headers += 'Content-Length: ' + parseInt(content.length) + '\r\n\r\n';
+ headers += content;
+ }
+ var port = 443;
+ if (obj.url.split(':').length === 3) {
+ //the port is explicitely provided in URL
+ port = parseInt(obj.url.split(':')[2].split('/')[0]);
+ }
+ return {
+ 'headers': headers,
+ 'server': host,
+ 'port': port
+ };
+}
+
+
+
+
+function renamePGSG(dir, newname) {
+ console.log('about to rename');
+ writeFile(dir, 'meta', newname)
+ .then(function() {
+ chrome.storage.local.get(null, function(i) {
+ console.log(i)
+ });
+ populateTable();
+ });
+}
+
+
+function deletePGSG(dir) {
+ chrome.storage.local.remove(dir, function() {
+ populateTable();
+ });
+ return;
+}
+
+function process_message(data) {
+ if (data.destination !== 'extension') return;
+ console.log('ext got msg', data);
+ if (data.message === 'rename') {
+ renamePGSG(data.args.dir, data.args.newname);
+ } else if (data.message === 'delete') {
+ deletePGSG(data.args.dir);
+ } else if (data.message === 'import') {
+ verify_tlsn_and_show_data(data.args.data, true);
+ } else if (data.message === 'export') {
+ //Not in use: manager is doing the exporting
+ } else if (data.message === 'notarize') {
+ notarizeNowSelected();
+ } else if (data.message === 'notarizeAfter') {
+ notarizeAfterClickSelected();
+ } else if (data.message === 'manage') {
+ openManager();
+ } else if (data.message === 'refresh') {
+ populateTable();
+ } else if (data.message === 'openLink1') {
+ chrome.tabs.create({
+ url: 'https://www.tlsnotary.org'
+ });
+ } else if (data.message === 'donate link') {
+ chrome.tabs.create({
+ url: 'https://www.tlsnotary.org/#Donate'
+ });
+ } else if (data.message === 'viewdata') {
+ openTabs(data.args.dir);
+ } else if (data.message === 'viewraw') {
+ viewRaw(data.args.dir);
+ } else if (data.message === 'file picker') {
+ var prefix = is_chrome ? 'webextension/' : '';
+ var url = chrome.extension.getURL(prefix + 'content/file_picker.html');
+ chrome.tabs.create({
+ url: url
+ }, function(t) {
+ console.log('tabid of file picker is', t.id);
+ });
+ } else if (data.message === 'openInstallLink') {
+ //Chrome only
+ chrome.tabs.create({
+ url: 'https://chrome.google.com/webstore/detail/pagesigner-helper-app/oclohfdjoojomkfddjclanpogcnjhemd'
+ });
+ } else if (data.message === 'openChromeExtensions') {
+ //Chrome only
+ chrome.tabs.query({
+ url: 'chrome://extensions/*'
+ }, function(tabs) {
+ if (tabs.length === 0) {
+ chrome.tabs.create({
+ url: 'chrome://extensions'
+ });
+ return;
+ }
+ chrome.tabs.update(tabs[0].id, {
+ active: true
+ });
+ });
+ } else if (data.message === 'popup active') {
+ if (notarization_in_progress) {
+ sendToPopup({
+ 'destination': 'popup',
+ 'message': 'notarization_in_progress'
+ });
+ return;
+ }
+ if (waiting_for_click) {
+ sendToPopup({
+ 'destination': 'popup',
+ 'message': 'waiting_for_click'
+ });
+ return;
+ }
+ if (!is_chrome) {
+ if (popupError) {
+ sendToPopup({
+ 'destination': 'popup',
+ 'message': 'popup error',
+ 'data': popupError
+ });
+ popupError = null;
+ loadNormalIcon();
+ } else {
+ sendToPopup({
+ 'destination': 'popup',
+ 'message': 'show_menu'
+ });
+ }
+ return;
+ }
+ //else{} the checks below are only for Chrome
+ chrome.management.get(appId, function(info) {
+ if (!info) {
+ chrome.runtime.sendMessage({
+ 'destination': 'popup',
+ 'message': 'app_not_installed'
+ });
+ return;
+ }
+ if (info.enabled === false) {
+ chrome.runtime.sendMessage({
+ 'destination': 'popup',
+ 'message': 'app_disabled'
+ });
+ return;
+ }
+ if (popupError) {
+ chrome.runtime.sendMessage({
+ 'destination': 'popup',
+ 'message': 'popup error',
+ 'data': popupError
+ });
+ popupError = null;
+ loadNormalIcon();
+ } else {
+ chrome.runtime.sendMessage({
+ 'destination': 'popup',
+ 'message': 'show_menu'
+ });
+ }
+ });
+ }
+}
+
+
+function browser_specific_init() {
+ getPref('valid_hashes')
+ .then(function(hashes) {
+ if (hashes !== 'undefined') {
+ valid_hashes = hashes;
+ }
+ });
+ //console.log('is_chrome', is_chrome);
+ if (is_chrome) {
+ appId = "oclohfdjoojomkfddjclanpogcnjhemd"; //id of the helper app
+ chrome.runtime.onMessage.addListener(function(data) {
+ process_message(data);
+ });
+ } else {
+ appId = chrome.runtime.id;
+ //console.log('installing listener');
+ //Temporary kludge for FF53 to use Ports for communication
+ chrome.runtime.onConnect.addListener(function(port) {
+ console.log('chrome.runtime.onConnect.addListener with port', port);
+ if (port.name == 'popup-to-extension') {
+ portPopup = port;
+ console.log('in extension port connection from', port.name);
+ port.onMessage.addListener(function(data) {
+ console.log('in port listener, got', data);
+ process_message(data);
+ });
+ } else if (port.name == 'filepicker-to-extension') {
+ port.onMessage.addListener(function(data) {
+ console.log('in filepicker-to-extension got data', data);
+ if (data.destination !== 'extension') return;
+ if (data.message !== 'import') return;
+ verify_tlsn_and_show_data(data.args.data, true);
+ });
+ } else if (port.name == 'notification-to-extension') {
+ port.onMessage.addListener(function(data) {
+ console.log('in notification-to-extension got data', data);
+ if (data.destination !== 'extension') return;
+ if (data.message !== 'viewraw') return;
+ process_message(data);
+ });
+ } else if (port.name == 'manager-to-extension') {
+ portManager = port;
+ port.onMessage.addListener(function(data) {
+ console.log('in manager-to-extension got data', data);
+ if (data.destination !== 'extension') return;
+ process_message(data);
+ });
+ }
+ });
+ }
+ init();
+}
+
+
+
+function init() {
+ setPref('testing', false)
+ .then(function() {
+ return getPref('verbose');
+ })
+ .then(function(value) {
+ if (value !== true && !is_chrome) {
+ //Firefox pollutes browser window, disable logging
+ console.log = function(){};
+ }
+ return getPref('fallback');
+ })
+ .then(function(value) {
+ if (value === true) {
+ //TODO this should be configurable, e.g. choice from list
+ //or set in prefs
+ chosen_notary = pagesigner_servers[1];
+ oracles_intact = true;
+ } else {
+ chosen_notary = oracles[Math.random() * (oracles.length) << 0];
+ var oracle_hash = ba2hex(sha256(JSON.stringify(chosen_notary)));
+ var was_oracle_verified = false;
+ getPref('verifiedOracles.' + oracle_hash)
+ .then(function(value) {
+ if (value === true) {
+ oracles_intact = true;
+ } else {
+ //async check oracles and if the check fails, sets a global var
+ //which prevents notarization session from running
+ console.log('oracle not verified');
+ check_oracle(chosen_notary)
+ .then(function success() {
+ return setPref('verifiedOracles.' + oracle_hash, 'bool', true);
+ })
+ .then(function() {
+ oracles_intact = true;
+ })
+ .catch(function(err) {
+ console.log('caught error', err);
+ //query for a new oracle
+ //TODO fetch backup oracles list
+ });
+ }
+ });
+ }
+ import_reliable_sites();
+ browser_init_finished = true;
+ });
+}
+
+
+function import_reliable_sites() {
+ import_resource('pubkeys.txt')
+ .then(function(text_ba) {
+ parse_reliable_sites(ba2str(text_ba));
+ });
+}
+
+
+//we can import chrome:// and file:// URL
+function import_resource(filename) {
+
+ return new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.responseType = "arraybuffer";
+ xhr.onreadystatechange = function() {
+ if (xhr.readyState != 4)
+ return;
+
+ if (xhr.response) {
+ resolve(ab2ba(xhr.response));
+ }
+ };
+ var path;
+ if (is_chrome) path = chrome.extension.getURL('webextension/content/' + filename);
+ if (!is_chrome) path = chrome.extension.getURL('content/' + filename);
+ xhr.open('get', path, true);
+ xhr.send();
+ });
+}
+
+function get_xhr() {
+ return new XMLHttpRequest();
+}
+
+function parse_reliable_sites(text) {
+ var lines = text.split('\n');
+ var name = "";
+ var expires = "";
+ var modulus = [];
+ var i = -1;
+ var x;
+ var mod_str;
+ var line;
+ while (true) {
+ i += 1;
+ if (i >= lines.length) {
+ break;
+ }
+ x = lines[i];
+ if (x.startsWith('#')) {
+ continue;
+ } else if (x.startsWith('Name=')) {
+ name = x.slice('Name='.length);
+ } else if (x.startsWith('Expires=')) {
+ expires = x.slice('Expires='.length);
+ } else if (x.startsWith('Modulus=')) {
+ mod_str = '';
+ while (true) {
+ i += 1;
+ if (i >= lines.length) {
+ break;
+ }
+ line = lines[i];
+ if (line === '') {
+ break;
+ }
+ mod_str += line;
+ }
+ modulus = [];
+ var bytes = mod_str.split(' ');
+ for (var j = 0; j < bytes.length; j++) {
+ if (bytes[j] === '') {
+ continue;
+ }
+ modulus.push(hex2ba(bytes[j])[0]);
+ }
+ //Don't use pubkeys which expire less than 3 months from now
+ var ex = expires.split('/');
+ var extime = new Date(parseInt(ex[2]), parseInt(ex[0]) - 1, parseInt(ex[1])).getTime();
+ var now = new Date().getTime();
+ if ((extime - now) < 1000 * 60 * 60 * 24 * 90) {
+ continue;
+ }
+ reliable_sites.push({
+ 'name': name,
+ 'port': 443,
+ 'expires': expires,
+ 'modulus': modulus
+ });
+ }
+ console.log('reliable sites are: ', reliable_sites);
+ }
+}
+
+
+function startNotarizing(headers, server, port) {
+ if (!oracles_intact) {
+ //NotarizeAfterClick already changed the icon at this point, revert to normal
+ loadNormalIcon();
+ sendAlert({
+ title: 'PageSigner error',
+ text: 'Cannot notarize because something is wrong with PageSigner server. Please try again later'
+ });
+ return;
+ }
+ var modulus;
+ var certsha256;
+ var chain;
+ loadBusyIcon();
+ get_certificate(server, port)
+ .then(function(certchain) {
+ chain = certchain;
+ if (mustVerifyCert && !verifyCert(chain)) {
+ sendAlert({
+ title: "PageSigner error",
+ text: "This website cannot be audited by PageSigner because it presented an untrusted certificate"
+ });
+ return;
+ }
+ modulus = getModulus(chain[0]);
+ certsha256 = sha256(chain[0]);
+ random_uid = Math.random().toString(36).slice(-10);
+ previous_session_start_time = new Date().getTime();
+ //loop prepare_pms 10 times until succeeds
+ return new Promise(function(resolve, reject) {
+ var tries = 0;
+ var loop = function(resolve, reject) {
+ tries += 1;
+ prepare_pms(modulus).then(function(args) {
+ resolve(args);
+ }).catch(function(error) {
+ console.log('caught error', error);
+ if (error.startsWith('Timed out')) {
+ reject(error);
+ return;
+ }
+ if (error != 'PMS trial failed') {
+ reject('in prepare_pms: caught error ' + error);
+ return;
+ }
+ if (tries == 10) {
+ reject('Could not prepare PMS after 10 tries');
+ return;
+ }
+ //else PMS trial failed
+ loop(resolve, reject);
+ });
+ };
+ loop(resolve, reject);
+ });
+ })
+ .then(function(args) {
+ return start_audit(modulus, certsha256, server, port, headers, args[0], args[1], args[2]);
+ })
+ .then(function(args2) {
+ loadNormalIcon();
+ return save_session_and_open_data(args2, server);
+ })
+ .catch(function(err) {
+ //TODO need to get a decent stack trace
+ loadNormalIcon();
+ console.log('There was an error: ' + err);
+ if (err === "Server sent alert 2,40") {
+ sendAlert({
+ title: 'PageSigner error',
+ text: 'Pagesigner is not compatible with this website because the website does not use RSA ciphersuites'
+ });
+ } else if (err.startsWith('Timed out waiting for notary server to respond') &&
+ ((new Date().getTime() - previous_session_start_time) < 60 * 1000)) {
+ sendAlert({
+ title: 'PageSigner error',
+ text: 'You are signing pages way too fast. Please retry in 60 seconds'
+ });
+ } else {
+ sendAlert({
+ title: 'PageSigner error',
+ text: err
+ });
+ }
+ });
+}
+
+
+
+function save_session_and_open_data(args, server) {
+ assert(args.length === 18, "wrong args length");
+ var cipher_suite = args[0];
+ var client_random = args[1];
+ var server_random = args[2];
+ var pms1 = args[3];
+ var pms2 = args[4];
+ var server_certchain = args[5];
+ var tlsver = args[6];
+ var initial_tlsver = args[7];
+ var fullresp_length = args[8];
+ var fullresp = args[9];
+ var IV_after_finished_length = args[10];
+ var IV_after_finished = args[11];
+ var notary_modulus_length = args[12];
+ var signature = args[13];
+ var commit_hash = args[14];
+ var notary_modulus = args[15];
+ var data_with_headers = args[16];
+ var time = args[17];
+
+ var server_chain_serialized = []; //3-byte length prefix followed by cert
+ for (var i = 0; i < server_certchain.length; i++) {
+ var cert = server_certchain[i];
+ server_chain_serialized = [].concat(
+ server_chain_serialized,
+ bi2ba(cert.length, {
+ 'fixed': 3
+ }),
+ cert);
+ }
+
+ var pgsg = [].concat(
+ str2ba('tlsnotary notarization file\n\n'), [0x00, 0x02],
+ bi2ba(cipher_suite, {
+ 'fixed': 2
+ }),
+ client_random,
+ server_random,
+ pms1,
+ pms2,
+ bi2ba(server_chain_serialized.length, {
+ 'fixed': 3
+ }),
+ server_chain_serialized,
+ tlsver,
+ initial_tlsver,
+ bi2ba(fullresp_length, {
+ 'fixed': 8
+ }),
+ fullresp,
+ bi2ba(IV_after_finished_length, {
+ 'fixed': 2
+ }),
+ IV_after_finished,
+ bi2ba(notary_modulus_length, {
+ 'fixed': 2
+ }),
+ signature,
+ commit_hash,
+ notary_modulus,
+ time);
+
+ var commonName = getCommonName(server_certchain[0]);
+ var creationTime = getTime();
+ var session_dir = makeSessionDir(commonName, creationTime);
+ writeFile(session_dir, 'creationTime', creationTime)
+ .then(function() {
+ return writeDatafile(data_with_headers, session_dir)
+ })
+ .then(function() {
+ return writePgsg(pgsg, session_dir, commonName);
+ })
+ .then(function() {
+ return openTabs(session_dir);
+ })
+ .then(function() {
+ updateCache(sha256(pgsg));
+ populateTable(); //refresh manager
+ });
+}
+
+
+//data_with_headers is a string
+function writeDatafile(data_with_headers, session_dir) {
+ return new Promise(function(resolve, reject) {
+ var rv = data_with_headers.split('\r\n\r\n');
+ var headers = rv[0];
+ var data = rv.splice(1).join('\r\n\r\n');
+ var dirname = session_dir.split('/').pop();
+ var header_lines = headers.split('\r\n');
+ var type = 'html';
+ for (var i = 0; i < header_lines.length; i++) {
+ if (header_lines[i].search(/content-type:\s*/i) > -1) {
+ if (header_lines[i].search("html") > -1) {
+ type = 'html';
+ break;
+ } else if (header_lines[i].search("xml") > -1) {
+ type = 'xml';
+ break;
+ } else if (header_lines[i].search("json") > -1) {
+ type = 'json';
+ break;
+ } else if (header_lines[i].search("pdf") > -1) {
+ type = 'pdf';
+ break;
+ } else if (header_lines[i].search("zip") > -1) {
+ type = 'zip';
+ break;
+ }
+ }
+ }
+ if (type === "html") {
+ //disabling for now because there are no issues displaying without the marker
+ //html needs utf-8 byte order mark
+ //data = ''.concat(String.fromCharCode(0xef, 0xbb, 0xbf), data);
+ }
+ writeFile(dirname, 'metaDataFilename', 'data.' + type).then(function() {
+ return writeFile(dirname, 'data.' + type, data);
+ }).then(function() {
+ return writeFile(dirname, 'raw.txt', data_with_headers);
+ }).then(function() {
+ resolve();
+ });
+
+ });
+}
+
+
+
+function writePgsg(pgsg, session_dir, commonName) {
+ return new Promise(function(resolve, reject) {
+
+ var dirname = session_dir.split('/').pop();
+ var name = commonName.replace(/\*\./g, "");
+ writeFile(dirname, 'pgsg.pgsg', pgsg).then(function() {
+ return writeFile(dirname, 'meta', name);
+ }).then(function() {
+ return writeFile(dirname, 'metaDomainName', commonName);
+ }).then(function() {
+ resolve();
+ });
+ });
+}
+
+
+function writeFile(dirName, fileName, data) {
+ if (data.length === 0) return;
+ if (!is_chrome) {
+ //weird that even though chrome.storage.local.get is available in FF53
+ //it is undefined
+ chrome = browser;
+ }
+
+ return new Promise(function(resolve, reject) {
+ //get the Object, append data and write it back
+ chrome.storage.local.get(dirName, function(items) {
+ var obj = {};
+ if (Object.keys(items).length > 0) {
+ obj = items[dirName];
+ }
+ obj[fileName] = data;
+ obj['lastModified'] = new Date().toString();
+ chrome.storage.local.set({
+ [dirName]: obj
+ }, function() {
+ //lastError undefined on Chrome and null on Firefox
+ //TODO check error
+ //if (! chrome.runtime.lastError){
+ // console.log('error in storage.local.set: ', chrome.runtime.lastError.message);
+ // }
+ console.log('in WriteFile wrote: ', dirName, obj);
+ resolve();
+ });
+ });
+ });
+}
+
+
+//imported_data is an array of numbers
+function verify_tlsn(data, from_past) {
+ var offset = 0;
+ if (ba2str(data.slice(offset, offset += 29)) !== "tlsnotary notarization file\n\n") {
+ throw ('wrong header');
+ }
+ if (data.slice(offset, offset += 2).toString() !== [0x00, 0x02].toString()) {
+ throw ('wrong version');
+ }
+ var cs = ba2int(data.slice(offset, offset += 2));
+ var cr = data.slice(offset, offset += 32);
+ var sr = data.slice(offset, offset += 32);
+ var pms1 = data.slice(offset, offset += 24);
+ var pms2 = data.slice(offset, offset += 24);
+ var chain_serialized_len = ba2int(data.slice(offset, offset += 3));
+ var chain_serialized = data.slice(offset, offset += chain_serialized_len);
+ var tlsver = data.slice(offset, offset += 2);
+ var tlsver_initial = data.slice(offset, offset += 2);
+ var response_len = ba2int(data.slice(offset, offset += 8));
+ var response = data.slice(offset, offset += response_len);
+ var IV_len = ba2int(data.slice(offset, offset += 2));
+ var IV = data.slice(offset, offset += IV_len);
+ var sig_len = ba2int(data.slice(offset, offset += 2));
+ var sig = data.slice(offset, offset += sig_len);
+ var commit_hash = data.slice(offset, offset += 32);
+ var notary_pubkey = data.slice(offset, offset += sig_len);
+ var time = data.slice(offset, offset += 4);
+ assert(data.length === offset, 'invalid .pgsg length');
+
+ offset = 0;
+ var chain = []; //For now we only use the 1st cert in the chain
+ while (offset < chain_serialized.length) {
+ var len = ba2int(chain_serialized.slice(offset, offset += 3));
+ var cert = chain_serialized.slice(offset, offset += len);
+ chain.push(cert);
+ }
+
+ var commonName = getCommonName(chain[0]);
+ //verify cert
+ if (!verifyCert(chain)) {
+ throw ('certificate verification failed');
+ }
+ var modulus = getModulus(chain[0]);
+ //verify commit hash
+ if (sha256(response).toString() !== commit_hash.toString()) {
+ throw ('commit hash mismatch');
+ }
+ //verify sig
+ var signed_data = sha256([].concat(commit_hash, pms2, modulus, time));
+ var signing_key;
+ if (from_past) {
+ signing_key = notary_pubkey;
+ } else {
+ signing_key = chosen_notary.modulus;
+ }
+ if (!verify_commithash_signature(signed_data, sig, signing_key)) {
+ throw ('notary signature verification failed');
+ }
+
+ //decrypt html and check MAC
+ var s = new TLSNClientSession();
+ s.__init__();
+ s.unexpected_server_app_data_count = response.slice(0, 1);
+ s.chosen_cipher_suite = cs;
+ s.client_random = cr;
+ s.server_random = sr;
+ s.auditee_secret = pms1.slice(2, 2 + s.n_auditee_entropy);
+ s.initial_tlsver = tlsver_initial;
+ s.tlsver = tlsver;
+ s.server_modulus = modulus;
+ s.set_auditee_secret();
+ s.auditor_secret = pms2.slice(0, s.n_auditor_entropy);
+ s.set_auditor_secret();
+ s.set_master_secret_half(); //#without arguments sets the whole MS
+ s.do_key_expansion(); //#also resets encryption connection state
+ s.store_server_app_data_records(response.slice(1));
+ s.IV_after_finished = IV;
+ s.server_connection_state.seq_no += 1;
+ s.server_connection_state.IV = s.IV_after_finished;
+ html_with_headers = decrypt_html(s);
+ return [html_with_headers, commonName, data, notary_pubkey];
+}
+
+
+
+function makeSessionDir(server, creationTime, is_imported) {
+
+ if (typeof(is_imported) === "undefined") {
+ is_imported = false;
+ }
+ var imported_str = is_imported ? "-IMPORTED" : "";
+ var server_sanitized = server;
+ if (server.search(/\*/) > -1) {
+ var parts = server.split('.');
+ server_sanitized = parts[parts.length - 2] + '.' + parts[parts.length - 1];
+ }
+ var name = 'session-' + creationTime + '-' + server_sanitized + imported_str;
+ return name;
+}
+
+
+//imported_data is an array of numbers
+function verify_tlsn_and_show_data(imported_data, create) {
+ try {
+ var a = verify_tlsn(imported_data, create);
+ } catch (e) {
+ sendAlert({
+ title: 'PageSigner failed to import file',
+ text: 'The error was: ' + e
+ });
+ return;
+ }
+ if (create) {
+ var data_with_headers = a[0];
+ var commonName = a[1];
+ var imported_data = a[2];
+ var creationTime = getTime();
+ var session_dir = makeSessionDir(commonName, creationTime, true);
+ writeFile(session_dir, 'creationTime', creationTime)
+ .then(function() {
+ return writeDatafile(data_with_headers, session_dir);
+ })
+ .then(function() {
+ console.log('resolved from writeDataFile');
+ return writePgsg(imported_data, session_dir, commonName);
+ })
+ .then(function() {
+ console.log('resolved from writePgsg');
+ openTabs(session_dir);
+ updateCache(sha256(imported_data));
+ populateTable(); //refresh manager
+ })
+ .catch(function(error) {
+ console.log("got error in vtsh: " + error);
+ });
+ }
+}
+
+
+function openTabs(dirname) {
+ var commonName;
+ getFileContent(dirname, "metaDomainName")
+ .then(function(data) {
+ commonName = data;
+ return getFileContent(dirname, "metaDataFilename");
+ })
+ .then(function(name) {
+ return getFileContent(dirname, name);
+ })
+ .then(function(html) {
+ var url;
+ if (is_chrome) url = chrome.extension.getURL('webextension/content/viewer.html');
+ if (!is_chrome) url = chrome.extension.getURL('content/viewer.html');
+ chrome.tabs.create({
+ url: url
+ },
+ function(t) {
+ setTimeout(function() {
+ chrome.runtime.sendMessage({
+ destination: 'viewer',
+ type: 'html',
+ data: html,
+ sessionId: dirname,
+ serverName: commonName
+ });
+ }, 100);
+ });
+ });
+}
+
+
+
+function viewRaw(dirname) {
+ var commonName;
+ getFileContent(dirname, "metaDomainName")
+ .then(function(data) {
+ commonName = data;
+ return getFileContent(dirname, "raw.txt");
+ })
+ .then(function(data) {
+ var prefix = is_chrome ? 'webextension/' : '';
+ var url = chrome.extension.getURL(prefix + 'content/viewer.html');
+ chrome.tabs.create({
+ url: url
+ },
+ function(t) {
+ setTimeout(function() {
+ chrome.runtime.sendMessage({
+ destination: 'viewer',
+ data: data,
+ type: 'raw',
+ sessionId: dirname,
+ serverName: commonName
+ });
+ }, 100);
+ });
+ })
+}
+
+
+
+
+function getFileContent(dirname, filename) {
+ return new Promise(function(resolve, reject) {
+
+ chrome.storage.local.get(dirname, function(items) {
+ //TODO check if dirname filename exist
+ console.log('in getFileContent got', items);
+ resolve(items[dirname][filename]);
+ });
+ });
+}
+
+
+function populateTable() {
+ var prev_tdict = tdict;
+ tdict = {};
+ //get all sessions from storage
+ chrome.storage.local.get(null, function(items) {
+ var newEntries = [];
+ var keys = Object.keys(items);
+ for (var i = 0; i < keys.length; i++) {
+ if (!keys[i].startsWith('session')) continue;
+ if (!prev_tdict.hasOwnProperty(keys[i])) {
+ newEntries.push(keys[i]);
+ continue;
+ }
+ if (prev_tdict[keys[i]]['lastModified'] != items[keys[i]]['lastModified']) {
+ newEntries.push(keys[i]);
+ continue;
+ }
+ tdict[keys[i]] = prev_tdict[keys[i]];
+ }
+ processNewEntries(newEntries).then(function() {
+ sendTable();
+ });
+ });
+}
+
+
+function processNewEntries(dirnames) {
+ return new Promise(function(resolve, reject) {
+ chrome.storage.local.get(dirnames, function(items) {
+ var keys = Object.keys(items);
+ for (var i = 0; i < keys.length; i++) {
+ var imported = false;
+ if (keys[i].match("-IMPORTED$") == "-IMPORTED") {
+ imported = true;
+ }
+ tdict[keys[i]] = {
+ 'name': items[keys[i]]['meta'],
+ 'imported': imported,
+ 'hash': sha256(items[keys[i]]['pgsg.pgsg']),
+ 'pgsg': items[keys[i]]['pgsg.pgsg'],
+ 'modtime': items[keys[i]]['lastModified'],
+ 'creationTime': items[keys[i]]['creationTime'],
+ 'dir': keys[i]
+ };
+ }
+ resolve();
+ });
+ });
+}
+
+
+//Also check validity of pgsg before sending
+function sendTable() {
+ var rows = [];
+ for (var key in tdict) {
+ var row = tdict[key];
+ var is_valid = false;
+ if (valid_hashes.indexOf(row.hash.toString()) > -1) {
+ is_valid = true;
+ } else { //e.g. for some reason the cache was flushed
+ try {
+ verify_tlsn(row.pgsg, true);
+ //if it doesnt throw - the check passed
+ is_valid = true;
+ updateCache(row.hash);
+ } catch (e) {
+ is_valid = false;
+ }
+ }
+ rows.push({
+ 'name': row.name,
+ 'imported': row.imported,
+ 'valid': is_valid,
+ 'verifier': 'tlsnotarygroup4',
+ 'creationTime': row.creationTime,
+ 'dir': row.dir
+ });
+ }
+ sendToManager(rows);
+}
+
+
+function sendToManager(data) {
+ console.log('sending sendToManager ', data);
+ if (is_chrome) {
+ chrome.runtime.sendMessage({
+ 'destination': 'manager',
+ 'payload': data
+ });
+ } else {
+ console.log('will use portManager ', portManager);
+ //the manager may not have loaded yet
+ function do_send() {
+ console.log('do_send.count', do_send.count);
+ do_send.count++;
+ if (do_send.count > 30) return;
+ if (!portManager) { //null if manager was never active
+ setTimeout(do_send, 100);
+ } else {
+ portManager.postMessage({
+ 'destination': 'manager',
+ 'payload': data
+ });
+ }
+ }
+ do_send.count = 0;
+ do_send();
+ }
+}
+
+
+function getModulus(cert) {
+ var c = Certificate.decode(new Buffer(cert), 'der');
+ var pk = c.tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.data;
+ var pkba = ua2ba(pk);
+ //expected modulus length 256, 384, 512
+ var modlen = 256;
+ if (pkba.length > 384) modlen = 384;
+ if (pkba.length > 512) modlen = 512;
+ var modulus = pkba.slice(pkba.length - modlen - 5, pkba.length - 5);
+ return modulus;
+}
+
+
+function getCommonName(cert) {
+ var c = Certificate.decode(new Buffer(cert), 'der');
+ var fields = c.tbsCertificate.subject.value;
+ for (var i = 0; i < fields.length; i++) {
+ if (fields[i][0].type.toString() !== [2, 5, 4, 3].toString()) continue;
+ //first 2 bytes are DER-like metadata
+ return ba2str(fields[i][0].value.slice(2));
+ }
+ return 'unknown';
+}
+
+
+function permutator(inputArr) {
+ var results = [];
+
+ function permute(arr, memo) {
+ var cur, memo = memo || [];
+
+ for (var i = 0; i < arr.length; i++) {
+ cur = arr.splice(i, 1);
+ if (arr.length === 0) {
+ results.push(memo.concat(cur));
+ }
+ permute(arr.slice(), memo.concat(cur));
+ arr.splice(i, 0, cur[0]);
+ }
+
+ return results;
+ }
+
+ return permute(inputArr);
+}
+
+
+function verifyCert(chain) {
+ var chainperms = permutator(chain);
+ for (var i = 0; i < chainperms.length; i++) {
+ if (verifyCertChain(chainperms[i])) {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+function updateCache(hash) {
+ if (!(hash.toString() in valid_hashes)) {
+ valid_hashes.push(hash.toString());
+ chrome.storage.local.set({
+ 'valid_hashes': valid_hashes
+ });
+ }
+}
+
+
+
+
+
+function sendAlert(alertData) {
+ var prefix = is_chrome ? 'webextension/' : '';
+ //for some pages we cant inject js/css, use the ugly alert
+ function uglyAlert(alertData) {
+ var url = chrome.extension.getURL(prefix + 'content/icon_error.png');
+ chrome.browserAction.setIcon({
+ path: url
+ });
+ popupError = alertData;
+ }
+
+ chrome.tabs.query({
+ active: true
+ }, function(tabs) {
+ if (chrome.extension.lastError) {
+ uglyAlert(alertData);
+ return;
+ }
+ chrome.tabs.executeScript(tabs[0].id, {
+ file: (prefix + 'content/sweetalert.min.js')
+ }, function() {
+ if (chrome.extension.lastError) {
+ uglyAlert(alertData);
+ return;
+ }
+ chrome.tabs.insertCSS(tabs[0].id, {
+ file: (prefix + 'content/sweetalert.css')
+ }, function() {
+ if (chrome.extension.lastError) {
+ uglyAlert(alertData);
+ return;
+ }
+ chrome.tabs.executeScript(tabs[0].id, {
+ code: "swal(" + JSON.stringify(alertData) + ")"
+ });
+ if (chrome.extension.lastError) {
+ uglyAlert(alertData);
+ return;
+ }
+ });
+ });
+ });
+}
+
+
+function loadBusyIcon() {
+ var prefix = is_chrome ? 'webextension/' : '';
+ var url = chrome.extension.getURL(prefix + 'content/icon_spin.gif');
+ chrome.browserAction.setIcon({
+ path: url
+ });
+ notarization_in_progress = true;
+}
+
+function loadNormalIcon() {
+ var prefix = is_chrome ? 'webextension/' : '';
+ var url = chrome.extension.getURL(prefix + 'content/icon.png');
+ chrome.browserAction.setIcon({
+ path: url
+ });
+ notarization_in_progress = false;
+}
+
+
+function Socket(name, port) {
+ this.name = name;
+ this.port = port;
+ this.uid = Math.random().toString(36).slice(-10);
+ this.buffer = [];
+ this.recv_timeout = 20 * 1000;
+}
+//inherit the base class
+Socket.prototype = Object.create(AbstractSocket.prototype);
+Socket.prototype.constructor = Socket;
+
+Socket.prototype.connect = function() {
+ var that = this;
+ return new Promise(function(resolve, reject) {
+ chrome.runtime.sendMessage(appId, {
+ 'command': 'connect',
+ 'args': {
+ 'name': that.name,
+ 'port': that.port
+ },
+ 'uid': that.uid
+ },
+ function(response) {
+ console.log('in connect response', response);
+ clearInterval(timer);
+ if (response.retval === 'success') {
+ //endless data fetching loop for the lifetime of this Socket
+ var fetch = function() {
+ chrome.runtime.sendMessage(appId, {
+ 'command': 'recv',
+ 'uid': that.uid
+ }, function(response) {
+ console.log('fetched some data', response.data.length, that.uid);
+ that.buffer = [].concat(that.buffer, response.data);
+ setTimeout(function() {
+ fetch()
+ }, 100);
+ });
+ };
+ //only needed for Chrome
+ fetch();
+ resolve('ready');
+ }
+ reject(response.retval);
+ });
+ //dont wait for connect for too long
+ var timer = setTimeout(function() {
+ reject('connect: socket timed out');
+ }, 1000 * 20);
+ });
+};
+Socket.prototype.send = function(data_in) {
+ chrome.runtime.sendMessage(appId, {
+ 'command': 'send',
+ 'args': {
+ 'data': data_in
+ },
+ 'uid': this.uid
+ });
+};
+Socket.prototype.close = function() {
+ console.log('closing socket', this.uid);
+ chrome.runtime.sendMessage(appId, {
+ 'command': 'close',
+ 'uid': this.uid
+ });
+};
+
+
+//This must be at the bottom, otherwise we'd have to define each function
+//before it gets used.
+browser_specific_init();
diff --git a/content/manage.png b/webextension/content/manage.png
similarity index 100%
rename from content/manage.png
rename to webextension/content/manage.png
diff --git a/webextension/content/manager.css b/webextension/content/manager.css
new file mode 100644
index 0000000..03a58e2
--- /dev/null
+++ b/webextension/content/manager.css
@@ -0,0 +1,133 @@
+a:link {
+ text-decoration: none;
+ color: #0000ff
+}
+
+a:visited {
+ text-decoration: none;
+ color: #0000ff;
+}
+
+a:hover {
+ text-decoration: underline;
+ color: #aa00aa;
+}
+
+a:active {
+ text-decoration: underline;
+}
+
+h1 {
+ text-shadow: 4px 4px 4px #CCCCCC;
+}
+
+
+/* Table 1 Style */
+
+table.table1 {
+ font-family: "Trebuchet MS", sans-serif;
+ font-size: 16px;
+ font-weight: bold;
+ line-height: 1.4em;
+ font-style: normal;
+ border-collapse: separate;
+}
+
+.table1 thead th {
+ padding: 15px;
+ color: #fff;
+ text-shadow: 1px 1px 1px #568F23;
+ border: 1px solid #93CE37;
+ border-bottom: 3px solid #9ED929;
+ background-color: #9DD929;
+ background: -webkit-gradient( linear, left bottom, left top, color-stop(0.02, rgb(123, 192, 67)), color-stop(0.51, rgb(139, 198, 66)), color-stop(0.87, rgb(158, 217, 41)));
+ background: -moz-linear-gradient( center bottom, rgb(123, 192, 67) 2%, rgb(139, 198, 66) 51%, rgb(158, 217, 41) 87%);
+ -webkit-border-top-left-radius: 5px;
+ -webkit-border-top-right-radius: 5px;
+ -moz-border-radius: 5px 5px 0px 0px;
+ border-top-left-radius: 5px;
+ border-top-right-radius: 5px;
+}
+
+.table1 thead th:empty {
+ background: transparent;
+ border: none;
+}
+
+.table1 tbody th {
+ color: #fff;
+ text-shadow: 1px 1px 1px #568F23;
+ background-color: #9DD929;
+ border: 1px solid #93CE37;
+ border-right: 3px solid #9ED929;
+ padding: 0px 10px;
+ background: -webkit-gradient( linear, left bottom, right top, color-stop(0.02, rgb(158, 217, 41)), color-stop(0.51, rgb(139, 198, 66)), color-stop(0.87, rgb(123, 192, 67)));
+ background: -moz-linear-gradient( left bottom, rgb(158, 217, 41) 2%, rgb(139, 198, 66) 51%, rgb(123, 192, 67) 87%);
+ -moz-border-radius: 5px 0px 0px 5px;
+ -webkit-border-top-left-radius: 5px;
+ -webkit-border-bottom-left-radius: 5px;
+ border-top-left-radius: 5px;
+ border-bottom-left-radius: 5px;
+}
+
+.table1 tfoot td {
+ color: #9CD009;
+ font-size: 32px;
+ text-align: center;
+ padding: 10px 0px;
+ text-shadow: 1px 1px 1px #444;
+}
+
+.table1 tfoot th {
+ color: #666;
+}
+
+.table1 tbody td {
+ padding: 0px;
+ text-align: center;
+ background-color: #DEF3CA;
+ border: 2px solid #E7EFE0;
+ -moz-border-radius: 2px;
+ -webkit-border-radius: 2px;
+ border-radius: 2px;
+ color: #666;
+ text-shadow: 1px 1px 1px #fff;
+ height: 10px !important;
+}
+
+.table1 tbody tr {
+ height: 10px;
+}
+
+.table1 tbody span.check::before {
+ content: url(chrome://pagesigner/content/check.png)
+}
+
+button {
+ margin: 0px;
+ padding: 0px;
+ font-family: Lucida Sans MS, Tahoma;
+ font-size: 12px;
+ color: #000;
+ white-space: nowrap;
+ width: 95%;
+ overflow: visible;
+ height: auto;
+}
+
+button em {
+ /*vertical-align:middle;
+ margin:0 2px;
+ display:inline-block;
+ width:24px;
+ height:auto;*/
+ background-image: url(chrome://pagesigner/content/refresh.png);
+}
+
+button em.leftImage {
+ background-position: left center;
+}
+
+button em.rightImage {
+ background-position: -64px -16px;
+}
diff --git a/webextension/content/manager.html b/webextension/content/manager.html
new file mode 100644
index 0000000..e03e304
--- /dev/null
+++ b/webextension/content/manager.html
@@ -0,0 +1,61 @@
+
+
+
+
+ Manage your PageSigner notarization files.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ | File |
+ Creation date |
+ Verified |
+ Verifier |
+ View Data |
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/webextension/content/manager.js b/webextension/content/manager.js
new file mode 100644
index 0000000..a842883
--- /dev/null
+++ b/webextension/content/manager.js
@@ -0,0 +1,276 @@
+var is_chrome = window.navigator.userAgent.match('Chrome') ? true : false;
+var port;
+
+var table_populated = false; //used in testing only
+chrome.storage.local.get('testing', function(obj) {
+ if (obj.testing == true) {
+ var script = document.createElement('script');
+ script.src = 'testing/manager_test.js';
+ document.body.appendChild(script);
+ }
+});
+
+function onload() {
+ if (is_chrome) {
+ chrome.runtime.onMessage.addListener(function(data) {
+ if (data.destination == 'manager') {
+ console.log('hooray', data.payload);
+ process_data(data.payload);
+ }
+ });
+ } else {
+ port = chrome.runtime.connect({
+ name: "manager-to-extension"
+ });
+ port.onMessage.addListener(function(data) {
+ //console.log("Message from legacy add-on: ", data);
+ process_data(data.payload);
+ });
+ }
+
+ sendMessage({
+ 'destination': 'extension',
+ 'message': 'refresh'
+ });
+}
+
+document.addEventListener('load', onload);
+window.addEventListener('load', onload);
+
+
+function process_data(rows) {
+ tb = document.getElementsByTagName('tbody')[0];
+ var initial_row_length = tb.rows.length;
+ for (var j = 0; j < initial_row_length; j++) {
+ tb.deleteRow(0);
+ }
+ //create descending sort order based on creation time
+ rows.sort(function(a, b) {
+ var as = a.creationTime,
+ bs = b.creationTime;
+ return as == bs ? 0 : (as < bs ? 1 : -1);
+ });
+
+ for (var i = 0; i < rows.length; i++) {
+ var r = rows[i];
+ addRow({
+ 'name': r.name,
+ 'imported': r.imported,
+ 'valid': r.valid,
+ 'verifier': r.verifier,
+ 'creationTime': r.creationTime,
+ 'dir': r.dir
+ });
+ }
+ table_populated = true;
+}
+
+
+
+function addRow(args) {
+ var dir = args.dir;
+ var tb, row, td, a, img, text;
+ tb = document.getElementById('myTableBody');
+ row = tb.insertRow(tb.rows.length);
+
+ td = document.createElement("td");
+ td.id = 'toprightimg';
+ td.appendChild(document.createTextNode(args.name));
+ var importimg = document.createElement("img");
+ importimg.src = 'import.png';
+ importimg.width = 12;
+ importimg.height = 12;
+ importimg.id = 'topright';
+ importimg.title = 'This notarization file was imported';
+ if (!args.imported) {
+ importimg.hidden = true;
+ }
+ td.appendChild(importimg);
+
+ row.appendChild(td);
+
+ td = document.createElement("td");
+ a = document.createElement("a");
+ a.title = 'Give the file a more memorable name';
+ a.style = 'float: right';
+ a.onclick = function(event) {
+ doRename(event.target, args.name, dir);
+ };
+ a.text = 'Rename';
+ td.appendChild(a);
+ row.appendChild(td);
+
+ td = document.createElement("td");
+ a = document.createElement("a");
+ a.title = 'Save the file so you can transfer it to others';
+ a.style = 'float: right';
+ a.onclick = function(event) {
+ console.log('export clicked');
+ swal({
+ title: 'You MUST LOG OUT before exporting',
+ text: 'Before exporting you MUST LOG OUT of any sessions associated with the data you are about to export. Please LOG OUT NOW if you have any active sessions running and press OK to proceed',
+ type: "warning"
+ },
+ function() {
+ //sendMessage({'destination':'extension','message':'export', 'args':{'dir': dir, 'file': args.name}});
+ //get the Blob and create an invisible download link
+ chrome.storage.local.get(dir, function(item) {
+
+ function ba2ab(ba) {
+ var ab = new ArrayBuffer(ba.length);
+ var dv = new DataView(ab);
+ for (var i = 0; i < ba.length; i++) {
+ dv.setUint8(i, ba[i]);
+ }
+ return ab;
+ };
+
+ var ba = item[dir]['pgsg.pgsg'];
+ var ab = ba2ab(ba);
+ var exportedBlob = new Blob([ab]);
+ var exportedBlobUrl = URL.createObjectURL(exportedBlob, {
+ type: 'application/octet-stream'
+ });
+ var fauxLink = document.createElement('a');
+ fauxLink.href = exportedBlobUrl;
+ fauxLink.setAttribute('download', item[dir]['meta'] + '.pgsg');
+ document.body.appendChild(fauxLink);
+ fauxLink.click();
+ });
+ });
+ };
+ a.text = 'Export';
+ td.appendChild(a);
+ row.appendChild(td);
+
+ td = document.createElement("td");
+ a = document.createElement("a");
+ a.title = 'permanently remove this set of files from disk';
+ a.style = 'float: right';
+ a.onclick = function(event) {
+ swal({
+ title: 'Removing notarization data',
+ text: "This will remove all notarized data of " + args.name + ". Are you sure?",
+ type: "warning"
+ },
+ function() {
+ sendMessage({
+ 'destination': 'extension',
+ 'message': 'delete',
+ 'args': {
+ 'dir': dir
+ }
+ });
+ });
+ };
+ a.text = 'Delete';
+ td.appendChild(a);
+ row.appendChild(td);
+
+ td = document.createElement("td");
+ td.textContent = args.creationTime;
+ row.appendChild(td);
+
+ td = document.createElement("td");
+ img = document.createElement("img");
+ img.height = 30;
+ img.width = 30;
+ var label;
+ if (args.valid) {
+ img.src = 'check.png';
+ label = 'valid';
+ } else {
+ img.src = 'cross.png';
+ label = 'invalid';
+ }
+ text = document.createElement("text");
+ text.textContent = label;
+ td.appendChild(img);
+ td.appendChild(text);
+ row.appendChild(td);
+
+ td = document.createElement("td");
+ td.textContent = args.verifier;
+ row.appendChild(td);
+
+ td = document.createElement("td");
+ a = document.createElement("a");
+ a.onclick = function(event) {
+ sendMessage({
+ 'destination': 'extension',
+ 'message': 'viewdata',
+ 'args': {
+ 'dir': dir
+ }
+ });
+ };
+ a.text = "view";
+ td.appendChild(a);
+ text = document.createElement("text");
+ text.textContent = ' , ';
+ td.appendChild(text);
+ a = document.createElement("a");
+ a.onclick = function(event) {
+ sendMessage({
+ 'destination': 'extension',
+ 'message': 'viewraw',
+ 'args': {
+ 'dir': dir
+ }
+ });
+ };
+ a.text = "raw";
+ td.appendChild(a);
+
+ row.appendChild(td);
+}
+
+
+function doRename(t, oldname, dir) {
+ var isValid = (function() {
+ var rg1 = /^[^\\/:\*\?"<>\|]+$/; // forbidden characters \ / : * ? " < > |
+ var rg2 = /^\./; // cannot start with dot (.)
+ var rg3 = /^(nul|prn|con|lpt[0-9]|com[0-9])(\.|$)/i; // forbidden file names
+ return function isValid(fname) {
+ return rg1.test(fname) && !rg2.test(fname) && !rg3.test(fname);
+ };
+ })();
+ swal({
+ title: "Enter a new name for the notarization file",
+ type: "input",
+ inputPlaceholder: "Write something"
+ },
+ function(new_name) {
+ if (!(isValid(new_name))) {
+ console.log('detected invalid name', new_name);
+ //swal glitch - need a timeout
+ setTimeout(function() {
+ swal({
+ title: "Invalid filename",
+ text: 'Please only use alphanumerical characters',
+ type: 'warning'
+ });
+ }, 200);
+ } else if (new_name === null) return; //escape pressed
+ else {
+ sendMessage({
+ 'destination': 'extension',
+ 'message': 'rename',
+ 'args': {
+ 'dir': dir,
+ 'newname': new_name
+ }
+ });
+ }
+ });
+}
+
+
+
+function sendMessage(msg) {
+ if (is_chrome) {
+ chrome.runtime.sendMessage(msg);
+ } else {
+ port.postMessage(msg);
+ }
+}
diff --git a/webextension/content/notification_bar.js b/webextension/content/notification_bar.js
new file mode 100644
index 0000000..417d987
--- /dev/null
+++ b/webextension/content/notification_bar.js
@@ -0,0 +1,100 @@
+var is_chrome = window.navigator.userAgent.match('Chrome') ? true : false;
+
+function install_bar(sessionId, serverName, hideButton) {
+ if (hideButton === undefined) {
+ hideButton = false;
+ }
+
+ var table = document.createElement("table");
+ table.style.position = "fixed";
+ table.style.top = "0px";
+ table.style.left = "100px";
+ table.style.background = "rgba(242, 241, 240, 0.9)";
+ table.style.width = "80%";
+ table.style.height = "32px";
+ table.style.visibility = 'hidden';
+ table.style.opacity = '0';
+ table.style.webkitTransition = 'visibility 0s 2s, opacity 2s linear';
+ table.style.transition = 'visibility 0s 2s, opacity 2s linear';
+ var row = document.createElement("tr");
+
+ var cell1 = document.createElement("td");
+ var cell2 = document.createElement("td");
+ var cell3 = document.createElement("td");
+ var cell4 = document.createElement("td");
+
+ cell3.style.align = "right";
+ cell4.style.align = "right";
+ var img = document.createElement("img");
+ //icon.png base64-encoded
+ img.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAS2QAAEtkBYE3f7AAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAlySURBVHic5Zt7cFT1Fcc/597NJimgqICvGR8dO07V8YG0HaRDdolkg9rio6SPcaoZaSlko0DVCj6Y+Gi14AOyCzLVYp36IuMDK7orj71RxKJorUOtOlbrozOUoBUiJCTZ3+kfuwubzSbZezfQzfT7V36Pc37nfnN+597f75yVwJWJivKvMVOFXwCnM3zQichq0eTSWGTKm16VWOmHb2Z4PTxABao/Vay1E2a9UeZViZX+zw9nHHOUr326V2GL4fef7wuLM7yL/p/D109/J/DeoTSkQBwOnDSUCvsj4L14JHD2UC40FAg1OhejPD2UOvsjYFhBVN/J/F3b4Jyiwp0gx6K6F9HXxdJnYsumvJZP9pDEgOAsZ0xTU9PBWuvVWCS4GmDCrDfKVHgOuAz0PITzQRaosbaEws6mUNi5KFf4oHhAbTgxXZFpwBnA6f4yRm9uq+oINTrvoGxT2NQ9locSiwI9RS6llso1mcaRZbsbQU7tZ+4k4E814cQqqexujC+u2QND7AHT5mw4sybsJBR5BpiVXnR0ergS5VzgCoHf+dt4e2pjIlTMegJ/eCFa9TrABVe/PFaQWwaXkXrtKH/t/LkbjoYhJKC2MXGrsew3BQIFinzTUomFwk5LsMnx4onttrIg00gaczupt8SgEPQ0u8eOTb/qlVFDsgVCYecKkJvzDG1H2YalHylynCin0/c19gN/G58B89ysKXDH2mhgO0BNg3O2iMx0afbZ+yq75hVNQGhO4lwsuT+n+0tRbj6srW3F6tUzktkDtY2Jy1RlCb2JmBtqcLbGo4FHClz2H5bVcV+mISJLQV17syI/L34L2BIBKg50yF7LJKti0UAk9+EBYs3BJ5MkJyh83GtAWFHoVhD0l88vm7YPoKaxdQboZE+2Kx8WRcB581/NBLYs68y8F5ZXvz2Q3PpI9eeWZepyukeV7WTwjy9lfSwSXAMQrHcqUF3s0uz9MLCwKAJGdnZ+G8g+impSzZOFyKY+TOSTnO5JAwopPZgDscI/gmsFTizU3hw8vi4a2FQUASLW+Jyuf66PVH9euAZ9o5c+JFdfL9hWx7r4isA2gOrZG48Hbih8rV7oQK3roejXoJbndHS5E8+Zb/ro64XMvgfw2dZdwAhX6x1Y9654dPKnMEzPAtPCrROBn3iTlk/ay/2/zbSG3X2ACJJElwLiTYFev/meiR2ZpicPqA0n6hSZieRGbTk5FHbeLdwYjstpXxAKO5tBH49HgsvyiUyd41wh8C33VgPKy/FI4InsLtcEXBjeeKJiPZJfVv1Af4eRQjAKmAgysbbBeT8WDcSyB4MNzki/xa9RL6rFGPvAwSkD11ugB2sihyB2GOl7pvBb3IhyrBd9gv5+3bLJf8ntHzYxYOqcDV9H3Z0XsrBLk1035hsoWQIENdltS+y7gQFfk/3qUm6Lr6jZkW+sVAlQsdgfrGrCiWqEi70oEuT9nT2j8gZUKFkC5LHYsuBfAerqWmwRuW8wiX5hzPytK8/t7m+4FAnoNqZn/93CrrFjZqGeEx+x2PLg2oEmlBwBKqxct7z6Q4CL5mw6ApFbParqRgYPmqVGwFfS03VbptFtdTcBR3nSJBqJNwcG/SgrKQIUuScTrS+4esNpILM9qmrr8hXmOSVEgOyo7PAtybSS6rsXrx9cojcl7g18WcjUkiFAxMxb8+CkdoBQQ+v3UK3xqOqt88a89EChk90TINYWoNiERi6eizUHHwWoa2rxI3qPV0VqdO6iRYvM4DNTcO1i8ebJH4UancuB2SgnAEaQpKJGESOiBiUpilELg0oSUYOKEdSokEQxAkbBgL5rW53XZfTvaht3Degpbu0CEGiJLw+2upHxtMfizYEngCcGnegS58/dcLSN7yaP4p025rrBp/VGycQAALvbvgP0MC+yIrp4bWTKx4PP7I2SIaA2vHE8IvUexT/zd+2504tgyRCgWHd6ye4AiOivnl150V4vsqV0Kfpdb2KyOdYceNTroiXhAek6v0oPoqqifa653KAkCBhXvjdTQ4BAtE/esB8o+tCLzYGtxaw9pAR4zPNjksn9ef3yjrIF66KBk9VoAOWZAcTajc8sGGC8IAwJAbXhjeNDYecxfxv/qWlo/bFbeWPtryJJrnlwUrsq+uLyYGs8GrgEZCH0vQdW0dvX31f972JtL5qA2ganVrE2AT8CRlrCBNdKVNMeIHuC9U5F9lA8UvUbQS+h9+f3B6PH7Nx/S1QzJ1HlwXSgSAJC4dapKrKGrACmou2ujZCMB+hh/hF8FGp0rs0UQE+du/6E9LE4vb3kE8H8cPWiGV0A0xoSl4otQa/P4JmA1HldW9LJkDTE+NSscqtLtVdtzzEoi8f42reEGpwbrB7fNoUQoKrcX9HhOyNTHl87P36kEVmSX2th8BS0QrNfHIftX0uqKGkPB7K0O03Scp2zUxidK6TCOcA5qZZ8KMrMeLQqkRmfftUro0xleUzgZC/PkIFrDwjWOxXY/mdJ1fi0AduzzB5nbNlSG3ZW1TW1+PNryGtGP9VdYkCWUbnvzFjWw9c1tfj3VXY/7TlHmAVXHiCChBp4WOE7QAfwFKl6wCzoOIUrd7eN+2Jqw0sP2JK0kmKniLa7dvV0+75IRANf9dJ7oJYwq0/eT6petS4a2JTd39TUZO1uq/qjQrUb2/uDKwJqGhJ3KDIDxFhqLjfI+HSS+kngUlIp6yRgKzrfEp2vCFYmydPjwy8QCrd2ofqx2ObyVKmM2ZeV7U6i3Lu73H9Ldho7g81tkyPADK8PnIuCCahtbK0HWQAgqte/EA0+VVfXsmbX2KNXJsu6u+we30TQ9xD9AJWfpaTkC+DPqAoWR6ByAuixoH6Eb5C0zwJe60nqUp8tQZDdYBbGo8EteW24OnFWEReleVEQAaHwxslgrUy1dEUsGrwbIF0G92l62vGQihH+EWwUS/8ebw68rdr7IyZY71T4R3KSwsh4pGorwIYVU/5FAftZDRWDzXGLgghQsWaIUgby/OE72hoHmptYFegEHgcgT0YuPV54EUUWjEqZ+3fMwCjoLVC5t2yhKNMO37Hj+/mKHw8VbMTzr8P6Q0EekL6ujg068SAjaRm/pUPrAiVxHC4Ulh46Dzg1FHbeGurFhgCeLkwHQn8EVABnDfVipYhhtQUOBizgb/9rI4qGYZtXUUuU3B87DDds/7xn1Bqvwta+vTwgSiPDzxM6EXlYMBcOVAM0GP4LS9tKkcQmJqcAAAAASUVORK5CYII=";
+ img.height = 16;
+ img.width = 16;
+ var text = document.createElement("text");
+ text.textContent = "PageSigner verified that this page was received from ";
+ var domain = document.createElement("text");
+ domain.id = "domainName";
+ domain.textContent = serverName;
+ var button = document.createElement("button");
+ button.id = "viewRaw";
+ button.textContent = "View raw data";
+ button.style.MozBorderRadius = "4px";
+ button.style.WebkitBorderRadius = "4px";
+ button.style.borderRadius = "4px";
+ button.onclick = function() {
+ if (is_chrome) {
+ chrome.runtime.sendMessage({
+ destination: 'extension',
+ message: 'viewraw',
+ args: {
+ dir: sessionId
+ }
+ });
+ } else {
+ var port = chrome.runtime.connect({
+ name: "notification-to-extension"
+ });
+ port.postMessage({
+ 'destination': 'extension',
+ 'message': 'viewraw',
+ args: {
+ dir: sessionId
+ }
+ });
+ }
+ };
+ if (hideButton) {
+ button.hidden = true;
+ }
+
+ var close = document.createElement("a");
+ close.text = "x";
+ close.style = 'cursor: pointer;';
+ close.onclick = function(event) {
+ document.getElementById('tablediv').hidden = true;
+ }
+
+ cell4.appendChild(close);
+ cell3.appendChild(button)
+ cell2.appendChild(text);
+ cell2.appendChild(domain);
+ cell1.appendChild(img);
+ row.appendChild(cell1);
+ row.appendChild(cell2);
+ row.appendChild(cell3);
+ row.appendChild(cell4);
+ table.appendChild(row);
+ var tablediv = document.createElement('div');
+ tablediv.appendChild(table);
+ tablediv.id = 'tablediv';
+ document.body.appendChild(tablediv);
+
+ setTimeout(function() {
+ //make a transition to visible
+ table.style.visibility = 'visible';
+ table.style.opacity = '1';
+ table.style.webkitTransition = 'opacity 2s linear';
+ table.style.transition = 'opacity 2s linear';
+ }, 0);
+
+}
diff --git a/webextension/content/oracles.js b/webextension/content/oracles.js
new file mode 100644
index 0000000..acc9be2
--- /dev/null
+++ b/webextension/content/oracles.js
@@ -0,0 +1,273 @@
+var snapshotID = 'snap-2c1fab9b';
+var imageID = 'ami-15192302';
+var oracles_intact = false; //must be explicitely set to true
+
+var oracle = {
+ 'name': 'tlsnotarygroup4',
+ 'IP': '54.152.4.116',
+ 'port': '10011',
+ 'DI': 'https://ec2.us-east-1.amazonaws.com/?AWSAccessKeyId=AKIAIHZGACNJKBHFWOTQ&Action=DescribeInstances&Expires=2019-01-01&InstanceId=i-4b3aff5c&SignatureMethod=HmacSHA256&SignatureVersion=2&Version=2014-10-01&Signature=ByJUrXXgB%2BmJwc2Irk%2BxfZQh1yR3tYiqcA6Hp2gKciE%3D',
+ 'DV': 'https://ec2.us-east-1.amazonaws.com/?AWSAccessKeyId=AKIAIHZGACNJKBHFWOTQ&Action=DescribeVolumes&Expires=2019-01-01&SignatureMethod=HmacSHA256&SignatureVersion=2&Version=2014-10-01&VolumeId=vol-006fce93&Signature=DXq7nf5BrpjUF7Rj%2FMJgk%2Bbs959FrVcJvfquT%2BeNS%2BM%3D',
+ 'GCO': 'https://ec2.us-east-1.amazonaws.com/?AWSAccessKeyId=AKIAIHZGACNJKBHFWOTQ&Action=GetConsoleOutput&Expires=2019-01-01&InstanceId=i-4b3aff5c&SignatureMethod=HmacSHA256&SignatureVersion=2&Version=2014-10-01&Signature=ZUPfAD0jruIIupYSUrXUW7SR9vDhiNIyuVLvO7kgLLM%3D',
+ 'GU': 'https://iam.amazonaws.com/?AWSAccessKeyId=AKIAIHZGACNJKBHFWOTQ&Action=GetUser&Expires=2019-01-01&SignatureMethod=HmacSHA256&SignatureVersion=2&Version=2010-05-08&Signature=C%2B2l1fpHxTt4p0JnROsu%2FMLlOnAJBjQ%2FS%2B8p%2FumAcH0%3D',
+ 'DIA': 'https://ec2.us-east-1.amazonaws.com/?AWSAccessKeyId=AKIAIHZGACNJKBHFWOTQ&Action=DescribeInstanceAttribute&Attribute=userData&Expires=2019-01-01&InstanceId=i-4b3aff5c&SignatureMethod=HmacSHA256&SignatureVersion=2&Version=2014-10-01&Signature=FBc9vervde7ofgVYS3MR1kIjrW8yyAJQ8wDtURAJkwM%3D',
+ 'instanceId': 'i-4b3aff5c',
+ 'modulus': [160, 219, 242, 71, 45, 207, 8, 59, 79, 223, 247, 65, 118, 79, 92, 119, 51, 107, 26, 66, 49, 174, 16, 126, 182, 43, 221, 31, 56, 45, 138, 214, 69, 246, 225, 36, 162, 66, 241, 197, 137, 45, 96, 224, 13, 213, 205, 59, 163, 225, 202, 179, 175, 99, 112, 135, 37, 149, 17, 87, 168, 15, 93, 245, 138, 106, 137, 39, 236, 125, 88, 170, 131, 191, 243, 226, 163, 209, 235, 135, 152, 55, 101, 152, 168, 71, 152, 48, 157, 184, 96, 196, 19, 187, 171, 238, 168, 208, 59, 101, 32, 119, 124, 132, 16, 43, 162, 173, 242, 160, 81, 39, 173, 128, 196, 136, 86, 121, 80, 10, 12, 233, 53, 185, 147, 114, 124, 68, 216, 23, 186, 156, 117, 53, 21, 52, 200, 223, 222, 52, 201, 180, 208, 17, 165, 33, 212, 48, 55, 111, 235, 30, 189, 200, 248, 218, 90, 191, 253, 172, 93, 146, 140, 248, 150, 70, 93, 221, 161, 172, 179, 156, 58, 230, 161, 111, 95, 45, 90, 27, 102, 206, 136, 222, 127, 191, 203, 43, 156, 198, 50, 21, 232, 229, 41, 110, 195, 37, 206, 62, 126, 249, 50, 1, 45, 157, 87, 13, 172, 255, 161, 110, 34, 151, 53, 233, 96, 201, 139, 149, 220, 67, 182, 190, 23, 135, 40, 93, 221, 214, 41, 159, 219, 183, 119, 132, 86, 205, 216, 161, 97, 0, 28, 124, 91, 1, 125, 209, 106, 47, 220, 75, 108, 224, 143, 139, 150, 188, 23, 23, 15, 203, 42, 231, 76, 253, 239, 195, 6, 111, 246, 30, 31, 156, 115, 190, 52, 52, 37, 213, 102, 0, 150, 110, 7, 150, 120, 61, 190, 135, 244, 228, 107, 87, 87, 223, 24, 212, 178, 205, 198, 61, 140, 16, 44, 6, 224, 168, 214, 53, 201, 247, 121, 138, 240, 72, 7, 73, 149, 181, 133, 147, 124, 221, 222, 46, 121, 176, 200, 162, 48, 33, 59, 241, 254, 30, 247, 7, 165, 91, 166, 113, 133, 119, 234, 229, 129, 162, 64, 164, 205, 172, 79, 182, 147, 63, 226, 133, 82, 201, 26, 251, 17, 227, 251, 0, 25, 238, 38, 70, 85, 229, 92, 103, 180, 87, 60, 159, 148, 113, 135, 33, 169, 101, 184, 138, 239, 71, 40, 187, 1, 133, 134, 49, 160, 236, 165, 160, 250, 77, 140, 213, 234, 172, 225, 231, 174, 21, 29, 220, 60, 221, 177, 21, 26, 245, 163, 155, 187, 28, 66, 50, 159, 184, 97, 107, 14, 86, 26, 145, 171, 88, 137, 238, 212, 36, 79, 123, 183, 190, 202, 177, 201, 132, 121, 178, 127, 149, 13, 184, 243, 47, 132, 120, 153, 28, 41, 169, 72, 251, 152, 86, 153, 212, 63, 247, 29, 52, 173, 26, 252, 249, 63, 146, 188, 53, 97, 244, 90, 123, 71, 47, 195, 142, 91, 123, 213, 151, 166, 229, 208, 154, 127, 208, 243, 253, 168, 154, 171, 110, 253, 153, 129, 176, 27, 155, 195, 103, 49, 211, 182, 55]
+}
+
+
+//there can be potentially multiple oracles to choose from
+var oracles = [];
+oracles.push(oracle);
+//all servers trusted to perform notary (including non-oracles)
+//TODO: configurable
+var pagesigner_servers = [oracle];
+
+//assuming both events happened on the same day, get the time
+//difference between them in seconds
+//the time string looks like "2015-04-15T19:00:59.000Z"
+function getSecondsDelta(later, sooner) {
+ assert(later.length == 24);
+ if (later.slice(0, 11) !== sooner.slice(0, 11)) {
+ return 999999; //not on the same day
+ }
+ var laterTime = later.slice(11, 19).split(':');
+ var soonerTime = sooner.slice(11, 19).split(':');
+ var laterSecs = parseInt(laterTime[0]) * 3600 + parseInt(laterTime[1]) * 60 + parseInt(laterTime[2]);
+ var soonerSecs = parseInt(soonerTime[0]) * 3600 + parseInt(soonerTime[1]) * 60 + parseInt(soonerTime[2]);
+ return laterSecs - soonerSecs;
+}
+
+
+
+function modulus_from_pubkey(pem_pubkey) {
+ var b64_str = '';
+ var lines = pem_pubkey.split('\n');
+ //omit header and footer lines
+ for (var i = 1; i < (lines.length - 1); i++) {
+ b64_str += lines[i];
+ }
+ var der = b64decode(b64_str);
+ //last 5 bytes are 2 DER bytes and 3 bytes exponent, our pubkey is the preceding 512 bytes
+ var pubkey = der.slice(der.length - 517, der.length - 5);
+ return pubkey;
+}
+
+
+function checkDescribeInstances(xmlDoc, instanceId, IP) {
+ try {
+ var rs = xmlDoc.getElementsByTagName('reservationSet');
+ assert(rs.length === 1);
+ var rs_items = rs[0].children;
+ assert(rs_items.length === 1);
+ var ownerId = rs_items[0].getElementsByTagName('ownerId')[0].textContent;
+ var isets = rs_items[0].getElementsByTagName('instancesSet');
+ assert(isets.length === 1);
+ var instances = isets[0].children;
+ assert(instances.length === 1);
+ var parent = instances[0];
+ assert(parent.getElementsByTagName('instanceId')[0].textContent === instanceId);
+ assert(parent.getElementsByTagName('imageId')[0].textContent === imageID);
+ assert(parent.getElementsByTagName('instanceState')[0].getElementsByTagName('name')[0].textContent === 'running');
+ var launchTime = parent.getElementsByTagName('launchTime')[0].textContent;
+ assert(parent.getElementsByTagName('ipAddress')[0].textContent === IP);
+ assert(parent.getElementsByTagName('rootDeviceType')[0].textContent === 'ebs');
+ assert(parent.getElementsByTagName('rootDeviceName')[0].textContent === '/dev/xvda');
+ var devices = parent.getElementsByTagName('blockDeviceMapping')[0].getElementsByTagName('item');
+ assert(devices.length === 1);
+ assert(devices[0].getElementsByTagName('deviceName')[0].textContent === '/dev/xvda');
+ assert(devices[0].getElementsByTagName('ebs')[0].getElementsByTagName('status')[0].textContent === 'attached');
+ var volAttachTime = devices[0].getElementsByTagName('ebs')[0].getElementsByTagName('attachTime')[0].textContent;
+ var volumeId = devices[0].getElementsByTagName('ebs')[0].getElementsByTagName('volumeId')[0].textContent;
+ //get seconds from "2015-04-15T19:00:59.000Z"
+ assert(getSecondsDelta(volAttachTime, launchTime) <= 3);
+ assert(parent.getElementsByTagName('virtualizationType')[0].textContent === 'hvm');
+ } catch (e) {
+ return false;
+ }
+ return {
+ 'ownerId': ownerId,
+ 'volumeId': volumeId,
+ 'volAttachTime': volAttachTime,
+ 'launchTime': launchTime
+ };
+}
+
+
+function checkDescribeVolumes(xmlDoc, instanceId, volumeId, volAttachTime) {
+ try {
+ var volumes = xmlDoc.getElementsByTagName('volumeSet')[0].children;
+ assert(volumes.length === 1);
+ var volume = volumes[0];
+ assert(volume.getElementsByTagName('volumeId')[0].textContent === volumeId);
+ assert(volume.getElementsByTagName('snapshotId')[0].textContent === snapshotID);
+ assert(volume.getElementsByTagName('status')[0].textContent === 'in-use');
+ var volCreateTime = volume.getElementsByTagName('createTime')[0].textContent;
+ var attVolumes = volume.getElementsByTagName('attachmentSet')[0].getElementsByTagName('item');
+ assert(attVolumes.length === 1);
+ var attVolume = attVolumes[0];
+ assert(attVolume.getElementsByTagName('volumeId')[0].textContent === volumeId);
+ assert(attVolume.getElementsByTagName('instanceId')[0].textContent === instanceId);
+ assert(attVolume.getElementsByTagName('device')[0].textContent === '/dev/xvda');
+ assert(attVolume.getElementsByTagName('status')[0].textContent === 'attached');
+ var attTime = attVolume.getElementsByTagName('attachTime')[0].textContent;
+ assert(volAttachTime === attTime);
+ //Crucial: volume was created from snapshot and attached at the same instant
+ //this guarantees that there was no time window to modify it
+ assert(getSecondsDelta(attTime, volCreateTime) === 0);
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
+
+
+function checkGetConsoleOutput(xmlDoc, instanceId, launchTime) {
+ try {
+ assert(xmlDoc.getElementsByTagName('instanceId')[0].textContent === instanceId);
+ var timestamp = xmlDoc.getElementsByTagName('timestamp')[0].textContent;
+ //prevent funny business: last consoleLog entry no later than 5 minutes after instance starts
+ assert(getSecondsDelta(timestamp, launchTime) <= 300);
+ var b64data = xmlDoc.getElementsByTagName('output')[0].textContent;
+ var logstr = ba2str(b64decode(b64data));
+ var sigmark = 'PageSigner public key for verification';
+ var pkstartmark = '-----BEGIN PUBLIC KEY-----';
+ var pkendmark = '-----END PUBLIC KEY-----';
+
+ var mark_start = logstr.search(sigmark);
+ assert(mark_start !== -1);
+ var pubkey_start = mark_start + logstr.slice(mark_start).search(pkstartmark);
+ var pubkey_end = pubkey_start + logstr.slice(pubkey_start).search(pkendmark) + pkendmark.length;
+ var pk = logstr.slice(pubkey_start, pubkey_end);
+ assert(pk.length > 0);
+ return pk;
+ } catch (e) {
+ return false;
+ }
+}
+
+// "userData" allows to pass an arbitrary script to the instance at launch. It MUST be empty.
+// This is a sanity check because the instance is stripped of the code which parses userData.
+function checkDescribeInstanceAttribute(xmlDoc, instanceId) {
+ try {
+ assert(xmlDoc.getElementsByTagName('instanceId')[0].textContent === instanceId);
+ assert(xmlDoc.getElementsByTagName('userData')[0].textContent === "");
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
+
+
+function checkGetUser(xmlDoc, ownerId) {
+ try {
+ assert(xmlDoc.getElementsByTagName('UserId')[0].textContent === ownerId);
+ assert(xmlDoc.getElementsByTagName('Arn')[0].textContent.slice(-(ownerId.length + ':root'.length)) === ownerId + ':root');
+ } catch (e) {
+ return false;
+ }
+ return true;
+}
+
+
+function check_oracle(o) {
+ return new Promise(function(resolve, reject) {
+ var xhr = get_xhr();
+ xhr.open('GET', o.DI, true);
+ xhr.onload = function() {
+ var xmlDoc = xhr.responseXML;
+ var result = checkDescribeInstances(xmlDoc, o.instanceId, o.IP);
+ if (!result) {
+ reject('checkDescribeInstances');
+ } else {
+ resolve(result);
+ }
+ };
+ xhr.send();
+ })
+ .then(function(args) {
+ return new Promise(function(resolve, reject) {
+ var xhr = get_xhr();
+ xhr.open('GET', o.DV, true);
+ xhr.onload = function() {
+ var xmlDoc = xhr.responseXML;
+ var result = checkDescribeVolumes(xmlDoc, o.instanceId, args.volumeId, args.volAttachTime);
+ if (!result) {
+ reject('checkDescribeVolumes');
+ } else {
+ resolve({
+ 'ownerId': args.ownerId,
+ 'launchTime': args.launchTime
+ });
+ }
+ };
+ xhr.send();
+ });
+ })
+ .then(function(args) {
+ return new Promise(function(resolve, reject) {
+ var xhr = get_xhr();
+ xhr.open('GET', o.GU, true);
+ xhr.onload = function() {
+ var xmlDoc = xhr.responseXML;
+ var result = checkGetUser(xmlDoc, args.ownerId);
+ if (!result) {
+ reject('checkGetUser');
+ } else {
+ resolve(args.launchTime);
+ }
+ };
+ xhr.send();
+ });
+ })
+ .then(function(launchTime) {
+ return new Promise(function(resolve, reject) {
+ var xhr = get_xhr();
+ xhr.open('GET', o.GCO, true);
+ xhr.onload = function() {
+ var xmlDoc = xhr.responseXML;
+ var result = checkGetConsoleOutput(xmlDoc, o.instanceId, launchTime);
+ if (!result) {
+ reject('checkGetConsoleOutput');
+ } else {
+ if (modulus_from_pubkey(result).toString() !== o.modulus.toString()) {
+ reject('modulus_from_pubkey');
+ }
+ resolve();
+ }
+ };
+ xhr.send();
+ });
+ })
+ .then(function() {
+ return new Promise(function(resolve, reject) {
+ var xhr = get_xhr();
+ xhr.open('GET', o.DIA, true);
+ xhr.onload = function() {
+ var xmlDoc = xhr.responseXML;
+ var result = checkDescribeInstanceAttribute(xmlDoc, o.instanceId);
+ if (!result) {
+ reject('checkDescribeInstanceAttribute');
+ } else {
+ resolve();
+ }
+ };
+ xhr.send();
+ });
+ })
+ .then(function() {
+ var mark = 'AWSAccessKeyId=';
+ var start;
+ var id;
+ var ids = [];
+ //"AWSAccessKeyId" should be the same to prove that the queries are made on behalf of AWS user "root".
+ //The attacker can be a user with limited privileges for whom the API would report only partial information.
+ for (var url in [o.DI, o.DV, o.GU, o.GCO, o.DIA]) {
+ start = url.search(mark) + mark.length;
+ id = url.slice(start, start + url.slice(start).search('&'));
+ ids.push(id);
+ }
+ assert(new Set(ids).size === 1);
+ console.log('oracle verification successfully finished');
+ });
+}
diff --git a/content/pako.js b/webextension/content/pako.js
similarity index 100%
rename from content/pako.js
rename to webextension/content/pako.js
diff --git a/webextension/content/popup.html b/webextension/content/popup.html
new file mode 100644
index 0000000..b5c5bbd
--- /dev/null
+++ b/webextension/content/popup.html
@@ -0,0 +1,102 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Click here to install the helper app needed for PageSigner to work
+
+ Click here and then enable PageSigner helper app
+
+ Notarization in progress. Please wait...
+
+ Waiting for you to click any https:// link on the page...
+
+
+
+
+
+
+
diff --git a/webextension/content/popup.js b/webextension/content/popup.js
new file mode 100644
index 0000000..94dca3b
--- /dev/null
+++ b/webextension/content/popup.js
@@ -0,0 +1,161 @@
+//in FF53 in popup.js I tried
+//chrome.runtime.sendMessage - it gave me Dead Object
+//also tried browser.tabs.sendMessage() - it reprodicibly crashed FF while debugging
+//so as a temporary kludge I use ports for communication
+var is_chrome = window.navigator.userAgent.match('Chrome') ? true : false;
+var port;
+
+function getPref(pref) {
+ return new Promise(function(resolve, reject) {
+ chrome.storage.local.get(pref, function(obj) {
+ if (Object.keys(obj).length === 0) {
+ resolve('undefined');
+ return;
+ } else {
+ resolve(obj[pref]);
+ }
+ });
+ });
+}
+
+getPref('verbose')
+ .then(function(value) {
+ if (value !== true && !is_chrome) {
+ //Firefox pollutes browser window, disable logging
+ console.log = function(){};
+ }
+});
+
+if (!is_chrome) {
+ port = chrome.runtime.connect({
+ name: "popup-to-extension"
+ });
+ port.onMessage.addListener(function(message) {
+ console.log("Message from legacy add-on: ", message);
+ process_message(message);
+ });
+}
+sendMessage({
+ 'destination': 'extension',
+ 'message': 'popup active'
+});
+
+
+function process_message(data) {
+ if (data.destination !== 'popup') return;
+ if (data.message === 'app_not_installed') {
+ document.getElementById("app_not_installed").removeAttribute('hidden');
+ } else if (data.message === 'app_disabled') {
+ document.getElementById("app_disabled").removeAttribute('hidden');
+ } else if (data.message === 'show_menu') {
+ document.getElementById("menu").removeAttribute('hidden');
+ } else if (data.message === 'notarization_in_progress') {
+ document.getElementById("notarization_in_progress").removeAttribute('hidden');
+ } else if (data.message === 'waiting_for_click') {
+ document.getElementById("waiting_for_click").removeAttribute('hidden');
+ } else if (data.message === 'popup error') {
+ console.log('got popup error with', data);
+ var error_text = document.getElementById("popup_error")
+ error_text.removeAttribute('hidden');
+ error_text.textContent = data.data.text;
+ } else {
+ console.log('popup received unexpected message ' + data.message);
+ }
+}
+
+function sendMessage(msg) {
+ if (is_chrome) {
+ chrome.runtime.sendMessage(msg);
+ } else {
+ port.postMessage(msg);
+ }
+}
+
+
+chrome.runtime.onMessage.addListener(function(data) {
+ process_message(data);
+});
+
+
+document.getElementById("notarize").addEventListener("click",
+ function() {
+ window.close();
+ sendMessage({
+ 'destination': 'extension',
+ 'message': 'notarize'
+ });
+ });
+
+document.getElementById("notarizeAfter").addEventListener("click",
+ function() {
+ window.close();
+ sendMessage({
+ 'destination': 'extension',
+ 'message': 'notarizeAfter'
+ });
+ });
+
+document.getElementById("manage").addEventListener("click",
+ function() {
+ window.close();
+ sendMessage({
+ 'destination': 'extension',
+ 'message': 'manage'
+ });
+ });
+
+document.getElementById("import").addEventListener("click",
+ function() {
+ window.close();
+ sendMessage({
+ destination: 'extension',
+ message: 'file picker'
+ });
+ });
+
+document.getElementById("donate_link").addEventListener("click",
+ function() {
+ window.close();
+ sendMessage({
+ destination: 'extension',
+ message: 'donate link'
+ });
+ });
+
+document.getElementById("about").addEventListener("click",
+ function() {
+ var prefix = is_chrome ? 'webextension/' : '';
+ var url = chrome.extension.getURL(prefix + 'content/about.html');
+ document.getElementById("menu").hidden = true;
+ document.getElementById("aboutWindow").hidden = false;
+ });
+
+
+
+var app_not_installed = document.getElementById("app_not_installed");
+app_not_installed.addEventListener("click", function(evt) {
+ chrome.runtime.sendMessage({
+ 'destination': 'extension',
+ 'message': 'openInstallLink'
+ });
+ window.close();
+});
+
+var app_disabled = document.getElementById("app_disabled");
+app_disabled.addEventListener("click", function(evt) {
+ chrome.runtime.sendMessage({
+ 'destination': 'extension',
+ 'message': 'openChromeExtensions'
+ });
+ window.close();
+});
+
+
+
+setTimeout(function() {
+ chrome.storage.local.get('testing', function(obj) {
+ if (obj.testing == true) {
+ document.getElementById('manage').click();
+ }
+ });
+}, 100);
diff --git a/content/pubkeys.txt b/webextension/content/pubkeys.txt
similarity index 100%
rename from content/pubkeys.txt
rename to webextension/content/pubkeys.txt
diff --git a/webextension/content/socket.js b/webextension/content/socket.js
new file mode 100644
index 0000000..cd5568d
--- /dev/null
+++ b/webextension/content/socket.js
@@ -0,0 +1,117 @@
+//The only way to determine if the server is done sending data is to check that the receiving
+//buffer has nothing but complete TLS records i.e. that there is no incomplete TLS records
+//However it was observed that in cases when getting e.g. zip files, some servers first send HTTP header as one
+//TLS record followed by the body as another record(s)
+//That's why after receiving a complete TLS record we wait to get some more data
+//This extra waiting must not be done for the handshake messages to avoid adding latency and having the handshake
+//dropped by the server
+function AbstractSocket() {};
+AbstractSocket.prototype.recv = function(is_handshake) {
+ if (typeof(is_handshake) === 'undefined') {
+ is_handshake = false;
+ }
+ var that = this;
+ return new Promise(function(resolve, reject) {
+ var startTime = new Date().getTime();
+ var complete_records = [];
+ var buf = [];
+ var resolved = false;
+
+ var timer = setTimeout(function() {
+ reject('recv: socket timed out');
+ resolved = true;
+ }, that.recv_timeout);
+
+ var check = function() {
+ //console.log('check()ing for more data', uid);
+ if (resolved) {
+ console.log('returning because resolved');
+ return;
+ }
+ if (that.buffer.length === 0) {
+ setTimeout(function() {
+ check()
+ }, 100);
+ return;
+ }
+ console.log('new data in check', that.buffer.length);
+ //else got new data
+ buf = [].concat(buf, that.buffer);
+ that.buffer = [];
+ var rv = check_complete_records(buf);
+ complete_records = [].concat(complete_records, rv.comprecs);
+ if (!rv.is_complete) {
+ console.log("check_complete_records failed", that.uid);
+ buf = rv.incomprecs;
+ setTimeout(function() {
+ check()
+ }, 100);
+ return;
+ } else {
+ function finished_receiving() {
+ clearTimeout(timer);
+ console.log('recv promise resolving', that.uid);
+ resolved = true;
+ resolve(complete_records);
+ };
+
+ console.log("got complete records", that.uid);
+ if (is_handshake) {
+ finished_receiving();
+ return;
+ } else {
+ console.log("in recv waiting for an extra second", that.uid);
+ buf = [];
+ //give the server another second to send more data
+ setTimeout(function() {
+ if (that.buffer.length === 0) {
+ finished_receiving();
+ return;
+ } else {
+ console.log('more data received after waiting for a second', that.uid);
+ check();
+ }
+ }, 1000);
+ }
+ }
+ };
+ check();
+ });
+};
+
+
+function check_complete_records(d) {
+ /*'''Given a response d from a server,
+ we want to know if its contents represents
+ a complete set of records, however many.'''
+ */
+ var complete_records = [];
+ var incomplete_records = [];
+
+ while (d) {
+ if (d.length < 5) {
+ return {
+ 'is_complete': false,
+ 'comprecs': complete_records,
+ 'incomprecs': d
+ };
+ }
+ var l = ba2int(d.slice(3, 5));
+ if (d.length < (l + 5)) {
+ return {
+ 'is_complete': false,
+ 'comprecs': complete_records,
+ 'incomprecs': d
+ };
+ } else if (d.length === (l + 5)) {
+ return {
+ 'is_complete': true,
+ 'comprecs': [].concat(complete_records, d)
+ };
+ } else {
+ complete_records = [].concat(complete_records, d.slice(0, l + 5));
+ d = d.slice(l + 5);
+ continue;
+ }
+ }
+}
diff --git a/webextension/content/sweetalert.css b/webextension/content/sweetalert.css
new file mode 100644
index 0000000..f390a2f
--- /dev/null
+++ b/webextension/content/sweetalert.css
@@ -0,0 +1,961 @@
+body.stop-scrolling {
+ height: 100%;
+ overflow: hidden;
+}
+
+.sweet-overlay {
+ background-color: black;
+ /* IE8 */
+ -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=40)";
+ /* IE8 */
+ background-color: rgba(0, 0, 0, 0.4);
+ position: fixed;
+ left: 0;
+ right: 0;
+ top: 0;
+ bottom: 0;
+ display: none;
+ z-index: 10000;
+}
+
+.sweet-alert {
+ background-color: white;
+ font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
+ width: 478px;
+ padding: 17px;
+ border-radius: 5px;
+ text-align: center;
+ position: fixed;
+ left: 50%;
+ top: 50%;
+ margin-left: -256px;
+ margin-top: -200px;
+ overflow: hidden;
+ display: none;
+ z-index: 99999;
+}
+
+@media all and (max-width: 540px) {
+ .sweet-alert {
+ width: auto;
+ margin-left: 0;
+ margin-right: 0;
+ left: 15px;
+ right: 15px;
+ }
+}
+
+.sweet-alert h2 {
+ color: #575757;
+ font-size: 30px;
+ text-align: center;
+ font-weight: 600;
+ text-transform: none;
+ position: relative;
+ margin: 25px 0;
+ padding: 0;
+ line-height: 40px;
+ display: block;
+}
+
+.sweet-alert p {
+ color: #797979;
+ font-size: 16px;
+ text-align: center;
+ font-weight: 300;
+ position: relative;
+ text-align: inherit;
+ float: none;
+ margin: 0;
+ padding: 0;
+ line-height: normal;
+}
+
+.sweet-alert fieldset {
+ border: none;
+ position: relative;
+}
+
+.sweet-alert .sa-error-container {
+ background-color: #f1f1f1;
+ margin-left: -17px;
+ margin-right: -17px;
+ overflow: hidden;
+ padding: 0 10px;
+ max-height: 0;
+ webkit-transition: padding 0.15s, max-height 0.15s;
+ transition: padding 0.15s, max-height 0.15s;
+}
+
+.sweet-alert .sa-error-container.show {
+ padding: 10px 0;
+ max-height: 100px;
+ webkit-transition: padding 0.2s, max-height 0.2s;
+ transition: padding 0.25s, max-height 0.25s;
+}
+
+.sweet-alert .sa-error-container .icon {
+ display: inline-block;
+ width: 24px;
+ height: 24px;
+ border-radius: 50%;
+ background-color: #ea7d7d;
+ color: white;
+ line-height: 24px;
+ text-align: center;
+ margin-right: 3px;
+}
+
+.sweet-alert .sa-error-container p {
+ display: inline-block;
+}
+
+.sweet-alert .sa-input-error {
+ position: absolute;
+ top: 29px;
+ right: 26px;
+ width: 20px;
+ height: 20px;
+ opacity: 0;
+ -webkit-transform: scale(0.5);
+ transform: scale(0.5);
+ -webkit-transform-origin: 50% 50%;
+ transform-origin: 50% 50%;
+ -webkit-transition: all 0.1s;
+ transition: all 0.1s;
+}
+
+.sweet-alert .sa-input-error::before, .sweet-alert .sa-input-error::after {
+ content: "";
+ width: 20px;
+ height: 6px;
+ background-color: #f06e57;
+ border-radius: 3px;
+ position: absolute;
+ top: 50%;
+ margin-top: -4px;
+ left: 50%;
+ margin-left: -9px;
+}
+
+.sweet-alert .sa-input-error::before {
+ -webkit-transform: rotate(-45deg);
+ transform: rotate(-45deg);
+}
+
+.sweet-alert .sa-input-error::after {
+ -webkit-transform: rotate(45deg);
+ transform: rotate(45deg);
+}
+
+.sweet-alert .sa-input-error.show {
+ opacity: 1;
+ -webkit-transform: scale(1);
+ transform: scale(1);
+}
+
+.sweet-alert input {
+ width: 100%;
+ box-sizing: border-box;
+ border-radius: 3px;
+ border: 1px solid #d7d7d7;
+ height: 43px;
+ margin-top: 10px;
+ margin-bottom: 17px;
+ font-size: 18px;
+ box-shadow: inset 0px 1px 1px rgba(0, 0, 0, 0.06);
+ padding: 0 12px;
+ display: none;
+ -webkit-transition: all 0.3s;
+ transition: all 0.3s;
+}
+
+.sweet-alert input:focus {
+ outline: none;
+ box-shadow: 0px 0px 3px #c4e6f5;
+ border: 1px solid #b4dbed;
+}
+
+.sweet-alert input:focus::-moz-placeholder {
+ transition: opacity 0.3s 0.03s ease;
+ opacity: 0.5;
+}
+
+.sweet-alert input:focus:-ms-input-placeholder {
+ transition: opacity 0.3s 0.03s ease;
+ opacity: 0.5;
+}
+
+.sweet-alert input:focus::-webkit-input-placeholder {
+ transition: opacity 0.3s 0.03s ease;
+ opacity: 0.5;
+}
+
+.sweet-alert input::-moz-placeholder {
+ color: #bdbdbd;
+}
+
+.sweet-alert input:-ms-input-placeholder {
+ color: #bdbdbd;
+}
+
+.sweet-alert input::-webkit-input-placeholder {
+ color: #bdbdbd;
+}
+
+.sweet-alert.show-input input {
+ display: block;
+}
+
+.sweet-alert button {
+ background-color: #AEDEF4;
+ color: white;
+ border: none;
+ box-shadow: none;
+ font-size: 17px;
+ font-weight: 500;
+ -webkit-border-radius: 4px;
+ border-radius: 5px;
+ padding: 10px 32px;
+ margin: 26px 5px 0 5px;
+ cursor: pointer;
+}
+
+.sweet-alert button:focus {
+ outline: none;
+ box-shadow: 0 0 2px rgba(128, 179, 235, 0.5), inset 0 0 0 1px rgba(0, 0, 0, 0.05);
+}
+
+.sweet-alert button:hover {
+ background-color: #a1d9f2;
+}
+
+.sweet-alert button:active {
+ background-color: #81ccee;
+}
+
+.sweet-alert button.cancel {
+ background-color: #D0D0D0;
+}
+
+.sweet-alert button.cancel:hover {
+ background-color: #c8c8c8;
+}
+
+.sweet-alert button.cancel:active {
+ background-color: #b6b6b6;
+}
+
+.sweet-alert button.cancel:focus {
+ box-shadow: rgba(197, 205, 211, 0.8) 0px 0px 2px, rgba(0, 0, 0, 0.0470588) 0px 0px 0px 1px inset !important;
+}
+
+.sweet-alert button::-moz-focus-inner {
+ border: 0;
+}
+
+.sweet-alert[data-has-cancel-button=false] button {
+ box-shadow: none !important;
+}
+
+.sweet-alert[data-has-confirm-button=false][data-has-cancel-button=false] {
+ padding-bottom: 40px;
+}
+
+.sweet-alert .sa-icon {
+ width: 80px;
+ height: 80px;
+ border: 4px solid gray;
+ -webkit-border-radius: 40px;
+ border-radius: 40px;
+ border-radius: 50%;
+ margin: 20px auto;
+ padding: 0;
+ position: relative;
+ box-sizing: content-box;
+}
+
+.sweet-alert .sa-icon.sa-error {
+ border-color: #F27474;
+}
+
+.sweet-alert .sa-icon.sa-error .sa-x-mark {
+ position: relative;
+ display: block;
+}
+
+.sweet-alert .sa-icon.sa-error .sa-line {
+ position: absolute;
+ height: 5px;
+ width: 47px;
+ background-color: #F27474;
+ display: block;
+ top: 37px;
+ border-radius: 2px;
+}
+
+.sweet-alert .sa-icon.sa-error .sa-line.sa-left {
+ -webkit-transform: rotate(45deg);
+ transform: rotate(45deg);
+ left: 17px;
+}
+
+.sweet-alert .sa-icon.sa-error .sa-line.sa-right {
+ -webkit-transform: rotate(-45deg);
+ transform: rotate(-45deg);
+ right: 16px;
+}
+
+.sweet-alert .sa-icon.sa-warning {
+ border-color: #F8BB86;
+}
+
+.sweet-alert .sa-icon.sa-warning .sa-body {
+ position: absolute;
+ width: 5px;
+ height: 47px;
+ left: 50%;
+ top: 10px;
+ -webkit-border-radius: 2px;
+ border-radius: 2px;
+ margin-left: -2px;
+ background-color: #F8BB86;
+}
+
+.sweet-alert .sa-icon.sa-warning .sa-dot {
+ position: absolute;
+ width: 7px;
+ height: 7px;
+ -webkit-border-radius: 50%;
+ border-radius: 50%;
+ margin-left: -3px;
+ left: 50%;
+ bottom: 10px;
+ background-color: #F8BB86;
+}
+
+.sweet-alert .sa-icon.sa-info {
+ border-color: #C9DAE1;
+}
+
+.sweet-alert .sa-icon.sa-info::before {
+ content: "";
+ position: absolute;
+ width: 5px;
+ height: 29px;
+ left: 50%;
+ bottom: 17px;
+ border-radius: 2px;
+ margin-left: -2px;
+ background-color: #C9DAE1;
+}
+
+.sweet-alert .sa-icon.sa-info::after {
+ content: "";
+ position: absolute;
+ width: 7px;
+ height: 7px;
+ border-radius: 50%;
+ margin-left: -3px;
+ top: 19px;
+ background-color: #C9DAE1;
+}
+
+.sweet-alert .sa-icon.sa-success {
+ border-color: #A5DC86;
+}
+
+.sweet-alert .sa-icon.sa-success::before, .sweet-alert .sa-icon.sa-success::after {
+ content: '';
+ -webkit-border-radius: 40px;
+ border-radius: 40px;
+ border-radius: 50%;
+ position: absolute;
+ width: 60px;
+ height: 120px;
+ background: white;
+ -webkit-transform: rotate(45deg);
+ transform: rotate(45deg);
+}
+
+.sweet-alert .sa-icon.sa-success::before {
+ -webkit-border-radius: 120px 0 0 120px;
+ border-radius: 120px 0 0 120px;
+ top: -7px;
+ left: -33px;
+ -webkit-transform: rotate(-45deg);
+ transform: rotate(-45deg);
+ -webkit-transform-origin: 60px 60px;
+ transform-origin: 60px 60px;
+}
+
+.sweet-alert .sa-icon.sa-success::after {
+ -webkit-border-radius: 0 120px 120px 0;
+ border-radius: 0 120px 120px 0;
+ top: -11px;
+ left: 30px;
+ -webkit-transform: rotate(-45deg);
+ transform: rotate(-45deg);
+ -webkit-transform-origin: 0px 60px;
+ transform-origin: 0px 60px;
+}
+
+.sweet-alert .sa-icon.sa-success .sa-placeholder {
+ width: 80px;
+ height: 80px;
+ border: 4px solid rgba(165, 220, 134, 0.2);
+ -webkit-border-radius: 40px;
+ border-radius: 40px;
+ border-radius: 50%;
+ box-sizing: content-box;
+ position: absolute;
+ left: -4px;
+ top: -4px;
+ z-index: 2;
+}
+
+.sweet-alert .sa-icon.sa-success .sa-fix {
+ width: 5px;
+ height: 90px;
+ background-color: white;
+ position: absolute;
+ left: 28px;
+ top: 8px;
+ z-index: 1;
+ -webkit-transform: rotate(-45deg);
+ transform: rotate(-45deg);
+}
+
+.sweet-alert .sa-icon.sa-success .sa-line {
+ height: 5px;
+ background-color: #A5DC86;
+ display: block;
+ border-radius: 2px;
+ position: absolute;
+ z-index: 2;
+}
+
+.sweet-alert .sa-icon.sa-success .sa-line.sa-tip {
+ width: 25px;
+ left: 14px;
+ top: 46px;
+ -webkit-transform: rotate(45deg);
+ transform: rotate(45deg);
+}
+
+.sweet-alert .sa-icon.sa-success .sa-line.sa-long {
+ width: 47px;
+ right: 8px;
+ top: 38px;
+ -webkit-transform: rotate(-45deg);
+ transform: rotate(-45deg);
+}
+
+.sweet-alert .sa-icon.sa-custom {
+ background-size: contain;
+ border-radius: 0;
+ border: none;
+ background-position: center center;
+ background-repeat: no-repeat;
+}
+
+
+/*
+ * Animations
+ */
+
+@-webkit-keyframes showSweetAlert {
+ 0% {
+ transform: scale(0.7);
+ -webkit-transform: scale(0.7);
+ }
+ 45% {
+ transform: scale(1.05);
+ -webkit-transform: scale(1.05);
+ }
+ 80% {
+ transform: scale(0.95);
+ -webkit-transform: scale(0.95);
+ }
+ 100% {
+ transform: scale(1);
+ -webkit-transform: scale(1);
+ }
+}
+
+@keyframes showSweetAlert {
+ 0% {
+ transform: scale(0.7);
+ -webkit-transform: scale(0.7);
+ }
+ 45% {
+ transform: scale(1.05);
+ -webkit-transform: scale(1.05);
+ }
+ 80% {
+ transform: scale(0.95);
+ -webkit-transform: scale(0.95);
+ }
+ 100% {
+ transform: scale(1);
+ -webkit-transform: scale(1);
+ }
+}
+
+@-webkit-keyframes hideSweetAlert {
+ 0% {
+ transform: scale(1);
+ -webkit-transform: scale(1);
+ }
+ 100% {
+ transform: scale(0.5);
+ -webkit-transform: scale(0.5);
+ }
+}
+
+@keyframes hideSweetAlert {
+ 0% {
+ transform: scale(1);
+ -webkit-transform: scale(1);
+ }
+ 100% {
+ transform: scale(0.5);
+ -webkit-transform: scale(0.5);
+ }
+}
+
+@-webkit-keyframes slideFromTop {
+ 0% {
+ top: 0%;
+ }
+ 100% {
+ top: 50%;
+ }
+}
+
+@keyframes slideFromTop {
+ 0% {
+ top: 0%;
+ }
+ 100% {
+ top: 50%;
+ }
+}
+
+@-webkit-keyframes slideToTop {
+ 0% {
+ top: 50%;
+ }
+ 100% {
+ top: 0%;
+ }
+}
+
+@keyframes slideToTop {
+ 0% {
+ top: 50%;
+ }
+ 100% {
+ top: 0%;
+ }
+}
+
+@-webkit-keyframes slideFromBottom {
+ 0% {
+ top: 70%;
+ }
+ 100% {
+ top: 50%;
+ }
+}
+
+@keyframes slideFromBottom {
+ 0% {
+ top: 70%;
+ }
+ 100% {
+ top: 50%;
+ }
+}
+
+@-webkit-keyframes slideToBottom {
+ 0% {
+ top: 50%;
+ }
+ 100% {
+ top: 70%;
+ }
+}
+
+@keyframes slideToBottom {
+ 0% {
+ top: 50%;
+ }
+ 100% {
+ top: 70%;
+ }
+}
+
+.showSweetAlert[data-animation=pop] {
+ -webkit-animation: showSweetAlert 0.3s;
+ animation: showSweetAlert 0.3s;
+}
+
+.showSweetAlert[data-animation=none] {
+ -webkit-animation: none;
+ animation: none;
+}
+
+.showSweetAlert[data-animation=slide-from-top] {
+ -webkit-animation: slideFromTop 0.3s;
+ animation: slideFromTop 0.3s;
+}
+
+.showSweetAlert[data-animation=slide-from-bottom] {
+ -webkit-animation: slideFromBottom 0.3s;
+ animation: slideFromBottom 0.3s;
+}
+
+.hideSweetAlert[data-animation=pop] {
+ -webkit-animation: hideSweetAlert 0.2s;
+ animation: hideSweetAlert 0.2s;
+}
+
+.hideSweetAlert[data-animation=none] {
+ -webkit-animation: none;
+ animation: none;
+}
+
+.hideSweetAlert[data-animation=slide-from-top] {
+ -webkit-animation: slideToTop 0.4s;
+ animation: slideToTop 0.4s;
+}
+
+.hideSweetAlert[data-animation=slide-from-bottom] {
+ -webkit-animation: slideToBottom 0.3s;
+ animation: slideToBottom 0.3s;
+}
+
+@-webkit-keyframes animateSuccessTip {
+ 0% {
+ width: 0;
+ left: 1px;
+ top: 19px;
+ }
+ 54% {
+ width: 0;
+ left: 1px;
+ top: 19px;
+ }
+ 70% {
+ width: 50px;
+ left: -8px;
+ top: 37px;
+ }
+ 84% {
+ width: 17px;
+ left: 21px;
+ top: 48px;
+ }
+ 100% {
+ width: 25px;
+ left: 14px;
+ top: 45px;
+ }
+}
+
+@keyframes animateSuccessTip {
+ 0% {
+ width: 0;
+ left: 1px;
+ top: 19px;
+ }
+ 54% {
+ width: 0;
+ left: 1px;
+ top: 19px;
+ }
+ 70% {
+ width: 50px;
+ left: -8px;
+ top: 37px;
+ }
+ 84% {
+ width: 17px;
+ left: 21px;
+ top: 48px;
+ }
+ 100% {
+ width: 25px;
+ left: 14px;
+ top: 45px;
+ }
+}
+
+@-webkit-keyframes animateSuccessLong {
+ 0% {
+ width: 0;
+ right: 46px;
+ top: 54px;
+ }
+ 65% {
+ width: 0;
+ right: 46px;
+ top: 54px;
+ }
+ 84% {
+ width: 55px;
+ right: 0px;
+ top: 35px;
+ }
+ 100% {
+ width: 47px;
+ right: 8px;
+ top: 38px;
+ }
+}
+
+@keyframes animateSuccessLong {
+ 0% {
+ width: 0;
+ right: 46px;
+ top: 54px;
+ }
+ 65% {
+ width: 0;
+ right: 46px;
+ top: 54px;
+ }
+ 84% {
+ width: 55px;
+ right: 0px;
+ top: 35px;
+ }
+ 100% {
+ width: 47px;
+ right: 8px;
+ top: 38px;
+ }
+}
+
+@-webkit-keyframes rotatePlaceholder {
+ 0% {
+ transform: rotate(-45deg);
+ -webkit-transform: rotate(-45deg);
+ }
+ 5% {
+ transform: rotate(-45deg);
+ -webkit-transform: rotate(-45deg);
+ }
+ 12% {
+ transform: rotate(-405deg);
+ -webkit-transform: rotate(-405deg);
+ }
+ 100% {
+ transform: rotate(-405deg);
+ -webkit-transform: rotate(-405deg);
+ }
+}
+
+@keyframes rotatePlaceholder {
+ 0% {
+ transform: rotate(-45deg);
+ -webkit-transform: rotate(-45deg);
+ }
+ 5% {
+ transform: rotate(-45deg);
+ -webkit-transform: rotate(-45deg);
+ }
+ 12% {
+ transform: rotate(-405deg);
+ -webkit-transform: rotate(-405deg);
+ }
+ 100% {
+ transform: rotate(-405deg);
+ -webkit-transform: rotate(-405deg);
+ }
+}
+
+.animateSuccessTip {
+ -webkit-animation: animateSuccessTip 0.75s;
+ animation: animateSuccessTip 0.75s;
+}
+
+.animateSuccessLong {
+ -webkit-animation: animateSuccessLong 0.75s;
+ animation: animateSuccessLong 0.75s;
+}
+
+.sa-icon.sa-success.animate::after {
+ -webkit-animation: rotatePlaceholder 4.25s ease-in;
+ animation: rotatePlaceholder 4.25s ease-in;
+}
+
+@-webkit-keyframes animateErrorIcon {
+ 0% {
+ transform: rotateX(100deg);
+ -webkit-transform: rotateX(100deg);
+ opacity: 0;
+ }
+ 100% {
+ transform: rotateX(0deg);
+ -webkit-transform: rotateX(0deg);
+ opacity: 1;
+ }
+}
+
+@keyframes animateErrorIcon {
+ 0% {
+ transform: rotateX(100deg);
+ -webkit-transform: rotateX(100deg);
+ opacity: 0;
+ }
+ 100% {
+ transform: rotateX(0deg);
+ -webkit-transform: rotateX(0deg);
+ opacity: 1;
+ }
+}
+
+.animateErrorIcon {
+ -webkit-animation: animateErrorIcon 0.5s;
+ animation: animateErrorIcon 0.5s;
+}
+
+@-webkit-keyframes animateXMark {
+ 0% {
+ transform: scale(0.4);
+ -webkit-transform: scale(0.4);
+ margin-top: 26px;
+ opacity: 0;
+ }
+ 50% {
+ transform: scale(0.4);
+ -webkit-transform: scale(0.4);
+ margin-top: 26px;
+ opacity: 0;
+ }
+ 80% {
+ transform: scale(1.15);
+ -webkit-transform: scale(1.15);
+ margin-top: -6px;
+ }
+ 100% {
+ transform: scale(1);
+ -webkit-transform: scale(1);
+ margin-top: 0;
+ opacity: 1;
+ }
+}
+
+@keyframes animateXMark {
+ 0% {
+ transform: scale(0.4);
+ -webkit-transform: scale(0.4);
+ margin-top: 26px;
+ opacity: 0;
+ }
+ 50% {
+ transform: scale(0.4);
+ -webkit-transform: scale(0.4);
+ margin-top: 26px;
+ opacity: 0;
+ }
+ 80% {
+ transform: scale(1.15);
+ -webkit-transform: scale(1.15);
+ margin-top: -6px;
+ }
+ 100% {
+ transform: scale(1);
+ -webkit-transform: scale(1);
+ margin-top: 0;
+ opacity: 1;
+ }
+}
+
+.animateXMark {
+ -webkit-animation: animateXMark 0.5s;
+ animation: animateXMark 0.5s;
+}
+
+@-webkit-keyframes pulseWarning {
+ 0% {
+ border-color: #F8D486;
+ }
+ 100% {
+ border-color: #F8BB86;
+ }
+}
+
+@keyframes pulseWarning {
+ 0% {
+ border-color: #F8D486;
+ }
+ 100% {
+ border-color: #F8BB86;
+ }
+}
+
+.pulseWarning {
+ -webkit-animation: pulseWarning 0.75s infinite alternate;
+ animation: pulseWarning 0.75s infinite alternate;
+}
+
+@-webkit-keyframes pulseWarningIns {
+ 0% {
+ background-color: #F8D486;
+ }
+ 100% {
+ background-color: #F8BB86;
+ }
+}
+
+@keyframes pulseWarningIns {
+ 0% {
+ background-color: #F8D486;
+ }
+ 100% {
+ background-color: #F8BB86;
+ }
+}
+
+.pulseWarningIns {
+ -webkit-animation: pulseWarningIns 0.75s infinite alternate;
+ animation: pulseWarningIns 0.75s infinite alternate;
+}
+
+
+/* Internet Explorer 9 has some special quirks that are fixed here */
+
+
+/* The icons are not animated. */
+
+
+/* This file is automatically merged into sweet-alert.min.js through Gulp */
+
+
+/* Error icon */
+
+.sweet-alert .sa-icon.sa-error .sa-line.sa-left {
+ -ms-transform: rotate(45deg) \9;
+}
+
+.sweet-alert .sa-icon.sa-error .sa-line.sa-right {
+ -ms-transform: rotate(-45deg) \9;
+}
+
+
+/* Success icon */
+
+.sweet-alert .sa-icon.sa-success {
+ border-color: transparent\9;
+}
+
+.sweet-alert .sa-icon.sa-success .sa-line.sa-tip {
+ -ms-transform: rotate(45deg) \9;
+}
+
+.sweet-alert .sa-icon.sa-success .sa-line.sa-long {
+ -ms-transform: rotate(-45deg) \9;
+}
diff --git a/content/sweetalert.min.js2 b/webextension/content/sweetalert.min.js
similarity index 100%
rename from content/sweetalert.min.js2
rename to webextension/content/sweetalert.min.js
diff --git a/webextension/content/sweetalert_listener.js b/webextension/content/sweetalert_listener.js
new file mode 100644
index 0000000..d5366e7
--- /dev/null
+++ b/webextension/content/sweetalert_listener.js
@@ -0,0 +1,4 @@
+chrome.runtime.onMessage.addListener(function(data) {
+ if (data.destination !== 'sweetalert') return;
+ swal(data.args);
+});
diff --git a/webextension/content/testing/testing.js b/webextension/content/testing/testing.js
new file mode 100644
index 0000000..d50c869
--- /dev/null
+++ b/webextension/content/testing/testing.js
@@ -0,0 +1 @@
+//Do not remove this file
diff --git a/webextension/content/tlsn.js b/webextension/content/tlsn.js
new file mode 100644
index 0000000..88a566f
--- /dev/null
+++ b/webextension/content/tlsn.js
@@ -0,0 +1,2089 @@
+var is_chrome = navigator.userAgent.toLowerCase().indexOf('chrome') > -1;
+if (!is_chrome) {
+ if (typeof(win) !== "undefined") {
+ var window = win;
+ }
+}
+
+var global_tlsver = [0x03, 0x02];
+
+
+//#constants
+var md5_hash_len = 16;
+var sha1_hash_len = 20;
+var aes_block_size = 16;
+var tls_ver_1_0 = [3, 1];
+var tls_ver_1_1 = [3, 2];
+var tls_versions = [tls_ver_1_0, tls_ver_1_1];
+//#record types
+var appd = 0x17; //#Application Data
+var hs = 0x16; //#Handshake
+var chcis = 0x14; //#Change Cipher Spec
+var alrt = 0x15; //#Alert
+var tls_record_types = [appd, hs, chcis, alrt];
+//#handshake types
+var h_ch = 0x01; //#Client Hello
+var h_sh = 0x02; //#Server Hello
+var h_cert = 0x0b; //#Certificate
+var h_shd = 0x0e; //#Server Hello Done
+var h_cke = 0x10; //#Client Key Exchange
+var h_fin = 0x14; //#Finished
+var tls_handshake_types = [h_ch, h_sh, h_cert, h_shd, h_cke, h_fin];
+
+
+/*
+The amount of key material for each ciphersuite:
+AES256-CBC-SHA: mac key 20*2, encryption key 32*2, IV 16*2 == 136bytes
+AES128-CBC-SHA: mac key 20*2, encryption key 16*2, IV 16*2 == 104bytes
+RC4128_SHA: mac key 20*2, encryption key 16*2 == 72bytes
+RC4128_MD5: mac key 16*2, encryption key 16*2 == 64 bytes
+*/
+
+var tlsn_cipher_suites = [{
+ 47: ['AES128', 20, 20, 16, 16, 16, 16]
+ },
+ {
+ 53: ['AES256', 20, 20, 32, 32, 16, 16]
+ },
+ {
+ 5: ['RC4SHA', 20, 20, 16, 16, 0, 0]
+ },
+ {
+ 4: ['RC4MD5', 16, 16, 16, 16, 0, 0]
+ }
+];
+//#preprocessing: add the total number of bytes in the expanded keys format
+//#for each cipher suite, for ease of reference
+for (var i = 0; i < tlsn_cipher_suites.length; i++) {
+ var key = Object.keys(tlsn_cipher_suites[i])[0];
+ var sum = 0;
+ var values = tlsn_cipher_suites[i][key];
+ for (var j = 1; j < values.length; ++j) {
+ sum += values[j];
+ }
+ tlsn_cipher_suites[i][key].push(sum);
+}
+
+function get_cs(cs) {
+ for (var i = 0; i < tlsn_cipher_suites.length; i++) {
+ if (cs === parseInt(Object.keys(tlsn_cipher_suites[i])[0])) {
+ return tlsn_cipher_suites[i][cs];
+ }
+ }
+ throw ("Could not find cs " + cs.toString());
+}
+
+
+function send_and_recv(command, data, expected_response) {
+ return new Promise(function(resolve, reject) {
+ var req = get_xhr();
+ req.open("HEAD", "http://" + chosen_notary.IP + ":" + chosen_notary.port, true);
+ req.setRequestHeader("Request", command);
+ req.setRequestHeader("Data", b64encode(data));
+ req.setRequestHeader("UID", random_uid);
+ //disable headers which Firefox appends by default
+ //however, Chrome doesnt allow setting certain headers
+ req.setRequestHeader("Accept-Language", "");
+ req.setRequestHeader("Accept", "");
+ if (!is_chrome) {
+ req.setRequestHeader("Host", "");
+ req.setRequestHeader("User-Agent", "");
+ req.setRequestHeader("Accept-Encoding", "");
+ req.setRequestHeader("Connection", "close");
+ }
+ var timeout = setTimeout(function() {
+ reject('Timed out waiting for notary server to respond');
+ }, 20 * 1000);
+ req.onload = function() {
+ clearTimeout(timeout);
+ var response = req.getResponseHeader("Response");
+ if (response !== expected_response) {
+ reject('Unexpected response. Expected ' + expected_response + ' but got ' + response);
+ return;
+ }
+ var b64data = req.getResponseHeader("Data");
+ var data = b64decode(b64data);
+ console.log('got from oracle', response);
+ resolve(data);
+ };
+ console.log('sent to oracle', command);
+ req.send();
+ });
+}
+
+
+function prepare_pms(modulus, tryno) {
+ isdefined(modulus);
+ if (typeof(tryno) === "undefined") {
+ tryno = 1;
+ }
+ var rsapms2;
+ var random_rs = reliable_sites[Math.random() * (reliable_sites.length) << 0];
+ var rs_choice = random_rs.name;
+ console.log('random reliable site', rs_choice);
+ var pms_session = new TLSNClientSession();
+ pms_session.__init__({
+ 'server': rs_choice,
+ 'port': random_rs.port,
+ 'ccs': 53,
+ 'tlsver': global_tlsver
+ });
+ pms_session.server_modulus = random_rs.modulus;
+ pms_session.sckt = new Socket(pms_session.server_name, pms_session.ssl_port);
+ return pms_session.sckt.connect()
+ .then(function() {
+ pms_session.send_client_hello();
+ return pms_session.get_server_hello();
+ })
+ .then(function(handshake_objects) {
+ pms_session.process_server_hello(handshake_objects);
+ var comm = 'rcr_rsr_rsname_n';
+ var data = [].concat(pms_session.client_random, pms_session.server_random, str2ba(rs_choice).slice(0, 5), modulus);
+ return send_and_recv(comm, data, "rrsapms_rhmac_rsapms");
+ })
+ .then(function(reply_data) {
+ var rrsapms2 = reply_data.slice(0, 256);
+ pms_session.p_auditor = reply_data.slice(256, 304);
+ rsapms2 = reply_data.slice(304);
+ //assert(rsapms2.length === modulus.length, "rsapms2.length === modulus.length");
+ return pms_session.complete_handshake(rrsapms2);
+ })
+ .then(function(response) {
+ pms_session.sckt.close();
+ /*#judge success/fail based on whether a properly encoded
+ #Change Cipher Spec record is returned by the server (we could
+ #also check the server finished, but it isn't necessary)*/
+ var record_to_find = new TLSRecord();
+ record_to_find.__init__(chcis, [0x01], pms_session.tlsver);
+ if (response.toString().indexOf(record_to_find.serialized.toString()) < 0) {
+ console.log("PMS trial failed, retrying. (" + response.toString() + ")");
+ throw ("PMS trial failed");
+ }
+ return ([pms_session.auditee_secret, pms_session.auditee_padding_secret, rsapms2]);
+ });
+}
+
+
+function negotiate_crippled_secrets(tlsn_session) {
+ //'''Negotiate with auditor in order to create valid session keys
+ //(except server mac is garbage as auditor withholds it)'''
+ assert(tlsn_session.handshake_hash_md5 && tlsn_session.handshake_hash_sha);
+ tlsn_session.set_auditee_secret();
+ var s = tlsn_session;
+ var cs_cr_sr_hmacms_verifymd5sha = [].concat(s.chosen_cipher_suite, s.client_random, s.server_random,
+ s.p_auditee.slice(0, 24), s.handshake_hash_md5, s.handshake_hash_sha);
+ return send_and_recv('cs_cr_sr_hmacms_verifymd5sha', cs_cr_sr_hmacms_verifymd5sha, 'hmacms_hmacek_hmacverify')
+ .then(function(reply_data) {
+ var chosen_cs = get_cs(tlsn_session.chosen_cipher_suite);
+ var expanded_key_len = chosen_cs.slice(chosen_cs.length - 1)[0];
+ if (reply_data.length != 24 + expanded_key_len + 12) {
+ throw ('unexpected reply length in negotiate_crippled_secrets');
+ }
+ var hmacms = reply_data.slice(0, 24);
+ var hmacek = reply_data.slice(24, 24 + expanded_key_len);
+ var hmacverify = reply_data.slice(24 + expanded_key_len, 24 + expanded_key_len + 12);
+ tlsn_session.set_master_secret_half({
+ 'half': 2,
+ 'provided_p_value': hmacms
+ });
+ tlsn_session.p_master_secret_auditor = hmacek;
+ tlsn_session.do_key_expansion();
+ tlsn_session.send_client_finished(hmacverify);
+ return tlsn_session.get_server_finished();
+ })
+ .then(function(records) {
+ tlsn_session.process_server_finished(records);
+ var rv = tlsn_session.set_handshake_hashes({
+ 'server': true
+ });
+ var sha_digest2 = rv[0];
+ var md5_digest2 = rv[1];
+ return send_and_recv('verify_md5sha2', [].concat(md5_digest2, sha_digest2), 'verify_hmac2');
+ })
+ .then(function(verify_hmac2) {
+ if (!tlsn_session.check_server_ccs_finished(verify_hmac2)) {
+ throw ("Could not finish handshake with server successfully. Audit aborted");
+ }
+ });
+}
+
+
+
+function decrypt_html(tlsn_session) {
+ console.log("will decrypt cs:", tlsn_session.server_connection_state.cipher_suite);
+ var rv = tlsn_session.process_server_app_data_records();
+ var plaintext = rv[0];
+ var bad_mac = rv[1];
+ if (bad_mac) {
+ throw ("ERROR! Audit not valid! Plaintext is not authenticated.");
+ }
+ var plaintext_str = ba2str(plaintext);
+ var plaintext_dechunked = dechunk_http(plaintext_str);
+ var plaintext_gunzipped = gunzip_http(plaintext_dechunked);
+ console.log('returning plaintext of length ' + plaintext_gunzipped.length);
+ return plaintext_gunzipped;
+}
+
+
+function get_certificate(server, port) {
+ var probe_session = new TLSNClientSession();
+ probe_session.__init__({
+ 'server': server,
+ 'port': port,
+ 'tlsver': global_tlsver
+ });
+ probe_session.sckt = new Socket(probe_session.server_name, probe_session.ssl_port);
+ return probe_session.sckt.connect()
+ .then(function() {
+ probe_session.send_client_hello();
+ return probe_session.get_server_hello();
+ })
+ .then(function(handshake_objects) {
+ probe_session.process_server_hello(handshake_objects);
+ probe_session.sckt.close();
+ return probe_session.server_certificate.chain;
+ });
+}
+
+
+function start_audit(modulus, certhash, name, port, headers, ee_secret, ee_pad_secret, rsapms2) {
+ var tlsn_session = new TLSNClientSession();
+ tlsn_session.__init__({
+ 'server': name,
+ 'port': port,
+ 'tlsver': global_tlsver
+ });
+ tlsn_session.server_modulus = modulus;
+ tlsn_session.server_mod_length = modulus.length;
+ tlsn_session.auditee_secret = ee_secret;
+ tlsn_session.auditee_padding_secret = ee_pad_secret;
+ tlsn_session.enc_second_half_pms = rsapms2;
+ tlsn_session.set_enc_first_half_pms();
+ tlsn_session.set_encrypted_pms();
+ tlsn_session.sckt = new Socket(tlsn_session.server_name, tlsn_session.ssl_port);
+ var commit_hash;
+ var fullresp;
+ var signature;
+ var pms2;
+ return tlsn_session.sckt.connect().then(function() {
+ tlsn_session.send_client_hello();
+ return tlsn_session.get_server_hello();
+ })
+ .then(function(handshake_objects) {
+ tlsn_session.process_server_hello(handshake_objects);
+ console.log("negotiate_crippled_secrets");
+ return negotiate_crippled_secrets(tlsn_session);
+ })
+ .then(function() {
+ //#before sending any data to server compare this connection's cert to the
+ //#one which FF already validated earlier
+ if (sha256(tlsn_session.server_certificate.asn1cert).toString() != certhash.toString()) {
+ throw ('Certificate mismatch');
+ }
+ var headers_ba = str2ba(headers);
+ tlsn_session.build_request(headers_ba);
+ console.log("sent request");
+ return tlsn_session.sckt.recv(false); //#not handshake flag means we wait on timeout
+ })
+ .then(function(response) {
+ tlsn_session.store_server_app_data_records(response);
+ tlsn_session.sckt.close();
+ //#we return the full record set, not only the response to our request
+ //#prefix response with number of to-be-ignored records,
+ //#note: more than 256 unexpected records will cause a failure of audit. Just as well!
+ fullresp = [].concat(tlsn_session.unexpected_server_app_data_count,
+ tlsn_session.unexpected_server_app_data_raw, response);
+ commit_hash = sha256(fullresp);
+ return send_and_recv('commit_hash', commit_hash, 'pms2');
+ })
+ .then(function(response) {
+ pms2 = response.slice(0, 24);
+ signature = response.slice(24, 536);
+ var time = response.slice(536, 540);
+ var modulus = chosen_notary.modulus;
+ var signed_data = sha256([].concat(commit_hash, pms2, tlsn_session.server_modulus, time));
+ if (!verify_commithash_signature(signed_data, signature, modulus)) {
+ throw ('Failed to verify notary server signature');
+ }
+ tlsn_session.auditor_secret = pms2.slice(0, tlsn_session.n_auditor_entropy);
+ tlsn_session.set_auditor_secret();
+ tlsn_session.set_master_secret_half(); //#without arguments sets the whole MS
+ tlsn_session.do_key_expansion(); //#also resets encryption connection state
+
+ //#decrypt and verify mac of server finished as normal
+ if (tlsn_session.mac_check_server_finished() !== true) {
+ throw ('Failed to verify MAC for server finished');
+ }
+ var plaintext = decrypt_html(tlsn_session);
+ return [tlsn_session.chosen_cipher_suite,
+ tlsn_session.client_random,
+ tlsn_session.server_random,
+ tlsn_session.pms1,
+ tlsn_session.pms2,
+ tlsn_session.server_certificate.chain,
+ tlsn_session.tlsver,
+ tlsn_session.initial_tlsver,
+ fullresp.length,
+ fullresp,
+ tlsn_session.IV_after_finished.length,
+ tlsn_session.IV_after_finished,
+ chosen_notary.modulus.length,
+ signature,
+ commit_hash,
+ chosen_notary.modulus,
+ plaintext,
+ time
+ ];
+ });
+}
+
+
+function verify_commithash_signature(commithash, signature, modulus) {
+ //RSA verification is sig^e mod n, drop the padding and get the last 32 bytes
+ var bigint_signature = new BigInteger(ba2hex(signature), 16);
+ var bigint_mod = new BigInteger(ba2hex(modulus), 16);
+ var bigint_exp = new BigInteger(ba2hex(bi2ba(65537)), 16);
+ var bigint_result = bigint_signature.modPow(bigint_exp, bigint_mod);
+ var padded_hash = hex2ba(bigint_result.toString(16));
+ //check PKCS v1.5 padding 0001 || ff ff ... ff ff || 00 || data
+ if (padded_hash[0] !== 1 || padded_hash[478] !== 0) {
+ return false;
+ }
+ var arrayOfFFs = padded_hash.slice(1, 478);
+ for (var i = 0; i < arrayOfFFs.length; i++) {
+ if (arrayOfFFs[i] !== 255) return false;
+ }
+
+ var hash = padded_hash.slice(padded_hash.length - 32);
+ if (commithash.toString() === hash.toString()) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+
+
+function tls_record_decoder(d) {
+ /*'Given a binary data stream d,
+ separate it into TLS records and return
+ as a list of TLSRecord objects. If no
+ TLS record is found at the start of the stream,
+ return False. If any additional data is found
+ at the end of the final record, it is returned
+ as the second part of the returned tuple.
+ Note that record length is only validated here
+ in the decoder.*/
+ var records = [];
+ var remaining = [];
+ if (tls_record_types.indexOf(d[0]) < 0) {
+ return false;
+ }
+ while (d) {
+ var rt = d[0];
+ if (tls_record_types.indexOf(rt) < 0) {
+ remaining = d;
+ break;
+ }
+ var ver = d.slice(1, 3);
+ var version_found = false;
+ for (var i = 0; i < tls_versions.length; i++) {
+ if (tls_versions[i].toString() === ver.toString()) {
+ version_found = true;
+ break;
+ }
+ }
+ assert(version_found, "Incompatible TLS version");
+ var l = ba2int(d.slice(3, 5));
+ if (d.length < l + 5) {
+ throw ("incomplete TLS record");
+ }
+ var fragment = d.slice(5, 5 + l);
+ d = d.slice(5 + l);
+ var rec = new TLSRecord();
+ rec.__init__(rt, fragment, ver);
+ records.push(rec);
+ }
+ return [records, remaining];
+}
+
+
+function tls_record_fragment_decoder(t, d, args) {
+ /*'''Given the record type t and the data fragment d,
+ we construct as many objects of that type as we can find
+ in the fragment and return them as a list of Python objects.
+ If conn is not None, the record fragment is assumed to be
+ encrypted and is decrypted before processing. ''' */
+
+ //#dictionary to allow dynamic decoding of a handshake message in a record fragment
+ var hs_type_map = {};
+ hs_type_map[h_ch] = TLSClientHello;
+ hs_type_map[h_sh] = TLSServerHello;
+ hs_type_map[h_cert] = TLSCertificate;
+ hs_type_map[h_cke] = TLSClientKeyExchange;
+ hs_type_map[h_fin] = TLSFinished;
+ hs_type_map[h_shd] = TLSServerHelloDone;
+
+ var conn = null;
+ var ignore_mac = false;
+ var plaintext;
+ var validity;
+ var mac;
+ var rv;
+ if (typeof(args) !== "undefined") {
+ if (typeof(args.conn) !== "undefined") {
+ conn = args.conn;
+ }
+ if (typeof(args.ignore_mac) !== "undefined") {
+ ignore_mac = args.ignore_mac;
+ }
+ }
+ hlpos = [];
+ if (conn) {
+ if (ignore_mac) { //means we won't check it now, but store to be checked later
+ rv = conn.dtvm(d, t, true);
+ validity = rv[0];
+ plaintext = rv[1];
+ mac = rv[2];
+ } else {
+ rv = conn.dtvm(d, t);
+ validity = rv[0];
+ plaintext = rv[1];
+ }
+ if (!validity && !ignore_mac) {
+ throw ("Mac failure");
+ }
+ } else {
+ plaintext = d;
+ }
+ while (plaintext.length) {
+ if (t === hs) {
+ var hs_types = [];
+ var hs_types_keys = Object.keys(hs_type_map);
+ for (var i = 0; i < hs_types_keys.length; i++) {
+ hs_types.push(parseInt(hs_types_keys[i]));
+ }
+ assert(hs_types.indexOf(plaintext[0]) > -1, "Invalid handshake type");
+ constructed_obj = new hs_type_map[plaintext[0]]();
+ constructed_obj.__init__({
+ 'serialized': plaintext
+ });
+ } else if (t === appd) {
+ constructed_obj = new TLSAppData();
+ constructed_obj.__init__(plaintext);
+ } else if (t === alrt) {
+ constructed_obj = new TLSAlert();
+ constructed_obj.__init__({
+ 'serialized': plaintext
+ });
+ } else if (t === chcis) {
+ constructed_obj = new TLSChangeCipherSpec();
+ constructed_obj.__init__({
+ 'serialized': plaintext
+ });
+ } else {
+ throw ("Invalid record type");
+ }
+ hlpos.push(constructed_obj);
+ plaintext = constructed_obj.discarded;
+ }
+ if (conn) {
+ //#Note this assumes that only ONE encrypted message
+ hlpos[0].encrypted = d;
+ if (ignore_mac) {
+ hlpos[0].recorded_mac = mac;
+ }
+ }
+ return hlpos;
+}
+
+
+function TLSRecord() {}
+TLSRecord.prototype.__init__ = function(ct, fragment, tlsver) {
+ this.content_type = ct;
+ this.content_version = tlsver;
+ if (fragment) {
+ this.fragment = fragment;
+ this._length = this.fragment.length;
+ this.serialize();
+ }
+};
+TLSRecord.prototype.serialize = function() {
+ var check_contents = this.content_type && this.content_version && this._length && this.fragment;
+ assert(check_contents, "Cannot serialize record, data incomplete");
+ assert(this.fragment.length == this._length, "Incorrect record length");
+ this.serialized = [].concat(this.content_type, this.content_version, bi2ba(this._length, {
+ 'fixed': 2
+ }), this.fragment);
+};
+
+
+
+function TLSHandshake() {}
+TLSHandshake.prototype.__init__ = function(serialized, handshake_type) {
+ if (typeof(serialized) === 'undefined') serialized = null;
+ if (typeof(handshake_type) === 'undefined') handshake_type = null;
+ this.handshake_type = handshake_type;
+ if (serialized) {
+ this.serialized = serialized;
+ assert(this.handshake_type == this.serialized[0], "Mismatched handshake type");
+ assert([h_ch, h_sh, h_shd, h_cert, h_cke, h_fin].indexOf(this.handshake_type) > -1,
+ 'Unrecognized or unimplemented handshake type');
+ this.handshake_record_length = ba2int(this.serialized.slice(1, 4));
+ if (this.serialized.slice(4).length < this.handshake_record_length) {
+ throw ('Invalid handshake message length');
+ }
+ this.discarded = this.serialized.slice(4 + this.handshake_record_length);
+ /*if (this.discarded){
+ log ('Info: got a discarded data when constructing',
+ 'a handshake message of type: ', this.handshake_type.toString(),
+ ' and discarded length was: ', this.discarded.length);
+ }
+ * */
+ //#Note that we do *not* strip handshake headers for the serialized form;
+ //#this is a complete, valid handshake message.
+ this.serialized = this.serialized.slice(0, 4 + this.handshake_record_length);
+ }
+};
+TLSHandshake.prototype.serialize = function() {
+ if (typeof(this.serialized) === "undefined") {
+ this.serialized = [];
+ }
+ var len = bi2ba(this.serialized.length, {
+ 'fixed': 3
+ });
+ this.serialized = [].concat(this.handshake_type, len, this.serialized);
+};
+
+
+
+//inheritance
+TLSClientHello.prototype = new TLSHandshake();
+TLSClientHello.prototype.constructor = TLSClientHello;
+TLSClientHello.prototype.parent = TLSHandshake.prototype;
+
+function TLSClientHello() {}
+TLSClientHello.prototype.__init__ = function(args) {
+ var serialized = args.serialized;
+ var client_random = args.client_random;
+ var cipher_suites = args.cipher_suites;
+ var tlsver = args.tlsver;
+ var i;
+ this.typename = "TLSClientHello";
+ //TODO default args here
+ if (serialized) {
+ print('Not implemented instantiation of client hello',
+ 'with serialization; this is a client-only',
+ ' TLS implementation');
+ } else {
+ if (client_random) {
+ this.client_random = client_random;
+ } else {
+ var cr = [];
+ for (i = 0; i < 32; i++) {
+ cr.push(2);
+ }
+ //this.client_random = cr;
+ this.client_random = getRandom(32, window);
+
+ }
+ this.tlsver = tlsver;
+ this.serialized = [].concat(this.tlsver, this.client_random, 0);
+ this.cipher_suites = cipher_suites;
+ this.serialized = [].concat(this.serialized, 0x00, 2 * this.cipher_suites.length);
+ for (i = 0; i < this.cipher_suites.length; i++) {
+ var cs = this.cipher_suites[i];
+ this.serialized = [].concat(this.serialized, 0x00, cs);
+ }
+ this.serialized = [].concat(this.serialized, 0x01, 0x00); //compression methods - null only
+ //call sod in the context of this instance
+ TLSHandshake.prototype.__init__.call(this, null, h_ch);
+ TLSHandshake.prototype.serialize.call(this);
+ }
+};
+
+
+
+TLSServerHello.prototype = new TLSHandshake();
+TLSServerHello.prototype.constructor = TLSServerHello;
+TLSServerHello.prototype.parent = TLSHandshake.prototype;
+
+function TLSServerHello() {}
+TLSServerHello.prototype.__init__ = function(args) {
+ var serialized = args.serialized;
+ var server_random = args.server_random;
+ var cipher_suites = args.cipher_suites;
+ this.typename = "TLSServerHello";
+ if (serialized) {
+ this.parent.__init__(serialized, h_sh);
+ this.tlsver = this.serialized.slice(4, 6);
+ this.server_random = this.serialized.slice(6, 38);
+ this.session_id_length = ba2int([].concat(this.serialized[38]));
+ var remainder;
+ if (this.session_id_length !== 0) {
+ assert(this.session_id_length == 32, 'Server hello contains unrecognized session id format');
+ this.session_id = this.serialized.slice(39, 71);
+ remainder = this.serialized.slice(71);
+ } else {
+ remainder = this.serialized.slice(39);
+ this.session_id = null;
+ }
+
+ this.cipher_suite = ba2int(remainder.slice(0, 2));
+ var cs_keys = [];
+ for (var i = 0; i < tlsn_cipher_suites.length; i++) {
+ var key = Object.keys(tlsn_cipher_suites[i])[0];
+ cs_keys.push(parseInt(key));
+ }
+ assert(cs_keys.indexOf(this.cipher_suite) > -1,
+ 'Server chosen cipher suite not in TLS Notary allowed list, it was: ' + this.cipher_suite.toString());
+ assert(remainder.slice(2).toString() === [0x00].toString(), 'Received invalid server hello compression method');
+ //#At end of serialized instantiation, we have defined server
+ //#random and cipher suite
+ } else {
+ print("Not implemented instantiation of server hello without serialization; this is a client-only TLS implementation");
+ }
+};
+
+
+
+TLSCertificate.prototype = new TLSHandshake();
+TLSCertificate.prototype.constructor = TLSCertificate;
+TLSServerHello.prototype.parent = TLSHandshake.prototype;
+
+function TLSCertificate() {}
+TLSCertificate.prototype.__init__ = function(args) {
+ var serialized = args.serialized;
+ if (serialized) {
+ TLSHandshake.prototype.__init__.call(this, serialized, h_cert);
+ /*#This handshake message has format: hs_cert(1), hs_msg_len(3),
+ #certs_list_msg_len(3), [cert1_msg_len(3), cert1, cert_msg_len(3), cert2...]
+ #so the first cert data starts at byte position 10 */
+ this.cert_len = ba2int(this.serialized.slice(7, 10));
+ this.asn1cert = this.serialized.slice(10, 10 + this.cert_len);
+ var listlen = ba2int(this.serialized.slice(4, 7))
+ assert(this.serialized.length === listlen + 7);
+ var offset = 7;
+ var certlen;
+ var cert;
+ var chain = [];
+ while (offset < this.serialized.length - 1) {
+ certlen = ba2int(this.serialized.slice(offset, offset + 3));
+ offset += 3;
+ cert = this.serialized.slice(offset, offset + certlen);
+ offset += certlen;
+ chain.push(cert);
+ }
+ this.chain = chain;
+ this.typename = "TLSCertificate";
+ } else {
+ print('Not implemented instantiation of certificate without serialization; this is a client-only TLS implementation');
+ }
+};
+
+
+
+TLSServerHelloDone.prototype = new TLSHandshake();
+TLSServerHelloDone.prototype.constructor = TLSServerHelloDone;
+TLSServerHelloDone.prototype.parent = TLSHandshake.prototype;
+
+function TLSServerHelloDone() {}
+TLSServerHelloDone.prototype.__init__ = function(args) {
+ var serialized = args.serialized;
+ this.typename = "TLSServerHelloDone";
+ if (serialized) {
+ //call parent method in the context of this instance
+ TLSHandshake.prototype.__init__.call(this, serialized, h_shd);
+ } else {
+ print('Not implemented instantiation of server hello done without serialization; this is a client-only TLS implementation');
+ }
+};
+
+
+TLSClientKeyExchange.prototype = new TLSHandshake();
+TLSClientKeyExchange.prototype.constructor = TLSClientKeyExchange;
+TLSClientKeyExchange.prototype.parent = TLSHandshake.prototype;
+
+function TLSClientKeyExchange() {}
+TLSClientKeyExchange.prototype.__init__ = function(args) {
+ var serialized = null;
+ var encryptedPMS = null;
+ if (typeof(args) !== "undefined") {
+ if (typeof(args.serialized) !== "undefined") {
+ serialized = args.serialized;
+ }
+ if (typeof(args.encryptedPMS) !== "undefined") {
+ encryptedPMS = args.encryptedPMS;
+ }
+
+ }
+ this.typename = "TLSClientKeyExchange";
+ if (serialized) {
+ print('Not implemented instantiation of client key exchange with serialization; this is a client-only TLS implementation');
+ } else {
+ if (encryptedPMS) {
+ this.encryptedPMS = encryptedPMS;
+ }
+ //#Note that the encpms is preceded by its 2-byte length
+ this.serialized = [].concat(bi2ba(this.encryptedPMS.length, {
+ 'fixed': 2
+ }), this.encryptedPMS);
+ TLSHandshake.prototype.__init__.call(this, null, h_cke);
+ TLSHandshake.prototype.serialize.call(this);
+ }
+};
+
+
+function TLSChangeCipherSpec() {}
+TLSChangeCipherSpec.prototype.__init__ = function(args) {
+ var serialized = null;
+ if (typeof(args) !== "undefined") {
+ if (typeof(args.serialized) !== "undefined") {
+ serialized = args.serialized;
+ }
+ }
+ if (serialized) {
+ this.serialized = serialized;
+ assert(this.serialized[0] == 0x01, 'Invalid change cipher spec received');
+ this.discarded = this.serialized.slice(1);
+ this.serialized = this.serialized[0];
+ } else {
+ this.serialized = [0x01];
+ }
+ this.typename = "TLSChangeCipherSpec";
+};
+
+
+
+TLSFinished.prototype = new TLSHandshake();
+TLSFinished.prototype.constructor = TLSFinished;
+TLSFinished.prototype.parent = TLSHandshake.prototype;
+
+function TLSFinished() {}
+TLSFinished.prototype.__init__ = function(args) {
+ var serialized = null;
+ var verify_data = null;
+ this.typename = "TLSFinished";
+ if (typeof(args) !== "undefined") {
+ if (typeof(args.serialized) !== "undefined") {
+ serialized = args.serialized;
+ }
+ if (typeof(args.verify_data) !== "undefined") {
+ verify_data = args.verify_data;
+ }
+ }
+ if (serialized) {
+ TLSHandshake.prototype.__init__.call(this, serialized, h_fin);
+ this.validity = null;
+ this.verify_data = this.serialized.slice(4);
+ } else {
+ this.serialized = verify_data;
+ TLSHandshake.prototype.__init__.call(this, null, h_fin);
+ TLSHandshake.prototype.serialize.call(this);
+ }
+};
+TLSFinished.prototype.decrypt_verify_data = function(conn) {
+ this.encrypted = this.verify_data; //#the encrypted form is kept for later processing
+ var rv = conn.dtvm(this.verify_data, hs);
+ this.validity = rv[0];
+ this.verify_data = rv[1];
+};
+
+
+
+function TLSAppData() {}
+TLSAppData.prototype.__init__ = function(serialized, args) {
+ var encrypted = false;
+ if (typeof(args) !== "undefined") {
+ if (typeof(args.encrypted) !== "undefined") {
+ encrypted = args.encrypted;
+ }
+ }
+ /*#App Data is 'transparent' to the Record protocol layer
+ #(I borrow this slighly, ahem, opaque language from the
+ #RFC Section 10). This means that there is no notion of
+ #'length of an app data message'. Nor is there any meaning
+ #to the concept of 'serialization' in this context, since
+ #there is no structure. However the terminology is kept
+ #the same as other record types, for consistency.*/
+ this.serialized = serialized;
+ this.discarded = '';
+ this.typename = "TLSAppData";
+};
+TLSAppData.prototype.decrypt_app_data = function(conn) {
+ this.serialized = conn.dtvm(this.serialized, appd);
+};
+
+
+
+function TLSAlert() {}
+TLSAlert.prototype.__init__ = function(args) {
+ var serialized = args.serialized;
+ if (serialized) {
+ this.serialized = serialized;
+ this.discarded = '';
+ this.typename = "TLSAlert";
+ } else {
+ throw ("Alert creation not implemented");
+ }
+};
+
+
+
+
+function TLSConnectionState() {
+ /*Note that this implementation of connection
+ state uses the pre-computed expanded keys rather
+ than generating the secrets within it. A corollary
+ of this is that there is no need for this encapsulation
+ for the unencrypted portion of the TLS connection, and
+ so this object is only initiated once TLSNotary key
+ expansion is performed (after negotiation with auditor).
+ Mac failures should be treated as fatal in TLS, but
+ for specific cases in TLSNotary, the mac check is delayed,
+ hence mac failure is returned as False rather than raising
+ an exception.*/
+}
+TLSConnectionState.prototype.__init__ = function(cipher_suite, expanded_keys, is_client, tlsver) {
+ /*Provide the cipher suite as defined in the global
+ cipher suite list.
+ Currently only AES-CBC and RC4 cipher suites are
+ supported.
+ The format of expanded_keys must be as required
+ by the specified cipher suite.
+ If mac failures occur they will be flagged but
+ decrypted result is still made available.*/
+ this.tlsver = tlsver; //either TLS1.0 or 1.1
+ var version_found = false;
+ for (var i = 0; i < tls_versions.length; i++) {
+ if (tls_versions[i].toString() === this.tlsver.toString()) {
+ version_found = true;
+ break;
+ }
+ }
+ assert(version_found, "Unrecognised or invalid TLS version");
+ this.cipher_suite = cipher_suite;
+ if (is_client) {
+ this.end = 'client';
+ } else {
+ this.end = 'server';
+ }
+ if (cipher_suite === 4) {
+ this.mac_algo = 'md5';
+ } else {
+ this.mac_algo = 'sha1';
+ }
+ if (this.mac_algo === 'md5') {
+ this.hash_len = md5_hash_len;
+ } else {
+ this.hash_len = sha1_hash_len;
+ }
+ //set appropriate secrets for state
+ this.client_mac_key = expanded_keys[0];
+ this.server_mac_key = expanded_keys[1];
+ this.client_enc_key = expanded_keys[2];
+ this.server_enc_key = expanded_keys[3];
+ this.clientIV = expanded_keys[4];
+ this.serverIV = expanded_keys[5];
+ if (this.end == 'client') {
+ this.mac_key = this.client_mac_key;
+ this.enc_key = this.client_enc_key;
+ this.IV = this.clientIV;
+ } else {
+ this.mac_key = this.server_mac_key;
+ this.enc_key = this.server_enc_key;
+ this.IV = this.serverIV;
+ }
+ this.seq_no = 0;
+};
+TLSConnectionState.prototype.build_record_mac = function(cleartext, record_type) {
+ var seq_no_bytes = bi2ba(this.seq_no, {
+ 'fixed': 8
+ });
+ assert(this.mac_key, "Failed to build mac; mac key is missing");
+ var fragment_len = bi2ba(cleartext.length, {
+ 'fixed': 2
+ });
+ var record_mac = hmac(this.mac_key, [].concat(seq_no_bytes, record_type, this.tlsver, fragment_len, cleartext), this.mac_algo);
+ return record_mac;
+};
+TLSConnectionState.prototype.mte = function(cleartext, rec_type) {
+ if ([4, 5].indexOf(this.cipher_suite) > -1) {
+ return this.rc4_me(cleartext, rec_type);
+ } else {
+ return this.aes_cbc_mpe(cleartext, rec_type);
+ }
+};
+TLSConnectionState.prototype.dtvm = function(cleartext, rec_type, return_mac) {
+ //'''Decrypt then verify mac'''
+ if (typeof(return_mac) === "undefined") {
+ return_mac = false;
+ }
+ var retval;
+ if ([4, 5].indexOf(this.cipher_suite) > -1) {
+ retval = this.rc4_dm(cleartext, rec_type, return_mac);
+ return retval;
+ } else {
+ retval = this.aes_cbc_dum(cleartext, rec_type, return_mac);
+ return retval;
+ }
+};
+TLSConnectionState.prototype.verify_mac = function(cleartext, rec_type, args) {
+ var return_mac = false;
+ if (typeof(args) !== "undefined") {
+ if (typeof(args.return_mac) !== "undefined") {
+ return_mac = args.return_mac;
+ }
+ }
+ var len_wo_mac = cleartext.length - this.hash_len; //length without mac
+ var received_mac = cleartext.slice(len_wo_mac);
+ var check_mac = this.build_record_mac(cleartext.slice(0, len_wo_mac), rec_type);
+ this.seq_no += 1;
+ var validity = false;
+ if (return_mac) {
+ validity = (received_mac.toString() === check_mac.toString());
+ return [validity, cleartext.slice(0, len_wo_mac), received_mac];
+ } else {
+ validity = (received_mac.toString() === check_mac.toString());
+ return [validity, cleartext.slice(0, len_wo_mac)];
+ }
+};
+TLSConnectionState.prototype.rc4_me = function(cleartext, rec_type) {
+ //#mac
+ cleartext = [].concat(cleartext, this.build_record_mac(cleartext, rec_type));
+ //#encrypt
+ //#note: for RC4, the 'IV' is None at the start,
+ //#which tells the RC4 to initialize state
+ var rv = rc4_crypt(cleartext, this.enc_key, this.IV);
+ var ciphertext = rv[0];
+ this.IV = rv[1];
+ this.seq_no += 1;
+ return ciphertext;
+};
+TLSConnectionState.prototype.rc4_dm = function(ciphertext, rec_type, args) {
+ var return_mac = args.return_mac;
+ //#decrypt
+ var rv = rc4_crypt(ciphertext, this.enc_key, this.IV);
+ var plaintext = rv[0];
+ this.IV = rv[1];
+ //#mac check
+ return this.verify_mac(plaintext, rec_type, return_mac);
+};
+TLSConnectionState.prototype.aes_cbc_mpe = function(cleartext, rec_type) {
+ //#mac
+ cleartext = [].concat(cleartext, this.build_record_mac(cleartext, rec_type));
+ //#pad
+ var padded_cleartext = [].concat(cleartext, get_cbc_padding(cleartext.length));
+ var ciphertext = aes_encrypt(padded_cleartext, this.enc_key, this.IV);
+ if (this.tlsver.toString() === tls_ver_1_0.toString()) {
+ this.IV = ciphertext.slice(ciphertext.length - aes_block_size);
+ } else if (this.tlsver.toString() === tls_ver_1_1.toString()) {
+ //#the per-record IV is now sent as the start of the fragment
+ ciphertext = [].concat(this.IV, ciphertext);
+ this.IV = getRandom(aes_block_size, window); //#use a new, random IV for each record
+ }
+ this.seq_no += 1;
+ return ciphertext;
+};
+TLSConnectionState.prototype.aes_cbc_dum = function(ciphertext, rec_type, return_mac) {
+ if (typeof(return_mac) === "undefined") {
+ return_mac = false;
+ }
+ //#decrypt
+ if (this.tlsver.toString() === tls_ver_1_1.toString()) {
+ this.IV = ciphertext.slice(0, aes_block_size);
+ ciphertext = ciphertext.slice(aes_block_size);
+ }
+ //#else self.IV already stores the correct IV
+ var decrypted = aes_decrypt(ciphertext, this.enc_key, this.IV);
+ if (this.tlsver.toString() === tls_ver_1_0.toString()) {
+ this.IV = ciphertext.slice(ciphertext.length - aes_block_size);
+ }
+ //#unpad
+ var plaintext = cbc_unpad(decrypted);
+ //#mac check
+ var retval = this.verify_mac(plaintext, rec_type, {
+ 'return_mac': return_mac
+ });
+ return retval;
+};
+
+
+
+
+
+function tls_sender(sckt, msg, rec_type, tlsver, conn) {
+ /*'''Wrap a message in a TLS Record before sending
+ If conn argument provided, encrypt the payload
+ before sending'''*/
+ if (typeof(conn) !== "undefined" && conn !== null) {
+ msg = conn.mte(msg, rec_type);
+ }
+ var rec = new TLSRecord();
+ rec.__init__(rec_type, msg, tlsver);
+ return sckt.send(rec.serialized);
+}
+
+function recv_socket(sckt, is_handshake) {
+ return sckt.recv(sckt, is_handshake);
+}
+
+
+
+function TLSNClientSession() {}
+TLSNClientSession.prototype.__init__ = function(args) {
+ if (typeof(args) !== 'undefined') {
+ var server = args.server;
+ var port = args.port;
+ var ccs = args.ccs;
+ var tlsver = args.tlsver;
+ if (typeof(server) === 'undefined') server = null;
+ if (typeof(port) === 'undefined') port = 443;
+ if (typeof(ccs) === 'undefined') ccs = null;
+ if (typeof(tlsver) === 'undefined') tlsver = null;
+ }
+
+ this.server_name = server;
+ this.ssl_port = port;
+ this.sckt = null;
+ this.initial_tlsver = tlsver;
+ //#current TLS version may be downgraded
+ this.tlsver = tlsver;
+ this.n_auditee_entropy = 12;
+ this.n_auditor_entropy = 9;
+ this.auditor_secret = null;
+ this.auditee_secret = null;
+ this.auditor_padding_secret = null;
+ this.auditee_padding_secret = null;
+ this.pms1 = null; //#auditee's
+ this.pms2 = null; //#auditor's
+ this.enc_first_half_pms = null;
+ this.enc_second_half_pms = null;
+ this.enc_pms = null;
+ //#client hello, server hello, certificate, server hello done,
+ //#client key exchange, change cipher spec, finished
+ this.handshake_messages = [null, null, null, null, null, null, null];
+ this.handshake_hash_sha = null;
+ this.handshake_hash_md5 = null;
+ this.p_auditor = null;
+ this.p_auditee = null;
+ this.master_secret_half_auditor = null;
+ this.master_secret_half_auditee = null;
+ this.p_master_secret_auditor = null;
+ this.p_master_secret_auditee = null;
+ this.server_mac_key = null;
+ this.client_mac_key = null;
+ this.server_enc_key = null;
+ this.client_enc_key = null;
+ this.serverIV = null;
+ this.clientIV = null;
+ this.server_certificate = null;
+ this.server_modulus = null;
+ this.server_exponent = 65537;
+ this.server_mod_length = null;
+
+ //#array of ciphertexts from each SSL record
+ this.server_response_app_data = [];
+
+ //#unexpected app data is defined as that received after
+ //#server finished, but before client request. This will
+ //#be decrypted, but not included in plaintext result.
+ this.unexpected_server_app_data_count = 0;
+ this.unexpected_server_app_data_raw = [];
+
+ /*#the HMAC required to construct the verify data
+ #for the server Finished record
+ self.verify_hmac_for_server_finished = None
+
+ #for certain testing cases we want to limit the
+ #choice of cipher suite to 1, otherwise we use
+ #the globally defined standard 4: */
+ if (ccs) {
+ this.offered_cipher_suites = [];
+ var cs = {};
+ cs[ccs] = get_cs(ccs);
+ this.offered_cipher_suites.push(cs);
+ } else {
+ this.offered_cipher_suites = tlsn_cipher_suites;
+ }
+ this.chosen_cipher_suite = ccs;
+};
+
+TLSNClientSession.prototype.dump = function() {
+ //XXX implement this
+};
+
+TLSNClientSession.prototype.send_client_hello = function() {
+ var offered_cs_keys = [];
+ for (var i = 0; i < this.offered_cipher_suites.length; i++) {
+ var cs = Object.keys(this.offered_cipher_suites[i])[0];
+ //cast cs to int, otherwise we'll have strings
+ offered_cs_keys.push(parseInt(cs));
+ }
+ this.client_hello = new TLSClientHello();
+ this.client_hello.__init__({
+ 'cipher_suites': offered_cs_keys,
+ 'tlsver': this.tlsver
+ });
+ this.handshake_messages[0] = this.client_hello.serialized;
+
+ tls_sender(this.sckt, this.handshake_messages[0], hs, this.tlsver, null);
+}
+
+
+TLSNClientSession.prototype.get_server_hello = function() {
+ /* #the handshake messages: server hello, certificate, server hello done
+ #may be packed in arbitrary groupings into the TLS records, since
+ #they are all the same record type (Handshake) */
+
+ var handshake_objects = [];
+ var sckt = this.sckt;
+ return new Promise(function(resolve, reject) {
+ var loop = function(resolve, reject) {
+ console.log('get_server_hello next iteration');
+ sckt.recv(true)
+ .then(function(rspns) {
+ console.log('returned from sckt.recv with length', rspns.length);
+ var rv = tls_record_decoder(rspns);
+ var records = rv[0];
+ var remaining = rv[1];
+ assert(remaining.length === 0, "Server sent spurious non-TLS response");
+ if (records.length === 1) {
+ if (records[0].content_type === alrt) {
+ reject('Server sent alert ' + records[0].fragment.toString());
+ return;
+ }
+ }
+ for (var i = 0; i < records.length; i++) {
+ var decoded = tls_record_fragment_decoder(hs, records[i].fragment);
+ handshake_objects = [].concat(handshake_objects, decoded);
+ }
+ if (handshake_objects.length < 3) {
+ console.log('get_server_hello handshake_objects.length < 3');
+ loop(resolve, reject);
+ return;
+ }
+ //else
+ resolve(handshake_objects);
+ })
+ .catch(function(e) {
+ reject(e);
+ });
+ };
+ loop(resolve, reject);
+ });
+}
+
+
+TLSNClientSession.prototype.process_server_hello = function(handshake_objects) {
+
+ var handshake_types = [];
+ for (i = 0; i < handshake_objects.length; i++) {
+ var handshake_type = handshake_objects[i].handshake_type;
+ handshake_types.push(handshake_type);
+ }
+ assert(handshake_types.indexOf(h_sh) >= 0 && handshake_types.indexOf(h_cert) >= 0 &&
+ handshake_types.indexOf(h_shd) >= 0,
+ "Server failed to send server hello, certificate, server hello done");
+ this.server_hello = handshake_objects[0];
+ this.server_certificate = handshake_objects[1];
+ this.server_hello_done = handshake_objects[2];
+
+ this.handshake_messages[1] = handshake_objects[0].serialized;
+ this.handshake_messages[2] = handshake_objects[1].serialized;
+ this.handshake_messages[3] = handshake_objects[2].serialized;
+
+ this.client_random = this.client_hello.client_random;
+ this.server_random = this.server_hello.server_random;
+ this.chosen_cipher_suite = this.server_hello.cipher_suite;
+
+ if (this.server_hello.tlsver.toString() !== this.tlsver.toString()) {
+ if ((this.server_hello.tlsver.toString() === [0x03, 0x01].toString()) &&
+ (this.tlsver.toString() === [0x03, 0x02].toString())) {
+ /*#server requested downgrade
+ #note that this can only happen *before* a TLSConnectionState object is
+ #initialised, so the tlsversion used in that object will be synchronised.
+ #TODO: error checking to make sure this is the case.*/
+ this.tlsver = [0x03, 0x01];
+ } else {
+ throw ("Failed to negotiate valid TLS version with server");
+ }
+ }
+ //#for 'full' sessions, we can immediately precompute everything except
+ //#for finished, including the handshake hashes used to calc the Finished
+ if (this.enc_pms) {
+ this.client_key_exchange = new TLSClientKeyExchange();
+ this.client_key_exchange.__init__({
+ 'serialized': null,
+ 'encryptedPMS': this.enc_pms
+ });
+ this.change_cipher_spec = new TLSChangeCipherSpec();
+ this.change_cipher_spec.__init__();
+ this.handshake_messages[4] = this.client_key_exchange.serialized;
+ this.handshake_messages[5] = this.change_cipher_spec.serialized;
+ this.set_handshake_hashes();
+ }
+};
+
+TLSNClientSession.prototype.get_verify_data_for_finished = function(args) {
+ var sha_verify = null;
+ var md5_verify = null;
+ var half = 1;
+ var provided_p_value = null;
+ var is_for_client = true;
+ if (typeof(args.sha_verify) !== "undefined") {
+ sha_verify = args.sha_verify;
+ }
+ if (typeof(args.md5_verify) !== "undefined") {
+ md5_verify = args.md5_verify;
+ }
+ if (typeof(args.half) !== "undefined") {
+ half = args.half;
+ }
+ if (typeof(args.provided_p_value) !== "undefined") {
+ provided_p_value = args.provided_p_value;
+ }
+ if (typeof(args.is_for_client) !== "undefined") {
+ is_for_client = args.is_for_client;
+ }
+
+ if (!(sha_verify && md5_verify)) {
+ sha_verify = this.handshake_hash_sha;
+ md5_verify = this.handshake_hash_md5;
+ }
+
+ if (!provided_p_value) {
+ //#we calculate the verify data from the raw handshake messages
+ if (this.handshake_messages.slice(0, 6).indexOf(null) > -1) {
+ print('Here are the handshake messages: ' + this.handshake_messages.slice(0, 6).toString());
+ throw ('Handshake data was not complete, could not calculate verify data');
+ }
+ var label;
+ if (is_for_client) {
+ label = str2ba('client finished');
+ } else {
+ label = str2ba('server finished');
+ }
+ var seed = [].concat(md5_verify, sha_verify);
+ var ms = [].concat(this.master_secret_half_auditor, this.master_secret_half_auditee);
+ //#we don't store the verify data locally, just return it
+ return tls_10_prf([].concat(label, seed), {
+ 'req_bytes': 12,
+ 'full_secret': ms
+ })[2];
+ }
+ //#we calculate based on provided hmac by the other party
+ var verify_hmac = this.get_verify_hmac({
+ 'sha_verify': sha_verify,
+ 'md5_verify': md5_verify,
+ 'half': half,
+ 'is_for_client': is_for_client
+ });
+ return xor(provided_p_value.slice(0, 12), verify_hmac);
+};
+
+TLSNClientSession.prototype.set_handshake_hashes = function(args) {
+ var server = false;
+ if (typeof(args) !== "undefined") {
+ if (typeof(args.server) !== "undefined") {
+ server = args.server;
+ }
+ }
+ /* '''An obscure but important detail: the hashes used
+ for the server Finished use the *unencrypted* client finished;
+ in the current model this is automatic since the TLSFinished objects
+ store the verify data unencrypted.'''*/
+ var handshake_data = [];
+ for (var i = 0; i < 5; i++) {
+ handshake_data = handshake_data.concat(this.handshake_messages[i]);
+ }
+ if (server) {
+ handshake_data = [].concat(handshake_data, this.handshake_messages[6]); // #client finished
+ }
+ var handshake_hash_sha = sha1(handshake_data);
+ var handshake_hash_md5 = md5(handshake_data);
+ if (!server) {
+ this.handshake_hash_sha = handshake_hash_sha;
+ this.handshake_hash_md5 = handshake_hash_md5;
+ }
+ return [handshake_hash_sha, handshake_hash_md5];
+};
+
+TLSNClientSession.prototype.send_client_finished = function(provided_p_value) {
+ /* '''Creates the client finished handshake message without
+ access to the master secret, but on the P-hash data provided
+ by the auditor. Then receives the server ccs and finished.'''*/
+ var verify_data = this.get_verify_data_for_finished({
+ 'provided_p_value': provided_p_value,
+ 'half': 2
+ });
+ this.client_finished = new TLSFinished();
+ this.client_finished.__init__({
+ 'serialized': null,
+ 'verify_data': verify_data
+ });
+ this.handshake_messages[6] = this.client_finished.serialized;
+ //#Note that the three messages cannot be packed into one record;
+ //#change cipher spec is *not* a handshake message
+ tls_sender(this.sckt, this.handshake_messages[4], hs, this.tlsver);
+ tls_sender(this.sckt, this.handshake_messages[5], chcis, this.tlsver);
+ tls_sender(this.sckt, this.handshake_messages[6], hs, this.tlsver, this.client_connection_state);
+}
+
+
+TLSNClientSession.prototype.get_server_finished = function() {
+ //keep recv'ing more data until we get enough records
+ var that = this;
+ var records = [];
+ var sckt = this.sckt;
+ return new Promise(function(resolve, reject) {
+ var loop = function() {
+ console.log('get_server_finished next iteration');
+ sckt.recv(true).then(function(rspns) {
+ console.log('returned from sckt.recv');
+ var rv = tls_record_decoder(rspns);
+ var x = rv[0];
+ var remaining = rv[1];
+ assert(remaining.length === 0, "Server sent spurious non-TLS response");
+ records = [].concat(records, x);
+ if (records.length < 2) {
+ console.log('get_server_finished records.length < 2');
+ loop();
+ return;
+ }
+ //else
+ resolve(records);
+ })
+ .catch(function(e) {
+ reject(e);
+ });
+ };
+ loop();
+ });
+}
+
+
+
+TLSNClientSession.prototype.process_server_finished = function(records) {
+ var i, x;
+ /* #this strange-looking 'filtering' approach is based on observation
+ #in practice of CCS being repeated (and possible also Finished, although I don't remember)*/
+ var sccs = null;
+ for (i = 0; i < records.length; i++) {
+ x = records[i];
+ if (x.content_type === chcis) {
+ sccs = x;
+ break;
+ }
+ }
+ this.server_ccs = tls_record_fragment_decoder(chcis, sccs.fragment)[0];
+ var sf = null;
+ for (i = 0; i < records.length; i++) {
+ x = records[i];
+ if (x.content_type == hs) {
+ sf = x;
+ break;
+ }
+ }
+ this.server_finished = tls_record_fragment_decoder(hs, sf.fragment, {
+ 'conn': this.server_connection_state,
+ 'ignore_mac': true
+ })[0];
+ assert(this.server_finished.handshake_type === h_fin, "Server failed to send Finished");
+ //#store the IV immediately after decrypting Finished; this will be needed
+ //#by auditor in order to replay the decryption
+ this.IV_after_finished = this.server_connection_state.IV;
+
+ if (records.length > 2) {
+ //#we received extra records; are they app data? if not we have bigger problems..
+ for (i = 0; i < records.length; i++) {
+ x = records[i];
+ if ([chcis, hs].indexOf(x.content_type) > -1) {
+ continue;
+ }
+ if (x.content_type !== appd) {
+ //#this is too much; if it's an Alert or something, we give up.
+ throw ("Received unexpected TLS record before client request.");
+ }
+ //#store any app data records, in sequence, prior to processing all app data.
+ this.server_response_app_data = [].concat(this.server_response_app_data, tls_record_fragment_decoder(appd, x.fragment));
+ //#We have to store the raw form of these unexpected app data records, since they will
+ //#be needed by auditor.
+ this.unexpected_server_app_data_raw = [].concat(this.unexpected_server_app_data_raw, x.serialized); // #the full record serialization (otw bytes)
+ this.unexpected_server_app_data_count += 1; //#note: each appd record contains ONE appd message
+ }
+ }
+};
+
+TLSNClientSession.prototype.complete_handshake = function(rsapms2) {
+ /* '''Called from prepare_pms(). For auditee only,
+ who passes the second half of the encrypted
+ PMS product (see TLSNotary.pdf under documentation).'''*/
+ this.set_auditee_secret();
+ this.set_master_secret_half(); //#default values means full MS created
+ this.do_key_expansion();
+ this.enc_second_half_pms = rsapms2;
+ this.set_enc_first_half_pms();
+ this.set_encrypted_pms();
+ this.client_key_exchange = new TLSClientKeyExchange();
+ this.client_key_exchange.__init__({
+ 'encryptedPMS': this.enc_pms
+ });
+ this.handshake_messages[4] = this.client_key_exchange.serialized;
+ this.change_cipher_spec = new TLSChangeCipherSpec();
+ this.change_cipher_spec.__init__();
+ this.handshake_messages[5] = this.change_cipher_spec.serialized;
+ this.set_handshake_hashes();
+
+ var client_verify_data = this.get_verify_data_for_finished({
+ 'sha_verify': this.handshake_hash_sha,
+ 'md5_verify': this.handshake_hash_md5,
+ 'half': 1
+ });
+
+ this.client_finished = new TLSFinished();
+ this.client_finished.__init__({
+ 'verify_data': client_verify_data
+ });
+ this.handshake_messages[6] = this.client_finished.serialized;
+ //#Note that the three messages cannot be packed into one record;
+ //#change cipher spec is *not* a handshake message
+ tls_sender(this.sckt, this.handshake_messages[4], hs, this.tlsver)
+ tls_sender(this.sckt, this.handshake_messages[5], chcis, this.tlsver);
+ tls_sender(this.sckt, this.handshake_messages[6], hs, this.tlsver, this.client_connection_state);
+ return this.sckt.recv(true);
+}
+
+
+
+TLSNClientSession.prototype.set_encrypted_pms = function() {
+ assert(this.enc_first_half_pms && this.enc_second_half_pms && this.server_modulus,
+ 'failed to set enc_pms, first half was: ' + this.enc_first_half_pms.toString() +
+ ' second half was: ' + this.enc_second_half_pms.toString() + ' modulus was: ' +
+ this.server_modulus.toString());
+ var bigint_pms1 = new BigInteger(ba2hex(this.enc_first_half_pms), 16);
+ var bigint_pms2 = new BigInteger(ba2hex(this.enc_second_half_pms), 16);
+ var bigint_mod = new BigInteger(ba2hex(this.server_modulus), 16);
+ var bigint_pms = bigint_pms1.multiply(bigint_pms2).mod(bigint_mod);
+ var resulthex = bigint_pms.toString(16);
+ this.enc_pms = hex2ba(resulthex);
+ return this.enc_pms;
+};
+
+TLSNClientSession.prototype.set_enc_first_half_pms = function() {
+ assert(this.server_modulus && !this.enc_first_half_pms);
+ var ones_length = 23;
+ var trailing_zeroes = [];
+ var i;
+ for (i = 0; i < (24 - 2 - this.n_auditee_entropy); ++i) {
+ trailing_zeroes.push(0);
+ }
+ this.pms1 = [].concat(this.initial_tlsver, this.auditee_secret, trailing_zeroes);
+ var ones = [];
+ for (i = 0; i < ones_length; i++) {
+ ones.push(1);
+ }
+ var tailzeroes = [];
+ for (i = 0; i < 23; i++) {
+ tailzeroes.push(0);
+ }
+ var base = [].concat(0x02, ones, this.auditee_padding_secret, 0x00, this.pms1, tailzeroes, 0x01);
+ var bigint_base = new BigInteger(ba2hex(base), 16);
+ var bigint_mod = new BigInteger(ba2hex(this.server_modulus), 16);
+ var bigint_exp = new BigInteger(ba2hex(bi2ba(this.server_exponent)), 16);
+ var bigint_result = bigint_base.modPow(bigint_exp, bigint_mod);
+ var resultba = hex2ba(bigint_result.toString(16));
+ var padding_len = this.server_modulus.length - resultba.length;
+ for (i = 0; i < padding_len; i++) { //zero-pad
+ resultba = [].concat(0x00, resultba);
+ }
+ this.enc_first_half_pms = resultba;
+ assert(this.enc_first_half_pms.length === this.server_modulus.length, "this.enc_first_half_pms.length === tlsn_session.server_mod_length");
+};
+
+TLSNClientSession.prototype.set_auditee_secret = function() {
+ /*'''Sets up the auditee's half of the preparatory
+ secret material to create the master secret. Note
+ that according to the RFC, the tls version prepended to the
+ premaster secret must be that used in the client hello message,
+ not the negotiated/downgraded version set by the server hello.
+ See variable tlsver_ch.'''*/
+ var tlsver_ch = this.initial_tlsver;
+ var cr = this.client_random;
+ var sr = this.server_random;
+ assert(cr && sr, "one of client or server random not set");
+ if (!this.auditee_secret) {
+ this.auditee_secret = getRandom(this.n_auditee_entropy, window);
+ //this.auditee_secret = [1,2,3,4,5,6,7,8,9,10,11,12];
+ }
+ if (!this.auditee_padding_secret) {
+ this.auditee_padding_secret = getRandom(15, window);
+ //this.auditee_padding_secret = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
+ }
+ var label = str2ba('master secret');
+ var seed = [].concat(cr, sr);
+ var trailing_zeroes = [];
+ for (var i = 0; i < (24 - 2 - this.n_auditee_entropy); ++i) {
+ trailing_zeroes.push(0);
+ }
+ this.pms1 = [].concat(tlsver_ch, this.auditee_secret, trailing_zeroes);
+ this.p_auditee = tls_10_prf([].concat(label, seed), {
+ 'first_half': this.pms1
+ })[0];
+ //#encrypted PMS has already been calculated before the audit began
+ return this.p_auditee;
+};
+
+//-------------------NOT IN USE BY THE AUDITEE---------------
+TLSNClientSession.prototype.set_enc_second_half_pms = function() {
+ assert(this.server_modulus);
+ var ones_length = 103 + ba2int(this.server_mod_length) - 256;
+ var trailing_zeroes = [];
+ for (var i = 0; 24 - this.n_auditor_entropy - 1; ++i) {
+ trailing_zeroes.push(0);
+ }
+ this.pms2 = [].concat(this.auditor_secret, trailing_zeroes, 0x01);
+ ///XXX JS mod exp
+ this.enc_second_half_pms = pow(ba2int('\x01' + ('\x01' * (ones_length)) +
+ this.auditor_padding_secret + ('\x00' * 25) + this.pms2), this.server_exponent,
+ this.server_modulus);
+};
+
+TLSNClientSession.prototype.set_auditor_secret = function() {
+ /*'''Sets up the auditor's half of the preparatory
+ secret material to create the master secret, and
+ the encrypted premaster secret.
+ 'secret' should be a bytearray of length n_auditor_entropy'''*/
+ var cr = this.client_random;
+ var sr = this.server_random;
+ assert(cr && sr, "one of client or server random not set");
+ if (!this.auditor_secret) {
+ this.auditor_secret = getRandom(this.n_auditor_entropy, window);
+ //this.auditor_secret = [1,2,3,4,5,6,7,8,9];
+ }
+ if (!this.auditor_padding_secret) {
+ this.auditor_padding_secret = getRandom(15, window);
+ //this.auditor_padding_secret = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
+ }
+ var label = str2ba('master secret');
+ var seed = [].concat(cr, sr);
+ var trailing_zeroes = [];
+ for (var i = 0; i < (24 - this.n_auditor_entropy - 1); ++i) {
+ trailing_zeroes.push(0);
+ }
+ this.pms2 = [].concat(this.auditor_secret, trailing_zeroes, 0x01);
+ this.p_auditor = tls_10_prf([].concat(label, seed), {
+ 'second_half': this.pms2
+ })[1];
+ return this.p_auditor;
+};
+
+TLSNClientSession.prototype.set_master_secret_half = function(args) {
+ var half = 1;
+ var provided_p_value = null;
+ if (typeof(args) !== "undefined") {
+ if (typeof(args.half) !== "undefined") {
+ half = args.half;
+ }
+ if (typeof(args.provided_p_value) !== "undefined") {
+ provided_p_value = args.provided_p_value;
+ }
+ }
+ //#non provision of p value means we use the existing p
+ //#values to calculate the whole MS
+ if (!provided_p_value) {
+ this.master_secret_half_auditor = xor(this.p_auditee.slice(0, 24), this.p_auditor.slice(0, 24));
+ this.master_secret_half_auditee = xor(this.p_auditee.slice(24), this.p_auditor.slice(24));
+ return [].concat(this.master_secret_half_auditor, this.master_secret_half_auditee);
+ }
+ assert([1, 2].indexOf(half) > -1, "Must provide half argument as 1 or 2");
+ //#otherwise the p value must be enough to provide one half of MS
+ assert(provided_p_value.length === 24, "Wrong length of P-hash value for half MS setting.");
+ if (half === 1) {
+ this.master_secret_half_auditor = xor(this.p_auditor.slice(0, 24), provided_p_value);
+ return this.master_secret_half_auditor;
+ } else {
+ this.master_secret_half_auditee = xor(this.p_auditee.slice(24), provided_p_value);
+ return this.master_secret_half_auditee;
+ }
+};
+
+
+//------------------Not in use by the auditee. Can be removed
+TLSNClientSession.prototype.get_p_value_ms = function(ctrprty) {
+ /* '''Provide a list of keys that you want to 'garbageize' so as to hide
+ that key from the counterparty, in the array 'garbage', each number is
+ an index to that key in the cipher_suites dict
+ '''*/
+ var garbage = args.garbage;
+ assert(this.server_random && this.client_random && this.chosen_cipher_suite,
+ "server random, client random or cipher suite not set.");
+ var label = str2ba('key expansion');
+ var seed = [].concat(this.server_random, this.client_random);
+ var chosen_cs = get_cs(this.chosen_cipher_suite);
+ var expkeys_len = chosn_cs[chosen_cs.length - 1];
+ var i;
+ if (ctrprty == 'auditor') {
+ this.p_master_secret_auditor = tls_10_prf([].concat(label, seed), {
+ 'req_bytes': expkeys_len,
+ 'first_half': this.master_secret_half_auditor
+ })[0];
+ } else {
+ this.p_master_secret_auditee = tls_10_prf([].concat(label, seed), {
+ 'req_bytes': expkeys_len,
+ 'second_half': this.master_secret_half_auditee
+ })[1];
+ }
+
+ var tmp;
+ if (ctrprty == 'auditor') {
+ tmp = this.p_master_secret_auditor;
+ } else {
+ tmp = this.p_master_secret_auditee;
+ }
+ for (var j = 0; j < garbage.length; j++) {
+ var k = garbage[j];
+ var start = 0;
+ if (k == 1) {
+ start = 0;
+ } else {
+ for (i = 1; i < k; ++i) {
+ start += get_cs(this.chosen_cipher_suite)[i];
+ }
+ }
+ var end = 0;
+ for (i = 1; i < k + 1; ++i) {
+ end += get_cs(this.chosen_cipher_suite)[i];
+ }
+ var tmp2 = [].concat(tmp.slice(0, start), getRandom(end - start, window), tmp.slice(end));
+ tmp = tmp2;
+ }
+ return tmp;
+};
+
+TLSNClientSession.prototype.do_key_expansion = function() {
+ /*'''A note about partial expansions:
+ Often we will have sufficient information to extract particular
+ keys, e.g. the client keys, but not others, e.g. the server keys.
+ This should be handled by passing in garbage to fill out the relevant
+ portions of the two master secret halves. TODO find a way to make this
+ explicit so that querying the object will only give real keys.
+ '''*/
+
+ var cr = this.client_random;
+ var sr = this.server_random;
+ var cs = this.chosen_cipher_suite;
+ assert(cr && sr && cs, " need client and server random and cipher suite");
+ var label = str2ba('key expansion');
+ var seed = [].concat(sr, cr);
+ //#for maximum flexibility, we will compute the sha1 or md5 hmac
+ //#or the full keys, based on what secrets currently exist in this object
+ var chosen_cs = get_cs(cs);
+ var expkeys_len = chosen_cs[chosen_cs.length - 1];
+ if (this.master_secret_half_auditee) {
+ this.p_master_secret_auditee = tls_10_prf([].concat(label, seed), {
+ 'req_bytes': expkeys_len,
+ 'second_half': this.master_secret_half_auditee
+ })[1];
+ }
+ if (this.master_secret_half_auditor) {
+ this.p_master_secret_auditor = tls_10_prf([].concat(label, seed), {
+ 'req_bytes': expkeys_len,
+ 'first_half': this.master_secret_half_auditor
+ })[0];
+ }
+
+ var key_expansion;
+ if (this.master_secret_half_auditee && this.master_secret_half_auditor) {
+ key_expansion = tls_10_prf([].concat(label, seed), {
+ 'req_bytes': expkeys_len,
+ 'full_secret': [].concat(this.master_secret_half_auditor, this.master_secret_half_auditee)
+ })[2];
+ } else if (this.p_master_secret_auditee && this.p_master_secret_auditor) {
+ key_expansion = xor(this.p_master_secret_auditee, this.p_master_secret_auditor);
+ } else {
+ throw ('Cannot expand keys, insufficient data');
+ }
+
+ //#we have the raw key expansion, but want the keys. Use the data
+ //#embedded in the cipherSuite dict to identify the boundaries.
+ var key_accumulator = [];
+ var ctr = 0;
+ var i;
+ for (i = 0; i < 6; ++i) {
+ var keySize = get_cs(cs)[i + 1];
+ if (keySize === 0) {
+ key_accumulator.push(null);
+ } else {
+ key_accumulator.push(key_expansion.slice(ctr, ctr + keySize));
+ }
+ ctr += keySize;
+ }
+
+ this.client_mac_key = key_accumulator[0];
+ this.server_mac_key = key_accumulator[1];
+ this.client_enc_key = key_accumulator[2];
+ this.server_enc_key = key_accumulator[3];
+ this.clientIV = key_accumulator[4];
+ this.serverIV = key_accumulator[5];
+ /*#we now have sufficient information to initialise client and server
+ #connection state. NOTE: Since this wipes/restarts the encryption
+ #connection state, a call to do_key_expansion automatically restarts
+ #the session.*/
+ this.client_connection_state = new TLSConnectionState();
+ this.client_connection_state.__init__(cs, key_accumulator, true, this.tlsver);
+ this.server_connection_state = new TLSConnectionState();
+ this.server_connection_state.__init__(cs, key_accumulator, false, this.tlsver);
+ var keys = [];
+ for (i = 0; i < key_accumulator.length; ++i) {
+ if (key_accumulator[i] !== null) {
+ keys = [].concat(keys, key_accumulator[i]);
+ }
+ }
+ return keys;
+};
+
+TLSNClientSession.prototype.get_verify_hmac = function(args) {
+ var sha_verify = args.sha_verify;
+ var md5_verify = args.md5_verify;
+ var half = args.half;
+ var is_for_client = args.is_for_client;
+ //'''returns only 12 bytes of hmac'''
+ var label;
+ if (is_for_client) {
+ label = str2ba('client finished');
+ } else {
+ label = str2ba('server finished');
+ }
+ var seed = [].concat(md5_verify, sha_verify);
+ if (half == 1) {
+ return tls_10_prf([].concat(label, seed), {
+ 'req_bytes': 12,
+ 'first_half': this.master_secret_half_auditor
+ })[0];
+ } else {
+ return tls_10_prf([].concat(label, seed), {
+ 'req_bytes': 12,
+ 'second_half': this.master_secret_half_auditee
+ })[1];
+ }
+};
+
+TLSNClientSession.prototype.check_server_ccs_finished = function(provided_p_value) {
+ isdefined(provided_p_value);
+ //#verify the verify data:
+ var rv = this.set_handshake_hashes({
+ 'server': true
+ });
+ var sha_verify = rv[0];
+ var md5_verify = rv[1];
+ var verify_data_check = this.get_verify_data_for_finished({
+ 'sha_verify': sha_verify,
+ 'md5_verify': md5_verify,
+ 'provided_p_value': provided_p_value,
+ 'half': 2,
+ 'is_for_client': false
+ });
+ assert(this.server_finished.verify_data.toString() == verify_data_check.toString(),
+ "Server Finished record verify data is not valid.");
+ return true;
+};
+
+TLSNClientSession.prototype.build_request = function(cleartext) {
+ /*'''Constructs the raw bytes to send over TCP
+ for a given client request. Implicitly the request
+ will be less than 16kB and therefore only 1 SSL record.
+ This can in principle be used more than once.'''*/
+ this.tls_request = new TLSAppData();
+ this.tls_request.__init__(cleartext);
+ tls_sender(this.sckt, this.tls_request.serialized, appd, this.tlsver, this.client_connection_state);
+};
+
+TLSNClientSession.prototype.store_server_app_data_records = function(response) {
+ //#extract the ciphertext from the raw records as a list
+ //#for maximum flexibility in decryption
+ var rv = tls_record_decoder(response);
+ var recs = rv[0];
+ var remaining = rv[1];
+ assert(remaining.length === 0, "Server sent spurious non-TLS data");
+ for (var i = 0; i < recs.length; i++) {
+ var rec = recs[i];
+ var decoded = tls_record_fragment_decoder(rec.content_type, rec.fragment);
+ this.server_response_app_data = [].concat(this.server_response_app_data, decoded);
+ }
+ //#what has been stored is a list of TLSAppData objects in which
+ //#the .serialized property is still encrypted.
+};
+
+
+
+TLSNClientSession.prototype.mac_check_server_finished = function() {
+ /*
+ #Note server connection state has been reset after do_key_expansion
+ #(which was done to correct server mac key), so state is initialised
+ #correctly).'''
+ */
+ var rv = this.server_connection_state.dtvm(this.server_finished.encrypted, hs);
+ var validity = rv[0];
+ var plaintext = rv[1];
+ //#now sequence number and IV are correctly initialised for the app data
+ return validity;
+};
+
+TLSNClientSession.prototype.process_server_app_data_records = function() {
+ /*'''Using the encrypted records in self.server_response_ciphertexts,
+ containing the response from
+ the server to a GET or POST request (the *first* request after
+ the handshake), this function will process the response one record
+ at a time. Each of these records is decrypted and reassembled
+ into the plaintext form of the response. The plaintext is returned
+ along with the number of record mac failures (more than zero means
+ the response is unauthenticated/corrupted).
+ '''*/
+
+ assert(this.server_response_app_data.length > 0,
+ "Could not process the server response, no ciphertext found.");
+ var plaintexts = [];
+ var bad_record_mac = 0;
+
+ for (var i = 0; i < this.server_response_app_data.length; ++i) {
+ var ciphertext = this.server_response_app_data[i];
+ var rt;
+ if (ciphertext.typename === "TLSAppData") {
+ rt = appd;
+ } else if (ciphertext.typename === "TLSAlert") {
+ rt = alrt;
+ } else {
+ throw ("Server response contained unexpected record type: ",
+ ciphertext.typename);
+ }
+ var validity, plaintext;
+ var rv = this.server_connection_state.dtvm(ciphertext.serialized, rt);
+ validity = rv[0];
+ plaintext = rv[1];
+ if (validity !== true) {
+ bad_record_mac += 1;
+ }
+ //#plaintext is only included if it's appdata not alerts, and if it's
+ //#not part of the ignored set (the set that was delivered pre-client-request)
+ if (rt == appd && i > this.unexpected_server_app_data_count - 1) {
+ plaintexts = [].concat(plaintexts, plaintext);
+ }
+ }
+ return [plaintexts, bad_record_mac];
+};
+
+
+
+function get_cbc_padding(data_length) {
+ var req_padding = aes_block_size - data_length % aes_block_size;
+ var padding = [];
+ for (var i = 0; i < req_padding; i++) {
+ padding.push(req_padding - 1);
+ }
+ return padding;
+}
+
+
+function cbc_unpad(pt) {
+ /* '''Given binary string pt, return
+ unpadded string, raise fatal exception
+ if padding format is not valid'''
+ */
+ var pad_len = pt[pt.length - 1];
+ //#verify the padding
+ var padding = pt.slice(pt.length - pad_len - 1, pt.length - 1);
+ for (var i = 0; i < padding.length; i++) {
+ if (padding[i] !== pad_len) {
+ throw ("Invalid CBC padding.");
+ }
+ }
+ return pt.slice(0, pt.length - (pad_len + 1));
+}
+
+
+function aes_encrypt(padded_cleartext, enc_key, IV) {
+ var ct = CryptoJS.enc.Hex.parse(ba2hex(padded_cleartext));
+ var key = CryptoJS.enc.Hex.parse(ba2hex(enc_key));
+ var iv = CryptoJS.enc.Hex.parse(ba2hex(IV));
+ var enc_obj = CryptoJS.AES.encrypt(ct, key, {
+ iv: iv,
+ mode: CryptoJS.mode.CBC,
+ padding: CryptoJS.pad.NoPadding
+ });
+ return wa2ba(enc_obj.ciphertext.words);
+}
+
+
+//decrypt but leave the cbc padding intact
+function aes_decrypt(ciphertext, enc_key, IV) {
+ var ct = CryptoJS.enc.Hex.parse(ba2hex(ciphertext));
+ var key = CryptoJS.enc.Hex.parse(ba2hex(enc_key));
+ var iv = CryptoJS.enc.Hex.parse(ba2hex(IV));
+ var cipherParams = CryptoJS.lib.CipherParams.create({
+ ciphertext: ct
+ });
+ var decrypted = CryptoJS.AES.decrypt(cipherParams, key, {
+ iv: iv,
+ mode: CryptoJS.mode.CBC,
+ padding: CryptoJS.pad.NoPadding
+ });
+ return wa2ba(decrypted.words);
+}
+
+
+
+function rc4_crypt(data, key, state) {
+ if (typeof(state) === 'undefined') {
+ state = null;
+ }
+ /* """RC4 algorithm.
+ Symmetric, so performs encryption and decryption
+ 'state', if passed, is a tuple of three values,
+ box (a bytearray), x and y (integers), allowing
+ restart of the algorithm from an intermediate point.
+ This is necessary since stream ciphers
+ in TLS use the final state of the cipher at the end
+ of one record to initialise the next record (see RFC 2246)."""*/
+ var x = 0,
+ y = 0;
+ var box = [];
+ var t;
+ var i;
+ if (!state) {
+ for (i = 0; i < 256; i++) {
+ box[i] = i;
+ }
+ for (i = 0; i < 256; i++) {
+ x = ((x + box[i] + key[i % key.length]) % 256);
+ t = box[i];
+ box[i] = box[x];
+ box[x] = t;
+ }
+ x = y = 0;
+ } else {
+ box = state.slice(0, 256);
+ x = state[256];
+ y = state[257];
+ }
+
+ var out = [];
+ for (i = 0; i < data.length; i++) {
+ var onebyte = data[i];
+ x = (x + 1) % 256;
+ y = ((y + box[x]) % 256);
+ t = box[x];
+ box[x] = box[y];
+ box[y] = t;
+ out.push(onebyte ^ box[(box[x] + box[y]) % 256]);
+ }
+ var out_state = [].concat(box, x, y);
+ return [out, out_state];
+}
+
+function rc4_state_to_bytearray(state) {
+ var box = state[0];
+ var x = state[1];
+ var y = state[2];
+ var box = [].concat(box, x, y);
+ return box;
+}
+
+
+
+
+
+function tls_10_prf(seed, args) {
+ /*'''
+ Calculates all or part of the pseudo random function PRF
+ as defined in the TLS 1.0 RFC 2246 Section 5. If only first_half or
+ second_half are provided, then the appropriate HMAC is returned
+ as the first or second element of the returned tuple respectively.
+ If both are provided, the full result of PRF is provided also in
+ the third element of the returned tuple.
+ For maximum clarity, variable names correspond to those used in the RFC.
+ Notes:
+ The caller should provide one or other but not both of first_half and
+ second_half - the alternative is to provide full_secret. This is because
+ the algorithm for splitting into two halves as described in the RFC,
+ which varies depending on whether the secret length is odd or even,
+ cannot be correctly deduced from two halves.
+ '''*/
+ var x;
+ var i;
+ var req_bytes = args.req_bytes;
+ var first_half = args.first_half;
+ var second_half = args.second_half;
+ var full_secret = args.full_secret;
+ if (typeof(req_bytes) === 'undefined') req_bytes = 48;
+ if (typeof(first_half) === 'undefined') first_half = null;
+ if (typeof(second_half) === 'undefined') second_half = null;
+ if (typeof(full_secret) === 'undefined') full_secret = null;
+ //#sanity checks, (see choices of how to provide secrets under 'Notes' above)
+ if (!first_half && !second_half && !full_secret) {
+ throw ("Error in TLSPRF: at least one half of the secret is required.");
+ }
+ if ((full_secret && first_half) || (full_secret && second_half)) {
+ throw ("Error in TLSPRF: both full and half secrets should not be provided.");
+ }
+ if (first_half && second_half) {
+ throw ("Error in TLSPRF: please provide the secret in the parameter full_secret.");
+ }
+ var P_MD5 = null;
+ var P_SHA_1 = null;
+ var PRF = null;
+
+ //split the secret into two halves if necessary
+ if (full_secret) {
+ var L_S = full_secret.length;
+ var L_S1, L_S2;
+ L_S1 = L_S2 = Math.ceil(L_S / 2);
+ first_half = full_secret.slice(0, L_S1);
+ second_half = full_secret.slice(L_S2);
+ }
+
+ /*#To calculate P_MD5, we need at most floor(req_bytes/md5_hash_len) iterations
+ #of 'A'. If req_bytes is a multiple of md5_hash_len(16), we will use
+ #0 bytes of the final iteration, otherwise we will use 1-15 bytes of it.
+ #Note that A[0] is actually A(1) in the RFC, since A(0) in the RFC is the seed.*/
+ var A;
+ if (first_half) {
+ A = [hmac(first_half, seed, 'md5')];
+ for (i = 1; i < Math.floor(req_bytes / md5_hash_len) + 1; i++) {
+ A.push(hmac(first_half, A[A.length - 1], 'md5'));
+ }
+
+ var md5_P_hash = [];
+ for (i = 0; i < A.length; i++) {
+ x = A[i];
+ md5_P_hash = [].concat(md5_P_hash, hmac(first_half, [].concat(x, seed), 'md5'));
+ }
+
+ P_MD5 = md5_P_hash.slice(0, req_bytes);
+ }
+
+ /*#To calculate P_SHA_1, we need at most floor(req_bytes/sha1_hash_len) iterations
+ #of 'A'. If req_bytes is a multiple of sha1_hash_len(20), we will use
+ #0 bytes of the final iteration, otherwise we will use 1-19 bytes of it.
+ #Note that A[0] is actually A(1) in the RFC, since A(0) in the RFC is the seed.*/
+ if (second_half) {
+ A = [hmac(second_half, seed, 'sha1')];
+ for (i = 1; i < Math.floor(req_bytes / sha1_hash_len) + 1; i++) {
+ A.push(hmac(second_half, A[A.length - 1], 'sha1'));
+ }
+
+ var sha1_P_hash = [];
+ for (i = 0; i < A.length; i++) {
+ x = A[i];
+ sha1_P_hash = [].concat(sha1_P_hash, hmac(second_half, [].concat(x, seed), 'sha1'));
+ }
+
+ P_SHA_1 = sha1_P_hash.slice(0, req_bytes);
+ }
+
+ if (full_secret) {
+ PRF = xor(P_MD5, P_SHA_1);
+ }
+
+ return [P_MD5, P_SHA_1, PRF];
+}
diff --git a/webextension/content/tlsn_utils.js b/webextension/content/tlsn_utils.js
new file mode 100644
index 0000000..fc5b46e
--- /dev/null
+++ b/webextension/content/tlsn_utils.js
@@ -0,0 +1,283 @@
+//js native ArrayBuffer to Array of numbers
+function ab2ba(ab) {
+ var view = new DataView(ab);
+ var int_array = [];
+ for (var i = 0; i < view.byteLength; i++) {
+ int_array.push(view.getUint8(i));
+ }
+ return int_array;
+}
+
+
+function ba2ab(ba) {
+ var ab = new ArrayBuffer(ba.length);
+ var dv = new DataView(ab);
+ for (var i = 0; i < ba.length; i++) {
+ dv.setUint8(i, ba[i]);
+ }
+ return ab;
+}
+
+
+
+function ba2ua(ba) {
+ var ua = new Uint8Array(ba.length);
+ for (var i = 0; i < ba.length; i++) {
+ ua[i] = ba[i];
+ }
+ return ua;
+}
+
+function ua2ba(ua) {
+ var ba = [];
+ for (var i = 0; i < ua.byteLength; i++) {
+ ba.push(ua[i]);
+ }
+ return ba;
+}
+
+/*CryptoJS only exposes word arrays of ciphertexts which is awkward to use
+so we convert word(4byte) array into a 1-byte array*/
+function wa2ba(wordArray) {
+ var byteArray = [];
+ for (var i = 0; i < wordArray.length; ++i) {
+ var word = wordArray[i];
+ for (var j = 3; j >= 0; --j) {
+ byteArray.push((word >> 8 * j) & 0xFF);
+ }
+ }
+ return byteArray;
+}
+
+//CryptoJS doesnt accept bytearray input but it does accept a hexstring
+function ba2hex(bytearray) {
+ try {
+ var hexstring = '';
+ for (var i = 0; i < bytearray.length; i++) {
+ var hexchar = bytearray[i].toString(16);
+ if (hexchar.length == 1) {
+ hexchar = "0" + hexchar;
+ }
+ hexstring += hexchar;
+ }
+ return hexstring;
+ } catch (e) {
+ var place_for_breakpoint = 0;
+ }
+}
+
+
+//convert a hex string into byte array
+function hex2ba(str) {
+ var ba = [];
+ //pad with a leading 0 if necessary
+ if (str.length % 2) {
+ str = "0" + str;
+ }
+ for (var i = 0; i < str.length; i += 2) {
+ ba.push(parseInt("0x" + str.substr(i, 2)));
+ }
+ return ba;
+}
+
+//Turn a max 4 byte array (big-endian) into an int.
+function ba2int(x) {
+ assert(x.length <= 8, "Cannot convert bytearray larger than 8 bytes");
+ var retval = 0;
+ for (var i = 0; i < x.length; i++) {
+ retval |= x[x.length - 1 - i] << 8 * i;
+ }
+ return retval;
+}
+
+
+//Turn an int into a bytearray. Optionally left-pad with zeroes
+function bi2ba(x, args) {
+ assert(typeof(x) == "number", "Only can convert numbers");
+ var fixed = null;
+ if (typeof(args) !== 'undefined') {
+ fixed = args.fixed;
+ }
+ var bytes = [];
+ do {
+ var onebyte = x & (255);
+ x = x >> 8;
+ bytes = [].concat(onebyte, bytes);
+ } while (x !== 0);
+ var padding = [];
+ if (fixed) {
+ for (var i = 0; i < fixed - bytes.length; i++) {
+ padding = [].concat(padding, 0x00);
+ }
+ }
+ return [].concat(padding, bytes);
+}
+
+
+//converts string to bytearray
+function str2ba(str) {
+ if (typeof(str) !== "string") {
+ throw ("Only type string is allowed in str2ba");
+ }
+ ba = [];
+ for (var i = 0; i < str.length; i++) {
+ ba.push(str.charCodeAt(i));
+ }
+ return ba;
+}
+
+function ba2str(ba) {
+ if (typeof(ba) !== "object") {
+ throw ("Only type object is allowed in ba2str");
+ }
+ var result = "";
+ for (var i = 0; i < ba.length; i++) {
+ result += String.fromCharCode(ba[i]);
+ }
+ return result;
+}
+
+
+function hmac(key, msg, algo) {
+ var key_hex = ba2hex(key);
+ var msg_hex = ba2hex(msg);
+ var key_words = CryptoJS.enc.Hex.parse(key_hex);
+ var msg_words = CryptoJS.enc.Hex.parse(msg_hex);
+ var hash;
+ if (algo === 'md5') {
+ hash = CryptoJS.HmacMD5(msg_words, key_words);
+ return wa2ba(hash.words);
+ } else if (algo === 'sha1') {
+ hash = CryptoJS.HmacSHA1(msg_words, key_words);
+ return wa2ba(hash.words);
+ }
+}
+
+
+function sha1(ba) {
+ var ba_obj = CryptoJS.enc.Hex.parse(ba2hex(ba));
+ var hash = CryptoJS.SHA1(ba_obj);
+ return wa2ba(hash.words);
+}
+
+function sha256(ba) {
+ var ba_obj = CryptoJS.enc.Hex.parse(ba2hex(ba));
+ var hash = CryptoJS.SHA256(ba_obj);
+ return wa2ba(hash.words);
+}
+
+function md5(ba) {
+ var ba_obj = CryptoJS.enc.Hex.parse(ba2hex(ba));
+ var hash = CryptoJS.MD5(ba_obj);
+ return wa2ba(hash.words);
+}
+
+//input bytearrays must be of equal length
+function xor(a, b) {
+ assert(a.length === b.length, "length mismatch");
+ var c = [];
+ for (var i = 0; i < a.length; i++) {
+ c.push(a[i] ^ b[i]);
+ }
+ return c;
+}
+
+
+function assert(condition, message) {
+ if (!condition) {
+ throw message || "Assertion failed";
+ }
+}
+
+function isdefined(obj) {
+ assert(typeof(obj) !== "undefined", "obj was undefined");
+}
+
+//Not in use for now
+function log() {
+ if (verbose) {
+ console.log(Array.prototype.slice.call(arguments));
+ }
+}
+
+
+function getRandom(number, window) {
+ //window was undefined in this context, so i decided to pass it explicitely
+ var a = window.crypto.getRandomValues(new Uint8Array(number));
+ //convert to normal array
+ var b = Array.prototype.slice.call(a);
+ return b;
+}
+
+
+
+function b64encode(aBytes) {
+ return btoa(String.fromCharCode.apply(null, aBytes));
+}
+
+
+function b64decode(sBase64, nBlocksSize) {
+ return atob(sBase64).split("").map(function(c) {
+ return c.charCodeAt(0);
+ });
+}
+
+//plaintext must be string
+function dechunk_http(http_data) {
+ //'''Dechunk only if http_data is chunked otherwise return http_data unmodified'''
+ http_header = http_data.slice(0, http_data.search('\r\n\r\n') + '\r\n\r\n'.length);
+ //#\s* below means any amount of whitespaces
+ if (http_header.search(/transfer-encoding:\s*chunked/i) === -1) {
+ return http_data; //#nothing to dechunk
+ }
+ var http_body = http_data.slice(http_header.length);
+
+ var dechunked = http_header;
+ var cur_offset = 0;
+ var chunk_len = -1; //#initialize with a non-zero value
+ while (true) {
+ var new_offset = http_body.slice(cur_offset).search('\r\n');
+ if (new_offset === -1) { //#pre-caution against endless looping
+ //#pinterest.com is known to not send the last 0 chunk when HTTP gzip is disabled
+ return dechunked;
+ }
+ var chunk_len_hex = http_body.slice(cur_offset, cur_offset + new_offset);
+ var chunk_len = parseInt(chunk_len_hex, 16);
+ if (chunk_len === 0) {
+ break; //#for properly-formed html we should break here
+ }
+ cur_offset += new_offset + '\r\n'.length;
+ dechunked += http_body.slice(cur_offset, cur_offset + chunk_len);
+ cur_offset += chunk_len + '\r\n'.length;
+ }
+ return dechunked;
+}
+
+
+function gunzip_http(http_data) {
+ var http_header = http_data.slice(0, http_data.search('\r\n\r\n') + '\r\n\r\n'.length);
+ //#\s* below means any amount of whitespaces
+ if (http_header.search(/content-encoding:\s*deflate/i) > -1) {
+ //#TODO manually resend the request with compression disabled
+ throw ('Please set gzip_disabled = 1 in tlsnotary.ini and rerun the audit');
+ }
+ if (http_header.search(/content-encoding:\s.*gzip/i) === -1) {
+ console.log('nothing to gunzip');
+ return http_data; //#nothing to gunzip
+ }
+ var http_body = http_data.slice(http_header.length);
+ var ungzipped = http_header;
+ if (!http_body) {
+ //HTTP 304 Not Modified has no body
+ return ungzipped;
+ }
+ var inflated = pako.inflate(http_body);
+ ungzipped += ba2str(inflated);
+ return ungzipped;
+}
+
+function getTime() {
+ var today = new Date();
+ var time = today.getFullYear() + '-' + ("00" + (today.getMonth() + 1)).slice(-2) + '-' + ("00" + today.getDate()).slice(-2) + '-' + ("00" + today.getHours()).slice(-2) + '-' + ("00" + today.getMinutes()).slice(-2) + '-' + ("00" + today.getSeconds()).slice(-2);
+ return time;
+}
diff --git a/content/verify.png b/webextension/content/verify.png
similarity index 100%
rename from content/verify.png
rename to webextension/content/verify.png
diff --git a/content/verifychain/README b/webextension/content/verifychain/README
similarity index 100%
rename from content/verifychain/README
rename to webextension/content/verifychain/README
diff --git a/content/verifychain/asn1.js2 b/webextension/content/verifychain/asn1.js
similarity index 100%
rename from content/verifychain/asn1.js2
rename to webextension/content/verifychain/asn1.js
diff --git a/content/verifychain/buffer.js2 b/webextension/content/verifychain/buffer.js
similarity index 100%
rename from content/verifychain/buffer.js2
rename to webextension/content/verifychain/buffer.js
diff --git a/content/verifychain/jsrsasign-latest-all-min.js2 b/webextension/content/verifychain/jsrsasign-latest-all-min.js
similarity index 100%
rename from content/verifychain/jsrsasign-latest-all-min.js2
rename to webextension/content/verifychain/jsrsasign-latest-all-min.js
diff --git a/content/verifychain/rootcerts.js b/webextension/content/verifychain/rootcerts.js
similarity index 100%
rename from content/verifychain/rootcerts.js
rename to webextension/content/verifychain/rootcerts.js
diff --git a/content/verifychain/rootcertslist.js b/webextension/content/verifychain/rootcertslist.js
similarity index 100%
rename from content/verifychain/rootcertslist.js
rename to webextension/content/verifychain/rootcertslist.js
diff --git a/content/verifychain/verifychain.js b/webextension/content/verifychain/verifychain.js
similarity index 100%
rename from content/verifychain/verifychain.js
rename to webextension/content/verifychain/verifychain.js
diff --git a/webextension/content/viewer.html b/webextension/content/viewer.html
new file mode 100644
index 0000000..82a8e42
--- /dev/null
+++ b/webextension/content/viewer.html
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/webextension/content/viewer.js b/webextension/content/viewer.js
new file mode 100644
index 0000000..2786ad2
--- /dev/null
+++ b/webextension/content/viewer.js
@@ -0,0 +1,36 @@
+var received_once = false; // only receive the data to be displayed once
+//otherwise we will intercept the data meant for another viewer's tab
+
+//only load when html is displayed
+function loadViewerTest() {
+ chrome.storage.local.get('testing', function(obj) {
+ if (obj.testing == true) {
+ var script = document.createElement('script');
+ script.src = 'testing/viewer_test.js';
+ document.body.appendChild(script);
+ }
+ });
+}
+
+
+chrome.runtime.onMessage.addListener(function(msg) {
+ if (msg.destination !== 'viewer') return;
+ if (received_once) return;
+ received_once = true;
+ //console.log('got data in viewer', msg.data.slice(0, 100));
+ var utf_string = decodeURIComponent(escape(msg.data));
+ var hideButton = null;
+ if (msg.type == 'html') {
+ hideButton = false;
+ document.getElementsByTagName('html')[0].innerHTML = utf_string;
+ } else if (msg.type == 'raw') {
+ hideButton = true;
+ document.getElementsByTagName('plaintext')[0].innerHTML = utf_string;
+ document.getElementById('text').removeAttribute('hidden');
+ document.title = 'PageSigner raw viewer';
+ }
+ install_bar(msg.sessionId, msg.serverName, hideButton);
+ if (msg.type == 'html') {
+ loadViewerTest();
+ }
+});
diff --git a/webextension/manifest.json b/webextension/manifest.json
new file mode 100644
index 0000000..db505dc
--- /dev/null
+++ b/webextension/manifest.json
@@ -0,0 +1,51 @@
+{
+ "name": "PageSigner",
+ "version": "1.1.0",
+ "description": "PageSigner - a cryptographically secure webpage screenshot tool",
+ "manifest_version": 2,
+ "author": "TLSNotary Group",
+ "permissions": [
+ "webRequest",
+ "webRequestBlocking",
+ "activeTab",
+ "tabs",
+ "storage",
+ ""
+ ],
+ "background": {
+ "scripts": [
+ "content/socket.js",
+ "content/tlsn_utils.js",
+ "content/oracles.js",
+ "content/CryptoJS/components/core.js",
+ "content/CryptoJS/components/md5.js",
+ "content/CryptoJS/components/evpkdf.js",
+ "content/CryptoJS/components/enc-base64.js",
+ "content/CryptoJS/components/sha1.js",
+ "content/CryptoJS/components/sha256.js",
+ "content/CryptoJS/components/hmac.js",
+ "content/CryptoJS/components/cipher-core.js",
+ "content/CryptoJS/components/aes.js",
+ "content/CryptoJS/components/pad-nopadding.js",
+ "content/jsbn.js",
+ "content/jsbn2.js",
+ "content/pako.js",
+ "content/tlsn.js",
+ "content/main.js",
+ "content/testing/testing.js",
+ "content/verifychain/buffer.js",
+ "content/verifychain/asn1.js",
+ "content/verifychain/jsrsasign-latest-all-min.js",
+ "content/verifychain/rootcertslist.js",
+ "content/verifychain/rootcerts.js",
+ "content/verifychain/verifychain.js"
+ ]
+ },
+ "browser_action": {
+ "browser_style": true,
+ "default_icon": "content/icon.png",
+ "default_popup": "content/popup.html"
+ },
+ "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'"
+
+}