summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--build.sh5
-rw-r--r--tails-download-and-verify/conf.json19
-rw-r--r--tails-download-and-verify/data/download-page.js38
-rw-r--r--tails-download-and-verify/index.js3
-rw-r--r--tails-download-and-verify/lib/cert-pinner.js46
-rw-r--r--tails-download-and-verify/lib/downloader.js10
-rw-r--r--tails-download-and-verify/package.json2
-rw-r--r--www/dave.xpibin19043 -> 23179 bytes
-rw-r--r--www/download.html2
9 files changed, 85 insertions, 40 deletions
diff --git a/build.sh b/build.sh
index 13ab33e..231b711 100644
--- a/build.sh
+++ b/build.sh
@@ -2,6 +2,7 @@
base=$(dirname "$0")
dir="$base/tails-download-and-verify"
mirror_dispatcher_url='https://git-tails.immerda.ch/mirror-pool-dispatcher/plain/lib/js/mirror-dispatcher.js'
+mirror_dispatcher_file="lib/mirror-dispatcher.js"
pushd "$dir" >/dev/null 2>&1 || (
echo >&2 "FATAL: Channot chdir to $dir."
exit 1
@@ -10,10 +11,10 @@ echo -n "Removing old XPIs... "
rm -f *.xpi >/dev/null && echo "done."
ver=$(egrep '"version": "[0-9]+\.[^"]+"' package.json | sed -re 's/.*"([0-9]+\.[^"]+)".*/\1/')
echo "Importing mirror-dispatcher.js library"
-curl \
+[ -f "$mirror_dispatcher_file" ] || curl \
--proto -all,https \
--tlsv1 \
- --output "lib/mirror-dispatcher.js" \
+ --output "$mirror_dispatcher_file" \
"$mirror_dispatcher_url"
echo "Building extension version $ver"
jpm xpi || exit 2
diff --git a/tails-download-and-verify/conf.json b/tails-download-and-verify/conf.json
index 59afc60..35e89d5 100644
--- a/tails-download-and-verify/conf.json
+++ b/tails-download-and-verify/conf.json
@@ -6,8 +6,11 @@
"pins": {
"domains": {
"tails.boum.org": {
- "cert": null,
- "issuer": "Gandi"
+ "certs": null,
+ "issuers": ["Let's Encrypt", "Gandi"]
+ },
+ "labs.riseup.net": {
+ "issuers": ["Let's Encrypt", "Gandi"]
}
},
"certs": {
@@ -16,14 +19,20 @@
"issuerOrganization":"Gandi",
"sha256Fingerprint":"FB:89:1F:85:61:8D:6F:62:EA:A6:6E:92:4D:3A:FC:80:17:03:D6:FB:D5:F4:B0:31:E7:D7:5A:7F:55:06:74:2D",
"serialNumber":"00:84:A7:E7:40:C4:D4:54:54:64:E4:35:22:38:F0:29:53"
- }
- },
- "issuers": {
+ },
"Gandi": {
"subjectName":"CN=Gandi Standard SSL CA 2,O=Gandi,L=Paris,ST=Paris,C=FR",
"issuerOrganization":"The USERTRUST Network",
"sha256Fingerprint":"B9:F2:16:43:23:63:8D:CE:0B:92:21:8B:43:C4:1C:1B:2B:26:96:38:93:29:DB:19:F5:CF:7A:D4:9B:5C:B3:72",
"serialNumber":"05:E4:DC:3B:94:38:AB:3B:85:97:CB:A6:A1:98:50:E3"
+ },
+ "Let's Encrypt": {
+ "subjectName":"CN=Let's Encrypt Authority X3,O=Let's Encrypt,C=US",
+ "commonName":"Let's Encrypt Authority X3",
+ "issuerOrganization":"Digital Signature Trust Co.",
+ "organization":"Let's Encrypt",
+ "sha256Fingerprint":"25:84:7D:66:8E:B4:F0:4F:DD:40:B1:2B:6B:07:40:C5:67:DA:7D:02:43:08:EB:6C:2C:96:FE:41:D9:DE:21:8D",
+ "serialNumber":"0A:01:41:42:00:00:01:53:85:73:6A:0B:85:EC:A7:08"
}
}
}
diff --git a/tails-download-and-verify/data/download-page.js b/tails-download-and-verify/data/download-page.js
index 0c181f6..5a6f434 100644
--- a/tails-download-and-verify/data/download-page.js
+++ b/tails-download-and-verify/data/download-page.js
@@ -193,7 +193,6 @@ initWidgets = (status) => {
}
}
on("a.iso-url", "click", e => {
- e.preventDefault();
if (/-retry$/.test(e.target.id)) return;
Download.cmd('start', { prompt: e.target.title });
});
@@ -201,30 +200,24 @@ initWidgets = (status) => {
for (let action of ['pause', 'resume', 'cancel']) {
let cmd = action;
on(`#download-button-state-${cmd}`, "click", e => {
- e.preventDefault();
- Download.cmd(cmd);
+ if (/\bdownload-again\b/.test(e.target.className)) {
+ Download.reset();
+ } else {
+ Download.cmd(cmd);
+ }
});
}
+ on(".download-again", "click", e => Download.reset());
+ on("#download-button-state-retry", "click", e => Download.retry());
- on("#download-button-state-retry", "click", e => {
- e.preventDefault();
- Download.cmd('retry');
- });
-
- on("#i_have_iso", "click", e => {
- e.preventDefault();
- Download.cmd('verify', { prompt: e.target.title });
- });
+ on("#i_have_iso", "click", e => Download.cmd('verify', { prompt: e.target.title }));
- on("#verify-text-success .btn", "click", e => {
- // if (verifySuccess) Download.cmd('reveal', { path: verifySuccess });
- e.preventDefault();
- Download.cmd('retry');
- });
+ on("#verify-text-success .btn", "click", e => Download.cmd('retry'));
initWidgets = null;
},
Download = {
+ resetting: false,
start() {
toggle("#download-eta, #download-path", false);
@@ -232,6 +225,12 @@ Download = {
document.documentElement.dataset.phase = "started";
this.cmd('init');
},
+
+ reset() {
+ this.resetting = true;
+ this.cmd("reset");
+ },
+
cmd(cmd, data) {
console.log(`Sending command Download.${cmd}`);
self.port.emit("download-control", Object.assign({ cmd, page: document.URL, domain: document.domain }, data));
@@ -267,6 +266,11 @@ Download = {
case 'verified':
updateVerifyView(status);
break;
+ case 'init':
+ if (Download.resetting) {
+ reload();
+ }
+ break;
}
document.documentElement.dataset.phase = status.phase;
diff --git a/tails-download-and-verify/index.js b/tails-download-and-verify/index.js
index 771ee93..4c4861a 100644
--- a/tails-download-and-verify/index.js
+++ b/tails-download-and-verify/index.js
@@ -3,7 +3,7 @@ var self = require('sdk/self');
var downloader = null;
var workers = new Set();
-var Config = require("../conf.json");
+var Config = require("conf.json");
if (Config.pinGlobally) {
require("./lib/cert-pinner").pinGlobally(Config.pins);
@@ -30,6 +30,7 @@ function onUI(msg) {
case "cancel":
case "resume":
case "retry":
+ case "reset":
downloader[msg.cmd].call(downloader, msg.page);
break;
case "verify":
diff --git a/tails-download-and-verify/lib/cert-pinner.js b/tails-download-and-verify/lib/cert-pinner.js
index beae8ba..06c7ad2 100644
--- a/tails-download-and-verify/lib/cert-pinner.js
+++ b/tails-download-and-verify/lib/cert-pinner.js
@@ -5,14 +5,25 @@ Cu.import("resource://gre/modules/Services.jsm");
const NS_BINDING_ABORTED = 0x804b0002;
const DOCUMENT_LOAD_FLAGS = Ci.nsIChannel.LOAD_DOCUMENT_URI | Ci.nsIChannel.LOAD_CALL_CONTENT_SNIFFERS;
-function checkCertificate(cert, known) {
- if (!cert) throw new Error(`No certification bound to channel (pin: "${known.subjectName}")`);
- if (!known) throw new Error(`No certification pinning data for ${cert.subjectName}!`);
- for (let p of Object.keys(known)) {
- if (known[p] !== cert[p]) {
- throw new Error(`Certificate mismatch for ${p}@${cert.subjectName}: "${known[p]}" != "${cert[p]}"!`);
+function checkCertificate(cert, knownCerts) {
+ if (!cert) throw new Error(`No certificate bound to channel (pin: "${known.subjectName}")`);
+ if (!(Array.isArray(knownCerts) && knownCerts.length)) throw new Error(`No certification pinning data for ${cert.subjectName}!`);
+ let mismatches = [];
+ for (let known of knownCerts) {
+ let match = true;
+ for (let p of Object.keys(known)) {
+ if (known[p] !== cert[p]) {
+ mismatches.push(`${p}@${known.subjectName} ("${known[p]}" != "${cert[p]}")`);
+ match = false;
+ break;
+ }
+ }
+ if (match) {
+ console.log(`Certificate ${cert.subjectName} matches :)`);
+ return;
}
}
+ throw new Error(`Certificate mismatch: ${cert.subjectName} does not match ${mismatches.join(", ")}!`);
}
function checkChannel(channel, pins, allowUnpinned = false) {
@@ -24,7 +35,7 @@ function checkChannel(channel, pins, allowUnpinned = false) {
throw new Error(`No configured pin for ${host}!`);
}
let pin = pins.domains[host];
- if (!(pin.cert || pin.issuer)) return;
+ if (!(pin.certs || pin.issuers)) return;
let securityInfo = channel.securityInfo;
if (!(securityInfo instanceof Ci.nsITransportSecurityInfo)) {
throw new Error(`No certificate for ${host}!`);
@@ -33,10 +44,23 @@ function checkChannel(channel, pins, allowUnpinned = false) {
.QueryInterface(Ci.nsISSLStatusProvider).SSLStatus.serverCert;
console.log(`Checking pins for ${host}...`);
- if (pin.cert) checkCertificate(cert, pins.certs[pin.cert]);
- if (pin.issuer) {
- if (!cert.issuer) throw new Error(`No issuer for certificate "${cert.subjectName}"@${channel.name}!`);
- checkCertificate(cert.issuer, pins.issuers[pin.issuer]);
+
+ let certs = names => names.map(name => pins.certs[name]);
+
+ if (pin.certs) {
+ checkCertificate(cert, certs(pin.certs));
+ }
+ if (pin.issuers) {
+ let errors = [];
+ for (let parent = cert.issuer; parent; parent = parent.issuer) {
+ try {
+ checkCertificate(parent, certs(pin.issuers));
+ return;
+ } catch (e) {
+ errors.push(e.message);
+ }
+ }
+ throw new Error(errors.length ? errors.join("\n") : `No issuer for certificate "${cert.subjectName}"@${channel.name}!`);
}
}
diff --git a/tails-download-and-verify/lib/downloader.js b/tails-download-and-verify/lib/downloader.js
index 1eaf056..35f0bfd 100644
--- a/tails-download-and-verify/lib/downloader.js
+++ b/tails-download-and-verify/lib/downloader.js
@@ -258,8 +258,8 @@ var Verify = {
status.phase = 'verifying';
status.failed = false;
},
- get working() status.phase === 'verifying',
- get done() status.phase === 'verified',
+ get working() { return status.phase === 'verifying' },
+ get done() { return status.phase === 'verified' },
onProgressUnclamped(read, total) {
if (!Verify.working) return;
@@ -524,6 +524,12 @@ var api = {
downloadList.removeView(downloadView);
downloadList = downloadView = null;
}
+ },
+
+ reset() {
+ this.cancel();
+ reset();
+ notify();
}
};
diff --git a/tails-download-and-verify/package.json b/tails-download-and-verify/package.json
index 7d853f7..c386f3d 100644
--- a/tails-download-and-verify/package.json
+++ b/tails-download-and-verify/package.json
@@ -3,7 +3,7 @@
"id": "dave@tails.boum.org",
"name": "tails-download-and-verify",
"homepage": "https://tails.boum.org",
- "version": "0.2.7rc60",
+ "version": "0.2.8rc2",
"description": "A browser extension to download and verify Tails ISO images",
"main": "index.js",
"permissions": {
diff --git a/www/dave.xpi b/www/dave.xpi
index bcd34fe..514a043 100644
--- a/www/dave.xpi
+++ b/www/dave.xpi
Binary files differ
diff --git a/www/download.html b/www/download.html
index 73fc652..3695d06 100644
--- a/www/download.html
+++ b/www/download.html
@@ -250,7 +250,7 @@ For your security, it is very important to also verify your download. We propose
two techniques to do this verification automatically.</p>
<div id="download-and-verify" class="chrome-unsupported">
- <div id="extension-version">0.2.7rc60</div>
+ <div id="extension-version">0.2.8rc2</div>
<div id="undetected-browser">
<p>We failed to detect your browser vendor, maybe because JavaScript is disabled.</p>