Notarize after click. Port to WebExtensions. Beautify JS. Bump version to 1.1.0
22
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.
|
||||
|
||||
361
bootstrap.js
vendored
@@ -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) {}
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
content pagesigner content/
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>About PageSigner.</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<link rel="stylesheet" href="manager.css" type="text/css" media="screen"/>
|
||||
</head>
|
||||
<body>
|
||||
<h1 align="center">PageSigner</h1>
|
||||
<hbox flex="1" style="overflow: auto">
|
||||
|
||||
<vbox>
|
||||
<p align="center">Using <a href="#" id="link1">TLSNotary</a> technology.</p>
|
||||
</vbox>
|
||||
<vbox hidden>
|
||||
<p align="center">For help and information, go to </p>
|
||||
<p align="center">
|
||||
<a href="#" id="link2">the PageSigner FAQ</a></p>
|
||||
</vbox>
|
||||
|
||||
<vbox>
|
||||
<p align="center">Email the developers: </p>
|
||||
<p align="center">
|
||||
<a href="mailto:tlsnotarygroup@gmail.com">tlsnotarygroup@gmail.com</a></p>
|
||||
</vbox>
|
||||
<vbox>
|
||||
<p align="center">Donations welcome:</p>
|
||||
<p align="center"> <a href="#" id="link3">367SYUMqo1Fi4tQsycnmCtB6Ces1Z7EZLH</a></p>
|
||||
</vbox>
|
||||
</hbox>
|
||||
|
||||
<script src="about.js"></script>
|
||||
|
||||
</body>
|
||||
@@ -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();
|
||||
});
|
||||
@@ -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: ["<all_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: ["<all_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(){});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
<body>
|
||||
<input type="file" id="import" ></input>
|
||||
<script src="file_picker.js"></script>
|
||||
</body>
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
@@ -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';
|
||||
@@ -1,74 +0,0 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<style>
|
||||
body {
|
||||
margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px;
|
||||
width: 15em;
|
||||
/* Fonts Chrome specifies for each OS: */
|
||||
/* Linux: 'DejaVu Sans', Arial, sans-serif */
|
||||
/* Mac: 'Lucida Grande', sans-serif */
|
||||
/* Windows: 'Segoe UI', Tahoma, sans-serif */
|
||||
font-family: 'Lucida Grande', 'Segoe UI', Tahoma, 'DejaVu Sans', Arial, sans-serif;
|
||||
font-size: 75%;
|
||||
color: #303942;
|
||||
}
|
||||
|
||||
tr.border_bottom td {
|
||||
border-bottom:1pt solid grey;
|
||||
}
|
||||
|
||||
tr:hover {
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
td{
|
||||
font-size:11pt;
|
||||
padding-top: 7px;
|
||||
padding-bottom: 7px;
|
||||
padding-right: 0px;
|
||||
padding-left: 0px;
|
||||
}
|
||||
|
||||
.menu_img {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
|
||||
|
||||
<body>
|
||||
|
||||
<table style="width:100%" id='menu' hidden>
|
||||
<tr class="border_bottom">
|
||||
<td id="notarize"><img class='menu_img' src="../../icon.png"></img>Notarize this page</td>
|
||||
</tr>
|
||||
<tr class="border_bottom">
|
||||
<td id="manage"><img class='menu_img' src="../manage.png"></img>Manage files</td>
|
||||
</tr>
|
||||
<tr class="border_bottom">
|
||||
<td id="import"><img class='menu_img' src="../verify.png"></img>Import .pgsg file</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td id="about"><img class='menu_img' src="../../icon.png"></img>About</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<a href="https://chrome.google.com/webstore/detail/pagesigner-helper-app/oclohfdjoojomkfddjclanpogcnjhemd" id="app_not_installed" hidden><h1>Click here to install the helper app needed for PageSigner to work</h1></a>
|
||||
|
||||
<a href="chrome://extensions" id="app_disabled" hidden><h1>Click here and then enable PageSigner helper app</h1></a>
|
||||
|
||||
<div id='enable_file_access' hidden>
|
||||
<img src="../enable_file_access.png"><a href="chrome://extensions"><h1>Click here and then enable the checkbox marked in the image above</h1></a></img>
|
||||
</div>
|
||||
|
||||
<h1 id='notarization_in_progress' hidden>Notarization in progress. Please wait...</h1>
|
||||
|
||||
<script src="popup.js"></script>
|
||||
|
||||
</body>
|
||||
@@ -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);
|
||||
|
Before Width: | Height: | Size: 33 KiB |
@@ -1,41 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="chrome://global/skin/global.css" type="text/css"?>
|
||||
|
||||
<dialog
|
||||
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
id="about-dialog" title="About PageSigner"
|
||||
buttons="accept">
|
||||
|
||||
<script type="text/javascript;version=1.8" src="main.js"></script>
|
||||
<html:link rel="stylesheet" href="../manager.css" type="text/css" media="screen"/>
|
||||
<html:h1 align="center">PageSigner</html:h1>
|
||||
|
||||
<html:hbox flex="1" style="overflow: auto">
|
||||
|
||||
<html:vbox>
|
||||
<html:p align="center">Using <html:a href="#"
|
||||
onclick="openT('https://www.tlsnotary.org')">TLSNotary</html:a> technology.</html:p>
|
||||
</html:vbox>
|
||||
|
||||
<html:vbox>
|
||||
<html:p align="center">Email the developers: </html:p>
|
||||
<html:p align="center">
|
||||
<html:a href="mailto:tlsnotarygroup@gmail.com">tlsnotarygroup@gmail.com</html:a></html:p>
|
||||
</html:vbox>
|
||||
<html:vbox>
|
||||
<html:p align="center">Donations welcome:</html:p>
|
||||
<html:p align="center"> <html:a href="bitcoin:367SYUMqo1Fi4tQsycnmCtB6Ces1Z7EZLH">367SYUMqo1Fi4tQsycnmCtB6Ces1Z7EZLH</html:a></html:p>
|
||||
</html:vbox>
|
||||
</html:hbox>
|
||||
<script type="text/javascript;version=1.8">
|
||||
function openT(url){
|
||||
var gBrowser = window.arguments[0];
|
||||
var t = gBrowser.addTab(url);
|
||||
gBrowser.selectedTab = t;
|
||||
window.close();
|
||||
}
|
||||
</script>
|
||||
</dialog>
|
||||
|
||||
|
||||
@@ -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 || "");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
|
||||
|
||||
<!DOCTYPE vbox SYSTEM "chrome://pagesigner/locale/overlay.dtd">
|
||||
|
||||
<window
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
width="1"
|
||||
height="1"
|
||||
onload="window.close();"> <!-- Close window if it gets opened directly for some reason -->
|
||||
<setting pref="extensions.pagesigner.fallback" type="string" title="Use fallback server (if oracle is not available)" />
|
||||
<setting pref="extensions.pagesigner.verbose" type="bool" title="Verbose logging" value="false" />
|
||||
</window>
|
||||
|
Before Width: | Height: | Size: 659 B |
690
content/main.js
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>Manage your PageSigner notarization files.</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<meta name="description" content="PageSigner" />
|
||||
<link rel="stylesheet" href="manager.css" type="text/css" media="screen"/>
|
||||
<link rel="stylesheet" type="text/css" href="sweetalert.css">
|
||||
<style>
|
||||
a {
|
||||
cursor: pointer; cursor: hand;
|
||||
text-decoration: underline;
|
||||
color: blue;
|
||||
}
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<textarea hidden id="extension2manager"></textarea>
|
||||
<textarea hidden id="manager2extension"></textarea>
|
||||
|
||||
<div id="mydata">
|
||||
<b>Your stored pagesigner notarization (.pgsg) files. Refresh the page (F5) to update. </b>
|
||||
|
||||
<table class="table1" id="myTableData" width="100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th title='Name of notarization file; click Rename to change the name to something more descriptive' scope='col' colspan='4' abbr='File'>File</th>
|
||||
<th title='date file was created and site name of the page' scope='col' abbr='Filename'>Creation date , server name</th>
|
||||
<th title='mine if the file was created by you, imported otherwise' scope='col' abbr='Date'>Mine/imported</th>
|
||||
<th title='whether the addon verifies that the contents are signed correctly' scope='col' abbr='Verified'>Verified</th>
|
||||
<th title='the identity of the verifying notary server' scope='col' abbr='Verifier'>Verifier</th>
|
||||
<th title='links to the html file on disk which is verified to come from the given site; raw shows the file in text' scope='col' abbr='Html'>View Data</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="myTableBody">
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<script src="manager.js2"></script>
|
||||
<script src="sweetalert.min.js2"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 937 B |
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
@@ -1,4 +0,0 @@
|
||||
chrome.runtime.onMessage.addListener(function(data){
|
||||
if (data.destination !== 'sweetalert') return;
|
||||
swal(data.args);
|
||||
});
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
//Do not remove this empty file
|
||||
@@ -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
|
||||
2013
content/tlsn.js
@@ -1,282 +0,0 @@
|
||||
//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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -5,18 +5,19 @@
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
<em:id>pagesigner@tlsnotary</em:id>
|
||||
<em:version>1.0.7.2</em:version>
|
||||
<em:version>1.1.0</em:version>
|
||||
<em:type>2</em:type>
|
||||
<em:bootstrap>true</em:bootstrap>
|
||||
<em:unpack>true</em:unpack>
|
||||
<em:hasEmbeddedWebExtension>true</em:hasEmbeddedWebExtension>
|
||||
|
||||
<!-- Target Application this extension can install into,
|
||||
with minimum and maximum supported versions. -->
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
|
||||
<em:minVersion>43.0</em:minVersion>
|
||||
<em:maxVersion>45.0</em:maxVersion>
|
||||
<em:minVersion>53.0</em:minVersion>
|
||||
<em:maxVersion>*</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
|
||||
|
||||
@@ -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"
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
8
webextension/content/about.js
Normal file
@@ -0,0 +1,8 @@
|
||||
var link1 = document.getElementById("link1");
|
||||
link1.addEventListener("click", function(evt) {
|
||||
sendMessage({
|
||||
'destination': 'extension',
|
||||
'message': 'openLink1'
|
||||
});
|
||||
window.close();
|
||||
});
|
||||
BIN
webextension/content/arrow16.png
Normal file
|
After Width: | Height: | Size: 536 B |
BIN
webextension/content/arrow24.png
Normal file
|
After Width: | Height: | Size: 979 B |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.9 KiB |
4
webextension/content/file_picker.html
Normal file
@@ -0,0 +1,4 @@
|
||||
<body>
|
||||
<input type="file" id="import"></input>
|
||||
<script src="file_picker.js"></script>
|
||||
</body>
|
||||
52
webextension/content/file_picker.js
Normal file
@@ -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);
|
||||
}
|
||||
});
|
||||
BIN
webextension/content/icon.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
webextension/content/icon_error.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
BIN
webextension/content/import.png
Normal file
|
After Width: | Height: | Size: 501 B |
1374
webextension/content/main.js
Normal file
|
Before Width: | Height: | Size: 543 B After Width: | Height: | Size: 543 B |
133
webextension/content/manager.css
Normal file
@@ -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;
|
||||
}
|
||||
61
webextension/content/manager.html
Normal file
@@ -0,0 +1,61 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
|
||||
<head>
|
||||
<title>Manage your PageSigner notarization files.</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<meta name="description" content="PageSigner" />
|
||||
<link rel="stylesheet" href="manager.css" type="text/css" media="screen" />
|
||||
<link rel="stylesheet" type="text/css" href="sweetalert.css">
|
||||
<style>
|
||||
a {
|
||||
cursor: pointer;
|
||||
cursor: hand;
|
||||
text-decoration: underline;
|
||||
color: blue;
|
||||
}
|
||||
|
||||
#toprightimg {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#toprightimg img {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
right: 0px;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<textarea hidden id="extension2manager"></textarea>
|
||||
<textarea hidden id="manager2extension"></textarea>
|
||||
|
||||
<div id="mydata">
|
||||
|
||||
<table class="table1" id="myTableData" width="100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope='col' colspan='4' abbr='File'>File</th>
|
||||
<th scope='col' abbr='Filename'>Creation date</th>
|
||||
<th scope='col' abbr='Verified'>Verified</th>
|
||||
<th title='The identity of the verifying notary server' scope='col' abbr='Verifier'>Verifier</th>
|
||||
<th scope='col' abbr='Html'>View Data</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="myTableBody">
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<script src="manager.js"></script>
|
||||
<script src="sweetalert.min.js"></script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
276
webextension/content/manager.js
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
100
webextension/content/notification_bar.js
Normal file
@@ -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);
|
||||
|
||||
}
|
||||
273
webextension/content/oracles.js
Normal file
@@ -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');
|
||||
});
|
||||
}
|
||||
102
webextension/content/popup.html
Normal file
@@ -0,0 +1,102 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<style>
|
||||
body {
|
||||
margin-top: 0px;
|
||||
margin-bottom: 0px;
|
||||
margin-left: 0px;
|
||||
margin-right: 0px;
|
||||
width: 16em;
|
||||
/* Fonts Chrome specifies for each OS: */
|
||||
/* Linux: 'DejaVu Sans', Arial, sans-serif */
|
||||
/* Mac: 'Lucida Grande', sans-serif */
|
||||
/* Windows: 'Segoe UI', Tahoma, sans-serif */
|
||||
font-family: 'Lucida Grande', 'Segoe UI', Tahoma, 'DejaVu Sans', Arial, sans-serif;
|
||||
font-size: 75%;
|
||||
color: #303942;
|
||||
}
|
||||
|
||||
tr.border_bottom td {
|
||||
border-bottom: 1pt solid grey;
|
||||
}
|
||||
|
||||
tr:hover {
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
td {
|
||||
font-size: 11pt;
|
||||
padding-top: 7px;
|
||||
padding-bottom: 7px;
|
||||
padding-right: 0px;
|
||||
padding-left: 0px;
|
||||
}
|
||||
|
||||
.menu_img {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
padding-right: 0px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
|
||||
<body>
|
||||
|
||||
<div style='padding: 3px; word-wrap: break-word;' id='aboutWindow' hidden>
|
||||
<h1 align="center">PageSigner</h1>
|
||||
<p align="center">Using <a href="#" id="link1">TLSNotary</a> technology.</p>
|
||||
<p align="center">Email the developers: </p>
|
||||
<p align="center">
|
||||
<a href="mailto:tlsnotarygroup@gmail.com">tlsnotarygroup@gmail.com</a></p>
|
||||
<p align="center">Please support development by donating bitcoin <a href='#' id="donate_link">here</a></p>
|
||||
</div>
|
||||
|
||||
|
||||
<table style="width:100%" id='menu' hidden>
|
||||
<tr class="border_bottom">
|
||||
|
||||
<td id="notarize">
|
||||
<img class='menu_img' src="icon.png"></img>
|
||||
<text style='padding-right: 5px;'>Notarize this page</text>
|
||||
|
||||
</td>
|
||||
<td id="notarizeAfter">
|
||||
<img style="cursor:pointer;width:12px;height:12px;position:fixed;right:0px;top:2px" title="Click this tiny cursor icon if you want to make a click on the page first. As soon as you click on the page, PageSigner will start notarizing" class='menu_img'
|
||||
src="arrow16.png"></img>
|
||||
</td>
|
||||
<tr class="border_bottom">
|
||||
<td id="manage">
|
||||
<img class='menu_img' src="manage.png"></img>
|
||||
<text>Manage files</text>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="border_bottom">
|
||||
<td id="import">
|
||||
<img class='menu_img' src="verify.png"></img>
|
||||
<text>Import .pgsg files</text>
|
||||
</tr>
|
||||
<tr>
|
||||
<td id="about">
|
||||
<img class='menu_img' src="icon.png"></img>
|
||||
<text>About</text>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<a href="https://chrome.google.com/webstore/detail/pagesigner-helper-app/oclohfdjoojomkfddjclanpogcnjhemd" id="app_not_installed" hidden><h1>Click here to install the helper app needed for PageSigner to work</h1></a>
|
||||
|
||||
<a href="chrome://extensions" id="app_disabled" hidden><h1>Click here and then enable PageSigner helper app</h1></a>
|
||||
|
||||
<h3 id='notarization_in_progress' hidden>Notarization in progress. Please wait...</h3>
|
||||
|
||||
<h3 id='waiting_for_click' hidden>Waiting for you to click any https:// link on the page...</h3>
|
||||
|
||||
<h3 id='popup_error' hidden></h3>
|
||||
|
||||
<script src="popup.js"></script>
|
||||
<script src="about.js"></script>
|
||||
|
||||
</body>
|
||||
161
webextension/content/popup.js
Normal file
@@ -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);
|
||||
117
webextension/content/socket.js
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
961
webextension/content/sweetalert.css
Normal file
@@ -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;
|
||||
}
|
||||
4
webextension/content/sweetalert_listener.js
Normal file
@@ -0,0 +1,4 @@
|
||||
chrome.runtime.onMessage.addListener(function(data) {
|
||||
if (data.destination !== 'sweetalert') return;
|
||||
swal(data.args);
|
||||
});
|
||||
1
webextension/content/testing/testing.js
Normal file
@@ -0,0 +1 @@
|
||||
//Do not remove this file
|
||||
2089
webextension/content/tlsn.js
Normal file
283
webextension/content/tlsn_utils.js
Normal file
@@ -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;
|
||||
}
|
||||
|
Before Width: | Height: | Size: 614 B After Width: | Height: | Size: 614 B |
9
webextension/content/viewer.html
Normal file
@@ -0,0 +1,9 @@
|
||||
<html>
|
||||
<script src="viewer.js"></script>
|
||||
<script src="notification_bar.js"></script>
|
||||
|
||||
<body>
|
||||
<plaintext id='text' hidden></plaintext>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
36
webextension/content/viewer.js
Normal file
@@ -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();
|
||||
}
|
||||
});
|
||||
51
webextension/manifest.json
Normal file
@@ -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",
|
||||
"<all_urls>"
|
||||
],
|
||||
"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'"
|
||||
|
||||
}
|
||||