/src/mozilla-central/toolkit/components/resistfingerprinting/tests/test_reduceprecision.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- |
2 | | * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ : |
3 | | * This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include <math.h> |
8 | | |
9 | | #include "gtest/gtest.h" |
10 | | #include "nsIPrefService.h" |
11 | | #include "nsIPrefBranch.h" |
12 | | #include "nsRFPService.h" |
13 | | |
14 | | using namespace mozilla; |
15 | | |
16 | | /* |
17 | | Hello! Are you looking at this file because you got an error you don't understand? |
18 | | Perhaps something that looks like the following? |
19 | | |
20 | | toolkit/components/resistfingerprinting/tests/test_reduceprecision.cpp:15: Failure |
21 | | Expected: reduced1 |
22 | | Which is: 2064.83 |
23 | | To be equal to: reduced2 |
24 | | Which is: 2064.83 |
25 | | |
26 | | "Gosh," you might say, "They sure look equal to me. What the heck is going on here?" |
27 | | |
28 | | The answer lies beyond what you can see, in that which you cannot see. One must |
29 | | journey into the depths, the hidden, that which the world fights its hardest to |
30 | | conceal from us. |
31 | | |
32 | | Specially: you need to look at more decimal places. Run the test with: |
33 | | MOZ_LOG="nsResistFingerprinting:5" |
34 | | |
35 | | And look for two successive lines similar to the below (the format will certainly |
36 | | be different by the time you read this comment): |
37 | | V/nsResistFingerprinting Given: 2064.83384599999999, Reciprocal Rounding with 50000.00000000000000, Intermediate: 103241692.00000000000000, Got: 2064.83383999999978 |
38 | | V/nsResistFingerprinting Given: 2064.83383999999978, Reciprocal Rounding with 50000.00000000000000, Intermediate: 103241691.00000000000000, Got: 2064.83381999999983 |
39 | | |
40 | | Look at the last two values: |
41 | | Got: 2064.83383999999978 |
42 | | Got: 2064.83381999999983 |
43 | | |
44 | | They're supposed to be equal. They're not. But they both round to 2064.83. |
45 | | */ |
46 | | |
47 | 0 | bool setupJitter(bool enabled) { |
48 | 0 | nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID); |
49 | 0 |
|
50 | 0 | bool jitterEnabled = false; |
51 | 0 | if (prefs) { |
52 | 0 | prefs->GetBoolPref("privacy.resistFingerprinting.reduceTimerPrecision.jitter", &jitterEnabled); |
53 | 0 | prefs->SetBoolPref("privacy.resistFingerprinting.reduceTimerPrecision.jitter", enabled); |
54 | 0 | } |
55 | 0 |
|
56 | 0 | return jitterEnabled; |
57 | 0 | } |
58 | | |
59 | 0 | void cleanupJitter(bool jitterWasEnabled) { |
60 | 0 | nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID); |
61 | 0 | if (prefs) { |
62 | 0 | prefs->SetBoolPref("privacy.resistFingerprinting.reduceTimerPrecision.jitter", jitterWasEnabled); |
63 | 0 | } |
64 | 0 | } |
65 | | |
66 | 0 | void process(double clock, nsRFPService::TimeScale clockUnits, double precision) { |
67 | 0 | double reduced1 = nsRFPService::ReduceTimePrecisionImpl(clock, clockUnits, precision, -1, TimerPrecisionType::All); |
68 | 0 | double reduced2 = nsRFPService::ReduceTimePrecisionImpl(reduced1, clockUnits, precision, -1, TimerPrecisionType::All); |
69 | 0 | ASSERT_EQ(reduced1, reduced2); |
70 | 0 | } |
71 | | |
72 | 0 | TEST(ResistFingerprinting, ReducePrecision_Assumptions) { |
73 | 0 | ASSERT_EQ(FLT_RADIX, 2); |
74 | 0 | ASSERT_EQ(DBL_MANT_DIG, 53); |
75 | 0 | } |
76 | | |
77 | 0 | TEST(ResistFingerprinting, ReducePrecision_Reciprocal) { |
78 | 0 | bool jitterEnabled = setupJitter(false); |
79 | 0 | // This one has a rounding error in the Reciprocal case: |
80 | 0 | process(2064.8338460, nsRFPService::TimeScale::MicroSeconds, 20); |
81 | 0 | // These are just big values |
82 | 0 | process(1516305819, nsRFPService::TimeScale::MicroSeconds, 20); |
83 | 0 | process(69053.12, nsRFPService::TimeScale::MicroSeconds, 20); |
84 | 0 | cleanupJitter(jitterEnabled); |
85 | 0 | } |
86 | | |
87 | 0 | TEST(ResistFingerprinting, ReducePrecision_KnownGood) { |
88 | 0 | bool jitterEnabled = setupJitter(false); |
89 | 0 | process(2064.8338460, nsRFPService::TimeScale::MilliSeconds, 20); |
90 | 0 | process(69027.62, nsRFPService::TimeScale::MilliSeconds, 20); |
91 | 0 | process(69053.12, nsRFPService::TimeScale::MilliSeconds, 20); |
92 | 0 | cleanupJitter(jitterEnabled); |
93 | 0 | } |
94 | | |
95 | 0 | TEST(ResistFingerprinting, ReducePrecision_KnownBad) { |
96 | 0 | bool jitterEnabled = setupJitter(false); |
97 | 0 | process(1054.842405, nsRFPService::TimeScale::MilliSeconds, 20); |
98 | 0 | process(273.53038600000002, nsRFPService::TimeScale::MilliSeconds, 20); |
99 | 0 | process(628.66686500000003, nsRFPService::TimeScale::MilliSeconds, 20); |
100 | 0 | process(521.28919100000007, nsRFPService::TimeScale::MilliSeconds, 20); |
101 | 0 | cleanupJitter(jitterEnabled); |
102 | 0 | } |
103 | | |
104 | 0 | TEST(ResistFingerprinting, ReducePrecision_Edge) { |
105 | 0 | bool jitterEnabled = setupJitter(false); |
106 | 0 | process(2611.14, nsRFPService::TimeScale::MilliSeconds, 20); |
107 | 0 | process(2611.16, nsRFPService::TimeScale::MilliSeconds, 20); |
108 | 0 | process(2612.16, nsRFPService::TimeScale::MilliSeconds, 20); |
109 | 0 | process(2601.64, nsRFPService::TimeScale::MilliSeconds, 20); |
110 | 0 | process(2595.16, nsRFPService::TimeScale::MilliSeconds, 20); |
111 | 0 | process(2578.66, nsRFPService::TimeScale::MilliSeconds, 20); |
112 | 0 | cleanupJitter(jitterEnabled); |
113 | 0 | } |
114 | | |
115 | 0 | TEST(ResistFingerprinting, ReducePrecision_Expectations) { |
116 | 0 | bool jitterEnabled = setupJitter(false); |
117 | 0 | double result; |
118 | 0 | result = nsRFPService::ReduceTimePrecisionImpl(2611.14, nsRFPService::TimeScale::MilliSeconds, 20, -1, TimerPrecisionType::All); |
119 | 0 | ASSERT_EQ(result, 2611.14); |
120 | 0 | result = nsRFPService::ReduceTimePrecisionImpl(2611.145, nsRFPService::TimeScale::MilliSeconds, 20, -1, TimerPrecisionType::All); |
121 | 0 | ASSERT_EQ(result, 2611.14); |
122 | 0 | result = nsRFPService::ReduceTimePrecisionImpl(2611.141, nsRFPService::TimeScale::MilliSeconds, 20, -1, TimerPrecisionType::All); |
123 | 0 | ASSERT_EQ(result, 2611.14); |
124 | 0 | result = nsRFPService::ReduceTimePrecisionImpl(2611.15999, nsRFPService::TimeScale::MilliSeconds, 20, -1, TimerPrecisionType::All); |
125 | 0 | ASSERT_EQ(result, 2611.14); |
126 | 0 | result = nsRFPService::ReduceTimePrecisionImpl(2611.15, nsRFPService::TimeScale::MilliSeconds, 20, -1, TimerPrecisionType::All); |
127 | 0 | ASSERT_EQ(result, 2611.14); |
128 | 0 | result = nsRFPService::ReduceTimePrecisionImpl(2611.13, nsRFPService::TimeScale::MilliSeconds, 20, -1, TimerPrecisionType::All); |
129 | 0 | ASSERT_EQ(result, 2611.12); |
130 | 0 | cleanupJitter(jitterEnabled); |
131 | 0 | } |
132 | | |
133 | 0 | TEST(ResistFingerprinting, ReducePrecision_ExpectedLossOfPrecision) { |
134 | 0 | bool jitterEnabled = setupJitter(false); |
135 | 0 | double result; |
136 | 0 | // We lose integer precision at 9007199254740992 - let's confirm that. |
137 | 0 | result = nsRFPService::ReduceTimePrecisionImpl(9007199254740992.0, nsRFPService::TimeScale::MicroSeconds, 5, -1, TimerPrecisionType::All); |
138 | 0 | ASSERT_EQ(result, 9007199254740990.0); |
139 | 0 | // 9007199254740995 is approximated to 9007199254740996 |
140 | 0 | result = nsRFPService::ReduceTimePrecisionImpl(9007199254740995.0, nsRFPService::TimeScale::MicroSeconds, 5, -1, TimerPrecisionType::All); |
141 | 0 | ASSERT_EQ(result, 9007199254740996); |
142 | 0 | // 9007199254740999 is approximated as 9007199254741000 |
143 | 0 | result = nsRFPService::ReduceTimePrecisionImpl(9007199254740999.0, nsRFPService::TimeScale::MicroSeconds, 5, -1, TimerPrecisionType::All); |
144 | 0 | ASSERT_EQ(result, 9007199254741000.0); |
145 | 0 | // 9007199254743568 can be represented exactly, but will be clamped to 9007199254743564 |
146 | 0 | result = nsRFPService::ReduceTimePrecisionImpl(9007199254743568.0, nsRFPService::TimeScale::MicroSeconds, 5, -1, TimerPrecisionType::All); |
147 | 0 | ASSERT_EQ(result, 9007199254743564.0); |
148 | 0 | cleanupJitter(jitterEnabled); |
149 | 0 | } |
150 | | |
151 | | // Use an ugly but simple hack to turn an integer-based rand() |
152 | | // function to a double-based one. |
153 | 0 | #define RAND_DOUBLE (rand() * (rand() / (double)rand())) |
154 | | |
155 | | // If you're doing logging, you really don't want to run this test. |
156 | 0 | #define RUN_AGGRESSIVE false |
157 | | |
158 | 0 | TEST(ResistFingerprinting, ReducePrecision_Aggressive) { |
159 | 0 | if(!RUN_AGGRESSIVE) { |
160 | 0 | return; |
161 | 0 | } |
162 | 0 | |
163 | 0 | bool jitterEnabled = setupJitter(false); |
164 | 0 |
|
165 | 0 | for (int i=0; i<10000; i++) { |
166 | 0 | // Test three different time magnitudes, with decimals. |
167 | 0 | // Note that we need separate variables for the different units, as scaling |
168 | 0 | // them after calculating them will erase effects of approximation. |
169 | 0 | // A magnitude in the seconds since epoch range. |
170 | 0 | double time1_s = fmod(RAND_DOUBLE, 1516305819.0); |
171 | 0 | double time1_ms = fmod(RAND_DOUBLE, 1516305819000.0); |
172 | 0 | double time1_us = fmod(RAND_DOUBLE, 1516305819000000.0); |
173 | 0 | // A magnitude in the 'couple of minutes worth of milliseconds' range. |
174 | 0 | double time2_s = fmod(RAND_DOUBLE, (60.0 * 60 * 5)); |
175 | 0 | double time2_ms = fmod(RAND_DOUBLE, (1000.0 * 60 * 60 * 5)); |
176 | 0 | double time2_us = fmod(RAND_DOUBLE, (1000000.0 * 60 * 60 * 5)); |
177 | 0 | // A magnitude in the small range |
178 | 0 | double time3_s = fmod(RAND_DOUBLE, 10); |
179 | 0 | double time3_ms = fmod(RAND_DOUBLE, 10000); |
180 | 0 | double time3_us = fmod(RAND_DOUBLE, 10000000); |
181 | 0 |
|
182 | 0 | // Test two precision magnitudes, no decimals. |
183 | 0 | // A magnitude in the high milliseconds. |
184 | 0 | double precision1 = rand() % 250000; |
185 | 0 | // a magnitude in the low microseconds. |
186 | 0 | double precision2 = rand() % 200; |
187 | 0 |
|
188 | 0 | process(time1_s, nsRFPService::TimeScale::Seconds, precision1); |
189 | 0 | process(time1_s, nsRFPService::TimeScale::Seconds, precision2); |
190 | 0 | process(time2_s, nsRFPService::TimeScale::Seconds, precision1); |
191 | 0 | process(time2_s, nsRFPService::TimeScale::Seconds, precision2); |
192 | 0 | process(time3_s, nsRFPService::TimeScale::Seconds, precision1); |
193 | 0 | process(time3_s, nsRFPService::TimeScale::Seconds, precision2); |
194 | 0 |
|
195 | 0 | process(time1_ms, nsRFPService::TimeScale::MilliSeconds, precision1); |
196 | 0 | process(time1_ms, nsRFPService::TimeScale::MilliSeconds, precision2); |
197 | 0 | process(time2_ms, nsRFPService::TimeScale::MilliSeconds, precision1); |
198 | 0 | process(time2_ms, nsRFPService::TimeScale::MilliSeconds, precision2); |
199 | 0 | process(time3_ms, nsRFPService::TimeScale::MilliSeconds, precision1); |
200 | 0 | process(time3_ms, nsRFPService::TimeScale::MilliSeconds, precision2); |
201 | 0 |
|
202 | 0 | process(time1_us, nsRFPService::TimeScale::MicroSeconds, precision1); |
203 | 0 | process(time1_us, nsRFPService::TimeScale::MicroSeconds, precision2); |
204 | 0 | process(time2_us, nsRFPService::TimeScale::MicroSeconds, precision1); |
205 | 0 | process(time2_us, nsRFPService::TimeScale::MicroSeconds, precision2); |
206 | 0 | process(time3_us, nsRFPService::TimeScale::MicroSeconds, precision1); |
207 | 0 | process(time3_us, nsRFPService::TimeScale::MicroSeconds, precision2); |
208 | 0 | } |
209 | 0 | cleanupJitter(jitterEnabled); |
210 | 0 | } |
211 | | |
212 | | |
213 | 0 | TEST(ResistFingerprinting, ReducePrecision_JitterTestVectors) { |
214 | 0 | bool jitterEnabled = setupJitter(true); |
215 | 0 |
|
216 | 0 | /* |
217 | 0 | * Here's our test vector. First we set the secret to the 16 byte value |
218 | 0 | * 0x000102030405060708 0x101112131415161718 |
219 | 0 | * |
220 | 0 | * Then we work with a resolution of 500 us which will bucket things as such: |
221 | 0 | * Per-Clamp Buckets: [0, 500], [500, 1000], ... |
222 | 0 | * Per-Hash Buckets: [0, 4000], [4000, 8000], ... |
223 | 0 | * |
224 | 0 | * The first two hash values should be |
225 | 0 | * 0: SHA-256(0x0001020304050607 || 0x1011121314151617 || 0xa00f000000000000 || 0x0000000000000000) |
226 | 0 | * 78d2d811 804fcaa4 7d472a1e 9fe043d2 dd77b3df 06c1c4f2 9f35f28a e3afbec0 |
227 | 0 | * 4000: SHA-256(0x0001020304050607 || 0x1011121314151617 || 0xa00f000000000000 || 0xa00f000000000000) |
228 | 0 | * 1571bf19 92a89cd0 829259d5 b260a4a6 b8da8ad5 2e3ae33c 5571bb8d 8f69cca6 |
229 | 0 | * |
230 | 0 | * The midpoints are (if you're doing this manually, you need to correct endian-ness): |
231 | 0 | * 0 : 78d2d811 % 500 = 328 |
232 | 0 | * 500 : 804fcaa4 % 500 = 48 |
233 | 0 | * 1000: 7d472a1e % 500 = 293 |
234 | 0 | * 1500: 9fe043d2 % 500 = 275 |
235 | 0 | * 2000: dd77b3df % 500 = 297 |
236 | 0 | * 2500: 06c1c4f2 % 500 = 242 |
237 | 0 | * 3000: 9f35f28a % 500 = 247 |
238 | 0 | * 3500: e3afbec0 % 500 = 339 |
239 | 0 | * 4000: 1571bf19 % 500 = 225 |
240 | 0 | * 4500: 92a89cd0 % 500 = 198 |
241 | 0 | * 5000: 829259d5 % 500 = 218 |
242 | 0 | * 5500: b260a4a6 % 500 = 14 |
243 | 0 | */ |
244 | 0 |
|
245 | 0 | // Set the secret |
246 | 0 | long long throwAway; |
247 | 0 | uint8_t hardcodedSecret[16] = { |
248 | 0 | 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, |
249 | 0 | 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 }; |
250 | 0 |
|
251 | 0 | nsRFPService::RandomMidpoint(0, 500, -1, &throwAway, hardcodedSecret); |
252 | 0 |
|
253 | 0 | // Run the test vectors |
254 | 0 | double result; |
255 | 0 |
|
256 | 0 | result = nsRFPService::ReduceTimePrecisionImpl(1, nsRFPService::TimeScale::MicroSeconds, 500, 4000, TimerPrecisionType::All); |
257 | 0 | ASSERT_EQ(result, 0); |
258 | 0 | result = nsRFPService::ReduceTimePrecisionImpl(327, nsRFPService::TimeScale::MicroSeconds, 500, 4000, TimerPrecisionType::All); |
259 | 0 | ASSERT_EQ(result, 0); |
260 | 0 | result = nsRFPService::ReduceTimePrecisionImpl(328, nsRFPService::TimeScale::MicroSeconds, 500, 4000, TimerPrecisionType::All); |
261 | 0 | ASSERT_EQ(result, 500); |
262 | 0 | result = nsRFPService::ReduceTimePrecisionImpl(329, nsRFPService::TimeScale::MicroSeconds, 500, 4000, TimerPrecisionType::All); |
263 | 0 | ASSERT_EQ(result, 500); |
264 | 0 | result = nsRFPService::ReduceTimePrecisionImpl(499, nsRFPService::TimeScale::MicroSeconds, 500, 4000, TimerPrecisionType::All); |
265 | 0 | ASSERT_EQ(result, 500); |
266 | 0 |
|
267 | 0 | result = nsRFPService::ReduceTimePrecisionImpl(500, nsRFPService::TimeScale::MicroSeconds, 500, 4000, TimerPrecisionType::All); |
268 | 0 | ASSERT_EQ(result, 500); |
269 | 0 | result = nsRFPService::ReduceTimePrecisionImpl(540, nsRFPService::TimeScale::MicroSeconds, 500, 4000, TimerPrecisionType::All); |
270 | 0 | ASSERT_EQ(result, 500); |
271 | 0 | result = nsRFPService::ReduceTimePrecisionImpl(547, nsRFPService::TimeScale::MicroSeconds, 500, 4000, TimerPrecisionType::All); |
272 | 0 | ASSERT_EQ(result, 500); |
273 | 0 | result = nsRFPService::ReduceTimePrecisionImpl(548, nsRFPService::TimeScale::MicroSeconds, 500, 4000, TimerPrecisionType::All); |
274 | 0 | ASSERT_EQ(result, 1000); |
275 | 0 | result = nsRFPService::ReduceTimePrecisionImpl(930, nsRFPService::TimeScale::MicroSeconds, 500, 4000, TimerPrecisionType::All); |
276 | 0 | ASSERT_EQ(result, 1000); |
277 | 0 | result = nsRFPService::ReduceTimePrecisionImpl(1255, nsRFPService::TimeScale::MicroSeconds, 500, 4000, TimerPrecisionType::All); |
278 | 0 | ASSERT_EQ(result, 1000); |
279 | 0 |
|
280 | 0 | result = nsRFPService::ReduceTimePrecisionImpl(4000, nsRFPService::TimeScale::MicroSeconds, 500, 4000, TimerPrecisionType::All); |
281 | 0 | ASSERT_EQ(result, 4000); |
282 | 0 | result = nsRFPService::ReduceTimePrecisionImpl(4220, nsRFPService::TimeScale::MicroSeconds, 500, 4000, TimerPrecisionType::All); |
283 | 0 | ASSERT_EQ(result, 4000); |
284 | 0 | result = nsRFPService::ReduceTimePrecisionImpl(4224, nsRFPService::TimeScale::MicroSeconds, 500, 4000, TimerPrecisionType::All); |
285 | 0 | ASSERT_EQ(result, 4000); |
286 | 0 | result = nsRFPService::ReduceTimePrecisionImpl(4225, nsRFPService::TimeScale::MicroSeconds, 500, 4000, TimerPrecisionType::All); |
287 | 0 | ASSERT_EQ(result, 4500); |
288 | 0 | result = nsRFPService::ReduceTimePrecisionImpl(4340, nsRFPService::TimeScale::MicroSeconds, 500, 4000, TimerPrecisionType::All); |
289 | 0 | ASSERT_EQ(result, 4500); |
290 | 0 | result = nsRFPService::ReduceTimePrecisionImpl(4499, nsRFPService::TimeScale::MicroSeconds, 500, 4000, TimerPrecisionType::All); |
291 | 0 | ASSERT_EQ(result, 4500); |
292 | 0 |
|
293 | 0 | result = nsRFPService::ReduceTimePrecisionImpl(4500, nsRFPService::TimeScale::MicroSeconds, 500, 4000, TimerPrecisionType::All); |
294 | 0 | ASSERT_EQ(result, 4500); |
295 | 0 | result = nsRFPService::ReduceTimePrecisionImpl(4536, nsRFPService::TimeScale::MicroSeconds, 500, 4000, TimerPrecisionType::All); |
296 | 0 | ASSERT_EQ(result, 4500); |
297 | 0 | result = nsRFPService::ReduceTimePrecisionImpl(4695, nsRFPService::TimeScale::MicroSeconds, 500, 4000, TimerPrecisionType::All); |
298 | 0 | ASSERT_EQ(result, 4500); |
299 | 0 | result = nsRFPService::ReduceTimePrecisionImpl(4698, nsRFPService::TimeScale::MicroSeconds, 500, 4000, TimerPrecisionType::All); |
300 | 0 | ASSERT_EQ(result, 5000); |
301 | 0 | result = nsRFPService::ReduceTimePrecisionImpl(4726, nsRFPService::TimeScale::MicroSeconds, 500, 4000, TimerPrecisionType::All); |
302 | 0 | ASSERT_EQ(result, 5000); |
303 | 0 | result = nsRFPService::ReduceTimePrecisionImpl(5106, nsRFPService::TimeScale::MicroSeconds, 500, 4000, TimerPrecisionType::All); |
304 | 0 | ASSERT_EQ(result, 5000); |
305 | 0 |
|
306 | 0 | cleanupJitter(jitterEnabled); |
307 | 0 | } |