summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--tails-download-and-verify/conf.json19
-rw-r--r--tails-download-and-verify/lib/cert-pinner.js46
-rw-r--r--tails-download-and-verify/package.json2
-rw-r--r--www/dave.xpibin22776 -> 23179 bytes
-rw-r--r--www/download.html2
5 files changed, 51 insertions, 18 deletions
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/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/package.json b/tails-download-and-verify/package.json
index 28dce46..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.8rc1",
+ "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 753a80e..514a043 100644
--- a/www/dave.xpi
+++ b/www/dave.xpi
Binary files differ
diff --git a/www/download.html b/www/download.html
index 99a93f7..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.8rc1</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>