From 8d3130738cb6ae50a08fbbd2fdf72bda17e340af Mon Sep 17 00:00:00 2001 From: James Edington Date: Mon, 7 Sep 2020 23:58:54 -0500 Subject: [PATCH] big refactor --- src/js/main.js | 147 ++++++++++++++++------------------------------ src/js/util.js | 71 +++++++++++++++++++++- src/manifest.json | 2 +- stuff/welcome.rst | 12 ++++ 4 files changed, 134 insertions(+), 98 deletions(-) create mode 100644 stuff/welcome.rst diff --git a/src/js/main.js b/src/js/main.js index 9322c21..edbb24d 100644 --- a/src/js/main.js +++ b/src/js/main.js @@ -1,39 +1,41 @@ function identifySecType(securityInfo){ try { - if(securityInfo.state!='secure') throw {status:'insecure',securityInfo:securityInfo}; - //TODO: complain to mozilla for better access - // to ad-hoc/TOFU/self-signed connections. - //Not only do they always show state='insecure', - // (which is ARGUABLY correct,) but we're - // even deprived so much as *access to* their - // cert chain, so we cannot evaluate them!! + //TODO/FIXME: Mozilla doesn't provide + //any access whatsoever to self-signed + //or otherwise nominally-invalid certs + // https://discourse.mozilla.org/t/webrequest-getsecurityinfo-cant-get-self-signed-tofu-exception-certificates/67135 + if(securityInfo.state!='secure'){ + //once the above is fixed, this if-block + //will have to be factored out + return secTypes.insecure; + } let certChain=securityInfo.certificates; - if(!certChain.length) throw {status:'emptyCertChainButItsSecure',securityInfo:securityInfo}; + if(!certChain.length) { + //TODO: find out whytf this happens sometimes + throw {status:'emptyCertChainButItsSecure',securityInfo:securityInfo}; + } + let rootCert=certChain[certChain.length-1]; + //Now, this connection is... if(rootCert.isBuiltInRoot){ //...Mozilla-supported return secTypes.Mozilla; } else if(!rootCert.isUntrusted) { - //...supported by a Non-Mozilla cert... - if(isItMITM(rootCert)){ //TODO - //...TLS MITM proxy - return secTypes.MITM; - } else { - //...alternative Root CA - return secTypes.aRoot; - } - } else { - //??? - throw {status:'thisShouldNeverHappen',securityInfo:securityInfo}; + //...supported by a Non-Mozilla cert... + if(isItMitM(rootCert)){ //TODO + //...TLS MITM proxy + return secTypes.MITM; + } else { + //...alternative Root CA + return secTypes.aRoot; + } } + throw {status:'thisShouldNeverHappen',securityInfo:securityInfo}; } catch(e) { + //TODO this bit of code could be cleaner, I think switch(e.status){ - case 'insecure': - return secTypes.insecure; - break; case 'emptyCertChainButItsSecure': - //TODO: find out whytf this happens sometimes console.warn(e.status||e,securityInfo); return secTypes.indeterminate; break; @@ -44,65 +46,16 @@ function identifySecType(securityInfo){ } } -function genBrowserActionSpec(secType,certChain){ - let rootHost,iconPath; - switch(secType){ - case secTypes.Mozilla: - rootHost=sha256fp_host[certChain[certChain.length-1].fingerprint.sha256]; - return { - Icon: {path:`images/root_icons/${rootHost}.ico`}, - Title: {title: `\uD83E\uDD8A ${rootHost}`}, - // BadgeText: {text: '\u2026'}; //TODO? - // BadgeBackgroundColor: {color: 'LimeGreen'}; - }; - break; - case secTypes.MITM: - return { - Icon: {path:`images/Twemoji_1f441.svg`}, - Title: {title: "MITM TLS Proxy\n(Your IT team can see what you're doing)"}, - BadgeText: {text: '\u2026'}, //TODO: ...something? - BadgeBackgroundColor: {color: 'Fuchsia'} - }; - break; - case secTypes.aRoot: - rootHost=sha256fp_host_alt[certChain[certChain.length-1].fingerprint.sha256]; - if(rootHost){ - iconPath=`images/alt_root_icons/${rootHost}.ico`; - } else { - iconPath='images/Twemoji_1f50f.svg'; - } - return { - Icon: {path:iconPath}, - Title: {title: rootHost}, - BadgeText: {text: '\u2026'}, //TODO: which aRoot? - BadgeBackgroundColor: {color: 'Cyan'} - }; - break; - case secTypes.indeterminate: - return {} //TODO??? - break; - default: - return { - Icon: {path:`images/Twemoji_2716.svg`}, - // BadgeText: {text: '\u2026'}; - // BadgeBackgroundColor: {color: 'Grey'}; - }; - } -} - -function updateTabBrowserAction(tabId,browserActionSpec){ - for(let prop in browserActionSpec){ - let cmd=new Object(); - Object.assign(cmd,browserActionSpec[prop]); - Object.assign(cmd,{tabId:tabId}); - browser.browserAction['set'+prop](cmd); - } -} - -function isItMITM(cert){ +function isItMitM(cert){ + //TODO check with the user about this if(cert.fingerprint.sha256 in sha256fp_host || cert.fingerprint.sha256 in sha256fp_host_alt){ + //The cert was in EITHER database + //therefore it is legitimate, + //i.e. NOT a MitM: return false; } else { + //The cert was in NEITHER database + //therefore it IS a MitM: return true; } } @@ -115,27 +68,29 @@ browser.webRequest.onHeadersReceived.addListener( //TODO: pester Mozilla about this async function onHeadersReceivedListener(details) { let tabId=details.tabId; + let type=details.type; let requestId=details.requestId; - let securityInfo = await browser.webRequest.getSecurityInfo(details.requestId,{certificateChain:true}); - if(details.frameId==0){ - return onReceivingMainFrame(tabId,securityInfo); - } else { - return onReceivingNonMainFrame(tabId,securityInfo); + let securityInfo = await browser.webRequest.getSecurityInfo(requestId,{certificateChain:true}); + let secType=identifySecType(securityInfo); + let certChain=securityInfo.certificates; + let browserActionSpec; + switch(type){ + case 'main_frame': + let browserActionSpec=genBrowserActionSpec(secType,certChain); + setTimeout(()=>{ + updateTabBrowserAction(tabId,browserActionSpec); + },250); //TODO TODO TODO TODO + //I know this is absolutely disgusting, but it + //yields the best UX for now + return; + break; + default: + //TODO + return; } }, { - urls:[''] + urls:[''], }, ['blocking'] //this has to be blocking, or getSecurityInfo doesn't work ); - -function onReceivingMainFrame(tabId,securityInfo){ - let secType=identifySecType(securityInfo); - let certChain=securityInfo.certificates; - let browserActionSpec=genBrowserActionSpec(secType,certChain); - updateTabBrowserAction(tabId,browserActionSpec); -} - -function onReceivingNonMainFrame(tabId,securityInfo){ - //TODO -} diff --git a/src/js/util.js b/src/js/util.js index 5d3ea47..657de6b 100644 --- a/src/js/util.js +++ b/src/js/util.js @@ -9,7 +9,8 @@ function getAsset(path){ const secTypes={ Mozilla: 0, MITM: 1, - aRoot: 2, + aRootKnown: 2, + aRootUnknown: 3, indeterminate: 254, insecure: 255 } @@ -55,3 +56,71 @@ const sha256fp_host_alt = {'07:ED:BD:82:4A:49:88:CF:EF:42:15:DA:20:D4:8C:2B:41:D }); Object.freeze(host_country); } + +function genBrowserActionSpec(secType,certChain){ + let rootHost,iconPath; + switch(secType){ + case secTypes.Mozilla: + rootHost=sha256fp_host[certChain[certChain.length-1].fingerprint.sha256]; + return { + Icon: {path: `images/root_icons/${rootHost}.ico`}, + Title: {title: rootHost}, + BadgeText: {text: '\uD83E\uDD8A'}, + BadgeBackgroundColor: {color: 'LimeGreen'} + }; + break; + case secTypes.MITM: + return { + Icon: {path: `images/Twemoji_1f441.svg`}, + Title: {title: "MitM TLS Proxy\n(Your network administrator is inspecting this connection.)"}, + BadgeText: {text: '\u2026'}, //TODO: ...something? + BadgeBackgroundColor: {color: 'Fuchsia'} + }; + break; + case secTypes.aRootKnown: + rootHost=sha256fp_host_alt[certChain[certChain.length-1].fingerprint.sha256]; + return { + Icon: {path: `images/alt_root_icons/${rootHost}.ico`}, + Title: {title: rootHost}, + BadgeText: {text: rootHost.slice(0,1).toUpperCase()}, //TODO + BadgeBackgroundColor: {color: 'Cyan'} + }; + break; + case secTypes.aRootUnknown: + return { + Icon: {path: 'images/Twemoji_1f50f.svg'}, + Title: {title: certChain[certChain.length-1].fingerprint.sha256}, + BadgeText: {text: '\u2026'}, //TODO + BadgeBackgroundColor: {color: 'Cyan'} + }; + break; + case secTypes.indeterminate: + return {} //TODO??? + break; + default: + return { + Icon: {path: `images/Twemoji_27a0.svg`}, + BadgeText: {text: '\u2026'}, + BadgeBackgroundColor: {color: 'Grey'} + }; + } +} + +function updateTabBrowserAction(tabId,browserActionSpec){ + for(let prop in browserActionSpec){ + let cmd=new Object(); + Object.assign(cmd,browserActionSpec[prop]); + Object.assign(cmd,{tabId:tabId}); + browser.browserAction['set'+prop](cmd); + } +} + +browser.runtime.onInstalled.addListener( + function onInstalledListener(details){ + console.log("onInstalledListener was run!"); + console.log(details); + if(details.reason=="install" || details.temporary){ + browser.tabs.create({url:'https://github.com/JamesTheAwesomeDude/cerdicator/blob/v0.0.7/stuff/welcome.rst'}); + } + } +); diff --git a/src/manifest.json b/src/manifest.json index 26f262d..33511eb 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "cerdicator", - "version": "0.0.6", + "version": "0.0.7", "description": "Enhanced TLS indicator with an emphasis on information about the Root Certificate Authority from which the connection's authenticity is derived.", diff --git a/stuff/welcome.rst b/stuff/welcome.rst new file mode 100644 index 0000000..b99abe6 --- /dev/null +++ b/stuff/welcome.rst @@ -0,0 +1,12 @@ +You've just installed or updated ``cerdicator`` to version 0.0.7! + +If you haven't done so already, take a moment to do the following: + +1. If you see a **gap** on the top-left of your browser, between the URL bar and the Home, Reload, and Forward/Back buttons, right-click it and hit ``Customize`` +2. Click and drag that spacer down from where it was, into the big tray of items below it +3. Click and ``cerdicator``\ 🧿 into its place +4. You're done! (This add-on will become more useful eventually...) + +If you're a coder and would like to lend a hand, please check out the `open Issues on GitHub `_! + +I look forward to completing this and adding some more features. Feel free to suggest things you want at the Issues link above.