From 03084cf6a50a7acb27be2f9beaef146f05214c2f Mon Sep 17 00:00:00 2001 From: themighty1 Date: Thu, 20 Jan 2022 17:39:14 +0300 Subject: [PATCH] feat: allow to preview notarization. Often the user may want to see what the notarization result will look like without spending time on the 2PC protocol. This adds a "Preview" menu item to allow to do that. --- core/Main.js | 126 ++++++----- core/TLS.js | 3 +- core/TLSNotarySession.js | 4 +- core/TLSprobe.js | 204 ++++++++++++++++++ core/globals.js | 8 +- core/twopc/TWOPC.js | 173 +++++++++------ ui/DetailsViewer.js | 31 +++ ui/FileChooser.js | 8 +- ui/Manager.js | 61 +++--- ui/NotificationBar.js | 42 ++-- ui/Popup.js | 42 +++- ui/RawViewer.js | 30 --- ui/Viewer.js | 13 +- .../{rawviewer.html => detailsViewer.html} | 2 +- ui/html/popup.html | 44 ++-- ui/img/SOURCES | 42 ++++ ui/img/delete.svg | 46 +++- ui/img/edited.svg | 1 - ui/img/export.svg | 76 ++----- ui/img/files.svg | 17 +- ui/img/import.svg | 35 +-- ui/img/preview.svg | 8 + ui/img/rename.svg | 26 +-- 23 files changed, 697 insertions(+), 345 deletions(-) create mode 100644 core/TLSprobe.js create mode 100644 ui/DetailsViewer.js delete mode 100644 ui/RawViewer.js rename ui/html/{rawviewer.html => detailsViewer.html} (93%) create mode 100644 ui/img/SOURCES delete mode 100644 ui/img/edited.svg create mode 100644 ui/img/preview.svg diff --git a/core/Main.js b/core/Main.js index 89c24d2..06d0e5e 100644 --- a/core/Main.js +++ b/core/Main.js @@ -13,6 +13,7 @@ import {Socket} from './Socket.js'; import {TLS, getExpandedKeys, decrypt_tls_responseV6} from './TLS.js'; import {verifyNotary, getURLFetcherDoc} from './oracles.js'; import {TLSNotarySession} from './TLSNotarySession.js'; +import {TLSprobe} from './TLSprobe.js'; import {ProgressMonitor} from './ProgressMonitor.js'; import {FirstTimeSetup} from './FirstTimeSetup.js'; @@ -81,8 +82,8 @@ export class Main{ // Some preferences may not exist if we are upgrading from // a previous PageSigner version. Create the preferences. - if (await getPref('firstTimeInitCompleted') === null){ - await addNewPreference('firstTimeInitCompleted', false); + if (await getPref('firstTimeInitCompletedv2') === null){ + await addNewPreference('firstTimeInitCompletedv2', false); await addNewPreference('parsedCircuits', null); } if (await getPref('trustedOracle') === null){ @@ -331,8 +332,8 @@ export class Main{ } - async prepareNotarization(after_click) { - if (!this.trustedOracleReady) { + async prepareNotarization(after_click = false, isPreview = false) { + if (!isPreview && !this.trustedOracleReady) { this.sendAlert({ title: 'PageSigner error.', text: 'Cannot notarize because something is wrong with PageSigner server. Please try again later' @@ -435,7 +436,7 @@ export class Main{ oBSH_details['requestBody'] = oBR_details.requestBody; } const rv = this.getHeaders(oBSH_details); - this.startNotarization(rv.headers, rv.server, rv.port) + this.startNotarization(rv.headers, rv.server, rv.port, isPreview) .catch(err => { console.log('Notarization aborted.', err); console.trace(); @@ -527,20 +528,20 @@ export class Main{ console.log('ext got msg', data); switch (data.message){ case 'rename': - await renameSession(data.args.dir, data.args.newname); + await renameSession(data.sid, data.newname); this.sendSessions(await getAllSessions()); break; case 'delete': - await deleteSession(data.args.dir); + await deleteSession(data.sid); this.sendSessions(await getAllSessions()); break; case 'import': // data is js array - this.importPgsgAndShow(new Uint8Array(data.args.data)); + this.importPgsgAndShow(new Uint8Array(data.data)); break; case 'export': - this.sendToManager({'pgsg': JSON.stringify(await this.getPGSG(data.args.dir)), - 'name': (await getSession(data.args.dir)).sessionName}, 'export'); + this.sendToManager({'pgsg': JSON.stringify(await this.getPGSG(data.sid)), + 'name': (await getSession(data.sid)).sessionName}, 'export'); break; case 'notarize': this.prepareNotarization(false); @@ -548,6 +549,9 @@ export class Main{ case 'notarizeAfter': this.prepareNotarization(true); break; + case 'preview': + this.prepareNotarization(false, true); + break; case 'manage': this.openManager(); break; @@ -557,19 +561,16 @@ export class Main{ case 'openLink1': chrome.tabs.create({url: 'https://www.tlsnotary.org'}); break; - case 'donate link': - chrome.tabs.create({url: 'https://www.tlsnotary.org/#Donate'}); + case 'showSession': + this.showSession(data.sid); break; - case 'viewdata': - this.openViewer(data.args.dir); + case 'showDetails': + this.openDetails(data.sid); break; - case 'viewraw': - this.openDetails(data.args.dir, false); + case 'showPreviewDetails': + this.openPreviewDetails(data.serverName, data.request, data.response); break; - case 'raw editor': - this.openDetails(data.args.dir, true); - break; - case 'file picker': + case 'fileChooser': this.openFileChooser(); break; case 'openChromeExtensions': @@ -582,7 +583,7 @@ export class Main{ this.openPythonScript(); break; case 'pendingAction': - this.pendingAction = data.args; + this.pendingAction = data.action; break; case 'useNotaryNoSandbox': this.useNotaryNoSandbox(data.IP); @@ -592,10 +593,17 @@ export class Main{ } } - async startNotarization(headers, server, port) { + // startNotarization starts a TLSNotary session, saves the session result + // and displays it to the user. If we are in the preview mode, then we send + // the request directly to the server and display the result. + async startNotarization(headers, server, port, isPreview=false) { + if (isPreview){ + await this.startPreview(headers, server, port); + return; + } this.notarization_in_progress = true; this.pm.init(); - this.isFirstTimeSetupNeeded = ! await getPref('firstTimeInitCompleted'); + this.isFirstTimeSetupNeeded = ! await getPref('firstTimeInitCompletedv2'); chrome.runtime.sendMessage({ destination: 'popup', message: 'notarization_in_progress', @@ -606,7 +614,7 @@ export class Main{ console.time('setPref'); await setPref('parsedCircuits', obj); console.timeEnd('setPref'); - await setPref('firstTimeInitCompleted', true); + await setPref('firstTimeInitCompletedv2', true); } const circuits = await getPref('parsedCircuits'); const session = new TLSNotarySession( @@ -624,6 +632,18 @@ export class Main{ this.showSession(date); } + // startPreview does not perfrom a TLSNotary session but simply fetches the + // resource from the webserver and shows the user a preview of what the + // notarization result will look like, were the user to initiate a notarization. + // This is especially useful when the the user picks which headers/resources + // to include in the notarization and wants to have a quick preview. + async startPreview(headers, server, port) { + const preview = new TLSprobe(server, port, headers, globals.sessionOptions); + const response = await preview.start(); + console.log('response was: ', response); + await this.openViewer(server, headers, response); + } + loadDefaultIcon(){ const url = chrome.extension.getURL('ui/img/icon.png'); chrome.browserAction.setIcon({path: url}); @@ -632,11 +652,13 @@ export class Main{ // opens a tab showing the session. sid is a unique session id // creation time is sid. async showSession (sid){ - await this.openViewer(sid); + const data = await getSession(sid); + const blob = await getSessionBlob(sid); + if (data === null) {throw('failed to get index', sid);} + await this.openViewer(data.serverName, blob.request, blob.response, sid); this.sendSessions( await getAllSessions()); // refresh manager } - openPythonScript(){ const url = chrome.extension.getURL('pagesigner.py'); chrome.tabs.create({url: url}, function(t){ @@ -885,13 +907,10 @@ export class Main{ } - async openViewer(sid) { - const data = await getSession(sid); - const blob = await getSessionBlob(sid); - if (data === null) {throw('failed to get index', sid);} - const commonName = data.serverName; - const request = blob.request; - const response = blob.response; + // openViewer opens a new browser tab (or reuses the import tab, if importing + // happened), waits for the tab to fully load and sends data for the viewer + // to display. + async openViewer(serverName, request, response, sid) { let tabId = null;// the id of the tab that we will be sending to const url = chrome.extension.getURL('ui/html/viewer.html'); @@ -933,25 +952,32 @@ export class Main{ } console.log('send to viewer'); - // the tab is either an already opened import tab or a fully-loaded new viewer tab + // the tab is either an already opened import tab or a fully-loaded new viewer tab. + // if sid was not set, then this is a preview tab. // We already checked that the new viewer's tab DOM was loaded. Proceed to send the data chrome.runtime.sendMessage({ destination: 'viewer', message: 'show', tabId: tabId, - data: { - request: request, - response: response, - sessionId: sid, - serverName: commonName - } + request: request, + response: response, + sessionId: sid, + serverName: serverName }); } - async openDetails(sid, isEditor) { + openPreviewDetails(serverName, request, response){ + this.doOpenDetails(serverName, request, response); + } + + async openDetails(sid){ const data = await getSession(sid); const blob = await getSessionBlob(sid); - const url = chrome.extension.getURL('ui/html/rawviewer.html'); + this.doOpenDetails(data.serverName, blob.request, blob.response, sid); + } + + async doOpenDetails(serverName, request, response, sid) { + const url = chrome.extension.getURL('ui/html/detailsViewer.html'); let tabId = null; // id of the tab to which we will send the data const myTabs = []; @@ -964,21 +990,19 @@ export class Main{ await new Promise(function(resolve) { chrome.tabs.create({url: url}, async function(t){ tabId = t.id; - await that.checkIfTabOpened(t, 'isRawViewer', myTabs); + await that.checkIfTabOpened(t, 'isDetailsViewer', myTabs); resolve(); }); }); chrome.runtime.sendMessage({ - destination: 'rawviewer', - message: isEditor ? 'edit' : 'show', + destination: 'detailsViewer', + message: 'show', tabId: tabId, - data: { - request: blob.request, - response: blob.response, - sessionId: sid, - serverName: data.serverName - } + request: request, + response: response, + sessionId: sid, + serverName: serverName }); } diff --git a/core/TLS.js b/core/TLS.js index 406bc88..535980e 100644 --- a/core/TLS.js +++ b/core/TLS.js @@ -358,8 +358,7 @@ export class TLS { return this.rsaSig; } - // TODO this description is incorrect - // sendClientFinished accepts encrypted Client Finished (CF), auth tag for CF, verify_data for CF. + // sendClientFinished accepts encrypted Client Finished (CF). // It then sends Client Key Exchange, Change Cipher Spec and encrypted Client Finished async sendClientFinished(encCF, tagCF){ const cke_tls_record_header = new Uint8Array([0x16, 0x03, 0x03, 0x00, 0x46]); // Type: Handshake, Version: TLS 1.2, Length diff --git a/core/TLSNotarySession.js b/core/TLSNotarySession.js index ad9bac4..ffeb35b 100644 --- a/core/TLSNotarySession.js +++ b/core/TLSNotarySession.js @@ -27,10 +27,8 @@ export class TLSNotarySession{ await this.twopc.init(); await this.tls.buildAndSendClientHello(); const serverEcPubkey = await this.tls.receiveAndParseServerHello(); - const serverX = serverEcPubkey.slice(1, 33); - const serverY = serverEcPubkey.slice(33, 65); if ( this.pm) this.pm.update('last_stage', {'current': 3, 'total': 10}); - const [pmsShare, cpubBytes] = await this.twopc.getECDHShare(serverX, serverY); + const [pmsShare, cpubBytes] = await this.twopc.getECDHShare(serverEcPubkey); if ( this.pm) this.pm.update('last_stage', {'current': 4, 'total': 10}); await this.tls.buildClientKeyExchange(cpubBytes); diff --git a/core/TLSprobe.js b/core/TLSprobe.js new file mode 100644 index 0000000..49c7b0a --- /dev/null +++ b/core/TLSprobe.js @@ -0,0 +1,204 @@ +/* global bcuNode, ECSimple */ + +import {TLS, decrypt_tls_responseV6} from './TLS.js'; + + +var bcu; +if (typeof(window) !== 'undefined'){ + import('./third-party/math.js').then((module) => { + bcu = module; + }); +} else { + // we are in node. bcuNode must have been made global + bcu = bcuNode; +} +import {assert, concatTA, int2ba, str2ba, ba2int, getRandom, eq, sha256, ba2str} + from './utils.js'; + +// class TLSprobe is used when we want to give a preview of notarization. +// It creates a TLS connection and fetches a resource. +export class TLSprobe extends TLS{ + constructor(server, port, request, sessionOptions){ + super(server, port, request, sessionOptions); + } + + async start(){ + await super.buildAndSendClientHello(); + const serverEcPubkey = await super.receiveAndParseServerHello(); + const [pms, cpubBytes] = this.generatePMS(serverEcPubkey); + await super.buildClientKeyExchange(cpubBytes); + const [cr, sr] = await super.getRandoms(); + const [cwk, swk, civ, siv, MS_CryptoKey] = await this.getExpandedKeys(pms, cr, sr); + const verifyData = await this.computeVerifyDataCF(MS_CryptoKey, super.getAllHandshakes()); + const cf = concatTA(new Uint8Array([0x14, 0x00, 0x00, 0x0c]), verifyData); + super.updateAllHandshakes(cf); + const [encCF, tagCF] = await this.encryptClientFinished(cf, cwk, civ); + await super.sendClientFinished(encCF, tagCF); + const encSF = await super.receiveServerFinished(); + await this.checkServerFinished(encSF, super.getAllHandshakes(), MS_CryptoKey, swk, siv); + const encReq = await this.encryptRequest(str2ba(this.headers), cwk, civ); + this.sendRequest([encReq]); + const serverRecords = await super.receiveServerResponse(); + const plaintextRecs = await decrypt_tls_responseV6(serverRecords, swk, siv); + let plaintextFlat = ''; + for (const rec of plaintextRecs){ + plaintextFlat += ba2str(rec); + } + return plaintextFlat; + } + + + // serverEcPubkey is webserver's ephemeral pubkey from the Server Key Exchange + generatePMS(serverEcPubkey) { + const x = serverEcPubkey.slice(1, 33); + const y = serverEcPubkey.slice(33, 65); + this.secp256r1 = new ECSimple.Curve( + 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFCn, + 0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604Bn, + 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551n, + (2n ** 224n) * (2n ** 32n - 1n) + 2n ** 192n + 2n ** 96n - 1n, + new ECSimple.ModPoint( + 0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296n, + 0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5n, + ), + ); + + const Q_server = new ECSimple.ModPoint(ba2int(x), ba2int(y)); + const d = bcu.randBetween(this.secp256r1.n - 1n, 1n); + const Q = this.secp256r1.multiply(this.secp256r1.g, d); + const ECpoint = this.secp256r1.multiply(Q_server, d); + const pms = ECpoint.x; + + const pubBytes = concatTA(int2ba(Q.x, 32), int2ba(Q.y, 32)); + return [int2ba(pms, 32), pubBytes]; + } + + async getExpandedKeys(preMasterSecret, cr, sr){ + const Secret_CryptoKey = await crypto.subtle.importKey( + 'raw', + preMasterSecret.buffer, + {name: 'HMAC', hash:'SHA-256'}, + true, + ['sign']); + + // calculate Master Secret and expanded keys + const seed = concatTA(str2ba('master secret'), cr, sr); + const a0 = seed; + const a1 = new Uint8Array (await crypto.subtle.sign('HMAC', Secret_CryptoKey, a0.buffer)); + const a2 = new Uint8Array (await crypto.subtle.sign('HMAC', Secret_CryptoKey, a1.buffer)); + const p1 = new Uint8Array (await crypto.subtle.sign('HMAC', Secret_CryptoKey, concatTA(a1, seed).buffer)); + const p2 = new Uint8Array (await crypto.subtle.sign('HMAC', Secret_CryptoKey, concatTA(a2, seed).buffer)); + const ms = concatTA(p1, p2).slice(0, 48); + const MS_CryptoKey = await crypto.subtle.importKey('raw', ms.buffer, {name: 'HMAC', hash:'SHA-256'}, true, ['sign']); + + // Expand keys + const eseed = concatTA(str2ba('key expansion'), sr, cr); + const ea0 = eseed; + const ea1 = new Uint8Array (await crypto.subtle.sign('HMAC', MS_CryptoKey, ea0.buffer)); + const ea2 = new Uint8Array (await crypto.subtle.sign('HMAC', MS_CryptoKey, ea1.buffer)); + const ep1 = new Uint8Array (await crypto.subtle.sign('HMAC', MS_CryptoKey, concatTA(ea1, eseed).buffer)); + const ep2 = new Uint8Array (await crypto.subtle.sign('HMAC', MS_CryptoKey, concatTA(ea2, eseed).buffer)); + + const ek = concatTA(ep1, ep2).slice(0, 40); + // GCM doesnt need MAC keys + const client_write_key = ek.slice(0, 16); + const server_write_key = ek.slice(16, 32); + const client_write_IV = ek.slice(32, 36); + const server_write_IV = ek.slice(36, 40); + return [client_write_key, server_write_key, client_write_IV, server_write_IV, MS_CryptoKey]; + } + + // returns verify_data for Client Finished + async computeVerifyDataCF(MS_CryptoKey, allHandshakes){ + const hshash = await sha256(allHandshakes); + const seed = concatTA(str2ba('client finished'), hshash); + const a0 = seed; + const a1 = new Uint8Array (await crypto.subtle.sign('HMAC', MS_CryptoKey, a0.buffer)); + const p1 = new Uint8Array (await crypto.subtle.sign('HMAC', MS_CryptoKey, concatTA(a1, seed).buffer)); + const verifyData = p1.slice(0, 12); + return verifyData; + } + + // encrypt Client Finished and return the encrypted CF (without the 8-byte nonce) + // and the auth tag + async encryptClientFinished(cf, cwk, civ){ + const explicit_nonce = int2ba(1, 8); //explicit nonce is hard-coded to 1 for now + //const explicit_nonce = getRandom(8); + const nonce = concatTA(civ, explicit_nonce); + const seq_num = 0; + const tmp = []; + tmp.push(...Array.from(int2ba(seq_num, 8))); + tmp.push(0x16); // type 0x16 = Handshake + tmp.push(0x03, 0x03); // TLS Version 1.2 + tmp.push(0x00, 0x10); // 16 bytes of unencrypted data + //additional authenticated data + const aad = new Uint8Array(tmp); + const key = await crypto.subtle.importKey('raw', cwk.buffer, 'AES-GCM', + true, ['encrypt']); + const ciphertext = new Uint8Array(await crypto.subtle.encrypt({name: 'AES-GCM', + iv: nonce.buffer, additionalData: aad.buffer}, key, cf.buffer)); + console.log('len in encryptClientFinished is ', ciphertext.length); + return [ciphertext.slice(0, -16), ciphertext.slice(-16)]; + } + + // encrypt Server Finished using the explicit_nonce + // return ciphertext (without the nonce) and the tag + async encryptServerFinished(sf, explicit_nonce, swk, siv){ + const nonce = concatTA(siv, explicit_nonce); + const seq_num = 0; + const tmp = []; + tmp.push(...Array.from(int2ba(seq_num, 8))); + tmp.push(0x16, 0x03, 0x03); // Handshake, TLS Version 1.2 + tmp.push(0x00, 0x10); // 16 bytes of unencrypted data + //additional authenticated data + const aad = new Uint8Array(tmp); + const key = await crypto.subtle.importKey('raw', swk.buffer, 'AES-GCM', + true, ['encrypt']); + const ciphertext = new Uint8Array(await crypto.subtle.encrypt({name: 'AES-GCM', + iv: nonce.buffer, additionalData: aad.buffer}, key, sf.buffer)); + return [ciphertext.slice(0, -16), ciphertext.slice(-16)]; + } + + async checkServerFinished(encSF, allHandshakes, MS_CryptoKey, swk, siv){ + const hshash = await sha256(allHandshakes); + const seed = concatTA(str2ba('server finished'), hshash); + const a0 = seed; + const a1 = new Uint8Array (await crypto.subtle.sign('HMAC', MS_CryptoKey, a0.buffer)); + const p1 = new Uint8Array (await crypto.subtle.sign('HMAC', MS_CryptoKey, concatTA(a1, seed).buffer)); + const verifyData = p1.slice(0, 12); + const sf = concatTA(new Uint8Array([0x14, 0x00, 0x00, 0x0c]), verifyData); + const nonceFromWire = encSF.slice(0, 8); + const [ct, tag] = await this.encryptServerFinished(sf, nonceFromWire, swk, siv); + assert(eq(concatTA(nonceFromWire, ct, tag), encSF)); + } + + async encryptRequest(req, cwk, civ){ + const explicit_nonce = getRandom(8); + const nonce = concatTA(civ, explicit_nonce); + let seq_num = 1; + const tmp = []; + tmp.push(...Array.from(int2ba(seq_num, 8))); + tmp.push(0x17, 0x03, 0x03); // Application data, TLS Version 1.2 + tmp.push(...Array.from(int2ba(req.length, 2))); // bytelength of unencrypted data + //additional authenticated data + const aad = new Uint8Array(tmp); + const key = await crypto.subtle.importKey('raw', cwk.buffer, 'AES-GCM', + true, ['encrypt']); + return concatTA(explicit_nonce, new Uint8Array( + await crypto.subtle.encrypt({name: 'AES-GCM', iv: nonce.buffer, + additionalData: aad.buffer}, key, req.buffer))); + } + + sendRequest(records){ + let appdata = new Uint8Array(); + for (let i=0; i< records.length; i++){ + appdata = concatTA( + appdata, + new Uint8Array([0x17, 0x03, 0x03]), // Type: Application data, TLS Version 1.2 + int2ba(records[i].length, 2), // 2-byte length of encrypted data + records[i]); + } + console.log('sending http request'); + this.sckt.send(appdata); + } +} diff --git a/core/globals.js b/core/globals.js index 81a78ea..8cdc1b2 100644 --- a/core/globals.js +++ b/core/globals.js @@ -2,8 +2,8 @@ export const globals = { // defaultNotaryIP/Port is the default IP address/port of the notary server. // If this IP address becomes unavailable, Pagesigner will query backupUrl for // a new notary's IP address and will save the new IP address in the preferences. - //defaultNotaryIP: '127.0.0.1', - defaultNotaryIP: '18.207.130.193', + defaultNotaryIP: '127.0.0.1', + //defaultNotaryIP: '18.207.130.193', defaultNotaryPort: 10011, // backupUrl is the URL to query to get the IP address of another notary // server in case if defaultNotaryIP is unreachable @@ -26,6 +26,6 @@ export const globals = { // if useNotaryNoSandbox is set to true, then we fetch notary's pubkey by // querying /getPubKey and trust it. This is only useful when notary runs // in a non-sandbox environment. - useNotaryNoSandbox: false - //useNotaryNoSandbox: true + //useNotaryNoSandbox: false + useNotaryNoSandbox: true }; \ No newline at end of file diff --git a/core/twopc/TWOPC.js b/core/twopc/TWOPC.js index 8754d3d..83fa69a 100644 --- a/core/twopc/TWOPC.js +++ b/core/twopc/TWOPC.js @@ -12,6 +12,12 @@ import {OTSender} from './OTSender.js'; import {OTReceiver} from './OTReceiver.js'; import {GHASH} from './GHASH.js'; +// class TWOPC implement two-party computation techniques used in the TLSNotary +// session. Paillier 2PC, Gabrled Circuits, Oblivious Transfer. + +// The description of each step of the TLS PRF computation, both inside the +// garbled circuit and outside of it: +// [REF 1] https://github.com/tlsnotary/circuits/blob/master/README export class TWOPC { constructor(notary, plaintextLen, circuits, progressMonitor) { @@ -38,10 +44,18 @@ export class TWOPC { this.uid = Math.random().toString(36).slice(-10); // cs is an array of circuits, where each circuit is an object with the fields: // gatesBlob, gatesCount, wiresCount, notaryInputSize, clientInputSize, - // outputSize, andGateCount + // outputSize, andGateCount, outputsSizes this.cs = Object.keys(circuits).map(k => circuits[k]); - // start count of circuits with 1, push an empy element to index 0 + // start count of circuits with 1, push an empty element to index 0 this.cs.splice(0, 0, undefined); + // The output of a circuit is actually multiple concatenated values. We need + // to know how many bits each output value has in order to parse the output + this.cs[1]['outputsSizes'] = [256, 256]; + this.cs[2]['outputsSizes'] = [256, 256]; + this.cs[3]['outputsSizes'] = [128, 128, 32, 32, 128, 128, 128]; + this.cs[4]['outputsSizes'] = [128, 128, 128, 96]; + this.cs[5]['outputsSizes'] = [128]; + this.cs[6]['outputsSizes'] = [128]; // output is the output of the circuit as array of bytes this.output = Array(this.cs.length); // Commits are used to ensure malicious security of garbled circuits @@ -113,8 +127,11 @@ export class TWOPC { } } - async getECDHShare(x, y){ - const paillier = new Paillier2PC(x, y); + // serverEcPubkey is webserver's ephemeral pubkey from the Server Key Exchange + async getECDHShare(serverEcPubkey){ + const serverX = serverEcPubkey.slice(1, 33); + const serverY = serverEcPubkey.slice(33, 65); + const paillier = new Paillier2PC(serverX, serverY); const step1Resp = await this.send('step1', paillier.step1()); const step2Promise = this.send('step2', paillier.step2(step1Resp), true); // while Notary is responding to step2 we can do some more computations @@ -141,25 +158,26 @@ export class TWOPC { } const c1Mask = getRandom(32); - const c1Out = await this.runCircuit([this.clientPMSShare, c1Mask], 1); - // unmask the output - const innerState1Masked = c1Out[0].slice(0, 32); - const innerState1 = xor(innerState1Masked, c1Mask); + // [REF 1] Step 2 + const c1Out = (await this.runCircuit([this.clientPMSShare, c1Mask], 1))[0]; + // unmask only the outputs relevant to the client + const pmsInnerHashState = xor(c1Out.slice(32, 64), c1Mask); - const [c2_p2, c2_p1inner] = await this.phase2(innerState1); + // [REF 1] Steps 3,5,7,9 + const [c2_p2, c2_p1inner] = await this.phase2(pmsInnerHashState); const c2Mask = getRandom(32); const input2 = [c2_p1inner, c2_p2.subarray(0, 16), c2Mask]; - const c2Out = await this.runCircuit(input2, 2); + // [REF 1] Step 10, 12 + const c2Out = (await this.runCircuit(input2, 2))[0]; // unmask the output - const innerState2Masked = c2Out[0].slice(0, 32); - const innerState2 = xor(innerState2Masked, c2Mask); - - const [c3_p1inner_vd, c3_p2inner, c3_p1inner] = await this.phase3(innerState2); - // for readability, mask indexing starts from 1 - const masks3 = [0, 16, 16, 4, 4, 16, 16, 16, 12].map(x => getRandom(x)); - const input3 = [c3_p1inner, c3_p2inner, c3_p1inner_vd, ...masks3.slice(1)]; - const c3Out = await this.runCircuit(input3, 3); - const [encFinished, tag, verify_data] = await this.phase4(c3Out, masks3); + const msInnerHashState = xor(c2Out.slice(32, 64), c2Mask); + // [REF 1] Steps 13,15,17,20,22 + const [verify_data, c3_p2inner, c3_p1inner] = await this.phase3(msInnerHashState); + // for readability, indexing of masks starts from 1 + const masks3 = [0, 16, 16, 4, 4, 16, 16, 16].map(x => getRandom(x)); + const input3 = [c3_p1inner, c3_p2inner, ...masks3.slice(1)]; + const c3Out = (await this.runCircuit(input3, 3))[0]; + const [encFinished, tag] = await this.phase4(c3Out, masks3, verify_data); return [encFinished, tag, verify_data]; } @@ -174,28 +192,31 @@ export class TWOPC { const seed = concatTA(str2ba('server finished'), hshash); const a0 = seed; + // [REF 1] Step 25 const a1inner = innerHash(this.innerState_MS, a0); const a1 = await this.send('c4_pre1', a1inner); + // [REF 1] Step 27 const p1inner = innerHash(this.innerState_MS, concatTA(a1, seed)); // for readability, mask indexing starts from 1 const masks4 = [0, 16, 16, 16, 12].map(x => getRandom(x)); const input4 = [p1inner, this.swkShare, this.sivShare, sf_nonce, ...masks4.slice(1)]; - const outArray = await this.runCircuit(input4, 4); - const c4_output = outArray[0]; - console.log('c4Output.length', c4_output.length); - + // [REF 1] Step 28 + const c4out = (await this.runCircuit(input4, 4))[0]; let o = 0; // offset - const verify_dataMasked = c4_output.slice(o, o+=12); - const verify_data = xor(verify_dataMasked, masks4[4]); - const encCounterMasked = c4_output.slice(o, o+=16); - const encCounter = xor(encCounterMasked, masks4[3]); - const gctrSFMasked = c4_output.slice(o, o+=16); - const gctrShare = xor(gctrSFMasked, masks4[2]); - const H1MaskedTwice = c4_output.slice(o, o+=16); + // parse outputs + const H1MaskedTwice = c4out.slice(o, o+=16); + const gctrSFMasked = c4out.slice(o, o+=16); + const encCounterMasked = c4out.slice(o, o+=16); + const verify_dataMasked = c4out.slice(o, o+=12); + // unmask outputs // H1 xor-masked by notary is our share of H^1, the mask is notary's share of H^1 const H1share = xor(H1MaskedTwice, masks4[1]); + const gctrShare = xor(gctrSFMasked, masks4[2]); + const encCounter = xor(encCounterMasked, masks4[3]); + const verify_data = xor(verify_dataMasked, masks4[4]); + const sf = concatTA(new Uint8Array([0x14, 0x00, 0x00, 0x0c]), verify_data); const encSF = xor(sf, encCounter); assert(eq(sf_pure, encSF)); @@ -230,10 +251,10 @@ export class TWOPC { input5 = [].concat(input5, [this.cwkShare, this.civShare, masks5[i], fixedNonce, 10, counter, 10]); } - const outArray = await this.runCircuit(input5, 5); + const c5out = await this.runCircuit(input5, 5); const encCounters = []; for (let i=0; i < this.C5Count; i++){ - encCounters.push(xor(outArray[i], masks5[i])); + encCounters.push(xor(c5out[i], masks5[i])); } return encCounters; } @@ -249,7 +270,7 @@ export class TWOPC { masks6.push(getRandom(16)); input6 = [].concat(input6, [this.cwkShare, this.civShare, masks6[i], nonce]); } - const outArray = await this.runCircuit(input6, 6); + const c6out = await this.runCircuit(input6, 6); const c6CommitSalt = await this.send('checkC6Commit', this.myCommit[6]); // the commit which notary computed on their side must be equal to our commit const saltedCommit = await sha256(concatTA(this.myCommit[6], c6CommitSalt)); @@ -257,7 +278,7 @@ export class TWOPC { const gctrBlocks = []; for (let i=0; i < this.C6Count; i++){ - gctrBlocks.push(xor(outArray[i], masks6[i])); + gctrBlocks.push(xor(c6out[i], masks6[i])); } return gctrBlocks; } @@ -537,7 +558,6 @@ export class TWOPC { } async phase2(innerStateUint8) { - const innerState = new Int32Array(8); for (let i = 0; i < 8; i++) { var hex = ba2hex(innerStateUint8).slice(i * 8, (i + 1) * 8); @@ -554,14 +574,17 @@ export class TWOPC { // ms = (p1+p2)[0:48] const seed = concatTA(str2ba('master secret'), this.client_random, this.server_random); + // [REF 1] Step 3 const a1inner = innerHash(innerState, seed); const a1 = await this.send('c1_step3', a1inner); + // [REF 1] Step 5 const a2inner = innerHash(innerState, a1); const a2 = await this.send('c1_step4', a2inner); + // [REF 1] Step 7 const p2inner = innerHash(innerState, concatTA(a2, seed)); const p2 = await this.send('c1_step5', p2inner); + // [REF 1] Step 9 const p1inner = innerHash(innerState, concatTA(a1, seed)); - return [p2, p1inner]; } @@ -585,42 +608,43 @@ export class TWOPC { const seed = concatTA(str2ba('key expansion'), this.server_random, this.client_random); // at the same time also compute verify_data for Client Finished const seed_vd = concatTA(str2ba('client finished'), this.hs_hash); - + // [REF 1] Step 13 const a1inner = innerHash(this.innerState_MS, seed); + // [REF 1] Step 20 const a1inner_vd = innerHash(this.innerState_MS, seed_vd); const resp4 = await this.send('c2_step3', concatTA(a1inner, a1inner_vd)); const a1 = resp4.subarray(0, 32); const a1_vd = resp4.subarray(32, 64); - + // [REF 1] Step 15 const a2inner = innerHash(this.innerState_MS, a1); + // [REF 1] Step 22 const p1inner_vd = innerHash(this.innerState_MS, concatTA(a1_vd, seed_vd)); const resp5 = await this.send('c2_step4', concatTA(a2inner, p1inner_vd)); const a2 = resp5.subarray(0, 32); - + const verify_data = resp5.subarray(32, 44); + // [REF 1] Step 17 const p1inner = innerHash(this.innerState_MS, concatTA(a1, seed)); const p2inner = innerHash(this.innerState_MS, concatTA(a2, seed)); - - return [p1inner_vd, p2inner, p1inner]; + return [verify_data, p2inner, p1inner]; } - async phase4(outArray, masks3) { - const c3_output = outArray[0]; + async phase4(c3out, masks3, verify_data) { let o = 0; // offset - const verify_dataMasked = c3_output.slice(o, o+=12); - const verify_data = xor(verify_dataMasked, masks3[8]); - const encCounterMasked = c3_output.slice(o, o+=16); - const encCounter = xor(encCounterMasked, masks3[7]); - const gctrMaskedTwice = c3_output.slice(o, o+=16); - const myGctrShare = xor(gctrMaskedTwice, masks3[6]); - const H1MaskedTwice = c3_output.slice(o, o+=16); - const civMaskedTwice = c3_output.slice(o, o+=4); - this.civShare = xor(civMaskedTwice, masks3[4]); - const sivMaskedTwice = c3_output.slice(o, o+=4); - this.sivShare = xor(sivMaskedTwice, masks3[3]); - const cwkMaskedTwice = c3_output.slice(o, o+=16); - const swkMaskedTwice = c3_output.slice(o, o+=16); - this.cwkShare = xor(cwkMaskedTwice, masks3[2]); + // parse all outputs + const swkMaskedTwice = c3out.slice(o, o+=16); + const cwkMaskedTwice = c3out.slice(o, o+=16); + const sivMaskedTwice = c3out.slice(o, o+=4); + const civMaskedTwice = c3out.slice(o, o+=4); + const H1MaskedTwice = c3out.slice(o, o+=16); + const gctrMaskedTwice = c3out.slice(o, o+=16); + const encCounterMasked = c3out.slice(o, o+=16); + // unmask all outputs this.swkShare = xor(swkMaskedTwice, masks3[1]); + this.cwkShare = xor(cwkMaskedTwice, masks3[2]); + this.sivShare = xor(sivMaskedTwice, masks3[3]); + this.civShare = xor(civMaskedTwice, masks3[4]); + const myGctrShare = xor(gctrMaskedTwice, masks3[6]); + const encCounter = xor(encCounterMasked, masks3[7]); // H1 xor-masked by notary is our (i.e. the client's) share of H^1, // notary's mask is his share of H^1. @@ -640,7 +664,7 @@ export class TWOPC { const tagShare = this.ghash.processFinResponse(otResp, [aad, encCF, lenAlenC]); // notary's gctr share is already included in notaryTagShare const tagFromPowersOfH = xor(xor(notaryTagShare, tagShare), myGctrShare); - return [encCF, tagFromPowersOfH, verify_data]; + return [encCF, tagFromPowersOfH]; } // getClientFinishedResumed runs a circuit to obtain data needed to construct the @@ -669,9 +693,10 @@ export class TWOPC { return [this.ephemeralKey, this.eValidFrom, this.eValidUntil, this.eSigByMasterKey]; } - // eslint-disable-next-line class-methods-use-this - // optionally send extra data - async runCircuit(inputs, cNo, extraData = new Uint8Array(0)) { + // runCircuit evaluates a circuit and returns the circuit's output. It exchanges + // OT messages in order to get it's input labels and send to notary his input labels. + // The notary is also evaluating this circuit on his end. + async runCircuit(inputs, cNo) { console.log('in runCircuit', cNo); // inputs is an array of inputs in the order in which the inputs appear in the c*.casm files. // The circuit expects the least bit of the input to be the first bit @@ -688,12 +713,12 @@ export class TWOPC { } const c = this.cs[cNo]; // how many times to repeat evaluation (> 1 only for circuits 5&7 ) - const repeatCount = [0, 1, 1, 1, 1, this.C5Count, 1, this.C6Count][cNo]; + const repeatCount = [0, 1, 1, 1, 1, this.C5Count, this.C6Count][cNo]; // for circuit 1, there was no previous commit const prevCommit = cNo > 1 ? this.myCommit[cNo-1] : new Uint8Array(0); const otReq = this.otR.createRequest(inputBits); - const blob1 = await this.send(`c${cNo}_step1`, concatTA(prevCommit, otReq, extraData)); + const blob1 = await this.send(`c${cNo}_step1`, concatTA(prevCommit, otReq)); let o = 0; if (cNo > 1){ @@ -711,8 +736,8 @@ export class TWOPC { assert(blob1.length == o); const allClientLabels = this.otR.parseResponse(inputBits, hisOtResp); - const senderMsg = this.g.getNotaryLabels(cNo); - const otResp = this.otS.processRequest(hisOtReq, senderMsg); + const notaryLabels = this.g.getNotaryLabels(cNo); + const otResp = this.otS.processRequest(hisOtReq, notaryLabels); const clientLabels = this.g.getClientLabels(inputBits, cNo); const sendPromise = this.send(`c${cNo}_step2`, concatTA(otResp, clientLabels), true); @@ -748,7 +773,9 @@ export class TWOPC { console.log('evaluator output does not match the garbled outputs'); } } - const out = bitsToBytes(bits); + // reverse output bits so that the values of the output be placed in + // the same order as they appear in the *.casm files + const out = this.parseOutputBits(cNo, bits); this.output[cNo] = out; output.push(out); } @@ -757,11 +784,21 @@ export class TWOPC { const cStep2Out = await sendPromise; this.hisSaltedCommit[cNo] = cStep2Out.subarray(0, 32); - const extraDataOut = cStep2Out.subarray(32); - output.push(extraDataOut); return output; } + // parseOutputBits converts the output bits of the circuit number "cNo" into + // a slice of output values in the same order as they appear in the *.casm files + parseOutputBits(cNo, outBits){ + let o = 0; //offset + let outBytes = new Uint8Array(0); + for (const outSize of this.cs[cNo]['outputsSizes']){ + outBytes = concatTA(outBytes, bitsToBytes(outBits.slice(o, o+=outSize))); + } + assert(o == this.cs[cNo].outputSize); + return outBytes; + } + // eslint-disable-next-line class-methods-use-this processBlob(blob) { diff --git a/ui/DetailsViewer.js b/ui/DetailsViewer.js new file mode 100644 index 0000000..23106db --- /dev/null +++ b/ui/DetailsViewer.js @@ -0,0 +1,31 @@ +/* global chrome*/ + +import {decode_str} from './utils.js'; + +// DetailsViewer show session details: raw request, response, notarization time +class DetailsViewer{ + constructor(){ + window.tabid = null; // allow the extension to put the id of the tab which opened this page + window.isDetailsViewer = true; + // isReady will be se to true after message listener is installed + window.isReady = false; + } + + main(){ + chrome.runtime.onMessage.addListener(function(obj) { + if (obj.destination !== 'detailsViewer') return; + console.log('got obj', obj); + if (obj.tabId != window.tabid) return; + document.getElementById('request').textContent = decode_str(obj.request); + document.getElementById('response').textContent = decode_str(obj.response); + if (obj.sessionId != undefined){ + document.getElementById('notarization_time').textContent = obj.sessionId; + } + }); + window.isReady = true; + } +} + +window.detailsViewer = new DetailsViewer(); +window.detailsViewer.main(); + diff --git a/ui/FileChooser.js b/ui/FileChooser.js index df0b23e..1190086 100644 --- a/ui/FileChooser.js +++ b/ui/FileChooser.js @@ -25,11 +25,9 @@ export class FileChooser{ const import_label = document.getElementById('import_label'); import_label.classList.toggle('m-fadeOut'); chrome.runtime.sendMessage({ - 'destination': 'extension', - 'message': 'import', - 'args': { - 'data': Array.from(new Uint8Array(e.target.result)) - } + destination: 'extension', + message: 'import', + data: Array.from(new Uint8Array(e.target.result)) }); // don't close the window, we reuse it to display html } diff --git a/ui/Manager.js b/ui/Manager.js index 1ec6408..ef4be48 100644 --- a/ui/Manager.js +++ b/ui/Manager.js @@ -11,7 +11,6 @@ class Manager{ window.isManager = true; // isReady will be se to true after message listener is installed window.isReady = false; - } main() { @@ -36,8 +35,8 @@ class Manager{ }); window.isReady = true; chrome.runtime.sendMessage({ - 'destination': 'extension', - 'message': 'refresh' + destination: 'extension', + message: 'refresh' }); } @@ -67,7 +66,7 @@ class Manager{ addRow(args) { const that = this; - const session = args.creationTime; + const sid = args.creationTime; const tb = document.getElementById('tableBody'); const row = tb.insertRow(tb.rows.length); @@ -112,11 +111,9 @@ class Manager{ }, function() { chrome.runtime.sendMessage({ - 'destination': 'extension', - 'message': 'export', - 'args': { - 'dir': session - } + destination: 'extension', + message: 'export', + sid: sid }); }); }; @@ -131,7 +128,7 @@ class Manager{ imgRen.style.marginLeft = 10; imgRen.title = 'Give the session a more memorable name'; imgRen.onclick = function(event) { - that.doRename(event.target, session); + that.doRename(event.target, sid); }; iconDiv.appendChild(imgRen); @@ -150,11 +147,9 @@ class Manager{ }, function() { chrome.runtime.sendMessage({ - 'destination': 'extension', - 'message': 'delete', - 'args': { - 'dir': session - } + destination: 'extension', + message: 'delete', + sid: sid }); }); }; @@ -181,11 +176,9 @@ class Manager{ input1.className = 'btn'; input1.onclick = function() { chrome.runtime.sendMessage({ - 'destination': 'extension', - 'message': 'viewdata', - 'args': { - 'dir': session - } + destination: 'extension', + message: 'showSession', + sid: sid }); }; input1.value = 'HTML'; @@ -196,11 +189,9 @@ class Manager{ input2.className = 'btn'; input2.onclick = function() { chrome.runtime.sendMessage({ - 'destination': 'extension', - 'message': 'viewraw', - 'args': { - 'dir': session - } + destination: 'extension', + message: 'showDetails', + sid: sid }); }; input2.value = 'Details'; @@ -211,11 +202,9 @@ class Manager{ input3.className = 'btn'; input3.onclick = function() { chrome.runtime.sendMessage({ - 'destination': 'extension', - 'message': 'raw editor', - 'args': { - 'dir': session - } + destination: 'extension', + message: 'raw editor', + sid: sid }); }; input3.value = 'Edit'; @@ -237,7 +226,7 @@ class Manager{ row.appendChild(td3); } - doRename(t, dir) { + doRename(t, sid) { var isValid = (function() { var rg1 = /^[^\\/:\*\?"<>\|]+$/; // forbidden characters \ / : * ? " < > | var rg2 = /^\./; // cannot start with dot (.) @@ -266,12 +255,10 @@ class Manager{ } else if (new_name === null) return; // escape pressed else { chrome.runtime.sendMessage({ - 'destination': 'extension', - 'message': 'rename', - 'args': { - 'dir': dir, - 'newname': new_name - } + destination: 'extension', + message: 'rename', + sid: sid, + newname: new_name }); } }); diff --git a/ui/NotificationBar.js b/ui/NotificationBar.js index b4b4b9f..4ad29b0 100644 --- a/ui/NotificationBar.js +++ b/ui/NotificationBar.js @@ -3,8 +3,11 @@ export class NotificationBar{ constructor(){} - show(sessionId, serverName, hideButton) { + // show shows the notification bar at the top of the page. + show(sessionId, serverName, request, response, hideButton) { hideButton = hideButton || false; + // if sessionId was not passed, it means we are in the preview mode + const isPreview = sessionId != undefined ? false : true; const table = document.createElement('table'); table.style.position = 'fixed'; @@ -30,25 +33,40 @@ export class NotificationBar{ img.height = 24; img.width = 24; const text = document.createElement('text'); - text.textContent = 'PageSigner verified that this page was received from '; const domain = document.createElement('text'); domain.id = 'domainName'; - domain.textContent = serverName; + + if (isPreview){ + text.textContent = 'This is a preview of what the notarization result will look like. Press Details to preview the raw HTML data.'; + } else { + text.textContent = 'PageSigner verified that this page was received from '; + domain.textContent = serverName; + } const button = document.createElement('button'); button.id = 'viewRaw'; button.textContent = 'Details'; button.style.MozBorderRadius = '4px'; button.style.WebkitBorderRadius = '4px'; button.style.borderRadius = '4px'; - button.onclick = function() { - chrome.runtime.sendMessage({ - destination: 'extension', - message: 'viewraw', - args: { - dir: sessionId - } - }); - }; + if (isPreview){ + button.onclick = function() { + chrome.runtime.sendMessage({ + destination: 'extension', + message: 'showPreviewDetails', + serverName: serverName, + request: request, + response: response + }); + }; + } else { + button.onclick = function() { + chrome.runtime.sendMessage({ + destination: 'extension', + message: 'showDetails', + sid: sessionId + }); + }; + } if (hideButton) { button.hidden = true; } diff --git a/ui/Popup.js b/ui/Popup.js index 0451633..fa63bba 100644 --- a/ui/Popup.js +++ b/ui/Popup.js @@ -24,8 +24,13 @@ class Popup{ document.getElementById('notarize').addEventListener('click', function(){ that.notarizeClicked(false); }); - document.getElementById('notarizeAfter').addEventListener('click', function(){ - that.notarizeClicked(true); + + document.getElementById('notarizeNow').addEventListener('click', function(){ + that.notarizeNowClicked(false); + }); + + document.getElementById('preview').addEventListener('click', function(){ + that.previewClicked(false); }); document.getElementById('manage').addEventListener('click', function() { @@ -39,7 +44,7 @@ class Popup{ document.getElementById('import').addEventListener('click', function() { chrome.runtime.sendMessage({ destination: 'extension', - message: 'file picker' + message: 'fileChooser' }); window.close(); }); @@ -67,8 +72,25 @@ class Popup{ }); } - // notarizeClicked is triggered when Notarize of Notariza after click was pressed - notarizeClicked(isAfterClick){ + // notarizeClicked triggers a dropdown menu when "Notarize this page" was pressed + notarizeClicked(){ + const nn = document.getElementById('notarizeNow'); + const p = document.getElementById('preview'); + const pe = document.getElementById('previewExplanation'); + if (nn.hidden == true){ + nn.hidden = false; + p.hidden = false; + pe.hidden = false; + } else { + nn.hidden = true; + p.hidden = true; + pe.hidden = true; + } + return; + } + + // notarizeNowClicked is triggered when "Notarize now" was pressed + notarizeNowClicked(isAfterClick){ isAfterClick = isAfterClick || false; const msg = isAfterClick ? 'notarizeAfter' : 'notarize'; if (this.is_firefox && ! this.hasPermission && this.currentUrl.startsWith('https://')){ @@ -81,7 +103,7 @@ class Popup{ chrome.runtime.sendMessage({ destination: 'extension', message: 'pendingAction', - args: msg + action: msg }); browser.permissions.request({origins: [this.currentUrl]}); } @@ -96,6 +118,14 @@ class Popup{ } } + previewClicked(){ + chrome.runtime.sendMessage({ + destination: 'extension', + message: 'preview' + }); + window.close(); + } + processMessages(data) { console.log('popup got message', data); if (data.destination !== 'popup') return; diff --git a/ui/RawViewer.js b/ui/RawViewer.js deleted file mode 100644 index 66fcb50..0000000 --- a/ui/RawViewer.js +++ /dev/null @@ -1,30 +0,0 @@ -/* global chrome*/ - -import {decode_str} from './utils.js'; - -class RawViewer{ - constructor(){ - window.tabid = null; // allow the extension to put the id of the tab which opened this page - window.isRawViewer = true; - // isReady will be se to true after message listener is installed - window.isReady = false; - } - - main(){ - chrome.runtime.onMessage.addListener(function(obj) { - if (obj.destination !== 'rawviewer') return; - console.log('got obj', obj); - if (obj.tabId != window.tabid) return; - const request = decode_str(obj.data.request); - const response = decode_str(obj.data.response); - document.getElementById('request').textContent = request; - document.getElementById('response').textContent = response; - document.getElementById('notarization_time').textContent = obj.data.sessionId; - }); - window.isReady = true; - } -} - -window.rawViewer = new RawViewer(); -window.rawViewer.main(); - diff --git a/ui/Viewer.js b/ui/Viewer.js index 385d977..9513d08 100644 --- a/ui/Viewer.js +++ b/ui/Viewer.js @@ -34,13 +34,12 @@ class Viewer{ throw 'unexpected message'; } console.log('got data in viewer'); - var hideButton = false; - var text = msg.data.response; - console.log('text size is', text.length); + let hideButton = false; + console.log('text size is', msg.response.length); // remove the HTTP headers - var http_body = text.split('\r\n\r\n').splice(1).join('\r\n\r\n'); + let http_body = msg.response.split('\r\n\r\n').splice(1).join('\r\n\r\n'); - let type = that.getType(text); + let type = that.getType(msg.response); if (['html', 'json', 'xml', 'txt'].indexOf(type) > -1) { // add CSP to prevent loading any resources from the page const csp = '\r\n'; @@ -53,9 +52,11 @@ class Viewer{ that.view_file(str2ba(http_body), msg.serverName + '.' + type);}; document.getElementById('view file').removeAttribute('hidden'); } + console.log('msg.data.sessionId', msg.sessionId); setTimeout(function(){ // we need timeout because the body may not yet be available - new NotificationBar().show(msg.data.sessionId, msg.data.serverName, hideButton); + new NotificationBar().show(msg.sessionId, msg.serverName, msg.request, + msg.response, hideButton); document.body.style.marginTop = '30px'; }, 1000); }); diff --git a/ui/html/rawviewer.html b/ui/html/detailsViewer.html similarity index 93% rename from ui/html/rawviewer.html rename to ui/html/detailsViewer.html index 8dc8aeb..cfb5911 100644 --- a/ui/html/rawviewer.html +++ b/ui/html/detailsViewer.html @@ -1,4 +1,4 @@ - + - -
- - - - - - + + + + + + + + + + + + + -
+ + Notarize this page +
@@ -134,13 +146,15 @@ Import session +
About +
+