1 From 6147cea4de151dade922b3c2787016f70c222458 Mon Sep 17 00:00:00 2001
2 From: Mike Perry <mikeperry-git@torproject.org>
3 Date: Tue, 24 Apr 2012 17:21:45 -0700
4 Subject: [PATCH 12/16] Randomize HTTP request order and pipeline depth.
6 This is an experimental defense against
7 http://lorre.uni.lu/~andriy/papers/acmccs-wpes11-fingerprinting.pdf
10 https://blog.torproject.org/blog/experimental-defense-website-traffic-fingerprinting
12 This defense has been improved since that blog post to additionally randomize
13 the order and concurrency of non-pipelined HTTP requests.
15 netwerk/protocol/http/nsHttpConnectionMgr.cpp | 133 ++++++++++++++++++++++++-
16 netwerk/protocol/http/nsHttpConnectionMgr.h | 5 +
17 2 files changed, 133 insertions(+), 5 deletions(-)
19 diff --git a/netwerk/protocol/http/nsHttpConnectionMgr.cpp b/netwerk/protocol/http/nsHttpConnectionMgr.cpp
20 index 6e1099d..3eec5b3 100644
21 --- a/netwerk/protocol/http/nsHttpConnectionMgr.cpp
22 +++ b/netwerk/protocol/http/nsHttpConnectionMgr.cpp
23 @@ -100,6 +100,12 @@ nsHttpConnectionMgr::nsHttpConnectionMgr()
25 mAlternateProtocolHash.Init(16);
26 mSpdyPreferredHash.Init();
29 + mRandomGenerator = do_GetService("@mozilla.org/security/random-generator;1", &rv);
30 + if (NS_FAILED(rv)) {
31 + mRandomGenerator = nsnull;
35 nsHttpConnectionMgr::~nsHttpConnectionMgr()
36 @@ -353,8 +359,12 @@ nsHttpConnectionMgr::AddTransactionToPipeline(nsHttpPipeline *pipeline)
37 nsConnectionEntry *ent = mCT.Get(ci->HashKey());
39 // search for another request to pipeline...
40 - PRInt32 i, count = ent->mPendingQ.Length();
41 - for (i=0; i<count; ++i) {
42 + PRInt32 i, h, count = ent->mPendingQ.Length();
43 + PRInt32* ind = new PRInt32[count];
44 + ShuffleRequestOrder((PRUint32*)ind, (PRUint32)count);
46 + for (h=0; h<count; ++h) {
47 + i = ind[h]; // random request sequence
48 nsHttpTransaction *trans = ent->mPendingQ[i];
49 if (trans->Caps() & NS_HTTP_ALLOW_PIPELINING) {
50 pipeline->AddTransaction(trans);
60 @@ -898,12 +908,17 @@ nsHttpConnectionMgr::ProcessPendingQForEntry(nsConnectionEntry *ent)
62 ProcessSpdyPendingQ(ent);
64 - PRUint32 i, count = ent->mPendingQ.Length();
65 + PRUint32 h, i = 0, count = ent->mPendingQ.Length();
67 LOG((" pending-count=%u\n", count));
68 nsHttpTransaction *trans = nsnull;
69 nsHttpConnection *conn = nsnull;
70 - for (i = 0; i < count; ++i) {
72 + PRUint32* ind = new PRUint32[count];
73 + ShuffleRequestOrder(ind, count);
75 + for (h=0; h<count; ++h) {
76 + i = ind[h]; // random request sequence
77 trans = ent->mPendingQ[i];
79 // When this transaction has already established a half-open
81 "something mutated pending queue from "
86 LOG((" dispatching pending transaction...\n"));
88 @@ -1011,6 +1026,19 @@ nsHttpConnectionMgr::AtActiveConnectionLimit(nsConnectionEntry *ent, PRUint8 cap
89 maxPersistConns = mMaxPersistConnsPerHost;
92 + // Fuzz maxConns for website fingerprinting attack
93 + // We create a range of maxConns/5 up to 6*maxConns/5
94 + // because this function is called repeatedly, and we'll
95 + // end up converging on a the high side of concurrent connections
96 + // after a short while.
97 + PRUint8 *bytes = nsnull;
98 + nsresult rv = mRandomGenerator->GenerateRandomBytes(1, &bytes);
99 + NS_ENSURE_SUCCESS(rv, rv);
101 + bytes[0] = bytes[0] % (maxConns + 1);
102 + maxConns = (maxConns/5) + bytes[0];
105 // use >= just to be safe
106 return (totalCount >= maxConns) || ( (caps & NS_HTTP_ALLOW_KEEPALIVE) &&
107 (persistCount >= maxPersistConns) );
108 @@ -1227,7 +1255,7 @@ nsHttpConnectionMgr::DispatchTransaction(nsConnectionEntry *ent,
110 if (conn->SupportsPipelining() && (caps & NS_HTTP_ALLOW_PIPELINING)) {
111 LOG((" looking to build pipeline...\n"));
112 - if (BuildPipeline(ent, trans, &pipeline))
113 + if (BuildRandomizedPipeline(ent, trans, &pipeline))
117 @@ -1300,6 +1328,101 @@ nsHttpConnectionMgr::BuildPipeline(nsConnectionEntry *ent,
122 +// Generate a shuffled request ordering sequence
124 +nsHttpConnectionMgr::ShuffleRequestOrder(PRUint32 *ind, PRUint32 count)
129 + for (i=0; i<count; ++i) {
132 + nsresult rv = mRandomGenerator->GenerateRandomBytes(sizeof(PRUint32)*count,
133 + (PRUint8**)&rints);
135 + return; // Leave unshuffled if error
137 + for (i=0; i < count; ++i) {
138 + PRInt32 temp = ind[i];
139 + ind[i] = ind[rints[i]%count];
140 + ind[rints[i]%count] = temp;
146 +nsHttpConnectionMgr::BuildRandomizedPipeline(nsConnectionEntry *ent,
147 + nsAHttpTransaction *firstTrans,
148 + nsHttpPipeline **result)
150 + if (mRandomGenerator == nsnull)
151 + return BuildPipeline(ent, firstTrans, result);
152 + if (mMaxPipelinedRequests < 2)
156 + PRUint8 *bytes = nsnull;
158 + nsHttpPipeline *pipeline = nsnull;
159 + nsHttpTransaction *trans;
161 + PRUint32 i = 0, numAdded = 0, numAllowed = 0;
164 + while (i < ent->mPendingQ.Length()) {
165 + if (ent->mPendingQ[i]->Caps() & NS_HTTP_ALLOW_PIPELINING)
170 + rv = mRandomGenerator->GenerateRandomBytes(1, &bytes);
171 + NS_ENSURE_SUCCESS(rv, rv);
173 + max = 4 + (bytes[0] % (mMaxPipelinedRequests + 1));
176 + while (numAllowed > 0) {
177 + rv = mRandomGenerator->GenerateRandomBytes(1, &bytes);
178 + NS_ENSURE_SUCCESS(rv, rv);
179 + i = bytes[0] % ent->mPendingQ.Length();
182 + trans = ent->mPendingQ[i];
184 + if (!(ent->mPendingQ[i]->Caps() & NS_HTTP_ALLOW_PIPELINING))
187 + if (numAdded == 0) {
188 + pipeline = new nsHttpPipeline;
191 + pipeline->AddTransaction(firstTrans);
194 + pipeline->AddTransaction(trans);
196 + // remove transaction from pending queue
197 + ent->mPendingQ.RemoveElementAt(i);
202 + if (++numAdded == max)
206 + //fprintf(stderr, "Yay!!! pipelined %u/%u transactions\n", numAdded, max);
207 + LOG((" pipelined %u/%u transactions\n", numAdded, max));
212 + NS_ADDREF(*result = pipeline);
217 nsHttpConnectionMgr::ProcessNewTransaction(nsHttpTransaction *trans)
219 diff --git a/netwerk/protocol/http/nsHttpConnectionMgr.h b/netwerk/protocol/http/nsHttpConnectionMgr.h
220 index a13da0f..59ee9b9 100644
221 --- a/netwerk/protocol/http/nsHttpConnectionMgr.h
222 +++ b/netwerk/protocol/http/nsHttpConnectionMgr.h
224 #include "nsIObserver.h"
225 #include "nsITimer.h"
226 #include "nsIX509Cert3.h"
227 +#include "nsIRandomGenerator.h"
229 class nsHttpPipeline;
231 @@ -317,6 +318,8 @@ private:
232 nsresult DispatchTransaction(nsConnectionEntry *, nsHttpTransaction *,
233 PRUint8 caps, nsHttpConnection *);
234 bool BuildPipeline(nsConnectionEntry *, nsAHttpTransaction *, nsHttpPipeline **);
235 + bool BuildRandomizedPipeline(nsConnectionEntry *, nsAHttpTransaction *, nsHttpPipeline **);
236 + void ShuffleRequestOrder(PRUint32 *, PRUint32);
237 nsresult ProcessNewTransaction(nsHttpTransaction *);
238 nsresult EnsureSocketThreadTargetIfOnline();
239 void ClosePersistentConnections(nsConnectionEntry *ent);
240 @@ -409,6 +412,8 @@ private:
241 PRUint64 mTimeOfNextWakeUp;
242 // Timer for next pruning of dead connections.
243 nsCOMPtr<nsITimer> mTimer;
244 + // Random number generator for reordering HTTP pipeline
245 + nsCOMPtr<nsIRandomGenerator> mRandomGenerator;
248 // the connection table