Skip to content

Commit 9182334

Browse files
Dan AuerbachDan Auerbach
authored andcommitted
Adding basic whitelist functionality
1 parent 240e418 commit 9182334

File tree

3 files changed

+281
-17
lines changed

3 files changed

+281
-17
lines changed

src/chrome/content/code/X509ChainWhitelist.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// Decentralized SSL Observatory alpha release. These should not be
33
// resubmitted.
44

5-
X509ChainWhitelist = {
5+
const X509ChainWhitelist = {
66
'00206DD7A2117DA9DD865553A634AD1ABF1EC61D2D88EFBB3B06714A3F4AFECD' : true,
77
'0025A0FEB703F1C8B93369CB80F36AA5641AAD9521A175F76A31CBCB52794ADF' : true,
88
'007E4AB4C13E4D98379EAD2261CC13E12C906A84A56F4490386A9AEE7D5B91C7' : true,
@@ -1003,4 +1003,4 @@ X509ChainWhitelist = {
10031003
'FF36771C92659E54A33976E8B50337265209286A4A95C47A4690DD25DA109251' : true,
10041004
'FF80BF957C49004739893CDB70D5310FCF0E202DA3A350EFA25907B0048BCD3B' : true,
10051005
'FFC0FF875E27533E30FFFDE1C204D26DD7BBDC6366CF2403F9DD099865FB7F4D' : true,
1006-
} ;
1006+
} ;

src/chrome/content/code/sha256.js

Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
/*
2+
* A JavaScript implementation of the SHA256 hash function.
3+
*
4+
* FILE:sha256.js
5+
* VERSION:0.8
6+
* AUTHOR:Christoph Bichlmeier <informatik@zombiearena.de>
7+
*
8+
* NOTE: This version is not tested thoroughly!
9+
*
10+
* Copyright (c) 2003, Christoph Bichlmeier
11+
* All rights reserved.
12+
*
13+
* Redistribution and use in source and binary forms, with or without
14+
* modification, are permitted provided that the following conditions
15+
* are met:
16+
* 1. Redistributions of source code must retain the above copyright
17+
* notice, this list of conditions and the following disclaimer.
18+
* 2. Redistributions in binary form must reproduce the above copyright
19+
* notice, this list of conditions and the following disclaimer in the
20+
* documentation and/or other materials provided with the distribution.
21+
* 3. Neither the name of the copyright holder nor the names of contributors
22+
* may be used to endorse or promote products derived from this software
23+
* without specific prior written permission.
24+
*
25+
* ======================================================================
26+
*
27+
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS
28+
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
29+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30+
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
31+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
34+
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
35+
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
36+
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
37+
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38+
*/
39+
40+
/* SHA256 logical functions */
41+
function rotateRight(n,x) {
42+
return ((x >>> n) | (x << (32 - n)));
43+
}
44+
function choice(x,y,z) {
45+
return ((x & y) ^ (~x & z));
46+
}
47+
function majority(x,y,z) {
48+
return ((x & y) ^ (x & z) ^ (y & z));
49+
}
50+
function sha256_Sigma0(x) {
51+
return (rotateRight(2, x) ^ rotateRight(13, x) ^ rotateRight(22, x));
52+
}
53+
function sha256_Sigma1(x) {
54+
return (rotateRight(6, x) ^ rotateRight(11, x) ^ rotateRight(25, x));
55+
}
56+
function sha256_sigma0(x) {
57+
return (rotateRight(7, x) ^ rotateRight(18, x) ^ (x >>> 3));
58+
}
59+
function sha256_sigma1(x) {
60+
return (rotateRight(17, x) ^ rotateRight(19, x) ^ (x >>> 10));
61+
}
62+
function sha256_expand(W, j) {
63+
return (W[j&0x0f] += sha256_sigma1(W[(j+14)&0x0f]) + W[(j+9)&0x0f] +
64+
sha256_sigma0(W[(j+1)&0x0f]));
65+
}
66+
67+
/* Hash constant words K: */
68+
var K256 = new Array(
69+
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
70+
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
71+
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
72+
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
73+
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
74+
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
75+
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
76+
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
77+
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
78+
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
79+
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
80+
0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
81+
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
82+
0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
83+
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
84+
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
85+
);
86+
87+
/* global arrays */
88+
var ihash, count, buffer;
89+
var sha256_hex_digits = "0123456789abcdef";
90+
91+
/* Add 32-bit integers with 16-bit operations (bug in some JS-interpreters:
92+
overflow) */
93+
function safe_add(x, y)
94+
{
95+
var lsw = (x & 0xffff) + (y & 0xffff);
96+
var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
97+
return (msw << 16) | (lsw & 0xffff);
98+
}
99+
100+
/* Initialise the SHA256 computation */
101+
function sha256_init() {
102+
ihash = new Array(8);
103+
count = new Array(2);
104+
buffer = new Array(64);
105+
count[0] = count[1] = 0;
106+
ihash[0] = 0x6a09e667;
107+
ihash[1] = 0xbb67ae85;
108+
ihash[2] = 0x3c6ef372;
109+
ihash[3] = 0xa54ff53a;
110+
ihash[4] = 0x510e527f;
111+
ihash[5] = 0x9b05688c;
112+
ihash[6] = 0x1f83d9ab;
113+
ihash[7] = 0x5be0cd19;
114+
}
115+
116+
/* Transform a 512-bit message block */
117+
function sha256_transform() {
118+
var a, b, c, d, e, f, g, h, T1, T2;
119+
var W = new Array(16);
120+
121+
/* Initialize registers with the previous intermediate value */
122+
a = ihash[0];
123+
b = ihash[1];
124+
c = ihash[2];
125+
d = ihash[3];
126+
e = ihash[4];
127+
f = ihash[5];
128+
g = ihash[6];
129+
h = ihash[7];
130+
131+
/* make 32-bit words */
132+
for(var i=0; i<16; i++)
133+
W[i] = ((buffer[(i<<2)+3]) | (buffer[(i<<2)+2] << 8) | (buffer[(i<<2)+1]
134+
<< 16) | (buffer[i<<2] << 24));
135+
136+
for(var j=0; j<64; j++) {
137+
T1 = h + sha256_Sigma1(e) + choice(e, f, g) + K256[j];
138+
if(j < 16) T1 += W[j];
139+
else T1 += sha256_expand(W, j);
140+
T2 = sha256_Sigma0(a) + majority(a, b, c);
141+
h = g;
142+
g = f;
143+
f = e;
144+
e = safe_add(d, T1);
145+
d = c;
146+
c = b;
147+
b = a;
148+
a = safe_add(T1, T2);
149+
}
150+
151+
/* Compute the current intermediate hash value */
152+
ihash[0] += a;
153+
ihash[1] += b;
154+
ihash[2] += c;
155+
ihash[3] += d;
156+
ihash[4] += e;
157+
ihash[5] += f;
158+
ihash[6] += g;
159+
ihash[7] += h;
160+
}
161+
162+
/* Read the next chunk of data and update the SHA256 computation */
163+
function sha256_update(data, inputLen) {
164+
var i, index, curpos = 0;
165+
/* Compute number of bytes mod 64 */
166+
index = ((count[0] >> 3) & 0x3f);
167+
var remainder = (inputLen & 0x3f);
168+
169+
/* Update number of bits */
170+
if ((count[0] += (inputLen << 3)) < (inputLen << 3)) count[1]++;
171+
count[1] += (inputLen >> 29);
172+
173+
/* Transform as many times as possible */
174+
for(i=0; i+63<inputLen; i+=64) {
175+
for(var j=index; j<64; j++)
176+
buffer[j] = data.charCodeAt(curpos++);
177+
sha256_transform();
178+
index = 0;
179+
}
180+
181+
/* Buffer remaining input */
182+
for(var j=0; j<remainder; j++)
183+
buffer[j] = data.charCodeAt(curpos++);
184+
}
185+
186+
/* Finish the computation by operations such as padding */
187+
function sha256_final() {
188+
var index = ((count[0] >> 3) & 0x3f);
189+
buffer[index++] = 0x80;
190+
if(index <= 56) {
191+
for(var i=index; i<56; i++)
192+
buffer[i] = 0;
193+
} else {
194+
for(var i=index; i<64; i++)
195+
buffer[i] = 0;
196+
sha256_transform();
197+
for(var i=0; i<56; i++)
198+
buffer[i] = 0;
199+
}
200+
buffer[56] = (count[1] >>> 24) & 0xff;
201+
buffer[57] = (count[1] >>> 16) & 0xff;
202+
buffer[58] = (count[1] >>> 8) & 0xff;
203+
buffer[59] = count[1] & 0xff;
204+
buffer[60] = (count[0] >>> 24) & 0xff;
205+
buffer[61] = (count[0] >>> 16) & 0xff;
206+
buffer[62] = (count[0] >>> 8) & 0xff;
207+
buffer[63] = count[0] & 0xff;
208+
sha256_transform();
209+
}
210+
211+
/* Split the internal hash values into an array of bytes */
212+
function sha256_encode_bytes() {
213+
var j=0;
214+
var output = new Array(32);
215+
for(var i=0; i<8; i++) {
216+
output[j++] = ((ihash[i] >>> 24) & 0xff);
217+
output[j++] = ((ihash[i] >>> 16) & 0xff);
218+
output[j++] = ((ihash[i] >>> 8) & 0xff);
219+
output[j++] = (ihash[i] & 0xff);
220+
}
221+
return output;
222+
}
223+
224+
/* Get the internal hash as a hex string */
225+
function sha256_encode_hex() {
226+
var output = new String();
227+
for(var i=0; i<8; i++) {
228+
for(var j=28; j>=0; j-=4)
229+
output += sha256_hex_digits.charAt((ihash[i] >>> j) & 0x0f);
230+
}
231+
return output;
232+
}
233+
234+
/* Main function: returns a hex string representing the SHA256 value of the
235+
given data */
236+
function sha256_digest(data) {
237+
sha256_init();
238+
sha256_update(data, data.length);
239+
sha256_final();
240+
return sha256_encode_hex();
241+
}
242+
243+
/* test if the JS-interpreter is working properly */
244+
function sha256_self_test()
245+
{
246+
return sha256_digest("message digest") ==
247+
"f7846f55cf23e14eebeab5b4e1550cad5b509e3348fbc4efa3a1413d393cb650";
248+
}
249+

src/components/ssl-observatory.js

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ const INCLUDE = function(name) {
4444
}
4545

4646
INCLUDE('Root-CAs');
47+
INCLUDE('sha256');
48+
INCLUDE('X509ChainWhitelist');
4749

4850
function SSLObservatory() {
4951
this.prefs = CC["@mozilla.org/preferences-service;1"]
@@ -64,9 +66,6 @@ function SSLObservatory() {
6466
this.already_submitted = {};
6567
OS.addObserver(this, "cookie-changed", false);
6668

67-
// XXX: Read these from a file? Or hardcode them?
68-
this.popular_fps = {};
69-
7069
// The url to submit to
7170
var host=this.prefs.getCharPref("extensions.https_everywhere._observatory.server_host");
7271
this.submit_url = "https://" + host + "/submit_cert";
@@ -212,7 +211,6 @@ SSLObservatory.prototype = {
212211
this.getClientASN();
213212
},
214213

215-
216214
observe: function(subject, topic, data) {
217215
if (topic == "cookie-changed" && data == "cleared") {
218216
this.already_submitted = {};
@@ -247,15 +245,29 @@ SSLObservatory.prototype = {
247245
if (certchain) {
248246
var chainEnum = certchain.getChain();
249247
var chainArray = [];
248+
var chainArrayFpStr = '';
249+
var fps = [];
250250
for(var i = 0; i < chainEnum.length; i++) {
251251
var cert = chainEnum.queryElementAt(i, Ci.nsIX509Cert);
252252
chainArray.push(cert);
253+
var fp = (cert.md5Fingerprint+cert.sha1Fingerprint).replace(":", "", "g");
254+
fps.push(fp);
255+
chainArrayFpStr = chainArrayFpStr + fp;
253256
}
257+
var chain_hash = sha256_digest(chainArrayFpStr).toUpperCase();
258+
this.log(INFO, "SHA-256 hash of cert chain for "+new String(subject.URI.host)+" is "+ chain_hash);
259+
260+
if (this.isChainWhitelisted(chain_hash)) {
261+
this.log(INFO, "This cert chain is whitelisted. Not submitting.");
262+
return;
263+
}
264+
this.log(INFO, "Cert chain is NOT whitelisted. Proceeding with submission.");
265+
254266

255267
if (subject.URI.port == -1) {
256-
this.submitChain(chainArray, new String(subject.URI.host), subject);
268+
this.submitChain(chainArray, fps, new String(subject.URI.host), subject);
257269
} else {
258-
this.submitChain(chainArray, subject.URI.host+":"+subject.URI.port, subject);
270+
this.submitChain(chainArray, fps, subject.URI.host+":"+subject.URI.port, subject);
259271
}
260272
}
261273
}
@@ -293,14 +305,22 @@ SSLObservatory.prototype = {
293305
return this.prefs.getBoolPref ("extensions.https_everywhere._observatory." + prefstring);
294306
},
295307

296-
submitChain: function(certArray, domain, channel) {
308+
isChainWhitelisted: function(chainhash) {
309+
if (X509ChainWhitelist == null) {
310+
this.log(WARN, "Could not find whitelist of popular certificate chains, so ignoring whitelist");
311+
return false;
312+
}
313+
if (X509ChainWhitelist[chainhash] != null) {
314+
return true;
315+
}
316+
return false;
317+
},
318+
319+
submitChain: function(certArray, fps, domain, channel) {
297320
var base64Certs = [];
298-
var fps = [];
299321
var rootidx = -1;
300322

301323
for (var i = 0; i < certArray.length; i++) {
302-
var fp = (certArray[i].md5Fingerprint+certArray[i].sha1Fingerprint).replace(":", "", "g");
303-
fps.push(fp);
304324
if (certArray[i].issuer && certArray[i].equals(certArray[i].issuer)) {
305325
this.log(INFO, "Got root cert at position: "+i);
306326
rootidx = i;
@@ -322,11 +342,6 @@ SSLObservatory.prototype = {
322342
return;
323343
}
324344

325-
if (fps[0] in this.popular_fps) {
326-
this.log(INFO, "Excluding popuar cert for "+domain);
327-
return;
328-
}
329-
330345
var wm = CC["@mozilla.org/appshell/window-mediator;1"]
331346
.getService(Components.interfaces.nsIWindowMediator);
332347
var browserWindow = wm.getMostRecentWindow("navigator:browser");

0 commit comments

Comments
 (0)